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