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