xref: /freebsd/sys/cddl/dev/dtrace/powerpc/dtrace_isa.c (revision cddbc3b40812213ff00041f79174cac0be360a2a)
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  * Portions Copyright 2012,2013 Justin Hibbits <jhibbits@freebsd.org>
23  *
24  * $FreeBSD$
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 #include <sys/stack.h>
36 #include <sys/sysent.h>
37 #include <sys/pcpu.h>
38 
39 #include <machine/frame.h>
40 #include <machine/md_var.h>
41 #include <machine/psl.h>
42 #include <machine/reg.h>
43 #include <machine/stack.h>
44 
45 #include <vm/vm.h>
46 #include <vm/vm_param.h>
47 #include <vm/pmap.h>
48 
49 #include "regset.h"
50 
51 /* Offset to the LR Save word (ppc32) */
52 #define RETURN_OFFSET	4
53 /* Offset to LR Save word (ppc64).  CR Save area sits between back chain and LR */
54 #define RETURN_OFFSET64	16
55 
56 #ifdef __powerpc64__
57 #define OFFSET 4 /* Account for the TOC reload slot */
58 #define	FRAME_OFFSET	48
59 #else
60 #define OFFSET 0
61 #define	FRAME_OFFSET	8
62 #endif
63 
64 #define INKERNEL(x)	((x) <= VM_MAX_KERNEL_ADDRESS && \
65 		(x) >= VM_MIN_KERNEL_ADDRESS)
66 
67 static __inline int
68 dtrace_sp_inkernel(uintptr_t sp)
69 {
70 	struct trapframe *frame;
71 	vm_offset_t callpc;
72 
73 #ifdef __powerpc64__
74 	callpc = *(vm_offset_t *)(sp + RETURN_OFFSET64);
75 #else
76 	callpc = *(vm_offset_t *)(sp + RETURN_OFFSET);
77 #endif
78 	if ((callpc & 3) || (callpc < 0x100))
79 		return (0);
80 
81 	/*
82 	 * trapexit() and asttrapexit() are sentinels
83 	 * for kernel stack tracing.
84 	 */
85 	if (callpc + OFFSET == (vm_offset_t) &trapexit ||
86 	    callpc + OFFSET == (vm_offset_t) &asttrapexit) {
87 		if (sp == 0)
88 			return (0);
89 		frame = (struct trapframe *)(sp + FRAME_OFFSET);
90 
91 		return ((frame->srr1 & PSL_PR) == 0);
92 	}
93 
94 	return (1);
95 }
96 
97 static __inline void
98 dtrace_next_sp_pc(uintptr_t sp, uintptr_t *nsp, uintptr_t *pc)
99 {
100 	vm_offset_t callpc;
101 	struct trapframe *frame;
102 
103 #ifdef __powerpc64__
104 	callpc = *(vm_offset_t *)(sp + RETURN_OFFSET64);
105 #else
106 	callpc = *(vm_offset_t *)(sp + RETURN_OFFSET);
107 #endif
108 
109 	/*
110 	 * trapexit() and asttrapexit() are sentinels
111 	 * for kernel stack tracing.
112 	 */
113 	if ((callpc + OFFSET == (vm_offset_t) &trapexit ||
114 	    callpc + OFFSET == (vm_offset_t) &asttrapexit)) {
115 		/* Access the trap frame */
116 		frame = (struct trapframe *)(sp + FRAME_OFFSET);
117 
118 		if (nsp != NULL)
119 			*nsp = frame->fixreg[1];
120 		if (pc != NULL)
121 			*pc = frame->srr0;
122 	}
123 
124 	if (nsp != NULL)
125 		*nsp = *(uintptr_t *)sp;
126 	if (pc != NULL)
127 		*pc = callpc;
128 }
129 
130 greg_t
131 dtrace_getfp(void)
132 {
133 	return (greg_t)__builtin_frame_address(0);
134 }
135 
136 void
137 dtrace_getpcstack(pc_t *pcstack, int pcstack_limit, int aframes,
138     uint32_t *intrpc)
139 {
140 	int depth = 0;
141 	uintptr_t osp, sp;
142 	vm_offset_t callpc;
143 	pc_t caller = (pc_t) solaris_cpu[curcpu].cpu_dtrace_caller;
144 
145 	osp = PAGE_SIZE;
146 	if (intrpc != 0)
147 		pcstack[depth++] = (pc_t) intrpc;
148 
149 	aframes++;
150 
151 	sp = dtrace_getfp();
152 
153 	while (depth < pcstack_limit) {
154 		if (sp <= osp)
155 			break;
156 
157 		if (!dtrace_sp_inkernel(sp))
158 			break;
159 		osp = sp;
160 		dtrace_next_sp_pc(osp, &sp, &callpc);
161 
162 		if (aframes > 0) {
163 			aframes--;
164 			if ((aframes == 0) && (caller != 0)) {
165 				pcstack[depth++] = caller;
166 			}
167 		}
168 		else {
169 			pcstack[depth++] = callpc;
170 		}
171 	}
172 
173 	for (; depth < pcstack_limit; depth++) {
174 		pcstack[depth] = 0;
175 	}
176 }
177 
178 static int
179 dtrace_getustack_common(uint64_t *pcstack, int pcstack_limit, uintptr_t pc,
180     uintptr_t sp)
181 {
182 	proc_t *p = curproc;
183 	int ret = 0;
184 
185 	ASSERT(pcstack == NULL || pcstack_limit > 0);
186 
187 	while (pc != 0) {
188 		ret++;
189 		if (pcstack != NULL) {
190 			*pcstack++ = (uint64_t)pc;
191 			pcstack_limit--;
192 			if (pcstack_limit <= 0)
193 				break;
194 		}
195 
196 		if (sp == 0)
197 			break;
198 
199 		if (SV_PROC_FLAG(p, SV_ILP32)) {
200 			pc = dtrace_fuword32((void *)(sp + RETURN_OFFSET));
201 			sp = dtrace_fuword32((void *)sp);
202 		}
203 		else {
204 			pc = dtrace_fuword64((void *)(sp + RETURN_OFFSET64));
205 			sp = dtrace_fuword64((void *)sp);
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;
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->srr0;
241 	sp = tf->fixreg[1];
242 
243 	if (DTRACE_CPUFLAG_ISSET(CPU_DTRACE_ENTRY)) {
244 		/*
245 		 * In an entry probe.  The frame pointer has not yet been
246 		 * pushed (that happens in the function prologue).  The
247 		 * best approach is to add the current pc as a missing top
248 		 * of stack and back the pc up to the caller, which is stored
249 		 * at the current stack pointer address since the call
250 		 * instruction puts it there right before the branch.
251 		 */
252 
253 		*pcstack++ = (uint64_t)pc;
254 		pcstack_limit--;
255 		if (pcstack_limit <= 0)
256 			return;
257 
258 		pc = tf->lr;
259 	}
260 
261 	n = dtrace_getustack_common(pcstack, pcstack_limit, pc, sp);
262 	ASSERT(n >= 0);
263 	ASSERT(n <= pcstack_limit);
264 
265 	pcstack += n;
266 	pcstack_limit -= n;
267 
268 zero:
269 	while (pcstack_limit-- > 0)
270 		*pcstack++ = 0;
271 }
272 
273 int
274 dtrace_getustackdepth(void)
275 {
276 	proc_t *p = curproc;
277 	struct trapframe *tf;
278 	uintptr_t pc, sp;
279 	int n = 0;
280 
281 	if (p == NULL || (tf = curthread->td_frame) == NULL)
282 		return (0);
283 
284 	if (DTRACE_CPUFLAG_ISSET(CPU_DTRACE_FAULT))
285 		return (-1);
286 
287 	pc = tf->srr0;
288 	sp = tf->fixreg[1];
289 
290 	if (DTRACE_CPUFLAG_ISSET(CPU_DTRACE_ENTRY)) {
291 		/*
292 		 * In an entry probe.  The frame pointer has not yet been
293 		 * pushed (that happens in the function prologue).  The
294 		 * best approach is to add the current pc as a missing top
295 		 * of stack and back the pc up to the caller, which is stored
296 		 * at the current stack pointer address since the call
297 		 * instruction puts it there right before the branch.
298 		 */
299 
300 		if (SV_PROC_FLAG(p, SV_ILP32)) {
301 			pc = dtrace_fuword32((void *) sp);
302 		}
303 		else
304 			pc = dtrace_fuword64((void *) sp);
305 		n++;
306 	}
307 
308 	n += dtrace_getustack_common(NULL, 0, pc, sp);
309 
310 	return (n);
311 }
312 
313 void
314 dtrace_getufpstack(uint64_t *pcstack, uint64_t *fpstack, int pcstack_limit)
315 {
316 	proc_t *p = curproc;
317 	struct trapframe *tf;
318 	uintptr_t pc, sp;
319 	volatile uint16_t *flags =
320 	    (volatile uint16_t *)&cpu_core[curcpu].cpuc_dtrace_flags;
321 #ifdef notyet	/* XXX signal stack */
322 	uintptr_t oldcontext;
323 	size_t s1, s2;
324 #endif
325 
326 	if (*flags & CPU_DTRACE_FAULT)
327 		return;
328 
329 	if (pcstack_limit <= 0)
330 		return;
331 
332 	/*
333 	 * If there's no user context we still need to zero the stack.
334 	 */
335 	if (p == NULL || (tf = curthread->td_frame) == NULL)
336 		goto zero;
337 
338 	*pcstack++ = (uint64_t)p->p_pid;
339 	pcstack_limit--;
340 
341 	if (pcstack_limit <= 0)
342 		return;
343 
344 	pc = tf->srr0;
345 	sp = tf->fixreg[1];
346 
347 #ifdef notyet /* XXX signal stack */
348 	oldcontext = lwp->lwp_oldcontext;
349 	s1 = sizeof (struct xframe) + 2 * sizeof (long);
350 	s2 = s1 + sizeof (siginfo_t);
351 #endif
352 
353 	if (DTRACE_CPUFLAG_ISSET(CPU_DTRACE_ENTRY)) {
354 		*pcstack++ = (uint64_t)pc;
355 		*fpstack++ = 0;
356 		pcstack_limit--;
357 		if (pcstack_limit <= 0)
358 			return;
359 
360 		if (SV_PROC_FLAG(p, SV_ILP32)) {
361 			pc = dtrace_fuword32((void *)sp);
362 		}
363 		else {
364 			pc = dtrace_fuword64((void *)sp);
365 		}
366 	}
367 
368 	while (pc != 0) {
369 		*pcstack++ = (uint64_t)pc;
370 		*fpstack++ = sp;
371 		pcstack_limit--;
372 		if (pcstack_limit <= 0)
373 			break;
374 
375 		if (sp == 0)
376 			break;
377 
378 #ifdef notyet /* XXX signal stack */
379 		if (oldcontext == sp + s1 || oldcontext == sp + s2) {
380 			ucontext_t *ucp = (ucontext_t *)oldcontext;
381 			greg_t *gregs = ucp->uc_mcontext.gregs;
382 
383 			sp = dtrace_fulword(&gregs[REG_FP]);
384 			pc = dtrace_fulword(&gregs[REG_PC]);
385 
386 			oldcontext = dtrace_fulword(&ucp->uc_link);
387 		} else
388 #endif /* XXX */
389 		{
390 			if (SV_PROC_FLAG(p, SV_ILP32)) {
391 				pc = dtrace_fuword32((void *)(sp + RETURN_OFFSET));
392 				sp = dtrace_fuword32((void *)sp);
393 			}
394 			else {
395 				pc = dtrace_fuword64((void *)(sp + RETURN_OFFSET64));
396 				sp = dtrace_fuword64((void *)sp);
397 			}
398 		}
399 
400 		/*
401 		 * This is totally bogus:  if we faulted, we're going to clear
402 		 * the fault and break.  This is to deal with the apparently
403 		 * broken Java stacks on x86.
404 		 */
405 		if (*flags & CPU_DTRACE_FAULT) {
406 			*flags &= ~CPU_DTRACE_FAULT;
407 			break;
408 		}
409 	}
410 
411 zero:
412 	while (pcstack_limit-- > 0)
413 		*pcstack++ = 0;
414 }
415 
416 /*ARGSUSED*/
417 uint64_t
418 dtrace_getarg(int arg, int aframes)
419 {
420 	uintptr_t val;
421 	uintptr_t *fp = (uintptr_t *)dtrace_getfp();
422 	uintptr_t *stack;
423 	int i;
424 
425 	/*
426 	 * A total of 8 arguments are passed via registers; any argument with
427 	 * index of 7 or lower is therefore in a register.
428 	 */
429 	int inreg = 7;
430 
431 	for (i = 1; i <= aframes; i++) {
432 		fp = (uintptr_t *)*fp;
433 
434 		/*
435 		 * On ppc32 AIM, and booke, trapexit() is the immediately following
436 		 * label.  On ppc64 AIM trapexit() follows a nop.
437 		 */
438 #ifdef __powerpc64__
439 		if ((long)(fp[2]) + 4 == (long)trapexit) {
440 #else
441 		if ((long)(fp[1]) == (long)trapexit) {
442 #endif
443 			/*
444 			 * In the case of powerpc, we will use the pointer to the regs
445 			 * structure that was pushed when we took the trap.  To get this
446 			 * structure, we must increment beyond the frame structure.  If the
447 			 * argument that we're seeking is passed on the stack, we'll pull
448 			 * the true stack pointer out of the saved registers and decrement
449 			 * our argument by the number of arguments passed in registers; if
450 			 * the argument we're seeking is passed in regsiters, we can just
451 			 * load it directly.
452 			 */
453 #ifdef __powerpc64__
454 			struct reg *rp = (struct reg *)((uintptr_t)fp[0] + 48);
455 #else
456 			struct reg *rp = (struct reg *)((uintptr_t)fp[0] + 8);
457 #endif
458 
459 			if (arg <= inreg) {
460 				stack = &rp->fixreg[3];
461 			} else {
462 				stack = (uintptr_t *)(rp->fixreg[1]);
463 				arg -= inreg;
464 			}
465 			goto load;
466 		}
467 
468 	}
469 
470 	/*
471 	 * We know that we did not come through a trap to get into
472 	 * dtrace_probe() -- the provider simply called dtrace_probe()
473 	 * directly.  As this is the case, we need to shift the argument
474 	 * that we're looking for:  the probe ID is the first argument to
475 	 * dtrace_probe(), so the argument n will actually be found where
476 	 * one would expect to find argument (n + 1).
477 	 */
478 	arg++;
479 
480 	if (arg <= inreg) {
481 		/*
482 		 * This shouldn't happen.  If the argument is passed in a
483 		 * register then it should have been, well, passed in a
484 		 * register...
485 		 */
486 		DTRACE_CPUFLAG_SET(CPU_DTRACE_ILLOP);
487 		return (0);
488 	}
489 
490 	arg -= (inreg + 1);
491 	stack = fp + 2;
492 
493 load:
494 	DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT);
495 	val = stack[arg];
496 	DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT);
497 
498 	return (val);
499 }
500 
501 int
502 dtrace_getstackdepth(int aframes)
503 {
504 	int depth = 0;
505 	uintptr_t osp, sp;
506 	vm_offset_t callpc;
507 
508 	osp = PAGE_SIZE;
509 	aframes++;
510 	sp = dtrace_getfp();
511 	depth++;
512 	for(;;) {
513 		if (sp <= osp)
514 			break;
515 
516 		if (!dtrace_sp_inkernel(sp))
517 			break;
518 
519 		if (aframes == 0)
520 			depth++;
521 		else
522 			aframes--;
523 		osp = sp;
524 		dtrace_next_sp_pc(sp, &sp, NULL);
525 	}
526 	if (depth < aframes)
527 		return (0);
528 
529 	return (depth);
530 }
531 
532 ulong_t
533 dtrace_getreg(struct trapframe *rp, uint_t reg)
534 {
535 	if (reg < 32)
536 		return (rp->fixreg[reg]);
537 
538 	switch (reg) {
539 	case 32:
540 		return (rp->lr);
541 	case 33:
542 		return (rp->cr);
543 	case 34:
544 		return (rp->xer);
545 	case 35:
546 		return (rp->ctr);
547 	case 36:
548 		return (rp->srr0);
549 	case 37:
550 		return (rp->srr1);
551 	case 38:
552 		return (rp->exc);
553 	default:
554 		DTRACE_CPUFLAG_SET(CPU_DTRACE_ILLOP);
555 		return (0);
556 	}
557 }
558 
559 static int
560 dtrace_copycheck(uintptr_t uaddr, uintptr_t kaddr, size_t size)
561 {
562 	ASSERT(INKERNEL(kaddr) && kaddr + size >= kaddr);
563 
564 	if (uaddr + size > VM_MAXUSER_ADDRESS || uaddr + size < uaddr) {
565 		DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
566 		cpu_core[curcpu].cpuc_dtrace_illval = uaddr;
567 		return (0);
568 	}
569 
570 	return (1);
571 }
572 
573 void
574 dtrace_copyin(uintptr_t uaddr, uintptr_t kaddr, size_t size,
575     volatile uint16_t *flags)
576 {
577 	if (dtrace_copycheck(uaddr, kaddr, size))
578 		if (copyin((const void *)uaddr, (void *)kaddr, size)) {
579 			DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
580 			cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
581 		}
582 }
583 
584 void
585 dtrace_copyout(uintptr_t kaddr, uintptr_t uaddr, size_t size,
586     volatile uint16_t *flags)
587 {
588 	if (dtrace_copycheck(uaddr, kaddr, size)) {
589 		if (copyout((const void *)kaddr, (void *)uaddr, size)) {
590 			DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
591 			cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
592 		}
593 	}
594 }
595 
596 void
597 dtrace_copyinstr(uintptr_t uaddr, uintptr_t kaddr, size_t size,
598     volatile uint16_t *flags)
599 {
600 	size_t actual;
601 	int    error;
602 
603 	if (dtrace_copycheck(uaddr, kaddr, size)) {
604 		error = copyinstr((const void *)uaddr, (void *)kaddr,
605 		    size, &actual);
606 
607 		/* ENAMETOOLONG is not a fault condition. */
608 		if (error && error != ENAMETOOLONG) {
609 			DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
610 			cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
611 		}
612 	}
613 }
614 
615 /*
616  * The bulk of this function could be replaced to match dtrace_copyinstr()
617  * if we ever implement a copyoutstr().
618  */
619 void
620 dtrace_copyoutstr(uintptr_t kaddr, uintptr_t uaddr, size_t size,
621     volatile uint16_t *flags)
622 {
623 	size_t len;
624 
625 	if (dtrace_copycheck(uaddr, kaddr, size)) {
626 		len = strlen((const char *)kaddr);
627 		if (len > size)
628 			len = size;
629 
630 		if (copyout((const void *)kaddr, (void *)uaddr, len)) {
631 			DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
632 			cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
633 		}
634 	}
635 }
636 
637 uint8_t
638 dtrace_fuword8(void *uaddr)
639 {
640 	if ((uintptr_t)uaddr > VM_MAXUSER_ADDRESS) {
641 		DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
642 		cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
643 		return (0);
644 	}
645 	return (fubyte(uaddr));
646 }
647 
648 uint16_t
649 dtrace_fuword16(void *uaddr)
650 {
651 	uint16_t ret = 0;
652 
653 	if (dtrace_copycheck((uintptr_t)uaddr, (uintptr_t)&ret, sizeof(ret))) {
654 		if (copyin((const void *)uaddr, (void *)&ret, sizeof(ret))) {
655 			DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
656 			cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
657 		}
658 	}
659 	return ret;
660 }
661 
662 uint32_t
663 dtrace_fuword32(void *uaddr)
664 {
665 	if ((uintptr_t)uaddr > VM_MAXUSER_ADDRESS) {
666 		DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
667 		cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
668 		return (0);
669 	}
670 	return (fuword32(uaddr));
671 }
672 
673 uint64_t
674 dtrace_fuword64(void *uaddr)
675 {
676 	uint64_t ret = 0;
677 
678 	if (dtrace_copycheck((uintptr_t)uaddr, (uintptr_t)&ret, sizeof(ret))) {
679 		if (copyin((const void *)uaddr, (void *)&ret, sizeof(ret))) {
680 			DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
681 			cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
682 		}
683 	}
684 	return ret;
685 }
686 
687 uintptr_t
688 dtrace_fulword(void *uaddr)
689 {
690 	uintptr_t ret = 0;
691 
692 	if (dtrace_copycheck((uintptr_t)uaddr, (uintptr_t)&ret, sizeof(ret))) {
693 		if (copyin((const void *)uaddr, (void *)&ret, sizeof(ret))) {
694 			DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
695 			cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
696 		}
697 	}
698 	return ret;
699 }
700