xref: /freebsd/sys/cddl/dev/fbt/x86/fbt_isa.c (revision 1c05a6ea6b849ff95e539c31adea887c644a6a01)
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  *
23  * $FreeBSD$
24  *
25  */
26 
27 /*
28  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
29  * Use is subject to license terms.
30  */
31 
32 #include <sys/cdefs.h>
33 #include <sys/param.h>
34 
35 #include <sys/dtrace.h>
36 
37 #include "fbt.h"
38 
39 #define	FBT_PUSHL_EBP		0x55
40 #define	FBT_MOVL_ESP_EBP0_V0	0x8b
41 #define	FBT_MOVL_ESP_EBP1_V0	0xec
42 #define	FBT_MOVL_ESP_EBP0_V1	0x89
43 #define	FBT_MOVL_ESP_EBP1_V1	0xe5
44 #define	FBT_REX_RSP_RBP		0x48
45 
46 #define	FBT_POPL_EBP		0x5d
47 #define	FBT_RET			0xc3
48 #define	FBT_RET_IMM16		0xc2
49 #define	FBT_LEAVE		0xc9
50 
51 #ifdef __amd64__
52 #define	FBT_PATCHVAL		0xcc
53 #else
54 #define	FBT_PATCHVAL		0xf0
55 #endif
56 
57 #define	FBT_ENTRY	"entry"
58 #define	FBT_RETURN	"return"
59 
60 int
61 fbt_invop(uintptr_t addr, struct trapframe *frame, uintptr_t rval)
62 {
63 	solaris_cpu_t *cpu;
64 	uintptr_t *stack;
65 	uintptr_t arg0, arg1, arg2, arg3, arg4;
66 	fbt_probe_t *fbt;
67 
68 #ifdef __amd64__
69 	stack = (uintptr_t *)frame->tf_rsp;
70 #else
71 	/* Skip hardware-saved registers. */
72 	stack = (uintptr_t *)frame->tf_isp + 3;
73 #endif
74 
75 	cpu = &solaris_cpu[curcpu];
76 	fbt = fbt_probetab[FBT_ADDR2NDX(addr)];
77 	for (; fbt != NULL; fbt = fbt->fbtp_hashnext) {
78 		if ((uintptr_t)fbt->fbtp_patchpoint == addr) {
79 			if (fbt->fbtp_roffset == 0) {
80 #ifdef __amd64__
81 				/* fbt->fbtp_rval == DTRACE_INVOP_PUSHQ_RBP */
82 				DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT);
83 				cpu->cpu_dtrace_caller = stack[0];
84 				DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT |
85 				    CPU_DTRACE_BADADDR);
86 
87 				arg0 = frame->tf_rdi;
88 				arg1 = frame->tf_rsi;
89 				arg2 = frame->tf_rdx;
90 				arg3 = frame->tf_rcx;
91 				arg4 = frame->tf_r8;
92 #else
93 				int i = 0;
94 
95 				/*
96 				 * When accessing the arguments on the stack,
97 				 * we must protect against accessing beyond
98 				 * the stack.  We can safely set NOFAULT here
99 				 * -- we know that interrupts are already
100 				 * disabled.
101 				 */
102 				DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT);
103 				cpu->cpu_dtrace_caller = stack[i++];
104 				arg0 = stack[i++];
105 				arg1 = stack[i++];
106 				arg2 = stack[i++];
107 				arg3 = stack[i++];
108 				arg4 = stack[i++];
109 				DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT |
110 				    CPU_DTRACE_BADADDR);
111 #endif
112 
113 				dtrace_probe(fbt->fbtp_id, arg0, arg1,
114 				    arg2, arg3, arg4);
115 
116 				cpu->cpu_dtrace_caller = 0;
117 			} else {
118 #ifdef __amd64__
119 				/*
120 				 * On amd64, we instrument the ret, not the
121 				 * leave.  We therefore need to set the caller
122 				 * to ensure that the top frame of a stack()
123 				 * action is correct.
124 				 */
125 				DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT);
126 				cpu->cpu_dtrace_caller = stack[0];
127 				DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT |
128 				    CPU_DTRACE_BADADDR);
129 #endif
130 
131 				dtrace_probe(fbt->fbtp_id, fbt->fbtp_roffset,
132 				    rval, 0, 0, 0);
133 				cpu->cpu_dtrace_caller = 0;
134 			}
135 
136 			return (fbt->fbtp_rval);
137 		}
138 	}
139 
140 	return (0);
141 }
142 
143 void
144 fbt_patch_tracepoint(fbt_probe_t *fbt, fbt_patchval_t val)
145 {
146 
147 	*fbt->fbtp_patchpoint = val;
148 }
149 
150 int
151 fbt_provide_module_function(linker_file_t lf, int symindx,
152     linker_symval_t *symval, void *opaque)
153 {
154 	char *modname = opaque;
155 	const char *name = symval->name;
156 	fbt_probe_t *fbt, *retfbt;
157 	int j;
158 	int size;
159 	uint8_t *instr, *limit;
160 
161 	if (fbt_excluded(name))
162 		return (0);
163 
164 	/*
165 	 * trap_check() is a wrapper for DTrace's fault handler, so we don't
166 	 * want to be able to instrument it.
167 	 */
168 	if (strcmp(name, "trap_check") == 0)
169 		return (0);
170 
171 	size = symval->size;
172 
173 	instr = (uint8_t *) symval->value;
174 	limit = (uint8_t *) symval->value + symval->size;
175 
176 #ifdef __amd64__
177 	while (instr < limit) {
178 		if (*instr == FBT_PUSHL_EBP)
179 			break;
180 
181 		if ((size = dtrace_instr_size(instr)) <= 0)
182 			break;
183 
184 		instr += size;
185 	}
186 
187 	if (instr >= limit || *instr != FBT_PUSHL_EBP) {
188 		/*
189 		 * We either don't save the frame pointer in this
190 		 * function, or we ran into some disassembly
191 		 * screw-up.  Either way, we bail.
192 		 */
193 		return (0);
194 	}
195 #else
196 	if (instr[0] != FBT_PUSHL_EBP)
197 		return (0);
198 
199 	if (!(instr[1] == FBT_MOVL_ESP_EBP0_V0 &&
200 	    instr[2] == FBT_MOVL_ESP_EBP1_V0) &&
201 	    !(instr[1] == FBT_MOVL_ESP_EBP0_V1 &&
202 	    instr[2] == FBT_MOVL_ESP_EBP1_V1))
203 		return (0);
204 #endif
205 
206 	fbt = malloc(sizeof (fbt_probe_t), M_FBT, M_WAITOK | M_ZERO);
207 	fbt->fbtp_name = name;
208 	fbt->fbtp_id = dtrace_probe_create(fbt_id, modname,
209 	    name, FBT_ENTRY, 3, fbt);
210 	fbt->fbtp_patchpoint = instr;
211 	fbt->fbtp_ctl = lf;
212 	fbt->fbtp_loadcnt = lf->loadcnt;
213 	fbt->fbtp_rval = DTRACE_INVOP_PUSHL_EBP;
214 	fbt->fbtp_savedval = *instr;
215 	fbt->fbtp_patchval = FBT_PATCHVAL;
216 	fbt->fbtp_symindx = symindx;
217 
218 	fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)];
219 	fbt_probetab[FBT_ADDR2NDX(instr)] = fbt;
220 
221 	lf->fbt_nentries++;
222 
223 	retfbt = NULL;
224 again:
225 	if (instr >= limit)
226 		return (0);
227 
228 	/*
229 	 * If this disassembly fails, then we've likely walked off into
230 	 * a jump table or some other unsuitable area.  Bail out of the
231 	 * disassembly now.
232 	 */
233 	if ((size = dtrace_instr_size(instr)) <= 0)
234 		return (0);
235 
236 #ifdef __amd64__
237 	/*
238 	 * We only instrument "ret" on amd64 -- we don't yet instrument
239 	 * ret imm16, largely because the compiler doesn't seem to
240 	 * (yet) emit them in the kernel...
241 	 */
242 	if (*instr != FBT_RET) {
243 		instr += size;
244 		goto again;
245 	}
246 #else
247 	if (!(size == 1 &&
248 	    (*instr == FBT_POPL_EBP || *instr == FBT_LEAVE) &&
249 	    (*(instr + 1) == FBT_RET ||
250 	    *(instr + 1) == FBT_RET_IMM16))) {
251 		instr += size;
252 		goto again;
253 	}
254 #endif
255 
256 	/*
257 	 * We (desperately) want to avoid erroneously instrumenting a
258 	 * jump table, especially given that our markers are pretty
259 	 * short:  two bytes on x86, and just one byte on amd64.  To
260 	 * determine if we're looking at a true instruction sequence
261 	 * or an inline jump table that happens to contain the same
262 	 * byte sequences, we resort to some heuristic sleeze:  we
263 	 * treat this instruction as being contained within a pointer,
264 	 * and see if that pointer points to within the body of the
265 	 * function.  If it does, we refuse to instrument it.
266 	 */
267 	for (j = 0; j < sizeof (uintptr_t); j++) {
268 		caddr_t check = (caddr_t) instr - j;
269 		uint8_t *ptr;
270 
271 		if (check < symval->value)
272 			break;
273 
274 		if (check + sizeof (caddr_t) > (caddr_t)limit)
275 			continue;
276 
277 		ptr = *(uint8_t **)check;
278 
279 		if (ptr >= (uint8_t *) symval->value && ptr < limit) {
280 			instr += size;
281 			goto again;
282 		}
283 	}
284 
285 	/*
286 	 * We have a winner!
287 	 */
288 	fbt = malloc(sizeof (fbt_probe_t), M_FBT, M_WAITOK | M_ZERO);
289 	fbt->fbtp_name = name;
290 
291 	if (retfbt == NULL) {
292 		fbt->fbtp_id = dtrace_probe_create(fbt_id, modname,
293 		    name, FBT_RETURN, 3, fbt);
294 	} else {
295 		retfbt->fbtp_next = fbt;
296 		fbt->fbtp_id = retfbt->fbtp_id;
297 	}
298 
299 	retfbt = fbt;
300 	fbt->fbtp_patchpoint = instr;
301 	fbt->fbtp_ctl = lf;
302 	fbt->fbtp_loadcnt = lf->loadcnt;
303 	fbt->fbtp_symindx = symindx;
304 
305 #ifndef __amd64__
306 	if (*instr == FBT_POPL_EBP) {
307 		fbt->fbtp_rval = DTRACE_INVOP_POPL_EBP;
308 	} else {
309 		ASSERT(*instr == FBT_LEAVE);
310 		fbt->fbtp_rval = DTRACE_INVOP_LEAVE;
311 	}
312 	fbt->fbtp_roffset =
313 	    (uintptr_t)(instr - (uint8_t *) symval->value) + 1;
314 
315 #else
316 	ASSERT(*instr == FBT_RET);
317 	fbt->fbtp_rval = DTRACE_INVOP_RET;
318 	fbt->fbtp_roffset =
319 	    (uintptr_t)(instr - (uint8_t *) symval->value);
320 #endif
321 
322 	fbt->fbtp_savedval = *instr;
323 	fbt->fbtp_patchval = FBT_PATCHVAL;
324 	fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)];
325 	fbt_probetab[FBT_ADDR2NDX(instr)] = fbt;
326 
327 	lf->fbt_nentries++;
328 
329 	instr += size;
330 	goto again;
331 }
332