xref: /netbsd-src/external/cddl/osnet/dev/fbt/x86/fbt_isa.c (revision b5c47949a45ac972130c38cf13dfd8afb1f09285)
1 /*	$NetBSD: fbt_isa.c,v 1.3 2020/05/02 11:37:17 maxv 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  *
25  * $FreeBSD: head/sys/cddl/dev/fbt/x86/fbt_isa.c 309785 2016-12-10 03:11:05Z markj $
26  *
27  */
28 
29 /*
30  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
31  * Use is subject to license terms.
32  */
33 
34 #include <sys/cdefs.h>
35 #include <sys/proc.h>
36 #include <sys/param.h>
37 #include <sys/cpu.h>
38 #include <sys/module.h>
39 #include <sys/kmem.h>
40 
41 #include <sys/dtrace.h>
42 
43 #if 1
44 #include <machine/cpufunc.h>
45 #include <machine/specialreg.h>
46 #if 0
47 #include <x86/cpuvar.h>
48 #endif
49 #include <x86/cputypes.h>
50 #endif
51 
52 #include "fbt.h"
53 
54 #define	FBT_PUSHL_EBP		0x55
55 #define	FBT_MOVL_ESP_EBP0_V0	0x8b
56 #define	FBT_MOVL_ESP_EBP1_V0	0xec
57 #define	FBT_MOVL_ESP_EBP0_V1	0x89
58 #define	FBT_MOVL_ESP_EBP1_V1	0xe5
59 #define	FBT_REX_RSP_RBP		0x48
60 
61 #define	FBT_POPL_EBP		0x5d
62 #define	FBT_RET			0xc3
63 #define	FBT_RET_IMM16		0xc2
64 #define	FBT_LEAVE		0xc9
65 
66 #ifdef __amd64__
67 #define	FBT_PATCHVAL		0xcc
68 #else
69 #define	FBT_PATCHVAL		0xf0
70 #endif
71 
72 #define	FBT_ENTRY	"entry"
73 #define	FBT_RETURN	"return"
74 
75 int
76 fbt_invop(uintptr_t addr, struct trapframe *frame, uintptr_t rval)
77 {
78 	solaris_cpu_t *cpu;
79 	uintptr_t *stack;
80 	uintptr_t arg0, arg1, arg2, arg3, arg4;
81 	fbt_probe_t *fbt;
82 
83 #ifdef __amd64__
84 	stack = (uintptr_t *)frame->tf_rsp;
85 #else
86 	/* Skip hardware-saved registers. */
87 #ifdef __NetBSD__
88 	stack = (uintptr_t *)&frame->tf_esp;
89 #else
90 	stack = (uintptr_t *)frame->tf_isp + 3;
91 #endif
92 #endif
93 
94 	cpu = &solaris_cpu[cpu_number()];
95 	fbt = fbt_probetab[FBT_ADDR2NDX(addr)];
96 	for (; fbt != NULL; fbt = fbt->fbtp_hashnext) {
97 		if ((uintptr_t)fbt->fbtp_patchpoint == addr) {
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 			return (fbt->fbtp_rval);
156 		}
157 	}
158 
159 	return (0);
160 }
161 
162 
163 
164 
165 #ifdef __FreeBSD__
166 void
167 fbt_patch_tracepoint(fbt_probe_t *fbt, fbt_patchval_t val)
168 {
169 
170 	*fbt->fbtp_patchpoint = val;
171 }
172 #endif
173 
174 #ifdef __NetBSD__
175 /*
176  * XXX XXX XXX This is absolutely unsafe, the mere existence of this code is a
177  * problem, because this function is too easily ROP-able. But this gets
178  * compiled as a module and never in the kernel, so we are fine "by default".
179  * XXX Add a #warning if it gets compiled in the kernel?
180  */
181 void
182 fbt_patch_tracepoint(fbt_probe_t *fbt, fbt_patchval_t val)
183 {
184 	u_long psl, cr0;
185 
186 	/* Disable interrupts. */
187 	psl = x86_read_psl();
188 	x86_disable_intr();
189 
190 	/* Disable write protection in supervisor mode. */
191 	cr0 = rcr0();
192 	lcr0(cr0 & ~CR0_WP);
193 
194 	/* XXX XXX XXX Shouldn't rely on caller-provided dst! */
195 	/* XXX XXX XXX Shouldn't rely on caller-provided val! */
196 	for (; fbt != NULL; fbt = fbt->fbtp_next) {
197 		*fbt->fbtp_patchpoint = val;
198 	}
199 
200 	/* Write back and invalidate cache, flush pipelines. */
201 	wbinvd();
202 	x86_flush();
203 
204 	/* Re-enable write protection. */
205 	lcr0(cr0);
206 
207 	/* Restore the PSL, potentially re-enabling interrupts. */
208 	x86_write_psl(psl);
209 }
210 #endif
211 
212 
213 #ifdef __FreeBSD__
214 int
215 fbt_provide_module_function(linker_file_t lf, int symindx,
216     linker_symval_t *symval, void *opaque)
217 #endif
218 #ifdef __NetBSD__
219 int
220 fbt_provide_module_cb(const char *name, int symindx, void *value,
221     uint32_t symsize, int type, void *opaque)
222 #endif
223 {
224 	fbt_probe_t *fbt, *retfbt;
225 	u_int8_t *instr, *limit;
226 	int j;
227 	int size;
228 
229 #ifdef __FreeBSD_
230 	char *modname = opaque;
231 	const char *name = symval->name;
232 	size_t symsize = symval->size;
233 	void *value = symval->value;
234 
235 	/*
236 	 * trap_check() is a wrapper for DTrace's fault handler, so we don't
237 	 * want to be able to instrument it.
238 	 */
239 	if (strcmp(name, "trap_check") == 0)
240 		return (0);
241 #endif
242 #ifdef __NetBSD__
243 	struct fbt_ksyms_arg *fka = opaque;
244 	modctl_t *mod = fka->fka_mod;
245 	const char *modname = module_name(mod);
246 
247 	/* got a function? */
248 	if (ELF_ST_TYPE(type) != STT_FUNC)
249 		return 0;
250 
251 	/*
252 	 * Exclude some more symbols which can be called from probe context.
253 	 */
254 	if (strcmp(name, "x86_curcpu") == 0 ||
255 	    strcmp(name, "x86_curlwp") == 0) {
256 		return 0;
257 	}
258 #endif
259 
260 	if (fbt_excluded(name))
261 		return (0);
262 
263 	instr = (u_int8_t *) value;
264 	limit = (u_int8_t *) value + symsize;
265 
266 #ifdef __amd64__
267 	while (instr < limit) {
268 		if (*instr == FBT_PUSHL_EBP)
269 			break;
270 
271 		if ((size = dtrace_instr_size(instr)) <= 0)
272 			break;
273 
274 		instr += size;
275 	}
276 
277 	if (instr >= limit || *instr != FBT_PUSHL_EBP) {
278 		/*
279 		 * We either don't save the frame pointer in this
280 		 * function, or we ran into some disassembly
281 		 * screw-up.  Either way, we bail.
282 		 */
283 		return (0);
284 	}
285 #else
286 	if (instr[0] != FBT_PUSHL_EBP)
287 		return (0);
288 
289 	if (!(instr[1] == FBT_MOVL_ESP_EBP0_V0 &&
290 	    instr[2] == FBT_MOVL_ESP_EBP1_V0) &&
291 	    !(instr[1] == FBT_MOVL_ESP_EBP0_V1 &&
292 	    instr[2] == FBT_MOVL_ESP_EBP1_V1))
293 		return (0);
294 #endif
295 
296 	fbt = kmem_zalloc(sizeof (*fbt), KM_SLEEP);
297 	fbt->fbtp_name = name;
298 	fbt->fbtp_id = dtrace_probe_create(fbt_id, modname,
299 	    name, FBT_ENTRY, 3, fbt);
300 	fbt->fbtp_patchpoint = instr;
301 #ifdef __FreeBSD__
302 	fbt->fbtp_ctl = lf;
303 	fbt->fbtp_loadcnt = lf->loadcnt;
304 #endif
305 #ifdef __NetBSD__
306 	fbt->fbtp_ctl = mod;
307 #endif
308 	fbt->fbtp_rval = DTRACE_INVOP_PUSHL_EBP;
309 	fbt->fbtp_savedval = *instr;
310 	fbt->fbtp_patchval = FBT_PATCHVAL;
311 	fbt->fbtp_symindx = symindx;
312 
313 	fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)];
314 	fbt_probetab[FBT_ADDR2NDX(instr)] = fbt;
315 #ifdef __FreeBSD__
316 	lf->fbt_nentries++;
317 #endif
318 
319 	retfbt = NULL;
320 again:
321 	if (instr >= limit)
322 		return (0);
323 
324 	/*
325 	 * If this disassembly fails, then we've likely walked off into
326 	 * a jump table or some other unsuitable area.  Bail out of the
327 	 * disassembly now.
328 	 */
329 	if ((size = dtrace_instr_size(instr)) <= 0)
330 		return (0);
331 
332 #ifdef __amd64__
333 	/*
334 	 * We only instrument "ret" on amd64 -- we don't yet instrument
335 	 * ret imm16, largely because the compiler doesn't seem to
336 	 * (yet) emit them in the kernel...
337 	 */
338 	if (*instr != FBT_RET) {
339 		instr += size;
340 		goto again;
341 	}
342 #else
343 	if (!(size == 1 &&
344 	    (*instr == FBT_POPL_EBP || *instr == FBT_LEAVE) &&
345 	    (*(instr + 1) == FBT_RET ||
346 	    *(instr + 1) == FBT_RET_IMM16))) {
347 		instr += size;
348 		goto again;
349 	}
350 #endif
351 
352 	/*
353 	 * We (desperately) want to avoid erroneously instrumenting a
354 	 * jump table, especially given that our markers are pretty
355 	 * short:  two bytes on x86, and just one byte on amd64.  To
356 	 * determine if we're looking at a true instruction sequence
357 	 * or an inline jump table that happens to contain the same
358 	 * byte sequences, we resort to some heuristic sleeze:  we
359 	 * treat this instruction as being contained within a pointer,
360 	 * and see if that pointer points to within the body of the
361 	 * function.  If it does, we refuse to instrument it.
362 	 */
363 	for (j = 0; j < sizeof (uintptr_t); j++) {
364 		caddr_t check = (caddr_t) instr - j;
365 		uint8_t *ptr;
366 
367 		if (check < (caddr_t)value)
368 			break;
369 
370 		if (check + sizeof (caddr_t) > (caddr_t)limit)
371 			continue;
372 
373 		ptr = *(uint8_t **)check;
374 
375 		if (ptr >= (uint8_t *) value && ptr < limit) {
376 			instr += size;
377 			goto again;
378 		}
379 	}
380 
381 	/*
382 	 * We have a winner!
383 	 */
384 	fbt = kmem_zalloc(sizeof (*fbt), KM_SLEEP);
385 	fbt->fbtp_name = name;
386 
387 	if (retfbt == NULL) {
388 		fbt->fbtp_id = dtrace_probe_create(fbt_id, modname,
389 		    name, FBT_RETURN, 3, fbt);
390 	} else {
391 		retfbt->fbtp_next = fbt;
392 		fbt->fbtp_id = retfbt->fbtp_id;
393 	}
394 
395 	retfbt = fbt;
396 	fbt->fbtp_patchpoint = instr;
397 #ifdef __FreeBSD__
398 	fbt->fbtp_ctl = lf;
399 	fbt->fbtp_loadcnt = lf->loadcnt;
400 #endif
401 #ifdef __NetBSD__
402 	fbt->fbtp_ctl = mod;
403 #endif
404 	fbt->fbtp_symindx = symindx;
405 
406 #ifndef __amd64__
407 	if (*instr == FBT_POPL_EBP) {
408 		fbt->fbtp_rval = DTRACE_INVOP_POPL_EBP;
409 	} else {
410 		ASSERT(*instr == FBT_LEAVE);
411 		fbt->fbtp_rval = DTRACE_INVOP_LEAVE;
412 	}
413 	fbt->fbtp_roffset =
414 	    (uintptr_t)(instr - (uint8_t *) value) + 1;
415 
416 #else
417 	ASSERT(*instr == FBT_RET);
418 	fbt->fbtp_rval = DTRACE_INVOP_RET;
419 	fbt->fbtp_roffset =
420 		(uintptr_t)(instr - (uint8_t *) value);
421 #endif
422 
423 	fbt->fbtp_savedval = *instr;
424 	fbt->fbtp_patchval = FBT_PATCHVAL;
425 	fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)];
426 	fbt_probetab[FBT_ADDR2NDX(instr)] = fbt;
427 
428 #ifdef __FreeBSD__
429 	lf->fbt_nentries++;
430 #endif
431 
432 	instr += size;
433 	goto again;
434 }
435