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