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