xref: /netbsd-src/external/cddl/osnet/dev/fbt/arm/fbt_isa.c (revision 68e2407866124771d91deaa556a34460ec7875b7)
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