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