aboutsummaryrefslogtreecommitdiff
path: root/sys/cddl/dev/dtrace/powerpc
diff options
context:
space:
mode:
Diffstat (limited to 'sys/cddl/dev/dtrace/powerpc')
-rw-r--r--sys/cddl/dev/dtrace/powerpc/dtrace_asm.S170
-rw-r--r--sys/cddl/dev/dtrace/powerpc/dtrace_isa.c723
-rw-r--r--sys/cddl/dev/dtrace/powerpc/dtrace_subr.c360
-rw-r--r--sys/cddl/dev/dtrace/powerpc/regset.h63
4 files changed, 1316 insertions, 0 deletions
diff --git a/sys/cddl/dev/dtrace/powerpc/dtrace_asm.S b/sys/cddl/dev/dtrace/powerpc/dtrace_asm.S
new file mode 100644
index 000000000000..3adfbe38cd7f
--- /dev/null
+++ b/sys/cddl/dev/dtrace/powerpc/dtrace_asm.S
@@ -0,0 +1,170 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ * Portions Copyright 2012,2013 Justin Hibbits <jhibbits@freebsd.org>
+ *
+ * $FreeBSD$
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include "assym.s"
+
+#define _ASM
+
+#include <sys/cpuvar_defs.h>
+#include <sys/dtrace.h>
+
+#include <machine/asm.h>
+/*
+#include <machine/cpu.h>
+*/
+
+/*
+ * Primitives
+ */
+
+ .text
+
+/*
+void dtrace_membar_producer(void)
+*/
+ASENTRY_NOPROF(dtrace_membar_producer)
+ sync
+ blr
+END(dtrace_membar_producer)
+
+/*
+void dtrace_membar_consumer(void)
+*/
+ASENTRY_NOPROF(dtrace_membar_consumer)
+ isync
+ blr
+END(dtrace_membar_consumer)
+
+/*
+dtrace_icookie_t dtrace_interrupt_disable(void)
+*/
+ASENTRY_NOPROF(dtrace_interrupt_disable)
+ mfmsr %r3
+ andi. %r0,%r3,~PSL_EE@l
+ mtmsr %r0
+ blr
+END(dtrace_interrupt_disable)
+
+/*
+void dtrace_interrupt_enable(dtrace_icookie_t cookie)
+*/
+ASENTRY_NOPROF(dtrace_interrupt_enable)
+ mtmsr %r3
+ blr
+END(dtrace_interrupt_enable)
+
+/*
+uint32_t dtrace_cas32(uint32_t *target, uint32_t cmp, uint32_t new)
+*/
+ASENTRY_NOPROF(dtrace_cas32)
+1:
+ lwarx %r0,0,%r3
+ cmpw %r4,%r0
+ bne 2f
+ stwcx. %r5,0,%r3
+ bne 1b
+2: mr %r3,%r0
+ blr
+END(dtrace_cas32)
+
+/*
+void *
+dtrace_casptr(void *target, void *cmp, void *new)
+*/
+ASENTRY_NOPROF(dtrace_casptr)
+#ifdef __powerpc64__
+1:
+ ldarx %r0,0,%r3
+ cmpd %r4,%r0
+ bne 2f
+ stdcx. %r5,0,%r3
+ bne 1b
+#else
+1:
+ lwarx %r0,0,%r3
+ cmpw %r4,%r0
+ bne 2f
+ stwcx. %r5,0,%r3
+ bne 1b
+#endif
+2: mr %r3,%r0
+ blr
+END(dtrace_casptr)
+
+
+/*
+XXX: unoptimized
+void
+dtrace_copy(uintptr_t src, uintptr_t dest, size_t size)
+*/
+ASENTRY_NOPROF(dtrace_copy)
+ subi %r7,%r3,1
+ subi %r8,%r4,1
+ mtctr %r5
+1:
+ lbzu %r3,1(%r7)
+ stbu %r3,1(%r8)
+ bdnz 1b
+2:
+ blr
+END(dtrace_copy)
+
+/*
+void
+dtrace_copystr(uintptr_t uaddr, uintptr_t kaddr, size_t size,
+ volatile uint16_t *flags)
+*/
+ASENTRY_NOPROF(dtrace_copystr)
+ addme %r7,%r3
+ addme %r8,%r4
+1:
+ lbzu %r3,1(%r7)
+ stbu %r3,1(%r8)
+ addme %r5,%r5
+ beq 2f
+ or %r3,%r3,%r3
+ beq 2f
+ andi. %r0,%r5,0x0fff
+ beq 2f
+ lwz %r0,0(%r6)
+ andi. %r0,%r0,CPU_DTRACE_BADADDR
+ beq 1b
+2:
+ blr
+END(dtrace_copystr)
+
+/*
+uintptr_t
+dtrace_caller(int aframes)
+*/
+ASENTRY_NOPROF(dtrace_caller)
+ li %r3, -1
+ blr
+END(dtrace_caller)
diff --git a/sys/cddl/dev/dtrace/powerpc/dtrace_isa.c b/sys/cddl/dev/dtrace/powerpc/dtrace_isa.c
new file mode 100644
index 000000000000..d80e95004861
--- /dev/null
+++ b/sys/cddl/dev/dtrace/powerpc/dtrace_isa.c
@@ -0,0 +1,723 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ * Portions Copyright 2012,2013 Justin Hibbits <jhibbits@freebsd.org>
+ *
+ * $FreeBSD$
+ */
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+#include <sys/cdefs.h>
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/stack.h>
+#include <sys/sysent.h>
+#include <sys/pcpu.h>
+
+#include <machine/frame.h>
+#include <machine/md_var.h>
+#include <machine/reg.h>
+#include <machine/stack.h>
+
+#include <vm/vm.h>
+#include <vm/vm_param.h>
+#include <vm/pmap.h>
+
+#include "regset.h"
+
+/* Offset to the LR Save word (ppc32) */
+#define RETURN_OFFSET 4
+/* Offset to LR Save word (ppc64). CR Save area sits between back chain and LR */
+#define RETURN_OFFSET64 16
+
+#ifdef __powerpc64__
+#define OFFSET 4 /* Account for the TOC reload slot */
+#else
+#define OFFSET 0
+#endif
+
+#define INKERNEL(x) ((x) <= VM_MAX_KERNEL_ADDRESS && \
+ (x) >= VM_MIN_KERNEL_ADDRESS)
+
+static __inline int
+dtrace_sp_inkernel(uintptr_t sp, int aframes)
+{
+ vm_offset_t callpc;
+
+#ifdef __powerpc64__
+ callpc = *(vm_offset_t *)(sp + RETURN_OFFSET64);
+#else
+ callpc = *(vm_offset_t *)(sp + RETURN_OFFSET);
+#endif
+ if ((callpc & 3) || (callpc < 0x100))
+ return (0);
+
+ /*
+ * trapexit() and asttrapexit() are sentinels
+ * for kernel stack tracing.
+ *
+ * Special-case this for 'aframes == 0', because fbt sets aframes to the
+ * trap callchain depth, so we want to break out of it.
+ */
+ if ((callpc + OFFSET == (vm_offset_t) &trapexit ||
+ callpc + OFFSET == (vm_offset_t) &asttrapexit) &&
+ aframes != 0)
+ return (0);
+
+ return (1);
+}
+
+static __inline uintptr_t
+dtrace_next_sp(uintptr_t sp)
+{
+ vm_offset_t callpc;
+
+#ifdef __powerpc64__
+ callpc = *(vm_offset_t *)(sp + RETURN_OFFSET64);
+#else
+ callpc = *(vm_offset_t *)(sp + RETURN_OFFSET);
+#endif
+
+ /*
+ * trapexit() and asttrapexit() are sentinels
+ * for kernel stack tracing.
+ *
+ * Special-case this for 'aframes == 0', because fbt sets aframes to the
+ * trap callchain depth, so we want to break out of it.
+ */
+ if ((callpc + OFFSET == (vm_offset_t) &trapexit ||
+ callpc + OFFSET == (vm_offset_t) &asttrapexit))
+ /* Access the trap frame */
+#ifdef __powerpc64__
+ return (*(uintptr_t *)sp + 48 + sizeof(register_t));
+#else
+ return (*(uintptr_t *)sp + 8 + sizeof(register_t));
+#endif
+
+ return (*(uintptr_t*)sp);
+}
+
+static __inline uintptr_t
+dtrace_get_pc(uintptr_t sp)
+{
+ vm_offset_t callpc;
+
+#ifdef __powerpc64__
+ callpc = *(vm_offset_t *)(sp + RETURN_OFFSET64);
+#else
+ callpc = *(vm_offset_t *)(sp + RETURN_OFFSET);
+#endif
+
+ /*
+ * trapexit() and asttrapexit() are sentinels
+ * for kernel stack tracing.
+ *
+ * Special-case this for 'aframes == 0', because fbt sets aframes to the
+ * trap callchain depth, so we want to break out of it.
+ */
+ if ((callpc + OFFSET == (vm_offset_t) &trapexit ||
+ callpc + OFFSET == (vm_offset_t) &asttrapexit))
+ /* Access the trap frame */
+#ifdef __powerpc64__
+ return (*(uintptr_t *)sp + 48 + offsetof(struct trapframe, lr));
+#else
+ return (*(uintptr_t *)sp + 8 + offsetof(struct trapframe, lr));
+#endif
+
+ return (callpc);
+}
+
+greg_t
+dtrace_getfp(void)
+{
+ return (greg_t)__builtin_frame_address(0);
+}
+
+void
+dtrace_getpcstack(pc_t *pcstack, int pcstack_limit, int aframes,
+ uint32_t *intrpc)
+{
+ int depth = 0;
+ uintptr_t osp, sp;
+ vm_offset_t callpc;
+ pc_t caller = (pc_t) solaris_cpu[curcpu].cpu_dtrace_caller;
+
+ osp = PAGE_SIZE;
+ if (intrpc != 0)
+ pcstack[depth++] = (pc_t) intrpc;
+
+ aframes++;
+
+ sp = dtrace_getfp();
+
+ while (depth < pcstack_limit) {
+ if (sp <= osp)
+ break;
+
+ if (!dtrace_sp_inkernel(sp, aframes))
+ break;
+ callpc = dtrace_get_pc(sp);
+
+ if (aframes > 0) {
+ aframes--;
+ if ((aframes == 0) && (caller != 0)) {
+ pcstack[depth++] = caller;
+ }
+ }
+ else {
+ pcstack[depth++] = callpc;
+ }
+
+ osp = sp;
+ sp = dtrace_next_sp(sp);
+ }
+
+ for (; depth < pcstack_limit; depth++) {
+ pcstack[depth] = 0;
+ }
+}
+
+static int
+dtrace_getustack_common(uint64_t *pcstack, int pcstack_limit, uintptr_t pc,
+ uintptr_t sp)
+{
+ proc_t *p = curproc;
+ int ret = 0;
+
+ ASSERT(pcstack == NULL || pcstack_limit > 0);
+
+ while (pc != 0) {
+ ret++;
+ if (pcstack != NULL) {
+ *pcstack++ = (uint64_t)pc;
+ pcstack_limit--;
+ if (pcstack_limit <= 0)
+ break;
+ }
+
+ if (sp == 0)
+ break;
+
+ if (SV_PROC_FLAG(p, SV_ILP32)) {
+ pc = dtrace_fuword32((void *)(sp + RETURN_OFFSET));
+ sp = dtrace_fuword32((void *)sp);
+ }
+ else {
+ pc = dtrace_fuword64((void *)(sp + RETURN_OFFSET64));
+ sp = dtrace_fuword64((void *)sp);
+ }
+ }
+
+ return (ret);
+}
+
+void
+dtrace_getupcstack(uint64_t *pcstack, int pcstack_limit)
+{
+ proc_t *p = curproc;
+ struct trapframe *tf;
+ uintptr_t pc, sp;
+ volatile uint16_t *flags =
+ (volatile uint16_t *)&cpu_core[curcpu].cpuc_dtrace_flags;
+ int n;
+
+ if (*flags & CPU_DTRACE_FAULT)
+ return;
+
+ if (pcstack_limit <= 0)
+ return;
+
+ /*
+ * If there's no user context we still need to zero the stack.
+ */
+ if (p == NULL || (tf = curthread->td_frame) == NULL)
+ goto zero;
+
+ *pcstack++ = (uint64_t)p->p_pid;
+ pcstack_limit--;
+
+ if (pcstack_limit <= 0)
+ return;
+
+ pc = tf->srr0;
+ sp = tf->fixreg[1];
+
+ if (DTRACE_CPUFLAG_ISSET(CPU_DTRACE_ENTRY)) {
+ /*
+ * In an entry probe. The frame pointer has not yet been
+ * pushed (that happens in the function prologue). The
+ * best approach is to add the current pc as a missing top
+ * of stack and back the pc up to the caller, which is stored
+ * at the current stack pointer address since the call
+ * instruction puts it there right before the branch.
+ */
+
+ *pcstack++ = (uint64_t)pc;
+ pcstack_limit--;
+ if (pcstack_limit <= 0)
+ return;
+
+ pc = tf->lr;
+ }
+
+ n = dtrace_getustack_common(pcstack, pcstack_limit, pc, sp);
+ ASSERT(n >= 0);
+ ASSERT(n <= pcstack_limit);
+
+ pcstack += n;
+ pcstack_limit -= n;
+
+zero:
+ while (pcstack_limit-- > 0)
+ *pcstack++ = 0;
+}
+
+int
+dtrace_getustackdepth(void)
+{
+ proc_t *p = curproc;
+ struct trapframe *tf;
+ uintptr_t pc, sp;
+ int n = 0;
+
+ if (p == NULL || (tf = curthread->td_frame) == NULL)
+ return (0);
+
+ if (DTRACE_CPUFLAG_ISSET(CPU_DTRACE_FAULT))
+ return (-1);
+
+ pc = tf->srr0;
+ sp = tf->fixreg[1];
+
+ if (DTRACE_CPUFLAG_ISSET(CPU_DTRACE_ENTRY)) {
+ /*
+ * In an entry probe. The frame pointer has not yet been
+ * pushed (that happens in the function prologue). The
+ * best approach is to add the current pc as a missing top
+ * of stack and back the pc up to the caller, which is stored
+ * at the current stack pointer address since the call
+ * instruction puts it there right before the branch.
+ */
+
+ if (SV_PROC_FLAG(p, SV_ILP32)) {
+ pc = dtrace_fuword32((void *) sp);
+ }
+ else
+ pc = dtrace_fuword64((void *) sp);
+ n++;
+ }
+
+ n += dtrace_getustack_common(NULL, 0, pc, sp);
+
+ return (n);
+}
+
+void
+dtrace_getufpstack(uint64_t *pcstack, uint64_t *fpstack, int pcstack_limit)
+{
+ proc_t *p = curproc;
+ struct trapframe *tf;
+ uintptr_t pc, sp;
+ volatile uint16_t *flags =
+ (volatile uint16_t *)&cpu_core[curcpu].cpuc_dtrace_flags;
+#ifdef notyet /* XXX signal stack */
+ uintptr_t oldcontext;
+ size_t s1, s2;
+#endif
+
+ if (*flags & CPU_DTRACE_FAULT)
+ return;
+
+ if (pcstack_limit <= 0)
+ return;
+
+ /*
+ * If there's no user context we still need to zero the stack.
+ */
+ if (p == NULL || (tf = curthread->td_frame) == NULL)
+ goto zero;
+
+ *pcstack++ = (uint64_t)p->p_pid;
+ pcstack_limit--;
+
+ if (pcstack_limit <= 0)
+ return;
+
+ pc = tf->srr0;
+ sp = tf->fixreg[1];
+
+#ifdef notyet /* XXX signal stack */
+ oldcontext = lwp->lwp_oldcontext;
+ s1 = sizeof (struct xframe) + 2 * sizeof (long);
+ s2 = s1 + sizeof (siginfo_t);
+#endif
+
+ if (DTRACE_CPUFLAG_ISSET(CPU_DTRACE_ENTRY)) {
+ *pcstack++ = (uint64_t)pc;
+ *fpstack++ = 0;
+ pcstack_limit--;
+ if (pcstack_limit <= 0)
+ return;
+
+ if (SV_PROC_FLAG(p, SV_ILP32)) {
+ pc = dtrace_fuword32((void *)sp);
+ }
+ else {
+ pc = dtrace_fuword64((void *)sp);
+ }
+ }
+
+ while (pc != 0) {
+ *pcstack++ = (uint64_t)pc;
+ *fpstack++ = sp;
+ pcstack_limit--;
+ if (pcstack_limit <= 0)
+ break;
+
+ if (sp == 0)
+ break;
+
+#ifdef notyet /* XXX signal stack */
+ if (oldcontext == sp + s1 || oldcontext == sp + s2) {
+ ucontext_t *ucp = (ucontext_t *)oldcontext;
+ greg_t *gregs = ucp->uc_mcontext.gregs;
+
+ sp = dtrace_fulword(&gregs[REG_FP]);
+ pc = dtrace_fulword(&gregs[REG_PC]);
+
+ oldcontext = dtrace_fulword(&ucp->uc_link);
+ } else
+#endif /* XXX */
+ {
+ if (SV_PROC_FLAG(p, SV_ILP32)) {
+ pc = dtrace_fuword32((void *)(sp + RETURN_OFFSET));
+ sp = dtrace_fuword32((void *)sp);
+ }
+ else {
+ pc = dtrace_fuword64((void *)(sp + RETURN_OFFSET64));
+ sp = dtrace_fuword64((void *)sp);
+ }
+ }
+
+ /*
+ * This is totally bogus: if we faulted, we're going to clear
+ * the fault and break. This is to deal with the apparently
+ * broken Java stacks on x86.
+ */
+ if (*flags & CPU_DTRACE_FAULT) {
+ *flags &= ~CPU_DTRACE_FAULT;
+ break;
+ }
+ }
+
+zero:
+ while (pcstack_limit-- > 0)
+ *pcstack++ = 0;
+}
+
+/*ARGSUSED*/
+uint64_t
+dtrace_getarg(int arg, int aframes)
+{
+ uintptr_t val;
+ uintptr_t *fp = (uintptr_t *)dtrace_getfp();
+ uintptr_t *stack;
+ int i;
+
+ /*
+ * A total of 8 arguments are passed via registers; any argument with
+ * index of 7 or lower is therefore in a register.
+ */
+ int inreg = 7;
+
+ for (i = 1; i <= aframes; i++) {
+ fp = (uintptr_t *)*fp;
+
+ /*
+ * On ppc32 AIM, and booke, trapexit() is the immediately following
+ * label. On ppc64 AIM trapexit() follows a nop.
+ */
+#ifdef __powerpc64__
+ if ((long)(fp[2]) + 4 == (long)trapexit) {
+#else
+ if ((long)(fp[1]) == (long)trapexit) {
+#endif
+ /*
+ * In the case of powerpc, we will use the pointer to the regs
+ * structure that was pushed when we took the trap. To get this
+ * structure, we must increment beyond the frame structure. If the
+ * argument that we're seeking is passed on the stack, we'll pull
+ * the true stack pointer out of the saved registers and decrement
+ * our argument by the number of arguments passed in registers; if
+ * the argument we're seeking is passed in regsiters, we can just
+ * load it directly.
+ */
+#ifdef __powerpc64__
+ struct reg *rp = (struct reg *)((uintptr_t)fp[0] + 48);
+#else
+ struct reg *rp = (struct reg *)((uintptr_t)fp[0] + 8);
+#endif
+
+ if (arg <= inreg) {
+ stack = &rp->fixreg[3];
+ } else {
+ stack = (uintptr_t *)(rp->fixreg[1]);
+ arg -= inreg;
+ }
+ goto load;
+ }
+
+ }
+
+ /*
+ * We know that we did not come through a trap to get into
+ * dtrace_probe() -- the provider simply called dtrace_probe()
+ * directly. As this is the case, we need to shift the argument
+ * that we're looking for: the probe ID is the first argument to
+ * dtrace_probe(), so the argument n will actually be found where
+ * one would expect to find argument (n + 1).
+ */
+ arg++;
+
+ if (arg <= inreg) {
+ /*
+ * This shouldn't happen. If the argument is passed in a
+ * register then it should have been, well, passed in a
+ * register...
+ */
+ DTRACE_CPUFLAG_SET(CPU_DTRACE_ILLOP);
+ return (0);
+ }
+
+ arg -= (inreg + 1);
+ stack = fp + 2;
+
+load:
+ DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT);
+ val = stack[arg];
+ DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT);
+
+ return (val);
+}
+
+int
+dtrace_getstackdepth(int aframes)
+{
+ int depth = 0;
+ uintptr_t osp, sp;
+ vm_offset_t callpc;
+
+ osp = PAGE_SIZE;
+ aframes++;
+ sp = dtrace_getfp();
+ depth++;
+ for(;;) {
+ if (sp <= osp)
+ break;
+
+ if (!dtrace_sp_inkernel(sp, aframes))
+ break;
+
+ if (aframes == 0)
+ depth++;
+ else
+ aframes--;
+ osp = sp;
+ sp = *(uintptr_t *)sp;
+ }
+ if (depth < aframes)
+ return (0);
+
+ return (depth);
+}
+
+ulong_t
+dtrace_getreg(struct trapframe *rp, uint_t reg)
+{
+ if (reg < 32)
+ return (rp->fixreg[reg]);
+
+ switch (reg) {
+ case 33:
+ return (rp->lr);
+ case 34:
+ return (rp->cr);
+ case 35:
+ return (rp->xer);
+ case 36:
+ return (rp->ctr);
+ case 37:
+ return (rp->srr0);
+ case 38:
+ return (rp->srr1);
+ case 39:
+ return (rp->exc);
+ default:
+ DTRACE_CPUFLAG_SET(CPU_DTRACE_ILLOP);
+ return (0);
+ }
+}
+
+static int
+dtrace_copycheck(uintptr_t uaddr, uintptr_t kaddr, size_t size)
+{
+ ASSERT(INKERNEL(kaddr) && kaddr + size >= kaddr);
+
+ if (uaddr + size > VM_MAXUSER_ADDRESS || uaddr + size < uaddr) {
+ DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
+ cpu_core[curcpu].cpuc_dtrace_illval = uaddr;
+ return (0);
+ }
+
+ return (1);
+}
+
+void
+dtrace_copyin(uintptr_t uaddr, uintptr_t kaddr, size_t size,
+ volatile uint16_t *flags)
+{
+ if (dtrace_copycheck(uaddr, kaddr, size))
+ if (copyin((const void *)uaddr, (void *)kaddr, size)) {
+ DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
+ cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
+ }
+}
+
+void
+dtrace_copyout(uintptr_t kaddr, uintptr_t uaddr, size_t size,
+ volatile uint16_t *flags)
+{
+ if (dtrace_copycheck(uaddr, kaddr, size)) {
+ if (copyout((const void *)kaddr, (void *)uaddr, size)) {
+ DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
+ cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
+ }
+ }
+}
+
+void
+dtrace_copyinstr(uintptr_t uaddr, uintptr_t kaddr, size_t size,
+ volatile uint16_t *flags)
+{
+ size_t actual;
+ int error;
+
+ if (dtrace_copycheck(uaddr, kaddr, size)) {
+ error = copyinstr((const void *)uaddr, (void *)kaddr,
+ size, &actual);
+
+ /* ENAMETOOLONG is not a fault condition. */
+ if (error && error != ENAMETOOLONG) {
+ DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
+ cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
+ }
+ }
+}
+
+/*
+ * The bulk of this function could be replaced to match dtrace_copyinstr()
+ * if we ever implement a copyoutstr().
+ */
+void
+dtrace_copyoutstr(uintptr_t kaddr, uintptr_t uaddr, size_t size,
+ volatile uint16_t *flags)
+{
+ size_t len;
+
+ if (dtrace_copycheck(uaddr, kaddr, size)) {
+ len = strlen((const char *)kaddr);
+ if (len > size)
+ len = size;
+
+ if (copyout((const void *)kaddr, (void *)uaddr, len)) {
+ DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
+ cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
+ }
+ }
+}
+
+uint8_t
+dtrace_fuword8(void *uaddr)
+{
+ if ((uintptr_t)uaddr > VM_MAXUSER_ADDRESS) {
+ DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
+ cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
+ return (0);
+ }
+ return (fubyte(uaddr));
+}
+
+uint16_t
+dtrace_fuword16(void *uaddr)
+{
+ uint16_t ret = 0;
+
+ if (dtrace_copycheck((uintptr_t)uaddr, (uintptr_t)&ret, sizeof(ret))) {
+ if (copyin((const void *)uaddr, (void *)&ret, sizeof(ret))) {
+ DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
+ cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
+ }
+ }
+ return ret;
+}
+
+uint32_t
+dtrace_fuword32(void *uaddr)
+{
+ if ((uintptr_t)uaddr > VM_MAXUSER_ADDRESS) {
+ DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
+ cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
+ return (0);
+ }
+ return (fuword32(uaddr));
+}
+
+uint64_t
+dtrace_fuword64(void *uaddr)
+{
+ uint64_t ret = 0;
+
+ if (dtrace_copycheck((uintptr_t)uaddr, (uintptr_t)&ret, sizeof(ret))) {
+ if (copyin((const void *)uaddr, (void *)&ret, sizeof(ret))) {
+ DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
+ cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
+ }
+ }
+ return ret;
+}
+
+uintptr_t
+dtrace_fulword(void *uaddr)
+{
+ uintptr_t ret = 0;
+
+ if (dtrace_copycheck((uintptr_t)uaddr, (uintptr_t)&ret, sizeof(ret))) {
+ if (copyin((const void *)uaddr, (void *)&ret, sizeof(ret))) {
+ DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
+ cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
+ }
+ }
+ return ret;
+}
diff --git a/sys/cddl/dev/dtrace/powerpc/dtrace_subr.c b/sys/cddl/dev/dtrace/powerpc/dtrace_subr.c
new file mode 100644
index 000000000000..c0360fd413a1
--- /dev/null
+++ b/sys/cddl/dev/dtrace/powerpc/dtrace_subr.c
@@ -0,0 +1,360 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ * $FreeBSD$
+ *
+ */
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/types.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/kmem.h>
+#include <sys/smp.h>
+#include <sys/dtrace_impl.h>
+#include <sys/dtrace_bsd.h>
+#include <machine/clock.h>
+#include <machine/frame.h>
+#include <machine/trap.h>
+#include <vm/pmap.h>
+
+#define DELAYBRANCH(x) ((int)(x) < 0)
+
+extern uintptr_t dtrace_in_probe_addr;
+extern int dtrace_in_probe;
+extern dtrace_id_t dtrace_probeid_error;
+extern int (*dtrace_invop_jump_addr)(struct trapframe *);
+
+extern void dtrace_getnanotime(struct timespec *tsp);
+
+int dtrace_invop(uintptr_t, uintptr_t *, uintptr_t);
+void dtrace_invop_init(void);
+void dtrace_invop_uninit(void);
+
+typedef struct dtrace_invop_hdlr {
+ int (*dtih_func)(uintptr_t, uintptr_t *, uintptr_t);
+ struct dtrace_invop_hdlr *dtih_next;
+} dtrace_invop_hdlr_t;
+
+dtrace_invop_hdlr_t *dtrace_invop_hdlr;
+
+int
+dtrace_invop(uintptr_t addr, uintptr_t *stack, uintptr_t arg0)
+{
+ dtrace_invop_hdlr_t *hdlr;
+ int rval;
+
+ for (hdlr = dtrace_invop_hdlr; hdlr != NULL; hdlr = hdlr->dtih_next)
+ if ((rval = hdlr->dtih_func(addr, stack, arg0)) != 0)
+ return (rval);
+
+ return (0);
+}
+
+void
+dtrace_invop_add(int (*func)(uintptr_t, uintptr_t *, uintptr_t))
+{
+ dtrace_invop_hdlr_t *hdlr;
+
+ hdlr = kmem_alloc(sizeof (dtrace_invop_hdlr_t), KM_SLEEP);
+ hdlr->dtih_func = func;
+ hdlr->dtih_next = dtrace_invop_hdlr;
+ dtrace_invop_hdlr = hdlr;
+}
+
+void
+dtrace_invop_remove(int (*func)(uintptr_t, uintptr_t *, uintptr_t))
+{
+ dtrace_invop_hdlr_t *hdlr = dtrace_invop_hdlr, *prev = NULL;
+
+ for (;;) {
+ if (hdlr == NULL)
+ panic("attempt to remove non-existent invop handler");
+
+ if (hdlr->dtih_func == func)
+ break;
+
+ prev = hdlr;
+ hdlr = hdlr->dtih_next;
+ }
+
+ if (prev == NULL) {
+ ASSERT(dtrace_invop_hdlr == hdlr);
+ dtrace_invop_hdlr = hdlr->dtih_next;
+ } else {
+ ASSERT(dtrace_invop_hdlr != hdlr);
+ prev->dtih_next = hdlr->dtih_next;
+ }
+
+ kmem_free(hdlr, 0);
+}
+
+
+/*ARGSUSED*/
+void
+dtrace_toxic_ranges(void (*func)(uintptr_t base, uintptr_t limit))
+{
+ /*
+ * No toxic regions?
+ */
+}
+
+void
+dtrace_xcall(processorid_t cpu, dtrace_xcall_t func, void *arg)
+{
+ cpuset_t cpus;
+
+ if (cpu == DTRACE_CPUALL)
+ cpus = all_cpus;
+ else
+ CPU_SETOF(cpu, &cpus);
+
+ smp_rendezvous_cpus(cpus, smp_no_rendevous_barrier, func,
+ smp_no_rendevous_barrier, arg);
+}
+
+static void
+dtrace_sync_func(void)
+{
+}
+
+void
+dtrace_sync(void)
+{
+ dtrace_xcall(DTRACE_CPUALL, (dtrace_xcall_t)dtrace_sync_func, NULL);
+}
+
+static int64_t tgt_cpu_tsc;
+static int64_t hst_cpu_tsc;
+static int64_t timebase_skew[MAXCPU];
+static uint64_t nsec_scale;
+
+/* See below for the explanation of this macro. */
+/* This is taken from the amd64 dtrace_subr, to provide a synchronized timer
+ * between multiple processors in dtrace. Since PowerPC Timebases can be much
+ * lower than x86, the scale shift is 26 instead of 28, allowing for a 15.63MHz
+ * timebase.
+ */
+#define SCALE_SHIFT 26
+
+static void
+dtrace_gethrtime_init_cpu(void *arg)
+{
+ uintptr_t cpu = (uintptr_t) arg;
+
+ if (cpu == curcpu)
+ tgt_cpu_tsc = mftb();
+ else
+ hst_cpu_tsc = mftb();
+}
+
+static void
+dtrace_gethrtime_init(void *arg)
+{
+ struct pcpu *pc;
+ uint64_t tb_f;
+ cpuset_t map;
+ int i;
+
+ tb_f = cpu_tickrate();
+
+ /*
+ * The following line checks that nsec_scale calculated below
+ * doesn't overflow 32-bit unsigned integer, so that it can multiply
+ * another 32-bit integer without overflowing 64-bit.
+ * Thus minimum supported Timebase frequency is 15.63MHz.
+ */
+ KASSERT(tb_f > (NANOSEC >> (32 - SCALE_SHIFT)), ("Timebase frequency is too low"));
+
+ /*
+ * We scale up NANOSEC/tb_f ratio to preserve as much precision
+ * as possible.
+ * 2^26 factor was chosen quite arbitrarily from practical
+ * considerations:
+ * - it supports TSC frequencies as low as 15.63MHz (see above);
+ */
+ nsec_scale = ((uint64_t)NANOSEC << SCALE_SHIFT) / tb_f;
+
+ /* The current CPU is the reference one. */
+ sched_pin();
+ timebase_skew[curcpu] = 0;
+ CPU_FOREACH(i) {
+ if (i == curcpu)
+ continue;
+
+ pc = pcpu_find(i);
+ CPU_SETOF(PCPU_GET(cpuid), &map);
+ CPU_SET(pc->pc_cpuid, &map);
+
+ smp_rendezvous_cpus(map, NULL,
+ dtrace_gethrtime_init_cpu,
+ smp_no_rendevous_barrier, (void *)(uintptr_t) i);
+
+ timebase_skew[i] = tgt_cpu_tsc - hst_cpu_tsc;
+ }
+ sched_unpin();
+}
+
+SYSINIT(dtrace_gethrtime_init, SI_SUB_SMP, SI_ORDER_ANY, dtrace_gethrtime_init, NULL);
+
+/*
+ * DTrace needs a high resolution time function which can
+ * be called from a probe context and guaranteed not to have
+ * instrumented with probes itself.
+ *
+ * Returns nanoseconds since boot.
+ */
+uint64_t
+dtrace_gethrtime()
+{
+ uint64_t timebase;
+ uint32_t lo;
+ uint32_t hi;
+
+ /*
+ * We split timebase value into lower and higher 32-bit halves and separately
+ * scale them with nsec_scale, then we scale them down by 2^28
+ * (see nsec_scale calculations) taking into account 32-bit shift of
+ * the higher half and finally add.
+ */
+ timebase = mftb() - timebase_skew[curcpu];
+ lo = timebase;
+ hi = timebase >> 32;
+ return (((lo * nsec_scale) >> SCALE_SHIFT) +
+ ((hi * nsec_scale) << (32 - SCALE_SHIFT)));
+}
+
+uint64_t
+dtrace_gethrestime(void)
+{
+ struct timespec curtime;
+
+ dtrace_getnanotime(&curtime);
+
+ return (curtime.tv_sec * 1000000000UL + curtime.tv_nsec);
+}
+
+/* Function to handle DTrace traps during probes. See powerpc/powerpc/trap.c */
+int
+dtrace_trap(struct trapframe *frame, u_int type)
+{
+
+ /*
+ * A trap can occur while DTrace executes a probe. Before
+ * executing the probe, DTrace blocks re-scheduling and sets
+ * a flag in its per-cpu flags to indicate that it doesn't
+ * want to fault. On returning from the probe, the no-fault
+ * flag is cleared and finally re-scheduling is enabled.
+ *
+ * Check if DTrace has enabled 'no-fault' mode:
+ */
+ if ((cpu_core[curcpu].cpuc_dtrace_flags & CPU_DTRACE_NOFAULT) != 0) {
+ /*
+ * There are only a couple of trap types that are expected.
+ * All the rest will be handled in the usual way.
+ */
+ switch (type) {
+ /* Page fault. */
+ case EXC_DSI:
+ case EXC_DSE:
+ /* Flag a bad address. */
+ cpu_core[curcpu].cpuc_dtrace_flags |= CPU_DTRACE_BADADDR;
+ cpu_core[curcpu].cpuc_dtrace_illval = frame->dar;
+
+ /*
+ * Offset the instruction pointer to the instruction
+ * following the one causing the fault.
+ */
+ frame->srr0 += sizeof(int);
+ return (1);
+ case EXC_ISI:
+ case EXC_ISE:
+ /* Flag a bad address. */
+ cpu_core[curcpu].cpuc_dtrace_flags |= CPU_DTRACE_BADADDR;
+ cpu_core[curcpu].cpuc_dtrace_illval = frame->srr0;
+
+ /*
+ * Offset the instruction pointer to the instruction
+ * following the one causing the fault.
+ */
+ frame->srr0 += sizeof(int);
+ return (1);
+ default:
+ /* Handle all other traps in the usual way. */
+ break;
+ }
+ }
+
+ /* Handle the trap in the usual way. */
+ return (0);
+}
+
+void
+dtrace_probe_error(dtrace_state_t *state, dtrace_epid_t epid, int which,
+ int fault, int fltoffs, uintptr_t illval)
+{
+
+ dtrace_probe(dtrace_probeid_error, (uint64_t)(uintptr_t)state,
+ (uintptr_t)epid,
+ (uintptr_t)which, (uintptr_t)fault, (uintptr_t)fltoffs);
+}
+
+static int
+dtrace_invop_start(struct trapframe *frame)
+{
+ switch (dtrace_invop(frame->srr0, (uintptr_t *)frame, frame->fixreg[3])) {
+ case DTRACE_INVOP_JUMP:
+ break;
+ case DTRACE_INVOP_BCTR:
+ frame->srr0 = frame->ctr;
+ break;
+ case DTRACE_INVOP_BLR:
+ frame->srr0 = frame->lr;
+ break;
+ case DTRACE_INVOP_MFLR_R0:
+ frame->fixreg[0] = frame->lr;
+ frame->srr0 = frame->srr0 + 4;
+ break;
+ default:
+ return (-1);
+ break;
+ }
+
+ return (0);
+}
+
+void dtrace_invop_init(void)
+{
+ dtrace_invop_jump_addr = dtrace_invop_start;
+}
+
+void dtrace_invop_uninit(void)
+{
+ dtrace_invop_jump_addr = 0;
+}
diff --git a/sys/cddl/dev/dtrace/powerpc/regset.h b/sys/cddl/dev/dtrace/powerpc/regset.h
new file mode 100644
index 000000000000..64973885fc96
--- /dev/null
+++ b/sys/cddl/dev/dtrace/powerpc/regset.h
@@ -0,0 +1,63 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ * $FreeBSD$
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1990, 1991 UNIX System Laboratories, Inc. */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+#ifndef _REGSET_H
+#define _REGSET_H
+
+/*
+ * #pragma ident "@(#)regset.h 1.11 05/06/08 SMI"
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * XXXDTRACE: define registers properly
+ */
+
+#if 0
+#define REG_PC PC
+#define REG_FP EBP
+#define REG_SP SP
+#define REG_PS EFL
+#define REG_R0 EAX
+#define REG_R1 EDX
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _REGSET_H */
+