| 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, void *edata, | 
|---|
| 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 | static nokprobe_inline int | 
|---|
| 71 | fetch_store_symstrlen(unsigned long addr) | 
|---|
| 72 | { | 
|---|
| 73 | char namebuf[KSYM_SYMBOL_LEN]; | 
|---|
| 74 | int ret; | 
|---|
| 75 |  | 
|---|
| 76 | ret = sprint_symbol(buffer: namebuf, address: addr); | 
|---|
| 77 | if (ret < 0) | 
|---|
| 78 | return 0; | 
|---|
| 79 |  | 
|---|
| 80 | return ret + 1; | 
|---|
| 81 | } | 
|---|
| 82 |  | 
|---|
| 83 | /* | 
|---|
| 84 | * Fetch a null-terminated symbol string + offset. Caller MUST set *(u32 *)buf | 
|---|
| 85 | * with max length and relative data location. | 
|---|
| 86 | */ | 
|---|
| 87 | static nokprobe_inline int | 
|---|
| 88 | fetch_store_symstring(unsigned long addr, void *dest, void *base) | 
|---|
| 89 | { | 
|---|
| 90 | int maxlen = get_loc_len(*(u32 *)dest); | 
|---|
| 91 | void *__dest; | 
|---|
| 92 |  | 
|---|
| 93 | if (unlikely(!maxlen)) | 
|---|
| 94 | return -ENOMEM; | 
|---|
| 95 |  | 
|---|
| 96 | __dest = get_loc_data(dl: dest, ent: base); | 
|---|
| 97 |  | 
|---|
| 98 | return sprint_symbol(buffer: __dest, address: addr); | 
|---|
| 99 | } | 
|---|
| 100 |  | 
|---|
| 101 | /* common part of process_fetch_insn*/ | 
|---|
| 102 | static nokprobe_inline int | 
|---|
| 103 | process_common_fetch_insn(struct fetch_insn *code, unsigned long *val) | 
|---|
| 104 | { | 
|---|
| 105 | switch (code->op) { | 
|---|
| 106 | case FETCH_OP_IMM: | 
|---|
| 107 | *val = code->immediate; | 
|---|
| 108 | break; | 
|---|
| 109 | case FETCH_OP_COMM: | 
|---|
| 110 | *val = (unsigned long)current->comm; | 
|---|
| 111 | break; | 
|---|
| 112 | case FETCH_OP_DATA: | 
|---|
| 113 | *val = (unsigned long)code->data; | 
|---|
| 114 | break; | 
|---|
| 115 | default: | 
|---|
| 116 | return -EILSEQ; | 
|---|
| 117 | } | 
|---|
| 118 | return 0; | 
|---|
| 119 | } | 
|---|
| 120 |  | 
|---|
| 121 | /* From the 2nd stage, routine is same */ | 
|---|
| 122 | static nokprobe_inline int | 
|---|
| 123 | process_fetch_insn_bottom(struct fetch_insn *code, unsigned long val, | 
|---|
| 124 | void *dest, void *base) | 
|---|
| 125 | { | 
|---|
| 126 | struct fetch_insn *s3 = NULL; | 
|---|
| 127 | int total = 0, ret = 0, i = 0; | 
|---|
| 128 | u32 loc = 0; | 
|---|
| 129 | unsigned long lval = val; | 
|---|
| 130 |  | 
|---|
| 131 | stage2: | 
|---|
| 132 | /* 2nd stage: dereference memory if needed */ | 
|---|
| 133 | do { | 
|---|
| 134 | if (code->op == FETCH_OP_DEREF) { | 
|---|
| 135 | lval = val; | 
|---|
| 136 | ret = probe_mem_read(dest: &val, src: (void *)val + code->offset, | 
|---|
| 137 | size: sizeof(val)); | 
|---|
| 138 | } else if (code->op == FETCH_OP_UDEREF) { | 
|---|
| 139 | lval = val; | 
|---|
| 140 | ret = probe_mem_read_user(dest: &val, | 
|---|
| 141 | src: (void *)val + code->offset, size: sizeof(val)); | 
|---|
| 142 | } else | 
|---|
| 143 | break; | 
|---|
| 144 | if (ret) | 
|---|
| 145 | return ret; | 
|---|
| 146 | code++; | 
|---|
| 147 | } while (1); | 
|---|
| 148 |  | 
|---|
| 149 | s3 = code; | 
|---|
| 150 | stage3: | 
|---|
| 151 | /* 3rd stage: store value to buffer */ | 
|---|
| 152 | if (unlikely(!dest)) { | 
|---|
| 153 | switch (code->op) { | 
|---|
| 154 | case FETCH_OP_ST_STRING: | 
|---|
| 155 | ret = fetch_store_strlen(addr: val + code->offset); | 
|---|
| 156 | code++; | 
|---|
| 157 | goto array; | 
|---|
| 158 | case FETCH_OP_ST_USTRING: | 
|---|
| 159 | ret = fetch_store_strlen_user(addr: val + code->offset); | 
|---|
| 160 | code++; | 
|---|
| 161 | goto array; | 
|---|
| 162 | case FETCH_OP_ST_SYMSTR: | 
|---|
| 163 | ret = fetch_store_symstrlen(addr: val + code->offset); | 
|---|
| 164 | code++; | 
|---|
| 165 | goto array; | 
|---|
| 166 | default: | 
|---|
| 167 | return -EILSEQ; | 
|---|
| 168 | } | 
|---|
| 169 | } | 
|---|
| 170 |  | 
|---|
| 171 | switch (code->op) { | 
|---|
| 172 | case FETCH_OP_ST_RAW: | 
|---|
| 173 | fetch_store_raw(val, code, buf: dest); | 
|---|
| 174 | break; | 
|---|
| 175 | case FETCH_OP_ST_MEM: | 
|---|
| 176 | probe_mem_read(dest, src: (void *)val + code->offset, size: code->size); | 
|---|
| 177 | break; | 
|---|
| 178 | case FETCH_OP_ST_UMEM: | 
|---|
| 179 | probe_mem_read_user(dest, src: (void *)val + code->offset, size: code->size); | 
|---|
| 180 | break; | 
|---|
| 181 | case FETCH_OP_ST_STRING: | 
|---|
| 182 | loc = *(u32 *)dest; | 
|---|
| 183 | ret = fetch_store_string(addr: val + code->offset, dest, base); | 
|---|
| 184 | break; | 
|---|
| 185 | case FETCH_OP_ST_USTRING: | 
|---|
| 186 | loc = *(u32 *)dest; | 
|---|
| 187 | ret = fetch_store_string_user(addr: val + code->offset, dest, base); | 
|---|
| 188 | break; | 
|---|
| 189 | case FETCH_OP_ST_SYMSTR: | 
|---|
| 190 | loc = *(u32 *)dest; | 
|---|
| 191 | ret = fetch_store_symstring(addr: val + code->offset, dest, base); | 
|---|
| 192 | break; | 
|---|
| 193 | default: | 
|---|
| 194 | return -EILSEQ; | 
|---|
| 195 | } | 
|---|
| 196 | code++; | 
|---|
| 197 |  | 
|---|
| 198 | /* 4th stage: modify stored value if needed */ | 
|---|
| 199 | if (code->op == FETCH_OP_MOD_BF) { | 
|---|
| 200 | fetch_apply_bitfield(code, buf: dest); | 
|---|
| 201 | code++; | 
|---|
| 202 | } | 
|---|
| 203 |  | 
|---|
| 204 | array: | 
|---|
| 205 | /* the last stage: Loop on array */ | 
|---|
| 206 | if (code->op == FETCH_OP_LP_ARRAY) { | 
|---|
| 207 | if (ret < 0) | 
|---|
| 208 | ret = 0; | 
|---|
| 209 | total += ret; | 
|---|
| 210 | if (++i < code->param) { | 
|---|
| 211 | code = s3; | 
|---|
| 212 | if (s3->op != FETCH_OP_ST_STRING && | 
|---|
| 213 | s3->op != FETCH_OP_ST_USTRING) { | 
|---|
| 214 | dest += s3->size; | 
|---|
| 215 | val += s3->size; | 
|---|
| 216 | goto stage3; | 
|---|
| 217 | } | 
|---|
| 218 | code--; | 
|---|
| 219 | val = lval + sizeof(char *); | 
|---|
| 220 | if (dest) { | 
|---|
| 221 | dest += sizeof(u32); | 
|---|
| 222 | *(u32 *)dest = update_data_loc(loc, consumed: ret); | 
|---|
| 223 | } | 
|---|
| 224 | goto stage2; | 
|---|
| 225 | } | 
|---|
| 226 | code++; | 
|---|
| 227 | ret = total; | 
|---|
| 228 | } | 
|---|
| 229 |  | 
|---|
| 230 | return code->op == FETCH_OP_END ? ret : -EILSEQ; | 
|---|
| 231 | } | 
|---|
| 232 |  | 
|---|
| 233 | /* Sum up total data length for dynamic arrays (strings) */ | 
|---|
| 234 | static nokprobe_inline int | 
|---|
| 235 | __get_data_size(struct trace_probe *tp, void *regs, void *edata) | 
|---|
| 236 | { | 
|---|
| 237 | struct probe_arg *arg; | 
|---|
| 238 | int i, len, ret = 0; | 
|---|
| 239 |  | 
|---|
| 240 | for (i = 0; i < tp->nr_args; i++) { | 
|---|
| 241 | arg = tp->args + i; | 
|---|
| 242 | if (unlikely(arg->dynamic)) { | 
|---|
| 243 | len = process_fetch_insn(code: arg->code, rec: regs, edata, NULL, NULL); | 
|---|
| 244 | if (len > 0) | 
|---|
| 245 | ret += len; | 
|---|
| 246 | } | 
|---|
| 247 | } | 
|---|
| 248 |  | 
|---|
| 249 | return ret; | 
|---|
| 250 | } | 
|---|
| 251 |  | 
|---|
| 252 | /* Store the value of each argument */ | 
|---|
| 253 | static nokprobe_inline void | 
|---|
| 254 | store_trace_args(void *data, struct trace_probe *tp, void *rec, void *edata, | 
|---|
| 255 | int , int maxlen) | 
|---|
| 256 | { | 
|---|
| 257 | struct probe_arg *arg; | 
|---|
| 258 | void *base = data - header_size; | 
|---|
| 259 | void *dyndata = data + tp->size; | 
|---|
| 260 | u32 *dl;	/* Data location */ | 
|---|
| 261 | int ret, i; | 
|---|
| 262 |  | 
|---|
| 263 | for (i = 0; i < tp->nr_args; i++) { | 
|---|
| 264 | arg = tp->args + i; | 
|---|
| 265 | dl = data + arg->offset; | 
|---|
| 266 | /* Point the dynamic data area if needed */ | 
|---|
| 267 | if (unlikely(arg->dynamic)) | 
|---|
| 268 | *dl = make_data_loc(maxlen, dyndata - base); | 
|---|
| 269 | ret = process_fetch_insn(code: arg->code, rec, edata, dest: dl, base); | 
|---|
| 270 | if (arg->dynamic && likely(ret > 0)) { | 
|---|
| 271 | dyndata += ret; | 
|---|
| 272 | maxlen -= ret; | 
|---|
| 273 | } | 
|---|
| 274 | } | 
|---|
| 275 | } | 
|---|
| 276 |  | 
|---|