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