aboutsummaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
Diffstat (limited to 'sys')
-rw-r--r--sys/arm/arm/exception.S15
-rw-r--r--sys/arm/arm/identcpu.c2
-rw-r--r--sys/arm/arm/trap.c10
-rw-r--r--sys/arm/conf/BEAGLEBONE6
-rw-r--r--sys/arm/include/cpufunc.h2
-rw-r--r--sys/cddl/contrib/opensolaris/uts/arm/dtrace/fasttrap_isa.c30
-rw-r--r--sys/cddl/contrib/opensolaris/uts/arm/sys/fasttrap_isa.h94
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/dtrace/dtrace.c2
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/sys/dtrace.h7
-rw-r--r--sys/cddl/dev/dtrace/arm/dtrace_asm.S197
-rw-r--r--sys/cddl/dev/dtrace/arm/dtrace_isa.c356
-rw-r--r--sys/cddl/dev/dtrace/arm/dtrace_subr.c261
-rw-r--r--sys/cddl/dev/dtrace/arm/regset.h57
-rw-r--r--sys/cddl/dev/fbt/arm/fbt_isa.c1303
-rw-r--r--sys/cddl/dev/fbt/arm/fbt_isa.h30
-rw-r--r--sys/cddl/dev/lockstat/lockstat.c3
-rw-r--r--sys/cddl/dev/profile/profile.c10
-rw-r--r--sys/modules/dtrace/Makefile4
-rw-r--r--sys/modules/dtrace/dtrace/Makefile5
19 files changed, 2389 insertions, 5 deletions
diff --git a/sys/arm/arm/exception.S b/sys/arm/arm/exception.S
index 17172f823c6c..58ae61255774 100644
--- a/sys/arm/arm/exception.S
+++ b/sys/arm/arm/exception.S
@@ -48,11 +48,26 @@
#include "assym.s"
+#include "opt_kdtrace.h"
#include <machine/asm.h>
#include <machine/armreg.h>
#include <machine/asmacros.h>
__FBSDID("$FreeBSD$");
+#ifdef KDTRACE_HOOKS
+ .bss
+ .align 4
+ .global _C_LABEL(dtrace_invop_jump_addr)
+_C_LABEL(dtrace_invop_jump_addr):
+ .word 0
+ .word 0
+
+ .global _C_LABEL(dtrace_invop_calltrap_addr)
+_C_LABEL(dtrace_invop_calltrap_addr):
+ .word 0
+ .word 0
+#endif
+
.text
.align 2
diff --git a/sys/arm/arm/identcpu.c b/sys/arm/arm/identcpu.c
index b37a1b3d1b69..75bf08cde618 100644
--- a/sys/arm/arm/identcpu.c
+++ b/sys/arm/arm/identcpu.c
@@ -387,7 +387,7 @@ identify_arm_cpu(void)
u_int8_t type, linesize;
int i;
- cpuid = cpu_id();
+ cpuid = cpu_ident();
if (cpuid == 0) {
printf("Processor failed probe - no CPU ID\n");
diff --git a/sys/arm/arm/trap.c b/sys/arm/arm/trap.c
index 081cfaaacf58..0f142ce06b5b 100644
--- a/sys/arm/arm/trap.c
+++ b/sys/arm/arm/trap.c
@@ -78,6 +78,9 @@
* Created : 28/11/94
*/
+#ifdef KDTRACE_HOOKS
+#include <sys/dtrace_bsd.h>
+#endif
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
@@ -427,6 +430,13 @@ dab_fatal(struct trapframe *tf, u_int fsr, u_int far, struct thread *td,
{
const char *mode;
+#ifdef KDTRACE_HOOKS
+ if (!TRAP_USERMODE(tf)) {
+ if (dtrace_trap_func != NULL && (*dtrace_trap_func)(tf, far & FAULT_TYPE_MASK))
+ return (0);
+ }
+#endif
+
mode = TRAP_USERMODE(tf) ? "user" : "kernel";
disable_interrupts(PSR_I|PSR_F);
diff --git a/sys/arm/conf/BEAGLEBONE b/sys/arm/conf/BEAGLEBONE
index 44e5c4e6c17e..975d6b241551 100644
--- a/sys/arm/conf/BEAGLEBONE
+++ b/sys/arm/conf/BEAGLEBONE
@@ -27,6 +27,12 @@ include "../ti/am335x/std.am335x"
makeoptions WITHOUT_MODULES="ahc"
+# DTrace support
+options KDTRACE_HOOKS # Kernel DTrace hooks
+options DDB_CTF # all architectures - kernel ELF linker loads CTF data
+makeoptions WITH_CTF=1
+makeoptions MODULES_OVERRIDE="opensolaris dtrace dtrace/lockstat dtrace/profile dtrace/fbt"
+
options HZ=100
options SCHED_4BSD # 4BSD scheduler
options PREEMPTION # Enable kernel thread preemption
diff --git a/sys/arm/include/cpufunc.h b/sys/arm/include/cpufunc.h
index ae2f979ba203..25e5efe25551 100644
--- a/sys/arm/include/cpufunc.h
+++ b/sys/arm/include/cpufunc.h
@@ -175,7 +175,7 @@ struct cpu_functions {
extern struct cpu_functions cpufuncs;
extern u_int cputype;
-#define cpu_id() cpufuncs.cf_id()
+#define cpu_ident() cpufuncs.cf_id()
#define cpu_cpwait() cpufuncs.cf_cpwait()
#define cpu_control(c, e) cpufuncs.cf_control(c, e)
diff --git a/sys/cddl/contrib/opensolaris/uts/arm/dtrace/fasttrap_isa.c b/sys/cddl/contrib/opensolaris/uts/arm/dtrace/fasttrap_isa.c
new file mode 100644
index 000000000000..18e3837b35b6
--- /dev/null
+++ b/sys/cddl/contrib/opensolaris/uts/arm/dtrace/fasttrap_isa.c
@@ -0,0 +1,30 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (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
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+/*
+ * XXX: Placeholder for ARM fasttrap code
+ */
diff --git a/sys/cddl/contrib/opensolaris/uts/arm/sys/fasttrap_isa.h b/sys/cddl/contrib/opensolaris/uts/arm/sys/fasttrap_isa.h
new file mode 100644
index 000000000000..10361cbed8de
--- /dev/null
+++ b/sys/cddl/contrib/opensolaris/uts/arm/sys/fasttrap_isa.h
@@ -0,0 +1,94 @@
+/*
+ * 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
+ */
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _FASTTRAP_ISA_H
+#define _FASTTRAP_ISA_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * This is our reserved trap instruction: ta 0x38
+ */
+#define FASTTRAP_INSTR 0x91d02038
+
+#define FASTTRAP_SUNWDTRACE_SIZE 128
+
+typedef uint32_t fasttrap_instr_t;
+
+typedef struct fasttrap_machtp {
+ fasttrap_instr_t ftmt_instr; /* original instruction */
+ uintptr_t ftmt_dest; /* destination of DCTI */
+ uint8_t ftmt_type; /* emulation type */
+ uint8_t ftmt_flags; /* emulation flags */
+ uint8_t ftmt_cc; /* which cc to look at */
+ uint8_t ftmt_code; /* branch condition */
+} fasttrap_machtp_t;
+
+#define ftt_instr ftt_mtp.ftmt_instr
+#define ftt_dest ftt_mtp.ftmt_dest
+#define ftt_type ftt_mtp.ftmt_type
+#define ftt_flags ftt_mtp.ftmt_flags
+#define ftt_cc ftt_mtp.ftmt_cc
+#define ftt_code ftt_mtp.ftmt_code
+
+#define FASTTRAP_T_COMMON 0x00 /* common case -- no emulation */
+#define FASTTRAP_T_CCR 0x01 /* integer condition code branch */
+#define FASTTRAP_T_FCC 0x02 /* floating-point branch */
+#define FASTTRAP_T_REG 0x03 /* register predicated branch */
+#define FASTTRAP_T_ALWAYS 0x04 /* branch always */
+#define FASTTRAP_T_CALL 0x05 /* call instruction */
+#define FASTTRAP_T_JMPL 0x06 /* jmpl instruction */
+#define FASTTRAP_T_RDPC 0x07 /* rdpc instruction */
+#define FASTTRAP_T_RETURN 0x08 /* return instruction */
+
+/*
+ * For performance rather than correctness.
+ */
+#define FASTTRAP_T_SAVE 0x10 /* save instruction (func entry only) */
+#define FASTTRAP_T_RESTORE 0x11 /* restore instruction */
+#define FASTTRAP_T_OR 0x12 /* mov instruction */
+#define FASTTRAP_T_SETHI 0x13 /* sethi instruction (includes nop) */
+
+#define FASTTRAP_F_ANNUL 0x01 /* branch is annulled */
+#define FASTTRAP_F_RETMAYBE 0x02 /* not definitely a return site */
+
+#define FASTTRAP_AFRAMES 3
+#define FASTTRAP_RETURN_AFRAMES 4
+#define FASTTRAP_ENTRY_AFRAMES 3
+#define FASTTRAP_OFFSET_AFRAMES 3
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _FASTTRAP_ISA_H */
diff --git a/sys/cddl/contrib/opensolaris/uts/common/dtrace/dtrace.c b/sys/cddl/contrib/opensolaris/uts/common/dtrace/dtrace.c
index dcf33c6e9829..818f18064f89 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/dtrace/dtrace.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/dtrace/dtrace.c
@@ -11880,7 +11880,7 @@ err:
int i;
*factor = 1;
-#if defined(__amd64__) || defined(__mips__) || defined(__powerpc__)
+#if defined(__amd64__) || defined(__arm__) || defined(__mips__) || defined(__powerpc__)
/*
* FreeBSD isn't good at limiting the amount of memory we
* ask to malloc, so let's place a limit here before trying
diff --git a/sys/cddl/contrib/opensolaris/uts/common/sys/dtrace.h b/sys/cddl/contrib/opensolaris/uts/common/sys/dtrace.h
index d449294d7507..3cb7eb27c7c4 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/sys/dtrace.h
+++ b/sys/cddl/contrib/opensolaris/uts/common/sys/dtrace.h
@@ -2434,6 +2434,13 @@ extern void dtrace_helpers_destroy(proc_t *);
#define DTRACE_INVOP_MFLR_R0 5
#define DTRACE_INVOP_NOP 6
+#elif defined(__arm__)
+
+#define DTRACE_INVOP_PUSHM 1
+#define DTRACE_INVOP_POPM 2
+#define DTRACE_INVOP_B 3
+
+
#endif
#ifdef __cplusplus
diff --git a/sys/cddl/dev/dtrace/arm/dtrace_asm.S b/sys/cddl/dev/dtrace/arm/dtrace_asm.S
new file mode 100644
index 000000000000..a536492c02b7
--- /dev/null
+++ b/sys/cddl/dev/dtrace/arm/dtrace_asm.S
@@ -0,0 +1,197 @@
+/*
+ * 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.
+ */
+
+#define _ASM
+#define _LOCORE
+#define LOCORE
+
+#include <sys/cpuvar_defs.h>
+#include <sys/dtrace.h>
+
+#include <machine/armreg.h>
+#include <machine/asm.h>
+
+#include "assym.s"
+
+/*
+void dtrace_membar_producer(void)
+*/
+ENTRY(dtrace_membar_producer)
+ RET
+END(dtrace_membar_producer)
+
+/*
+void dtrace_membar_consumer(void)
+*/
+ENTRY(dtrace_membar_consumer)
+ RET
+END(dtrace_membar_consumer)
+
+/*
+dtrace_icookie_t dtrace_interrupt_disable(void)
+*/
+ENTRY(dtrace_interrupt_disable)
+ mrs r0, cpsr
+ mov r1, r0
+ orr r1, r1, #(PSR_I | PSR_F)
+ msr cpsr_c, r1
+ RET
+END(dtrace_interrupt_disable)
+
+/*
+void dtrace_interrupt_enable(dtrace_icookie_t cookie)
+*/
+ENTRY(dtrace_interrupt_enable)
+ and r0, r0, #(PSR_I | PSR_F)
+ mrs r1, cpsr
+ bic r1, r1, #(PSR_I | PSR_F)
+ orr r1, r1, r0
+ msr cpsr_c, r1
+ RET
+END(dtrace_interrupt_enable)
+/*
+uint8_t
+dtrace_fuword8_nocheck(void *addr)
+*/
+ENTRY(dtrace_fuword8_nocheck)
+ ldrb r3, [r0]
+ mov r0, r3
+ RET
+END(dtrace_fuword8_nocheck)
+
+/*
+uint16_t
+dtrace_fuword16_nocheck(void *addr)
+*/
+ENTRY(dtrace_fuword16_nocheck)
+ ldrh r3, [r0]
+ mov r0, r3
+ RET
+END(dtrace_fuword16_nocheck)
+
+/*
+uint32_t
+dtrace_fuword32_nocheck(void *addr)
+*/
+ENTRY(dtrace_fuword32_nocheck)
+ ldr r3, [r0]
+ mov r0, r3
+ RET
+END(dtrace_fuword32_nocheck)
+
+/*
+uint64_t
+dtrace_fuword64_nocheck(void *addr)
+*/
+ENTRY(dtrace_fuword64_nocheck)
+ ldm r0, {r2, r3}
+
+ mov r0, r2
+ mov r1, r3
+#if defined(__BIG_ENDIAN__)
+/* big endian */
+ mov r0, r3
+ mov r1, r2
+#else
+/* little endian */
+ mov r0, r2
+ mov r1, r3
+
+#endif
+ RET
+END(dtrace_fuword64_nocheck)
+
+/*
+void
+dtrace_copy(uintptr_t uaddr, uintptr_t kaddr, size_t size)
+*/
+ENTRY(dtrace_copy)
+ stmfd sp!, {r4-r5} /* stack is 8 byte aligned */
+ teq r2, #0x00000000
+ mov r5, #0x00000000
+ beq 2f
+
+1: ldrb r4, [r0], #0x0001
+ add r5, r5, #0x00000001
+ strb r4, [r1], #0x0001
+ teqne r5, r2
+ bne 1b
+
+2: ldmfd sp!, {r4-r5} /* stack is 8 byte aligned */
+ RET
+END(dtrace_copy)
+
+/*
+void
+dtrace_copystr(uintptr_t uaddr, uintptr_t kaddr, size_t size,
+ volatile uint16_t *flags)
+XXX: Check for flags?
+*/
+ENTRY(dtrace_copystr)
+ stmfd sp!, {r4-r5} /* stack is 8 byte aligned */
+ teq r2, #0x00000000
+ mov r5, #0x00000000
+ beq 2f
+
+1: ldrb r4, [r0], #0x0001
+ add r5, r5, #0x00000001
+ teq r4, #0x00000000
+ strb r4, [r1], #0x0001
+ teqne r5, r2
+ bne 1b
+
+2: ldmfd sp!, {r4-r5} /* stack is 8 byte aligned */
+ RET
+END(dtrace_copystr)
+
+/*
+void
+vpanic(const char *format, va_list alist)
+*/
+ENTRY(vpanic) /* Initial stack layout: */
+vpanic_common:
+ RET
+END(vpanic)
+
+/*
+void
+dtrace_vpanic(const char *format, va_list alist)
+*/
+ENTRY(dtrace_vpanic) /* Initial stack layout: */
+ b vpanic
+ RET
+END(dtrace_vpanic) /* Initial stack layout: */
+
+/*
+uintptr_t
+dtrace_caller(int aframes)
+*/
+ENTRY(dtrace_caller)
+ mov r0, #-1
+ RET
+END(dtrace_caller)
diff --git a/sys/cddl/dev/dtrace/arm/dtrace_isa.c b/sys/cddl/dev/dtrace/arm/dtrace_isa.c
new file mode 100644
index 000000000000..7d3dc2e03801
--- /dev/null
+++ b/sys/cddl/dev/dtrace/arm/dtrace_isa.c
@@ -0,0 +1,356 @@
+/*
+ * 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>
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/stack.h>
+#include <sys/pcpu.h>
+
+#include <machine/frame.h>
+#include <machine/md_var.h>
+#include <machine/reg.h>
+
+#include <vm/vm.h>
+#include <vm/vm_param.h>
+#include <vm/pmap.h>
+
+#include <machine/atomic.h>
+#include <machine/db_machdep.h>
+#include <machine/md_var.h>
+#include <machine/vmparam.h>
+#include <machine/stack.h>
+#include <ddb/db_sym.h>
+#include <ddb/ddb.h>
+#include <sys/kdb.h>
+
+#include "regset.h"
+
+/*
+ * Wee need some reasonable default to prevent backtrace code
+ * from wandering too far
+ */
+#define MAX_FUNCTION_SIZE 0x10000
+#define MAX_PROLOGUE_SIZE 0x100
+
+
+uint8_t dtrace_fuword8_nocheck(void *);
+uint16_t dtrace_fuword16_nocheck(void *);
+uint32_t dtrace_fuword32_nocheck(void *);
+uint64_t dtrace_fuword64_nocheck(void *);
+
+void
+dtrace_getpcstack(pc_t *pcstack, int pcstack_limit, int aframes,
+ uint32_t *intrpc)
+{
+ u_int32_t *frame, *lastframe;
+ int scp_offset;
+ int depth = 0;
+ pc_t caller = (pc_t) solaris_cpu[curcpu].cpu_dtrace_caller;
+
+ if (intrpc != 0)
+ pcstack[depth++] = (pc_t) intrpc;
+
+ aframes++;
+
+ frame = (u_int32_t *)__builtin_frame_address(0);;
+ lastframe = NULL;
+ scp_offset = -(get_pc_str_offset() >> 2);
+
+ while ((frame != NULL) && (depth < pcstack_limit)) {
+ db_addr_t scp;
+#if 0
+ u_int32_t savecode;
+ int r;
+ u_int32_t *rp;
+#endif
+
+ /*
+ * In theory, the SCP isn't guaranteed to be in the function
+ * that generated the stack frame. We hope for the best.
+ */
+ scp = frame[FR_SCP];
+
+ if (aframes > 0) {
+ aframes--;
+ if ((aframes == 0) && (caller != 0)) {
+ pcstack[depth++] = caller;
+ }
+ }
+ else {
+ pcstack[depth++] = scp;
+ }
+
+#if 0
+ savecode = ((u_int32_t *)scp)[scp_offset];
+ if ((savecode & 0x0e100000) == 0x08000000) {
+ /* Looks like an STM */
+ rp = frame - 4;
+ for (r = 10; r >= 0; r--) {
+ if (savecode & (1 << r)) {
+ /* register r == *rp-- */
+ }
+ }
+ }
+#endif
+
+ /*
+ * Switch to next frame up
+ */
+ if (frame[FR_RFP] == 0)
+ break; /* Top of stack */
+
+ lastframe = frame;
+ frame = (u_int32_t *)(frame[FR_RFP]);
+
+ if (INKERNEL((int)frame)) {
+ /* staying in kernel */
+ if (frame <= lastframe) {
+ /* bad frame pointer */
+ break;
+ }
+ }
+ else
+ break;
+ }
+
+ for (; depth < pcstack_limit; depth++) {
+ pcstack[depth] = 0;
+ }
+}
+
+void
+dtrace_getupcstack(uint64_t *pcstack, int pcstack_limit)
+{
+ printf("IMPLEMENT ME: %s\n", __func__);
+}
+
+int
+dtrace_getustackdepth(void)
+{
+ printf("IMPLEMENT ME: %s\n", __func__);
+ return (0);
+}
+
+void
+dtrace_getufpstack(uint64_t *pcstack, uint64_t *fpstack, int pcstack_limit)
+{
+ printf("IMPLEMENT ME: %s\n", __func__);
+}
+
+/*ARGSUSED*/
+uint64_t
+dtrace_getarg(int arg, int aframes)
+{
+/* struct arm_frame *fp = (struct arm_frame *)dtrace_getfp();*/
+
+ return (0);
+}
+
+int
+dtrace_getstackdepth(int aframes)
+{
+ u_int32_t *frame, *lastframe;
+ int scp_offset;
+ int depth = 1;
+
+ frame = (u_int32_t *)__builtin_frame_address(0);;
+ lastframe = NULL;
+ scp_offset = -(get_pc_str_offset() >> 2);
+
+ while (frame != NULL) {
+ db_addr_t scp;
+#if 0
+ u_int32_t savecode;
+ int r;
+ u_int32_t *rp;
+#endif
+
+ /*
+ * In theory, the SCP isn't guaranteed to be in the function
+ * that generated the stack frame. We hope for the best.
+ */
+ scp = frame[FR_SCP];
+
+ depth++;
+
+ /*
+ * Switch to next frame up
+ */
+ if (frame[FR_RFP] == 0)
+ break; /* Top of stack */
+
+ lastframe = frame;
+ frame = (u_int32_t *)(frame[FR_RFP]);
+
+ if (INKERNEL((int)frame)) {
+ /* staying in kernel */
+ if (frame <= lastframe) {
+ /* bad frame pointer */
+ break;
+ }
+ }
+ else
+ break;
+ }
+
+ if (depth < aframes)
+ return 0;
+ else
+ return depth - aframes;
+
+}
+
+ulong_t
+dtrace_getreg(struct trapframe *rp, uint_t reg)
+{
+ printf("IMPLEMENT ME: %s\n", __func__);
+
+ return (0);
+}
+
+static int
+dtrace_copycheck(uintptr_t uaddr, uintptr_t kaddr, size_t size)
+{
+
+ 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))
+ dtrace_copy(uaddr, kaddr, size);
+}
+
+void
+dtrace_copyout(uintptr_t kaddr, uintptr_t uaddr, size_t size,
+ volatile uint16_t *flags)
+{
+ if (dtrace_copycheck(uaddr, kaddr, size))
+ dtrace_copy(kaddr, uaddr, size);
+}
+
+void
+dtrace_copyinstr(uintptr_t uaddr, uintptr_t kaddr, size_t size,
+ volatile uint16_t *flags)
+{
+ if (dtrace_copycheck(uaddr, kaddr, size))
+ dtrace_copystr(uaddr, kaddr, size, flags);
+}
+
+void
+dtrace_copyoutstr(uintptr_t kaddr, uintptr_t uaddr, size_t size,
+ volatile uint16_t *flags)
+{
+ if (dtrace_copycheck(uaddr, kaddr, size))
+ dtrace_copystr(kaddr, uaddr, size, flags);
+}
+
+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 (dtrace_fuword8_nocheck(uaddr));
+}
+
+uint16_t
+dtrace_fuword16(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 (dtrace_fuword16_nocheck(uaddr));
+}
+
+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 (dtrace_fuword32_nocheck(uaddr));
+}
+
+uint64_t
+dtrace_fuword64(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 (dtrace_fuword64_nocheck(uaddr));
+}
+
+#define __with_interrupts_disabled(expr) \
+ do { \
+ u_int cpsr_save, tmp; \
+ \
+ __asm __volatile( \
+ "mrs %0, cpsr;" \
+ "orr %1, %0, %2;" \
+ "msr cpsr_fsxc, %1;" \
+ : "=r" (cpsr_save), "=r" (tmp) \
+ : "I" (PSR_I | PSR_F) \
+ : "cc" ); \
+ (expr); \
+ __asm __volatile( \
+ "msr cpsr_fsxc, %0" \
+ : /* no output */ \
+ : "r" (cpsr_save) \
+ : "cc" ); \
+ } while(0)
+
+uint32_t dtrace_cas32(uint32_t *target, uint32_t cmp, uint32_t new)
+{
+ return atomic_cmpset_32((uint32_t*)target, (uint32_t)cmp, (uint32_t)new);
+
+}
+
+void * dtrace_casptr(volatile void *target, volatile void *cmp, volatile void *new)
+{
+ return (void*)dtrace_cas32((uint32_t*)target, (uint32_t)cmp, (uint32_t)new);
+}
+
diff --git a/sys/cddl/dev/dtrace/arm/dtrace_subr.c b/sys/cddl/dev/dtrace/arm/dtrace_subr.c
new file mode 100644
index 000000000000..d4c12a6a0f11
--- /dev/null
+++ b/sys/cddl/dev/dtrace/arm/dtrace_subr.c
@@ -0,0 +1,261 @@
+/*
+ * 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/armreg.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 *);
+
+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 eax)
+{
+ 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, eax)) != 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))
+{
+ printf("IMPLEMENT ME: dtrace_toxic_ranges\n");
+}
+
+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);
+}
+
+/*
+ * 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()
+{
+ struct timespec curtime;
+
+ nanouptime(&curtime);
+
+ return (curtime.tv_sec * 1000000000UL + curtime.tv_nsec);
+
+}
+
+uint64_t
+dtrace_gethrestime(void)
+{
+ struct timespec curtime;
+
+ getnanotime(&curtime);
+
+ return (curtime.tv_sec * 1000000000UL + curtime.tv_nsec);
+}
+
+/* Function to handle DTrace traps during probes. See amd64/amd64/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 it's 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 FAULT_ALIGN:
+ /* Flag a bad address. */
+ cpu_core[curcpu].cpuc_dtrace_flags |= CPU_DTRACE_BADADDR;
+ cpu_core[curcpu].cpuc_dtrace_illval = 0;
+
+ /*
+ * Offset the instruction pointer to the instruction
+ * following the one causing the fault.
+ */
+ frame->tf_pc += 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)
+{
+ printf("IMPLEMENT ME: %s\n", __func__);
+ switch (dtrace_invop(frame->tf_pc, (uintptr_t *)frame, frame->tf_pc)) {
+ case DTRACE_INVOP_PUSHM:
+ // TODO:
+ break;
+ case DTRACE_INVOP_POPM:
+ // TODO:
+ break;
+ case DTRACE_INVOP_B:
+ // TODO
+ 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/arm/regset.h b/sys/cddl/dev/dtrace/arm/regset.h
new file mode 100644
index 000000000000..ce9e97ea7a09
--- /dev/null
+++ b/sys/cddl/dev/dtrace/arm/regset.h
@@ -0,0 +1,57 @@
+/*
+ * 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
+
+#if 0
+#define REG_LINK R14
+#define REG_SP R12
+#define REG_PS R0
+#define REG_R0 R0
+#define REG_R1 R1
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _REGSET_H */
diff --git a/sys/cddl/dev/fbt/arm/fbt_isa.c b/sys/cddl/dev/fbt/arm/fbt_isa.c
new file mode 100644
index 000000000000..aaff6d6e06a6
--- /dev/null
+++ b/sys/cddl/dev/fbt/arm/fbt_isa.c
@@ -0,0 +1,1303 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (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 2006-2008 John Birrell jb@freebsd.org
+ * Portions Copyright 2013 Justin Hibbits jhibbits@freebsd.org
+ * Portions Copyright 2013 Howard Su howardsu@freebsd.org
+ *
+ * $FreeBSD$
+ *
+ */
+
+/*
+ * Copyright 2006 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/conf.h>
+#include <sys/cpuvar.h>
+#include <sys/fcntl.h>
+#include <sys/filio.h>
+#include <sys/kdb.h>
+#include <sys/kernel.h>
+#include <sys/kmem.h>
+#include <sys/kthread.h>
+#include <sys/limits.h>
+#include <sys/linker.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/pcpu.h>
+#include <sys/poll.h>
+#include <sys/proc.h>
+#include <sys/selinfo.h>
+#include <sys/smp.h>
+#include <sys/syscall.h>
+#include <sys/sysent.h>
+#include <sys/sysproto.h>
+#include <sys/uio.h>
+#include <sys/unistd.h>
+#include <machine/frame.h>
+#include <machine/md_var.h>
+#include <machine/stdarg.h>
+
+#include <sys/dtrace.h>
+#include <sys/dtrace_bsd.h>
+
+static MALLOC_DEFINE(M_FBT, "fbt", "Function Boundary Tracing");
+
+#define FBT_PATCHVAL 0xe06a0cfe // illegal instruction
+
+#define FBT_PUSHM 0xe92d0000
+#define FBT_POPM 0xe8bd0000
+#define FBT_JUMP 0xea000000
+
+static d_open_t fbt_open;
+static int fbt_unload(void);
+static void fbt_getargdesc(void *, dtrace_id_t, void *, dtrace_argdesc_t *);
+static void fbt_provide_module(void *, modctl_t *);
+static void fbt_destroy(void *, dtrace_id_t, void *);
+static void fbt_enable(void *, dtrace_id_t, void *);
+static void fbt_disable(void *, dtrace_id_t, void *);
+static void fbt_load(void *);
+static void fbt_suspend(void *, dtrace_id_t, void *);
+static void fbt_resume(void *, dtrace_id_t, void *);
+
+#define FBT_ENTRY "entry"
+#define FBT_RETURN "return"
+#define FBT_ADDR2NDX(addr) ((((uintptr_t)(addr)) >> 4) & fbt_probetab_mask)
+#define FBT_PROBETAB_SIZE 0x8000 /* 32k entries -- 128K total */
+
+static struct cdevsw fbt_cdevsw = {
+ .d_version = D_VERSION,
+ .d_open = fbt_open,
+ .d_name = "fbt",
+};
+
+static dtrace_pattr_t fbt_attr = {
+{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
+{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
+{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_ISA },
+{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
+{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_ISA },
+};
+
+static dtrace_pops_t fbt_pops = {
+ NULL,
+ fbt_provide_module,
+ fbt_enable,
+ fbt_disable,
+ fbt_suspend,
+ fbt_resume,
+ fbt_getargdesc,
+ NULL,
+ NULL,
+ fbt_destroy
+};
+
+typedef struct fbt_probe {
+ struct fbt_probe *fbtp_hashnext;
+ uint32_t *fbtp_patchpoint;
+ int8_t fbtp_rval;
+ uint32_t fbtp_patchval;
+ uint32_t fbtp_savedval;
+ uintptr_t fbtp_roffset;
+ dtrace_id_t fbtp_id;
+ const char *fbtp_name;
+ modctl_t *fbtp_ctl;
+ int fbtp_loadcnt;
+ int fbtp_primary;
+ int fbtp_invop_cnt;
+ int fbtp_symindx;
+ struct fbt_probe *fbtp_next;
+} fbt_probe_t;
+
+static struct cdev *fbt_cdev;
+static dtrace_provider_id_t fbt_id;
+static fbt_probe_t **fbt_probetab;
+static int fbt_probetab_size;
+static int fbt_probetab_mask;
+static int fbt_verbose = 0;
+
+static int
+fbt_invop(uintptr_t addr, uintptr_t *stack, uintptr_t rval)
+{
+ struct trapframe *frame = (struct trapframe *)stack;
+ solaris_cpu_t *cpu = &solaris_cpu[curcpu];
+ fbt_probe_t *fbt = fbt_probetab[FBT_ADDR2NDX(addr)];
+
+ for (; fbt != NULL; fbt = fbt->fbtp_hashnext) {
+ if ((uintptr_t)fbt->fbtp_patchpoint == addr) {
+ fbt->fbtp_invop_cnt++;
+ cpu->cpu_dtrace_caller = addr;
+
+ dtrace_probe(fbt->fbtp_id, frame->tf_r0,
+ frame->tf_r1, frame->tf_r2,
+ frame->tf_r3, 0); // TODO: Need 5th parameter from stack
+
+ cpu->cpu_dtrace_caller = 0;
+
+ return (fbt->fbtp_rval);
+ }
+ }
+
+ return (0);
+}
+
+static int
+fbt_provide_module_function(linker_file_t lf, int symindx,
+ linker_symval_t *symval, void *opaque)
+{
+ char *modname = opaque;
+ const char *name = symval->name;
+ fbt_probe_t *fbt, *retfbt;
+ int popm;
+ u_int32_t *instr, *limit;
+
+ if (strncmp(name, "dtrace_", 7) == 0 &&
+ strncmp(name, "dtrace_safe_", 12) != 0) {
+ /*
+ * Anything beginning with "dtrace_" may be called
+ * from probe context unless it explicitly indicates
+ * that it won't be called from probe context by
+ * using the prefix "dtrace_safe_".
+ */
+ return (0);
+ }
+
+ if (name[0] == '_' && name[1] == '_')
+ return (0);
+
+ instr = (u_int32_t *) symval->value;
+ limit = (u_int32_t *)(symval->value + symval->size);
+
+ for (; instr < limit; instr++)
+ if ((*instr & 0xffff0000) == FBT_PUSHM && (*instr & 0x4000) != 0)
+ break;
+
+ if (instr >= limit)
+ return (0);
+
+ fbt = malloc(sizeof (fbt_probe_t), M_FBT, M_WAITOK | M_ZERO);
+ fbt->fbtp_name = name;
+ fbt->fbtp_id = dtrace_probe_create(fbt_id, modname,
+ name, FBT_ENTRY, 3, fbt);
+ fbt->fbtp_patchpoint = instr;
+ fbt->fbtp_ctl = lf;
+ fbt->fbtp_loadcnt = lf->loadcnt;
+ fbt->fbtp_savedval = *instr;
+ fbt->fbtp_patchval = FBT_PATCHVAL;
+ fbt->fbtp_rval = DTRACE_INVOP_PUSHM;
+ fbt->fbtp_symindx = symindx;
+
+ fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)];
+ fbt_probetab[FBT_ADDR2NDX(instr)] = fbt;
+
+ lf->fbt_nentries++;
+
+ popm = FBT_POPM | ((*instr) & 0x3FFF) | 0x8000;
+
+
+ retfbt = NULL;
+again:
+ for(; instr < limit; instr++)
+ {
+ if (*instr == popm)
+ break;
+ else if ((*instr & 0xff000000) == FBT_JUMP)
+ {
+ int offset;
+ u_int32_t *target, *start;
+ offset = (*instr & 0xffffff);
+ offset <<= 8;
+ offset /= 64;
+ target = instr + (2 + offset);
+ start = (u_int32_t *) symval->value;
+ if (target >= limit || target < start)
+ break;
+ instr++; //skip delay slot
+ }
+ }
+
+ if (instr >= limit)
+ return (0);
+
+ /*
+ * We have a winner!
+ */
+ fbt = malloc(sizeof (fbt_probe_t), M_FBT, M_WAITOK | M_ZERO);
+ fbt->fbtp_name = name;
+ if (retfbt == NULL) {
+ fbt->fbtp_id = dtrace_probe_create(fbt_id, modname,
+ name, FBT_RETURN, 5, fbt);
+ } else {
+ retfbt->fbtp_next = fbt;
+ fbt->fbtp_id = retfbt->fbtp_id;
+ }
+ retfbt = fbt;
+
+ fbt->fbtp_patchpoint = instr;
+ fbt->fbtp_ctl = lf;
+ fbt->fbtp_loadcnt = lf->loadcnt;
+ fbt->fbtp_symindx = symindx;
+ if ((*instr & 0xff000000) == FBT_JUMP)
+ fbt->fbtp_rval = DTRACE_INVOP_B;
+ else
+ fbt->fbtp_rval = DTRACE_INVOP_POPM;
+ fbt->fbtp_savedval = *instr;
+ fbt->fbtp_patchval = FBT_PATCHVAL;
+ fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)];
+ fbt_probetab[FBT_ADDR2NDX(instr)] = fbt;
+
+ lf->fbt_nentries++;
+
+ instr++;
+ goto again;
+}
+
+static void
+fbt_provide_module(void *arg, modctl_t *lf)
+{
+ char modname[MAXPATHLEN];
+ int i;
+ size_t len;
+
+ strlcpy(modname, lf->filename, sizeof(modname));
+ len = strlen(modname);
+ if (len > 3 && strcmp(modname + len - 3, ".ko") == 0)
+ modname[len - 3] = '\0';
+
+ /*
+ * Employees of dtrace and their families are ineligible. Void
+ * where prohibited.
+ */
+ if (strcmp(modname, "dtrace") == 0)
+ return;
+
+ /*
+ * The cyclic timer subsystem can be built as a module and DTrace
+ * depends on that, so it is ineligible too.
+ */
+ if (strcmp(modname, "cyclic") == 0)
+ return;
+
+ /*
+ * To register with DTrace, a module must list 'dtrace' as a
+ * dependency in order for the kernel linker to resolve
+ * symbols like dtrace_register(). All modules with such a
+ * dependency are ineligible for FBT tracing.
+ */
+ for (i = 0; i < lf->ndeps; i++)
+ if (strncmp(lf->deps[i]->filename, "dtrace", 6) == 0)
+ return;
+
+ if (lf->fbt_nentries) {
+ /*
+ * This module has some FBT entries allocated; we're afraid
+ * to screw with it.
+ */
+ return;
+ }
+
+ /*
+ * List the functions in the module and the symbol values.
+ */
+ (void) linker_file_function_listall(lf, fbt_provide_module_function, modname);
+}
+
+static void
+fbt_destroy(void *arg, dtrace_id_t id, void *parg)
+{
+ fbt_probe_t *fbt = parg, *next, *hash, *last;
+ modctl_t *ctl;
+ int ndx;
+
+ do {
+ ctl = fbt->fbtp_ctl;
+
+ ctl->fbt_nentries--;
+
+ /*
+ * Now we need to remove this probe from the fbt_probetab.
+ */
+ ndx = FBT_ADDR2NDX(fbt->fbtp_patchpoint);
+ last = NULL;
+ hash = fbt_probetab[ndx];
+
+ while (hash != fbt) {
+ ASSERT(hash != NULL);
+ last = hash;
+ hash = hash->fbtp_hashnext;
+ }
+
+ if (last != NULL) {
+ last->fbtp_hashnext = fbt->fbtp_hashnext;
+ } else {
+ fbt_probetab[ndx] = fbt->fbtp_hashnext;
+ }
+
+ next = fbt->fbtp_next;
+ free(fbt, M_FBT);
+
+ fbt = next;
+ } while (fbt != NULL);
+}
+
+static void
+fbt_enable(void *arg, dtrace_id_t id, void *parg)
+{
+ fbt_probe_t *fbt = parg;
+ modctl_t *ctl = fbt->fbtp_ctl;
+
+ ctl->nenabled++;
+
+ /*
+ * Now check that our modctl has the expected load count. If it
+ * doesn't, this module must have been unloaded and reloaded -- and
+ * we're not going to touch it.
+ */
+ if (ctl->loadcnt != fbt->fbtp_loadcnt) {
+ if (fbt_verbose) {
+ printf("fbt is failing for probe %s "
+ "(module %s reloaded)",
+ fbt->fbtp_name, ctl->filename);
+ }
+
+ return;
+ }
+
+ for (; fbt != NULL; fbt = fbt->fbtp_next) {
+ *fbt->fbtp_patchpoint = fbt->fbtp_patchval;
+ cpu_icache_sync_range((vm_offset_t)fbt->fbtp_patchpoint, 4);
+ }
+}
+
+static void
+fbt_disable(void *arg, dtrace_id_t id, void *parg)
+{
+ fbt_probe_t *fbt = parg;
+ modctl_t *ctl = fbt->fbtp_ctl;
+
+ ASSERT(ctl->nenabled > 0);
+ ctl->nenabled--;
+
+ if ((ctl->loadcnt != fbt->fbtp_loadcnt))
+ return;
+
+ for (; fbt != NULL; fbt = fbt->fbtp_next) {
+ *fbt->fbtp_patchpoint = fbt->fbtp_savedval;
+ cpu_icache_sync_range((vm_offset_t)fbt->fbtp_patchpoint, 4);
+ }
+}
+
+static void
+fbt_suspend(void *arg, dtrace_id_t id, void *parg)
+{
+ fbt_probe_t *fbt = parg;
+ modctl_t *ctl = fbt->fbtp_ctl;
+
+ ASSERT(ctl->nenabled > 0);
+
+ if ((ctl->loadcnt != fbt->fbtp_loadcnt))
+ return;
+
+ for (; fbt != NULL; fbt = fbt->fbtp_next) {
+ *fbt->fbtp_patchpoint = fbt->fbtp_savedval;
+ cpu_icache_sync_range((vm_offset_t)fbt->fbtp_patchpoint, 4);
+ }
+}
+
+static void
+fbt_resume(void *arg, dtrace_id_t id, void *parg)
+{
+ fbt_probe_t *fbt = parg;
+ modctl_t *ctl = fbt->fbtp_ctl;
+
+ ASSERT(ctl->nenabled > 0);
+
+ if ((ctl->loadcnt != fbt->fbtp_loadcnt))
+ return;
+
+ for (; fbt != NULL; fbt = fbt->fbtp_next) {
+ *fbt->fbtp_patchpoint = fbt->fbtp_patchval;
+ cpu_icache_sync_range((vm_offset_t)fbt->fbtp_patchpoint, 4);
+ }
+}
+
+static int
+fbt_ctfoff_init(modctl_t *lf, linker_ctf_t *lc)
+{
+ const Elf_Sym *symp = lc->symtab;;
+ const ctf_header_t *hp = (const ctf_header_t *) lc->ctftab;
+ const uint8_t *ctfdata = lc->ctftab + sizeof(ctf_header_t);
+ int i;
+ uint32_t *ctfoff;
+ uint32_t objtoff = hp->cth_objtoff;
+ uint32_t funcoff = hp->cth_funcoff;
+ ushort_t info;
+ ushort_t vlen;
+
+ /* Sanity check. */
+ if (hp->cth_magic != CTF_MAGIC) {
+ printf("Bad magic value in CTF data of '%s'\n",lf->pathname);
+ return (EINVAL);
+ }
+
+ if (lc->symtab == NULL) {
+ printf("No symbol table in '%s'\n",lf->pathname);
+ return (EINVAL);
+ }
+
+ if ((ctfoff = malloc(sizeof(uint32_t) * lc->nsym, M_LINKER, M_WAITOK)) == NULL)
+ return (ENOMEM);
+
+ *lc->ctfoffp = ctfoff;
+
+ for (i = 0; i < lc->nsym; i++, ctfoff++, symp++) {
+ if (symp->st_name == 0 || symp->st_shndx == SHN_UNDEF) {
+ *ctfoff = 0xffffffff;
+ continue;
+ }
+
+ switch (ELF_ST_TYPE(symp->st_info)) {
+ case STT_OBJECT:
+ if (objtoff >= hp->cth_funcoff ||
+ (symp->st_shndx == SHN_ABS && symp->st_value == 0)) {
+ *ctfoff = 0xffffffff;
+ break;
+ }
+
+ *ctfoff = objtoff;
+ objtoff += sizeof (ushort_t);
+ break;
+
+ case STT_FUNC:
+ if (funcoff >= hp->cth_typeoff) {
+ *ctfoff = 0xffffffff;
+ break;
+ }
+
+ *ctfoff = funcoff;
+
+ info = *((const ushort_t *)(ctfdata + funcoff));
+ vlen = CTF_INFO_VLEN(info);
+
+ /*
+ * If we encounter a zero pad at the end, just skip it.
+ * Otherwise skip over the function and its return type
+ * (+2) and the argument list (vlen).
+ */
+ if (CTF_INFO_KIND(info) == CTF_K_UNKNOWN && vlen == 0)
+ funcoff += sizeof (ushort_t); /* skip pad */
+ else
+ funcoff += sizeof (ushort_t) * (vlen + 2);
+ break;
+
+ default:
+ *ctfoff = 0xffffffff;
+ break;
+ }
+ }
+
+ return (0);
+}
+
+static ssize_t
+fbt_get_ctt_size(uint8_t version, const ctf_type_t *tp, ssize_t *sizep,
+ ssize_t *incrementp)
+{
+ ssize_t size, increment;
+
+ if (version > CTF_VERSION_1 &&
+ tp->ctt_size == CTF_LSIZE_SENT) {
+ size = CTF_TYPE_LSIZE(tp);
+ increment = sizeof (ctf_type_t);
+ } else {
+ size = tp->ctt_size;
+ increment = sizeof (ctf_stype_t);
+ }
+
+ if (sizep)
+ *sizep = size;
+ if (incrementp)
+ *incrementp = increment;
+
+ return (size);
+}
+
+static int
+fbt_typoff_init(linker_ctf_t *lc)
+{
+ const ctf_header_t *hp = (const ctf_header_t *) lc->ctftab;
+ const ctf_type_t *tbuf;
+ const ctf_type_t *tend;
+ const ctf_type_t *tp;
+ const uint8_t *ctfdata = lc->ctftab + sizeof(ctf_header_t);
+ int ctf_typemax = 0;
+ uint32_t *xp;
+ ulong_t pop[CTF_K_MAX + 1] = { 0 };
+
+
+ /* Sanity check. */
+ if (hp->cth_magic != CTF_MAGIC)
+ return (EINVAL);
+
+ tbuf = (const ctf_type_t *) (ctfdata + hp->cth_typeoff);
+ tend = (const ctf_type_t *) (ctfdata + hp->cth_stroff);
+
+ int child = hp->cth_parname != 0;
+
+ /*
+ * We make two passes through the entire type section. In this first
+ * pass, we count the number of each type and the total number of types.
+ */
+ for (tp = tbuf; tp < tend; ctf_typemax++) {
+ ushort_t kind = CTF_INFO_KIND(tp->ctt_info);
+ ulong_t vlen = CTF_INFO_VLEN(tp->ctt_info);
+ ssize_t size, increment;
+
+ size_t vbytes;
+ uint_t n;
+
+ (void) fbt_get_ctt_size(hp->cth_version, tp, &size, &increment);
+
+ switch (kind) {
+ case CTF_K_INTEGER:
+ case CTF_K_FLOAT:
+ vbytes = sizeof (uint_t);
+ break;
+ case CTF_K_ARRAY:
+ vbytes = sizeof (ctf_array_t);
+ break;
+ case CTF_K_FUNCTION:
+ vbytes = sizeof (ushort_t) * (vlen + (vlen & 1));
+ break;
+ case CTF_K_STRUCT:
+ case CTF_K_UNION:
+ if (size < CTF_LSTRUCT_THRESH) {
+ ctf_member_t *mp = (ctf_member_t *)
+ ((uintptr_t)tp + increment);
+
+ vbytes = sizeof (ctf_member_t) * vlen;
+ for (n = vlen; n != 0; n--, mp++)
+ child |= CTF_TYPE_ISCHILD(mp->ctm_type);
+ } else {
+ ctf_lmember_t *lmp = (ctf_lmember_t *)
+ ((uintptr_t)tp + increment);
+
+ vbytes = sizeof (ctf_lmember_t) * vlen;
+ for (n = vlen; n != 0; n--, lmp++)
+ child |=
+ CTF_TYPE_ISCHILD(lmp->ctlm_type);
+ }
+ break;
+ case CTF_K_ENUM:
+ vbytes = sizeof (ctf_enum_t) * vlen;
+ break;
+ case CTF_K_FORWARD:
+ /*
+ * For forward declarations, ctt_type is the CTF_K_*
+ * kind for the tag, so bump that population count too.
+ * If ctt_type is unknown, treat the tag as a struct.
+ */
+ if (tp->ctt_type == CTF_K_UNKNOWN ||
+ tp->ctt_type >= CTF_K_MAX)
+ pop[CTF_K_STRUCT]++;
+ else
+ pop[tp->ctt_type]++;
+ /*FALLTHRU*/
+ case CTF_K_UNKNOWN:
+ vbytes = 0;
+ break;
+ case CTF_K_POINTER:
+ case CTF_K_TYPEDEF:
+ case CTF_K_VOLATILE:
+ case CTF_K_CONST:
+ case CTF_K_RESTRICT:
+ child |= CTF_TYPE_ISCHILD(tp->ctt_type);
+ vbytes = 0;
+ break;
+ default:
+ printf("%s(%d): detected invalid CTF kind -- %u\n", __func__, __LINE__, kind);
+ return (EIO);
+ }
+ tp = (ctf_type_t *)((uintptr_t)tp + increment + vbytes);
+ pop[kind]++;
+ }
+
+ /* account for a sentinel value below */
+ ctf_typemax++;
+ *lc->typlenp = ctf_typemax;
+
+ if ((xp = malloc(sizeof(uint32_t) * ctf_typemax, M_LINKER, M_ZERO | M_WAITOK)) == NULL)
+ return (ENOMEM);
+
+ *lc->typoffp = xp;
+
+ /* type id 0 is used as a sentinel value */
+ *xp++ = 0;
+
+ /*
+ * In the second pass, fill in the type offset.
+ */
+ for (tp = tbuf; tp < tend; xp++) {
+ ushort_t kind = CTF_INFO_KIND(tp->ctt_info);
+ ulong_t vlen = CTF_INFO_VLEN(tp->ctt_info);
+ ssize_t size, increment;
+
+ size_t vbytes;
+ uint_t n;
+
+ (void) fbt_get_ctt_size(hp->cth_version, tp, &size, &increment);
+
+ switch (kind) {
+ case CTF_K_INTEGER:
+ case CTF_K_FLOAT:
+ vbytes = sizeof (uint_t);
+ break;
+ case CTF_K_ARRAY:
+ vbytes = sizeof (ctf_array_t);
+ break;
+ case CTF_K_FUNCTION:
+ vbytes = sizeof (ushort_t) * (vlen + (vlen & 1));
+ break;
+ case CTF_K_STRUCT:
+ case CTF_K_UNION:
+ if (size < CTF_LSTRUCT_THRESH) {
+ ctf_member_t *mp = (ctf_member_t *)
+ ((uintptr_t)tp + increment);
+
+ vbytes = sizeof (ctf_member_t) * vlen;
+ for (n = vlen; n != 0; n--, mp++)
+ child |= CTF_TYPE_ISCHILD(mp->ctm_type);
+ } else {
+ ctf_lmember_t *lmp = (ctf_lmember_t *)
+ ((uintptr_t)tp + increment);
+
+ vbytes = sizeof (ctf_lmember_t) * vlen;
+ for (n = vlen; n != 0; n--, lmp++)
+ child |=
+ CTF_TYPE_ISCHILD(lmp->ctlm_type);
+ }
+ break;
+ case CTF_K_ENUM:
+ vbytes = sizeof (ctf_enum_t) * vlen;
+ break;
+ case CTF_K_FORWARD:
+ case CTF_K_UNKNOWN:
+ vbytes = 0;
+ break;
+ case CTF_K_POINTER:
+ case CTF_K_TYPEDEF:
+ case CTF_K_VOLATILE:
+ case CTF_K_CONST:
+ case CTF_K_RESTRICT:
+ vbytes = 0;
+ break;
+ default:
+ printf("%s(%d): detected invalid CTF kind -- %u\n", __func__, __LINE__, kind);
+ return (EIO);
+ }
+ *xp = (uint32_t)((uintptr_t) tp - (uintptr_t) ctfdata);
+ tp = (ctf_type_t *)((uintptr_t)tp + increment + vbytes);
+ }
+
+ return (0);
+}
+
+/*
+ * CTF Declaration Stack
+ *
+ * In order to implement ctf_type_name(), we must convert a type graph back
+ * into a C type declaration. Unfortunately, a type graph represents a storage
+ * class ordering of the type whereas a type declaration must obey the C rules
+ * for operator precedence, and the two orderings are frequently in conflict.
+ * For example, consider these CTF type graphs and their C declarations:
+ *
+ * CTF_K_POINTER -> CTF_K_FUNCTION -> CTF_K_INTEGER : int (*)()
+ * CTF_K_POINTER -> CTF_K_ARRAY -> CTF_K_INTEGER : int (*)[]
+ *
+ * In each case, parentheses are used to raise operator * to higher lexical
+ * precedence, so the string form of the C declaration cannot be constructed by
+ * walking the type graph links and forming the string from left to right.
+ *
+ * The functions in this file build a set of stacks from the type graph nodes
+ * corresponding to the C operator precedence levels in the appropriate order.
+ * The code in ctf_type_name() can then iterate over the levels and nodes in
+ * lexical precedence order and construct the final C declaration string.
+ */
+typedef struct ctf_list {
+ struct ctf_list *l_prev; /* previous pointer or tail pointer */
+ struct ctf_list *l_next; /* next pointer or head pointer */
+} ctf_list_t;
+
+#define ctf_list_prev(elem) ((void *)(((ctf_list_t *)(elem))->l_prev))
+#define ctf_list_next(elem) ((void *)(((ctf_list_t *)(elem))->l_next))
+
+typedef enum {
+ CTF_PREC_BASE,
+ CTF_PREC_POINTER,
+ CTF_PREC_ARRAY,
+ CTF_PREC_FUNCTION,
+ CTF_PREC_MAX
+} ctf_decl_prec_t;
+
+typedef struct ctf_decl_node {
+ ctf_list_t cd_list; /* linked list pointers */
+ ctf_id_t cd_type; /* type identifier */
+ uint_t cd_kind; /* type kind */
+ uint_t cd_n; /* type dimension if array */
+} ctf_decl_node_t;
+
+typedef struct ctf_decl {
+ ctf_list_t cd_nodes[CTF_PREC_MAX]; /* declaration node stacks */
+ int cd_order[CTF_PREC_MAX]; /* storage order of decls */
+ ctf_decl_prec_t cd_qualp; /* qualifier precision */
+ ctf_decl_prec_t cd_ordp; /* ordered precision */
+ char *cd_buf; /* buffer for output */
+ char *cd_ptr; /* buffer location */
+ char *cd_end; /* buffer limit */
+ size_t cd_len; /* buffer space required */
+ int cd_err; /* saved error value */
+} ctf_decl_t;
+
+/*
+ * Simple doubly-linked list append routine. This implementation assumes that
+ * each list element contains an embedded ctf_list_t as the first member.
+ * An additional ctf_list_t is used to store the head (l_next) and tail
+ * (l_prev) pointers. The current head and tail list elements have their
+ * previous and next pointers set to NULL, respectively.
+ */
+static void
+ctf_list_append(ctf_list_t *lp, void *new)
+{
+ ctf_list_t *p = lp->l_prev; /* p = tail list element */
+ ctf_list_t *q = new; /* q = new list element */
+
+ lp->l_prev = q;
+ q->l_prev = p;
+ q->l_next = NULL;
+
+ if (p != NULL)
+ p->l_next = q;
+ else
+ lp->l_next = q;
+}
+
+/*
+ * Prepend the specified existing element to the given ctf_list_t. The
+ * existing pointer should be pointing at a struct with embedded ctf_list_t.
+ */
+static void
+ctf_list_prepend(ctf_list_t *lp, void *new)
+{
+ ctf_list_t *p = new; /* p = new list element */
+ ctf_list_t *q = lp->l_next; /* q = head list element */
+
+ lp->l_next = p;
+ p->l_prev = NULL;
+ p->l_next = q;
+
+ if (q != NULL)
+ q->l_prev = p;
+ else
+ lp->l_prev = p;
+}
+
+static void
+ctf_decl_init(ctf_decl_t *cd, char *buf, size_t len)
+{
+ int i;
+
+ bzero(cd, sizeof (ctf_decl_t));
+
+ for (i = CTF_PREC_BASE; i < CTF_PREC_MAX; i++)
+ cd->cd_order[i] = CTF_PREC_BASE - 1;
+
+ cd->cd_qualp = CTF_PREC_BASE;
+ cd->cd_ordp = CTF_PREC_BASE;
+
+ cd->cd_buf = buf;
+ cd->cd_ptr = buf;
+ cd->cd_end = buf + len;
+}
+
+static void
+ctf_decl_fini(ctf_decl_t *cd)
+{
+ ctf_decl_node_t *cdp, *ndp;
+ int i;
+
+ for (i = CTF_PREC_BASE; i < CTF_PREC_MAX; i++) {
+ for (cdp = ctf_list_next(&cd->cd_nodes[i]);
+ cdp != NULL; cdp = ndp) {
+ ndp = ctf_list_next(cdp);
+ free(cdp, M_FBT);
+ }
+ }
+}
+
+static const ctf_type_t *
+ctf_lookup_by_id(linker_ctf_t *lc, ctf_id_t type)
+{
+ const ctf_type_t *tp;
+ uint32_t offset;
+ uint32_t *typoff = *lc->typoffp;
+
+ if (type >= *lc->typlenp) {
+ printf("%s(%d): type %d exceeds max %ld\n",__func__,__LINE__,(int) type,*lc->typlenp);
+ return(NULL);
+ }
+
+ /* Check if the type isn't cross-referenced. */
+ if ((offset = typoff[type]) == 0) {
+ printf("%s(%d): type %d isn't cross referenced\n",__func__,__LINE__, (int) type);
+ return(NULL);
+ }
+
+ tp = (const ctf_type_t *)(lc->ctftab + offset + sizeof(ctf_header_t));
+
+ return (tp);
+}
+
+static void
+fbt_array_info(linker_ctf_t *lc, ctf_id_t type, ctf_arinfo_t *arp)
+{
+ const ctf_header_t *hp = (const ctf_header_t *) lc->ctftab;
+ const ctf_type_t *tp;
+ const ctf_array_t *ap;
+ ssize_t increment;
+
+ bzero(arp, sizeof(*arp));
+
+ if ((tp = ctf_lookup_by_id(lc, type)) == NULL)
+ return;
+
+ if (CTF_INFO_KIND(tp->ctt_info) != CTF_K_ARRAY)
+ return;
+
+ (void) fbt_get_ctt_size(hp->cth_version, tp, NULL, &increment);
+
+ ap = (const ctf_array_t *)((uintptr_t)tp + increment);
+ arp->ctr_contents = ap->cta_contents;
+ arp->ctr_index = ap->cta_index;
+ arp->ctr_nelems = ap->cta_nelems;
+}
+
+static const char *
+ctf_strptr(linker_ctf_t *lc, int name)
+{
+ const ctf_header_t *hp = (const ctf_header_t *) lc->ctftab;;
+ const char *strp = "";
+
+ if (name < 0 || name >= hp->cth_strlen)
+ return (strp);
+
+ strp = (const char *)(lc->ctftab + hp->cth_stroff + name + sizeof(ctf_header_t));
+
+ return (strp);
+}
+
+static void
+ctf_decl_push(ctf_decl_t *cd, linker_ctf_t *lc, ctf_id_t type)
+{
+ ctf_decl_node_t *cdp;
+ ctf_decl_prec_t prec;
+ uint_t kind, n = 1;
+ int is_qual = 0;
+
+ const ctf_type_t *tp;
+ ctf_arinfo_t ar;
+
+ if ((tp = ctf_lookup_by_id(lc, type)) == NULL) {
+ cd->cd_err = ENOENT;
+ return;
+ }
+
+ switch (kind = CTF_INFO_KIND(tp->ctt_info)) {
+ case CTF_K_ARRAY:
+ fbt_array_info(lc, type, &ar);
+ ctf_decl_push(cd, lc, ar.ctr_contents);
+ n = ar.ctr_nelems;
+ prec = CTF_PREC_ARRAY;
+ break;
+
+ case CTF_K_TYPEDEF:
+ if (ctf_strptr(lc, tp->ctt_name)[0] == '\0') {
+ ctf_decl_push(cd, lc, tp->ctt_type);
+ return;
+ }
+ prec = CTF_PREC_BASE;
+ break;
+
+ case CTF_K_FUNCTION:
+ ctf_decl_push(cd, lc, tp->ctt_type);
+ prec = CTF_PREC_FUNCTION;
+ break;
+
+ case CTF_K_POINTER:
+ ctf_decl_push(cd, lc, tp->ctt_type);
+ prec = CTF_PREC_POINTER;
+ break;
+
+ case CTF_K_VOLATILE:
+ case CTF_K_CONST:
+ case CTF_K_RESTRICT:
+ ctf_decl_push(cd, lc, tp->ctt_type);
+ prec = cd->cd_qualp;
+ is_qual++;
+ break;
+
+ default:
+ prec = CTF_PREC_BASE;
+ }
+
+ if ((cdp = malloc(sizeof (ctf_decl_node_t), M_FBT, M_WAITOK)) == NULL) {
+ cd->cd_err = EAGAIN;
+ return;
+ }
+
+ cdp->cd_type = type;
+ cdp->cd_kind = kind;
+ cdp->cd_n = n;
+
+ if (ctf_list_next(&cd->cd_nodes[prec]) == NULL)
+ cd->cd_order[prec] = cd->cd_ordp++;
+
+ /*
+ * Reset cd_qualp to the highest precedence level that we've seen so
+ * far that can be qualified (CTF_PREC_BASE or CTF_PREC_POINTER).
+ */
+ if (prec > cd->cd_qualp && prec < CTF_PREC_ARRAY)
+ cd->cd_qualp = prec;
+
+ /*
+ * C array declarators are ordered inside out so prepend them. Also by
+ * convention qualifiers of base types precede the type specifier (e.g.
+ * const int vs. int const) even though the two forms are equivalent.
+ */
+ if (kind == CTF_K_ARRAY || (is_qual && prec == CTF_PREC_BASE))
+ ctf_list_prepend(&cd->cd_nodes[prec], cdp);
+ else
+ ctf_list_append(&cd->cd_nodes[prec], cdp);
+}
+
+static void
+ctf_decl_sprintf(ctf_decl_t *cd, const char *format, ...)
+{
+ size_t len = (size_t)(cd->cd_end - cd->cd_ptr);
+ va_list ap;
+ size_t n;
+
+ va_start(ap, format);
+ n = vsnprintf(cd->cd_ptr, len, format, ap);
+ va_end(ap);
+
+ cd->cd_ptr += MIN(n, len);
+ cd->cd_len += n;
+}
+
+static ssize_t
+fbt_type_name(linker_ctf_t *lc, ctf_id_t type, char *buf, size_t len)
+{
+ ctf_decl_t cd;
+ ctf_decl_node_t *cdp;
+ ctf_decl_prec_t prec, lp, rp;
+ int ptr, arr;
+ uint_t k;
+
+ if (lc == NULL && type == CTF_ERR)
+ return (-1); /* simplify caller code by permitting CTF_ERR */
+
+ ctf_decl_init(&cd, buf, len);
+ ctf_decl_push(&cd, lc, type);
+
+ if (cd.cd_err != 0) {
+ ctf_decl_fini(&cd);
+ return (-1);
+ }
+
+ /*
+ * If the type graph's order conflicts with lexical precedence order
+ * for pointers or arrays, then we need to surround the declarations at
+ * the corresponding lexical precedence with parentheses. This can
+ * result in either a parenthesized pointer (*) as in int (*)() or
+ * int (*)[], or in a parenthesized pointer and array as in int (*[])().
+ */
+ ptr = cd.cd_order[CTF_PREC_POINTER] > CTF_PREC_POINTER;
+ arr = cd.cd_order[CTF_PREC_ARRAY] > CTF_PREC_ARRAY;
+
+ rp = arr ? CTF_PREC_ARRAY : ptr ? CTF_PREC_POINTER : -1;
+ lp = ptr ? CTF_PREC_POINTER : arr ? CTF_PREC_ARRAY : -1;
+
+ k = CTF_K_POINTER; /* avoid leading whitespace (see below) */
+
+ for (prec = CTF_PREC_BASE; prec < CTF_PREC_MAX; prec++) {
+ for (cdp = ctf_list_next(&cd.cd_nodes[prec]);
+ cdp != NULL; cdp = ctf_list_next(cdp)) {
+
+ const ctf_type_t *tp =
+ ctf_lookup_by_id(lc, cdp->cd_type);
+ const char *name = ctf_strptr(lc, tp->ctt_name);
+
+ if (k != CTF_K_POINTER && k != CTF_K_ARRAY)
+ ctf_decl_sprintf(&cd, " ");
+
+ if (lp == prec) {
+ ctf_decl_sprintf(&cd, "(");
+ lp = -1;
+ }
+
+ switch (cdp->cd_kind) {
+ case CTF_K_INTEGER:
+ case CTF_K_FLOAT:
+ case CTF_K_TYPEDEF:
+ ctf_decl_sprintf(&cd, "%s", name);
+ break;
+ case CTF_K_POINTER:
+ ctf_decl_sprintf(&cd, "*");
+ break;
+ case CTF_K_ARRAY:
+ ctf_decl_sprintf(&cd, "[%u]", cdp->cd_n);
+ break;
+ case CTF_K_FUNCTION:
+ ctf_decl_sprintf(&cd, "()");
+ break;
+ case CTF_K_STRUCT:
+ case CTF_K_FORWARD:
+ ctf_decl_sprintf(&cd, "struct %s", name);
+ break;
+ case CTF_K_UNION:
+ ctf_decl_sprintf(&cd, "union %s", name);
+ break;
+ case CTF_K_ENUM:
+ ctf_decl_sprintf(&cd, "enum %s", name);
+ break;
+ case CTF_K_VOLATILE:
+ ctf_decl_sprintf(&cd, "volatile");
+ break;
+ case CTF_K_CONST:
+ ctf_decl_sprintf(&cd, "const");
+ break;
+ case CTF_K_RESTRICT:
+ ctf_decl_sprintf(&cd, "restrict");
+ break;
+ }
+
+ k = cdp->cd_kind;
+ }
+
+ if (rp == prec)
+ ctf_decl_sprintf(&cd, ")");
+ }
+
+ ctf_decl_fini(&cd);
+ return (cd.cd_len);
+}
+
+static void
+fbt_getargdesc(void *arg __unused, dtrace_id_t id __unused, void *parg, dtrace_argdesc_t *desc)
+{
+ const ushort_t *dp;
+ fbt_probe_t *fbt = parg;
+ linker_ctf_t lc;
+ modctl_t *ctl = fbt->fbtp_ctl;
+ int ndx = desc->dtargd_ndx;
+ int symindx = fbt->fbtp_symindx;
+ uint32_t *ctfoff;
+ uint32_t offset;
+ ushort_t info, kind, n;
+
+ if (fbt->fbtp_roffset != 0 && desc->dtargd_ndx == 0) {
+ (void) strcpy(desc->dtargd_native, "int");
+ return;
+ }
+
+ desc->dtargd_ndx = DTRACE_ARGNONE;
+
+ /* Get a pointer to the CTF data and it's length. */
+ if (linker_ctf_get(ctl, &lc) != 0)
+ /* No CTF data? Something wrong? *shrug* */
+ return;
+
+ /* Check if this module hasn't been initialised yet. */
+ if (*lc.ctfoffp == NULL) {
+ /*
+ * Initialise the CTF object and function symindx to
+ * byte offset array.
+ */
+ if (fbt_ctfoff_init(ctl, &lc) != 0)
+ return;
+
+ /* Initialise the CTF type to byte offset array. */
+ if (fbt_typoff_init(&lc) != 0)
+ return;
+ }
+
+ ctfoff = *lc.ctfoffp;
+
+ if (ctfoff == NULL || *lc.typoffp == NULL)
+ return;
+
+ /* Check if the symbol index is out of range. */
+ if (symindx >= lc.nsym)
+ return;
+
+ /* Check if the symbol isn't cross-referenced. */
+ if ((offset = ctfoff[symindx]) == 0xffffffff)
+ return;
+
+ dp = (const ushort_t *)(lc.ctftab + offset + sizeof(ctf_header_t));
+
+ info = *dp++;
+ kind = CTF_INFO_KIND(info);
+ n = CTF_INFO_VLEN(info);
+
+ if (kind == CTF_K_UNKNOWN && n == 0) {
+ printf("%s(%d): Unknown function!\n",__func__,__LINE__);
+ return;
+ }
+
+ if (kind != CTF_K_FUNCTION) {
+ printf("%s(%d): Expected a function!\n",__func__,__LINE__);
+ return;
+ }
+
+ if (fbt->fbtp_roffset != 0) {
+ /* Only return type is available for args[1] in return probe. */
+ if (ndx > 1)
+ return;
+ ASSERT(ndx == 1);
+ } else {
+ /* Check if the requested argument doesn't exist. */
+ if (ndx >= n)
+ return;
+
+ /* Skip the return type and arguments up to the one requested. */
+ dp += ndx + 1;
+ }
+
+ if (fbt_type_name(&lc, *dp, desc->dtargd_native, sizeof(desc->dtargd_native)) > 0)
+ desc->dtargd_ndx = ndx;
+
+ return;
+}
+
+static int
+fbt_linker_file_cb(linker_file_t lf, void *arg)
+{
+
+ fbt_provide_module(arg, lf);
+
+ return (0);
+}
+
+static void
+fbt_load(void *dummy)
+{
+ /* Create the /dev/dtrace/fbt entry. */
+ fbt_cdev = make_dev(&fbt_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600,
+ "dtrace/fbt");
+
+ /* Default the probe table size if not specified. */
+ if (fbt_probetab_size == 0)
+ fbt_probetab_size = FBT_PROBETAB_SIZE;
+
+ /* Choose the hash mask for the probe table. */
+ fbt_probetab_mask = fbt_probetab_size - 1;
+
+ /* Allocate memory for the probe table. */
+ fbt_probetab =
+ malloc(fbt_probetab_size * sizeof (fbt_probe_t *), M_FBT, M_WAITOK | M_ZERO);
+
+ dtrace_invop_add(fbt_invop);
+
+ if (dtrace_register("fbt", &fbt_attr, DTRACE_PRIV_USER,
+ NULL, &fbt_pops, NULL, &fbt_id) != 0)
+ return;
+
+ /* Create probes for the kernel and already-loaded modules. */
+ linker_file_foreach(fbt_linker_file_cb, NULL);
+}
+
+
+static int
+fbt_unload()
+{
+ int error = 0;
+
+ /* De-register the invalid opcode handler. */
+ dtrace_invop_remove(fbt_invop);
+
+ /* De-register this DTrace provider. */
+ if ((error = dtrace_unregister(fbt_id)) != 0)
+ return (error);
+
+ /* Free the probe table. */
+ free(fbt_probetab, M_FBT);
+ fbt_probetab = NULL;
+ fbt_probetab_mask = 0;
+
+ destroy_dev(fbt_cdev);
+
+ return (error);
+}
+
+static int
+fbt_modevent(module_t mod __unused, int type, void *data __unused)
+{
+ int error = 0;
+
+ switch (type) {
+ case MOD_LOAD:
+ break;
+
+ case MOD_UNLOAD:
+ break;
+
+ case MOD_SHUTDOWN:
+ break;
+
+ default:
+ error = EOPNOTSUPP;
+ break;
+
+ }
+
+ return (error);
+}
+
+static int
+fbt_open(struct cdev *dev __unused, int oflags __unused, int devtype __unused, struct thread *td __unused)
+{
+ return (0);
+}
+
+SYSINIT(fbt_load, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, fbt_load, NULL);
+SYSUNINIT(fbt_unload, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, fbt_unload, NULL);
+
+DEV_MODULE(fbt, fbt_modevent, NULL);
+MODULE_VERSION(fbt, 1);
+MODULE_DEPEND(fbt, dtrace, 1, 1, 1);
+MODULE_DEPEND(fbt, opensolaris, 1, 1, 1);
diff --git a/sys/cddl/dev/fbt/arm/fbt_isa.h b/sys/cddl/dev/fbt/arm/fbt_isa.h
new file mode 100644
index 000000000000..5552f31a64a6
--- /dev/null
+++ b/sys/cddl/dev/fbt/arm/fbt_isa.h
@@ -0,0 +1,30 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (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$
+ *
+ */
+
+#ifndef _FBT_ISA_H_
+#define _FBT_ISA_H_
+
+typedef uint32_t fbt_patchval_t;
+
+#endif
diff --git a/sys/cddl/dev/lockstat/lockstat.c b/sys/cddl/dev/lockstat/lockstat.c
index b8d12ac77d0e..a4e4efd93c58 100644
--- a/sys/cddl/dev/lockstat/lockstat.c
+++ b/sys/cddl/dev/lockstat/lockstat.c
@@ -44,7 +44,8 @@
#include <sys/lockstat.h>
#if defined(__i386__) || defined(__amd64__) || \
- defined(__mips__) || defined(__powerpc__)
+ defined(__mips__) || defined(__powerpc__) || \
+ defined(__arm__)
#define LOCKSTAT_AFRAMES 1
#else
#error "architecture not supported"
diff --git a/sys/cddl/dev/profile/profile.c b/sys/cddl/dev/profile/profile.c
index 5291bb168041..51105e209833 100644
--- a/sys/cddl/dev/profile/profile.c
+++ b/sys/cddl/dev/profile/profile.c
@@ -128,6 +128,16 @@
struct profile_probe_percpu;
+#ifdef __mips
+/* bogus */
+#define PROF_ARTIFICIAL_FRAMES 3
+#endif
+
+#ifdef __arm__
+/* bogus */
+#define PROF_ARTIFICIAL_FRAMES 3
+#endif
+
typedef struct profile_probe {
char prof_name[PROF_NAMELEN];
dtrace_id_t prof_id;
diff --git a/sys/modules/dtrace/Makefile b/sys/modules/dtrace/Makefile
index 08b6937bb4de..94a7a423dfab 100644
--- a/sys/modules/dtrace/Makefile
+++ b/sys/modules/dtrace/Makefile
@@ -22,5 +22,7 @@ SUBDIR+= fbt fasttrap
.if ${MACHINE_CPUARCH} == "amd64" || ${MACHINE_ARCH} == "powerpc64"
SUBDIR+= systrace_freebsd32
.endif
-
+.if ${MACHINE_CPUARCH} == "arm"
+SUBDIR+= fbt
+.endif
.include <bsd.subdir.mk>
diff --git a/sys/modules/dtrace/dtrace/Makefile b/sys/modules/dtrace/dtrace/Makefile
index 5492c6f2d55a..417266c5641b 100644
--- a/sys/modules/dtrace/dtrace/Makefile
+++ b/sys/modules/dtrace/dtrace/Makefile
@@ -53,6 +53,11 @@ EXPORT_SYMS= dtrace_register \
dtrace_asm.o: assym.s
+.if ${MACHINE_CPUARCH} == "arm"
+assym.o: assym.s
+ ${AS} -meabi=5 -o assym.o assym.s
+.endif
+
.include <bsd.kmod.mk>
CFLAGS+= -include ${SYSDIR}/cddl/compat/opensolaris/sys/debug_compat.h