| b.liu | e958203 | 2025-04-17 19:18:16 +0800 | [diff] [blame] | 1 | /* SPDX-License-Identifier: GPL-2.0 */ |
| 2 | /* |
| 3 | * Traceprobe fetch helper inlines |
| 4 | */ |
| 5 | |
| 6 | static nokprobe_inline void |
| 7 | fetch_store_raw(unsigned long val, struct fetch_insn *code, void *buf) |
| 8 | { |
| 9 | switch (code->size) { |
| 10 | case 1: |
| 11 | *(u8 *)buf = (u8)val; |
| 12 | break; |
| 13 | case 2: |
| 14 | *(u16 *)buf = (u16)val; |
| 15 | break; |
| 16 | case 4: |
| 17 | *(u32 *)buf = (u32)val; |
| 18 | break; |
| 19 | case 8: |
| 20 | //TBD: 32bit signed |
| 21 | *(u64 *)buf = (u64)val; |
| 22 | break; |
| 23 | default: |
| 24 | *(unsigned long *)buf = val; |
| 25 | } |
| 26 | } |
| 27 | |
| 28 | static nokprobe_inline void |
| 29 | fetch_apply_bitfield(struct fetch_insn *code, void *buf) |
| 30 | { |
| 31 | switch (code->basesize) { |
| 32 | case 1: |
| 33 | *(u8 *)buf <<= code->lshift; |
| 34 | *(u8 *)buf >>= code->rshift; |
| 35 | break; |
| 36 | case 2: |
| 37 | *(u16 *)buf <<= code->lshift; |
| 38 | *(u16 *)buf >>= code->rshift; |
| 39 | break; |
| 40 | case 4: |
| 41 | *(u32 *)buf <<= code->lshift; |
| 42 | *(u32 *)buf >>= code->rshift; |
| 43 | break; |
| 44 | case 8: |
| 45 | *(u64 *)buf <<= code->lshift; |
| 46 | *(u64 *)buf >>= code->rshift; |
| 47 | break; |
| 48 | } |
| 49 | } |
| 50 | |
| 51 | /* |
| 52 | * These functions must be defined for each callsite. |
| 53 | * Return consumed dynamic data size (>= 0), or error (< 0). |
| 54 | * If dest is NULL, don't store result and return required dynamic data size. |
| 55 | */ |
| 56 | static int |
| 57 | process_fetch_insn(struct fetch_insn *code, void *rec, |
| 58 | void *dest, void *base); |
| 59 | static nokprobe_inline int fetch_store_strlen(unsigned long addr); |
| 60 | static nokprobe_inline int |
| 61 | fetch_store_string(unsigned long addr, void *dest, void *base); |
| 62 | static nokprobe_inline int fetch_store_strlen_user(unsigned long addr); |
| 63 | static nokprobe_inline int |
| 64 | fetch_store_string_user(unsigned long addr, void *dest, void *base); |
| 65 | static nokprobe_inline int |
| 66 | probe_mem_read(void *dest, void *src, size_t size); |
| 67 | static nokprobe_inline int |
| 68 | probe_mem_read_user(void *dest, void *src, size_t size); |
| 69 | |
| 70 | /* From the 2nd stage, routine is same */ |
| 71 | static nokprobe_inline int |
| 72 | process_fetch_insn_bottom(struct fetch_insn *code, unsigned long val, |
| 73 | void *dest, void *base) |
| 74 | { |
| 75 | struct fetch_insn *s3 = NULL; |
| 76 | int total = 0, ret = 0, i = 0; |
| 77 | u32 loc = 0; |
| 78 | unsigned long lval = val; |
| 79 | |
| 80 | stage2: |
| 81 | /* 2nd stage: dereference memory if needed */ |
| 82 | do { |
| 83 | if (code->op == FETCH_OP_DEREF) { |
| 84 | lval = val; |
| 85 | ret = probe_mem_read(&val, (void *)val + code->offset, |
| 86 | sizeof(val)); |
| 87 | } else if (code->op == FETCH_OP_UDEREF) { |
| 88 | lval = val; |
| 89 | ret = probe_mem_read_user(&val, |
| 90 | (void *)val + code->offset, sizeof(val)); |
| 91 | } else |
| 92 | break; |
| 93 | if (ret) |
| 94 | return ret; |
| 95 | code++; |
| 96 | } while (1); |
| 97 | |
| 98 | s3 = code; |
| 99 | stage3: |
| 100 | /* 3rd stage: store value to buffer */ |
| 101 | if (unlikely(!dest)) { |
| 102 | if (code->op == FETCH_OP_ST_STRING) { |
| 103 | ret = fetch_store_strlen(val + code->offset); |
| 104 | code++; |
| 105 | goto array; |
| 106 | } else if (code->op == FETCH_OP_ST_USTRING) { |
| 107 | ret += fetch_store_strlen_user(val + code->offset); |
| 108 | code++; |
| 109 | goto array; |
| 110 | } else |
| 111 | return -EILSEQ; |
| 112 | } |
| 113 | |
| 114 | switch (code->op) { |
| 115 | case FETCH_OP_ST_RAW: |
| 116 | fetch_store_raw(val, code, dest); |
| 117 | break; |
| 118 | case FETCH_OP_ST_MEM: |
| 119 | probe_mem_read(dest, (void *)val + code->offset, code->size); |
| 120 | break; |
| 121 | case FETCH_OP_ST_UMEM: |
| 122 | probe_mem_read_user(dest, (void *)val + code->offset, code->size); |
| 123 | break; |
| 124 | case FETCH_OP_ST_STRING: |
| 125 | loc = *(u32 *)dest; |
| 126 | ret = fetch_store_string(val + code->offset, dest, base); |
| 127 | break; |
| 128 | case FETCH_OP_ST_USTRING: |
| 129 | loc = *(u32 *)dest; |
| 130 | ret = fetch_store_string_user(val + code->offset, dest, base); |
| 131 | break; |
| 132 | default: |
| 133 | return -EILSEQ; |
| 134 | } |
| 135 | code++; |
| 136 | |
| 137 | /* 4th stage: modify stored value if needed */ |
| 138 | if (code->op == FETCH_OP_MOD_BF) { |
| 139 | fetch_apply_bitfield(code, dest); |
| 140 | code++; |
| 141 | } |
| 142 | |
| 143 | array: |
| 144 | /* the last stage: Loop on array */ |
| 145 | if (code->op == FETCH_OP_LP_ARRAY) { |
| 146 | if (ret < 0) |
| 147 | ret = 0; |
| 148 | total += ret; |
| 149 | if (++i < code->param) { |
| 150 | code = s3; |
| 151 | if (s3->op != FETCH_OP_ST_STRING && |
| 152 | s3->op != FETCH_OP_ST_USTRING) { |
| 153 | dest += s3->size; |
| 154 | val += s3->size; |
| 155 | goto stage3; |
| 156 | } |
| 157 | code--; |
| 158 | val = lval + sizeof(char *); |
| 159 | if (dest) { |
| 160 | dest += sizeof(u32); |
| 161 | *(u32 *)dest = update_data_loc(loc, ret); |
| 162 | } |
| 163 | goto stage2; |
| 164 | } |
| 165 | code++; |
| 166 | ret = total; |
| 167 | } |
| 168 | |
| 169 | return code->op == FETCH_OP_END ? ret : -EILSEQ; |
| 170 | } |
| 171 | |
| 172 | /* Sum up total data length for dynamic arraies (strings) */ |
| 173 | static nokprobe_inline int |
| 174 | __get_data_size(struct trace_probe *tp, struct pt_regs *regs) |
| 175 | { |
| 176 | struct probe_arg *arg; |
| 177 | int i, len, ret = 0; |
| 178 | |
| 179 | for (i = 0; i < tp->nr_args; i++) { |
| 180 | arg = tp->args + i; |
| 181 | if (unlikely(arg->dynamic)) { |
| 182 | len = process_fetch_insn(arg->code, regs, NULL, NULL); |
| 183 | if (len > 0) |
| 184 | ret += len; |
| 185 | } |
| 186 | } |
| 187 | |
| 188 | return ret; |
| 189 | } |
| 190 | |
| 191 | /* Store the value of each argument */ |
| 192 | static nokprobe_inline void |
| 193 | store_trace_args(void *data, struct trace_probe *tp, void *rec, |
| 194 | int header_size, int maxlen) |
| 195 | { |
| 196 | struct probe_arg *arg; |
| 197 | void *base = data - header_size; |
| 198 | void *dyndata = data + tp->size; |
| 199 | u32 *dl; /* Data location */ |
| 200 | int ret, i; |
| 201 | |
| 202 | for (i = 0; i < tp->nr_args; i++) { |
| 203 | arg = tp->args + i; |
| 204 | dl = data + arg->offset; |
| 205 | /* Point the dynamic data area if needed */ |
| 206 | if (unlikely(arg->dynamic)) |
| 207 | *dl = make_data_loc(maxlen, dyndata - base); |
| 208 | ret = process_fetch_insn(arg->code, rec, dl, base); |
| 209 | if (arg->dynamic) { |
| 210 | if (unlikely(ret < 0)) { |
| 211 | *dl = make_data_loc(0, dyndata - base); |
| 212 | } else { |
| 213 | dyndata += ret; |
| 214 | maxlen -= ret; |
| 215 | } |
| 216 | } |
| 217 | } |
| 218 | } |
| 219 | |
| 220 | static inline int |
| 221 | print_probe_args(struct trace_seq *s, struct probe_arg *args, int nr_args, |
| 222 | u8 *data, void *field) |
| 223 | { |
| 224 | void *p; |
| 225 | int i, j; |
| 226 | |
| 227 | for (i = 0; i < nr_args; i++) { |
| 228 | struct probe_arg *a = args + i; |
| 229 | |
| 230 | trace_seq_printf(s, " %s=", a->name); |
| 231 | if (likely(!a->count)) { |
| 232 | if (!a->type->print(s, data + a->offset, field)) |
| 233 | return -ENOMEM; |
| 234 | continue; |
| 235 | } |
| 236 | trace_seq_putc(s, '{'); |
| 237 | p = data + a->offset; |
| 238 | for (j = 0; j < a->count; j++) { |
| 239 | if (!a->type->print(s, p, field)) |
| 240 | return -ENOMEM; |
| 241 | trace_seq_putc(s, j == a->count - 1 ? '}' : ','); |
| 242 | p += a->type->size; |
| 243 | } |
| 244 | } |
| 245 | return 0; |
| 246 | } |