xref: /netbsd-src/external/cddl/osnet/dev/dtrace/i386/dtrace_isa.c (revision 87d689fb734c654d2486f87f7be32f1b53ecdbec)
1 /*	$NetBSD: dtrace_isa.c,v 1.5 2017/02/27 06:47:00 chs 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, Version 1.0 only
8  * (the "License").  You may not use this file except in compliance
9  * with the License.
10  *
11  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
12  * or http://www.opensolaris.org/os/licensing.
13  * See the License for the specific language governing permissions
14  * and limitations under the License.
15  *
16  * When distributing Covered Code, include this CDDL HEADER in each
17  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
18  * If applicable, add the following below this CDDL HEADER, with the
19  * fields enclosed by brackets "[]" replaced with your own identifying
20  * information: Portions Copyright [yyyy] [name of copyright owner]
21  *
22  * CDDL HEADER END
23  *
24  * $FreeBSD: src/sys/cddl/dev/dtrace/i386/dtrace_isa.c,v 1.1.4.1 2009/08/03 08:13:06 kensmith Exp $
25  */
26 /*
27  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
28  * Use is subject to license terms.
29  */
30 #include <sys/cdefs.h>
31 
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/kernel.h>
35 
36 #include <machine/vmparam.h>
37 #include <machine/pmap.h>
38 
39 uintptr_t kernelbase = (uintptr_t)KERNBASE;
40 
41 #define INKERNEL(va) \
42 	(((vm_offset_t)(va)) >= VM_MIN_KERNEL_ADDRESS && \
43 	 ((vm_offset_t)(va)) < VM_MAX_KERNEL_ADDRESS)
44 
45 struct i386_frame {
46 	struct i386_frame	*f_frame;
47 	int			 f_retaddr;
48 };
49 
50 typedef	unsigned long	vm_offset_t;
51 
52 uint8_t dtrace_fuword8_nocheck(void *);
53 uint16_t dtrace_fuword16_nocheck(void *);
54 uint32_t dtrace_fuword32_nocheck(void *);
55 uint64_t dtrace_fuword64_nocheck(void *);
56 
57 void
58 dtrace_getpcstack(pc_t *pcstack, int pcstack_limit, int aframes,
59     uint32_t *intrpc)
60 {
61 	int depth = 0;
62 	register_t ebp;
63 	struct i386_frame *frame;
64 	vm_offset_t callpc;
65 	pc_t caller = (pc_t) solaris_cpu[cpu_number()].cpu_dtrace_caller;
66 
67 	if (intrpc != 0)
68 		pcstack[depth++] = (pc_t) intrpc;
69 
70 	aframes++;
71 
72 	__asm __volatile("movl %%ebp,%0" : "=r" (ebp));
73 
74 	frame = (struct i386_frame *)ebp;
75 	while (depth < pcstack_limit) {
76 		if (!INKERNEL(frame))
77 			break;
78 
79 		callpc = frame->f_retaddr;
80 
81 		if (!INKERNEL(callpc))
82 			break;
83 
84 		if (aframes > 0) {
85 			aframes--;
86 			if ((aframes == 0) && (caller != 0)) {
87 				pcstack[depth++] = caller;
88 			}
89 		}
90 		else {
91 			pcstack[depth++] = callpc;
92 		}
93 
94 		if (frame->f_frame <= frame ||
95 		    (vm_offset_t)frame->f_frame >=
96 		    (vm_offset_t)ebp + KSTACK_SIZE)
97 			break;
98 		frame = frame->f_frame;
99 	}
100 
101 	for (; depth < pcstack_limit; depth++) {
102 		pcstack[depth] = 0;
103 	}
104 }
105 
106 static int
107 dtrace_getustack_common(uint64_t *pcstack, int pcstack_limit, uintptr_t pc,
108     uintptr_t sp)
109 {
110 #ifdef notyet
111 	proc_t *p = curproc;
112 	uintptr_t oldcontext = lwp->lwp_oldcontext; /* XXX signal stack. */
113 	size_t s1, s2;
114 #endif
115 	volatile uint16_t *flags =
116 	    (volatile uint16_t *)&cpu_core[cpu_number()].cpuc_dtrace_flags;
117 	int ret = 0;
118 
119 	ASSERT(pcstack == NULL || pcstack_limit > 0);
120 
121 #ifdef notyet /* XXX signal stack. */
122 	if (p->p_model == DATAMODEL_NATIVE) {
123 		s1 = sizeof (struct frame) + 2 * sizeof (long);
124 		s2 = s1 + sizeof (siginfo_t);
125 	} else {
126 		s1 = sizeof (struct frame32) + 3 * sizeof (int);
127 		s2 = s1 + sizeof (siginfo32_t);
128 	}
129 #endif
130 
131 	while (pc != 0) {
132 		ret++;
133 		if (pcstack != NULL) {
134 			*pcstack++ = (uint64_t)pc;
135 			pcstack_limit--;
136 			if (pcstack_limit <= 0)
137 				break;
138 		}
139 
140 		if (sp == 0)
141 			break;
142 
143 #ifdef notyet /* XXX signal stack. */
144 		if (oldcontext == sp + s1 || oldcontext == sp + s2) {
145 			if (p->p_model == DATAMODEL_NATIVE) {
146 				ucontext_t *ucp = (ucontext_t *)oldcontext;
147 				greg_t *gregs = ucp->uc_mcontext.gregs;
148 
149 				sp = dtrace_fulword(&gregs[REG_FP]);
150 				pc = dtrace_fulword(&gregs[REG_PC]);
151 
152 				oldcontext = dtrace_fulword(&ucp->uc_link);
153 			} else {
154 				ucontext32_t *ucp = (ucontext32_t *)oldcontext;
155 				greg32_t *gregs = ucp->uc_mcontext.gregs;
156 
157 				sp = dtrace_fuword32(&gregs[EBP]);
158 				pc = dtrace_fuword32(&gregs[EIP]);
159 
160 				oldcontext = dtrace_fuword32(&ucp->uc_link);
161 			}
162 		} else {
163 			if (p->p_model == DATAMODEL_NATIVE) {
164 				struct frame *fr = (struct frame *)sp;
165 
166 				pc = dtrace_fulword(&fr->fr_savpc);
167 				sp = dtrace_fulword(&fr->fr_savfp);
168 			} else {
169 				struct frame32 *fr = (struct frame32 *)sp;
170 
171 				pc = dtrace_fuword32(&fr->fr_savpc);
172 				sp = dtrace_fuword32(&fr->fr_savfp);
173 			}
174 		}
175 #else
176 		pc = dtrace_fuword32((void *)(sp +
177 			offsetof(struct i386_frame, f_retaddr)));
178 		sp = dtrace_fuword32((void *)sp);
179 #endif /* ! notyet */
180 
181 		/*
182 		 * This is totally bogus:  if we faulted, we're going to clear
183 		 * the fault and break.  This is to deal with the apparently
184 		 * broken Java stacks on x86.
185 		 */
186 		if (*flags & CPU_DTRACE_FAULT) {
187 			*flags &= ~CPU_DTRACE_FAULT;
188 			break;
189 		}
190 	}
191 
192 	return (ret);
193 }
194 
195 void
196 dtrace_getupcstack(uint64_t *pcstack, int pcstack_limit)
197 {
198 	proc_t *p = curproc;
199 	struct trapframe *tf;
200 	uintptr_t pc, sp, fp;
201 	volatile uint16_t *flags =
202 	    (volatile uint16_t *)&cpu_core[cpu_number()].cpuc_dtrace_flags;
203 	int n;
204 
205 	if (*flags & CPU_DTRACE_FAULT)
206 		return;
207 
208 	if (pcstack_limit <= 0)
209 		return;
210 
211 	/*
212 	 * If there's no user context we still need to zero the stack.
213 	 */
214 	if (p == NULL || (tf = curlwp->l_md.md_regs) == NULL)
215 		goto zero;
216 
217 	*pcstack++ = (uint64_t)p->p_pid;
218 	pcstack_limit--;
219 
220 	if (pcstack_limit <= 0)
221 		return;
222 
223 	pc = tf->tf_eip;
224 	fp = tf->tf_ebp;
225 	sp = tf->tf_esp;
226 
227 	if (DTRACE_CPUFLAG_ISSET(CPU_DTRACE_ENTRY)) {
228 		/*
229 		 * In an entry probe.  The frame pointer has not yet been
230 		 * pushed (that happens in the function prologue).  The
231 		 * best approach is to add the current pc as a missing top
232 		 * of stack and back the pc up to the caller, which is stored
233 		 * at the current stack pointer address since the call
234 		 * instruction puts it there right before the branch.
235 		 */
236 
237 		*pcstack++ = (uint64_t)pc;
238 		pcstack_limit--;
239 		if (pcstack_limit <= 0)
240 			return;
241 
242 		pc = dtrace_fuword32((void *) sp);
243 	}
244 
245 	n = dtrace_getustack_common(pcstack, pcstack_limit, pc, fp);
246 	ASSERT(n >= 0);
247 	ASSERT(n <= pcstack_limit);
248 
249 	pcstack += n;
250 	pcstack_limit -= n;
251 
252 zero:
253 	while (pcstack_limit-- > 0)
254 		*pcstack++ = 0;
255 }
256 
257 int
258 dtrace_getustackdepth(void)
259 {
260 	proc_t *p = curproc;
261 	struct trapframe *tf;
262 	uintptr_t pc, fp, sp;
263 	int n = 0;
264 
265 	if (p == NULL || (tf = curlwp->l_md.md_regs) == NULL)
266 		return (0);
267 
268 	if (DTRACE_CPUFLAG_ISSET(CPU_DTRACE_FAULT))
269 		return (-1);
270 
271 	pc = tf->tf_eip;
272 	fp = tf->tf_ebp;
273 	sp = tf->tf_esp;
274 
275 	if (DTRACE_CPUFLAG_ISSET(CPU_DTRACE_ENTRY)) {
276 		/*
277 		 * In an entry probe.  The frame pointer has not yet been
278 		 * pushed (that happens in the function prologue).  The
279 		 * best approach is to add the current pc as a missing top
280 		 * of stack and back the pc up to the caller, which is stored
281 		 * at the current stack pointer address since the call
282 		 * instruction puts it there right before the branch.
283 		 */
284 
285 		pc = dtrace_fuword32((void *) sp);
286 		n++;
287 	}
288 
289 	n += dtrace_getustack_common(NULL, 0, pc, fp);
290 
291 	return (n);
292 }
293 
294 void
295 dtrace_getufpstack(uint64_t *pcstack, uint64_t *fpstack, int pcstack_limit)
296 {
297 	proc_t *p = curproc;
298 	struct trapframe *tf;
299 	uintptr_t pc, sp, fp;
300 	volatile uint16_t *flags =
301 	    (volatile uint16_t *)&cpu_core[cpu_number()].cpuc_dtrace_flags;
302 #ifdef notyet /* XXX signal stack */
303 	uintptr_t oldcontext;
304 	size_t s1, s2;
305 #endif
306 
307 	if (*flags & CPU_DTRACE_FAULT)
308 		return;
309 
310 	if (pcstack_limit <= 0)
311 		return;
312 
313 	/*
314 	 * If there's no user context we still need to zero the stack.
315 	 */
316 	if (p == NULL || (tf = curlwp->l_md.md_regs) == NULL)
317 		goto zero;
318 
319 	*pcstack++ = (uint64_t)p->p_pid;
320 	pcstack_limit--;
321 
322 	if (pcstack_limit <= 0)
323 		return;
324 
325 	pc = tf->tf_eip;
326 	fp = tf->tf_ebp;
327 	sp = tf->tf_esp;
328 
329 #ifdef notyet /* XXX signal stack */
330 	oldcontext = lwp->lwp_oldcontext;
331 
332 	if (p->p_model == DATAMODEL_NATIVE) {
333 		s1 = sizeof (struct frame) + 2 * sizeof (long);
334 		s2 = s1 + sizeof (siginfo_t);
335 	} else {
336 		s1 = sizeof (struct frame32) + 3 * sizeof (int);
337 		s2 = s1 + sizeof (siginfo32_t);
338 	}
339 #endif
340 
341 	if (DTRACE_CPUFLAG_ISSET(CPU_DTRACE_ENTRY)) {
342 		*pcstack++ = (uint64_t)pc;
343 		*fpstack++ = 0;
344 		pcstack_limit--;
345 		if (pcstack_limit <= 0)
346 			return;
347 
348 		pc = dtrace_fuword32((void *)sp);
349 	}
350 
351 	while (pc != 0) {
352 		*pcstack++ = (uint64_t)pc;
353 		*fpstack++ = fp;
354 		pcstack_limit--;
355 		if (pcstack_limit <= 0)
356 			break;
357 
358 		if (fp == 0)
359 			break;
360 
361 #ifdef notyet /* XXX signal stack */
362 		if (oldcontext == sp + s1 || oldcontext == sp + s2) {
363 			if (p->p_model == DATAMODEL_NATIVE) {
364 				ucontext_t *ucp = (ucontext_t *)oldcontext;
365 				greg_t *gregs = ucp->uc_mcontext.gregs;
366 
367 				sp = dtrace_fulword(&gregs[REG_FP]);
368 				pc = dtrace_fulword(&gregs[REG_PC]);
369 
370 				oldcontext = dtrace_fulword(&ucp->uc_link);
371 			} else {
372 				ucontext_t *ucp = (ucontext_t *)oldcontext;
373 				greg_t *gregs = ucp->uc_mcontext.gregs;
374 
375 				sp = dtrace_fuword32(&gregs[EBP]);
376 				pc = dtrace_fuword32(&gregs[EIP]);
377 
378 				oldcontext = dtrace_fuword32(&ucp->uc_link);
379 			}
380 		} else
381 #endif /* XXX */
382 		{
383 			pc = dtrace_fuword32((void *)(fp +
384 				offsetof(struct i386_frame, f_retaddr)));
385 			fp = dtrace_fuword32((void *)fp);
386 		}
387 
388 		/*
389 		 * This is totally bogus:  if we faulted, we're going to clear
390 		 * the fault and break.  This is to deal with the apparently
391 		 * broken Java stacks on x86.
392 		 */
393 		if (*flags & CPU_DTRACE_FAULT) {
394 			*flags &= ~CPU_DTRACE_FAULT;
395 			break;
396 		}
397 	}
398 
399 zero:
400 	while (pcstack_limit-- > 0)
401 		*pcstack++ = 0;
402 }
403 
404 uint64_t
405 dtrace_getarg(int arg, int aframes)
406 {
407 	struct trapframe *frame;
408 	struct i386_frame *fp = (struct i386_frame *)dtrace_getfp();
409 	uintptr_t *stack, val;
410 	int i;
411 
412 	for (i = 1; i <= aframes; i++) {
413 		fp = fp->f_frame;
414 
415 		if (P2ROUNDUP(fp->f_retaddr, 16) ==
416 		    (long)dtrace_invop_callsite) {
417 			/*
418 			 * If we pass through the invalid op handler, we will
419 			 * use the trap frame pointer that it pushed on the
420 			 * stack as the second argument to dtrace_invop() as
421 			 * the pointer to the stack.
422 			 */
423 			frame = (struct trapframe *)(((uintptr_t **)&fp[1])[1]);
424 
425 			/*
426 			 * Skip the three hardware-saved registers and the
427 			 * return address.
428 			 */
429 			stack = (uintptr_t *)&frame->tf_esp + 1;
430 			goto load;
431 		}
432 	}
433 
434 	/*
435 	 * We know that we did not come through a trap to get into
436 	 * dtrace_probe() -- the provider simply called dtrace_probe()
437 	 * directly.  As this is the case, we need to shift the argument
438 	 * that we're looking for:  the probe ID is the first argument to
439 	 * dtrace_probe(), so the argument n will actually be found where
440 	 * one would expect to find argument (n + 1).
441 	 */
442 	arg++;
443 
444 	stack = (uintptr_t *)fp + 2;
445 
446 load:
447 	DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT);
448 	val = stack[arg];
449 	DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT);
450 
451 	return (val);
452 }
453 
454 int
455 dtrace_getstackdepth(int aframes)
456 {
457 	int depth = 0;
458 	struct i386_frame *frame;
459 	vm_offset_t ebp;
460 
461 	aframes++;
462 	ebp = dtrace_getfp();
463 	frame = (struct i386_frame *)ebp;
464 	depth++;
465 	for(;;) {
466 		if (!INKERNEL((long) frame))
467 			break;
468 		if (!INKERNEL((long) frame->f_frame))
469 			break;
470 		depth++;
471 		if (frame->f_frame <= frame ||
472 		    (vm_offset_t)frame->f_frame >=
473 		    (vm_offset_t)ebp + KSTACK_SIZE)
474 			break;
475 		frame = frame->f_frame;
476 	}
477 	if (depth < aframes)
478 		return 0;
479 	else
480 		return depth - aframes;
481 }
482 
483 #ifdef notyet
484 ulong_t
485 dtrace_getreg(struct regs *rp, uint_t reg)
486 {
487 #if defined(__amd64)
488 	int regmap[] = {
489 		REG_GS,		/* GS */
490 		REG_FS,		/* FS */
491 		REG_ES,		/* ES */
492 		REG_DS,		/* DS */
493 		REG_RDI,	/* EDI */
494 		REG_RSI,	/* ESI */
495 		REG_RBP,	/* EBP */
496 		REG_RSP,	/* ESP */
497 		REG_RBX,	/* EBX */
498 		REG_RDX,	/* EDX */
499 		REG_RCX,	/* ECX */
500 		REG_RAX,	/* EAX */
501 		REG_TRAPNO,	/* TRAPNO */
502 		REG_ERR,	/* ERR */
503 		REG_RIP,	/* EIP */
504 		REG_CS,		/* CS */
505 		REG_RFL,	/* EFL */
506 		REG_RSP,	/* UESP */
507 		REG_SS		/* SS */
508 	};
509 
510 	if (reg <= SS) {
511 		if (reg >= sizeof (regmap) / sizeof (int)) {
512 			DTRACE_CPUFLAG_SET(CPU_DTRACE_ILLOP);
513 			return (0);
514 		}
515 
516 		reg = regmap[reg];
517 	} else {
518 		reg -= SS + 1;
519 	}
520 
521 	switch (reg) {
522 	case REG_RDI:
523 		return (rp->r_rdi);
524 	case REG_RSI:
525 		return (rp->r_rsi);
526 	case REG_RDX:
527 		return (rp->r_rdx);
528 	case REG_RCX:
529 		return (rp->r_rcx);
530 	case REG_R8:
531 		return (rp->r_r8);
532 	case REG_R9:
533 		return (rp->r_r9);
534 	case REG_RAX:
535 		return (rp->r_rax);
536 	case REG_RBX:
537 		return (rp->r_rbx);
538 	case REG_RBP:
539 		return (rp->r_rbp);
540 	case REG_R10:
541 		return (rp->r_r10);
542 	case REG_R11:
543 		return (rp->r_r11);
544 	case REG_R12:
545 		return (rp->r_r12);
546 	case REG_R13:
547 		return (rp->r_r13);
548 	case REG_R14:
549 		return (rp->r_r14);
550 	case REG_R15:
551 		return (rp->r_r15);
552 	case REG_DS:
553 		return (rp->r_ds);
554 	case REG_ES:
555 		return (rp->r_es);
556 	case REG_FS:
557 		return (rp->r_fs);
558 	case REG_GS:
559 		return (rp->r_gs);
560 	case REG_TRAPNO:
561 		return (rp->r_trapno);
562 	case REG_ERR:
563 		return (rp->r_err);
564 	case REG_RIP:
565 		return (rp->r_rip);
566 	case REG_CS:
567 		return (rp->r_cs);
568 	case REG_SS:
569 		return (rp->r_ss);
570 	case REG_RFL:
571 		return (rp->r_rfl);
572 	case REG_RSP:
573 		return (rp->r_rsp);
574 	default:
575 		DTRACE_CPUFLAG_SET(CPU_DTRACE_ILLOP);
576 		return (0);
577 	}
578 
579 #else
580 	if (reg > SS) {
581 		DTRACE_CPUFLAG_SET(CPU_DTRACE_ILLOP);
582 		return (0);
583 	}
584 
585 	return ((&rp->r_gs)[reg]);
586 #endif
587 }
588 #endif
589 
590 static int
591 dtrace_copycheck(uintptr_t uaddr, uintptr_t kaddr, size_t size)
592 {
593 	ASSERT(kaddr >= kernelbase && kaddr + size >= kaddr);
594 
595 	if (uaddr + size >= kernelbase || uaddr + size < uaddr) {
596 		DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
597 		cpu_core[cpu_number()].cpuc_dtrace_illval = uaddr;
598 		return (0);
599 	}
600 
601 	return (1);
602 }
603 
604 void
605 dtrace_copyin(uintptr_t uaddr, uintptr_t kaddr, size_t size,
606     volatile uint16_t *flags)
607 {
608 	if (dtrace_copycheck(uaddr, kaddr, size))
609 		dtrace_copy(uaddr, kaddr, size);
610 }
611 
612 void
613 dtrace_copyout(uintptr_t kaddr, uintptr_t uaddr, size_t size,
614     volatile uint16_t *flags)
615 {
616 	if (dtrace_copycheck(uaddr, kaddr, size))
617 		dtrace_copy(kaddr, uaddr, size);
618 }
619 
620 void
621 dtrace_copyinstr(uintptr_t uaddr, uintptr_t kaddr, size_t size,
622     volatile uint16_t *flags)
623 {
624 	if (dtrace_copycheck(uaddr, kaddr, size))
625 		dtrace_copystr(uaddr, kaddr, size, flags);
626 }
627 
628 void
629 dtrace_copyoutstr(uintptr_t kaddr, uintptr_t uaddr, size_t size,
630     volatile uint16_t *flags)
631 {
632 	if (dtrace_copycheck(uaddr, kaddr, size))
633 		dtrace_copystr(kaddr, uaddr, size, flags);
634 }
635 
636 uint8_t
637 dtrace_fuword8(void *uaddr)
638 {
639 	if ((uintptr_t)uaddr >= kernelbase) {
640 		DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
641 		cpu_core[cpu_number()].cpuc_dtrace_illval = (uintptr_t)uaddr;
642 		return (0);
643 	}
644 	return (dtrace_fuword8_nocheck(uaddr));
645 }
646 
647 uint16_t
648 dtrace_fuword16(void *uaddr)
649 {
650 	if ((uintptr_t)uaddr >= kernelbase) {
651 		DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
652 		cpu_core[cpu_number()].cpuc_dtrace_illval = (uintptr_t)uaddr;
653 		return (0);
654 	}
655 	return (dtrace_fuword16_nocheck(uaddr));
656 }
657 
658 uint32_t
659 dtrace_fuword32(void *uaddr)
660 {
661 	if ((uintptr_t)uaddr >= kernelbase) {
662 		DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
663 		cpu_core[cpu_number()].cpuc_dtrace_illval = (uintptr_t)uaddr;
664 		return (0);
665 	}
666 	return (dtrace_fuword32_nocheck(uaddr));
667 }
668 
669 uint64_t
670 dtrace_fuword64(void *uaddr)
671 {
672 	if ((uintptr_t)uaddr >= kernelbase) {
673 		DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
674 		cpu_core[cpu_number()].cpuc_dtrace_illval = (uintptr_t)uaddr;
675 		return (0);
676 	}
677 	return (dtrace_fuword64_nocheck(uaddr));
678 }
679