xref: /freebsd/sys/cddl/dev/fbt/aarch64/fbt_isa.c (revision 4da070ce6c015a994ec4ecf3d31ee94810ea19f1)
1b78ee15eSRuslan Bukin /*
2b78ee15eSRuslan Bukin  * CDDL HEADER START
3b78ee15eSRuslan Bukin  *
4b78ee15eSRuslan Bukin  * The contents of this file are subject to the terms of the
5b78ee15eSRuslan Bukin  * Common Development and Distribution License (the "License").
6b78ee15eSRuslan Bukin  * You may not use this file except in compliance with the License.
7b78ee15eSRuslan Bukin  *
8b78ee15eSRuslan Bukin  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9b78ee15eSRuslan Bukin  * or http://www.opensolaris.org/os/licensing.
10b78ee15eSRuslan Bukin  * See the License for the specific language governing permissions
11b78ee15eSRuslan Bukin  * and limitations under the License.
12b78ee15eSRuslan Bukin  *
13b78ee15eSRuslan Bukin  * When distributing Covered Code, include this CDDL HEADER in each
14b78ee15eSRuslan Bukin  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15b78ee15eSRuslan Bukin  * If applicable, add the following below this CDDL HEADER, with the
16b78ee15eSRuslan Bukin  * fields enclosed by brackets "[]" replaced with your own identifying
17b78ee15eSRuslan Bukin  * information: Portions Copyright [yyyy] [name of copyright owner]
18b78ee15eSRuslan Bukin  *
19b78ee15eSRuslan Bukin  * CDDL HEADER END
20b78ee15eSRuslan Bukin  *
21b78ee15eSRuslan Bukin  * Portions Copyright 2006-2008 John Birrell jb@freebsd.org
22b78ee15eSRuslan Bukin  * Portions Copyright 2013 Justin Hibbits jhibbits@freebsd.org
23b78ee15eSRuslan Bukin  * Portions Copyright 2013 Howard Su howardsu@freebsd.org
24b78ee15eSRuslan Bukin  * Portions Copyright 2015 Ruslan Bukin <br@bsdpad.com>
25b78ee15eSRuslan Bukin  */
26b78ee15eSRuslan Bukin 
27b78ee15eSRuslan Bukin /*
28b78ee15eSRuslan Bukin  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
29b78ee15eSRuslan Bukin  * Use is subject to license terms.
30b78ee15eSRuslan Bukin  */
31b78ee15eSRuslan Bukin 
32b78ee15eSRuslan Bukin #include <sys/param.h>
33b78ee15eSRuslan Bukin 
34b78ee15eSRuslan Bukin #include <sys/dtrace.h>
35b78ee15eSRuslan Bukin 
36b78ee15eSRuslan Bukin #include "fbt.h"
37b78ee15eSRuslan Bukin 
38bab7781eSChristos Margiolis #define	FBT_PATCHVAL	DTRACE_PATCHVAL
39599fb1d1SRobert Watson #define	FBT_AFRAMES	4
40b78ee15eSRuslan Bukin 
41b78ee15eSRuslan Bukin int
426c280659SMark Johnston fbt_invop(uintptr_t addr, struct trapframe *frame, uintptr_t rval)
43b78ee15eSRuslan Bukin {
44b78ee15eSRuslan Bukin 	solaris_cpu_t *cpu;
45b78ee15eSRuslan Bukin 	fbt_probe_t *fbt;
46b78ee15eSRuslan Bukin 
47b78ee15eSRuslan Bukin 	cpu = &solaris_cpu[curcpu];
48b78ee15eSRuslan Bukin 	fbt = fbt_probetab[FBT_ADDR2NDX(addr)];
49b78ee15eSRuslan Bukin 
50b78ee15eSRuslan Bukin 	for (; fbt != NULL; fbt = fbt->fbtp_hashnext) {
51e3ccf4f9SAndrew Turner 		if ((uintptr_t)fbt->fbtp_patchpoint != addr)
52e3ccf4f9SAndrew Turner 			continue;
53e3ccf4f9SAndrew Turner 
54b78ee15eSRuslan Bukin 		cpu->cpu_dtrace_caller = addr;
55b78ee15eSRuslan Bukin 
56e3ccf4f9SAndrew Turner 		if (fbt->fbtp_roffset == 0) {
57b78ee15eSRuslan Bukin 			dtrace_probe(fbt->fbtp_id, frame->tf_x[0],
58b78ee15eSRuslan Bukin 			    frame->tf_x[1], frame->tf_x[2],
59b78ee15eSRuslan Bukin 			    frame->tf_x[3], frame->tf_x[4]);
60e3ccf4f9SAndrew Turner 		} else {
61e3ccf4f9SAndrew Turner 			dtrace_probe(fbt->fbtp_id, fbt->fbtp_roffset, rval,
62e3ccf4f9SAndrew Turner 			    0, 0, 0);
63e3ccf4f9SAndrew Turner 		}
64b78ee15eSRuslan Bukin 		cpu->cpu_dtrace_caller = 0;
65b78ee15eSRuslan Bukin 		return (fbt->fbtp_savedval);
66b78ee15eSRuslan Bukin 	}
67b78ee15eSRuslan Bukin 
68b78ee15eSRuslan Bukin 	return (0);
69b78ee15eSRuslan Bukin }
70b78ee15eSRuslan Bukin 
71b78ee15eSRuslan Bukin void
72b78ee15eSRuslan Bukin fbt_patch_tracepoint(fbt_probe_t *fbt, fbt_patchval_t val)
73b78ee15eSRuslan Bukin {
74e48770deSJohn Baldwin 	void *addr;
75b78ee15eSRuslan Bukin 
76e48770deSJohn Baldwin 	if (!arm64_get_writable_addr(fbt->fbtp_patchpoint, &addr))
773d2533f5SAndrew Turner 		panic("%s: Unable to write new instruction", __func__);
783d2533f5SAndrew Turner 
793d2533f5SAndrew Turner 	*(fbt_patchval_t *)addr = val;
801e3f42b6SJohn Baldwin 	cpu_icache_sync_range(fbt->fbtp_patchpoint, 4);
81b78ee15eSRuslan Bukin }
82b78ee15eSRuslan Bukin 
83b78ee15eSRuslan Bukin int
84b78ee15eSRuslan Bukin fbt_provide_module_function(linker_file_t lf, int symindx,
85b78ee15eSRuslan Bukin     linker_symval_t *symval, void *opaque)
86b78ee15eSRuslan Bukin {
87b78ee15eSRuslan Bukin 	fbt_probe_t *fbt, *retfbt;
88b78ee15eSRuslan Bukin 	uint32_t *target, *start;
89b78ee15eSRuslan Bukin 	uint32_t *instr, *limit;
90b78ee15eSRuslan Bukin 	const char *name;
91b78ee15eSRuslan Bukin 	char *modname;
92b78ee15eSRuslan Bukin 	int offs;
93b78ee15eSRuslan Bukin 
94b78ee15eSRuslan Bukin 	modname = opaque;
95b78ee15eSRuslan Bukin 	name = symval->name;
96b78ee15eSRuslan Bukin 
97b78ee15eSRuslan Bukin 	/* Check if function is excluded from instrumentation */
98b78ee15eSRuslan Bukin 	if (fbt_excluded(name))
99b78ee15eSRuslan Bukin 		return (0);
100b78ee15eSRuslan Bukin 
10130b68ecdSRobert Watson 	/*
10230b68ecdSRobert Watson 	 * Instrumenting certain exception handling functions can lead to FBT
10330b68ecdSRobert Watson 	 * recursion, so exclude from instrumentation.
10430b68ecdSRobert Watson 	 */
10530b68ecdSRobert Watson 	 if (strcmp(name, "handle_el1h_sync") == 0 ||
10630b68ecdSRobert Watson 	    strcmp(name, "do_el1h_sync") == 0)
10730b68ecdSRobert Watson 		return (1);
10830b68ecdSRobert Watson 
109b78ee15eSRuslan Bukin 	instr = (uint32_t *)(symval->value);
110b78ee15eSRuslan Bukin 	limit = (uint32_t *)(symval->value + symval->size);
111b78ee15eSRuslan Bukin 
112b5876847SAndrew Turner 	/*
113b5876847SAndrew Turner 	 * Ignore any bti instruction at the start of the function
114b5876847SAndrew Turner 	 * we need to keep it there for any indirect branches calling
115b5876847SAndrew Turner 	 * the function on Armv8.5+
116b5876847SAndrew Turner 	 */
117b5876847SAndrew Turner 	if ((*instr & BTI_MASK) == BTI_INSTR)
118b5876847SAndrew Turner 		instr++;
119b5876847SAndrew Turner 
12028d94520SAndrew Turner 	/*
12128d94520SAndrew Turner 	 * If the first instruction is a nop it's a specially marked
12228d94520SAndrew Turner 	 * asm function. We only support a nop first as it's not a normal
12328d94520SAndrew Turner 	 * part of the function prologue.
12428d94520SAndrew Turner 	 */
12528d94520SAndrew Turner 	if (*instr == NOP_INSTR)
126b4db386fSIgor Ostapenko 		goto found;
127b4db386fSIgor Ostapenko 
128b4db386fSIgor Ostapenko 	/* Look for stp (pre-indexed) or sub operation */
129b4db386fSIgor Ostapenko 	for (; instr < limit; instr++) {
13028d94520SAndrew Turner 		/*
131980746e5SChristos Margiolis 		 * Functions start with "stp xt1, xt2, [xn, <const>]!" or
132980746e5SChristos Margiolis 		 * "sub sp, sp, <const>".
133980746e5SChristos Margiolis 		 *
134980746e5SChristos Margiolis 		 * Sometimes the compiler will have a sub instruction that is
135980746e5SChristos Margiolis 		 * not of the above type so don't stop if we see one.
13628d94520SAndrew Turner 		 */
137d0df1a2dSAndrew Turner 		if ((*instr & LDP_STP_MASK) == STP_64) {
138d0df1a2dSAndrew Turner 			/*
139980746e5SChristos Margiolis 			 * Assume any other store of this type means we are
140980746e5SChristos Margiolis 			 * past the function prologue.
141d0df1a2dSAndrew Turner 			 */
142d0df1a2dSAndrew Turner 			if (((*instr >> ADDR_SHIFT) & ADDR_MASK) == 31)
143b4db386fSIgor Ostapenko 				break;
144980746e5SChristos Margiolis 		} else if ((*instr & SUB_MASK) == SUB_INSTR &&
145c00ec4daSAndrew Turner 		    ((*instr >> SUB_RD_SHIFT) & SUB_R_MASK) == 31 &&
146980746e5SChristos Margiolis 		    ((*instr >> SUB_RN_SHIFT) & SUB_R_MASK) == 31)
147b4db386fSIgor Ostapenko 			break;
14828d94520SAndrew Turner 	}
149b4db386fSIgor Ostapenko found:
150b4db386fSIgor Ostapenko 	if (instr >= limit)
151b78ee15eSRuslan Bukin 		return (0);
152b78ee15eSRuslan Bukin 
153b78ee15eSRuslan Bukin 	fbt = malloc(sizeof (fbt_probe_t), M_FBT, M_WAITOK | M_ZERO);
154b78ee15eSRuslan Bukin 	fbt->fbtp_name = name;
155b78ee15eSRuslan Bukin 	fbt->fbtp_id = dtrace_probe_create(fbt_id, modname,
156599fb1d1SRobert Watson 	    name, FBT_ENTRY, FBT_AFRAMES, fbt);
157b78ee15eSRuslan Bukin 	fbt->fbtp_patchpoint = instr;
158b78ee15eSRuslan Bukin 	fbt->fbtp_ctl = lf;
159b78ee15eSRuslan Bukin 	fbt->fbtp_loadcnt = lf->loadcnt;
160b78ee15eSRuslan Bukin 	fbt->fbtp_savedval = *instr;
161b78ee15eSRuslan Bukin 	fbt->fbtp_patchval = FBT_PATCHVAL;
162c00ec4daSAndrew Turner 	if ((*instr & SUB_MASK) == SUB_INSTR)
163c00ec4daSAndrew Turner 		fbt->fbtp_rval = DTRACE_INVOP_SUB;
164c00ec4daSAndrew Turner 	else
165d0df1a2dSAndrew Turner 		fbt->fbtp_rval = DTRACE_INVOP_STP;
166b78ee15eSRuslan Bukin 	fbt->fbtp_symindx = symindx;
167b78ee15eSRuslan Bukin 
168b78ee15eSRuslan Bukin 	fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)];
169b78ee15eSRuslan Bukin 	fbt_probetab[FBT_ADDR2NDX(instr)] = fbt;
170b78ee15eSRuslan Bukin 
171b78ee15eSRuslan Bukin 	lf->fbt_nentries++;
172b78ee15eSRuslan Bukin 
173b78ee15eSRuslan Bukin 	retfbt = NULL;
174b78ee15eSRuslan Bukin again:
175b78ee15eSRuslan Bukin 	for (; instr < limit; instr++) {
176b78ee15eSRuslan Bukin 		if (*instr == RET_INSTR)
177b78ee15eSRuslan Bukin 			break;
178b78ee15eSRuslan Bukin 		else if ((*instr & B_MASK) == B_INSTR) {
179b78ee15eSRuslan Bukin 			offs = (*instr & B_DATA_MASK);
180*4da070ceSMark Johnston 			target = instr + offs;
181b78ee15eSRuslan Bukin 			start = (uint32_t *)symval->value;
182b78ee15eSRuslan Bukin 			if (target >= limit || target < start)
183b78ee15eSRuslan Bukin 				break;
184b78ee15eSRuslan Bukin 		}
185b78ee15eSRuslan Bukin 	}
186b78ee15eSRuslan Bukin 
187b78ee15eSRuslan Bukin 	if (instr >= limit)
188b78ee15eSRuslan Bukin 		return (0);
189b78ee15eSRuslan Bukin 
190b78ee15eSRuslan Bukin 	/*
191b78ee15eSRuslan Bukin 	 * We have a winner!
192b78ee15eSRuslan Bukin 	 */
193b78ee15eSRuslan Bukin 	fbt = malloc(sizeof (fbt_probe_t), M_FBT, M_WAITOK | M_ZERO);
194b78ee15eSRuslan Bukin 	fbt->fbtp_name = name;
195b78ee15eSRuslan Bukin 	if (retfbt == NULL) {
196b78ee15eSRuslan Bukin 		fbt->fbtp_id = dtrace_probe_create(fbt_id, modname,
197599fb1d1SRobert Watson 		    name, FBT_RETURN, FBT_AFRAMES, fbt);
198b78ee15eSRuslan Bukin 	} else {
199c208cb99SMark Johnston 		retfbt->fbtp_probenext = fbt;
200b78ee15eSRuslan Bukin 		fbt->fbtp_id = retfbt->fbtp_id;
201b78ee15eSRuslan Bukin 	}
202b78ee15eSRuslan Bukin 	retfbt = fbt;
203b78ee15eSRuslan Bukin 
204b78ee15eSRuslan Bukin 	fbt->fbtp_patchpoint = instr;
205b78ee15eSRuslan Bukin 	fbt->fbtp_ctl = lf;
206b78ee15eSRuslan Bukin 	fbt->fbtp_loadcnt = lf->loadcnt;
207b78ee15eSRuslan Bukin 	fbt->fbtp_symindx = symindx;
208b78ee15eSRuslan Bukin 	if ((*instr & B_MASK) == B_INSTR)
209b78ee15eSRuslan Bukin 		fbt->fbtp_rval = DTRACE_INVOP_B;
210b78ee15eSRuslan Bukin 	else
211b78ee15eSRuslan Bukin 		fbt->fbtp_rval = DTRACE_INVOP_RET;
212e3ccf4f9SAndrew Turner 	fbt->fbtp_roffset = (uintptr_t)instr - (uintptr_t)symval->value;
213b78ee15eSRuslan Bukin 	fbt->fbtp_savedval = *instr;
214b78ee15eSRuslan Bukin 	fbt->fbtp_patchval = FBT_PATCHVAL;
215b78ee15eSRuslan Bukin 	fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)];
216b78ee15eSRuslan Bukin 	fbt_probetab[FBT_ADDR2NDX(instr)] = fbt;
217b78ee15eSRuslan Bukin 
218b78ee15eSRuslan Bukin 	lf->fbt_nentries++;
219b78ee15eSRuslan Bukin 
220b78ee15eSRuslan Bukin 	instr++;
221b78ee15eSRuslan Bukin 	goto again;
222b78ee15eSRuslan Bukin }
223