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