dtrace_isa.c revision 289786
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: stable/10/sys/cddl/dev/dtrace/powerpc/dtrace_isa.c 289786 2015-10-23 07:31:04Z avg $
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
57greg_t
58dtrace_getfp(void)
59{
60	return (greg_t)__builtin_frame_address(0);
61}
62
63void
64dtrace_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
110static int
111dtrace_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
144void
145dtrace_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
200zero:
201	while (pcstack_limit-- > 0)
202		*pcstack++ = 0;
203}
204
205int
206dtrace_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
245void
246dtrace_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
343zero:
344	while (pcstack_limit-- > 0)
345		*pcstack++ = 0;
346}
347
348/*ARGSUSED*/
349uint64_t
350dtrace_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
422load:
423	DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT);
424	val = stack[arg];
425	DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT);
426
427	return (val);
428}
429
430int
431dtrace_getstackdepth(int aframes)
432{
433	int depth = 0;
434	register_t sp;
435
436	aframes++;
437	sp = dtrace_getfp();
438	depth++;
439	for(;;) {
440		if (!INKERNEL((long) sp))
441			break;
442		if (!INKERNEL((long) *(void **)sp))
443			break;
444		depth++;
445		sp = *(uintptr_t *)sp;
446	}
447	if (depth < aframes)
448		return 0;
449	else
450		return depth - aframes;
451}
452
453ulong_t
454dtrace_getreg(struct trapframe *rp, uint_t reg)
455{
456	if (reg < 32)
457		return (rp->fixreg[reg]);
458
459	switch (reg) {
460	case 33:
461		return (rp->lr);
462	case 34:
463		return (rp->cr);
464	case 35:
465		return (rp->xer);
466	case 36:
467		return (rp->ctr);
468	case 37:
469		return (rp->srr0);
470	case 38:
471		return (rp->srr1);
472	case 39:
473		return (rp->exc);
474	default:
475		DTRACE_CPUFLAG_SET(CPU_DTRACE_ILLOP);
476		return (0);
477	}
478}
479
480static int
481dtrace_copycheck(uintptr_t uaddr, uintptr_t kaddr, size_t size)
482{
483	ASSERT(INKERNEL(kaddr) && kaddr + size >= kaddr);
484
485	if (uaddr + size > VM_MAXUSER_ADDRESS || uaddr + size < uaddr) {
486		DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
487		cpu_core[curcpu].cpuc_dtrace_illval = uaddr;
488		return (0);
489	}
490
491	return (1);
492}
493
494void
495dtrace_copyin(uintptr_t uaddr, uintptr_t kaddr, size_t size,
496    volatile uint16_t *flags)
497{
498	if (dtrace_copycheck(uaddr, kaddr, size))
499		if (copyin((const void *)uaddr, (void *)kaddr, size)) {
500			DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
501			cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
502		}
503}
504
505void
506dtrace_copyout(uintptr_t kaddr, uintptr_t uaddr, size_t size,
507    volatile uint16_t *flags)
508{
509	if (dtrace_copycheck(uaddr, kaddr, size)) {
510		if (copyout((const void *)kaddr, (void *)uaddr, size)) {
511			DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
512			cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
513		}
514	}
515}
516
517void
518dtrace_copyinstr(uintptr_t uaddr, uintptr_t kaddr, size_t size,
519    volatile uint16_t *flags)
520{
521	size_t actual;
522	int    error;
523
524	if (dtrace_copycheck(uaddr, kaddr, size)) {
525		error = copyinstr((const void *)uaddr, (void *)kaddr,
526		    size, &actual);
527
528		/* ENAMETOOLONG is not a fault condition. */
529		if (error && error != ENAMETOOLONG) {
530			DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
531			cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
532		}
533	}
534}
535
536/*
537 * The bulk of this function could be replaced to match dtrace_copyinstr()
538 * if we ever implement a copyoutstr().
539 */
540void
541dtrace_copyoutstr(uintptr_t kaddr, uintptr_t uaddr, size_t size,
542    volatile uint16_t *flags)
543{
544	size_t len;
545
546	if (dtrace_copycheck(uaddr, kaddr, size)) {
547		len = strlen((const char *)kaddr);
548		if (len > size)
549			len = size;
550
551		if (copyout((const void *)kaddr, (void *)uaddr, len)) {
552			DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
553			cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
554		}
555	}
556}
557
558uint8_t
559dtrace_fuword8(void *uaddr)
560{
561	if ((uintptr_t)uaddr > VM_MAXUSER_ADDRESS) {
562		DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
563		cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
564		return (0);
565	}
566	return (fubyte(uaddr));
567}
568
569uint16_t
570dtrace_fuword16(void *uaddr)
571{
572	uint16_t ret = 0;
573
574	if (dtrace_copycheck((uintptr_t)uaddr, (uintptr_t)&ret, sizeof(ret))) {
575		if (copyin((const void *)uaddr, (void *)&ret, sizeof(ret))) {
576			DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
577			cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
578		}
579	}
580	return ret;
581}
582
583uint32_t
584dtrace_fuword32(void *uaddr)
585{
586	if ((uintptr_t)uaddr > VM_MAXUSER_ADDRESS) {
587		DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
588		cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
589		return (0);
590	}
591	return (fuword32(uaddr));
592}
593
594uint64_t
595dtrace_fuword64(void *uaddr)
596{
597	uint64_t ret = 0;
598
599	if (dtrace_copycheck((uintptr_t)uaddr, (uintptr_t)&ret, sizeof(ret))) {
600		if (copyin((const void *)uaddr, (void *)&ret, sizeof(ret))) {
601			DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
602			cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
603		}
604	}
605	return ret;
606}
607
608uintptr_t
609dtrace_fulword(void *uaddr)
610{
611	uintptr_t ret = 0;
612
613	if (dtrace_copycheck((uintptr_t)uaddr, (uintptr_t)&ret, sizeof(ret))) {
614		if (copyin((const void *)uaddr, (void *)&ret, sizeof(ret))) {
615			DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
616			cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
617		}
618	}
619	return ret;
620}
621