1 /* $NetBSD: fbt_isa.c,v 1.2 2023/05/22 15:12:54 riastradh Exp $ */
2
3 /*
4 * CDDL HEADER START
5 *
6 * The contents of this file are subject to the terms of the
7 * Common Development and Distribution License (the "License").
8 * You may not use this file except in compliance with the License.
9 *
10 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
11 * or http://www.opensolaris.org/os/licensing.
12 * See the License for the specific language governing permissions
13 * and limitations under the License.
14 *
15 * When distributing Covered Code, include this CDDL HEADER in each
16 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
17 * If applicable, add the following below this CDDL HEADER, with the
18 * fields enclosed by brackets "[]" replaced with your own identifying
19 * information: Portions Copyright [yyyy] [name of copyright owner]
20 *
21 * CDDL HEADER END
22 *
23 * Portions Copyright 2006-2008 John Birrell jb@freebsd.org
24 * Portions Copyright 2013 Justin Hibbits jhibbits@freebsd.org
25 * Portions Copyright 2013 Howard Su howardsu@freebsd.org
26 *
27 * $FreeBSD: head/sys/cddl/dev/fbt/arm/fbt_isa.c 312378 2017-01-18 13:27:24Z andrew $
28 *
29 */
30
31 /*
32 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
33 * Use is subject to license terms.
34 */
35
36 #include <sys/cdefs.h>
37 #include <sys/param.h>
38 #include <sys/module.h>
39 #include <sys/kmem.h>
40
41 #include <sys/dtrace.h>
42
43 #include <machine/trap.h>
44 #include <arm/cpufunc.h>
45 #include <arm/armreg.h>
46 #include <arm/frame.h>
47 #include <uvm/uvm_extern.h>
48
49 #include "fbt.h"
50
51 #define FBT_PUSHM 0xe92d0000
52 #define FBT_POPM 0xe8bd0000
53 #define FBT_JUMP 0xea000000
54 #define FBT_SUBSP 0xe24dd000
55
56 #define FBT_ENTRY "entry"
57 #define FBT_RETURN "return"
58
59 static uint32_t
ldinstr(const uint32_t * instr)60 ldinstr(const uint32_t *instr)
61 {
62 #ifdef _ARM_ARCH_BE8 /* big-endian data, big-endian instructions */
63 return *instr;
64 #else /* little-endian instructions */
65 return le32toh(*instr);
66 #endif
67 }
68
69 static void
stinstr(uint32_t * instr,uint32_t val)70 stinstr(uint32_t *instr, uint32_t val)
71 {
72
73 #ifdef _ARM_ARCH_BE8 /* big-endian data, big-endian instructions */
74 val = bswap32(val);
75 #endif
76 ktext_write(instr, &val, sizeof(val)); /* write little-endian */
77 }
78
79 int
fbt_invop(uintptr_t addr,struct trapframe * frame,uintptr_t rval)80 fbt_invop(uintptr_t addr, struct trapframe *frame, uintptr_t rval)
81 {
82 solaris_cpu_t *cpu = &solaris_cpu[cpu_number()];
83 fbt_probe_t *fbt = fbt_probetab[FBT_ADDR2NDX(addr)];
84 register_t fifthparam;
85
86 for (; fbt != NULL; fbt = fbt->fbtp_hashnext) {
87 if ((uintptr_t)fbt->fbtp_patchpoint == addr) {
88 if (fbt->fbtp_roffset == 0) {
89 /* Get 5th parameter from stack */
90 DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT);
91 fifthparam = *(register_t *)frame->tf_svc_sp;
92 DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT |
93 CPU_DTRACE_BADADDR);
94
95 cpu->cpu_dtrace_caller = frame->tf_svc_lr;
96 dtrace_probe(fbt->fbtp_id, frame->tf_r0,
97 frame->tf_r1, frame->tf_r2,
98 frame->tf_r3, fifthparam);
99 } else {
100 /* XXX set caller */
101 cpu->cpu_dtrace_caller = 0;
102 dtrace_probe(fbt->fbtp_id, fbt->fbtp_roffset,
103 rval, 0, 0, 0);
104 }
105
106 cpu->cpu_dtrace_caller = 0;
107 return (fbt->fbtp_rval);
108 }
109 }
110
111 return (0);
112 }
113
114
115 void
fbt_patch_tracepoint(fbt_probe_t * fbt,fbt_patchval_t val)116 fbt_patch_tracepoint(fbt_probe_t *fbt, fbt_patchval_t val)
117 {
118 dtrace_icookie_t c;
119
120 c = dtrace_interrupt_disable();
121 stinstr(fbt->fbtp_patchpoint, val);
122 dtrace_interrupt_enable(c);
123 }
124
125 #ifdef __FreeBSD__
126
127 int
fbt_provide_module_function(linker_file_t lf,int symindx,linker_symval_t * symval,void * opaque)128 fbt_provide_module_function(linker_file_t lf, int symindx,
129 linker_symval_t *symval, void *opaque)
130 {
131 char *modname = opaque;
132 const char *name = symval->name;
133 fbt_probe_t *fbt, *retfbt;
134 uint32_t *instr, *limit;
135 int popm;
136
137 if (fbt_excluded(name))
138 return (0);
139
140 instr = (uint32_t *)symval->value;
141 limit = (uint32_t *)(symval->value + symval->size);
142
143 /*
144 * va_arg functions has first instruction of
145 * sub sp, sp, #?
146 */
147 if ((ldinstr(instr) & 0xfffff000) == FBT_SUBSP)
148 instr++;
149
150 /*
151 * check if insn is a pushm with LR
152 */
153 if ((ldinstr(instr) & 0xffff0000) != FBT_PUSHM ||
154 (ldinstr(instr) & (1 << LR)) == 0)
155 return (0);
156
157 fbt = kmem_zalloc(sizeof (fbt_probe_t), KM_SLEEP);
158 fbt->fbtp_name = name;
159 fbt->fbtp_id = dtrace_probe_create(fbt_id, modname,
160 name, FBT_ENTRY, 5, fbt);
161 fbt->fbtp_patchpoint = instr;
162 fbt->fbtp_ctl = lf;
163 fbt->fbtp_loadcnt = lf->loadcnt;
164 fbt->fbtp_savedval = ldinstr(instr);
165 fbt->fbtp_patchval = FBT_BREAKPOINT;
166 fbt->fbtp_rval = DTRACE_INVOP_PUSHM;
167 fbt->fbtp_symindx = symindx;
168
169 fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)];
170 fbt_probetab[FBT_ADDR2NDX(instr)] = fbt;
171
172 lf->fbt_nentries++;
173
174 popm = FBT_POPM | (ldinstr(instr) & 0x3FFF) | 0x8000;
175
176 retfbt = NULL;
177 again:
178 for (; instr < limit; instr++) {
179 if (ldinstr(instr) == popm)
180 break;
181 else if ((ldinstr(instr) & 0xff000000) == FBT_JUMP) {
182 uint32_t *target, *start;
183 int offset;
184
185 offset = (ldinstr(instr) & 0xffffff);
186 offset <<= 8;
187 offset /= 64;
188 target = instr + (2 + offset);
189 start = (uint32_t *)symval->value;
190 if (target >= limit || target < start)
191 break;
192 }
193 }
194
195 if (instr >= limit)
196 return (0);
197
198 /*
199 * We have a winner!
200 */
201 fbt = kmem_zalloc(sizeof (fbt_probe_t), KM_SLEEP);
202 fbt->fbtp_name = name;
203 if (retfbt == NULL) {
204 fbt->fbtp_id = dtrace_probe_create(fbt_id, modname,
205 name, FBT_RETURN, 5, fbt);
206 } else {
207 retfbt->fbtp_next = fbt;
208 fbt->fbtp_id = retfbt->fbtp_id;
209 }
210 retfbt = fbt;
211
212 fbt->fbtp_patchpoint = instr;
213 fbt->fbtp_ctl = lf;
214 fbt->fbtp_loadcnt = lf->loadcnt;
215 fbt->fbtp_symindx = symindx;
216 if ((ldinstr(instr) & 0xff000000) == FBT_JUMP)
217 fbt->fbtp_rval = DTRACE_INVOP_B;
218 else
219 fbt->fbtp_rval = DTRACE_INVOP_POPM;
220 fbt->fbtp_savedval = ldinstr(instr);
221 fbt->fbtp_patchval = FBT_BREAKPOINT;
222 fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)];
223 fbt_probetab[FBT_ADDR2NDX(instr)] = fbt;
224
225 lf->fbt_nentries++;
226
227 instr++;
228 goto again;
229 }
230
231 #endif /* __FreeBSD_ */
232
233 #ifdef __NetBSD__
234
235 #define FBT_PATCHVAL DTRACE_BREAKPOINT
236
237 /* entry and return */
238 #define FBT_BX_LR_P(insn) (((insn) & ~INSN_COND_MASK) == 0x012fff1e)
239 #define FBT_B_LABEL_P(insn) (((insn) & 0xff000000) == 0xea000000)
240 /* entry */
241 #define FBT_MOV_IP_SP_P(insn) ((insn) == 0xe1a0c00d)
242 /* index=1, add=1, wback=0 */
243 #define FBT_LDR_IMM_P(insn) (((insn) & 0xfff00000) == 0xe5900000)
244 #define FBT_MOVW_P(insn) (((insn) & 0xfff00000) == 0xe3000000)
245 #define FBT_MOV_IMM_P(insn) (((insn) & 0xffff0000) == 0xe3a00000)
246 #define FBT_CMP_IMM_P(insn) (((insn) & 0xfff00000) == 0xe3500000)
247 #define FBT_PUSH_P(insn) (((insn) & 0xffff0000) == 0xe92d0000)
248 /* return */
249 /* cond=always, writeback=no, rn=sp and register_list includes pc */
250 #define FBT_LDM_P(insn) (((insn) & 0x0fff8000) == 0x089d8000)
251 #define FBT_LDMIB_P(insn) (((insn) & 0x0fff8000) == 0x099d8000)
252 #define FBT_MOV_PC_LR_P(insn) (((insn) & ~INSN_COND_MASK) == 0x01a0f00e)
253 /* cond=always, writeback=no, rn=sp and register_list includes lr, but not pc */
254 #define FBT_LDM_LR_P(insn) (((insn) & 0xffffc000) == 0xe89d4000)
255 #define FBT_LDMIB_LR_P(insn) (((insn) & 0xffffc000) == 0xe99d4000)
256
257 /* rval = insn | invop_id (overwriting cond with invop ID) */
258 #define BUILD_RVAL(insn, id) (((insn) & ~INSN_COND_MASK) | __SHIFTIN((id), INSN_COND_MASK))
259 /* encode cond in the first byte */
260 #define PATCHVAL_ENCODE_COND(insn) (FBT_PATCHVAL | __SHIFTOUT((insn), INSN_COND_MASK))
261
262 int
fbt_provide_module_cb(const char * name,int symindx,void * value,uint32_t symsize,int type,void * opaque)263 fbt_provide_module_cb(const char *name, int symindx, void *value,
264 uint32_t symsize, int type, void *opaque)
265 {
266 fbt_probe_t *fbt, *retfbt;
267 uint32_t *instr, *limit;
268 bool was_ldm_lr = false;
269 int size;
270
271 struct fbt_ksyms_arg *fka = opaque;
272 modctl_t *mod = fka->fka_mod;
273 const char *modname = module_name(mod);
274
275
276 /* got a function? */
277 if (ELF_ST_TYPE(type) != STT_FUNC)
278 return 0;
279
280 if (fbt_excluded(name))
281 return (0);
282
283 /*
284 * Exclude some more symbols which can be called from probe context.
285 */
286 if (strncmp(name, "_spl", 4) == 0 ||
287 strcmp(name, "binuptime") == 0 ||
288 strcmp(name, "nanouptime") == 0 ||
289 strcmp(name, "dosoftints") == 0 ||
290 strcmp(name, "fbt_emulate") == 0 ||
291 strcmp(name, "undefinedinstruction") == 0 ||
292 strncmp(name, "dmt_", 4) == 0 /* omap */ ||
293 strncmp(name, "mvsoctmr_", 9) == 0 /* marvell */ ) {
294 return 0;
295 }
296
297 instr = (uint32_t *) value;
298 limit = (uint32_t *)((uintptr_t)value + symsize);
299
300 if (!FBT_MOV_IP_SP_P(ldinstr(instr))
301 && !FBT_BX_LR_P(ldinstr(instr))
302 && !FBT_MOVW_P(ldinstr(instr))
303 && !FBT_MOV_IMM_P(ldinstr(instr))
304 && !FBT_B_LABEL_P(ldinstr(instr))
305 && !FBT_LDR_IMM_P(ldinstr(instr))
306 && !FBT_CMP_IMM_P(ldinstr(instr))
307 && !FBT_PUSH_P(ldinstr(instr))
308 ) {
309 return 0;
310 }
311
312 fbt = kmem_zalloc(sizeof (fbt_probe_t), KM_SLEEP);
313 fbt->fbtp_name = name;
314 fbt->fbtp_id = dtrace_probe_create(fbt_id, modname,
315 name, FBT_ENTRY, 5, fbt);
316 fbt->fbtp_patchpoint = instr;
317 fbt->fbtp_ctl = mod;
318 /* fbt->fbtp_loadcnt = lf->loadcnt; */
319 if (FBT_MOV_IP_SP_P(ldinstr(instr))) {
320 fbt->fbtp_rval = BUILD_RVAL(ldinstr(instr),
321 DTRACE_INVOP_MOV_IP_SP);
322 } else if (FBT_LDR_IMM_P(ldinstr(instr))) {
323 fbt->fbtp_rval = BUILD_RVAL(ldinstr(instr),
324 DTRACE_INVOP_LDR_IMM);
325 } else if (FBT_MOVW_P(ldinstr(instr))) {
326 fbt->fbtp_rval = BUILD_RVAL(ldinstr(instr), DTRACE_INVOP_MOVW);
327 } else if (FBT_MOV_IMM_P(ldinstr(instr))) {
328 fbt->fbtp_rval = BUILD_RVAL(ldinstr(instr),
329 DTRACE_INVOP_MOV_IMM);
330 } else if (FBT_CMP_IMM_P(ldinstr(instr))) {
331 fbt->fbtp_rval = BUILD_RVAL(ldinstr(instr),
332 DTRACE_INVOP_CMP_IMM);
333 } else if (FBT_BX_LR_P(ldinstr(instr))) {
334 fbt->fbtp_rval = BUILD_RVAL(ldinstr(instr),
335 DTRACE_INVOP_BX_LR);
336 } else if (FBT_PUSH_P(ldinstr(instr))) {
337 fbt->fbtp_rval = BUILD_RVAL(ldinstr(instr),
338 DTRACE_INVOP_PUSHM);
339 } else if (FBT_B_LABEL_P(ldinstr(instr))) {
340 fbt->fbtp_rval = BUILD_RVAL(ldinstr(instr),
341 DTRACE_INVOP_B);
342 } else {
343 KASSERT(0);
344 }
345
346 KASSERTMSG((fbt->fbtp_rval >> 28) != 0,
347 "fbt %p insn 0x%x name %s rval 0x%08x",
348 fbt, ldinstr(instr), name, fbt->fbtp_rval);
349
350 fbt->fbtp_patchval = PATCHVAL_ENCODE_COND(ldinstr(instr));
351 fbt->fbtp_savedval = ldinstr(instr);
352 fbt->fbtp_symindx = symindx;
353
354 fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)];
355 fbt_probetab[FBT_ADDR2NDX(instr)] = fbt;
356
357 retfbt = NULL;
358
359 while (instr < limit) {
360 if (instr >= limit)
361 return (0);
362
363 size = 1;
364
365 if (!FBT_BX_LR_P(ldinstr(instr))
366 && !FBT_MOV_PC_LR_P(ldinstr(instr))
367 && !FBT_LDM_P(ldinstr(instr))
368 && !FBT_LDMIB_P(ldinstr(instr))
369 && !(was_ldm_lr && FBT_B_LABEL_P(ldinstr(instr)))
370 ) {
371 if (FBT_LDM_LR_P(ldinstr(instr)) ||
372 FBT_LDMIB_LR_P(ldinstr(instr)))
373 was_ldm_lr = true;
374 else
375 was_ldm_lr = false;
376 instr += size;
377 continue;
378 }
379
380 /*
381 * We have a winner!
382 */
383 fbt = kmem_zalloc(sizeof (fbt_probe_t), KM_SLEEP);
384 fbt->fbtp_name = name;
385
386 if (retfbt == NULL) {
387 fbt->fbtp_id = dtrace_probe_create(fbt_id, modname,
388 name, FBT_RETURN, 5, fbt);
389 } else {
390 retfbt->fbtp_next = fbt;
391 fbt->fbtp_id = retfbt->fbtp_id;
392 }
393
394 retfbt = fbt;
395 fbt->fbtp_patchpoint = instr;
396 fbt->fbtp_ctl = mod;
397 /* fbt->fbtp_loadcnt = lf->loadcnt; */
398 fbt->fbtp_symindx = symindx;
399
400 if (FBT_BX_LR_P(ldinstr(instr))) {
401 fbt->fbtp_rval = BUILD_RVAL(ldinstr(instr),
402 DTRACE_INVOP_BX_LR);
403 } else if (FBT_MOV_PC_LR_P(ldinstr(instr))) {
404 fbt->fbtp_rval = BUILD_RVAL(ldinstr(instr),
405 DTRACE_INVOP_MOV_PC_LR);
406 } else if (FBT_LDM_P(ldinstr(instr))) {
407 fbt->fbtp_rval = BUILD_RVAL(ldinstr(instr),
408 DTRACE_INVOP_LDM);
409 } else if (FBT_LDMIB_P(ldinstr(instr))) {
410 fbt->fbtp_rval = BUILD_RVAL(ldinstr(instr),
411 DTRACE_INVOP_POPM);
412 } else if (FBT_B_LABEL_P(ldinstr(instr))) {
413 fbt->fbtp_rval = BUILD_RVAL(ldinstr(instr),
414 DTRACE_INVOP_B);
415 } else {
416 KASSERT(0);
417 }
418
419 KASSERTMSG((fbt->fbtp_rval >> 28) != 0, "fbt %p name %s rval 0x%08x",
420 fbt, name, fbt->fbtp_rval);
421
422 fbt->fbtp_roffset = (uintptr_t)(instr - (uint32_t *) value);
423 fbt->fbtp_patchval = PATCHVAL_ENCODE_COND(ldinstr(instr));
424
425 fbt->fbtp_savedval = ldinstr(instr);
426 fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)];
427 fbt_probetab[FBT_ADDR2NDX(instr)] = fbt;
428
429 instr += size;
430 was_ldm_lr = false;
431 }
432
433 return 0;
434 }
435
436 #endif /* __NetBSD__ */
437