xref: /netbsd-src/external/cddl/osnet/dev/fbt/arm/fbt_isa.c (revision 2718af68c3efc72c9769069b5c7f9ed36f6b9def)
1 /*	$NetBSD: fbt_isa.c,v 1.1 2018/05/28 23:47:39 chs 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 int
60 fbt_invop(uintptr_t addr, struct trapframe *frame, uintptr_t rval)
61 {
62 	solaris_cpu_t *cpu = &solaris_cpu[cpu_number()];
63 	fbt_probe_t *fbt = fbt_probetab[FBT_ADDR2NDX(addr)];
64 	register_t fifthparam;
65 
66 	for (; fbt != NULL; fbt = fbt->fbtp_hashnext) {
67 		if ((uintptr_t)fbt->fbtp_patchpoint == addr) {
68 			if (fbt->fbtp_roffset == 0) {
69 				/* Get 5th parameter from stack */
70 				DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT);
71 				fifthparam = *(register_t *)frame->tf_svc_sp;
72 				DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT |
73 				    CPU_DTRACE_BADADDR);
74 
75 				cpu->cpu_dtrace_caller = frame->tf_svc_lr;
76 				dtrace_probe(fbt->fbtp_id, frame->tf_r0,
77 					     frame->tf_r1, frame->tf_r2,
78 					     frame->tf_r3, fifthparam);
79 			} else {
80 				/* XXX set caller */
81 				cpu->cpu_dtrace_caller = 0;
82 				dtrace_probe(fbt->fbtp_id, fbt->fbtp_roffset,
83 				    rval, 0, 0, 0);
84 			}
85 
86 			cpu->cpu_dtrace_caller = 0;
87 			return (fbt->fbtp_rval);
88 		}
89 	}
90 
91 	return (0);
92 }
93 
94 
95 void
96 fbt_patch_tracepoint(fbt_probe_t *fbt, fbt_patchval_t val)
97 {
98 	dtrace_icookie_t c;
99 
100 	c = dtrace_interrupt_disable();
101 
102 	ktext_write(fbt->fbtp_patchpoint, &val, sizeof (val));
103 
104 	dtrace_interrupt_enable(c);
105 }
106 
107 #ifdef __FreeBSD__
108 
109 int
110 fbt_provide_module_function(linker_file_t lf, int symindx,
111     linker_symval_t *symval, void *opaque)
112 {
113 	char *modname = opaque;
114 	const char *name = symval->name;
115 	fbt_probe_t *fbt, *retfbt;
116 	uint32_t *instr, *limit;
117 	int popm;
118 
119 	if (fbt_excluded(name))
120 		return (0);
121 
122 	instr = (uint32_t *)symval->value;
123 	limit = (uint32_t *)(symval->value + symval->size);
124 
125 	/*
126 	 * va_arg functions has first instruction of
127 	 * sub sp, sp, #?
128 	 */
129 	if ((*instr & 0xfffff000) == FBT_SUBSP)
130 		instr++;
131 
132 	/*
133 	 * check if insn is a pushm with LR
134 	 */
135 	if ((*instr & 0xffff0000) != FBT_PUSHM ||
136 	    (*instr & (1 << LR)) == 0)
137 		return (0);
138 
139 	fbt = kmem_zalloc(sizeof (fbt_probe_t), KM_SLEEP);
140 	fbt->fbtp_name = name;
141 	fbt->fbtp_id = dtrace_probe_create(fbt_id, modname,
142 	    name, FBT_ENTRY, 5, fbt);
143 	fbt->fbtp_patchpoint = instr;
144 	fbt->fbtp_ctl = lf;
145 	fbt->fbtp_loadcnt = lf->loadcnt;
146 	fbt->fbtp_savedval = *instr;
147 	fbt->fbtp_patchval = FBT_BREAKPOINT;
148 	fbt->fbtp_rval = DTRACE_INVOP_PUSHM;
149 	fbt->fbtp_symindx = symindx;
150 
151 	fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)];
152 	fbt_probetab[FBT_ADDR2NDX(instr)] = fbt;
153 
154 	lf->fbt_nentries++;
155 
156 	popm = FBT_POPM | ((*instr) & 0x3FFF) | 0x8000;
157 
158 	retfbt = NULL;
159 again:
160 	for (; instr < limit; instr++) {
161 		if (*instr == popm)
162 			break;
163 		else if ((*instr & 0xff000000) == FBT_JUMP) {
164 			uint32_t *target, *start;
165 			int offset;
166 
167 			offset = (*instr & 0xffffff);
168 			offset <<= 8;
169 			offset /= 64;
170 			target = instr + (2 + offset);
171 			start = (uint32_t *)symval->value;
172 			if (target >= limit || target < start)
173 				break;
174 		}
175 	}
176 
177 	if (instr >= limit)
178 		return (0);
179 
180 	/*
181 	 * We have a winner!
182 	 */
183 	fbt = kmem_zalloc(sizeof (fbt_probe_t), KM_SLEEP);
184 	fbt->fbtp_name = name;
185 	if (retfbt == NULL) {
186 		fbt->fbtp_id = dtrace_probe_create(fbt_id, modname,
187 		    name, FBT_RETURN, 5, fbt);
188 	} else {
189 		retfbt->fbtp_next = fbt;
190 		fbt->fbtp_id = retfbt->fbtp_id;
191 	}
192 	retfbt = fbt;
193 
194 	fbt->fbtp_patchpoint = instr;
195 	fbt->fbtp_ctl = lf;
196 	fbt->fbtp_loadcnt = lf->loadcnt;
197 	fbt->fbtp_symindx = symindx;
198 	if ((*instr & 0xff000000) == FBT_JUMP)
199 		fbt->fbtp_rval = DTRACE_INVOP_B;
200 	else
201 		fbt->fbtp_rval = DTRACE_INVOP_POPM;
202 	fbt->fbtp_savedval = *instr;
203 	fbt->fbtp_patchval = FBT_BREAKPOINT;
204 	fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)];
205 	fbt_probetab[FBT_ADDR2NDX(instr)] = fbt;
206 
207 	lf->fbt_nentries++;
208 
209 	instr++;
210 	goto again;
211 }
212 
213 #endif /* __FreeBSD_ */
214 
215 #ifdef __NetBSD__
216 
217 #define	FBT_PATCHVAL		DTRACE_BREAKPOINT
218 
219 /* entry and return */
220 #define	FBT_BX_LR_P(insn)	(((insn) & ~INSN_COND_MASK) == 0x012fff1e)
221 #define	FBT_B_LABEL_P(insn)	(((insn) & 0xff000000) == 0xea000000)
222 /* entry */
223 #define	FBT_MOV_IP_SP_P(insn)	((insn) == 0xe1a0c00d)
224 /* index=1, add=1, wback=0 */
225 #define	FBT_LDR_IMM_P(insn)	(((insn) & 0xfff00000) == 0xe5900000)
226 #define	FBT_MOVW_P(insn)	(((insn) & 0xfff00000) == 0xe3000000)
227 #define	FBT_MOV_IMM_P(insn)	(((insn) & 0xffff0000) == 0xe3a00000)
228 #define	FBT_CMP_IMM_P(insn)	(((insn) & 0xfff00000) == 0xe3500000)
229 #define	FBT_PUSH_P(insn)	(((insn) & 0xffff0000) == 0xe92d0000)
230 /* return */
231 /* cond=always, writeback=no, rn=sp and register_list includes pc */
232 #define	FBT_LDM_P(insn)	(((insn) & 0x0fff8000) == 0x089d8000)
233 #define	FBT_LDMIB_P(insn)	(((insn) & 0x0fff8000) == 0x099d8000)
234 #define	FBT_MOV_PC_LR_P(insn)	(((insn) & ~INSN_COND_MASK) == 0x01a0f00e)
235 /* cond=always, writeback=no, rn=sp and register_list includes lr, but not pc */
236 #define	FBT_LDM_LR_P(insn)	(((insn) & 0xffffc000) == 0xe89d4000)
237 #define	FBT_LDMIB_LR_P(insn)	(((insn) & 0xffffc000) == 0xe99d4000)
238 
239 /* rval = insn | invop_id (overwriting cond with invop ID) */
240 #define	BUILD_RVAL(insn, id)	(((insn) & ~INSN_COND_MASK) | __SHIFTIN((id), INSN_COND_MASK))
241 /* encode cond in the first byte */
242 #define	PATCHVAL_ENCODE_COND(insn)	(FBT_PATCHVAL | __SHIFTOUT((insn), INSN_COND_MASK))
243 
244 int
245 fbt_provide_module_cb(const char *name, int symindx, void *value,
246 	uint32_t symsize, int type, void *opaque)
247 {
248 	fbt_probe_t *fbt, *retfbt;
249 	uint32_t *instr, *limit;
250 	bool was_ldm_lr = false;
251 	int size;
252 
253 	struct fbt_ksyms_arg *fka = opaque;
254 	modctl_t *mod = fka->fka_mod;
255 	const char *modname = module_name(mod);
256 
257 
258 	/* got a function? */
259 	if (ELF_ST_TYPE(type) != STT_FUNC)
260 		return 0;
261 
262 	if (fbt_excluded(name))
263 		return (0);
264 
265 	/*
266 	 * Exclude some more symbols which can be called from probe context.
267 	 */
268 	if (strncmp(name, "_spl", 4) == 0 ||
269 	    strcmp(name, "binuptime") == 0 ||
270 	    strcmp(name, "nanouptime") == 0 ||
271 	    strcmp(name, "dosoftints") == 0 ||
272 	    strcmp(name, "fbt_emulate") == 0 ||
273 	    strcmp(name, "undefinedinstruction") == 0 ||
274 	    strncmp(name, "dmt_", 4) == 0 /* omap */ ||
275 	    strncmp(name, "mvsoctmr_", 9) == 0 /* marvell */ ) {
276 		return 0;
277 	}
278 
279 	instr = (uint32_t *) value;
280 	limit = (uint32_t *)((uintptr_t)value + symsize);
281 
282 	if (!FBT_MOV_IP_SP_P(*instr)
283 	    && !FBT_BX_LR_P(*instr)
284 	    && !FBT_MOVW_P(*instr)
285 	    && !FBT_MOV_IMM_P(*instr)
286 	    && !FBT_B_LABEL_P(*instr)
287 	    && !FBT_LDR_IMM_P(*instr)
288 	    && !FBT_CMP_IMM_P(*instr)
289 	    && !FBT_PUSH_P(*instr)
290 	    ) {
291 		return 0;
292 	}
293 
294 	fbt = kmem_zalloc(sizeof (fbt_probe_t), KM_SLEEP);
295 	fbt->fbtp_name = name;
296 	fbt->fbtp_id = dtrace_probe_create(fbt_id, modname,
297 	    name, FBT_ENTRY, 5, fbt);
298 	fbt->fbtp_patchpoint = instr;
299 	fbt->fbtp_ctl = mod;
300 	/* fbt->fbtp_loadcnt = lf->loadcnt; */
301 	if (FBT_MOV_IP_SP_P(*instr))
302 		fbt->fbtp_rval = BUILD_RVAL(*instr, DTRACE_INVOP_MOV_IP_SP);
303 	else if (FBT_LDR_IMM_P(*instr))
304 		fbt->fbtp_rval = BUILD_RVAL(*instr, DTRACE_INVOP_LDR_IMM);
305 	else if (FBT_MOVW_P(*instr))
306 		fbt->fbtp_rval = BUILD_RVAL(*instr, DTRACE_INVOP_MOVW);
307 	else if (FBT_MOV_IMM_P(*instr))
308 		fbt->fbtp_rval = BUILD_RVAL(*instr, DTRACE_INVOP_MOV_IMM);
309 	else if (FBT_CMP_IMM_P(*instr))
310 		fbt->fbtp_rval = BUILD_RVAL(*instr, DTRACE_INVOP_CMP_IMM);
311 	else if (FBT_BX_LR_P(*instr))
312 		fbt->fbtp_rval = BUILD_RVAL(*instr, DTRACE_INVOP_BX_LR);
313 	else if (FBT_PUSH_P(*instr))
314 		fbt->fbtp_rval = BUILD_RVAL(*instr, DTRACE_INVOP_PUSHM);
315 	else if (FBT_B_LABEL_P(*instr))
316 		fbt->fbtp_rval = BUILD_RVAL(*instr, DTRACE_INVOP_B);
317 	else
318 		KASSERT(0);
319 
320 	KASSERTMSG((fbt->fbtp_rval >> 28) != 0,
321 		   "fbt %p insn 0x%x name %s rval 0x%08x",
322 		   fbt, *instr, name, fbt->fbtp_rval);
323 
324 	fbt->fbtp_patchval = PATCHVAL_ENCODE_COND(*instr);
325 	fbt->fbtp_savedval = *instr;
326 	fbt->fbtp_symindx = symindx;
327 
328 	fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)];
329 	fbt_probetab[FBT_ADDR2NDX(instr)] = fbt;
330 
331 	retfbt = NULL;
332 
333 	while (instr < limit) {
334 		if (instr >= limit)
335 			return (0);
336 
337 		size = 1;
338 
339 		if (!FBT_BX_LR_P(*instr)
340 		    && !FBT_MOV_PC_LR_P(*instr)
341 		    && !FBT_LDM_P(*instr)
342 		    && !FBT_LDMIB_P(*instr)
343 		    && !(was_ldm_lr && FBT_B_LABEL_P(*instr))
344 		    ) {
345 			if (FBT_LDM_LR_P(*instr) || FBT_LDMIB_LR_P(*instr))
346 				was_ldm_lr = true;
347 			else
348 				was_ldm_lr = false;
349 			instr += size;
350 			continue;
351 		}
352 
353 		/*
354 		 * We have a winner!
355 		 */
356 		fbt = kmem_zalloc(sizeof (fbt_probe_t), KM_SLEEP);
357 		fbt->fbtp_name = name;
358 
359 		if (retfbt == NULL) {
360 			fbt->fbtp_id = dtrace_probe_create(fbt_id, modname,
361 			    name, FBT_RETURN, 5, fbt);
362 		} else {
363 			retfbt->fbtp_next = fbt;
364 			fbt->fbtp_id = retfbt->fbtp_id;
365 		}
366 
367 		retfbt = fbt;
368 		fbt->fbtp_patchpoint = instr;
369 		fbt->fbtp_ctl = mod;
370 		/* fbt->fbtp_loadcnt = lf->loadcnt; */
371 		fbt->fbtp_symindx = symindx;
372 
373 		if (FBT_BX_LR_P(*instr))
374 			fbt->fbtp_rval = BUILD_RVAL(*instr, DTRACE_INVOP_BX_LR);
375 		else if (FBT_MOV_PC_LR_P(*instr))
376 			fbt->fbtp_rval = BUILD_RVAL(*instr, DTRACE_INVOP_MOV_PC_LR);
377 		else if (FBT_LDM_P(*instr))
378 			fbt->fbtp_rval = BUILD_RVAL(*instr, DTRACE_INVOP_LDM);
379 		else if (FBT_LDMIB_P(*instr))
380 			fbt->fbtp_rval = BUILD_RVAL(*instr, DTRACE_INVOP_POPM);
381 		else if (FBT_B_LABEL_P(*instr))
382 			fbt->fbtp_rval = BUILD_RVAL(*instr, DTRACE_INVOP_B);
383 		else
384 			KASSERT(0);
385 
386 		KASSERTMSG((fbt->fbtp_rval >> 28) != 0, "fbt %p name %s rval 0x%08x",
387 			   fbt, name, fbt->fbtp_rval);
388 
389 		fbt->fbtp_roffset = (uintptr_t)(instr - (uint32_t *) value);
390 		fbt->fbtp_patchval = PATCHVAL_ENCODE_COND(*instr);
391 
392 		fbt->fbtp_savedval = *instr;
393 		fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)];
394 		fbt_probetab[FBT_ADDR2NDX(instr)] = fbt;
395 
396 		instr += size;
397 		was_ldm_lr = false;
398 	}
399 
400 	return 0;
401 }
402 
403 #endif /* __NetBSD__ */
404