aboutsummaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
Diffstat (limited to 'sys')
-rw-r--r--sys/amd64/amd64/exception.S109
-rw-r--r--sys/amd64/amd64/local_apic.c17
-rw-r--r--sys/amd64/amd64/trap.c75
-rw-r--r--sys/amd64/amd64/tsc.c20
-rw-r--r--sys/amd64/conf/GENERIC3
-rw-r--r--sys/amd64/include/param.h2
-rw-r--r--sys/arm/include/param.h4
-rw-r--r--sys/cddl/compat/opensolaris/kern/opensolaris.c101
-rw-r--r--sys/cddl/compat/opensolaris/kern/opensolaris_cmn_err.c64
-rw-r--r--sys/cddl/compat/opensolaris/kern/opensolaris_kmem.c22
-rw-r--r--sys/cddl/compat/opensolaris/sys/atomic.h6
-rw-r--r--sys/cddl/compat/opensolaris/sys/bitmap.h118
-rw-r--r--sys/cddl/compat/opensolaris/sys/cmn_err.h40
-rw-r--r--sys/cddl/compat/opensolaris/sys/cpupart.h38
-rw-r--r--sys/cddl/compat/opensolaris/sys/cpuvar.h136
-rw-r--r--sys/cddl/compat/opensolaris/sys/cpuvar_defs.h61
-rw-r--r--sys/cddl/compat/opensolaris/sys/cyclic.h79
-rw-r--r--sys/cddl/compat/opensolaris/sys/cyclic_impl.h304
-rw-r--r--sys/cddl/compat/opensolaris/sys/elf.h116
-rw-r--r--sys/cddl/compat/opensolaris/sys/feature_tests.h35
-rw-r--r--sys/cddl/compat/opensolaris/sys/kcondvar.h9
-rw-r--r--sys/cddl/compat/opensolaris/sys/kmem.h8
-rw-r--r--sys/cddl/compat/opensolaris/sys/lock.h3
-rw-r--r--sys/cddl/compat/opensolaris/sys/mman.h (renamed from sys/cddl/compat/opensolaris/machine/endian.h)31
-rw-r--r--sys/cddl/compat/opensolaris/sys/modctl.h38
-rw-r--r--sys/cddl/compat/opensolaris/sys/mutex.h9
-rw-r--r--sys/cddl/compat/opensolaris/sys/objfs.h35
-rw-r--r--sys/cddl/compat/opensolaris/sys/param.h37
-rw-r--r--sys/cddl/compat/opensolaris/sys/pcpu.h37
-rw-r--r--sys/cddl/compat/opensolaris/sys/proc.h2
-rw-r--r--sys/cddl/compat/opensolaris/sys/rwlock.h9
-rw-r--r--sys/cddl/compat/opensolaris/sys/sema.h40
-rw-r--r--sys/cddl/compat/opensolaris/sys/stat.h38
-rw-r--r--sys/cddl/compat/opensolaris/sys/time.h21
-rw-r--r--sys/cddl/compat/opensolaris/sys/types.h19
-rw-r--r--sys/cddl/compat/opensolaris/sys/uio.h2
-rw-r--r--sys/cddl/contrib/opensolaris/OPENSOLARIS.LICENSE384
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/ctf/ctf_mod.c177
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/ctf/ctf_subr.c96
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/dtrace/dtrace.c16449
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/dtrace/fasttrap.c2376
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/dtrace/lockstat.c341
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/dtrace/profile.c576
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/dtrace/sdt_subr.c888
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/dtrace/systrace.c373
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/arc.c37
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_zfetch.c6
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/refcount.c2
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa_misc.c2
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/refcount.h5
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c1
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/rpc/xdr.c18
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/sys/cmn_err.h128
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/sys/cpupart.h162
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/sys/cpuvar.h737
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/sys/ctf.h360
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/sys/ctf_api.h245
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/sys/debug.h6
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/sys/dtrace.h2309
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/sys/dtrace_impl.h1323
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/sys/fasttrap.h93
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/sys/fasttrap_impl.h205
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/sys/isa_defs.h58
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/sys/procset.h2
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/sys/sdt.h176
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/sys/vmem.h142
-rw-r--r--sys/cddl/contrib/opensolaris/uts/intel/sys/fasttrap_isa.h114
-rw-r--r--sys/cddl/contrib/opensolaris/uts/sparc/sys/fasttrap_isa.h94
-rw-r--r--sys/cddl/dev/cyclic/amd64/cyclic_machdep.c133
-rw-r--r--sys/cddl/dev/cyclic/cyclic.c1421
-rw-r--r--sys/cddl/dev/cyclic/cyclic_test.c301
-rw-r--r--sys/cddl/dev/cyclic/i386/cyclic_machdep.c133
-rw-r--r--sys/cddl/dev/dtmalloc/dtmalloc.c220
-rw-r--r--sys/cddl/dev/dtrace/amd64/dis_tables.c3193
-rw-r--r--sys/cddl/dev/dtrace/amd64/dis_tables.h112
-rw-r--r--sys/cddl/dev/dtrace/amd64/dtrace_asm.S573
-rw-r--r--sys/cddl/dev/dtrace/amd64/dtrace_isa.c612
-rw-r--r--sys/cddl/dev/dtrace/amd64/dtrace_subr.c507
-rw-r--r--sys/cddl/dev/dtrace/amd64/instr_size.c132
-rw-r--r--sys/cddl/dev/dtrace/dtrace_anon.c84
-rw-r--r--sys/cddl/dev/dtrace/dtrace_cddl.h134
-rw-r--r--sys/cddl/dev/dtrace/dtrace_clone.c61
-rw-r--r--sys/cddl/dev/dtrace/dtrace_debug.c596
-rw-r--r--sys/cddl/dev/dtrace/dtrace_hacks.c13
-rw-r--r--sys/cddl/dev/dtrace/dtrace_ioctl.c778
-rw-r--r--sys/cddl/dev/dtrace/dtrace_load.c160
-rw-r--r--sys/cddl/dev/dtrace/dtrace_modevent.c47
-rw-r--r--sys/cddl/dev/dtrace/dtrace_sysctl.c82
-rw-r--r--sys/cddl/dev/dtrace/dtrace_test.c73
-rw-r--r--sys/cddl/dev/dtrace/dtrace_unload.c127
-rw-r--r--sys/cddl/dev/dtrace/dtrace_vtime.c101
-rw-r--r--sys/cddl/dev/dtrace/i386/dis_tables.c3193
-rw-r--r--sys/cddl/dev/dtrace/i386/dis_tables.h112
-rw-r--r--sys/cddl/dev/dtrace/i386/dtrace_asm.S527
-rw-r--r--sys/cddl/dev/dtrace/i386/dtrace_isa.c622
-rw-r--r--sys/cddl/dev/dtrace/i386/dtrace_subr.c503
-rw-r--r--sys/cddl/dev/dtrace/i386/instr_size.c132
-rw-r--r--sys/cddl/dev/fasttrap/fasttrap.c2333
-rw-r--r--sys/cddl/dev/fbt/fbt.c1411
-rw-r--r--sys/cddl/dev/lockstat/lockstat.c289
-rw-r--r--sys/cddl/dev/profile/profile.c531
-rw-r--r--sys/cddl/dev/prototype.c144
-rw-r--r--sys/cddl/dev/sdt/sdt.c254
-rw-r--r--sys/cddl/dev/systrace/systrace.c341
-rw-r--r--sys/conf/Makefile.amd643
-rw-r--r--sys/conf/NOTES6
-rw-r--r--sys/conf/files7
-rw-r--r--sys/conf/kern.post.mk3
-rw-r--r--sys/conf/kern.pre.mk15
-rw-r--r--sys/conf/options2
-rw-r--r--sys/conf/options.amd641
-rw-r--r--sys/i386/conf/GENERIC3
-rw-r--r--sys/i386/i386/exception.s59
-rw-r--r--sys/i386/i386/local_apic.c17
-rw-r--r--sys/i386/i386/trap.c69
-rw-r--r--sys/i386/include/param.h2
-rw-r--r--sys/i386/linux/Makefile2
-rw-r--r--sys/i386/linux/syscalls.conf4
-rw-r--r--sys/ia64/include/param.h4
-rw-r--r--sys/kern/kern_ctf.c325
-rw-r--r--sys/kern/kern_dtrace.c108
-rw-r--r--sys/kern/kern_exec.c31
-rw-r--r--sys/kern/kern_exit.c29
-rw-r--r--sys/kern/kern_fork.c23
-rw-r--r--sys/kern/kern_linker.c59
-rw-r--r--sys/kern/kern_malloc.c63
-rw-r--r--sys/kern/kern_proc.c39
-rw-r--r--sys/kern/kern_sdt.c273
-rw-r--r--sys/kern/link_elf.c49
-rw-r--r--sys/kern/link_elf_obj.c55
-rw-r--r--sys/kern/linker_if.m20
-rw-r--r--sys/kern/makesyscalls.sh18
-rw-r--r--sys/kern/sched_4bsd.c16
-rw-r--r--sys/kern/sched_ule.c17
-rw-r--r--sys/kern/subr_smp.c115
-rw-r--r--sys/kern/systrace_args.c4717
-rw-r--r--sys/modules/Makefile21
-rw-r--r--sys/modules/cyclic/Makefile19
-rw-r--r--sys/modules/cyclic_test/Makefile16
-rw-r--r--sys/modules/dtrace/Makefile21
-rw-r--r--sys/modules/dtrace/Makefile.inc29
-rw-r--r--sys/modules/dtrace/dtmalloc/Makefile13
-rw-r--r--sys/modules/dtrace/dtrace/Makefile44
-rw-r--r--sys/modules/dtrace/dtrace_test/Makefile14
-rw-r--r--sys/modules/dtrace/dtraceall/Makefile8
-rw-r--r--sys/modules/dtrace/dtraceall/dtraceall.c73
-rw-r--r--sys/modules/dtrace/fasttrap/Makefile19
-rw-r--r--sys/modules/dtrace/fbt/Makefile13
-rw-r--r--sys/modules/dtrace/linsystrace/Makefile16
-rw-r--r--sys/modules/dtrace/lockstat/Makefile14
-rw-r--r--sys/modules/dtrace/profile/Makefile13
-rw-r--r--sys/modules/dtrace/prototype/Makefile13
-rw-r--r--sys/modules/dtrace/sdt/Makefile13
-rw-r--r--sys/modules/dtrace/systrace/Makefile13
-rw-r--r--sys/modules/opensolaris/Makefile23
-rw-r--r--sys/modules/zfs/Makefile61
-rw-r--r--sys/powerpc/include/param.h4
-rw-r--r--sys/sparc64/include/param.h2
-rw-r--r--sys/sun4v/include/param.h2
-rw-r--r--sys/sys/dtrace_bsd.h111
-rw-r--r--sys/sys/elf.h4
-rw-r--r--sys/sys/elf_common.h3
-rw-r--r--sys/sys/kdb.h1
-rw-r--r--sys/sys/kernel.h9
-rw-r--r--sys/sys/linker.h50
-rw-r--r--sys/sys/malloc.h16
-rw-r--r--sys/sys/param.h16
-rw-r--r--sys/sys/proc.h5
-rw-r--r--sys/sys/sdt.h179
-rw-r--r--sys/sys/smp.h6
-rw-r--r--sys/sys/stat.h8
-rw-r--r--sys/sys/sysent.h2
-rw-r--r--sys/sys/systm.h6
-rw-r--r--sys/sys/time.h25
-rw-r--r--sys/sys/types.h30
175 files changed, 56028 insertions, 619 deletions
diff --git a/sys/amd64/amd64/exception.S b/sys/amd64/amd64/exception.S
index b0c4ba134964..c473362099e3 100644
--- a/sys/amd64/amd64/exception.S
+++ b/sys/amd64/amd64/exception.S
@@ -1,8 +1,12 @@
/*-
* Copyright (c) 1989, 1990 William F. Jolitz.
* Copyright (c) 1990 The Regents of the University of California.
+ * Copyright (c) 2007 The FreeBSD Foundation
* All rights reserved.
*
+ * Portions of this software were developed by A. Joseph Koshy under
+ * sponsorship from the FreeBSD Foundation and Google, Inc.
+ *
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
@@ -32,6 +36,8 @@
#include "opt_atpic.h"
#include "opt_compat.h"
+#include "opt_hwpmc_hooks.h"
+#include "opt_kdtrace.h"
#include <machine/asmacros.h>
#include <machine/psl.h>
@@ -39,7 +45,25 @@
#include "assym.s"
+#ifdef KDTRACE_HOOKS
+ .bss
+ .globl dtrace_invop_jump_addr
+ .align 8
+ .type dtrace_invop_jump_addr, @object
+ .size dtrace_invop_jump_addr, 8
+dtrace_invop_jump_addr:
+ .zero 8
+ .globl dtrace_invop_calltrap_addr
+ .align 8
+ .type dtrace_invop_calltrap_addr, @object
+ .size dtrace_invop_calltrap_addr, 8
+dtrace_invop_calltrap_addr:
+ .zero 8
+#endif
.text
+#ifdef HWPMC_HOOKS
+ ENTRY(start_exceptions)
+#endif
/*****************************************************************************/
/* Trap handling */
@@ -162,6 +186,30 @@ alltraps_pushregs_no_rdi:
movq %r14,TF_R14(%rsp)
movq %r15,TF_R15(%rsp)
FAKE_MCOUNT(TF_RIP(%rsp))
+#ifdef KDTRACE_HOOKS
+ /*
+ * DTrace Function Boundary Trace (fbt) and Statically Defined
+ * Trace (sdt) probes are triggered by int3 (0xcc) which causes
+ * the #BP (T_BPTFLT) breakpoint interrupt. For all other trap
+ * types, just handle them in the usual way.
+ */
+ cmpq $T_BPTFLT,TF_TRAPNO(%rsp)
+ jne calltrap
+
+ /* Check if there is no DTrace hook registered. */
+ cmpq $0,dtrace_invop_jump_addr
+ je calltrap
+
+ /*
+ * Set our jump address for the jump back in the event that
+ * the breakpoint wasn't caused by DTrace at all.
+ */
+ movq $calltrap, dtrace_invop_calltrap_addr(%rip)
+
+ /* Jump to the code hooked in by DTrace. */
+ movq dtrace_invop_jump_addr, %rax
+ jmpq *dtrace_invop_jump_addr
+#endif
.globl calltrap
.type calltrap,@function
calltrap:
@@ -348,6 +396,9 @@ IDTVEC(fast_syscall32)
* execute the NMI handler with interrupts disabled to prevent a
* nested interrupt from executing an 'iretq' instruction and
* inadvertently taking the processor out of NMI mode.
+ *
+ * Third, the NMI handler runs on its own stack (tss_ist1), shared
+ * with the double fault handler.
*/
IDTVEC(nmi)
@@ -386,6 +437,61 @@ nmi_calltrap:
movq %rsp, %rdi
call trap
MEXITCOUNT
+#ifdef HWPMC_HOOKS
+ /*
+ * Check if the current trap was from user mode and if so
+ * whether the current thread needs a user call chain to be
+ * captured. We are still in NMI mode at this point.
+ */
+ testb $SEL_RPL_MASK,TF_CS(%rsp)
+ jz nocallchain
+ movq PCPU(CURTHREAD),%rax /* curthread present? */
+ orq %rax,%rax
+ jz nocallchain
+ testl $TDP_CALLCHAIN,TD_PFLAGS(%rax) /* flagged for capture? */
+ jz nocallchain
+ /*
+ * A user callchain is to be captured, so:
+ * - Move execution to the regular kernel stack, to allow for
+ * nested NMI interrupts.
+ * - Take the processor out of "NMI" mode by faking an "iret".
+ * - Enable interrupts, so that copyin() can work.
+ */
+ movq %rsp,%rsi /* source stack pointer */
+ movq $TF_SIZE,%rcx
+ movq PCPU(RSP0),%rbx
+ subq %rcx,%rbx
+ movq %rbx,%rdi /* destination stack pointer */
+
+ shrq $3,%rcx /* trap frame size in long words */
+ cld
+ rep
+ movsq /* copy trapframe */
+
+ movl %ss,%eax
+ pushq %rax /* tf_ss */
+ pushq %rbx /* tf_rsp (on kernel stack) */
+ pushfq /* tf_rflags */
+ movl %cs,%eax
+ pushq %rax /* tf_cs */
+ pushq $outofnmi /* tf_rip */
+ iretq
+outofnmi:
+ /*
+ * At this point the processor has exited NMI mode and is running
+ * with interrupts turned off on the normal kernel stack.
+ * We turn interrupts back on, and take the usual 'doreti' exit
+ * path.
+ *
+ * If a pending NMI gets recognized at or after this point, it
+ * will cause a kernel callchain to be traced. Since this path
+ * is only taken for NMI interrupts from user space, our `swapgs'
+ * state is correct for taking the doreti path.
+ */
+ sti
+ jmp doreti
+nocallchain:
+#endif
testl %ebx,%ebx
jz nmi_restoreregs
swapgs
@@ -553,3 +659,6 @@ doreti_iret_fault:
movq $0,TF_ADDR(%rsp)
FAKE_MCOUNT(TF_RIP(%rsp))
jmp calltrap
+#ifdef HWPMC_HOOKS
+ ENTRY(end_exceptions)
+#endif
diff --git a/sys/amd64/amd64/local_apic.c b/sys/amd64/amd64/local_apic.c
index 9a3ec2a834ee..47bb78dadf56 100644
--- a/sys/amd64/amd64/local_apic.c
+++ b/sys/amd64/amd64/local_apic.c
@@ -35,6 +35,7 @@
__FBSDID("$FreeBSD$");
#include "opt_hwpmc_hooks.h"
+#include "opt_kdtrace.h"
#include "opt_ddb.h"
@@ -65,6 +66,11 @@ __FBSDID("$FreeBSD$");
#include <ddb/ddb.h>
#endif
+#ifdef KDTRACE_HOOKS
+#include <sys/dtrace_bsd.h>
+cyclic_clock_func_t lapic_cyclic_clock_func[MAXCPU];
+#endif
+
/* Sanity checks on IDT vectors. */
CTASSERT(APIC_IO_INTS + APIC_NUM_IOINTS == APIC_TIMER_INT);
CTASSERT(APIC_TIMER_INT < APIC_LOCAL_INTS);
@@ -668,6 +674,17 @@ lapic_handle_timer(struct trapframe *frame)
(*la->la_timer_count)++;
critical_enter();
+#ifdef KDTRACE_HOOKS
+ /*
+ * If the DTrace hooks are configured and a callback function
+ * has been registered, then call it to process the high speed
+ * timers.
+ */
+ int cpu = PCPU_GET(cpuid);
+ if (lapic_cyclic_clock_func[cpu] != NULL)
+ (*lapic_cyclic_clock_func[cpu])(frame);
+#endif
+
/* Fire hardclock at hz. */
la->la_hard_ticks += hz;
if (la->la_hard_ticks >= lapic_timer_hz) {
diff --git a/sys/amd64/amd64/trap.c b/sys/amd64/amd64/trap.c
index f7a2d1ac63e4..6b083415d50c 100644
--- a/sys/amd64/amd64/trap.c
+++ b/sys/amd64/amd64/trap.c
@@ -49,6 +49,7 @@ __FBSDID("$FreeBSD$");
#include "opt_hwpmc_hooks.h"
#include "opt_isa.h"
#include "opt_kdb.h"
+#include "opt_kdtrace.h"
#include "opt_ktrace.h"
#include <sys/param.h>
@@ -94,6 +95,24 @@ __FBSDID("$FreeBSD$");
#endif
#include <machine/tss.h>
+#ifdef KDTRACE_HOOKS
+#include <sys/dtrace_bsd.h>
+
+/*
+ * This is a hook which is initialised by the dtrace module
+ * to handle traps which might occur during DTrace probe
+ * execution.
+ */
+dtrace_trap_func_t dtrace_trap_func;
+
+/*
+ * This is a hook which is initialised by the systrace module
+ * when it is loaded. This keeps the DTrace syscall provider
+ * implementation opaque.
+ */
+systrace_probe_func_t systrace_probe_func;
+#endif
+
extern void trap(struct trapframe *frame);
extern void syscall(struct trapframe *frame);
void dblfault_handler(struct trapframe *frame);
@@ -195,11 +214,28 @@ trap(struct trapframe *frame)
* the NMI was handled by it and we can return immediately.
*/
if (type == T_NMI && pmc_intr &&
- (*pmc_intr)(PCPU_GET(cpuid), (uintptr_t) frame->tf_rip,
- TRAPF_USERMODE(frame)))
+ (*pmc_intr)(PCPU_GET(cpuid), frame))
goto out;
#endif
+#ifdef KDTRACE_HOOKS
+ /*
+ * 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 the probe, the no-fault
+ * flag is cleared and finally re-scheduling is enabled.
+ *
+ * If the DTrace kernel module has registered a trap handler,
+ * call it and if it returns non-zero, assume that it has
+ * handled the trap and modified the trap frame so that this
+ * function can return normally.
+ */
+ if (dtrace_trap_func != NULL)
+ if ((*dtrace_trap_func)(frame, type))
+ goto out;
+#endif
+
if ((frame->tf_rflags & PSL_I) == 0) {
/*
* Buggy application or kernel code has disabled
@@ -211,7 +247,7 @@ trap(struct trapframe *frame)
if (ISPL(frame->tf_cs) == SEL_UPL)
printf(
"pid %ld (%s): trap %d with interrupts disabled\n",
- (long)curproc->p_pid, curproc->p_comm, type);
+ (long)curproc->p_pid, curthread->td_name, type);
else if (type != T_NMI && type != T_BPTFLT &&
type != T_TRCTRAP) {
/*
@@ -708,8 +744,8 @@ trap_fatal(frame, eva)
printf("current process = ");
if (curproc) {
printf("%lu (%s)\n",
- (u_long)curproc->p_pid, curproc->p_comm ?
- curproc->p_comm : "");
+ (u_long)curproc->p_pid, curthread->td_name ?
+ curthread->td_name : "");
} else {
printf("Idle\n");
}
@@ -836,7 +872,7 @@ syscall(struct trapframe *frame)
#endif
CTR4(KTR_SYSC, "syscall enter thread %p pid %d proc %s code %d", td,
- td->td_proc->p_pid, td->td_proc->p_comm, code);
+ td->td_proc->p_pid, td->td_name, code);
td->td_syscalls++;
@@ -848,9 +884,34 @@ syscall(struct trapframe *frame)
PTRACESTOP_SC(p, td, S_PT_SCE);
+#ifdef KDTRACE_HOOKS
+ /*
+ * If the systrace module has registered it's probe
+ * callback and if there is a probe active for the
+ * syscall 'entry', process the probe.
+ */
+ if (systrace_probe_func != NULL && callp->sy_entry != 0)
+ (*systrace_probe_func)(callp->sy_entry, code, callp,
+ args);
+#endif
+
AUDIT_SYSCALL_ENTER(code, td);
error = (*callp->sy_call)(td, argp);
AUDIT_SYSCALL_EXIT(error, td);
+
+ /* Save the latest error return value. */
+ td->td_errno = error;
+
+#ifdef KDTRACE_HOOKS
+ /*
+ * If the systrace module has registered it's probe
+ * callback and if there is a probe active for the
+ * syscall 'return', process the probe.
+ */
+ if (systrace_probe_func != NULL && callp->sy_return != 0)
+ (*systrace_probe_func)(callp->sy_return, code, callp,
+ args);
+#endif
}
switch (error) {
@@ -918,7 +979,7 @@ syscall(struct trapframe *frame)
userret(td, frame);
CTR4(KTR_SYSC, "syscall exit thread %p pid %d proc %s code %d", td,
- td->td_proc->p_pid, td->td_proc->p_comm, code);
+ td->td_proc->p_pid, td->td_name, code);
#ifdef KTRACE
if (KTRPOINT(td, KTR_SYSRET))
diff --git a/sys/amd64/amd64/tsc.c b/sys/amd64/amd64/tsc.c
index 3c66a3355da4..416a4a795dfe 100644
--- a/sys/amd64/amd64/tsc.c
+++ b/sys/amd64/amd64/tsc.c
@@ -28,6 +28,7 @@
__FBSDID("$FreeBSD$");
#include "opt_clock.h"
+#include "opt_kdtrace.h"
#include <sys/param.h>
#include <sys/bus.h>
@@ -44,6 +45,10 @@ __FBSDID("$FreeBSD$");
#include <machine/md_var.h>
#include <machine/specialreg.h>
+#ifdef KDTRACE_HOOKS
+#include <sys/dtrace_bsd.h>
+#endif
+
#include "cpufreq_if.h"
uint64_t tsc_freq;
@@ -220,3 +225,18 @@ tsc_get_timecount(struct timecounter *tc)
{
return (rdtsc());
}
+
+#ifdef KDTRACE_HOOKS
+/*
+ * 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()
+{
+ return (rdtsc() * (uint64_t) 1000000000 / tsc_freq);
+}
+#endif
diff --git a/sys/amd64/conf/GENERIC b/sys/amd64/conf/GENERIC
index 57c7eb4f0644..d69ed146997e 100644
--- a/sys/amd64/conf/GENERIC
+++ b/sys/amd64/conf/GENERIC
@@ -64,6 +64,9 @@ options KBD_INSTALL_CDEV # install a CDEV entry in /dev
options ADAPTIVE_GIANT # Giant mutex is adaptive.
options STOP_NMI # Stop CPUS using NMI instead of IPI
options AUDIT # Security event auditing
+options PRINTF_BUFR_SIZE=128 # Prevent printf output being interspersed.
+options KDTRACE_FRAME # Ensure frames are compiled in
+options KDTRACE_HOOKS # Kernel DTrace hooks
# Make an SMP-capable kernel by default
options SMP # Symmetric MultiProcessor Kernel
diff --git a/sys/amd64/include/param.h b/sys/amd64/include/param.h
index b1b90f467658..3c1f974b9fe3 100644
--- a/sys/amd64/include/param.h
+++ b/sys/amd64/include/param.h
@@ -79,7 +79,7 @@
#define MACHINE_ARCH "amd64"
#endif
-#ifdef SMP
+#if defined(SMP) || defined(KLD_MODULE)
#define MAXCPU 16
#else
#define MAXCPU 1
diff --git a/sys/arm/include/param.h b/sys/arm/include/param.h
index 4c59eee53c6d..92affb6c9be0 100644
--- a/sys/arm/include/param.h
+++ b/sys/arm/include/param.h
@@ -72,8 +72,8 @@
#endif
#define MID_MACHINE MID_ARM6
-#ifdef SMP
-#define MAXCPU 2
+#if defined(SMP) || defined(KLD_MODULE)
+#define MAXCPU 2
#else
#define MAXCPU 1
#endif /* SMP */
diff --git a/sys/cddl/compat/opensolaris/kern/opensolaris.c b/sys/cddl/compat/opensolaris/kern/opensolaris.c
new file mode 100644
index 000000000000..f1f84fb570a4
--- /dev/null
+++ b/sys/cddl/compat/opensolaris/kern/opensolaris.c
@@ -0,0 +1,101 @@
+/*-
+ * Copyright 2007 John Birrell <jb@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ *
+ */
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+#include <sys/conf.h>
+#include <sys/cpuvar.h>
+#include <sys/errno.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+
+cpu_core_t cpu_core[MAXCPU];
+kmutex_t cpu_lock;
+solaris_cpu_t solaris_cpu[MAXCPU];
+
+/*
+ * OpenSolaris subsystem initialisation.
+ */
+static void
+opensolaris_load(void *dummy)
+{
+ int i;
+
+ printf("This module (opensolaris) contains code covered by the\n");
+ printf("Common Development and Distribution License (CDDL)\n");
+ printf("see http://opensolaris.org/os/licensing/opensolaris_license/\n");
+
+ /*
+ * "Enable" all CPUs even though they may not exist just so
+ * that the asserts work. On FreeBSD, if a CPU exists, it is
+ * enabled.
+ */
+ for (i = 0; i < MAXCPU; i++) {
+ solaris_cpu[i].cpuid = i;
+ solaris_cpu[i].cpu_flags &= CPU_ENABLE;
+ }
+
+ mutex_init(&cpu_lock, "OpenSolaris CPU lock", MUTEX_DEFAULT, NULL);
+}
+
+SYSINIT(opensolaris_register, SI_SUB_OPENSOLARIS, SI_ORDER_FIRST, opensolaris_load, NULL);
+
+static void
+opensolaris_unload(void)
+{
+ mutex_destroy(&cpu_lock);
+}
+
+SYSUNINIT(opensolaris_unregister, SI_SUB_OPENSOLARIS, SI_ORDER_FIRST, opensolaris_unload, NULL);
+
+static int
+opensolaris_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);
+}
+
+DEV_MODULE(opensolaris, opensolaris_modevent, NULL);
+MODULE_VERSION(opensolaris, 1);
diff --git a/sys/cddl/compat/opensolaris/kern/opensolaris_cmn_err.c b/sys/cddl/compat/opensolaris/kern/opensolaris_cmn_err.c
new file mode 100644
index 000000000000..665ef53bf3a3
--- /dev/null
+++ b/sys/cddl/compat/opensolaris/kern/opensolaris_cmn_err.c
@@ -0,0 +1,64 @@
+/*
+ * 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$
+ *
+ */
+
+#include <sys/cmn_err.h>
+
+void
+vcmn_err(int ce, const char *fmt, va_list adx)
+{
+ char buf[256];
+
+ switch (ce) {
+ case CE_CONT:
+ snprintf(buf, sizeof(buf), "Solaris(cont): %s\n", fmt);
+ break;
+ case CE_NOTE:
+ snprintf(buf, sizeof(buf), "Solaris: NOTICE: %s\n", fmt);
+ break;
+ case CE_WARN:
+ snprintf(buf, sizeof(buf), "Solaris: WARNING: %s\n", fmt);
+ break;
+ case CE_PANIC:
+ snprintf(buf, sizeof(buf), "Solaris(panic): %s\n", fmt);
+ break;
+ case CE_IGNORE:
+ break;
+ default:
+ panic("Solaris: unknown severity level");
+ }
+ if (ce == CE_PANIC)
+ panic(buf);
+ if (ce != CE_IGNORE)
+ vprintf(buf, adx);
+}
+
+void
+cmn_err(int type, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vcmn_err(type, fmt, ap);
+ va_end(ap);
+}
diff --git a/sys/cddl/compat/opensolaris/kern/opensolaris_kmem.c b/sys/cddl/compat/opensolaris/kern/opensolaris_kmem.c
index a73349f583e7..cfbad3d75739 100644
--- a/sys/cddl/compat/opensolaris/kern/opensolaris_kmem.c
+++ b/sys/cddl/compat/opensolaris/kern/opensolaris_kmem.c
@@ -40,6 +40,8 @@ __FBSDID("$FreeBSD$");
#include <vm/vm_kern.h>
#include <vm/vm_map.h>
+#define KMEM_DEBUG
+
#ifdef KMEM_DEBUG
#include <sys/queue.h>
#include <sys/stack.h>
@@ -93,6 +95,10 @@ void
zfs_kmem_free(void *buf, size_t size __unused)
{
#ifdef KMEM_DEBUG
+ if (buf == NULL) {
+ printf("%s: attempt to free NULL\n",__func__);
+ return;
+ }
struct kmem_item *i;
buf = (u_char *)buf - sizeof(struct kmem_item);
@@ -108,18 +114,18 @@ zfs_kmem_free(void *buf, size_t size __unused)
free(buf, M_SOLARIS);
}
-u_long
+uint64_t
kmem_size(void)
{
- return ((u_long)vm_kmem_size);
+ return ((uint64_t)vm_kmem_size);
}
-u_long
+uint64_t
kmem_used(void)
{
- return ((u_long)kmem_map->size);
+ return ((uint64_t)kmem_map->size);
}
static int
@@ -200,7 +206,6 @@ kmem_cache_free(kmem_cache_t *cache, void *buf)
}
#ifdef _KERNEL
-extern void zone_drain(uma_zone_t zone);
void
kmem_cache_reap_now(kmem_cache_t *cache)
{
@@ -237,7 +242,8 @@ calloc(size_t n, size_t s)
}
#ifdef KMEM_DEBUG
-static void
+void kmem_show(void *);
+void
kmem_show(void *dummy __unused)
{
struct kmem_item *i;
@@ -249,12 +255,10 @@ kmem_show(void *dummy __unused)
printf("KMEM_DEBUG: Leaked elements:\n\n");
LIST_FOREACH(i, &kmem_items, next) {
printf("address=%p\n", i);
- stack_print(&i->stack);
- printf("\n");
}
}
mtx_unlock(&kmem_items_mtx);
}
-SYSUNINIT(sol_kmem, SI_SUB_DRIVERS, SI_ORDER_FIRST, kmem_show, NULL);
+SYSUNINIT(sol_kmem, SI_SUB_CPU, SI_ORDER_FIRST, kmem_show, NULL);
#endif /* KMEM_DEBUG */
diff --git a/sys/cddl/compat/opensolaris/sys/atomic.h b/sys/cddl/compat/opensolaris/sys/atomic.h
index 2559415da3fa..17aa0971caa7 100644
--- a/sys/cddl/compat/opensolaris/sys/atomic.h
+++ b/sys/cddl/compat/opensolaris/sys/atomic.h
@@ -32,6 +32,10 @@
#include <sys/types.h>
#include <machine/atomic.h>
+#define casptr(_a, _b, _c) \
+ atomic_cmpset_ptr((volatile uintptr_t *)(_a), (uintptr_t)(_b), (uintptr_t) (_c))
+#define cas32 atomic_cmpset_32
+
#ifndef __LP64__
extern void atomic_add_64(volatile uint64_t *target, int64_t delta);
extern void *atomic_cas_ptr(volatile void *target, void *cmp, void *newval);
@@ -106,7 +110,7 @@ atomic_inc_64_nv(volatile uint64_t *target)
static __inline void *
atomic_cas_ptr(volatile void *target, void *cmp, void *newval)
{
- return ((void *)atomic_cas_64((uint64_t *)target, (uint64_t)cmp,
+ return ((void *)atomic_cas_64((volatile uint64_t *)target, (uint64_t)cmp,
(uint64_t)newval));
}
#endif
diff --git a/sys/cddl/compat/opensolaris/sys/bitmap.h b/sys/cddl/compat/opensolaris/sys/bitmap.h
new file mode 100644
index 000000000000..f3130dfb822f
--- /dev/null
+++ b/sys/cddl/compat/opensolaris/sys/bitmap.h
@@ -0,0 +1,118 @@
+/*
+ * 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$
+ */
+
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+#ifndef _COMPAT_OPENSOLARIS_SYS_BITMAP_H
+#define _COMPAT_OPENSOLARIS_SYS_BITMAP_H
+
+#include <sys/atomic.h>
+
+/*
+ * Operations on bitmaps of arbitrary size
+ * A bitmap is a vector of 1 or more ulong_t's.
+ * The user of the package is responsible for range checks and keeping
+ * track of sizes.
+ */
+
+#ifdef _LP64
+#define BT_ULSHIFT 6 /* log base 2 of BT_NBIPUL, to extract word index */
+#define BT_ULSHIFT32 5 /* log base 2 of BT_NBIPUL, to extract word index */
+#else
+#define BT_ULSHIFT 5 /* log base 2 of BT_NBIPUL, to extract word index */
+#endif
+
+#define BT_NBIPUL (1 << BT_ULSHIFT) /* n bits per ulong_t */
+#define BT_ULMASK (BT_NBIPUL - 1) /* to extract bit index */
+
+#ifdef _LP64
+#define BT_NBIPUL32 (1 << BT_ULSHIFT32) /* n bits per ulong_t */
+#define BT_ULMASK32 (BT_NBIPUL32 - 1) /* to extract bit index */
+#define BT_ULMAXMASK 0xffffffffffffffff /* used by bt_getlowbit */
+#else
+#define BT_ULMAXMASK 0xffffffff
+#endif
+
+/*
+ * bitmap is a ulong_t *, bitindex an index_t
+ *
+ * The macros BT_WIM and BT_BIW internal; there is no need
+ * for users of this package to use them.
+ */
+
+/*
+ * word in map
+ */
+#define BT_WIM(bitmap, bitindex) \
+ ((bitmap)[(bitindex) >> BT_ULSHIFT])
+/*
+ * bit in word
+ */
+#define BT_BIW(bitindex) \
+ (1UL << ((bitindex) & BT_ULMASK))
+
+#ifdef _LP64
+#define BT_WIM32(bitmap, bitindex) \
+ ((bitmap)[(bitindex) >> BT_ULSHIFT32])
+
+#define BT_BIW32(bitindex) \
+ (1UL << ((bitindex) & BT_ULMASK32))
+#endif
+
+/*
+ * These are public macros
+ *
+ * BT_BITOUL == n bits to n ulong_t's
+ */
+#define BT_BITOUL(nbits) \
+ (((nbits) + BT_NBIPUL - 1l) / BT_NBIPUL)
+#define BT_SIZEOFMAP(nbits) \
+ (BT_BITOUL(nbits) * sizeof (ulong_t))
+#define BT_TEST(bitmap, bitindex) \
+ ((BT_WIM((bitmap), (bitindex)) & BT_BIW(bitindex)) ? 1 : 0)
+#define BT_SET(bitmap, bitindex) \
+ { BT_WIM((bitmap), (bitindex)) |= BT_BIW(bitindex); }
+#define BT_CLEAR(bitmap, bitindex) \
+ { BT_WIM((bitmap), (bitindex)) &= ~BT_BIW(bitindex); }
+
+#ifdef _LP64
+#define BT_BITOUL32(nbits) \
+ (((nbits) + BT_NBIPUL32 - 1l) / BT_NBIPUL32)
+#define BT_SIZEOFMAP32(nbits) \
+ (BT_BITOUL32(nbits) * sizeof (uint_t))
+#define BT_TEST32(bitmap, bitindex) \
+ ((BT_WIM32((bitmap), (bitindex)) & BT_BIW32(bitindex)) ? 1 : 0)
+#define BT_SET32(bitmap, bitindex) \
+ { BT_WIM32((bitmap), (bitindex)) |= BT_BIW32(bitindex); }
+#define BT_CLEAR32(bitmap, bitindex) \
+ { BT_WIM32((bitmap), (bitindex)) &= ~BT_BIW32(bitindex); }
+#endif /* _LP64 */
+
+#endif /* _COMPAT_OPENSOLARIS_SYS_BITMAP_H */
diff --git a/sys/cddl/compat/opensolaris/sys/cmn_err.h b/sys/cddl/compat/opensolaris/sys/cmn_err.h
index b9987e8c75a9..9979a3f042c4 100644
--- a/sys/cddl/compat/opensolaris/sys/cmn_err.h
+++ b/sys/cddl/compat/opensolaris/sys/cmn_err.h
@@ -44,44 +44,8 @@ extern "C" {
#define CE_PANIC 3 /* panic */
#define CE_IGNORE 4 /* print nothing */
-static __inline void
-vcmn_err(int ce, const char *fmt, va_list adx)
-{
- char buf[256];
-
- switch (ce) {
- case CE_CONT:
- snprintf(buf, sizeof(buf), "ZFS(cont): %s\n", fmt);
- break;
- case CE_NOTE:
- snprintf(buf, sizeof(buf), "ZFS: NOTICE: %s\n", fmt);
- break;
- case CE_WARN:
- snprintf(buf, sizeof(buf), "ZFS: WARNING: %s\n", fmt);
- break;
- case CE_PANIC:
- snprintf(buf, sizeof(buf), "ZFS(panic): %s\n", fmt);
- break;
- case CE_IGNORE:
- break;
- default:
- panic("unknown severity level");
- }
- if (ce != CE_IGNORE)
- vprintf(buf, adx);
- if (ce == CE_PANIC)
- panic("ZFS");
-}
-
-static __inline void
-cmn_err(int ce, const char *fmt, ...)
-{
- va_list adx;
-
- va_start(adx, fmt);
- vcmn_err(ce, fmt, adx);
- va_end(adx);
-}
+void cmn_err(int, const char *, ...);
+void vcmn_err(int, const char *, va_list);
#ifdef __cplusplus
}
diff --git a/sys/cddl/compat/opensolaris/sys/cpupart.h b/sys/cddl/compat/opensolaris/sys/cpupart.h
new file mode 100644
index 000000000000..560339b0b35e
--- /dev/null
+++ b/sys/cddl/compat/opensolaris/sys/cpupart.h
@@ -0,0 +1,38 @@
+/*
+ * 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$
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _COMPAT_OPENSOLARIS_SYS_CPUPART_H
+#define _COMPAT_OPENSOLARIS_SYS_CPUPART_H
+
+typedef int cpupartid_t;
+
+typedef struct cpupart {
+ cpupartid_t cp_id; /* partition ID */
+} cpupart_t;
+
+#endif /* _COMPAT_OPENSOLARIS_SYS_CPUPART_H */
diff --git a/sys/cddl/compat/opensolaris/sys/cpuvar.h b/sys/cddl/compat/opensolaris/sys/cpuvar.h
new file mode 100644
index 000000000000..df0af5c41f14
--- /dev/null
+++ b/sys/cddl/compat/opensolaris/sys/cpuvar.h
@@ -0,0 +1,136 @@
+/*
+ * 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$
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _COMPAT_OPENSOLARIS_SYS_CPUVAR_H
+#define _COMPAT_OPENSOLARIS_SYS_CPUVAR_H
+
+#include <sys/mutex.h>
+#include <sys/cpuvar_defs.h>
+
+#ifdef _KERNEL
+
+struct cyc_cpu;
+
+typedef struct {
+ int cpuid;
+ struct cyc_cpu *cpu_cyclic;
+ uint32_t cpu_flags;
+ uint_t cpu_intr_actv;
+ uintptr_t cpu_profile_pc;
+ uintptr_t cpu_profile_upc;
+ uintptr_t cpu_dtrace_caller; /* DTrace: caller, if any */
+ hrtime_t cpu_dtrace_chillmark; /* DTrace: chill mark time */
+ hrtime_t cpu_dtrace_chilled; /* DTrace: total chill time */
+} solaris_cpu_t;
+
+/* Some code may choose to redefine this if pcpu_t would be more useful. */
+#define cpu_t solaris_cpu_t
+#define cpu_id cpuid
+
+extern solaris_cpu_t solaris_cpu[];
+
+#define CPU_CACHE_COHERENCE_SIZE 64
+
+/*
+ * The cpu_core structure consists of per-CPU state available in any context.
+ * On some architectures, this may mean that the page(s) containing the
+ * NCPU-sized array of cpu_core structures must be locked in the TLB -- it
+ * is up to the platform to assure that this is performed properly. Note that
+ * the structure is sized to avoid false sharing.
+ */
+#define CPUC_SIZE (sizeof (uint16_t) + sizeof (uintptr_t) + \
+ sizeof (kmutex_t))
+#define CPUC_PADSIZE CPU_CACHE_COHERENCE_SIZE - CPUC_SIZE
+
+typedef struct cpu_core {
+ uint16_t cpuc_dtrace_flags; /* DTrace flags */
+ uint8_t cpuc_pad[CPUC_PADSIZE]; /* padding */
+ uintptr_t cpuc_dtrace_illval; /* DTrace illegal value */
+ kmutex_t cpuc_pid_lock; /* DTrace pid provider lock */
+} cpu_core_t;
+
+extern cpu_core_t cpu_core[];
+
+extern kmutex_t cpu_lock;
+#endif /* _KERNEL */
+
+/*
+ * Flags in the CPU structure.
+ *
+ * These are protected by cpu_lock (except during creation).
+ *
+ * Offlined-CPUs have three stages of being offline:
+ *
+ * CPU_ENABLE indicates that the CPU is participating in I/O interrupts
+ * that can be directed at a number of different CPUs. If CPU_ENABLE
+ * is off, the CPU will not be given interrupts that can be sent elsewhere,
+ * but will still get interrupts from devices associated with that CPU only,
+ * and from other CPUs.
+ *
+ * CPU_OFFLINE indicates that the dispatcher should not allow any threads
+ * other than interrupt threads to run on that CPU. A CPU will not have
+ * CPU_OFFLINE set if there are any bound threads (besides interrupts).
+ *
+ * CPU_QUIESCED is set if p_offline was able to completely turn idle the
+ * CPU and it will not have to run interrupt threads. In this case it'll
+ * stay in the idle loop until CPU_QUIESCED is turned off.
+ *
+ * CPU_FROZEN is used only by CPR to mark CPUs that have been successfully
+ * suspended (in the suspend path), or have yet to be resumed (in the resume
+ * case).
+ *
+ * On some platforms CPUs can be individually powered off.
+ * The following flags are set for powered off CPUs: CPU_QUIESCED,
+ * CPU_OFFLINE, and CPU_POWEROFF. The following flags are cleared:
+ * CPU_RUNNING, CPU_READY, CPU_EXISTS, and CPU_ENABLE.
+ */
+#define CPU_RUNNING 0x001 /* CPU running */
+#define CPU_READY 0x002 /* CPU ready for cross-calls */
+#define CPU_QUIESCED 0x004 /* CPU will stay in idle */
+#define CPU_EXISTS 0x008 /* CPU is configured */
+#define CPU_ENABLE 0x010 /* CPU enabled for interrupts */
+#define CPU_OFFLINE 0x020 /* CPU offline via p_online */
+#define CPU_POWEROFF 0x040 /* CPU is powered off */
+#define CPU_FROZEN 0x080 /* CPU is frozen via CPR suspend */
+#define CPU_SPARE 0x100 /* CPU offline available for use */
+#define CPU_FAULTED 0x200 /* CPU offline diagnosed faulty */
+
+typedef enum {
+ CPU_INIT,
+ CPU_CONFIG,
+ CPU_UNCONFIG,
+ CPU_ON,
+ CPU_OFF,
+ CPU_CPUPART_IN,
+ CPU_CPUPART_OUT
+} cpu_setup_t;
+
+typedef int cpu_setup_func_t(cpu_setup_t, int, void *);
+
+
+#endif /* _COMPAT_OPENSOLARIS_SYS_CPUVAR_H */
diff --git a/sys/cddl/compat/opensolaris/sys/cpuvar_defs.h b/sys/cddl/compat/opensolaris/sys/cpuvar_defs.h
new file mode 100644
index 000000000000..af0947ba8ad6
--- /dev/null
+++ b/sys/cddl/compat/opensolaris/sys/cpuvar_defs.h
@@ -0,0 +1,61 @@
+/*
+ * 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$
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _COMPAT_OPENSOLARIS_SYS_CPUVAR_DEFS_H
+#define _COMPAT_OPENSOLARIS_SYS_CPUVAR_DEFS_H
+
+/*
+ * DTrace flags.
+ */
+#define CPU_DTRACE_NOFAULT 0x0001 /* Don't fault */
+#define CPU_DTRACE_DROP 0x0002 /* Drop this ECB */
+#define CPU_DTRACE_BADADDR 0x0004 /* DTrace fault: bad address */
+#define CPU_DTRACE_BADALIGN 0x0008 /* DTrace fault: bad alignment */
+#define CPU_DTRACE_DIVZERO 0x0010 /* DTrace fault: divide by zero */
+#define CPU_DTRACE_ILLOP 0x0020 /* DTrace fault: illegal operation */
+#define CPU_DTRACE_NOSCRATCH 0x0040 /* DTrace fault: out of scratch */
+#define CPU_DTRACE_KPRIV 0x0080 /* DTrace fault: bad kernel access */
+#define CPU_DTRACE_UPRIV 0x0100 /* DTrace fault: bad user access */
+#define CPU_DTRACE_TUPOFLOW 0x0200 /* DTrace fault: tuple stack overflow */
+#if defined(__sparc)
+#define CPU_DTRACE_FAKERESTORE 0x0400 /* pid provider hint to getreg */
+#endif
+#define CPU_DTRACE_ENTRY 0x0800 /* pid provider hint to ustack() */
+#define CPU_DTRACE_BADSTACK 0x1000 /* DTrace fault: bad stack */
+
+#define CPU_DTRACE_FAULT (CPU_DTRACE_BADADDR | CPU_DTRACE_BADALIGN | \
+ CPU_DTRACE_DIVZERO | CPU_DTRACE_ILLOP | \
+ CPU_DTRACE_NOSCRATCH | CPU_DTRACE_KPRIV | \
+ CPU_DTRACE_UPRIV | CPU_DTRACE_TUPOFLOW | \
+ CPU_DTRACE_BADSTACK)
+#define CPU_DTRACE_ERROR (CPU_DTRACE_FAULT | CPU_DTRACE_DROP)
+
+#define PANICSTKSIZE 8192
+#define REGSIZE 256
+
+#endif /* _COMPAT_OPENSOLARIS_SYS_CPUVAR_DEFS_H */
diff --git a/sys/cddl/compat/opensolaris/sys/cyclic.h b/sys/cddl/compat/opensolaris/sys/cyclic.h
new file mode 100644
index 000000000000..fced5dfb43b5
--- /dev/null
+++ b/sys/cddl/compat/opensolaris/sys/cyclic.h
@@ -0,0 +1,79 @@
+/*
+ * 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 (c) 1999-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef _COMPAT_OPENSOLARIS_SYS_CYCLIC_H_
+#define _COMPAT_OPENSOLARIS_SYS_CYCLIC_H_
+
+#ifndef _KERNEL
+typedef void cpu_t;
+#endif
+
+
+#ifndef _ASM
+#include <sys/time.h>
+#include <sys/cpuvar.h>
+#endif /* !_ASM */
+
+#ifndef _ASM
+
+typedef uintptr_t cyclic_id_t;
+typedef int cyc_index_t;
+typedef uint16_t cyc_level_t;
+typedef void (*cyc_func_t)(void *);
+typedef void *cyb_arg_t;
+
+#define CYCLIC_NONE ((cyclic_id_t)0)
+
+typedef struct cyc_handler {
+ cyc_func_t cyh_func;
+ void *cyh_arg;
+} cyc_handler_t;
+
+typedef struct cyc_time {
+ hrtime_t cyt_when;
+ hrtime_t cyt_interval;
+} cyc_time_t;
+
+typedef struct cyc_omni_handler {
+ void (*cyo_online)(void *, cpu_t *, cyc_handler_t *, cyc_time_t *);
+ void (*cyo_offline)(void *, cpu_t *, void *);
+ void *cyo_arg;
+} cyc_omni_handler_t;
+
+#ifdef _KERNEL
+
+cyclic_id_t cyclic_add(cyc_handler_t *, cyc_time_t *);
+cyclic_id_t cyclic_add_omni(cyc_omni_handler_t *);
+void cyclic_remove(cyclic_id_t);
+
+#endif /* _KERNEL */
+
+#endif /* !_ASM */
+
+#endif
diff --git a/sys/cddl/compat/opensolaris/sys/cyclic_impl.h b/sys/cddl/compat/opensolaris/sys/cyclic_impl.h
new file mode 100644
index 000000000000..a19525115cf5
--- /dev/null
+++ b/sys/cddl/compat/opensolaris/sys/cyclic_impl.h
@@ -0,0 +1,304 @@
+/*
+ * 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.
+ */
+
+#ifndef _COMPAT_OPENSOLARIS_SYS_CYCLIC_IMPL_H_
+#define _COMPAT_OPENSOLARIS_SYS_CYCLIC_IMPL_H_
+
+#include <sys/cyclic.h>
+
+/*
+ * Cyclic Subsystem Backend-supplied Interfaces
+ * --------------------------------------------
+ *
+ * 0 Background
+ *
+ * The design, implementation and interfaces of the cyclic subsystem are
+ * covered in detail in block comments in the implementation. This
+ * comment covers the interface from the cyclic subsystem into the cyclic
+ * backend. The backend is specified by a structure of function pointers
+ * defined below.
+ *
+ * 1 Overview
+ *
+ * cyb_configure() <-- Configures the backend on the specified CPU
+ * cyb_unconfigure() <-- Unconfigures the backend
+ * cyb_enable() <-- Enables the CY_HIGH_LEVEL interrupt source
+ * cyb_disable() <-- Disables the CY_HIGH_LEVEL interrupt source
+ * cyb_reprogram() <-- Reprograms the CY_HIGH_LEVEL interrupt source
+ * cyb_xcall() <-- Cross calls to the specified CPU
+ *
+ * 2 cyb_arg_t cyb_configure(cpu_t *)
+ *
+ * 2.1 Overview
+ *
+ * cyb_configure() should configure the specified CPU for cyclic operation.
+ *
+ * 2.2 Arguments and notes
+ *
+ * cyb_configure() should initialize any backend-specific per-CPU
+ * structures for the specified CPU. cyb_configure() will be called for
+ * each CPU (including the boot CPU) during boot. If the platform
+ * supports dynamic reconfiguration, cyb_configure() will be called for
+ * new CPUs as they are configured into the system.
+ *
+ * 2.3 Return value
+ *
+ * cyb_configure() is expected to return a cookie (a cyb_arg_t, which is
+ * of type void *) which will be used as the first argument for all future
+ * cyclic calls into the backend on the specified CPU.
+ *
+ * 2.4 Caller's context
+ *
+ * cpu_lock will be held. The caller's CPU is unspecified, and may or
+ * may not be the CPU specified to cyb_configure().
+ *
+ * 3 void cyb_unconfigure(cyb_arg_t arg)
+ *
+ * 3.1 Overview
+ *
+ * cyb_unconfigure() should unconfigure the specified backend.
+ *
+ * 3.2 Arguments and notes
+ *
+ * The only argument to cyb_unconfigure() is a cookie as returned from
+ * cyb_configure().
+ *
+ * cyb_unconfigure() should free any backend-specific per-CPU structures
+ * for the specified backend. cyb_unconfigure() will _only_ be called on
+ * platforms which support dynamic reconfiguration. If the platform does
+ * not support dynamic reconfiguration, cyb_unconfigure() may panic.
+ *
+ * After cyb_unconfigure() returns, the backend must not call cyclic_fire()
+ * on the corresponding CPU; doing so will result in a bad trap.
+ *
+ * 3.3 Return value
+ *
+ * None.
+ *
+ * 3.4 Caller's context
+ *
+ * cpu_lock will be held. The caller's CPU is unspecified, and may or
+ * may not be the CPU specified to cyb_unconfigure(). The specified
+ * CPU is guaranteed to exist at the time cyb_unconfigure() is called.
+ * The cyclic subsystem is guaranteed to be suspended when cyb_unconfigure()
+ * is called, and interrupts are guaranteed to be disabled.
+ *
+ * 4 void cyb_enable(cyb_arg_t arg)
+ *
+ * 4.1 Overview
+ *
+ * cyb_enable() should enable the CY_HIGH_LEVEL interrupt source on
+ * the specified backend.
+ *
+ * 4.2 Arguments and notes
+ *
+ * The only argument to cyb_enable() is a backend cookie as returned from
+ * cyb_configure().
+ *
+ * cyb_enable() will only be called if a) the specified backend has never
+ * been enabled or b) the specified backend has been explicitly disabled with
+ * cyb_disable(). In either case, cyb_enable() will only be called if
+ * the cyclic subsystem wishes to add a cyclic to the CPU corresponding
+ * to the specified backend. cyb_enable() will be called before
+ * cyb_reprogram() for a given backend.
+ *
+ * cyclic_fire() should not be called on a CPU which has not had its backend
+ * explicitly cyb_enable()'d, but to do so does not constitute fatal error.
+ *
+ * 4.3 Return value
+ *
+ * None.
+ *
+ * 4.4 Caller's context
+ *
+ * cyb_enable() will only be called from CY_HIGH_LEVEL context on the CPU
+ * corresponding to the specified backend.
+ *
+ * 5 void cyb_disable(cyb_arg_t arg)
+ *
+ * 5.1 Overview
+ *
+ * cyb_disable() should disable the CY_HIGH_LEVEL interrupt source on
+ * the specified backend.
+ *
+ * 5.2 Arguments and notes
+ *
+ * The only argument to cyb_disable() is a backend cookie as returned from
+ * cyb_configure().
+ *
+ * cyb_disable() will only be called on backends which have been previously
+ * been cyb_enable()'d. cyb_disable() will be called when all cyclics have
+ * been juggled away or removed from a cyb_enable()'d CPU.
+ *
+ * cyclic_fire() should not be called on a CPU which has had its backend
+ * explicitly cyb_disable()'d, but to do so does not constitute fatal
+ * error. cyb_disable() is thus not required to check for a pending
+ * CY_HIGH_LEVEL interrupt.
+ *
+ * 5.3 Return value
+ *
+ * None.
+ *
+ * 5.4 Caller's context
+ *
+ * cyb_disable() will only be called from CY_HIGH_LEVEL context on the CPU
+ * corresponding to the specified backend.
+ *
+ * 6 void cyb_reprogram(cyb_arg_t arg, hrtime_t time)
+ *
+ * 6.1 Overview
+ *
+ * cyb_reprogram() should reprogram the CY_HIGH_LEVEL interrupt source
+ * to fire at the absolute time specified.
+ *
+ * 6.2 Arguments and notes
+ *
+ * The first argument to cyb_reprogram() is a backend cookie as returned from
+ * cyb_configure().
+ *
+ * The second argument is an absolute time at which the CY_HIGH_LEVEL
+ * interrupt should fire. The specified time _may_ be in the past (albeit
+ * the very recent past). If this is the case, the backend should generate
+ * a CY_HIGH_LEVEL interrupt as soon as possible.
+ *
+ * The platform should not assume that cyb_reprogram() will be called with
+ * monotonically increasing values.
+ *
+ * If the platform does not allow for interrupts at arbitrary times in the
+ * future, cyb_reprogram() may do nothing -- as long as cyclic_fire() is
+ * called periodically at CY_HIGH_LEVEL. While this is clearly suboptimal
+ * (cyclic granularity will be bounded by the length of the period between
+ * cyclic_fire()'s), it allows the cyclic subsystem to be implemented on
+ * inferior hardware.
+ *
+ * 6.3 Return value
+ *
+ * None.
+ *
+ * 6.4 Caller's context
+ *
+ * cyb_reprogram() will only be called from CY_HIGH_LEVEL context on the CPU
+ * corresponding to the specified backend.
+ *
+ * 10 cyb_xcall(cyb_arg_t arg, cpu_t *, void(*func)(void *), void *farg)
+ *
+ * 10.1 Overview
+ *
+ * cyb_xcall() should execute the specified function on the specified CPU.
+ *
+ * 10.2 Arguments and notes
+ *
+ * The first argument to cyb_restore_level() is a backend cookie as returned
+ * from cyb_configure(). The second argument is a CPU on which the third
+ * argument, a function pointer, should be executed. The fourth argument,
+ * a void *, should be passed as the argument to the specified function.
+ *
+ * cyb_xcall() must provide exactly-once semantics. If the specified
+ * function is called more than once, or not at all, the cyclic subsystem
+ * will become internally inconsistent. The specified function must be
+ * be executed on the specified CPU, but may be executed in any context
+ * (any interrupt context or kernel context).
+ *
+ * cyb_xcall() cannot block. Any resources which cyb_xcall() needs to
+ * acquire must thus be protected by synchronization primitives which
+ * never require the caller to block.
+ *
+ * 10.3 Return value
+ *
+ * None.
+ *
+ * 10.4 Caller's context
+ *
+ * cpu_lock will be held and kernel preemption may be disabled. The caller
+ * may be unable to block, giving rise to the constraint outlined in
+ * 10.2, above.
+ *
+ */
+typedef struct cyc_backend {
+ cyb_arg_t (*cyb_configure)(cpu_t *);
+ void (*cyb_unconfigure)(cyb_arg_t);
+ void (*cyb_enable)(cyb_arg_t);
+ void (*cyb_disable)(cyb_arg_t);
+ void (*cyb_reprogram)(cyb_arg_t, hrtime_t);
+ void (*cyb_xcall)(cyb_arg_t, cpu_t *, cyc_func_t, void *);
+ cyb_arg_t cyb_arg;
+} cyc_backend_t;
+
+#define CYF_FREE 0x0001
+
+typedef struct cyclic {
+ hrtime_t cy_expire;
+ hrtime_t cy_interval;
+ void (*cy_handler)(void *);
+ void *cy_arg;
+ uint16_t cy_flags;
+} cyclic_t;
+
+typedef struct cyc_cpu {
+ cpu_t *cyp_cpu;
+ cyc_index_t *cyp_heap;
+ cyclic_t *cyp_cyclics;
+ cyc_index_t cyp_nelems;
+ cyc_index_t cyp_size;
+ cyc_backend_t *cyp_backend;
+ struct mtx cyp_mtx;
+} cyc_cpu_t;
+
+typedef struct cyc_omni_cpu {
+ cyc_cpu_t *cyo_cpu;
+ cyc_index_t cyo_ndx;
+ void *cyo_arg;
+ struct cyc_omni_cpu *cyo_next;
+} cyc_omni_cpu_t;
+
+typedef struct cyc_id {
+ cyc_cpu_t *cyi_cpu;
+ cyc_index_t cyi_ndx;
+ struct cyc_id *cyi_prev;
+ struct cyc_id *cyi_next;
+ cyc_omni_handler_t cyi_omni_hdlr;
+ cyc_omni_cpu_t *cyi_omni_list;
+} cyc_id_t;
+
+typedef struct cyc_xcallarg {
+ cyc_cpu_t *cyx_cpu;
+ hrtime_t cyx_exp;
+} cyc_xcallarg_t;
+
+#define CY_DEFAULT_PERCPU 1
+#define CY_PASSIVE_LEVEL -1
+
+#define CY_WAIT 0
+#define CY_NOWAIT 1
+
+#define CYC_HEAP_PARENT(ndx) (((ndx) - 1) >> 1)
+#define CYC_HEAP_RIGHT(ndx) (((ndx) + 1) << 1)
+#define CYC_HEAP_LEFT(ndx) ((((ndx) + 1) << 1) - 1)
+
+#endif
diff --git a/sys/cddl/compat/opensolaris/sys/elf.h b/sys/cddl/compat/opensolaris/sys/elf.h
new file mode 100644
index 000000000000..a630f28fbc21
--- /dev/null
+++ b/sys/cddl/compat/opensolaris/sys/elf.h
@@ -0,0 +1,116 @@
+/*
+ * 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$
+ *
+ * ELF compatibility definitions for OpenSolaris source.
+ *
+ */
+
+#ifndef _SYS__ELF_SOLARIS_H_
+#define _SYS__ELF_SOLARIS_H_
+
+#include_next <sys/elf.h>
+
+#define __sElfN(x) typedef __CONCAT(__CONCAT(__CONCAT(Elf,__ELF_WORD_SIZE),_),x) x
+
+__sElfN(Addr);
+__sElfN(Cap);
+__sElfN(Dyn);
+__sElfN(Ehdr);
+__sElfN(Move);
+__sElfN(Off);
+__sElfN(Phdr);
+__sElfN(Rel);
+__sElfN(Rela);
+__sElfN(Shdr);
+__sElfN(Sym);
+__sElfN(Syminfo);
+__sElfN(Verdaux);
+__sElfN(Verdef);
+__sElfN(Vernaux);
+__sElfN(Verneed);
+__sElfN(Versym);
+
+__sElfN(Half);
+__sElfN(Sword);
+__sElfN(Word);
+
+#if __ELF_WORD_SIZE == 32
+typedef Elf32_Word Xword; /* Xword/Sxword are 32-bits in Elf32 */
+typedef Elf32_Sword Sxword;
+#else
+typedef Elf64_Xword Xword;
+typedef Elf64_Sxword Sxword;
+#endif
+
+#define ELF_M_INFO __ELFN(M_INFO)
+#define ELF_M_SIZE __ELFN(M_SIZE)
+#define ELF_M_SYM __ELFN(M_SYM)
+
+/*
+ * Elf `printf' type-cast macros. These force arguments to be a fixed size
+ * so that Elf32 and Elf64 can share common format strings.
+ */
+#define EC_ADDR(a) ((Elf64_Addr)(a)) /* "ull" */
+#define EC_OFF(a) ((Elf64_Off)(a)) /* "ull" */
+#define EC_HALF(a) ((Elf64_Half)(a)) /* "d" */
+#define EC_WORD(a) ((Elf64_Word)(a)) /* "u" */
+#define EC_SWORD(a) ((Elf64_Sword)(a)) /* "d" */
+#define EC_XWORD(a) ((Elf64_Xword)(a)) /* "ull" */
+#define EC_SXWORD(a) ((Elf64_Sxword)(a)) /* "ll" */
+#define EC_LWORD(a) ((Elf64_Lword)(a)) /* "ull" */
+
+#define elf_checksum __elfN(checksum)
+#define elf_fsize __elfN(fsize)
+#define elf_getehdr __elfN(getehdr)
+#define elf_getphdr __elfN(getphdr)
+#define elf_newehdr __elfN(newehdr)
+#define elf_newphdr __elfN(newphdr)
+#define elf_getshdr __elfN(getshdr)
+#define elf_xlatetof __elfN(xlatetof)
+#define elf_xlatetom __elfN(xlatetom)
+
+#define Elf_cap_entry __ElfN(cap_entry)
+#define Elf_cap_title __ElfN(cap_title)
+#define Elf_demangle_name __ElfN(demangle_name)
+#define Elf_dyn_entry __ElfN(dyn_entry)
+#define Elf_dyn_title __ElfN(dyn_title)
+#define Elf_ehdr __ElfN(ehdr)
+#define Elf_got_entry __ElfN(got_entry)
+#define Elf_got_title __ElfN(got_title)
+#define Elf_reloc_apply_reg __ElfN(reloc_apply_reg)
+#define Elf_reloc_apply_val __ElfN(reloc_apply_val)
+#define Elf_reloc_entry_1 __ElfN(reloc_entry_1)
+#define Elf_reloc_entry_2 __ElfN(reloc_entry_2)
+#define Elf_reloc_title __ElfN(reloc_title)
+#define Elf_phdr __ElfN(phdr)
+#define Elf_shdr __ElfN(shdr)
+#define Elf_syms_table_entry __ElfN(syms_table_entry)
+#define Elf_syms_table_title __ElfN(syms_table_title)
+#define Elf_ver_def_title __ElfN(ver_def_title)
+#define Elf_ver_line_1 __ElfN(ver_line_1)
+#define Elf_ver_line_2 __ElfN(ver_line_2)
+#define Elf_ver_line_3 __ElfN(ver_line_3)
+#define Elf_ver_line_4 __ElfN(ver_line_4)
+#define Elf_ver_line_5 __ElfN(ver_line_5)
+#define Elf_ver_need_title __ElfN(ver_need_title)
+
+#endif /* !_SYS__ELF_SOLARIS_H_ */
diff --git a/sys/cddl/compat/opensolaris/sys/feature_tests.h b/sys/cddl/compat/opensolaris/sys/feature_tests.h
new file mode 100644
index 000000000000..7e2c74ff3fdf
--- /dev/null
+++ b/sys/cddl/compat/opensolaris/sys/feature_tests.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2007 John Birrell <jb@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ *
+ */
+
+#ifndef _COMPAT_OPENSOLARIS_SYS_FEATURE_TESTS_H_
+#define _COMPAT_OPENSOLARIS_SYS_FEATURE_TESTS_H_
+
+/* Empty. */
+
+#endif
diff --git a/sys/cddl/compat/opensolaris/sys/kcondvar.h b/sys/cddl/compat/opensolaris/sys/kcondvar.h
index 547d622b8dfd..0422ba0791ca 100644
--- a/sys/cddl/compat/opensolaris/sys/kcondvar.h
+++ b/sys/cddl/compat/opensolaris/sys/kcondvar.h
@@ -45,8 +45,15 @@ typedef enum {
} kcv_type_t;
#define zfs_cv_init(cv, name, type, arg) do { \
+ const char *_name; \
ASSERT((type) == CV_DEFAULT); \
- cv_init((cv), "zfs:" #cv); \
+ for (_name = #cv; *_name != '\0'; _name++) { \
+ if (*_name >= 'a' && *_name <= 'z') \
+ break; \
+ } \
+ if (*_name == '\0') \
+ _name = #cv; \
+ cv_init((cv), _name); \
} while (0)
#define cv_init(cv, name, type, arg) zfs_cv_init((cv), (name), (type), (arg))
diff --git a/sys/cddl/compat/opensolaris/sys/kmem.h b/sys/cddl/compat/opensolaris/sys/kmem.h
index 89dfac9bf634..5258cffe4edf 100644
--- a/sys/cddl/compat/opensolaris/sys/kmem.h
+++ b/sys/cddl/compat/opensolaris/sys/kmem.h
@@ -41,8 +41,6 @@
#define KM_NOSLEEP M_NOWAIT
#define KMC_NODEBUG 0
-typedef void vmem_t;
-
typedef struct kmem_cache {
char kc_name[32];
#ifdef _KERNEL
@@ -55,10 +53,12 @@ typedef struct kmem_cache {
void *kc_private;
} kmem_cache_t;
+#define vmem_t void
+
void *zfs_kmem_alloc(size_t size, int kmflags);
void zfs_kmem_free(void *buf, size_t size);
-u_long kmem_size(void);
-u_long kmem_used(void);
+uint64_t kmem_size(void);
+uint64_t kmem_used(void);
kmem_cache_t *kmem_cache_create(char *name, size_t bufsize, size_t align,
int (*constructor)(void *, void *, int), void (*destructor)(void *, void *),
void (*reclaim)(void *) __unused, void *private, vmem_t *vmp, int cflags);
diff --git a/sys/cddl/compat/opensolaris/sys/lock.h b/sys/cddl/compat/opensolaris/sys/lock.h
index 51fcd6789684..27663f46e446 100644
--- a/sys/cddl/compat/opensolaris/sys/lock.h
+++ b/sys/cddl/compat/opensolaris/sys/lock.h
@@ -35,8 +35,7 @@
#define LO_ALLMASK (LO_INITIALIZED | LO_WITNESS | LO_QUIET | \
LO_RECURSABLE | LO_SLEEPABLE | LO_UPGRADABLE | \
- LO_DUPOK | LO_ENROLLPEND | LO_CLASSMASK | \
- LO_NOPROFILE)
+ LO_DUPOK | LO_CLASSMASK | LO_NOPROFILE)
#define LO_EXPECTED (LO_INITIALIZED | LO_WITNESS | LO_RECURSABLE | \
LO_SLEEPABLE | LO_UPGRADABLE | LO_DUPOK | \
/* sx lock class */(2 << LO_CLASSSHIFT))
diff --git a/sys/cddl/compat/opensolaris/machine/endian.h b/sys/cddl/compat/opensolaris/sys/mman.h
index 855189f64c96..ca746898f65d 100644
--- a/sys/cddl/compat/opensolaris/machine/endian.h
+++ b/sys/cddl/compat/opensolaris/sys/mman.h
@@ -1,7 +1,7 @@
-/*-
- * Copyright (c) 2007 Pawel Jakub Dawidek <pjd@FreeBSD.org>
+/*
+ * Copyright (C) 2007 John Birrell <jb@freebsd.org>
* All rights reserved.
- *
+ *
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
@@ -10,11 +10,11 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
@@ -24,21 +24,14 @@
* SUCH DAMAGE.
*
* $FreeBSD$
+ *
*/
-#ifndef _OPENSOLARIS_MACHINE_ENDIAN_H_
-#define _OPENSOLARIS_MACHINE_ENDIAN_H_
+#ifndef _COMPAT_OPENSOLARIS_SYS_MMAN_H_
+#define _COMPAT_OPENSOLARIS_SYS_MMAN_H_
-#include_next <machine/endian.h>
+#include_next <sys/mman.h>
-/*
- * Solaris defines _LITTLE_ENDIAN or _BIG_ENDIAN, but never both and decides
- * which architecture it is based on which thing is defined.
- */
-#if _BYTE_ORDER == _LITTLE_ENDIAN
-#undef _BIG_ENDIAN
-#else
-#undef _LITTLE_ENDIAN
-#endif
+#define mmap64(_a,_b,_c,_d,_e,_f) mmap(_a,_b,_c,_d,_e,_f)
-#endif /* !_OPENSOLARIS_MACHINE_ENDIAN_H_ */
+#endif
diff --git a/sys/cddl/compat/opensolaris/sys/modctl.h b/sys/cddl/compat/opensolaris/sys/modctl.h
new file mode 100644
index 000000000000..7af39b090f3b
--- /dev/null
+++ b/sys/cddl/compat/opensolaris/sys/modctl.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2007 John Birrell <jb@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ *
+ */
+
+#ifndef _COMPAT_OPENSOLARIS_SYS_MODCTL_H
+#define _COMPAT_OPENSOLARIS_SYS_MODCTL_H
+
+#include <sys/param.h>
+#include <sys/linker.h>
+
+typedef struct linker_file modctl_t;
+
+#endif /* _COMPAT_OPENSOLARIS_SYS_MODCTL_H */
diff --git a/sys/cddl/compat/opensolaris/sys/mutex.h b/sys/cddl/compat/opensolaris/sys/mutex.h
index d7543af4363e..8756cd0534f4 100644
--- a/sys/cddl/compat/opensolaris/sys/mutex.h
+++ b/sys/cddl/compat/opensolaris/sys/mutex.h
@@ -53,11 +53,18 @@ typedef struct sx kmutex_t;
#endif
#define mutex_init(lock, desc, type, arg) do { \
+ const char *_name; \
ASSERT((type) == MUTEX_DEFAULT); \
KASSERT(((lock)->lock_object.lo_flags & LO_ALLMASK) != \
LO_EXPECTED, ("lock %s already initialized", #lock)); \
bzero((lock), sizeof(struct sx)); \
- sx_init_flags((lock), "zfs:" #lock, MUTEX_FLAGS); \
+ for (_name = #lock; *_name != '\0'; _name++) { \
+ if (*_name >= 'a' && *_name <= 'z') \
+ break; \
+ } \
+ if (*_name == '\0') \
+ _name = #lock; \
+ sx_init_flags((lock), _name, MUTEX_FLAGS); \
} while (0)
#define mutex_destroy(lock) sx_destroy(lock)
#define mutex_enter(lock) sx_xlock(lock)
diff --git a/sys/cddl/compat/opensolaris/sys/objfs.h b/sys/cddl/compat/opensolaris/sys/objfs.h
new file mode 100644
index 000000000000..b656e7803edc
--- /dev/null
+++ b/sys/cddl/compat/opensolaris/sys/objfs.h
@@ -0,0 +1,35 @@
+/*
+ * 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 _COMPAT_OPENSOLARIS_SYS_OBJFS_H
+#define _COMPAT_OPENSOLARIS_SYS_OBJFS_H
+
+/*
+ * Private data structure found in '.info' section
+ */
+typedef struct objfs_info {
+ int objfs_info_primary;
+} objfs_info_t;
+
+
+#endif /* _COMPAT_OPENSOLARIS_SYS_OBJFS_H */
diff --git a/sys/cddl/compat/opensolaris/sys/param.h b/sys/cddl/compat/opensolaris/sys/param.h
new file mode 100644
index 000000000000..8d36a9d07700
--- /dev/null
+++ b/sys/cddl/compat/opensolaris/sys/param.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2007 John Birrell <jb@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ *
+ */
+
+#ifndef _COMPAT_OPENSOLARIS_SYS_PARAM_H_
+#define _COMPAT_OPENSOLARIS_SYS_PARAM_H_
+
+#include_next <sys/param.h>
+
+#define PAGESIZE PAGE_SIZE
+
+#endif
diff --git a/sys/cddl/compat/opensolaris/sys/pcpu.h b/sys/cddl/compat/opensolaris/sys/pcpu.h
new file mode 100644
index 000000000000..eb9e6a68384b
--- /dev/null
+++ b/sys/cddl/compat/opensolaris/sys/pcpu.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2007 John Birrell <jb@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ *
+ */
+
+#ifndef _COMPAT_OPENSOLARIS_SYS_PCPU_H_
+#define _COMPAT_OPENSOLARIS_SYS_PCPU_H_
+
+#include_next <sys/pcpu.h>
+
+typedef struct pcpu pcpu_t;
+
+#endif
diff --git a/sys/cddl/compat/opensolaris/sys/proc.h b/sys/cddl/compat/opensolaris/sys/proc.h
index 2532c5760704..2e9200cb670c 100644
--- a/sys/cddl/compat/opensolaris/sys/proc.h
+++ b/sys/cddl/compat/opensolaris/sys/proc.h
@@ -44,8 +44,6 @@
#define max_ncpus mp_ncpus
#define boot_max_ncpus mp_ncpus
-extern int hz; /* system clock's frequency */
-
#define TS_RUN 0
#define p0 proc0
diff --git a/sys/cddl/compat/opensolaris/sys/rwlock.h b/sys/cddl/compat/opensolaris/sys/rwlock.h
index e87717566c15..a3e55153bbcf 100644
--- a/sys/cddl/compat/opensolaris/sys/rwlock.h
+++ b/sys/cddl/compat/opensolaris/sys/rwlock.h
@@ -60,10 +60,17 @@ typedef struct sx krwlock_t;
#define RW_ISWRITER(x) (rw_iswriter(x))
#define rw_init(lock, desc, type, arg) do { \
+ const char *_name; \
KASSERT(((lock)->lock_object.lo_flags & LO_ALLMASK) != \
LO_EXPECTED, ("lock %s already initialized", #lock)); \
bzero((lock), sizeof(struct sx)); \
- sx_init_flags((lock), "zfs:" #lock, RW_FLAGS); \
+ for (_name = #lock; *_name != '\0'; _name++) { \
+ if (*_name >= 'a' && *_name <= 'z') \
+ break; \
+ } \
+ if (*_name == '\0') \
+ _name = #lock; \
+ sx_init_flags((lock), _name, RW_FLAGS); \
} while (0)
#define rw_destroy(lock) sx_destroy(lock)
#define rw_enter(lock, how) do { \
diff --git a/sys/cddl/compat/opensolaris/sys/sema.h b/sys/cddl/compat/opensolaris/sys/sema.h
new file mode 100644
index 000000000000..08e4721c8664
--- /dev/null
+++ b/sys/cddl/compat/opensolaris/sys/sema.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2007 John Birrell <jb@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ *
+ */
+
+#ifndef _COMPAT_OPENSOLARIS_SYS_SEMA_H_
+#define _COMPAT_OPENSOLARIS_SYS_SEMA_H_
+
+#include_next <sys/sema.h>
+
+typedef struct sema ksema_t;
+
+#define sema_p sema_post
+#define sema_v sema_value
+
+#endif
diff --git a/sys/cddl/compat/opensolaris/sys/stat.h b/sys/cddl/compat/opensolaris/sys/stat.h
new file mode 100644
index 000000000000..5f45ebe08e90
--- /dev/null
+++ b/sys/cddl/compat/opensolaris/sys/stat.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2007 John Birrell <jb@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ *
+ */
+
+#ifndef _COMPAT_OPENSOLARIS_SYS_STAT_H_
+#define _COMPAT_OPENSOLARIS_SYS_STAT_H_
+
+#include_next <sys/stat.h>
+
+#define stat64 stat
+#define fstat64 fstat
+
+#endif
diff --git a/sys/cddl/compat/opensolaris/sys/time.h b/sys/cddl/compat/opensolaris/sys/time.h
index 94d24a52f6a9..770b25163191 100644
--- a/sys/cddl/compat/opensolaris/sys/time.h
+++ b/sys/cddl/compat/opensolaris/sys/time.h
@@ -31,9 +31,17 @@
#include_next <sys/time.h>
+#define SEC 1
+#define MILLISEC 1000
+#define MICROSEC 1000000
+#define NANOSEC 1000000000
+
+typedef longlong_t hrtime_t;
+
+#define LBOLT ((gethrtime() * hz) / NANOSEC)
+
#ifdef _KERNEL
-#define lbolt ((gethrtime() * hz) / NANOSEC)
-#define lbolt64 (int64_t)(lbolt)
+#define lbolt64 (int64_t)(LBOLT)
static __inline hrtime_t
gethrtime(void) {
@@ -53,6 +61,15 @@ gethrtime(void) {
#define gethrestime_sec() (time_second)
#define gethrestime(ts) getnanotime(ts)
+#else
+
+static __inline hrtime_t gethrtime(void) {
+ struct timespec ts;
+ clock_gettime(CLOCK_UPTIME,&ts);
+ return (((u_int64_t) ts.tv_sec) * NANOSEC + ts.tv_nsec);
+}
+
+
#endif /* _KERNEL */
#endif /* !_OPENSOLARIS_SYS_TIME_H_ */
diff --git a/sys/cddl/compat/opensolaris/sys/types.h b/sys/cddl/compat/opensolaris/sys/types.h
index 4006aec84f8a..7d5d9e41ed14 100644
--- a/sys/cddl/compat/opensolaris/sys/types.h
+++ b/sys/cddl/compat/opensolaris/sys/types.h
@@ -39,6 +39,18 @@
#define MAXNAMELEN 256
typedef struct timespec timestruc_t;
+typedef u_int uint_t;
+typedef u_char uchar_t;
+typedef u_short ushort_t;
+typedef u_long ulong_t;
+typedef long long longlong_t;
+typedef unsigned long long u_longlong_t;
+typedef off_t off64_t;
+typedef id_t taskid_t;
+typedef id_t projid_t;
+typedef id_t poolid_t;
+typedef id_t zoneid_t;
+typedef id_t ctid_t;
#ifdef _KERNEL
@@ -52,6 +64,13 @@ typedef void pathname_t;
typedef int64_t rlim64_t;
#else
+#ifdef NEED_SOLARIS_BOOLEAN
+#if defined(__XOPEN_OR_POSIX)
+typedef enum { _B_FALSE, _B_TRUE } boolean_t;
+#else
+typedef enum { B_FALSE, B_TRUE } boolean_t;
+#endif /* defined(__XOPEN_OR_POSIX) */
+#endif
typedef longlong_t offset_t;
typedef u_longlong_t u_offset_t;
diff --git a/sys/cddl/compat/opensolaris/sys/uio.h b/sys/cddl/compat/opensolaris/sys/uio.h
index 02ee1d831d91..d219ff0e960c 100644
--- a/sys/cddl/compat/opensolaris/sys/uio.h
+++ b/sys/cddl/compat/opensolaris/sys/uio.h
@@ -51,6 +51,7 @@ typedef struct iovec iovec_t;
#define uio_loffset uio_offset
+#ifdef BUILDING_ZFS
static __inline int
zfs_uiomove(void *cp, size_t n, enum uio_rw dir, uio_t *uio)
{
@@ -59,5 +60,6 @@ zfs_uiomove(void *cp, size_t n, enum uio_rw dir, uio_t *uio)
return (uiomove(cp, (int)n, uio));
}
#define uiomove(cp, n, dir, uio) zfs_uiomove((cp), (n), (dir), (uio))
+#endif
#endif /* !_OPENSOLARIS_SYS_UIO_H_ */
diff --git a/sys/cddl/contrib/opensolaris/OPENSOLARIS.LICENSE b/sys/cddl/contrib/opensolaris/OPENSOLARIS.LICENSE
new file mode 100644
index 000000000000..da23621dc843
--- /dev/null
+++ b/sys/cddl/contrib/opensolaris/OPENSOLARIS.LICENSE
@@ -0,0 +1,384 @@
+Unless otherwise noted, all files in this distribution are released
+under the Common Development and Distribution License (CDDL).
+Exceptions are noted within the associated source files.
+
+--------------------------------------------------------------------
+
+
+COMMON DEVELOPMENT AND DISTRIBUTION LICENSE Version 1.0
+
+1. Definitions.
+
+ 1.1. "Contributor" means each individual or entity that creates
+ or contributes to the creation of Modifications.
+
+ 1.2. "Contributor Version" means the combination of the Original
+ Software, prior Modifications used by a Contributor (if any),
+ and the Modifications made by that particular Contributor.
+
+ 1.3. "Covered Software" means (a) the Original Software, or (b)
+ Modifications, or (c) the combination of files containing
+ Original Software with files containing Modifications, in
+ each case including portions thereof.
+
+ 1.4. "Executable" means the Covered Software in any form other
+ than Source Code.
+
+ 1.5. "Initial Developer" means the individual or entity that first
+ makes Original Software available under this License.
+
+ 1.6. "Larger Work" means a work which combines Covered Software or
+ portions thereof with code not governed by the terms of this
+ License.
+
+ 1.7. "License" means this document.
+
+ 1.8. "Licensable" means having the right to grant, to the maximum
+ extent possible, whether at the time of the initial grant or
+ subsequently acquired, any and all of the rights conveyed
+ herein.
+
+ 1.9. "Modifications" means the Source Code and Executable form of
+ any of the following:
+
+ A. Any file that results from an addition to, deletion from or
+ modification of the contents of a file containing Original
+ Software or previous Modifications;
+
+ B. Any new file that contains any part of the Original
+ Software or previous Modifications; or
+
+ C. Any new file that is contributed or otherwise made
+ available under the terms of this License.
+
+ 1.10. "Original Software" means the Source Code and Executable
+ form of computer software code that is originally released
+ under this License.
+
+ 1.11. "Patent Claims" means any patent claim(s), now owned or
+ hereafter acquired, including without limitation, method,
+ process, and apparatus claims, in any patent Licensable by
+ grantor.
+
+ 1.12. "Source Code" means (a) the common form of computer software
+ code in which modifications are made and (b) associated
+ documentation included in or with such code.
+
+ 1.13. "You" (or "Your") means an individual or a legal entity
+ exercising rights under, and complying with all of the terms
+ of, this License. For legal entities, "You" includes any
+ entity which controls, is controlled by, or is under common
+ control with You. For purposes of this definition,
+ "control" means (a) the power, direct or indirect, to cause
+ the direction or management of such entity, whether by
+ contract or otherwise, or (b) ownership of more than fifty
+ percent (50%) of the outstanding shares or beneficial
+ ownership of such entity.
+
+2. License Grants.
+
+ 2.1. The Initial Developer Grant.
+
+ Conditioned upon Your compliance with Section 3.1 below and
+ subject to third party intellectual property claims, the Initial
+ Developer hereby grants You a world-wide, royalty-free,
+ non-exclusive license:
+
+ (a) under intellectual property rights (other than patent or
+ trademark) Licensable by Initial Developer, to use,
+ reproduce, modify, display, perform, sublicense and
+ distribute the Original Software (or portions thereof),
+ with or without Modifications, and/or as part of a Larger
+ Work; and
+
+ (b) under Patent Claims infringed by the making, using or
+ selling of Original Software, to make, have made, use,
+ practice, sell, and offer for sale, and/or otherwise
+ dispose of the Original Software (or portions thereof).
+
+ (c) The licenses granted in Sections 2.1(a) and (b) are
+ effective on the date Initial Developer first distributes
+ or otherwise makes the Original Software available to a
+ third party under the terms of this License.
+
+ (d) Notwithstanding Section 2.1(b) above, no patent license is
+ granted: (1) for code that You delete from the Original
+ Software, or (2) for infringements caused by: (i) the
+ modification of the Original Software, or (ii) the
+ combination of the Original Software with other software
+ or devices.
+
+ 2.2. Contributor Grant.
+
+ Conditioned upon Your compliance with Section 3.1 below and
+ subject to third party intellectual property claims, each
+ Contributor hereby grants You a world-wide, royalty-free,
+ non-exclusive license:
+
+ (a) under intellectual property rights (other than patent or
+ trademark) Licensable by Contributor to use, reproduce,
+ modify, display, perform, sublicense and distribute the
+ Modifications created by such Contributor (or portions
+ thereof), either on an unmodified basis, with other
+ Modifications, as Covered Software and/or as part of a
+ Larger Work; and
+
+ (b) under Patent Claims infringed by the making, using, or
+ selling of Modifications made by that Contributor either
+ alone and/or in combination with its Contributor Version
+ (or portions of such combination), to make, use, sell,
+ offer for sale, have made, and/or otherwise dispose of:
+ (1) Modifications made by that Contributor (or portions
+ thereof); and (2) the combination of Modifications made by
+ that Contributor with its Contributor Version (or portions
+ of such combination).
+
+ (c) The licenses granted in Sections 2.2(a) and 2.2(b) are
+ effective on the date Contributor first distributes or
+ otherwise makes the Modifications available to a third
+ party.
+
+ (d) Notwithstanding Section 2.2(b) above, no patent license is
+ granted: (1) for any code that Contributor has deleted
+ from the Contributor Version; (2) for infringements caused
+ by: (i) third party modifications of Contributor Version,
+ or (ii) the combination of Modifications made by that
+ Contributor with other software (except as part of the
+ Contributor Version) or other devices; or (3) under Patent
+ Claims infringed by Covered Software in the absence of
+ Modifications made by that Contributor.
+
+3. Distribution Obligations.
+
+ 3.1. Availability of Source Code.
+
+ Any Covered Software that You distribute or otherwise make
+ available in Executable form must also be made available in Source
+ Code form and that Source Code form must be distributed only under
+ the terms of this License. You must include a copy of this
+ License with every copy of the Source Code form of the Covered
+ Software You distribute or otherwise make available. You must
+ inform recipients of any such Covered Software in Executable form
+ as to how they can obtain such Covered Software in Source Code
+ form in a reasonable manner on or through a medium customarily
+ used for software exchange.
+
+ 3.2. Modifications.
+
+ The Modifications that You create or to which You contribute are
+ governed by the terms of this License. You represent that You
+ believe Your Modifications are Your original creation(s) and/or
+ You have sufficient rights to grant the rights conveyed by this
+ License.
+
+ 3.3. Required Notices.
+
+ You must include a notice in each of Your Modifications that
+ identifies You as the Contributor of the Modification. You may
+ not remove or alter any copyright, patent or trademark notices
+ contained within the Covered Software, or any notices of licensing
+ or any descriptive text giving attribution to any Contributor or
+ the Initial Developer.
+
+ 3.4. Application of Additional Terms.
+
+ You may not offer or impose any terms on any Covered Software in
+ Source Code form that alters or restricts the applicable version
+ of this License or the recipients' rights hereunder. You may
+ choose to offer, and to charge a fee for, warranty, support,
+ indemnity or liability obligations to one or more recipients of
+ Covered Software. However, you may do so only on Your own behalf,
+ and not on behalf of the Initial Developer or any Contributor.
+ You must make it absolutely clear that any such warranty, support,
+ indemnity or liability obligation is offered by You alone, and You
+ hereby agree to indemnify the Initial Developer and every
+ Contributor for any liability incurred by the Initial Developer or
+ such Contributor as a result of warranty, support, indemnity or
+ liability terms You offer.
+
+ 3.5. Distribution of Executable Versions.
+
+ You may distribute the Executable form of the Covered Software
+ under the terms of this License or under the terms of a license of
+ Your choice, which may contain terms different from this License,
+ provided that You are in compliance with the terms of this License
+ and that the license for the Executable form does not attempt to
+ limit or alter the recipient's rights in the Source Code form from
+ the rights set forth in this License. If You distribute the
+ Covered Software in Executable form under a different license, You
+ must make it absolutely clear that any terms which differ from
+ this License are offered by You alone, not by the Initial
+ Developer or Contributor. You hereby agree to indemnify the
+ Initial Developer and every Contributor for any liability incurred
+ by the Initial Developer or such Contributor as a result of any
+ such terms You offer.
+
+ 3.6. Larger Works.
+
+ You may create a Larger Work by combining Covered Software with
+ other code not governed by the terms of this License and
+ distribute the Larger Work as a single product. In such a case,
+ You must make sure the requirements of this License are fulfilled
+ for the Covered Software.
+
+4. Versions of the License.
+
+ 4.1. New Versions.
+
+ Sun Microsystems, Inc. is the initial license steward and may
+ publish revised and/or new versions of this License from time to
+ time. Each version will be given a distinguishing version number.
+ Except as provided in Section 4.3, no one other than the license
+ steward has the right to modify this License.
+
+ 4.2. Effect of New Versions.
+
+ You may always continue to use, distribute or otherwise make the
+ Covered Software available under the terms of the version of the
+ License under which You originally received the Covered Software.
+ If the Initial Developer includes a notice in the Original
+ Software prohibiting it from being distributed or otherwise made
+ available under any subsequent version of the License, You must
+ distribute and make the Covered Software available under the terms
+ of the version of the License under which You originally received
+ the Covered Software. Otherwise, You may also choose to use,
+ distribute or otherwise make the Covered Software available under
+ the terms of any subsequent version of the License published by
+ the license steward.
+
+ 4.3. Modified Versions.
+
+ When You are an Initial Developer and You want to create a new
+ license for Your Original Software, You may create and use a
+ modified version of this License if You: (a) rename the license
+ and remove any references to the name of the license steward
+ (except to note that the license differs from this License); and
+ (b) otherwise make it clear that the license contains terms which
+ differ from this License.
+
+5. DISCLAIMER OF WARRANTY.
+
+ COVERED SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS"
+ BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED,
+ INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED
+ SOFTWARE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR
+ PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND
+ PERFORMANCE OF THE COVERED SOFTWARE IS WITH YOU. SHOULD ANY
+ COVERED SOFTWARE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE
+ INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY
+ NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF
+ WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF
+ ANY COVERED SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS
+ DISCLAIMER.
+
+6. TERMINATION.
+
+ 6.1. This License and the rights granted hereunder will terminate
+ automatically if You fail to comply with terms herein and fail to
+ cure such breach within 30 days of becoming aware of the breach.
+ Provisions which, by their nature, must remain in effect beyond
+ the termination of this License shall survive.
+
+ 6.2. If You assert a patent infringement claim (excluding
+ declaratory judgment actions) against Initial Developer or a
+ Contributor (the Initial Developer or Contributor against whom You
+ assert such claim is referred to as "Participant") alleging that
+ the Participant Software (meaning the Contributor Version where
+ the Participant is a Contributor or the Original Software where
+ the Participant is the Initial Developer) directly or indirectly
+ infringes any patent, then any and all rights granted directly or
+ indirectly to You by such Participant, the Initial Developer (if
+ the Initial Developer is not the Participant) and all Contributors
+ under Sections 2.1 and/or 2.2 of this License shall, upon 60 days
+ notice from Participant terminate prospectively and automatically
+ at the expiration of such 60 day notice period, unless if within
+ such 60 day period You withdraw Your claim with respect to the
+ Participant Software against such Participant either unilaterally
+ or pursuant to a written agreement with Participant.
+
+ 6.3. In the event of termination under Sections 6.1 or 6.2 above,
+ all end user licenses that have been validly granted by You or any
+ distributor hereunder prior to termination (excluding licenses
+ granted to You by any distributor) shall survive termination.
+
+7. LIMITATION OF LIABILITY.
+
+ UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT
+ (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE
+ INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF
+ COVERED SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE
+ LIABLE TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
+ CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT
+ LIMITATION, DAMAGES FOR LOST PROFITS, LOSS OF GOODWILL, WORK
+ STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER
+ COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN
+ INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF
+ LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL
+ INJURY RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT
+ APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO
+ NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR
+ CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND LIMITATION MAY NOT
+ APPLY TO YOU.
+
+8. U.S. GOVERNMENT END USERS.
+
+ The Covered Software is a "commercial item," as that term is
+ defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial
+ computer software" (as that term is defined at 48
+ C.F.R. 252.227-7014(a)(1)) and "commercial computer software
+ documentation" as such terms are used in 48 C.F.R. 12.212
+ (Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48
+ C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all
+ U.S. Government End Users acquire Covered Software with only those
+ rights set forth herein. This U.S. Government Rights clause is in
+ lieu of, and supersedes, any other FAR, DFAR, or other clause or
+ provision that addresses Government rights in computer software
+ under this License.
+
+9. MISCELLANEOUS.
+
+ This License represents the complete agreement concerning subject
+ matter hereof. If any provision of this License is held to be
+ unenforceable, such provision shall be reformed only to the extent
+ necessary to make it enforceable. This License shall be governed
+ by the law of the jurisdiction specified in a notice contained
+ within the Original Software (except to the extent applicable law,
+ if any, provides otherwise), excluding such jurisdiction's
+ conflict-of-law provisions. Any litigation relating to this
+ License shall be subject to the jurisdiction of the courts located
+ in the jurisdiction and venue specified in a notice contained
+ within the Original Software, with the losing party responsible
+ for costs, including, without limitation, court costs and
+ reasonable attorneys' fees and expenses. The application of the
+ United Nations Convention on Contracts for the International Sale
+ of Goods is expressly excluded. Any law or regulation which
+ provides that the language of a contract shall be construed
+ against the drafter shall not apply to this License. You agree
+ that You alone are responsible for compliance with the United
+ States export administration regulations (and the export control
+ laws and regulation of any other countries) when You use,
+ distribute or otherwise make available any Covered Software.
+
+10. RESPONSIBILITY FOR CLAIMS.
+
+ As between Initial Developer and the Contributors, each party is
+ responsible for claims and damages arising, directly or
+ indirectly, out of its utilization of rights under this License
+ and You agree to work with Initial Developer and Contributors to
+ distribute such responsibility on an equitable basis. Nothing
+ herein is intended or shall be deemed to constitute any admission
+ of liability.
+
+--------------------------------------------------------------------
+
+NOTICE PURSUANT TO SECTION 9 OF THE COMMON DEVELOPMENT AND
+DISTRIBUTION LICENSE (CDDL)
+
+For Covered Software in this distribution, this License shall
+be governed by the laws of the State of California (excluding
+conflict-of-law provisions).
+
+Any litigation relating to this License shall be subject to the
+jurisdiction of the Federal Courts of the Northern District of
+California and the state courts of the State of California, with
+venue lying in Santa Clara County, California.
diff --git a/sys/cddl/contrib/opensolaris/uts/common/ctf/ctf_mod.c b/sys/cddl/contrib/opensolaris/uts/common/ctf/ctf_mod.c
new file mode 100644
index 000000000000..b34cf400cd2c
--- /dev/null
+++ b/sys/cddl/contrib/opensolaris/uts/common/ctf/ctf_mod.c
@@ -0,0 +1,177 @@
+/*
+ * 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 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/sysmacros.h>
+#include <sys/modctl.h>
+#include <sys/debug.h>
+#include <sys/mman.h>
+#include <sys/modctl.h>
+#include <sys/kobj.h>
+#include <ctf_impl.h>
+
+int ctf_leave_compressed = 0;
+
+static struct modlmisc modlmisc = {
+ &mod_miscops, "Compact C Type Format routines"
+};
+
+static struct modlinkage modlinkage = {
+ MODREV_1, &modlmisc, NULL
+};
+
+int
+_init(void)
+{
+ return (mod_install(&modlinkage));
+}
+
+int
+_info(struct modinfo *mip)
+{
+ return (mod_info(&modlinkage, mip));
+}
+
+int
+_fini(void)
+{
+ return (mod_remove(&modlinkage));
+}
+
+/*ARGSUSED*/
+void *
+ctf_zopen(int *errp)
+{
+ return ((void *)1); /* zmod is always loaded because we depend on it */
+}
+
+/*ARGSUSED*/
+const void *
+ctf_sect_mmap(ctf_sect_t *sp, int fd)
+{
+ return (MAP_FAILED); /* we don't support this in the kernel */
+}
+
+/*ARGSUSED*/
+void
+ctf_sect_munmap(const ctf_sect_t *sp)
+{
+ /* we don't support this in the kernel */
+}
+
+/*ARGSUSED*/
+ctf_file_t *
+ctf_fdopen(int fd, int *errp)
+{
+ return (ctf_set_open_errno(errp, ENOTSUP));
+}
+
+/*ARGSUSED*/
+ctf_file_t *
+ctf_open(const char *filename, int *errp)
+{
+ return (ctf_set_open_errno(errp, ENOTSUP));
+}
+
+/*ARGSUSED*/
+int
+ctf_write(ctf_file_t *fp, int fd)
+{
+ return (ctf_set_errno(fp, ENOTSUP));
+}
+
+int
+ctf_version(int version)
+{
+ ASSERT(version > 0 && version <= CTF_VERSION);
+
+ if (version > 0)
+ _libctf_version = MIN(CTF_VERSION, version);
+
+ return (_libctf_version);
+}
+
+/*ARGSUSED*/
+ctf_file_t *
+ctf_modopen(struct module *mp, int *error)
+{
+ ctf_sect_t ctfsect, symsect, strsect;
+ ctf_file_t *fp = NULL;
+ int err;
+
+ if (error == NULL)
+ error = &err;
+
+ ctfsect.cts_name = ".SUNW_ctf";
+ ctfsect.cts_type = SHT_PROGBITS;
+ ctfsect.cts_flags = SHF_ALLOC;
+ ctfsect.cts_data = mp->ctfdata;
+ ctfsect.cts_size = mp->ctfsize;
+ ctfsect.cts_entsize = 1;
+ ctfsect.cts_offset = 0;
+
+ symsect.cts_name = ".symtab";
+ symsect.cts_type = SHT_SYMTAB;
+ symsect.cts_flags = 0;
+ symsect.cts_data = mp->symtbl;
+ symsect.cts_size = mp->symhdr->sh_size;
+#ifdef _LP64
+ symsect.cts_entsize = sizeof (Elf64_Sym);
+#else
+ symsect.cts_entsize = sizeof (Elf32_Sym);
+#endif
+ symsect.cts_offset = 0;
+
+ strsect.cts_name = ".strtab";
+ strsect.cts_type = SHT_STRTAB;
+ strsect.cts_flags = 0;
+ strsect.cts_data = mp->strings;
+ strsect.cts_size = mp->strhdr->sh_size;
+ strsect.cts_entsize = 1;
+ strsect.cts_offset = 0;
+
+ ASSERT(MUTEX_HELD(&mod_lock));
+
+ if ((fp = ctf_bufopen(&ctfsect, &symsect, &strsect, error)) == NULL)
+ return (NULL);
+
+ if (!ctf_leave_compressed && (caddr_t)fp->ctf_base != mp->ctfdata) {
+ /*
+ * We must have just uncompressed the CTF data. To avoid
+ * others having to pay the (substantial) cost of decompressing
+ * the data, we're going to substitute the uncompressed version
+ * for the compressed version. Note that this implies that the
+ * first CTF consumer will induce memory impact on the system
+ * (but in the name of performance of future CTF consumers).
+ */
+ kobj_set_ctf(mp, (caddr_t)fp->ctf_base, fp->ctf_size);
+ fp->ctf_data.cts_data = fp->ctf_base;
+ fp->ctf_data.cts_size = fp->ctf_size;
+ }
+
+ return (fp);
+}
diff --git a/sys/cddl/contrib/opensolaris/uts/common/ctf/ctf_subr.c b/sys/cddl/contrib/opensolaris/uts/common/ctf/ctf_subr.c
new file mode 100644
index 000000000000..cd0a828628d4
--- /dev/null
+++ b/sys/cddl/contrib/opensolaris/uts/common/ctf/ctf_subr.c
@@ -0,0 +1,96 @@
+/*
+ * 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 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <ctf_impl.h>
+#include <sys/kobj.h>
+#include <sys/kobj_impl.h>
+
+/*
+ * This module is used both during the normal operation of the kernel (i.e.
+ * after kmem has been initialized) and during boot (before unix`_start has
+ * been called). kobj_alloc is able to tell the difference between the two
+ * cases, and as such must be used instead of kmem_alloc.
+ */
+
+void *
+ctf_data_alloc(size_t size)
+{
+ void *buf = kobj_alloc(size, KM_NOWAIT|KM_SCRATCH);
+
+ if (buf == NULL)
+ return (MAP_FAILED);
+
+ return (buf);
+}
+
+void
+ctf_data_free(void *buf, size_t size)
+{
+ kobj_free(buf, size);
+}
+
+/*ARGSUSED*/
+void
+ctf_data_protect(void *buf, size_t size)
+{
+ /* we don't support this operation in the kernel */
+}
+
+void *
+ctf_alloc(size_t size)
+{
+ return (kobj_alloc(size, KM_NOWAIT|KM_TMP));
+}
+
+/*ARGSUSED*/
+void
+ctf_free(void *buf, size_t size)
+{
+ kobj_free(buf, size);
+}
+
+/*ARGSUSED*/
+const char *
+ctf_strerror(int err)
+{
+ return (NULL); /* we don't support this operation in the kernel */
+}
+
+/*PRINTFLIKE1*/
+void
+ctf_dprintf(const char *format, ...)
+{
+ if (_libctf_debug) {
+ va_list alist;
+
+ va_start(alist, format);
+ (void) printf("ctf DEBUG: ");
+ (void) vprintf(format, alist);
+ va_end(alist);
+ }
+}
diff --git a/sys/cddl/contrib/opensolaris/uts/common/dtrace/dtrace.c b/sys/cddl/contrib/opensolaris/uts/common/dtrace/dtrace.c
new file mode 100644
index 000000000000..7600708dba75
--- /dev/null
+++ b/sys/cddl/contrib/opensolaris/uts/common/dtrace/dtrace.c
@@ -0,0 +1,16449 @@
+/*
+ * 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: src/sys/cddl/contrib/opensolaris/uts/common/dtrace/dtrace.c,v 1.6 2008/08/19 21:28:58 jb Exp $
+ */
+
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * DTrace - Dynamic Tracing for Solaris
+ *
+ * This is the implementation of the Solaris Dynamic Tracing framework
+ * (DTrace). The user-visible interface to DTrace is described at length in
+ * the "Solaris Dynamic Tracing Guide". The interfaces between the libdtrace
+ * library, the in-kernel DTrace framework, and the DTrace providers are
+ * described in the block comments in the <sys/dtrace.h> header file. The
+ * internal architecture of DTrace is described in the block comments in the
+ * <sys/dtrace_impl.h> header file. The comments contained within the DTrace
+ * implementation very much assume mastery of all of these sources; if one has
+ * an unanswered question about the implementation, one should consult them
+ * first.
+ *
+ * The functions here are ordered roughly as follows:
+ *
+ * - Probe context functions
+ * - Probe hashing functions
+ * - Non-probe context utility functions
+ * - Matching functions
+ * - Provider-to-Framework API functions
+ * - Probe management functions
+ * - DIF object functions
+ * - Format functions
+ * - Predicate functions
+ * - ECB functions
+ * - Buffer functions
+ * - Enabling functions
+ * - DOF functions
+ * - Anonymous enabling functions
+ * - Consumer state functions
+ * - Helper functions
+ * - Hook functions
+ * - Driver cookbook functions
+ *
+ * Each group of functions begins with a block comment labelled the "DTrace
+ * [Group] Functions", allowing one to find each block by searching forward
+ * on capital-f functions.
+ */
+#include <sys/errno.h>
+#if !defined(sun)
+#include <sys/time.h>
+#endif
+#include <sys/stat.h>
+#include <sys/modctl.h>
+#include <sys/conf.h>
+#include <sys/systm.h>
+#if defined(sun)
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#endif
+#include <sys/cpuvar.h>
+#include <sys/kmem.h>
+#if defined(sun)
+#include <sys/strsubr.h>
+#endif
+#include <sys/sysmacros.h>
+#include <sys/dtrace_impl.h>
+#include <sys/atomic.h>
+#include <sys/cmn_err.h>
+#if defined(sun)
+#include <sys/mutex_impl.h>
+#include <sys/rwlock_impl.h>
+#endif
+#include <sys/ctf_api.h>
+#if defined(sun)
+#include <sys/panic.h>
+#include <sys/priv_impl.h>
+#endif
+#include <sys/policy.h>
+#if defined(sun)
+#include <sys/cred_impl.h>
+#include <sys/procfs_isa.h>
+#endif
+#include <sys/taskq.h>
+#if defined(sun)
+#include <sys/mkdev.h>
+#include <sys/kdi.h>
+#endif
+#include <sys/zone.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+/* FreeBSD includes: */
+#if !defined(sun)
+#include <sys/callout.h>
+#include <sys/ctype.h>
+#include <sys/limits.h>
+#include <sys/kdb.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/sysctl.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/sx.h>
+#include <sys/dtrace_bsd.h>
+#include <netinet/in.h>
+#include "dtrace_cddl.h"
+#include "dtrace_debug.c"
+#endif
+
+/*
+ * DTrace Tunable Variables
+ *
+ * The following variables may be tuned by adding a line to /etc/system that
+ * includes both the name of the DTrace module ("dtrace") and the name of the
+ * variable. For example:
+ *
+ * set dtrace:dtrace_destructive_disallow = 1
+ *
+ * In general, the only variables that one should be tuning this way are those
+ * that affect system-wide DTrace behavior, and for which the default behavior
+ * is undesirable. Most of these variables are tunable on a per-consumer
+ * basis using DTrace options, and need not be tuned on a system-wide basis.
+ * When tuning these variables, avoid pathological values; while some attempt
+ * is made to verify the integrity of these variables, they are not considered
+ * part of the supported interface to DTrace, and they are therefore not
+ * checked comprehensively. Further, these variables should not be tuned
+ * dynamically via "mdb -kw" or other means; they should only be tuned via
+ * /etc/system.
+ */
+int dtrace_destructive_disallow = 0;
+dtrace_optval_t dtrace_nonroot_maxsize = (16 * 1024 * 1024);
+size_t dtrace_difo_maxsize = (256 * 1024);
+dtrace_optval_t dtrace_dof_maxsize = (256 * 1024);
+size_t dtrace_global_maxsize = (16 * 1024);
+size_t dtrace_actions_max = (16 * 1024);
+size_t dtrace_retain_max = 1024;
+dtrace_optval_t dtrace_helper_actions_max = 32;
+dtrace_optval_t dtrace_helper_providers_max = 32;
+dtrace_optval_t dtrace_dstate_defsize = (1 * 1024 * 1024);
+size_t dtrace_strsize_default = 256;
+dtrace_optval_t dtrace_cleanrate_default = 9900990; /* 101 hz */
+dtrace_optval_t dtrace_cleanrate_min = 200000; /* 5000 hz */
+dtrace_optval_t dtrace_cleanrate_max = (uint64_t)60 * NANOSEC; /* 1/minute */
+dtrace_optval_t dtrace_aggrate_default = NANOSEC; /* 1 hz */
+dtrace_optval_t dtrace_statusrate_default = NANOSEC; /* 1 hz */
+dtrace_optval_t dtrace_statusrate_max = (hrtime_t)10 * NANOSEC; /* 6/minute */
+dtrace_optval_t dtrace_switchrate_default = NANOSEC; /* 1 hz */
+dtrace_optval_t dtrace_nspec_default = 1;
+dtrace_optval_t dtrace_specsize_default = 32 * 1024;
+dtrace_optval_t dtrace_stackframes_default = 20;
+dtrace_optval_t dtrace_ustackframes_default = 20;
+dtrace_optval_t dtrace_jstackframes_default = 50;
+dtrace_optval_t dtrace_jstackstrsize_default = 512;
+int dtrace_msgdsize_max = 128;
+hrtime_t dtrace_chill_max = 500 * (NANOSEC / MILLISEC); /* 500 ms */
+hrtime_t dtrace_chill_interval = NANOSEC; /* 1000 ms */
+int dtrace_devdepth_max = 32;
+int dtrace_err_verbose;
+hrtime_t dtrace_deadman_interval = NANOSEC;
+hrtime_t dtrace_deadman_timeout = (hrtime_t)10 * NANOSEC;
+hrtime_t dtrace_deadman_user = (hrtime_t)30 * NANOSEC;
+
+/*
+ * DTrace External Variables
+ *
+ * As dtrace(7D) is a kernel module, any DTrace variables are obviously
+ * available to DTrace consumers via the backtick (`) syntax. One of these,
+ * dtrace_zero, is made deliberately so: it is provided as a source of
+ * well-known, zero-filled memory. While this variable is not documented,
+ * it is used by some translators as an implementation detail.
+ */
+const char dtrace_zero[256] = { 0 }; /* zero-filled memory */
+
+/*
+ * DTrace Internal Variables
+ */
+#if defined(sun)
+static dev_info_t *dtrace_devi; /* device info */
+#endif
+#if defined(sun)
+static vmem_t *dtrace_arena; /* probe ID arena */
+static vmem_t *dtrace_minor; /* minor number arena */
+static taskq_t *dtrace_taskq; /* task queue */
+#else
+static struct unrhdr *dtrace_arena; /* Probe ID number. */
+#endif
+static dtrace_probe_t **dtrace_probes; /* array of all probes */
+static int dtrace_nprobes; /* number of probes */
+static dtrace_provider_t *dtrace_provider; /* provider list */
+static dtrace_meta_t *dtrace_meta_pid; /* user-land meta provider */
+static int dtrace_opens; /* number of opens */
+static int dtrace_helpers; /* number of helpers */
+#if defined(sun)
+static void *dtrace_softstate; /* softstate pointer */
+#endif
+static dtrace_hash_t *dtrace_bymod; /* probes hashed by module */
+static dtrace_hash_t *dtrace_byfunc; /* probes hashed by function */
+static dtrace_hash_t *dtrace_byname; /* probes hashed by name */
+static dtrace_toxrange_t *dtrace_toxrange; /* toxic range array */
+static int dtrace_toxranges; /* number of toxic ranges */
+static int dtrace_toxranges_max; /* size of toxic range array */
+static dtrace_anon_t dtrace_anon; /* anonymous enabling */
+static kmem_cache_t *dtrace_state_cache; /* cache for dynamic state */
+static uint64_t dtrace_vtime_references; /* number of vtimestamp refs */
+static kthread_t *dtrace_panicked; /* panicking thread */
+static dtrace_ecb_t *dtrace_ecb_create_cache; /* cached created ECB */
+static dtrace_genid_t dtrace_probegen; /* current probe generation */
+static dtrace_helpers_t *dtrace_deferred_pid; /* deferred helper list */
+static dtrace_enabling_t *dtrace_retained; /* list of retained enablings */
+static dtrace_dynvar_t dtrace_dynhash_sink; /* end of dynamic hash chains */
+#if !defined(sun)
+static struct mtx dtrace_unr_mtx;
+MTX_SYSINIT(dtrace_unr_mtx, &dtrace_unr_mtx, "Unique resource identifier", MTX_DEF);
+int dtrace_in_probe; /* non-zero if executing a probe */
+#if defined(__i386__) || defined(__amd64__)
+uintptr_t dtrace_in_probe_addr; /* Address of invop when already in probe */
+#endif
+#endif
+
+/*
+ * DTrace Locking
+ * DTrace is protected by three (relatively coarse-grained) locks:
+ *
+ * (1) dtrace_lock is required to manipulate essentially any DTrace state,
+ * including enabling state, probes, ECBs, consumer state, helper state,
+ * etc. Importantly, dtrace_lock is _not_ required when in probe context;
+ * probe context is lock-free -- synchronization is handled via the
+ * dtrace_sync() cross call mechanism.
+ *
+ * (2) dtrace_provider_lock is required when manipulating provider state, or
+ * when provider state must be held constant.
+ *
+ * (3) dtrace_meta_lock is required when manipulating meta provider state, or
+ * when meta provider state must be held constant.
+ *
+ * The lock ordering between these three locks is dtrace_meta_lock before
+ * dtrace_provider_lock before dtrace_lock. (In particular, there are
+ * several places where dtrace_provider_lock is held by the framework as it
+ * calls into the providers -- which then call back into the framework,
+ * grabbing dtrace_lock.)
+ *
+ * There are two other locks in the mix: mod_lock and cpu_lock. With respect
+ * to dtrace_provider_lock and dtrace_lock, cpu_lock continues its historical
+ * role as a coarse-grained lock; it is acquired before both of these locks.
+ * With respect to dtrace_meta_lock, its behavior is stranger: cpu_lock must
+ * be acquired _between_ dtrace_meta_lock and any other DTrace locks.
+ * mod_lock is similar with respect to dtrace_provider_lock in that it must be
+ * acquired _between_ dtrace_provider_lock and dtrace_lock.
+ */
+static kmutex_t dtrace_lock; /* probe state lock */
+static kmutex_t dtrace_provider_lock; /* provider state lock */
+static kmutex_t dtrace_meta_lock; /* meta-provider state lock */
+
+#if !defined(sun)
+/* XXX FreeBSD hacks. */
+static kmutex_t mod_lock;
+
+#define cr_suid cr_svuid
+#define cr_sgid cr_svgid
+#define ipaddr_t in_addr_t
+#define mod_modname pathname
+#define vuprintf vprintf
+#define ttoproc(_a) ((_a)->td_proc)
+#define crgetzoneid(_a) 0
+#define NCPU MAXCPU
+#define SNOCD 0
+#define CPU_ON_INTR(_a) 0
+
+#define PRIV_EFFECTIVE (1 << 0)
+#define PRIV_DTRACE_KERNEL (1 << 1)
+#define PRIV_DTRACE_PROC (1 << 2)
+#define PRIV_DTRACE_USER (1 << 3)
+#define PRIV_PROC_OWNER (1 << 4)
+#define PRIV_PROC_ZONE (1 << 5)
+#define PRIV_ALL ~0
+
+SYSCTL_NODE(_debug, OID_AUTO, dtrace, CTLFLAG_RD, 0, "DTrace Information");
+#endif
+
+#if defined(sun)
+#define curcpu CPU->cpu_id
+#endif
+
+
+/*
+ * DTrace Provider Variables
+ *
+ * These are the variables relating to DTrace as a provider (that is, the
+ * provider of the BEGIN, END, and ERROR probes).
+ */
+static dtrace_pattr_t dtrace_provider_attr = {
+{ DTRACE_STABILITY_STABLE, DTRACE_STABILITY_STABLE, DTRACE_CLASS_COMMON },
+{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
+{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
+{ DTRACE_STABILITY_STABLE, DTRACE_STABILITY_STABLE, DTRACE_CLASS_COMMON },
+{ DTRACE_STABILITY_STABLE, DTRACE_STABILITY_STABLE, DTRACE_CLASS_COMMON },
+};
+
+static void
+dtrace_nullop(void)
+{}
+
+static dtrace_pops_t dtrace_provider_ops = {
+ (void (*)(void *, dtrace_probedesc_t *))dtrace_nullop,
+ (void (*)(void *, modctl_t *))dtrace_nullop,
+ (void (*)(void *, dtrace_id_t, void *))dtrace_nullop,
+ (void (*)(void *, dtrace_id_t, void *))dtrace_nullop,
+ (void (*)(void *, dtrace_id_t, void *))dtrace_nullop,
+ (void (*)(void *, dtrace_id_t, void *))dtrace_nullop,
+ NULL,
+ NULL,
+ NULL,
+ (void (*)(void *, dtrace_id_t, void *))dtrace_nullop
+};
+
+static dtrace_id_t dtrace_probeid_begin; /* special BEGIN probe */
+static dtrace_id_t dtrace_probeid_end; /* special END probe */
+dtrace_id_t dtrace_probeid_error; /* special ERROR probe */
+
+/*
+ * DTrace Helper Tracing Variables
+ */
+uint32_t dtrace_helptrace_next = 0;
+uint32_t dtrace_helptrace_nlocals;
+char *dtrace_helptrace_buffer;
+int dtrace_helptrace_bufsize = 512 * 1024;
+
+#ifdef DEBUG
+int dtrace_helptrace_enabled = 1;
+#else
+int dtrace_helptrace_enabled = 0;
+#endif
+
+/*
+ * DTrace Error Hashing
+ *
+ * On DEBUG kernels, DTrace will track the errors that has seen in a hash
+ * table. This is very useful for checking coverage of tests that are
+ * expected to induce DIF or DOF processing errors, and may be useful for
+ * debugging problems in the DIF code generator or in DOF generation . The
+ * error hash may be examined with the ::dtrace_errhash MDB dcmd.
+ */
+#ifdef DEBUG
+static dtrace_errhash_t dtrace_errhash[DTRACE_ERRHASHSZ];
+static const char *dtrace_errlast;
+static kthread_t *dtrace_errthread;
+static kmutex_t dtrace_errlock;
+#endif
+
+/*
+ * DTrace Macros and Constants
+ *
+ * These are various macros that are useful in various spots in the
+ * implementation, along with a few random constants that have no meaning
+ * outside of the implementation. There is no real structure to this cpp
+ * mishmash -- but is there ever?
+ */
+#define DTRACE_HASHSTR(hash, probe) \
+ dtrace_hash_str(*((char **)((uintptr_t)(probe) + (hash)->dth_stroffs)))
+
+#define DTRACE_HASHNEXT(hash, probe) \
+ (dtrace_probe_t **)((uintptr_t)(probe) + (hash)->dth_nextoffs)
+
+#define DTRACE_HASHPREV(hash, probe) \
+ (dtrace_probe_t **)((uintptr_t)(probe) + (hash)->dth_prevoffs)
+
+#define DTRACE_HASHEQ(hash, lhs, rhs) \
+ (strcmp(*((char **)((uintptr_t)(lhs) + (hash)->dth_stroffs)), \
+ *((char **)((uintptr_t)(rhs) + (hash)->dth_stroffs))) == 0)
+
+#define DTRACE_AGGHASHSIZE_SLEW 17
+
+#define DTRACE_V4MAPPED_OFFSET (sizeof (uint32_t) * 3)
+
+/*
+ * The key for a thread-local variable consists of the lower 61 bits of the
+ * t_did, plus the 3 bits of the highest active interrupt above LOCK_LEVEL.
+ * We add DIF_VARIABLE_MAX to t_did to assure that the thread key is never
+ * equal to a variable identifier. This is necessary (but not sufficient) to
+ * assure that global associative arrays never collide with thread-local
+ * variables. To guarantee that they cannot collide, we must also define the
+ * order for keying dynamic variables. That order is:
+ *
+ * [ key0 ] ... [ keyn ] [ variable-key ] [ tls-key ]
+ *
+ * Because the variable-key and the tls-key are in orthogonal spaces, there is
+ * no way for a global variable key signature to match a thread-local key
+ * signature.
+ */
+#if defined(sun)
+#define DTRACE_TLS_THRKEY(where) { \
+ uint_t intr = 0; \
+ uint_t actv = CPU->cpu_intr_actv >> (LOCK_LEVEL + 1); \
+ for (; actv; actv >>= 1) \
+ intr++; \
+ ASSERT(intr < (1 << 3)); \
+ (where) = ((curthread->t_did + DIF_VARIABLE_MAX) & \
+ (((uint64_t)1 << 61) - 1)) | ((uint64_t)intr << 61); \
+}
+#else
+#define DTRACE_TLS_THRKEY(where) { \
+ solaris_cpu_t *_c = &solaris_cpu[curcpu]; \
+ uint_t intr = 0; \
+ uint_t actv = _c->cpu_intr_actv; \
+ for (; actv; actv >>= 1) \
+ intr++; \
+ ASSERT(intr < (1 << 3)); \
+ (where) = ((curthread->td_tid + DIF_VARIABLE_MAX) & \
+ (((uint64_t)1 << 61) - 1)) | ((uint64_t)intr << 61); \
+}
+#endif
+
+#define DT_BSWAP_8(x) ((x) & 0xff)
+#define DT_BSWAP_16(x) ((DT_BSWAP_8(x) << 8) | DT_BSWAP_8((x) >> 8))
+#define DT_BSWAP_32(x) ((DT_BSWAP_16(x) << 16) | DT_BSWAP_16((x) >> 16))
+#define DT_BSWAP_64(x) ((DT_BSWAP_32(x) << 32) | DT_BSWAP_32((x) >> 32))
+
+#define DT_MASK_LO 0x00000000FFFFFFFFULL
+
+#define DTRACE_STORE(type, tomax, offset, what) \
+ *((type *)((uintptr_t)(tomax) + (uintptr_t)offset)) = (type)(what);
+
+#ifndef __i386
+#define DTRACE_ALIGNCHECK(addr, size, flags) \
+ if (addr & (size - 1)) { \
+ *flags |= CPU_DTRACE_BADALIGN; \
+ cpu_core[curcpu].cpuc_dtrace_illval = addr; \
+ return (0); \
+ }
+#else
+#define DTRACE_ALIGNCHECK(addr, size, flags)
+#endif
+
+/*
+ * Test whether a range of memory starting at testaddr of size testsz falls
+ * within the range of memory described by addr, sz. We take care to avoid
+ * problems with overflow and underflow of the unsigned quantities, and
+ * disallow all negative sizes. Ranges of size 0 are allowed.
+ */
+#define DTRACE_INRANGE(testaddr, testsz, baseaddr, basesz) \
+ ((testaddr) - (baseaddr) < (basesz) && \
+ (testaddr) + (testsz) - (baseaddr) <= (basesz) && \
+ (testaddr) + (testsz) >= (testaddr))
+
+/*
+ * Test whether alloc_sz bytes will fit in the scratch region. We isolate
+ * alloc_sz on the righthand side of the comparison in order to avoid overflow
+ * or underflow in the comparison with it. This is simpler than the INRANGE
+ * check above, because we know that the dtms_scratch_ptr is valid in the
+ * range. Allocations of size zero are allowed.
+ */
+#define DTRACE_INSCRATCH(mstate, alloc_sz) \
+ ((mstate)->dtms_scratch_base + (mstate)->dtms_scratch_size - \
+ (mstate)->dtms_scratch_ptr >= (alloc_sz))
+
+#define DTRACE_LOADFUNC(bits) \
+/*CSTYLED*/ \
+uint##bits##_t \
+dtrace_load##bits(uintptr_t addr) \
+{ \
+ size_t size = bits / NBBY; \
+ /*CSTYLED*/ \
+ uint##bits##_t rval; \
+ int i; \
+ volatile uint16_t *flags = (volatile uint16_t *) \
+ &cpu_core[curcpu].cpuc_dtrace_flags; \
+ \
+ DTRACE_ALIGNCHECK(addr, size, flags); \
+ \
+ for (i = 0; i < dtrace_toxranges; i++) { \
+ if (addr >= dtrace_toxrange[i].dtt_limit) \
+ continue; \
+ \
+ if (addr + size <= dtrace_toxrange[i].dtt_base) \
+ continue; \
+ \
+ /* \
+ * This address falls within a toxic region; return 0. \
+ */ \
+ *flags |= CPU_DTRACE_BADADDR; \
+ cpu_core[curcpu].cpuc_dtrace_illval = addr; \
+ return (0); \
+ } \
+ \
+ *flags |= CPU_DTRACE_NOFAULT; \
+ /*CSTYLED*/ \
+ rval = *((volatile uint##bits##_t *)addr); \
+ *flags &= ~CPU_DTRACE_NOFAULT; \
+ \
+ return (!(*flags & CPU_DTRACE_FAULT) ? rval : 0); \
+}
+
+#ifdef _LP64
+#define dtrace_loadptr dtrace_load64
+#else
+#define dtrace_loadptr dtrace_load32
+#endif
+
+#define DTRACE_DYNHASH_FREE 0
+#define DTRACE_DYNHASH_SINK 1
+#define DTRACE_DYNHASH_VALID 2
+
+#define DTRACE_MATCH_NEXT 0
+#define DTRACE_MATCH_DONE 1
+#define DTRACE_ANCHORED(probe) ((probe)->dtpr_func[0] != '\0')
+#define DTRACE_STATE_ALIGN 64
+
+#define DTRACE_FLAGS2FLT(flags) \
+ (((flags) & CPU_DTRACE_BADADDR) ? DTRACEFLT_BADADDR : \
+ ((flags) & CPU_DTRACE_ILLOP) ? DTRACEFLT_ILLOP : \
+ ((flags) & CPU_DTRACE_DIVZERO) ? DTRACEFLT_DIVZERO : \
+ ((flags) & CPU_DTRACE_KPRIV) ? DTRACEFLT_KPRIV : \
+ ((flags) & CPU_DTRACE_UPRIV) ? DTRACEFLT_UPRIV : \
+ ((flags) & CPU_DTRACE_TUPOFLOW) ? DTRACEFLT_TUPOFLOW : \
+ ((flags) & CPU_DTRACE_BADALIGN) ? DTRACEFLT_BADALIGN : \
+ ((flags) & CPU_DTRACE_NOSCRATCH) ? DTRACEFLT_NOSCRATCH : \
+ ((flags) & CPU_DTRACE_BADSTACK) ? DTRACEFLT_BADSTACK : \
+ DTRACEFLT_UNKNOWN)
+
+#define DTRACEACT_ISSTRING(act) \
+ ((act)->dta_kind == DTRACEACT_DIFEXPR && \
+ (act)->dta_difo->dtdo_rtype.dtdt_kind == DIF_TYPE_STRING)
+
+/* Function prototype definitions: */
+static size_t dtrace_strlen(const char *, size_t);
+static dtrace_probe_t *dtrace_probe_lookup_id(dtrace_id_t id);
+static void dtrace_enabling_provide(dtrace_provider_t *);
+static int dtrace_enabling_match(dtrace_enabling_t *, int *);
+static void dtrace_enabling_matchall(void);
+static dtrace_state_t *dtrace_anon_grab(void);
+#if defined(sun)
+static uint64_t dtrace_helper(int, dtrace_mstate_t *,
+ dtrace_state_t *, uint64_t, uint64_t);
+static dtrace_helpers_t *dtrace_helpers_create(proc_t *);
+#endif
+static void dtrace_buffer_drop(dtrace_buffer_t *);
+static intptr_t dtrace_buffer_reserve(dtrace_buffer_t *, size_t, size_t,
+ dtrace_state_t *, dtrace_mstate_t *);
+static int dtrace_state_option(dtrace_state_t *, dtrace_optid_t,
+ dtrace_optval_t);
+static int dtrace_ecb_create_enable(dtrace_probe_t *, void *);
+#if defined(sun)
+static void dtrace_helper_provider_destroy(dtrace_helper_provider_t *);
+#endif
+uint16_t dtrace_load16(uintptr_t);
+uint32_t dtrace_load32(uintptr_t);
+uint64_t dtrace_load64(uintptr_t);
+uint8_t dtrace_load8(uintptr_t);
+void dtrace_dynvar_clean(dtrace_dstate_t *);
+dtrace_dynvar_t *dtrace_dynvar(dtrace_dstate_t *, uint_t, dtrace_key_t *,
+ size_t, dtrace_dynvar_op_t, dtrace_mstate_t *, dtrace_vstate_t *);
+uintptr_t dtrace_dif_varstr(uintptr_t, dtrace_state_t *, dtrace_mstate_t *);
+
+/*
+ * DTrace Probe Context Functions
+ *
+ * These functions are called from probe context. Because probe context is
+ * any context in which C may be called, arbitrarily locks may be held,
+ * interrupts may be disabled, we may be in arbitrary dispatched state, etc.
+ * As a result, functions called from probe context may only call other DTrace
+ * support functions -- they may not interact at all with the system at large.
+ * (Note that the ASSERT macro is made probe-context safe by redefining it in
+ * terms of dtrace_assfail(), a probe-context safe function.) If arbitrary
+ * loads are to be performed from probe context, they _must_ be in terms of
+ * the safe dtrace_load*() variants.
+ *
+ * Some functions in this block are not actually called from probe context;
+ * for these functions, there will be a comment above the function reading
+ * "Note: not called from probe context."
+ */
+void
+dtrace_panic(const char *format, ...)
+{
+ va_list alist;
+
+ va_start(alist, format);
+ dtrace_vpanic(format, alist);
+ va_end(alist);
+}
+
+int
+dtrace_assfail(const char *a, const char *f, int l)
+{
+ dtrace_panic("assertion failed: %s, file: %s, line: %d", a, f, l);
+
+ /*
+ * We just need something here that even the most clever compiler
+ * cannot optimize away.
+ */
+ return (a[(uintptr_t)f]);
+}
+
+/*
+ * Atomically increment a specified error counter from probe context.
+ */
+static void
+dtrace_error(uint32_t *counter)
+{
+ /*
+ * Most counters stored to in probe context are per-CPU counters.
+ * However, there are some error conditions that are sufficiently
+ * arcane that they don't merit per-CPU storage. If these counters
+ * are incremented concurrently on different CPUs, scalability will be
+ * adversely affected -- but we don't expect them to be white-hot in a
+ * correctly constructed enabling...
+ */
+ uint32_t oval, nval;
+
+ do {
+ oval = *counter;
+
+ if ((nval = oval + 1) == 0) {
+ /*
+ * If the counter would wrap, set it to 1 -- assuring
+ * that the counter is never zero when we have seen
+ * errors. (The counter must be 32-bits because we
+ * aren't guaranteed a 64-bit compare&swap operation.)
+ * To save this code both the infamy of being fingered
+ * by a priggish news story and the indignity of being
+ * the target of a neo-puritan witch trial, we're
+ * carefully avoiding any colorful description of the
+ * likelihood of this condition -- but suffice it to
+ * say that it is only slightly more likely than the
+ * overflow of predicate cache IDs, as discussed in
+ * dtrace_predicate_create().
+ */
+ nval = 1;
+ }
+ } while (dtrace_cas32(counter, oval, nval) != oval);
+}
+
+/*
+ * Use the DTRACE_LOADFUNC macro to define functions for each of loading a
+ * uint8_t, a uint16_t, a uint32_t and a uint64_t.
+ */
+DTRACE_LOADFUNC(8)
+DTRACE_LOADFUNC(16)
+DTRACE_LOADFUNC(32)
+DTRACE_LOADFUNC(64)
+
+static int
+dtrace_inscratch(uintptr_t dest, size_t size, dtrace_mstate_t *mstate)
+{
+ if (dest < mstate->dtms_scratch_base)
+ return (0);
+
+ if (dest + size < dest)
+ return (0);
+
+ if (dest + size > mstate->dtms_scratch_ptr)
+ return (0);
+
+ return (1);
+}
+
+static int
+dtrace_canstore_statvar(uint64_t addr, size_t sz,
+ dtrace_statvar_t **svars, int nsvars)
+{
+ int i;
+
+ for (i = 0; i < nsvars; i++) {
+ dtrace_statvar_t *svar = svars[i];
+
+ if (svar == NULL || svar->dtsv_size == 0)
+ continue;
+
+ if (DTRACE_INRANGE(addr, sz, svar->dtsv_data, svar->dtsv_size))
+ return (1);
+ }
+
+ return (0);
+}
+
+/*
+ * Check to see if the address is within a memory region to which a store may
+ * be issued. This includes the DTrace scratch areas, and any DTrace variable
+ * region. The caller of dtrace_canstore() is responsible for performing any
+ * alignment checks that are needed before stores are actually executed.
+ */
+static int
+dtrace_canstore(uint64_t addr, size_t sz, dtrace_mstate_t *mstate,
+ dtrace_vstate_t *vstate)
+{
+ /*
+ * First, check to see if the address is in scratch space...
+ */
+ if (DTRACE_INRANGE(addr, sz, mstate->dtms_scratch_base,
+ mstate->dtms_scratch_size))
+ return (1);
+
+ /*
+ * Now check to see if it's a dynamic variable. This check will pick
+ * up both thread-local variables and any global dynamically-allocated
+ * variables.
+ */
+ if (DTRACE_INRANGE(addr, sz, (uintptr_t)vstate->dtvs_dynvars.dtds_base,
+ vstate->dtvs_dynvars.dtds_size)) {
+ dtrace_dstate_t *dstate = &vstate->dtvs_dynvars;
+ uintptr_t base = (uintptr_t)dstate->dtds_base +
+ (dstate->dtds_hashsize * sizeof (dtrace_dynhash_t));
+ uintptr_t chunkoffs;
+
+ /*
+ * Before we assume that we can store here, we need to make
+ * sure that it isn't in our metadata -- storing to our
+ * dynamic variable metadata would corrupt our state. For
+ * the range to not include any dynamic variable metadata,
+ * it must:
+ *
+ * (1) Start above the hash table that is at the base of
+ * the dynamic variable space
+ *
+ * (2) Have a starting chunk offset that is beyond the
+ * dtrace_dynvar_t that is at the base of every chunk
+ *
+ * (3) Not span a chunk boundary
+ *
+ */
+ if (addr < base)
+ return (0);
+
+ chunkoffs = (addr - base) % dstate->dtds_chunksize;
+
+ if (chunkoffs < sizeof (dtrace_dynvar_t))
+ return (0);
+
+ if (chunkoffs + sz > dstate->dtds_chunksize)
+ return (0);
+
+ return (1);
+ }
+
+ /*
+ * Finally, check the static local and global variables. These checks
+ * take the longest, so we perform them last.
+ */
+ if (dtrace_canstore_statvar(addr, sz,
+ vstate->dtvs_locals, vstate->dtvs_nlocals))
+ return (1);
+
+ if (dtrace_canstore_statvar(addr, sz,
+ vstate->dtvs_globals, vstate->dtvs_nglobals))
+ return (1);
+
+ return (0);
+}
+
+
+/*
+ * Convenience routine to check to see if the address is within a memory
+ * region in which a load may be issued given the user's privilege level;
+ * if not, it sets the appropriate error flags and loads 'addr' into the
+ * illegal value slot.
+ *
+ * DTrace subroutines (DIF_SUBR_*) should use this helper to implement
+ * appropriate memory access protection.
+ */
+static int
+dtrace_canload(uint64_t addr, size_t sz, dtrace_mstate_t *mstate,
+ dtrace_vstate_t *vstate)
+{
+ volatile uintptr_t *illval = &cpu_core[curcpu].cpuc_dtrace_illval;
+
+ /*
+ * If we hold the privilege to read from kernel memory, then
+ * everything is readable.
+ */
+ if ((mstate->dtms_access & DTRACE_ACCESS_KERNEL) != 0)
+ return (1);
+
+ /*
+ * You can obviously read that which you can store.
+ */
+ if (dtrace_canstore(addr, sz, mstate, vstate))
+ return (1);
+
+ /*
+ * We're allowed to read from our own string table.
+ */
+ if (DTRACE_INRANGE(addr, sz, (uintptr_t)mstate->dtms_difo->dtdo_strtab,
+ mstate->dtms_difo->dtdo_strlen))
+ return (1);
+
+ DTRACE_CPUFLAG_SET(CPU_DTRACE_KPRIV);
+ *illval = addr;
+ return (0);
+}
+
+/*
+ * Convenience routine to check to see if a given string is within a memory
+ * region in which a load may be issued given the user's privilege level;
+ * this exists so that we don't need to issue unnecessary dtrace_strlen()
+ * calls in the event that the user has all privileges.
+ */
+static int
+dtrace_strcanload(uint64_t addr, size_t sz, dtrace_mstate_t *mstate,
+ dtrace_vstate_t *vstate)
+{
+ size_t strsz;
+
+ /*
+ * If we hold the privilege to read from kernel memory, then
+ * everything is readable.
+ */
+ if ((mstate->dtms_access & DTRACE_ACCESS_KERNEL) != 0)
+ return (1);
+
+ strsz = 1 + dtrace_strlen((char *)(uintptr_t)addr, sz);
+ if (dtrace_canload(addr, strsz, mstate, vstate))
+ return (1);
+
+ return (0);
+}
+
+/*
+ * Convenience routine to check to see if a given variable is within a memory
+ * region in which a load may be issued given the user's privilege level.
+ */
+static int
+dtrace_vcanload(void *src, dtrace_diftype_t *type, dtrace_mstate_t *mstate,
+ dtrace_vstate_t *vstate)
+{
+ size_t sz;
+ ASSERT(type->dtdt_flags & DIF_TF_BYREF);
+
+ /*
+ * If we hold the privilege to read from kernel memory, then
+ * everything is readable.
+ */
+ if ((mstate->dtms_access & DTRACE_ACCESS_KERNEL) != 0)
+ return (1);
+
+ if (type->dtdt_kind == DIF_TYPE_STRING)
+ sz = dtrace_strlen(src,
+ vstate->dtvs_state->dts_options[DTRACEOPT_STRSIZE]) + 1;
+ else
+ sz = type->dtdt_size;
+
+ return (dtrace_canload((uintptr_t)src, sz, mstate, vstate));
+}
+
+/*
+ * Compare two strings using safe loads.
+ */
+static int
+dtrace_strncmp(char *s1, char *s2, size_t limit)
+{
+ uint8_t c1, c2;
+ volatile uint16_t *flags;
+
+ if (s1 == s2 || limit == 0)
+ return (0);
+
+ flags = (volatile uint16_t *)&cpu_core[curcpu].cpuc_dtrace_flags;
+
+ do {
+ if (s1 == NULL) {
+ c1 = '\0';
+ } else {
+ c1 = dtrace_load8((uintptr_t)s1++);
+ }
+
+ if (s2 == NULL) {
+ c2 = '\0';
+ } else {
+ c2 = dtrace_load8((uintptr_t)s2++);
+ }
+
+ if (c1 != c2)
+ return (c1 - c2);
+ } while (--limit && c1 != '\0' && !(*flags & CPU_DTRACE_FAULT));
+
+ return (0);
+}
+
+/*
+ * Compute strlen(s) for a string using safe memory accesses. The additional
+ * len parameter is used to specify a maximum length to ensure completion.
+ */
+static size_t
+dtrace_strlen(const char *s, size_t lim)
+{
+ uint_t len;
+
+ for (len = 0; len != lim; len++) {
+ if (dtrace_load8((uintptr_t)s++) == '\0')
+ break;
+ }
+
+ return (len);
+}
+
+/*
+ * Check if an address falls within a toxic region.
+ */
+static int
+dtrace_istoxic(uintptr_t kaddr, size_t size)
+{
+ uintptr_t taddr, tsize;
+ int i;
+
+ for (i = 0; i < dtrace_toxranges; i++) {
+ taddr = dtrace_toxrange[i].dtt_base;
+ tsize = dtrace_toxrange[i].dtt_limit - taddr;
+
+ if (kaddr - taddr < tsize) {
+ DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
+ cpu_core[curcpu].cpuc_dtrace_illval = kaddr;
+ return (1);
+ }
+
+ if (taddr - kaddr < size) {
+ DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
+ cpu_core[curcpu].cpuc_dtrace_illval = taddr;
+ return (1);
+ }
+ }
+
+ return (0);
+}
+
+/*
+ * Copy src to dst using safe memory accesses. The src is assumed to be unsafe
+ * memory specified by the DIF program. The dst is assumed to be safe memory
+ * that we can store to directly because it is managed by DTrace. As with
+ * standard bcopy, overlapping copies are handled properly.
+ */
+static void
+dtrace_bcopy(const void *src, void *dst, size_t len)
+{
+ if (len != 0) {
+ uint8_t *s1 = dst;
+ const uint8_t *s2 = src;
+
+ if (s1 <= s2) {
+ do {
+ *s1++ = dtrace_load8((uintptr_t)s2++);
+ } while (--len != 0);
+ } else {
+ s2 += len;
+ s1 += len;
+
+ do {
+ *--s1 = dtrace_load8((uintptr_t)--s2);
+ } while (--len != 0);
+ }
+ }
+}
+
+/*
+ * Copy src to dst using safe memory accesses, up to either the specified
+ * length, or the point that a nul byte is encountered. The src is assumed to
+ * be unsafe memory specified by the DIF program. The dst is assumed to be
+ * safe memory that we can store to directly because it is managed by DTrace.
+ * Unlike dtrace_bcopy(), overlapping regions are not handled.
+ */
+static void
+dtrace_strcpy(const void *src, void *dst, size_t len)
+{
+ if (len != 0) {
+ uint8_t *s1 = dst, c;
+ const uint8_t *s2 = src;
+
+ do {
+ *s1++ = c = dtrace_load8((uintptr_t)s2++);
+ } while (--len != 0 && c != '\0');
+ }
+}
+
+/*
+ * Copy src to dst, deriving the size and type from the specified (BYREF)
+ * variable type. The src is assumed to be unsafe memory specified by the DIF
+ * program. The dst is assumed to be DTrace variable memory that is of the
+ * specified type; we assume that we can store to directly.
+ */
+static void
+dtrace_vcopy(void *src, void *dst, dtrace_diftype_t *type)
+{
+ ASSERT(type->dtdt_flags & DIF_TF_BYREF);
+
+ if (type->dtdt_kind == DIF_TYPE_STRING) {
+ dtrace_strcpy(src, dst, type->dtdt_size);
+ } else {
+ dtrace_bcopy(src, dst, type->dtdt_size);
+ }
+}
+
+/*
+ * Compare s1 to s2 using safe memory accesses. The s1 data is assumed to be
+ * unsafe memory specified by the DIF program. The s2 data is assumed to be
+ * safe memory that we can access directly because it is managed by DTrace.
+ */
+static int
+dtrace_bcmp(const void *s1, const void *s2, size_t len)
+{
+ volatile uint16_t *flags;
+
+ flags = (volatile uint16_t *)&cpu_core[curcpu].cpuc_dtrace_flags;
+
+ if (s1 == s2)
+ return (0);
+
+ if (s1 == NULL || s2 == NULL)
+ return (1);
+
+ if (s1 != s2 && len != 0) {
+ const uint8_t *ps1 = s1;
+ const uint8_t *ps2 = s2;
+
+ do {
+ if (dtrace_load8((uintptr_t)ps1++) != *ps2++)
+ return (1);
+ } while (--len != 0 && !(*flags & CPU_DTRACE_FAULT));
+ }
+ return (0);
+}
+
+/*
+ * Zero the specified region using a simple byte-by-byte loop. Note that this
+ * is for safe DTrace-managed memory only.
+ */
+static void
+dtrace_bzero(void *dst, size_t len)
+{
+ uchar_t *cp;
+
+ for (cp = dst; len != 0; len--)
+ *cp++ = 0;
+}
+
+static void
+dtrace_add_128(uint64_t *addend1, uint64_t *addend2, uint64_t *sum)
+{
+ uint64_t result[2];
+
+ result[0] = addend1[0] + addend2[0];
+ result[1] = addend1[1] + addend2[1] +
+ (result[0] < addend1[0] || result[0] < addend2[0] ? 1 : 0);
+
+ sum[0] = result[0];
+ sum[1] = result[1];
+}
+
+/*
+ * Shift the 128-bit value in a by b. If b is positive, shift left.
+ * If b is negative, shift right.
+ */
+static void
+dtrace_shift_128(uint64_t *a, int b)
+{
+ uint64_t mask;
+
+ if (b == 0)
+ return;
+
+ if (b < 0) {
+ b = -b;
+ if (b >= 64) {
+ a[0] = a[1] >> (b - 64);
+ a[1] = 0;
+ } else {
+ a[0] >>= b;
+ mask = 1LL << (64 - b);
+ mask -= 1;
+ a[0] |= ((a[1] & mask) << (64 - b));
+ a[1] >>= b;
+ }
+ } else {
+ if (b >= 64) {
+ a[1] = a[0] << (b - 64);
+ a[0] = 0;
+ } else {
+ a[1] <<= b;
+ mask = a[0] >> (64 - b);
+ a[1] |= mask;
+ a[0] <<= b;
+ }
+ }
+}
+
+/*
+ * The basic idea is to break the 2 64-bit values into 4 32-bit values,
+ * use native multiplication on those, and then re-combine into the
+ * resulting 128-bit value.
+ *
+ * (hi1 << 32 + lo1) * (hi2 << 32 + lo2) =
+ * hi1 * hi2 << 64 +
+ * hi1 * lo2 << 32 +
+ * hi2 * lo1 << 32 +
+ * lo1 * lo2
+ */
+static void
+dtrace_multiply_128(uint64_t factor1, uint64_t factor2, uint64_t *product)
+{
+ uint64_t hi1, hi2, lo1, lo2;
+ uint64_t tmp[2];
+
+ hi1 = factor1 >> 32;
+ hi2 = factor2 >> 32;
+
+ lo1 = factor1 & DT_MASK_LO;
+ lo2 = factor2 & DT_MASK_LO;
+
+ product[0] = lo1 * lo2;
+ product[1] = hi1 * hi2;
+
+ tmp[0] = hi1 * lo2;
+ tmp[1] = 0;
+ dtrace_shift_128(tmp, 32);
+ dtrace_add_128(product, tmp, product);
+
+ tmp[0] = hi2 * lo1;
+ tmp[1] = 0;
+ dtrace_shift_128(tmp, 32);
+ dtrace_add_128(product, tmp, product);
+}
+
+/*
+ * This privilege check should be used by actions and subroutines to
+ * verify that the user credentials of the process that enabled the
+ * invoking ECB match the target credentials
+ */
+static int
+dtrace_priv_proc_common_user(dtrace_state_t *state)
+{
+ cred_t *cr, *s_cr = state->dts_cred.dcr_cred;
+
+ /*
+ * We should always have a non-NULL state cred here, since if cred
+ * is null (anonymous tracing), we fast-path bypass this routine.
+ */
+ ASSERT(s_cr != NULL);
+
+ if ((cr = CRED()) != NULL &&
+ s_cr->cr_uid == cr->cr_uid &&
+ s_cr->cr_uid == cr->cr_ruid &&
+ s_cr->cr_uid == cr->cr_suid &&
+ s_cr->cr_gid == cr->cr_gid &&
+ s_cr->cr_gid == cr->cr_rgid &&
+ s_cr->cr_gid == cr->cr_sgid)
+ return (1);
+
+ return (0);
+}
+
+/*
+ * This privilege check should be used by actions and subroutines to
+ * verify that the zone of the process that enabled the invoking ECB
+ * matches the target credentials
+ */
+static int
+dtrace_priv_proc_common_zone(dtrace_state_t *state)
+{
+#if defined(sun)
+ cred_t *cr, *s_cr = state->dts_cred.dcr_cred;
+
+ /*
+ * We should always have a non-NULL state cred here, since if cred
+ * is null (anonymous tracing), we fast-path bypass this routine.
+ */
+ ASSERT(s_cr != NULL);
+
+ if ((cr = CRED()) != NULL &&
+ s_cr->cr_zone == cr->cr_zone)
+ return (1);
+
+ return (0);
+#else
+ return (1);
+#endif
+}
+
+/*
+ * This privilege check should be used by actions and subroutines to
+ * verify that the process has not setuid or changed credentials.
+ */
+static int
+dtrace_priv_proc_common_nocd(void)
+{
+ proc_t *proc;
+
+ if ((proc = ttoproc(curthread)) != NULL &&
+ !(proc->p_flag & SNOCD))
+ return (1);
+
+ return (0);
+}
+
+static int
+dtrace_priv_proc_destructive(dtrace_state_t *state)
+{
+ int action = state->dts_cred.dcr_action;
+
+ if (((action & DTRACE_CRA_PROC_DESTRUCTIVE_ALLZONE) == 0) &&
+ dtrace_priv_proc_common_zone(state) == 0)
+ goto bad;
+
+ if (((action & DTRACE_CRA_PROC_DESTRUCTIVE_ALLUSER) == 0) &&
+ dtrace_priv_proc_common_user(state) == 0)
+ goto bad;
+
+ if (((action & DTRACE_CRA_PROC_DESTRUCTIVE_CREDCHG) == 0) &&
+ dtrace_priv_proc_common_nocd() == 0)
+ goto bad;
+
+ return (1);
+
+bad:
+ cpu_core[curcpu].cpuc_dtrace_flags |= CPU_DTRACE_UPRIV;
+
+ return (0);
+}
+
+static int
+dtrace_priv_proc_control(dtrace_state_t *state)
+{
+ if (state->dts_cred.dcr_action & DTRACE_CRA_PROC_CONTROL)
+ return (1);
+
+ if (dtrace_priv_proc_common_zone(state) &&
+ dtrace_priv_proc_common_user(state) &&
+ dtrace_priv_proc_common_nocd())
+ return (1);
+
+ cpu_core[curcpu].cpuc_dtrace_flags |= CPU_DTRACE_UPRIV;
+
+ return (0);
+}
+
+static int
+dtrace_priv_proc(dtrace_state_t *state)
+{
+ if (state->dts_cred.dcr_action & DTRACE_CRA_PROC)
+ return (1);
+
+ cpu_core[curcpu].cpuc_dtrace_flags |= CPU_DTRACE_UPRIV;
+
+ return (0);
+}
+
+static int
+dtrace_priv_kernel(dtrace_state_t *state)
+{
+ if (state->dts_cred.dcr_action & DTRACE_CRA_KERNEL)
+ return (1);
+
+ cpu_core[curcpu].cpuc_dtrace_flags |= CPU_DTRACE_KPRIV;
+
+ return (0);
+}
+
+static int
+dtrace_priv_kernel_destructive(dtrace_state_t *state)
+{
+ if (state->dts_cred.dcr_action & DTRACE_CRA_KERNEL_DESTRUCTIVE)
+ return (1);
+
+ cpu_core[curcpu].cpuc_dtrace_flags |= CPU_DTRACE_KPRIV;
+
+ return (0);
+}
+
+/*
+ * Note: not called from probe context. This function is called
+ * asynchronously (and at a regular interval) from outside of probe context to
+ * clean the dirty dynamic variable lists on all CPUs. Dynamic variable
+ * cleaning is explained in detail in <sys/dtrace_impl.h>.
+ */
+void
+dtrace_dynvar_clean(dtrace_dstate_t *dstate)
+{
+ dtrace_dynvar_t *dirty;
+ dtrace_dstate_percpu_t *dcpu;
+ int i, work = 0;
+
+ for (i = 0; i < NCPU; i++) {
+ dcpu = &dstate->dtds_percpu[i];
+
+ ASSERT(dcpu->dtdsc_rinsing == NULL);
+
+ /*
+ * If the dirty list is NULL, there is no dirty work to do.
+ */
+ if (dcpu->dtdsc_dirty == NULL)
+ continue;
+
+ /*
+ * If the clean list is non-NULL, then we're not going to do
+ * any work for this CPU -- it means that there has not been
+ * a dtrace_dynvar() allocation on this CPU (or from this CPU)
+ * since the last time we cleaned house.
+ */
+ if (dcpu->dtdsc_clean != NULL)
+ continue;
+
+ work = 1;
+
+ /*
+ * Atomically move the dirty list aside.
+ */
+ do {
+ dirty = dcpu->dtdsc_dirty;
+
+ /*
+ * Before we zap the dirty list, set the rinsing list.
+ * (This allows for a potential assertion in
+ * dtrace_dynvar(): if a free dynamic variable appears
+ * on a hash chain, either the dirty list or the
+ * rinsing list for some CPU must be non-NULL.)
+ */
+ dcpu->dtdsc_rinsing = dirty;
+ dtrace_membar_producer();
+ } while (dtrace_casptr(&dcpu->dtdsc_dirty,
+ dirty, NULL) != dirty);
+ }
+
+ if (!work) {
+ /*
+ * We have no work to do; we can simply return.
+ */
+ return;
+ }
+
+ dtrace_sync();
+
+ for (i = 0; i < NCPU; i++) {
+ dcpu = &dstate->dtds_percpu[i];
+
+ if (dcpu->dtdsc_rinsing == NULL)
+ continue;
+
+ /*
+ * We are now guaranteed that no hash chain contains a pointer
+ * into this dirty list; we can make it clean.
+ */
+ ASSERT(dcpu->dtdsc_clean == NULL);
+ dcpu->dtdsc_clean = dcpu->dtdsc_rinsing;
+ dcpu->dtdsc_rinsing = NULL;
+ }
+
+ /*
+ * Before we actually set the state to be DTRACE_DSTATE_CLEAN, make
+ * sure that all CPUs have seen all of the dtdsc_clean pointers.
+ * This prevents a race whereby a CPU incorrectly decides that
+ * the state should be something other than DTRACE_DSTATE_CLEAN
+ * after dtrace_dynvar_clean() has completed.
+ */
+ dtrace_sync();
+
+ dstate->dtds_state = DTRACE_DSTATE_CLEAN;
+}
+
+/*
+ * Depending on the value of the op parameter, this function looks-up,
+ * allocates or deallocates an arbitrarily-keyed dynamic variable. If an
+ * allocation is requested, this function will return a pointer to a
+ * dtrace_dynvar_t corresponding to the allocated variable -- or NULL if no
+ * variable can be allocated. If NULL is returned, the appropriate counter
+ * will be incremented.
+ */
+dtrace_dynvar_t *
+dtrace_dynvar(dtrace_dstate_t *dstate, uint_t nkeys,
+ dtrace_key_t *key, size_t dsize, dtrace_dynvar_op_t op,
+ dtrace_mstate_t *mstate, dtrace_vstate_t *vstate)
+{
+ uint64_t hashval = DTRACE_DYNHASH_VALID;
+ dtrace_dynhash_t *hash = dstate->dtds_hash;
+ dtrace_dynvar_t *free, *new_free, *next, *dvar, *start, *prev = NULL;
+ processorid_t me = curcpu, cpu = me;
+ dtrace_dstate_percpu_t *dcpu = &dstate->dtds_percpu[me];
+ size_t bucket, ksize;
+ size_t chunksize = dstate->dtds_chunksize;
+ uintptr_t kdata, lock, nstate;
+ uint_t i;
+
+ ASSERT(nkeys != 0);
+
+ /*
+ * Hash the key. As with aggregations, we use Jenkins' "One-at-a-time"
+ * algorithm. For the by-value portions, we perform the algorithm in
+ * 16-bit chunks (as opposed to 8-bit chunks). This speeds things up a
+ * bit, and seems to have only a minute effect on distribution. For
+ * the by-reference data, we perform "One-at-a-time" iterating (safely)
+ * over each referenced byte. It's painful to do this, but it's much
+ * better than pathological hash distribution. The efficacy of the
+ * hashing algorithm (and a comparison with other algorithms) may be
+ * found by running the ::dtrace_dynstat MDB dcmd.
+ */
+ for (i = 0; i < nkeys; i++) {
+ if (key[i].dttk_size == 0) {
+ uint64_t val = key[i].dttk_value;
+
+ hashval += (val >> 48) & 0xffff;
+ hashval += (hashval << 10);
+ hashval ^= (hashval >> 6);
+
+ hashval += (val >> 32) & 0xffff;
+ hashval += (hashval << 10);
+ hashval ^= (hashval >> 6);
+
+ hashval += (val >> 16) & 0xffff;
+ hashval += (hashval << 10);
+ hashval ^= (hashval >> 6);
+
+ hashval += val & 0xffff;
+ hashval += (hashval << 10);
+ hashval ^= (hashval >> 6);
+ } else {
+ /*
+ * This is incredibly painful, but it beats the hell
+ * out of the alternative.
+ */
+ uint64_t j, size = key[i].dttk_size;
+ uintptr_t base = (uintptr_t)key[i].dttk_value;
+
+ if (!dtrace_canload(base, size, mstate, vstate))
+ break;
+
+ for (j = 0; j < size; j++) {
+ hashval += dtrace_load8(base + j);
+ hashval += (hashval << 10);
+ hashval ^= (hashval >> 6);
+ }
+ }
+ }
+
+ if (DTRACE_CPUFLAG_ISSET(CPU_DTRACE_FAULT))
+ return (NULL);
+
+ hashval += (hashval << 3);
+ hashval ^= (hashval >> 11);
+ hashval += (hashval << 15);
+
+ /*
+ * There is a remote chance (ideally, 1 in 2^31) that our hashval
+ * comes out to be one of our two sentinel hash values. If this
+ * actually happens, we set the hashval to be a value known to be a
+ * non-sentinel value.
+ */
+ if (hashval == DTRACE_DYNHASH_FREE || hashval == DTRACE_DYNHASH_SINK)
+ hashval = DTRACE_DYNHASH_VALID;
+
+ /*
+ * Yes, it's painful to do a divide here. If the cycle count becomes
+ * important here, tricks can be pulled to reduce it. (However, it's
+ * critical that hash collisions be kept to an absolute minimum;
+ * they're much more painful than a divide.) It's better to have a
+ * solution that generates few collisions and still keeps things
+ * relatively simple.
+ */
+ bucket = hashval % dstate->dtds_hashsize;
+
+ if (op == DTRACE_DYNVAR_DEALLOC) {
+ volatile uintptr_t *lockp = &hash[bucket].dtdh_lock;
+
+ for (;;) {
+ while ((lock = *lockp) & 1)
+ continue;
+
+ if (dtrace_casptr((volatile void *)lockp,
+ (volatile void *)lock, (volatile void *)(lock + 1)) == (void *)lock)
+ break;
+ }
+
+ dtrace_membar_producer();
+ }
+
+top:
+ prev = NULL;
+ lock = hash[bucket].dtdh_lock;
+
+ dtrace_membar_consumer();
+
+ start = hash[bucket].dtdh_chain;
+ ASSERT(start != NULL && (start->dtdv_hashval == DTRACE_DYNHASH_SINK ||
+ start->dtdv_hashval != DTRACE_DYNHASH_FREE ||
+ op != DTRACE_DYNVAR_DEALLOC));
+
+ for (dvar = start; dvar != NULL; dvar = dvar->dtdv_next) {
+ dtrace_tuple_t *dtuple = &dvar->dtdv_tuple;
+ dtrace_key_t *dkey = &dtuple->dtt_key[0];
+
+ if (dvar->dtdv_hashval != hashval) {
+ if (dvar->dtdv_hashval == DTRACE_DYNHASH_SINK) {
+ /*
+ * We've reached the sink, and therefore the
+ * end of the hash chain; we can kick out of
+ * the loop knowing that we have seen a valid
+ * snapshot of state.
+ */
+ ASSERT(dvar->dtdv_next == NULL);
+ ASSERT(dvar == &dtrace_dynhash_sink);
+ break;
+ }
+
+ if (dvar->dtdv_hashval == DTRACE_DYNHASH_FREE) {
+ /*
+ * We've gone off the rails: somewhere along
+ * the line, one of the members of this hash
+ * chain was deleted. Note that we could also
+ * detect this by simply letting this loop run
+ * to completion, as we would eventually hit
+ * the end of the dirty list. However, we
+ * want to avoid running the length of the
+ * dirty list unnecessarily (it might be quite
+ * long), so we catch this as early as
+ * possible by detecting the hash marker. In
+ * this case, we simply set dvar to NULL and
+ * break; the conditional after the loop will
+ * send us back to top.
+ */
+ dvar = NULL;
+ break;
+ }
+
+ goto next;
+ }
+
+ if (dtuple->dtt_nkeys != nkeys)
+ goto next;
+
+ for (i = 0; i < nkeys; i++, dkey++) {
+ if (dkey->dttk_size != key[i].dttk_size)
+ goto next; /* size or type mismatch */
+
+ if (dkey->dttk_size != 0) {
+ if (dtrace_bcmp(
+ (void *)(uintptr_t)key[i].dttk_value,
+ (void *)(uintptr_t)dkey->dttk_value,
+ dkey->dttk_size))
+ goto next;
+ } else {
+ if (dkey->dttk_value != key[i].dttk_value)
+ goto next;
+ }
+ }
+
+ if (op != DTRACE_DYNVAR_DEALLOC)
+ return (dvar);
+
+ ASSERT(dvar->dtdv_next == NULL ||
+ dvar->dtdv_next->dtdv_hashval != DTRACE_DYNHASH_FREE);
+
+ if (prev != NULL) {
+ ASSERT(hash[bucket].dtdh_chain != dvar);
+ ASSERT(start != dvar);
+ ASSERT(prev->dtdv_next == dvar);
+ prev->dtdv_next = dvar->dtdv_next;
+ } else {
+ if (dtrace_casptr(&hash[bucket].dtdh_chain,
+ start, dvar->dtdv_next) != start) {
+ /*
+ * We have failed to atomically swing the
+ * hash table head pointer, presumably because
+ * of a conflicting allocation on another CPU.
+ * We need to reread the hash chain and try
+ * again.
+ */
+ goto top;
+ }
+ }
+
+ dtrace_membar_producer();
+
+ /*
+ * Now set the hash value to indicate that it's free.
+ */
+ ASSERT(hash[bucket].dtdh_chain != dvar);
+ dvar->dtdv_hashval = DTRACE_DYNHASH_FREE;
+
+ dtrace_membar_producer();
+
+ /*
+ * Set the next pointer to point at the dirty list, and
+ * atomically swing the dirty pointer to the newly freed dvar.
+ */
+ do {
+ next = dcpu->dtdsc_dirty;
+ dvar->dtdv_next = next;
+ } while (dtrace_casptr(&dcpu->dtdsc_dirty, next, dvar) != next);
+
+ /*
+ * Finally, unlock this hash bucket.
+ */
+ ASSERT(hash[bucket].dtdh_lock == lock);
+ ASSERT(lock & 1);
+ hash[bucket].dtdh_lock++;
+
+ return (NULL);
+next:
+ prev = dvar;
+ continue;
+ }
+
+ if (dvar == NULL) {
+ /*
+ * If dvar is NULL, it is because we went off the rails:
+ * one of the elements that we traversed in the hash chain
+ * was deleted while we were traversing it. In this case,
+ * we assert that we aren't doing a dealloc (deallocs lock
+ * the hash bucket to prevent themselves from racing with
+ * one another), and retry the hash chain traversal.
+ */
+ ASSERT(op != DTRACE_DYNVAR_DEALLOC);
+ goto top;
+ }
+
+ if (op != DTRACE_DYNVAR_ALLOC) {
+ /*
+ * If we are not to allocate a new variable, we want to
+ * return NULL now. Before we return, check that the value
+ * of the lock word hasn't changed. If it has, we may have
+ * seen an inconsistent snapshot.
+ */
+ if (op == DTRACE_DYNVAR_NOALLOC) {
+ if (hash[bucket].dtdh_lock != lock)
+ goto top;
+ } else {
+ ASSERT(op == DTRACE_DYNVAR_DEALLOC);
+ ASSERT(hash[bucket].dtdh_lock == lock);
+ ASSERT(lock & 1);
+ hash[bucket].dtdh_lock++;
+ }
+
+ return (NULL);
+ }
+
+ /*
+ * We need to allocate a new dynamic variable. The size we need is the
+ * size of dtrace_dynvar plus the size of nkeys dtrace_key_t's plus the
+ * size of any auxiliary key data (rounded up to 8-byte alignment) plus
+ * the size of any referred-to data (dsize). We then round the final
+ * size up to the chunksize for allocation.
+ */
+ for (ksize = 0, i = 0; i < nkeys; i++)
+ ksize += P2ROUNDUP(key[i].dttk_size, sizeof (uint64_t));
+
+ /*
+ * This should be pretty much impossible, but could happen if, say,
+ * strange DIF specified the tuple. Ideally, this should be an
+ * assertion and not an error condition -- but that requires that the
+ * chunksize calculation in dtrace_difo_chunksize() be absolutely
+ * bullet-proof. (That is, it must not be able to be fooled by
+ * malicious DIF.) Given the lack of backwards branches in DIF,
+ * solving this would presumably not amount to solving the Halting
+ * Problem -- but it still seems awfully hard.
+ */
+ if (sizeof (dtrace_dynvar_t) + sizeof (dtrace_key_t) * (nkeys - 1) +
+ ksize + dsize > chunksize) {
+ dcpu->dtdsc_drops++;
+ return (NULL);
+ }
+
+ nstate = DTRACE_DSTATE_EMPTY;
+
+ do {
+retry:
+ free = dcpu->dtdsc_free;
+
+ if (free == NULL) {
+ dtrace_dynvar_t *clean = dcpu->dtdsc_clean;
+ void *rval;
+
+ if (clean == NULL) {
+ /*
+ * We're out of dynamic variable space on
+ * this CPU. Unless we have tried all CPUs,
+ * we'll try to allocate from a different
+ * CPU.
+ */
+ switch (dstate->dtds_state) {
+ case DTRACE_DSTATE_CLEAN: {
+ void *sp = &dstate->dtds_state;
+
+ if (++cpu >= NCPU)
+ cpu = 0;
+
+ if (dcpu->dtdsc_dirty != NULL &&
+ nstate == DTRACE_DSTATE_EMPTY)
+ nstate = DTRACE_DSTATE_DIRTY;
+
+ if (dcpu->dtdsc_rinsing != NULL)
+ nstate = DTRACE_DSTATE_RINSING;
+
+ dcpu = &dstate->dtds_percpu[cpu];
+
+ if (cpu != me)
+ goto retry;
+
+ (void) dtrace_cas32(sp,
+ DTRACE_DSTATE_CLEAN, nstate);
+
+ /*
+ * To increment the correct bean
+ * counter, take another lap.
+ */
+ goto retry;
+ }
+
+ case DTRACE_DSTATE_DIRTY:
+ dcpu->dtdsc_dirty_drops++;
+ break;
+
+ case DTRACE_DSTATE_RINSING:
+ dcpu->dtdsc_rinsing_drops++;
+ break;
+
+ case DTRACE_DSTATE_EMPTY:
+ dcpu->dtdsc_drops++;
+ break;
+ }
+
+ DTRACE_CPUFLAG_SET(CPU_DTRACE_DROP);
+ return (NULL);
+ }
+
+ /*
+ * The clean list appears to be non-empty. We want to
+ * move the clean list to the free list; we start by
+ * moving the clean pointer aside.
+ */
+ if (dtrace_casptr(&dcpu->dtdsc_clean,
+ clean, NULL) != clean) {
+ /*
+ * We are in one of two situations:
+ *
+ * (a) The clean list was switched to the
+ * free list by another CPU.
+ *
+ * (b) The clean list was added to by the
+ * cleansing cyclic.
+ *
+ * In either of these situations, we can
+ * just reattempt the free list allocation.
+ */
+ goto retry;
+ }
+
+ ASSERT(clean->dtdv_hashval == DTRACE_DYNHASH_FREE);
+
+ /*
+ * Now we'll move the clean list to the free list.
+ * It's impossible for this to fail: the only way
+ * the free list can be updated is through this
+ * code path, and only one CPU can own the clean list.
+ * Thus, it would only be possible for this to fail if
+ * this code were racing with dtrace_dynvar_clean().
+ * (That is, if dtrace_dynvar_clean() updated the clean
+ * list, and we ended up racing to update the free
+ * list.) This race is prevented by the dtrace_sync()
+ * in dtrace_dynvar_clean() -- which flushes the
+ * owners of the clean lists out before resetting
+ * the clean lists.
+ */
+ rval = dtrace_casptr(&dcpu->dtdsc_free, NULL, clean);
+ ASSERT(rval == NULL);
+ goto retry;
+ }
+
+ dvar = free;
+ new_free = dvar->dtdv_next;
+ } while (dtrace_casptr(&dcpu->dtdsc_free, free, new_free) != free);
+
+ /*
+ * We have now allocated a new chunk. We copy the tuple keys into the
+ * tuple array and copy any referenced key data into the data space
+ * following the tuple array. As we do this, we relocate dttk_value
+ * in the final tuple to point to the key data address in the chunk.
+ */
+ kdata = (uintptr_t)&dvar->dtdv_tuple.dtt_key[nkeys];
+ dvar->dtdv_data = (void *)(kdata + ksize);
+ dvar->dtdv_tuple.dtt_nkeys = nkeys;
+
+ for (i = 0; i < nkeys; i++) {
+ dtrace_key_t *dkey = &dvar->dtdv_tuple.dtt_key[i];
+ size_t kesize = key[i].dttk_size;
+
+ if (kesize != 0) {
+ dtrace_bcopy(
+ (const void *)(uintptr_t)key[i].dttk_value,
+ (void *)kdata, kesize);
+ dkey->dttk_value = kdata;
+ kdata += P2ROUNDUP(kesize, sizeof (uint64_t));
+ } else {
+ dkey->dttk_value = key[i].dttk_value;
+ }
+
+ dkey->dttk_size = kesize;
+ }
+
+ ASSERT(dvar->dtdv_hashval == DTRACE_DYNHASH_FREE);
+ dvar->dtdv_hashval = hashval;
+ dvar->dtdv_next = start;
+
+ if (dtrace_casptr(&hash[bucket].dtdh_chain, start, dvar) == start)
+ return (dvar);
+
+ /*
+ * The cas has failed. Either another CPU is adding an element to
+ * this hash chain, or another CPU is deleting an element from this
+ * hash chain. The simplest way to deal with both of these cases
+ * (though not necessarily the most efficient) is to free our
+ * allocated block and tail-call ourselves. Note that the free is
+ * to the dirty list and _not_ to the free list. This is to prevent
+ * races with allocators, above.
+ */
+ dvar->dtdv_hashval = DTRACE_DYNHASH_FREE;
+
+ dtrace_membar_producer();
+
+ do {
+ free = dcpu->dtdsc_dirty;
+ dvar->dtdv_next = free;
+ } while (dtrace_casptr(&dcpu->dtdsc_dirty, free, dvar) != free);
+
+ return (dtrace_dynvar(dstate, nkeys, key, dsize, op, mstate, vstate));
+}
+
+/*ARGSUSED*/
+static void
+dtrace_aggregate_min(uint64_t *oval, uint64_t nval, uint64_t arg)
+{
+ if ((int64_t)nval < (int64_t)*oval)
+ *oval = nval;
+}
+
+/*ARGSUSED*/
+static void
+dtrace_aggregate_max(uint64_t *oval, uint64_t nval, uint64_t arg)
+{
+ if ((int64_t)nval > (int64_t)*oval)
+ *oval = nval;
+}
+
+static void
+dtrace_aggregate_quantize(uint64_t *quanta, uint64_t nval, uint64_t incr)
+{
+ int i, zero = DTRACE_QUANTIZE_ZEROBUCKET;
+ int64_t val = (int64_t)nval;
+
+ if (val < 0) {
+ for (i = 0; i < zero; i++) {
+ if (val <= DTRACE_QUANTIZE_BUCKETVAL(i)) {
+ quanta[i] += incr;
+ return;
+ }
+ }
+ } else {
+ for (i = zero + 1; i < DTRACE_QUANTIZE_NBUCKETS; i++) {
+ if (val < DTRACE_QUANTIZE_BUCKETVAL(i)) {
+ quanta[i - 1] += incr;
+ return;
+ }
+ }
+
+ quanta[DTRACE_QUANTIZE_NBUCKETS - 1] += incr;
+ return;
+ }
+
+ ASSERT(0);
+}
+
+static void
+dtrace_aggregate_lquantize(uint64_t *lquanta, uint64_t nval, uint64_t incr)
+{
+ uint64_t arg = *lquanta++;
+ int32_t base = DTRACE_LQUANTIZE_BASE(arg);
+ uint16_t step = DTRACE_LQUANTIZE_STEP(arg);
+ uint16_t levels = DTRACE_LQUANTIZE_LEVELS(arg);
+ int32_t val = (int32_t)nval, level;
+
+ ASSERT(step != 0);
+ ASSERT(levels != 0);
+
+ if (val < base) {
+ /*
+ * This is an underflow.
+ */
+ lquanta[0] += incr;
+ return;
+ }
+
+ level = (val - base) / step;
+
+ if (level < levels) {
+ lquanta[level + 1] += incr;
+ return;
+ }
+
+ /*
+ * This is an overflow.
+ */
+ lquanta[levels + 1] += incr;
+}
+
+/*ARGSUSED*/
+static void
+dtrace_aggregate_avg(uint64_t *data, uint64_t nval, uint64_t arg)
+{
+ data[0]++;
+ data[1] += nval;
+}
+
+/*ARGSUSED*/
+static void
+dtrace_aggregate_stddev(uint64_t *data, uint64_t nval, uint64_t arg)
+{
+ int64_t snval = (int64_t)nval;
+ uint64_t tmp[2];
+
+ data[0]++;
+ data[1] += nval;
+
+ /*
+ * What we want to say here is:
+ *
+ * data[2] += nval * nval;
+ *
+ * But given that nval is 64-bit, we could easily overflow, so
+ * we do this as 128-bit arithmetic.
+ */
+ if (snval < 0)
+ snval = -snval;
+
+ dtrace_multiply_128((uint64_t)snval, (uint64_t)snval, tmp);
+ dtrace_add_128(data + 2, tmp, data + 2);
+}
+
+/*ARGSUSED*/
+static void
+dtrace_aggregate_count(uint64_t *oval, uint64_t nval, uint64_t arg)
+{
+ *oval = *oval + 1;
+}
+
+/*ARGSUSED*/
+static void
+dtrace_aggregate_sum(uint64_t *oval, uint64_t nval, uint64_t arg)
+{
+ *oval += nval;
+}
+
+/*
+ * Aggregate given the tuple in the principal data buffer, and the aggregating
+ * action denoted by the specified dtrace_aggregation_t. The aggregation
+ * buffer is specified as the buf parameter. This routine does not return
+ * failure; if there is no space in the aggregation buffer, the data will be
+ * dropped, and a corresponding counter incremented.
+ */
+static void
+dtrace_aggregate(dtrace_aggregation_t *agg, dtrace_buffer_t *dbuf,
+ intptr_t offset, dtrace_buffer_t *buf, uint64_t expr, uint64_t arg)
+{
+ dtrace_recdesc_t *rec = &agg->dtag_action.dta_rec;
+ uint32_t i, ndx, size, fsize;
+ uint32_t align = sizeof (uint64_t) - 1;
+ dtrace_aggbuffer_t *agb;
+ dtrace_aggkey_t *key;
+ uint32_t hashval = 0, limit, isstr;
+ caddr_t tomax, data, kdata;
+ dtrace_actkind_t action;
+ dtrace_action_t *act;
+ uintptr_t offs;
+
+ if (buf == NULL)
+ return;
+
+ if (!agg->dtag_hasarg) {
+ /*
+ * Currently, only quantize() and lquantize() take additional
+ * arguments, and they have the same semantics: an increment
+ * value that defaults to 1 when not present. If additional
+ * aggregating actions take arguments, the setting of the
+ * default argument value will presumably have to become more
+ * sophisticated...
+ */
+ arg = 1;
+ }
+
+ action = agg->dtag_action.dta_kind - DTRACEACT_AGGREGATION;
+ size = rec->dtrd_offset - agg->dtag_base;
+ fsize = size + rec->dtrd_size;
+
+ ASSERT(dbuf->dtb_tomax != NULL);
+ data = dbuf->dtb_tomax + offset + agg->dtag_base;
+
+ if ((tomax = buf->dtb_tomax) == NULL) {
+ dtrace_buffer_drop(buf);
+ return;
+ }
+
+ /*
+ * The metastructure is always at the bottom of the buffer.
+ */
+ agb = (dtrace_aggbuffer_t *)(tomax + buf->dtb_size -
+ sizeof (dtrace_aggbuffer_t));
+
+ if (buf->dtb_offset == 0) {
+ /*
+ * We just kludge up approximately 1/8th of the size to be
+ * buckets. If this guess ends up being routinely
+ * off-the-mark, we may need to dynamically readjust this
+ * based on past performance.
+ */
+ uintptr_t hashsize = (buf->dtb_size >> 3) / sizeof (uintptr_t);
+
+ if ((uintptr_t)agb - hashsize * sizeof (dtrace_aggkey_t *) <
+ (uintptr_t)tomax || hashsize == 0) {
+ /*
+ * We've been given a ludicrously small buffer;
+ * increment our drop count and leave.
+ */
+ dtrace_buffer_drop(buf);
+ return;
+ }
+
+ /*
+ * And now, a pathetic attempt to try to get a an odd (or
+ * perchance, a prime) hash size for better hash distribution.
+ */
+ if (hashsize > (DTRACE_AGGHASHSIZE_SLEW << 3))
+ hashsize -= DTRACE_AGGHASHSIZE_SLEW;
+
+ agb->dtagb_hashsize = hashsize;
+ agb->dtagb_hash = (dtrace_aggkey_t **)((uintptr_t)agb -
+ agb->dtagb_hashsize * sizeof (dtrace_aggkey_t *));
+ agb->dtagb_free = (uintptr_t)agb->dtagb_hash;
+
+ for (i = 0; i < agb->dtagb_hashsize; i++)
+ agb->dtagb_hash[i] = NULL;
+ }
+
+ ASSERT(agg->dtag_first != NULL);
+ ASSERT(agg->dtag_first->dta_intuple);
+
+ /*
+ * Calculate the hash value based on the key. Note that we _don't_
+ * include the aggid in the hashing (but we will store it as part of
+ * the key). The hashing algorithm is Bob Jenkins' "One-at-a-time"
+ * algorithm: a simple, quick algorithm that has no known funnels, and
+ * gets good distribution in practice. The efficacy of the hashing
+ * algorithm (and a comparison with other algorithms) may be found by
+ * running the ::dtrace_aggstat MDB dcmd.
+ */
+ for (act = agg->dtag_first; act->dta_intuple; act = act->dta_next) {
+ i = act->dta_rec.dtrd_offset - agg->dtag_base;
+ limit = i + act->dta_rec.dtrd_size;
+ ASSERT(limit <= size);
+ isstr = DTRACEACT_ISSTRING(act);
+
+ for (; i < limit; i++) {
+ hashval += data[i];
+ hashval += (hashval << 10);
+ hashval ^= (hashval >> 6);
+
+ if (isstr && data[i] == '\0')
+ break;
+ }
+ }
+
+ hashval += (hashval << 3);
+ hashval ^= (hashval >> 11);
+ hashval += (hashval << 15);
+
+ /*
+ * Yes, the divide here is expensive -- but it's generally the least
+ * of the performance issues given the amount of data that we iterate
+ * over to compute hash values, compare data, etc.
+ */
+ ndx = hashval % agb->dtagb_hashsize;
+
+ for (key = agb->dtagb_hash[ndx]; key != NULL; key = key->dtak_next) {
+ ASSERT((caddr_t)key >= tomax);
+ ASSERT((caddr_t)key < tomax + buf->dtb_size);
+
+ if (hashval != key->dtak_hashval || key->dtak_size != size)
+ continue;
+
+ kdata = key->dtak_data;
+ ASSERT(kdata >= tomax && kdata < tomax + buf->dtb_size);
+
+ for (act = agg->dtag_first; act->dta_intuple;
+ act = act->dta_next) {
+ i = act->dta_rec.dtrd_offset - agg->dtag_base;
+ limit = i + act->dta_rec.dtrd_size;
+ ASSERT(limit <= size);
+ isstr = DTRACEACT_ISSTRING(act);
+
+ for (; i < limit; i++) {
+ if (kdata[i] != data[i])
+ goto next;
+
+ if (isstr && data[i] == '\0')
+ break;
+ }
+ }
+
+ if (action != key->dtak_action) {
+ /*
+ * We are aggregating on the same value in the same
+ * aggregation with two different aggregating actions.
+ * (This should have been picked up in the compiler,
+ * so we may be dealing with errant or devious DIF.)
+ * This is an error condition; we indicate as much,
+ * and return.
+ */
+ DTRACE_CPUFLAG_SET(CPU_DTRACE_ILLOP);
+ return;
+ }
+
+ /*
+ * This is a hit: we need to apply the aggregator to
+ * the value at this key.
+ */
+ agg->dtag_aggregate((uint64_t *)(kdata + size), expr, arg);
+ return;
+next:
+ continue;
+ }
+
+ /*
+ * We didn't find it. We need to allocate some zero-filled space,
+ * link it into the hash table appropriately, and apply the aggregator
+ * to the (zero-filled) value.
+ */
+ offs = buf->dtb_offset;
+ while (offs & (align - 1))
+ offs += sizeof (uint32_t);
+
+ /*
+ * If we don't have enough room to both allocate a new key _and_
+ * its associated data, increment the drop count and return.
+ */
+ if ((uintptr_t)tomax + offs + fsize >
+ agb->dtagb_free - sizeof (dtrace_aggkey_t)) {
+ dtrace_buffer_drop(buf);
+ return;
+ }
+
+ /*CONSTCOND*/
+ ASSERT(!(sizeof (dtrace_aggkey_t) & (sizeof (uintptr_t) - 1)));
+ key = (dtrace_aggkey_t *)(agb->dtagb_free - sizeof (dtrace_aggkey_t));
+ agb->dtagb_free -= sizeof (dtrace_aggkey_t);
+
+ key->dtak_data = kdata = tomax + offs;
+ buf->dtb_offset = offs + fsize;
+
+ /*
+ * Now copy the data across.
+ */
+ *((dtrace_aggid_t *)kdata) = agg->dtag_id;
+
+ for (i = sizeof (dtrace_aggid_t); i < size; i++)
+ kdata[i] = data[i];
+
+ /*
+ * Because strings are not zeroed out by default, we need to iterate
+ * looking for actions that store strings, and we need to explicitly
+ * pad these strings out with zeroes.
+ */
+ for (act = agg->dtag_first; act->dta_intuple; act = act->dta_next) {
+ int nul;
+
+ if (!DTRACEACT_ISSTRING(act))
+ continue;
+
+ i = act->dta_rec.dtrd_offset - agg->dtag_base;
+ limit = i + act->dta_rec.dtrd_size;
+ ASSERT(limit <= size);
+
+ for (nul = 0; i < limit; i++) {
+ if (nul) {
+ kdata[i] = '\0';
+ continue;
+ }
+
+ if (data[i] != '\0')
+ continue;
+
+ nul = 1;
+ }
+ }
+
+ for (i = size; i < fsize; i++)
+ kdata[i] = 0;
+
+ key->dtak_hashval = hashval;
+ key->dtak_size = size;
+ key->dtak_action = action;
+ key->dtak_next = agb->dtagb_hash[ndx];
+ agb->dtagb_hash[ndx] = key;
+
+ /*
+ * Finally, apply the aggregator.
+ */
+ *((uint64_t *)(key->dtak_data + size)) = agg->dtag_initial;
+ agg->dtag_aggregate((uint64_t *)(key->dtak_data + size), expr, arg);
+}
+
+/*
+ * Given consumer state, this routine finds a speculation in the INACTIVE
+ * state and transitions it into the ACTIVE state. If there is no speculation
+ * in the INACTIVE state, 0 is returned. In this case, no error counter is
+ * incremented -- it is up to the caller to take appropriate action.
+ */
+static int
+dtrace_speculation(dtrace_state_t *state)
+{
+ int i = 0;
+ dtrace_speculation_state_t current;
+ uint32_t *stat = &state->dts_speculations_unavail, count;
+
+ while (i < state->dts_nspeculations) {
+ dtrace_speculation_t *spec = &state->dts_speculations[i];
+
+ current = spec->dtsp_state;
+
+ if (current != DTRACESPEC_INACTIVE) {
+ if (current == DTRACESPEC_COMMITTINGMANY ||
+ current == DTRACESPEC_COMMITTING ||
+ current == DTRACESPEC_DISCARDING)
+ stat = &state->dts_speculations_busy;
+ i++;
+ continue;
+ }
+
+ if (dtrace_cas32((uint32_t *)&spec->dtsp_state,
+ current, DTRACESPEC_ACTIVE) == current)
+ return (i + 1);
+ }
+
+ /*
+ * We couldn't find a speculation. If we found as much as a single
+ * busy speculation buffer, we'll attribute this failure as "busy"
+ * instead of "unavail".
+ */
+ do {
+ count = *stat;
+ } while (dtrace_cas32(stat, count, count + 1) != count);
+
+ return (0);
+}
+
+/*
+ * This routine commits an active speculation. If the specified speculation
+ * is not in a valid state to perform a commit(), this routine will silently do
+ * nothing. The state of the specified speculation is transitioned according
+ * to the state transition diagram outlined in <sys/dtrace_impl.h>
+ */
+static void
+dtrace_speculation_commit(dtrace_state_t *state, processorid_t cpu,
+ dtrace_specid_t which)
+{
+ dtrace_speculation_t *spec;
+ dtrace_buffer_t *src, *dest;
+ uintptr_t daddr, saddr, dlimit;
+ dtrace_speculation_state_t current, new = 0;
+ intptr_t offs;
+
+ if (which == 0)
+ return;
+
+ if (which > state->dts_nspeculations) {
+ cpu_core[cpu].cpuc_dtrace_flags |= CPU_DTRACE_ILLOP;
+ return;
+ }
+
+ spec = &state->dts_speculations[which - 1];
+ src = &spec->dtsp_buffer[cpu];
+ dest = &state->dts_buffer[cpu];
+
+ do {
+ current = spec->dtsp_state;
+
+ if (current == DTRACESPEC_COMMITTINGMANY)
+ break;
+
+ switch (current) {
+ case DTRACESPEC_INACTIVE:
+ case DTRACESPEC_DISCARDING:
+ return;
+
+ case DTRACESPEC_COMMITTING:
+ /*
+ * This is only possible if we are (a) commit()'ing
+ * without having done a prior speculate() on this CPU
+ * and (b) racing with another commit() on a different
+ * CPU. There's nothing to do -- we just assert that
+ * our offset is 0.
+ */
+ ASSERT(src->dtb_offset == 0);
+ return;
+
+ case DTRACESPEC_ACTIVE:
+ new = DTRACESPEC_COMMITTING;
+ break;
+
+ case DTRACESPEC_ACTIVEONE:
+ /*
+ * This speculation is active on one CPU. If our
+ * buffer offset is non-zero, we know that the one CPU
+ * must be us. Otherwise, we are committing on a
+ * different CPU from the speculate(), and we must
+ * rely on being asynchronously cleaned.
+ */
+ if (src->dtb_offset != 0) {
+ new = DTRACESPEC_COMMITTING;
+ break;
+ }
+ /*FALLTHROUGH*/
+
+ case DTRACESPEC_ACTIVEMANY:
+ new = DTRACESPEC_COMMITTINGMANY;
+ break;
+
+ default:
+ ASSERT(0);
+ }
+ } while (dtrace_cas32((uint32_t *)&spec->dtsp_state,
+ current, new) != current);
+
+ /*
+ * We have set the state to indicate that we are committing this
+ * speculation. Now reserve the necessary space in the destination
+ * buffer.
+ */
+ if ((offs = dtrace_buffer_reserve(dest, src->dtb_offset,
+ sizeof (uint64_t), state, NULL)) < 0) {
+ dtrace_buffer_drop(dest);
+ goto out;
+ }
+
+ /*
+ * We have the space; copy the buffer across. (Note that this is a
+ * highly subobtimal bcopy(); in the unlikely event that this becomes
+ * a serious performance issue, a high-performance DTrace-specific
+ * bcopy() should obviously be invented.)
+ */
+ daddr = (uintptr_t)dest->dtb_tomax + offs;
+ dlimit = daddr + src->dtb_offset;
+ saddr = (uintptr_t)src->dtb_tomax;
+
+ /*
+ * First, the aligned portion.
+ */
+ while (dlimit - daddr >= sizeof (uint64_t)) {
+ *((uint64_t *)daddr) = *((uint64_t *)saddr);
+
+ daddr += sizeof (uint64_t);
+ saddr += sizeof (uint64_t);
+ }
+
+ /*
+ * Now any left-over bit...
+ */
+ while (dlimit - daddr)
+ *((uint8_t *)daddr++) = *((uint8_t *)saddr++);
+
+ /*
+ * Finally, commit the reserved space in the destination buffer.
+ */
+ dest->dtb_offset = offs + src->dtb_offset;
+
+out:
+ /*
+ * If we're lucky enough to be the only active CPU on this speculation
+ * buffer, we can just set the state back to DTRACESPEC_INACTIVE.
+ */
+ if (current == DTRACESPEC_ACTIVE ||
+ (current == DTRACESPEC_ACTIVEONE && new == DTRACESPEC_COMMITTING)) {
+ uint32_t rval = dtrace_cas32((uint32_t *)&spec->dtsp_state,
+ DTRACESPEC_COMMITTING, DTRACESPEC_INACTIVE);
+
+ ASSERT(rval == DTRACESPEC_COMMITTING);
+ }
+
+ src->dtb_offset = 0;
+ src->dtb_xamot_drops += src->dtb_drops;
+ src->dtb_drops = 0;
+}
+
+/*
+ * This routine discards an active speculation. If the specified speculation
+ * is not in a valid state to perform a discard(), this routine will silently
+ * do nothing. The state of the specified speculation is transitioned
+ * according to the state transition diagram outlined in <sys/dtrace_impl.h>
+ */
+static void
+dtrace_speculation_discard(dtrace_state_t *state, processorid_t cpu,
+ dtrace_specid_t which)
+{
+ dtrace_speculation_t *spec;
+ dtrace_speculation_state_t current, new = 0;
+ dtrace_buffer_t *buf;
+
+ if (which == 0)
+ return;
+
+ if (which > state->dts_nspeculations) {
+ cpu_core[cpu].cpuc_dtrace_flags |= CPU_DTRACE_ILLOP;
+ return;
+ }
+
+ spec = &state->dts_speculations[which - 1];
+ buf = &spec->dtsp_buffer[cpu];
+
+ do {
+ current = spec->dtsp_state;
+
+ switch (current) {
+ case DTRACESPEC_INACTIVE:
+ case DTRACESPEC_COMMITTINGMANY:
+ case DTRACESPEC_COMMITTING:
+ case DTRACESPEC_DISCARDING:
+ return;
+
+ case DTRACESPEC_ACTIVE:
+ case DTRACESPEC_ACTIVEMANY:
+ new = DTRACESPEC_DISCARDING;
+ break;
+
+ case DTRACESPEC_ACTIVEONE:
+ if (buf->dtb_offset != 0) {
+ new = DTRACESPEC_INACTIVE;
+ } else {
+ new = DTRACESPEC_DISCARDING;
+ }
+ break;
+
+ default:
+ ASSERT(0);
+ }
+ } while (dtrace_cas32((uint32_t *)&spec->dtsp_state,
+ current, new) != current);
+
+ buf->dtb_offset = 0;
+ buf->dtb_drops = 0;
+}
+
+/*
+ * Note: not called from probe context. This function is called
+ * asynchronously from cross call context to clean any speculations that are
+ * in the COMMITTINGMANY or DISCARDING states. These speculations may not be
+ * transitioned back to the INACTIVE state until all CPUs have cleaned the
+ * speculation.
+ */
+static void
+dtrace_speculation_clean_here(dtrace_state_t *state)
+{
+ dtrace_icookie_t cookie;
+ processorid_t cpu = curcpu;
+ dtrace_buffer_t *dest = &state->dts_buffer[cpu];
+ dtrace_specid_t i;
+
+ cookie = dtrace_interrupt_disable();
+
+ if (dest->dtb_tomax == NULL) {
+ dtrace_interrupt_enable(cookie);
+ return;
+ }
+
+ for (i = 0; i < state->dts_nspeculations; i++) {
+ dtrace_speculation_t *spec = &state->dts_speculations[i];
+ dtrace_buffer_t *src = &spec->dtsp_buffer[cpu];
+
+ if (src->dtb_tomax == NULL)
+ continue;
+
+ if (spec->dtsp_state == DTRACESPEC_DISCARDING) {
+ src->dtb_offset = 0;
+ continue;
+ }
+
+ if (spec->dtsp_state != DTRACESPEC_COMMITTINGMANY)
+ continue;
+
+ if (src->dtb_offset == 0)
+ continue;
+
+ dtrace_speculation_commit(state, cpu, i + 1);
+ }
+
+ dtrace_interrupt_enable(cookie);
+}
+
+/*
+ * Note: not called from probe context. This function is called
+ * asynchronously (and at a regular interval) to clean any speculations that
+ * are in the COMMITTINGMANY or DISCARDING states. If it discovers that there
+ * is work to be done, it cross calls all CPUs to perform that work;
+ * COMMITMANY and DISCARDING speculations may not be transitioned back to the
+ * INACTIVE state until they have been cleaned by all CPUs.
+ */
+static void
+dtrace_speculation_clean(dtrace_state_t *state)
+{
+ int work = 0, rv;
+ dtrace_specid_t i;
+
+ for (i = 0; i < state->dts_nspeculations; i++) {
+ dtrace_speculation_t *spec = &state->dts_speculations[i];
+
+ ASSERT(!spec->dtsp_cleaning);
+
+ if (spec->dtsp_state != DTRACESPEC_DISCARDING &&
+ spec->dtsp_state != DTRACESPEC_COMMITTINGMANY)
+ continue;
+
+ work++;
+ spec->dtsp_cleaning = 1;
+ }
+
+ if (!work)
+ return;
+
+ dtrace_xcall(DTRACE_CPUALL,
+ (dtrace_xcall_t)dtrace_speculation_clean_here, state);
+
+ /*
+ * We now know that all CPUs have committed or discarded their
+ * speculation buffers, as appropriate. We can now set the state
+ * to inactive.
+ */
+ for (i = 0; i < state->dts_nspeculations; i++) {
+ dtrace_speculation_t *spec = &state->dts_speculations[i];
+ dtrace_speculation_state_t current, new;
+
+ if (!spec->dtsp_cleaning)
+ continue;
+
+ current = spec->dtsp_state;
+ ASSERT(current == DTRACESPEC_DISCARDING ||
+ current == DTRACESPEC_COMMITTINGMANY);
+
+ new = DTRACESPEC_INACTIVE;
+
+ rv = dtrace_cas32((uint32_t *)&spec->dtsp_state, current, new);
+ ASSERT(rv == current);
+ spec->dtsp_cleaning = 0;
+ }
+}
+
+/*
+ * Called as part of a speculate() to get the speculative buffer associated
+ * with a given speculation. Returns NULL if the specified speculation is not
+ * in an ACTIVE state. If the speculation is in the ACTIVEONE state -- and
+ * the active CPU is not the specified CPU -- the speculation will be
+ * atomically transitioned into the ACTIVEMANY state.
+ */
+static dtrace_buffer_t *
+dtrace_speculation_buffer(dtrace_state_t *state, processorid_t cpuid,
+ dtrace_specid_t which)
+{
+ dtrace_speculation_t *spec;
+ dtrace_speculation_state_t current, new = 0;
+ dtrace_buffer_t *buf;
+
+ if (which == 0)
+ return (NULL);
+
+ if (which > state->dts_nspeculations) {
+ cpu_core[cpuid].cpuc_dtrace_flags |= CPU_DTRACE_ILLOP;
+ return (NULL);
+ }
+
+ spec = &state->dts_speculations[which - 1];
+ buf = &spec->dtsp_buffer[cpuid];
+
+ do {
+ current = spec->dtsp_state;
+
+ switch (current) {
+ case DTRACESPEC_INACTIVE:
+ case DTRACESPEC_COMMITTINGMANY:
+ case DTRACESPEC_DISCARDING:
+ return (NULL);
+
+ case DTRACESPEC_COMMITTING:
+ ASSERT(buf->dtb_offset == 0);
+ return (NULL);
+
+ case DTRACESPEC_ACTIVEONE:
+ /*
+ * This speculation is currently active on one CPU.
+ * Check the offset in the buffer; if it's non-zero,
+ * that CPU must be us (and we leave the state alone).
+ * If it's zero, assume that we're starting on a new
+ * CPU -- and change the state to indicate that the
+ * speculation is active on more than one CPU.
+ */
+ if (buf->dtb_offset != 0)
+ return (buf);
+
+ new = DTRACESPEC_ACTIVEMANY;
+ break;
+
+ case DTRACESPEC_ACTIVEMANY:
+ return (buf);
+
+ case DTRACESPEC_ACTIVE:
+ new = DTRACESPEC_ACTIVEONE;
+ break;
+
+ default:
+ ASSERT(0);
+ }
+ } while (dtrace_cas32((uint32_t *)&spec->dtsp_state,
+ current, new) != current);
+
+ ASSERT(new == DTRACESPEC_ACTIVEONE || new == DTRACESPEC_ACTIVEMANY);
+ return (buf);
+}
+
+/*
+ * Return a string. In the event that the user lacks the privilege to access
+ * arbitrary kernel memory, we copy the string out to scratch memory so that we
+ * don't fail access checking.
+ *
+ * dtrace_dif_variable() uses this routine as a helper for various
+ * builtin values such as 'execname' and 'probefunc.'
+ */
+uintptr_t
+dtrace_dif_varstr(uintptr_t addr, dtrace_state_t *state,
+ dtrace_mstate_t *mstate)
+{
+ uint64_t size = state->dts_options[DTRACEOPT_STRSIZE];
+ uintptr_t ret;
+ size_t strsz;
+
+ /*
+ * The easy case: this probe is allowed to read all of memory, so
+ * we can just return this as a vanilla pointer.
+ */
+ if ((mstate->dtms_access & DTRACE_ACCESS_KERNEL) != 0)
+ return (addr);
+
+ /*
+ * This is the tougher case: we copy the string in question from
+ * kernel memory into scratch memory and return it that way: this
+ * ensures that we won't trip up when access checking tests the
+ * BYREF return value.
+ */
+ strsz = dtrace_strlen((char *)addr, size) + 1;
+
+ if (mstate->dtms_scratch_ptr + strsz >
+ mstate->dtms_scratch_base + mstate->dtms_scratch_size) {
+ DTRACE_CPUFLAG_SET(CPU_DTRACE_NOSCRATCH);
+ return (0);
+ }
+
+ dtrace_strcpy((const void *)addr, (void *)mstate->dtms_scratch_ptr,
+ strsz);
+ ret = mstate->dtms_scratch_ptr;
+ mstate->dtms_scratch_ptr += strsz;
+ return (ret);
+}
+
+/*
+ * Return a string from a memoy address which is known to have one or
+ * more concatenated, individually zero terminated, sub-strings.
+ * In the event that the user lacks the privilege to access
+ * arbitrary kernel memory, we copy the string out to scratch memory so that we
+ * don't fail access checking.
+ *
+ * dtrace_dif_variable() uses this routine as a helper for various
+ * builtin values such as 'execargs'.
+ */
+static uintptr_t
+dtrace_dif_varstrz(uintptr_t addr, size_t strsz, dtrace_state_t *state,
+ dtrace_mstate_t *mstate)
+{
+ char *p;
+ size_t i;
+ uintptr_t ret;
+
+ if (mstate->dtms_scratch_ptr + strsz >
+ mstate->dtms_scratch_base + mstate->dtms_scratch_size) {
+ DTRACE_CPUFLAG_SET(CPU_DTRACE_NOSCRATCH);
+ return (0);
+ }
+
+ dtrace_bcopy((const void *)addr, (void *)mstate->dtms_scratch_ptr,
+ strsz);
+
+ /* Replace sub-string termination characters with a space. */
+ for (p = (char *) mstate->dtms_scratch_ptr, i = 0; i < strsz - 1;
+ p++, i++)
+ if (*p == '\0')
+ *p = ' ';
+
+ ret = mstate->dtms_scratch_ptr;
+ mstate->dtms_scratch_ptr += strsz;
+ return (ret);
+}
+
+/*
+ * This function implements the DIF emulator's variable lookups. The emulator
+ * passes a reserved variable identifier and optional built-in array index.
+ */
+static uint64_t
+dtrace_dif_variable(dtrace_mstate_t *mstate, dtrace_state_t *state, uint64_t v,
+ uint64_t ndx)
+{
+ /*
+ * If we're accessing one of the uncached arguments, we'll turn this
+ * into a reference in the args array.
+ */
+ if (v >= DIF_VAR_ARG0 && v <= DIF_VAR_ARG9) {
+ ndx = v - DIF_VAR_ARG0;
+ v = DIF_VAR_ARGS;
+ }
+
+ switch (v) {
+ case DIF_VAR_ARGS:
+ ASSERT(mstate->dtms_present & DTRACE_MSTATE_ARGS);
+ if (ndx >= sizeof (mstate->dtms_arg) /
+ sizeof (mstate->dtms_arg[0])) {
+ int aframes = mstate->dtms_probe->dtpr_aframes + 2;
+ dtrace_provider_t *pv;
+ uint64_t val;
+
+ pv = mstate->dtms_probe->dtpr_provider;
+ if (pv->dtpv_pops.dtps_getargval != NULL)
+ val = pv->dtpv_pops.dtps_getargval(pv->dtpv_arg,
+ mstate->dtms_probe->dtpr_id,
+ mstate->dtms_probe->dtpr_arg, ndx, aframes);
+ else
+ val = dtrace_getarg(ndx, aframes);
+
+ /*
+ * This is regrettably required to keep the compiler
+ * from tail-optimizing the call to dtrace_getarg().
+ * The condition always evaluates to true, but the
+ * compiler has no way of figuring that out a priori.
+ * (None of this would be necessary if the compiler
+ * could be relied upon to _always_ tail-optimize
+ * the call to dtrace_getarg() -- but it can't.)
+ */
+ if (mstate->dtms_probe != NULL)
+ return (val);
+
+ ASSERT(0);
+ }
+
+ return (mstate->dtms_arg[ndx]);
+
+#if defined(sun)
+ case DIF_VAR_UREGS: {
+ klwp_t *lwp;
+
+ if (!dtrace_priv_proc(state))
+ return (0);
+
+ if ((lwp = curthread->t_lwp) == NULL) {
+ DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
+ cpu_core[curcpu].cpuc_dtrace_illval = NULL;
+ return (0);
+ }
+
+ return (dtrace_getreg(lwp->lwp_regs, ndx));
+ return (0);
+ }
+#endif
+
+ case DIF_VAR_CURTHREAD:
+ if (!dtrace_priv_kernel(state))
+ return (0);
+ return ((uint64_t)(uintptr_t)curthread);
+
+ case DIF_VAR_TIMESTAMP:
+ if (!(mstate->dtms_present & DTRACE_MSTATE_TIMESTAMP)) {
+ mstate->dtms_timestamp = dtrace_gethrtime();
+ mstate->dtms_present |= DTRACE_MSTATE_TIMESTAMP;
+ }
+ return (mstate->dtms_timestamp);
+
+ case DIF_VAR_VTIMESTAMP:
+ ASSERT(dtrace_vtime_references != 0);
+ return (curthread->t_dtrace_vtime);
+
+ case DIF_VAR_WALLTIMESTAMP:
+ if (!(mstate->dtms_present & DTRACE_MSTATE_WALLTIMESTAMP)) {
+ mstate->dtms_walltimestamp = dtrace_gethrestime();
+ mstate->dtms_present |= DTRACE_MSTATE_WALLTIMESTAMP;
+ }
+ return (mstate->dtms_walltimestamp);
+
+#if defined(sun)
+ case DIF_VAR_IPL:
+ if (!dtrace_priv_kernel(state))
+ return (0);
+ if (!(mstate->dtms_present & DTRACE_MSTATE_IPL)) {
+ mstate->dtms_ipl = dtrace_getipl();
+ mstate->dtms_present |= DTRACE_MSTATE_IPL;
+ }
+ return (mstate->dtms_ipl);
+#endif
+
+ case DIF_VAR_EPID:
+ ASSERT(mstate->dtms_present & DTRACE_MSTATE_EPID);
+ return (mstate->dtms_epid);
+
+ case DIF_VAR_ID:
+ ASSERT(mstate->dtms_present & DTRACE_MSTATE_PROBE);
+ return (mstate->dtms_probe->dtpr_id);
+
+ case DIF_VAR_STACKDEPTH:
+ if (!dtrace_priv_kernel(state))
+ return (0);
+ if (!(mstate->dtms_present & DTRACE_MSTATE_STACKDEPTH)) {
+ int aframes = mstate->dtms_probe->dtpr_aframes + 2;
+
+ mstate->dtms_stackdepth = dtrace_getstackdepth(aframes);
+ mstate->dtms_present |= DTRACE_MSTATE_STACKDEPTH;
+ }
+ return (mstate->dtms_stackdepth);
+
+#if defined(sun)
+ case DIF_VAR_USTACKDEPTH:
+ if (!dtrace_priv_proc(state))
+ return (0);
+ if (!(mstate->dtms_present & DTRACE_MSTATE_USTACKDEPTH)) {
+ /*
+ * See comment in DIF_VAR_PID.
+ */
+ if (DTRACE_ANCHORED(mstate->dtms_probe) &&
+ CPU_ON_INTR(CPU)) {
+ mstate->dtms_ustackdepth = 0;
+ } else {
+ DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT);
+ mstate->dtms_ustackdepth =
+ dtrace_getustackdepth();
+ DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT);
+ }
+ mstate->dtms_present |= DTRACE_MSTATE_USTACKDEPTH;
+ }
+ return (mstate->dtms_ustackdepth);
+#endif
+
+ case DIF_VAR_CALLER:
+ if (!dtrace_priv_kernel(state))
+ return (0);
+ if (!(mstate->dtms_present & DTRACE_MSTATE_CALLER)) {
+ int aframes = mstate->dtms_probe->dtpr_aframes + 2;
+
+ if (!DTRACE_ANCHORED(mstate->dtms_probe)) {
+ /*
+ * If this is an unanchored probe, we are
+ * required to go through the slow path:
+ * dtrace_caller() only guarantees correct
+ * results for anchored probes.
+ */
+ pc_t caller[2] = {0, 0};
+
+ dtrace_getpcstack(caller, 2, aframes,
+ (uint32_t *)(uintptr_t)mstate->dtms_arg[0]);
+ mstate->dtms_caller = caller[1];
+ } else if ((mstate->dtms_caller =
+ dtrace_caller(aframes)) == -1) {
+ /*
+ * We have failed to do this the quick way;
+ * we must resort to the slower approach of
+ * calling dtrace_getpcstack().
+ */
+ pc_t caller = 0;
+
+ dtrace_getpcstack(&caller, 1, aframes, NULL);
+ mstate->dtms_caller = caller;
+ }
+
+ mstate->dtms_present |= DTRACE_MSTATE_CALLER;
+ }
+ return (mstate->dtms_caller);
+
+#if defined(sun)
+ case DIF_VAR_UCALLER:
+ if (!dtrace_priv_proc(state))
+ return (0);
+
+ if (!(mstate->dtms_present & DTRACE_MSTATE_UCALLER)) {
+ uint64_t ustack[3];
+
+ /*
+ * dtrace_getupcstack() fills in the first uint64_t
+ * with the current PID. The second uint64_t will
+ * be the program counter at user-level. The third
+ * uint64_t will contain the caller, which is what
+ * we're after.
+ */
+ ustack[2] = 0;
+ DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT);
+ dtrace_getupcstack(ustack, 3);
+ DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT);
+ mstate->dtms_ucaller = ustack[2];
+ mstate->dtms_present |= DTRACE_MSTATE_UCALLER;
+ }
+
+ return (mstate->dtms_ucaller);
+#endif
+
+ case DIF_VAR_PROBEPROV:
+ ASSERT(mstate->dtms_present & DTRACE_MSTATE_PROBE);
+ return (dtrace_dif_varstr(
+ (uintptr_t)mstate->dtms_probe->dtpr_provider->dtpv_name,
+ state, mstate));
+
+ case DIF_VAR_PROBEMOD:
+ ASSERT(mstate->dtms_present & DTRACE_MSTATE_PROBE);
+ return (dtrace_dif_varstr(
+ (uintptr_t)mstate->dtms_probe->dtpr_mod,
+ state, mstate));
+
+ case DIF_VAR_PROBEFUNC:
+ ASSERT(mstate->dtms_present & DTRACE_MSTATE_PROBE);
+ return (dtrace_dif_varstr(
+ (uintptr_t)mstate->dtms_probe->dtpr_func,
+ state, mstate));
+
+ case DIF_VAR_PROBENAME:
+ ASSERT(mstate->dtms_present & DTRACE_MSTATE_PROBE);
+ return (dtrace_dif_varstr(
+ (uintptr_t)mstate->dtms_probe->dtpr_name,
+ state, mstate));
+
+ case DIF_VAR_PID:
+ if (!dtrace_priv_proc(state))
+ return (0);
+
+#if defined(sun)
+ /*
+ * Note that we are assuming that an unanchored probe is
+ * always due to a high-level interrupt. (And we're assuming
+ * that there is only a single high level interrupt.)
+ */
+ if (DTRACE_ANCHORED(mstate->dtms_probe) && CPU_ON_INTR(CPU))
+ return (pid0.pid_id);
+
+ /*
+ * It is always safe to dereference one's own t_procp pointer:
+ * it always points to a valid, allocated proc structure.
+ * Further, it is always safe to dereference the p_pidp member
+ * of one's own proc structure. (These are truisms becuase
+ * threads and processes don't clean up their own state --
+ * they leave that task to whomever reaps them.)
+ */
+ return ((uint64_t)curthread->t_procp->p_pidp->pid_id);
+#else
+ return ((uint64_t)curproc->p_pid);
+#endif
+
+ case DIF_VAR_PPID:
+ if (!dtrace_priv_proc(state))
+ return (0);
+
+#if defined(sun)
+ /*
+ * See comment in DIF_VAR_PID.
+ */
+ if (DTRACE_ANCHORED(mstate->dtms_probe) && CPU_ON_INTR(CPU))
+ return (pid0.pid_id);
+
+ /*
+ * It is always safe to dereference one's own t_procp pointer:
+ * it always points to a valid, allocated proc structure.
+ * (This is true because threads don't clean up their own
+ * state -- they leave that task to whomever reaps them.)
+ */
+ return ((uint64_t)curthread->t_procp->p_ppid);
+#else
+ return ((uint64_t)curproc->p_pptr->p_pid);
+#endif
+
+ case DIF_VAR_TID:
+#if defined(sun)
+ /*
+ * See comment in DIF_VAR_PID.
+ */
+ if (DTRACE_ANCHORED(mstate->dtms_probe) && CPU_ON_INTR(CPU))
+ return (0);
+#endif
+
+ return ((uint64_t)curthread->t_tid);
+
+ case DIF_VAR_EXECARGS: {
+ struct pargs *p_args = curthread->td_proc->p_args;
+
+ if (p_args == NULL)
+ return(0);
+
+ return (dtrace_dif_varstrz(
+ (uintptr_t) p_args->ar_args, p_args->ar_length, state, mstate));
+ }
+
+ case DIF_VAR_EXECNAME:
+#if defined(sun)
+ if (!dtrace_priv_proc(state))
+ return (0);
+
+ /*
+ * See comment in DIF_VAR_PID.
+ */
+ if (DTRACE_ANCHORED(mstate->dtms_probe) && CPU_ON_INTR(CPU))
+ return ((uint64_t)(uintptr_t)p0.p_user.u_comm);
+
+ /*
+ * It is always safe to dereference one's own t_procp pointer:
+ * it always points to a valid, allocated proc structure.
+ * (This is true because threads don't clean up their own
+ * state -- they leave that task to whomever reaps them.)
+ */
+ return (dtrace_dif_varstr(
+ (uintptr_t)curthread->t_procp->p_user.u_comm,
+ state, mstate));
+#else
+ return (dtrace_dif_varstr(
+ (uintptr_t) curthread->td_proc->p_comm, state, mstate));
+#endif
+
+ case DIF_VAR_ZONENAME:
+#if defined(sun)
+ if (!dtrace_priv_proc(state))
+ return (0);
+
+ /*
+ * See comment in DIF_VAR_PID.
+ */
+ if (DTRACE_ANCHORED(mstate->dtms_probe) && CPU_ON_INTR(CPU))
+ return ((uint64_t)(uintptr_t)p0.p_zone->zone_name);
+
+ /*
+ * It is always safe to dereference one's own t_procp pointer:
+ * it always points to a valid, allocated proc structure.
+ * (This is true because threads don't clean up their own
+ * state -- they leave that task to whomever reaps them.)
+ */
+ return (dtrace_dif_varstr(
+ (uintptr_t)curthread->t_procp->p_zone->zone_name,
+ state, mstate));
+#else
+ return (0);
+#endif
+
+ case DIF_VAR_UID:
+ if (!dtrace_priv_proc(state))
+ return (0);
+
+#if defined(sun)
+ /*
+ * See comment in DIF_VAR_PID.
+ */
+ if (DTRACE_ANCHORED(mstate->dtms_probe) && CPU_ON_INTR(CPU))
+ return ((uint64_t)p0.p_cred->cr_uid);
+#endif
+
+ /*
+ * It is always safe to dereference one's own t_procp pointer:
+ * it always points to a valid, allocated proc structure.
+ * (This is true because threads don't clean up their own
+ * state -- they leave that task to whomever reaps them.)
+ *
+ * Additionally, it is safe to dereference one's own process
+ * credential, since this is never NULL after process birth.
+ */
+ return ((uint64_t)curthread->t_procp->p_cred->cr_uid);
+
+ case DIF_VAR_GID:
+ if (!dtrace_priv_proc(state))
+ return (0);
+
+#if defined(sun)
+ /*
+ * See comment in DIF_VAR_PID.
+ */
+ if (DTRACE_ANCHORED(mstate->dtms_probe) && CPU_ON_INTR(CPU))
+ return ((uint64_t)p0.p_cred->cr_gid);
+#endif
+
+ /*
+ * It is always safe to dereference one's own t_procp pointer:
+ * it always points to a valid, allocated proc structure.
+ * (This is true because threads don't clean up their own
+ * state -- they leave that task to whomever reaps them.)
+ *
+ * Additionally, it is safe to dereference one's own process
+ * credential, since this is never NULL after process birth.
+ */
+ return ((uint64_t)curthread->t_procp->p_cred->cr_gid);
+
+ case DIF_VAR_ERRNO: {
+#if defined(sun)
+ klwp_t *lwp;
+ if (!dtrace_priv_proc(state))
+ return (0);
+
+ /*
+ * See comment in DIF_VAR_PID.
+ */
+ if (DTRACE_ANCHORED(mstate->dtms_probe) && CPU_ON_INTR(CPU))
+ return (0);
+
+ /*
+ * It is always safe to dereference one's own t_lwp pointer in
+ * the event that this pointer is non-NULL. (This is true
+ * because threads and lwps don't clean up their own state --
+ * they leave that task to whomever reaps them.)
+ */
+ if ((lwp = curthread->t_lwp) == NULL)
+ return (0);
+
+ return ((uint64_t)lwp->lwp_errno);
+#else
+ return (curthread->td_errno);
+#endif
+ }
+ default:
+ DTRACE_CPUFLAG_SET(CPU_DTRACE_ILLOP);
+ return (0);
+ }
+}
+
+/*
+ * Emulate the execution of DTrace ID subroutines invoked by the call opcode.
+ * Notice that we don't bother validating the proper number of arguments or
+ * their types in the tuple stack. This isn't needed because all argument
+ * interpretation is safe because of our load safety -- the worst that can
+ * happen is that a bogus program can obtain bogus results.
+ */
+static void
+dtrace_dif_subr(uint_t subr, uint_t rd, uint64_t *regs,
+ dtrace_key_t *tupregs, int nargs,
+ dtrace_mstate_t *mstate, dtrace_state_t *state)
+{
+ volatile uint16_t *flags = &cpu_core[curcpu].cpuc_dtrace_flags;
+ volatile uintptr_t *illval = &cpu_core[curcpu].cpuc_dtrace_illval;
+ dtrace_vstate_t *vstate = &state->dts_vstate;
+
+#if defined(sun)
+ union {
+ mutex_impl_t mi;
+ uint64_t mx;
+ } m;
+
+ union {
+ krwlock_t ri;
+ uintptr_t rw;
+ } r;
+#else
+ union {
+ struct mtx *mi;
+ uintptr_t mx;
+ } m;
+ union {
+ struct sx *si;
+ uintptr_t sx;
+ } s;
+#endif
+
+ switch (subr) {
+ case DIF_SUBR_RAND:
+ regs[rd] = (dtrace_gethrtime() * 2416 + 374441) % 1771875;
+ break;
+
+#if defined(sun)
+ case DIF_SUBR_MUTEX_OWNED:
+ if (!dtrace_canload(tupregs[0].dttk_value, sizeof (kmutex_t),
+ mstate, vstate)) {
+ regs[rd] = 0;
+ break;
+ }
+
+ m.mx = dtrace_load64(tupregs[0].dttk_value);
+ if (MUTEX_TYPE_ADAPTIVE(&m.mi))
+ regs[rd] = MUTEX_OWNER(&m.mi) != MUTEX_NO_OWNER;
+ else
+ regs[rd] = LOCK_HELD(&m.mi.m_spin.m_spinlock);
+ break;
+
+ case DIF_SUBR_MUTEX_OWNER:
+ if (!dtrace_canload(tupregs[0].dttk_value, sizeof (kmutex_t),
+ mstate, vstate)) {
+ regs[rd] = 0;
+ break;
+ }
+
+ m.mx = dtrace_load64(tupregs[0].dttk_value);
+ if (MUTEX_TYPE_ADAPTIVE(&m.mi) &&
+ MUTEX_OWNER(&m.mi) != MUTEX_NO_OWNER)
+ regs[rd] = (uintptr_t)MUTEX_OWNER(&m.mi);
+ else
+ regs[rd] = 0;
+ break;
+
+ case DIF_SUBR_MUTEX_TYPE_ADAPTIVE:
+ if (!dtrace_canload(tupregs[0].dttk_value, sizeof (kmutex_t),
+ mstate, vstate)) {
+ regs[rd] = 0;
+ break;
+ }
+
+ m.mx = dtrace_load64(tupregs[0].dttk_value);
+ regs[rd] = MUTEX_TYPE_ADAPTIVE(&m.mi);
+ break;
+
+ case DIF_SUBR_MUTEX_TYPE_SPIN:
+ if (!dtrace_canload(tupregs[0].dttk_value, sizeof (kmutex_t),
+ mstate, vstate)) {
+ regs[rd] = 0;
+ break;
+ }
+
+ m.mx = dtrace_load64(tupregs[0].dttk_value);
+ regs[rd] = MUTEX_TYPE_SPIN(&m.mi);
+ break;
+
+ case DIF_SUBR_RW_READ_HELD: {
+ uintptr_t tmp;
+
+ if (!dtrace_canload(tupregs[0].dttk_value, sizeof (uintptr_t),
+ mstate, vstate)) {
+ regs[rd] = 0;
+ break;
+ }
+
+ r.rw = dtrace_loadptr(tupregs[0].dttk_value);
+ regs[rd] = _RW_READ_HELD(&r.ri, tmp);
+ break;
+ }
+
+ case DIF_SUBR_RW_WRITE_HELD:
+ if (!dtrace_canload(tupregs[0].dttk_value, sizeof (krwlock_t),
+ mstate, vstate)) {
+ regs[rd] = 0;
+ break;
+ }
+
+ r.rw = dtrace_loadptr(tupregs[0].dttk_value);
+ regs[rd] = _RW_WRITE_HELD(&r.ri);
+ break;
+
+ case DIF_SUBR_RW_ISWRITER:
+ if (!dtrace_canload(tupregs[0].dttk_value, sizeof (krwlock_t),
+ mstate, vstate)) {
+ regs[rd] = 0;
+ break;
+ }
+
+ r.rw = dtrace_loadptr(tupregs[0].dttk_value);
+ regs[rd] = _RW_ISWRITER(&r.ri);
+ break;
+
+#else
+ /*
+ * XXX - The following code works because mutex, rwlocks, & sxlocks
+ * all have similar data structures in FreeBSD. This may not be
+ * good if someone changes one of the lock data structures.
+ * Ideally, it would be nice if all these shared a common lock
+ * object.
+ */
+ case DIF_SUBR_MUTEX_OWNED:
+ /* XXX - need to use dtrace_canload() and dtrace_loadptr() */
+ m.mx = tupregs[0].dttk_value;
+
+#ifdef DOODAD
+ if (LO_CLASSINDEX(&(m.mi->lock_object)) < 2) {
+ regs[rd] = !(m.mi->mtx_lock & MTX_UNOWNED);
+ } else {
+ regs[rd] = !(m.mi->mtx_lock & SX_UNLOCKED);
+ }
+#endif
+ break;
+
+ case DIF_SUBR_MUTEX_OWNER:
+ /* XXX - need to use dtrace_canload() and dtrace_loadptr() */
+ m.mx = tupregs[0].dttk_value;
+
+ if (LO_CLASSINDEX(&(m.mi->lock_object)) < 2) {
+ regs[rd] = m.mi->mtx_lock & ~MTX_FLAGMASK;
+ } else {
+ if (!(m.mi->mtx_lock & SX_LOCK_SHARED))
+ regs[rd] = SX_OWNER(m.mi->mtx_lock);
+ else
+ regs[rd] = 0;
+ }
+ break;
+
+ case DIF_SUBR_MUTEX_TYPE_ADAPTIVE:
+ /* XXX - need to use dtrace_canload() and dtrace_loadptr() */
+ m.mx = tupregs[0].dttk_value;
+
+ regs[rd] = (LO_CLASSINDEX(&(m.mi->lock_object)) != 0);
+ break;
+
+ case DIF_SUBR_MUTEX_TYPE_SPIN:
+ /* XXX - need to use dtrace_canload() and dtrace_loadptr() */
+ m.mx = tupregs[0].dttk_value;
+
+ regs[rd] = (LO_CLASSINDEX(&(m.mi->lock_object)) == 0);
+ break;
+
+ case DIF_SUBR_RW_READ_HELD:
+ case DIF_SUBR_SX_SHARED_HELD:
+ /* XXX - need to use dtrace_canload() and dtrace_loadptr() */
+ s.sx = tupregs[0].dttk_value;
+ regs[rd] = ((s.si->sx_lock & SX_LOCK_SHARED) &&
+ (SX_OWNER(s.si->sx_lock) >> SX_SHARERS_SHIFT) != 0);
+ break;
+
+ case DIF_SUBR_RW_WRITE_HELD:
+ case DIF_SUBR_SX_EXCLUSIVE_HELD:
+ /* XXX - need to use dtrace_canload() and dtrace_loadptr() */
+ s.sx = tupregs[0].dttk_value;
+ regs[rd] = (SX_OWNER(s.si->sx_lock) == (uintptr_t) curthread);
+ break;
+
+ case DIF_SUBR_RW_ISWRITER:
+ case DIF_SUBR_SX_ISEXCLUSIVE:
+ /* XXX - need to use dtrace_canload() and dtrace_loadptr() */
+ s.sx = tupregs[0].dttk_value;
+ regs[rd] = ((s.si->sx_lock & SX_LOCK_EXCLUSIVE_WAITERS) ||
+ !(s.si->sx_lock & SX_LOCK_SHARED));
+ break;
+#endif /* ! defined(sun) */
+
+ case DIF_SUBR_BCOPY: {
+ /*
+ * We need to be sure that the destination is in the scratch
+ * region -- no other region is allowed.
+ */
+ uintptr_t src = tupregs[0].dttk_value;
+ uintptr_t dest = tupregs[1].dttk_value;
+ size_t size = tupregs[2].dttk_value;
+
+ if (!dtrace_inscratch(dest, size, mstate)) {
+ *flags |= CPU_DTRACE_BADADDR;
+ *illval = regs[rd];
+ break;
+ }
+
+ if (!dtrace_canload(src, size, mstate, vstate)) {
+ regs[rd] = 0;
+ break;
+ }
+
+ dtrace_bcopy((void *)src, (void *)dest, size);
+ break;
+ }
+
+ case DIF_SUBR_ALLOCA:
+ case DIF_SUBR_COPYIN: {
+ uintptr_t dest = P2ROUNDUP(mstate->dtms_scratch_ptr, 8);
+ uint64_t size =
+ tupregs[subr == DIF_SUBR_ALLOCA ? 0 : 1].dttk_value;
+ size_t scratch_size = (dest - mstate->dtms_scratch_ptr) + size;
+
+ /*
+ * This action doesn't require any credential checks since
+ * probes will not activate in user contexts to which the
+ * enabling user does not have permissions.
+ */
+
+ /*
+ * Rounding up the user allocation size could have overflowed
+ * a large, bogus allocation (like -1ULL) to 0.
+ */
+ if (scratch_size < size ||
+ !DTRACE_INSCRATCH(mstate, scratch_size)) {
+ DTRACE_CPUFLAG_SET(CPU_DTRACE_NOSCRATCH);
+ regs[rd] = 0;
+ break;
+ }
+
+ if (subr == DIF_SUBR_COPYIN) {
+ DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT);
+ dtrace_copyin(tupregs[0].dttk_value, dest, size, flags);
+ DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT);
+ }
+
+ mstate->dtms_scratch_ptr += scratch_size;
+ regs[rd] = dest;
+ break;
+ }
+
+ case DIF_SUBR_COPYINTO: {
+ uint64_t size = tupregs[1].dttk_value;
+ uintptr_t dest = tupregs[2].dttk_value;
+
+ /*
+ * This action doesn't require any credential checks since
+ * probes will not activate in user contexts to which the
+ * enabling user does not have permissions.
+ */
+ if (!dtrace_inscratch(dest, size, mstate)) {
+ *flags |= CPU_DTRACE_BADADDR;
+ *illval = regs[rd];
+ break;
+ }
+
+ DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT);
+ dtrace_copyin(tupregs[0].dttk_value, dest, size, flags);
+ DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT);
+ break;
+ }
+
+ case DIF_SUBR_COPYINSTR: {
+ uintptr_t dest = mstate->dtms_scratch_ptr;
+ uint64_t size = state->dts_options[DTRACEOPT_STRSIZE];
+
+ if (nargs > 1 && tupregs[1].dttk_value < size)
+ size = tupregs[1].dttk_value + 1;
+
+ /*
+ * This action doesn't require any credential checks since
+ * probes will not activate in user contexts to which the
+ * enabling user does not have permissions.
+ */
+ if (!DTRACE_INSCRATCH(mstate, size)) {
+ DTRACE_CPUFLAG_SET(CPU_DTRACE_NOSCRATCH);
+ regs[rd] = 0;
+ break;
+ }
+
+ DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT);
+ dtrace_copyinstr(tupregs[0].dttk_value, dest, size, flags);
+ DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT);
+
+ ((char *)dest)[size - 1] = '\0';
+ mstate->dtms_scratch_ptr += size;
+ regs[rd] = dest;
+ break;
+ }
+
+#if defined(sun)
+ case DIF_SUBR_MSGSIZE:
+ case DIF_SUBR_MSGDSIZE: {
+ uintptr_t baddr = tupregs[0].dttk_value, daddr;
+ uintptr_t wptr, rptr;
+ size_t count = 0;
+ int cont = 0;
+
+ while (baddr != 0 && !(*flags & CPU_DTRACE_FAULT)) {
+
+ if (!dtrace_canload(baddr, sizeof (mblk_t), mstate,
+ vstate)) {
+ regs[rd] = 0;
+ break;
+ }
+
+ wptr = dtrace_loadptr(baddr +
+ offsetof(mblk_t, b_wptr));
+
+ rptr = dtrace_loadptr(baddr +
+ offsetof(mblk_t, b_rptr));
+
+ if (wptr < rptr) {
+ *flags |= CPU_DTRACE_BADADDR;
+ *illval = tupregs[0].dttk_value;
+ break;
+ }
+
+ daddr = dtrace_loadptr(baddr +
+ offsetof(mblk_t, b_datap));
+
+ baddr = dtrace_loadptr(baddr +
+ offsetof(mblk_t, b_cont));
+
+ /*
+ * We want to prevent against denial-of-service here,
+ * so we're only going to search the list for
+ * dtrace_msgdsize_max mblks.
+ */
+ if (cont++ > dtrace_msgdsize_max) {
+ *flags |= CPU_DTRACE_ILLOP;
+ break;
+ }
+
+ if (subr == DIF_SUBR_MSGDSIZE) {
+ if (dtrace_load8(daddr +
+ offsetof(dblk_t, db_type)) != M_DATA)
+ continue;
+ }
+
+ count += wptr - rptr;
+ }
+
+ if (!(*flags & CPU_DTRACE_FAULT))
+ regs[rd] = count;
+
+ break;
+ }
+#endif
+
+ case DIF_SUBR_PROGENYOF: {
+ pid_t pid = tupregs[0].dttk_value;
+ proc_t *p;
+ int rval = 0;
+
+ DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT);
+
+ for (p = curthread->t_procp; p != NULL; p = p->p_parent) {
+#if defined(sun)
+ if (p->p_pidp->pid_id == pid) {
+#else
+ if (p->p_pid == pid) {
+#endif
+ rval = 1;
+ break;
+ }
+ }
+
+ DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT);
+
+ regs[rd] = rval;
+ break;
+ }
+
+ case DIF_SUBR_SPECULATION:
+ regs[rd] = dtrace_speculation(state);
+ break;
+
+ case DIF_SUBR_COPYOUT: {
+ uintptr_t kaddr = tupregs[0].dttk_value;
+ uintptr_t uaddr = tupregs[1].dttk_value;
+ uint64_t size = tupregs[2].dttk_value;
+
+ if (!dtrace_destructive_disallow &&
+ dtrace_priv_proc_control(state) &&
+ !dtrace_istoxic(kaddr, size)) {
+ DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT);
+ dtrace_copyout(kaddr, uaddr, size, flags);
+ DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT);
+ }
+ break;
+ }
+
+ case DIF_SUBR_COPYOUTSTR: {
+ uintptr_t kaddr = tupregs[0].dttk_value;
+ uintptr_t uaddr = tupregs[1].dttk_value;
+ uint64_t size = tupregs[2].dttk_value;
+
+ if (!dtrace_destructive_disallow &&
+ dtrace_priv_proc_control(state) &&
+ !dtrace_istoxic(kaddr, size)) {
+ DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT);
+ dtrace_copyoutstr(kaddr, uaddr, size, flags);
+ DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT);
+ }
+ break;
+ }
+
+ case DIF_SUBR_STRLEN: {
+ size_t sz;
+ uintptr_t addr = (uintptr_t)tupregs[0].dttk_value;
+ sz = dtrace_strlen((char *)addr,
+ state->dts_options[DTRACEOPT_STRSIZE]);
+
+ if (!dtrace_canload(addr, sz + 1, mstate, vstate)) {
+ regs[rd] = 0;
+ break;
+ }
+
+ regs[rd] = sz;
+
+ break;
+ }
+
+ case DIF_SUBR_STRCHR:
+ case DIF_SUBR_STRRCHR: {
+ /*
+ * We're going to iterate over the string looking for the
+ * specified character. We will iterate until we have reached
+ * the string length or we have found the character. If this
+ * is DIF_SUBR_STRRCHR, we will look for the last occurrence
+ * of the specified character instead of the first.
+ */
+ uintptr_t saddr = tupregs[0].dttk_value;
+ uintptr_t addr = tupregs[0].dttk_value;
+ uintptr_t limit = addr + state->dts_options[DTRACEOPT_STRSIZE];
+ char c, target = (char)tupregs[1].dttk_value;
+
+ for (regs[rd] = 0; addr < limit; addr++) {
+ if ((c = dtrace_load8(addr)) == target) {
+ regs[rd] = addr;
+
+ if (subr == DIF_SUBR_STRCHR)
+ break;
+ }
+
+ if (c == '\0')
+ break;
+ }
+
+ if (!dtrace_canload(saddr, addr - saddr, mstate, vstate)) {
+ regs[rd] = 0;
+ break;
+ }
+
+ break;
+ }
+
+ case DIF_SUBR_STRSTR:
+ case DIF_SUBR_INDEX:
+ case DIF_SUBR_RINDEX: {
+ /*
+ * We're going to iterate over the string looking for the
+ * specified string. We will iterate until we have reached
+ * the string length or we have found the string. (Yes, this
+ * is done in the most naive way possible -- but considering
+ * that the string we're searching for is likely to be
+ * relatively short, the complexity of Rabin-Karp or similar
+ * hardly seems merited.)
+ */
+ char *addr = (char *)(uintptr_t)tupregs[0].dttk_value;
+ char *substr = (char *)(uintptr_t)tupregs[1].dttk_value;
+ uint64_t size = state->dts_options[DTRACEOPT_STRSIZE];
+ size_t len = dtrace_strlen(addr, size);
+ size_t sublen = dtrace_strlen(substr, size);
+ char *limit = addr + len, *orig = addr;
+ int notfound = subr == DIF_SUBR_STRSTR ? 0 : -1;
+ int inc = 1;
+
+ regs[rd] = notfound;
+
+ if (!dtrace_canload((uintptr_t)addr, len + 1, mstate, vstate)) {
+ regs[rd] = 0;
+ break;
+ }
+
+ if (!dtrace_canload((uintptr_t)substr, sublen + 1, mstate,
+ vstate)) {
+ regs[rd] = 0;
+ break;
+ }
+
+ /*
+ * strstr() and index()/rindex() have similar semantics if
+ * both strings are the empty string: strstr() returns a
+ * pointer to the (empty) string, and index() and rindex()
+ * both return index 0 (regardless of any position argument).
+ */
+ if (sublen == 0 && len == 0) {
+ if (subr == DIF_SUBR_STRSTR)
+ regs[rd] = (uintptr_t)addr;
+ else
+ regs[rd] = 0;
+ break;
+ }
+
+ if (subr != DIF_SUBR_STRSTR) {
+ if (subr == DIF_SUBR_RINDEX) {
+ limit = orig - 1;
+ addr += len;
+ inc = -1;
+ }
+
+ /*
+ * Both index() and rindex() take an optional position
+ * argument that denotes the starting position.
+ */
+ if (nargs == 3) {
+ int64_t pos = (int64_t)tupregs[2].dttk_value;
+
+ /*
+ * If the position argument to index() is
+ * negative, Perl implicitly clamps it at
+ * zero. This semantic is a little surprising
+ * given the special meaning of negative
+ * positions to similar Perl functions like
+ * substr(), but it appears to reflect a
+ * notion that index() can start from a
+ * negative index and increment its way up to
+ * the string. Given this notion, Perl's
+ * rindex() is at least self-consistent in
+ * that it implicitly clamps positions greater
+ * than the string length to be the string
+ * length. Where Perl completely loses
+ * coherence, however, is when the specified
+ * substring is the empty string (""). In
+ * this case, even if the position is
+ * negative, rindex() returns 0 -- and even if
+ * the position is greater than the length,
+ * index() returns the string length. These
+ * semantics violate the notion that index()
+ * should never return a value less than the
+ * specified position and that rindex() should
+ * never return a value greater than the
+ * specified position. (One assumes that
+ * these semantics are artifacts of Perl's
+ * implementation and not the results of
+ * deliberate design -- it beggars belief that
+ * even Larry Wall could desire such oddness.)
+ * While in the abstract one would wish for
+ * consistent position semantics across
+ * substr(), index() and rindex() -- or at the
+ * very least self-consistent position
+ * semantics for index() and rindex() -- we
+ * instead opt to keep with the extant Perl
+ * semantics, in all their broken glory. (Do
+ * we have more desire to maintain Perl's
+ * semantics than Perl does? Probably.)
+ */
+ if (subr == DIF_SUBR_RINDEX) {
+ if (pos < 0) {
+ if (sublen == 0)
+ regs[rd] = 0;
+ break;
+ }
+
+ if (pos > len)
+ pos = len;
+ } else {
+ if (pos < 0)
+ pos = 0;
+
+ if (pos >= len) {
+ if (sublen == 0)
+ regs[rd] = len;
+ break;
+ }
+ }
+
+ addr = orig + pos;
+ }
+ }
+
+ for (regs[rd] = notfound; addr != limit; addr += inc) {
+ if (dtrace_strncmp(addr, substr, sublen) == 0) {
+ if (subr != DIF_SUBR_STRSTR) {
+ /*
+ * As D index() and rindex() are
+ * modeled on Perl (and not on awk),
+ * we return a zero-based (and not a
+ * one-based) index. (For you Perl
+ * weenies: no, we're not going to add
+ * $[ -- and shouldn't you be at a con
+ * or something?)
+ */
+ regs[rd] = (uintptr_t)(addr - orig);
+ break;
+ }
+
+ ASSERT(subr == DIF_SUBR_STRSTR);
+ regs[rd] = (uintptr_t)addr;
+ break;
+ }
+ }
+
+ break;
+ }
+
+ case DIF_SUBR_STRTOK: {
+ uintptr_t addr = tupregs[0].dttk_value;
+ uintptr_t tokaddr = tupregs[1].dttk_value;
+ uint64_t size = state->dts_options[DTRACEOPT_STRSIZE];
+ uintptr_t limit, toklimit = tokaddr + size;
+ uint8_t c = 0, tokmap[32]; /* 256 / 8 */
+ char *dest = (char *)mstate->dtms_scratch_ptr;
+ int i;
+
+ /*
+ * Check both the token buffer and (later) the input buffer,
+ * since both could be non-scratch addresses.
+ */
+ if (!dtrace_strcanload(tokaddr, size, mstate, vstate)) {
+ regs[rd] = 0;
+ break;
+ }
+
+ if (!DTRACE_INSCRATCH(mstate, size)) {
+ DTRACE_CPUFLAG_SET(CPU_DTRACE_NOSCRATCH);
+ regs[rd] = 0;
+ break;
+ }
+
+ if (addr == 0) {
+ /*
+ * If the address specified is NULL, we use our saved
+ * strtok pointer from the mstate. Note that this
+ * means that the saved strtok pointer is _only_
+ * valid within multiple enablings of the same probe --
+ * it behaves like an implicit clause-local variable.
+ */
+ addr = mstate->dtms_strtok;
+ } else {
+ /*
+ * If the user-specified address is non-NULL we must
+ * access check it. This is the only time we have
+ * a chance to do so, since this address may reside
+ * in the string table of this clause-- future calls
+ * (when we fetch addr from mstate->dtms_strtok)
+ * would fail this access check.
+ */
+ if (!dtrace_strcanload(addr, size, mstate, vstate)) {
+ regs[rd] = 0;
+ break;
+ }
+ }
+
+ /*
+ * First, zero the token map, and then process the token
+ * string -- setting a bit in the map for every character
+ * found in the token string.
+ */
+ for (i = 0; i < sizeof (tokmap); i++)
+ tokmap[i] = 0;
+
+ for (; tokaddr < toklimit; tokaddr++) {
+ if ((c = dtrace_load8(tokaddr)) == '\0')
+ break;
+
+ ASSERT((c >> 3) < sizeof (tokmap));
+ tokmap[c >> 3] |= (1 << (c & 0x7));
+ }
+
+ for (limit = addr + size; addr < limit; addr++) {
+ /*
+ * We're looking for a character that is _not_ contained
+ * in the token string.
+ */
+ if ((c = dtrace_load8(addr)) == '\0')
+ break;
+
+ if (!(tokmap[c >> 3] & (1 << (c & 0x7))))
+ break;
+ }
+
+ if (c == '\0') {
+ /*
+ * We reached the end of the string without finding
+ * any character that was not in the token string.
+ * We return NULL in this case, and we set the saved
+ * address to NULL as well.
+ */
+ regs[rd] = 0;
+ mstate->dtms_strtok = 0;
+ break;
+ }
+
+ /*
+ * From here on, we're copying into the destination string.
+ */
+ for (i = 0; addr < limit && i < size - 1; addr++) {
+ if ((c = dtrace_load8(addr)) == '\0')
+ break;
+
+ if (tokmap[c >> 3] & (1 << (c & 0x7)))
+ break;
+
+ ASSERT(i < size);
+ dest[i++] = c;
+ }
+
+ ASSERT(i < size);
+ dest[i] = '\0';
+ regs[rd] = (uintptr_t)dest;
+ mstate->dtms_scratch_ptr += size;
+ mstate->dtms_strtok = addr;
+ break;
+ }
+
+ case DIF_SUBR_SUBSTR: {
+ uintptr_t s = tupregs[0].dttk_value;
+ uint64_t size = state->dts_options[DTRACEOPT_STRSIZE];
+ char *d = (char *)mstate->dtms_scratch_ptr;
+ int64_t index = (int64_t)tupregs[1].dttk_value;
+ int64_t remaining = (int64_t)tupregs[2].dttk_value;
+ size_t len = dtrace_strlen((char *)s, size);
+ int64_t i = 0;
+
+ if (!dtrace_canload(s, len + 1, mstate, vstate)) {
+ regs[rd] = 0;
+ break;
+ }
+
+ if (!DTRACE_INSCRATCH(mstate, size)) {
+ DTRACE_CPUFLAG_SET(CPU_DTRACE_NOSCRATCH);
+ regs[rd] = 0;
+ break;
+ }
+
+ if (nargs <= 2)
+ remaining = (int64_t)size;
+
+ if (index < 0) {
+ index += len;
+
+ if (index < 0 && index + remaining > 0) {
+ remaining += index;
+ index = 0;
+ }
+ }
+
+ if (index >= len || index < 0) {
+ remaining = 0;
+ } else if (remaining < 0) {
+ remaining += len - index;
+ } else if (index + remaining > size) {
+ remaining = size - index;
+ }
+
+ for (i = 0; i < remaining; i++) {
+ if ((d[i] = dtrace_load8(s + index + i)) == '\0')
+ break;
+ }
+
+ d[i] = '\0';
+
+ mstate->dtms_scratch_ptr += size;
+ regs[rd] = (uintptr_t)d;
+ break;
+ }
+
+#if defined(sun)
+ case DIF_SUBR_GETMAJOR:
+#ifdef _LP64
+ regs[rd] = (tupregs[0].dttk_value >> NBITSMINOR64) & MAXMAJ64;
+#else
+ regs[rd] = (tupregs[0].dttk_value >> NBITSMINOR) & MAXMAJ;
+#endif
+ break;
+
+ case DIF_SUBR_GETMINOR:
+#ifdef _LP64
+ regs[rd] = tupregs[0].dttk_value & MAXMIN64;
+#else
+ regs[rd] = tupregs[0].dttk_value & MAXMIN;
+#endif
+ break;
+
+ case DIF_SUBR_DDI_PATHNAME: {
+ /*
+ * This one is a galactic mess. We are going to roughly
+ * emulate ddi_pathname(), but it's made more complicated
+ * by the fact that we (a) want to include the minor name and
+ * (b) must proceed iteratively instead of recursively.
+ */
+ uintptr_t dest = mstate->dtms_scratch_ptr;
+ uint64_t size = state->dts_options[DTRACEOPT_STRSIZE];
+ char *start = (char *)dest, *end = start + size - 1;
+ uintptr_t daddr = tupregs[0].dttk_value;
+ int64_t minor = (int64_t)tupregs[1].dttk_value;
+ char *s;
+ int i, len, depth = 0;
+
+ /*
+ * Due to all the pointer jumping we do and context we must
+ * rely upon, we just mandate that the user must have kernel
+ * read privileges to use this routine.
+ */
+ if ((mstate->dtms_access & DTRACE_ACCESS_KERNEL) == 0) {
+ *flags |= CPU_DTRACE_KPRIV;
+ *illval = daddr;
+ regs[rd] = 0;
+ }
+
+ if (!DTRACE_INSCRATCH(mstate, size)) {
+ DTRACE_CPUFLAG_SET(CPU_DTRACE_NOSCRATCH);
+ regs[rd] = 0;
+ break;
+ }
+
+ *end = '\0';
+
+ /*
+ * We want to have a name for the minor. In order to do this,
+ * we need to walk the minor list from the devinfo. We want
+ * to be sure that we don't infinitely walk a circular list,
+ * so we check for circularity by sending a scout pointer
+ * ahead two elements for every element that we iterate over;
+ * if the list is circular, these will ultimately point to the
+ * same element. You may recognize this little trick as the
+ * answer to a stupid interview question -- one that always
+ * seems to be asked by those who had to have it laboriously
+ * explained to them, and who can't even concisely describe
+ * the conditions under which one would be forced to resort to
+ * this technique. Needless to say, those conditions are
+ * found here -- and probably only here. Is this the only use
+ * of this infamous trick in shipping, production code? If it
+ * isn't, it probably should be...
+ */
+ if (minor != -1) {
+ uintptr_t maddr = dtrace_loadptr(daddr +
+ offsetof(struct dev_info, devi_minor));
+
+ uintptr_t next = offsetof(struct ddi_minor_data, next);
+ uintptr_t name = offsetof(struct ddi_minor_data,
+ d_minor) + offsetof(struct ddi_minor, name);
+ uintptr_t dev = offsetof(struct ddi_minor_data,
+ d_minor) + offsetof(struct ddi_minor, dev);
+ uintptr_t scout;
+
+ if (maddr != NULL)
+ scout = dtrace_loadptr(maddr + next);
+
+ while (maddr != NULL && !(*flags & CPU_DTRACE_FAULT)) {
+ uint64_t m;
+#ifdef _LP64
+ m = dtrace_load64(maddr + dev) & MAXMIN64;
+#else
+ m = dtrace_load32(maddr + dev) & MAXMIN;
+#endif
+ if (m != minor) {
+ maddr = dtrace_loadptr(maddr + next);
+
+ if (scout == NULL)
+ continue;
+
+ scout = dtrace_loadptr(scout + next);
+
+ if (scout == NULL)
+ continue;
+
+ scout = dtrace_loadptr(scout + next);
+
+ if (scout == NULL)
+ continue;
+
+ if (scout == maddr) {
+ *flags |= CPU_DTRACE_ILLOP;
+ break;
+ }
+
+ continue;
+ }
+
+ /*
+ * We have the minor data. Now we need to
+ * copy the minor's name into the end of the
+ * pathname.
+ */
+ s = (char *)dtrace_loadptr(maddr + name);
+ len = dtrace_strlen(s, size);
+
+ if (*flags & CPU_DTRACE_FAULT)
+ break;
+
+ if (len != 0) {
+ if ((end -= (len + 1)) < start)
+ break;
+
+ *end = ':';
+ }
+
+ for (i = 1; i <= len; i++)
+ end[i] = dtrace_load8((uintptr_t)s++);
+ break;
+ }
+ }
+
+ while (daddr != NULL && !(*flags & CPU_DTRACE_FAULT)) {
+ ddi_node_state_t devi_state;
+
+ devi_state = dtrace_load32(daddr +
+ offsetof(struct dev_info, devi_node_state));
+
+ if (*flags & CPU_DTRACE_FAULT)
+ break;
+
+ if (devi_state >= DS_INITIALIZED) {
+ s = (char *)dtrace_loadptr(daddr +
+ offsetof(struct dev_info, devi_addr));
+ len = dtrace_strlen(s, size);
+
+ if (*flags & CPU_DTRACE_FAULT)
+ break;
+
+ if (len != 0) {
+ if ((end -= (len + 1)) < start)
+ break;
+
+ *end = '@';
+ }
+
+ for (i = 1; i <= len; i++)
+ end[i] = dtrace_load8((uintptr_t)s++);
+ }
+
+ /*
+ * Now for the node name...
+ */
+ s = (char *)dtrace_loadptr(daddr +
+ offsetof(struct dev_info, devi_node_name));
+
+ daddr = dtrace_loadptr(daddr +
+ offsetof(struct dev_info, devi_parent));
+
+ /*
+ * If our parent is NULL (that is, if we're the root
+ * node), we're going to use the special path
+ * "devices".
+ */
+ if (daddr == 0)
+ s = "devices";
+
+ len = dtrace_strlen(s, size);
+ if (*flags & CPU_DTRACE_FAULT)
+ break;
+
+ if ((end -= (len + 1)) < start)
+ break;
+
+ for (i = 1; i <= len; i++)
+ end[i] = dtrace_load8((uintptr_t)s++);
+ *end = '/';
+
+ if (depth++ > dtrace_devdepth_max) {
+ *flags |= CPU_DTRACE_ILLOP;
+ break;
+ }
+ }
+
+ if (end < start)
+ DTRACE_CPUFLAG_SET(CPU_DTRACE_NOSCRATCH);
+
+ if (daddr == 0) {
+ regs[rd] = (uintptr_t)end;
+ mstate->dtms_scratch_ptr += size;
+ }
+
+ break;
+ }
+#endif
+
+ case DIF_SUBR_STRJOIN: {
+ char *d = (char *)mstate->dtms_scratch_ptr;
+ uint64_t size = state->dts_options[DTRACEOPT_STRSIZE];
+ uintptr_t s1 = tupregs[0].dttk_value;
+ uintptr_t s2 = tupregs[1].dttk_value;
+ int i = 0;
+
+ if (!dtrace_strcanload(s1, size, mstate, vstate) ||
+ !dtrace_strcanload(s2, size, mstate, vstate)) {
+ regs[rd] = 0;
+ break;
+ }
+
+ if (!DTRACE_INSCRATCH(mstate, size)) {
+ DTRACE_CPUFLAG_SET(CPU_DTRACE_NOSCRATCH);
+ regs[rd] = 0;
+ break;
+ }
+
+ for (;;) {
+ if (i >= size) {
+ DTRACE_CPUFLAG_SET(CPU_DTRACE_NOSCRATCH);
+ regs[rd] = 0;
+ break;
+ }
+
+ if ((d[i++] = dtrace_load8(s1++)) == '\0') {
+ i--;
+ break;
+ }
+ }
+
+ for (;;) {
+ if (i >= size) {
+ DTRACE_CPUFLAG_SET(CPU_DTRACE_NOSCRATCH);
+ regs[rd] = 0;
+ break;
+ }
+
+ if ((d[i++] = dtrace_load8(s2++)) == '\0')
+ break;
+ }
+
+ if (i < size) {
+ mstate->dtms_scratch_ptr += i;
+ regs[rd] = (uintptr_t)d;
+ }
+
+ break;
+ }
+
+ case DIF_SUBR_LLTOSTR: {
+ int64_t i = (int64_t)tupregs[0].dttk_value;
+ int64_t val = i < 0 ? i * -1 : i;
+ uint64_t size = 22; /* enough room for 2^64 in decimal */
+ char *end = (char *)mstate->dtms_scratch_ptr + size - 1;
+
+ if (!DTRACE_INSCRATCH(mstate, size)) {
+ DTRACE_CPUFLAG_SET(CPU_DTRACE_NOSCRATCH);
+ regs[rd] = 0;
+ break;
+ }
+
+ for (*end-- = '\0'; val; val /= 10)
+ *end-- = '0' + (val % 10);
+
+ if (i == 0)
+ *end-- = '0';
+
+ if (i < 0)
+ *end-- = '-';
+
+ regs[rd] = (uintptr_t)end + 1;
+ mstate->dtms_scratch_ptr += size;
+ break;
+ }
+
+ case DIF_SUBR_HTONS:
+ case DIF_SUBR_NTOHS:
+#if BYTE_ORDER == BIG_ENDIAN
+ regs[rd] = (uint16_t)tupregs[0].dttk_value;
+#else
+ regs[rd] = DT_BSWAP_16((uint16_t)tupregs[0].dttk_value);
+#endif
+ break;
+
+
+ case DIF_SUBR_HTONL:
+ case DIF_SUBR_NTOHL:
+#if BYTE_ORDER == BIG_ENDIAN
+ regs[rd] = (uint32_t)tupregs[0].dttk_value;
+#else
+ regs[rd] = DT_BSWAP_32((uint32_t)tupregs[0].dttk_value);
+#endif
+ break;
+
+
+ case DIF_SUBR_HTONLL:
+ case DIF_SUBR_NTOHLL:
+#if BYTE_ORDER == BIG_ENDIAN
+ regs[rd] = (uint64_t)tupregs[0].dttk_value;
+#else
+ regs[rd] = DT_BSWAP_64((uint64_t)tupregs[0].dttk_value);
+#endif
+ break;
+
+
+ case DIF_SUBR_DIRNAME:
+ case DIF_SUBR_BASENAME: {
+ char *dest = (char *)mstate->dtms_scratch_ptr;
+ uint64_t size = state->dts_options[DTRACEOPT_STRSIZE];
+ uintptr_t src = tupregs[0].dttk_value;
+ int i, j, len = dtrace_strlen((char *)src, size);
+ int lastbase = -1, firstbase = -1, lastdir = -1;
+ int start, end;
+
+ if (!dtrace_canload(src, len + 1, mstate, vstate)) {
+ regs[rd] = 0;
+ break;
+ }
+
+ if (!DTRACE_INSCRATCH(mstate, size)) {
+ DTRACE_CPUFLAG_SET(CPU_DTRACE_NOSCRATCH);
+ regs[rd] = 0;
+ break;
+ }
+
+ /*
+ * The basename and dirname for a zero-length string is
+ * defined to be "."
+ */
+ if (len == 0) {
+ len = 1;
+ src = (uintptr_t)".";
+ }
+
+ /*
+ * Start from the back of the string, moving back toward the
+ * front until we see a character that isn't a slash. That
+ * character is the last character in the basename.
+ */
+ for (i = len - 1; i >= 0; i--) {
+ if (dtrace_load8(src + i) != '/')
+ break;
+ }
+
+ if (i >= 0)
+ lastbase = i;
+
+ /*
+ * Starting from the last character in the basename, move
+ * towards the front until we find a slash. The character
+ * that we processed immediately before that is the first
+ * character in the basename.
+ */
+ for (; i >= 0; i--) {
+ if (dtrace_load8(src + i) == '/')
+ break;
+ }
+
+ if (i >= 0)
+ firstbase = i + 1;
+
+ /*
+ * Now keep going until we find a non-slash character. That
+ * character is the last character in the dirname.
+ */
+ for (; i >= 0; i--) {
+ if (dtrace_load8(src + i) != '/')
+ break;
+ }
+
+ if (i >= 0)
+ lastdir = i;
+
+ ASSERT(!(lastbase == -1 && firstbase != -1));
+ ASSERT(!(firstbase == -1 && lastdir != -1));
+
+ if (lastbase == -1) {
+ /*
+ * We didn't find a non-slash character. We know that
+ * the length is non-zero, so the whole string must be
+ * slashes. In either the dirname or the basename
+ * case, we return '/'.
+ */
+ ASSERT(firstbase == -1);
+ firstbase = lastbase = lastdir = 0;
+ }
+
+ if (firstbase == -1) {
+ /*
+ * The entire string consists only of a basename
+ * component. If we're looking for dirname, we need
+ * to change our string to be just "."; if we're
+ * looking for a basename, we'll just set the first
+ * character of the basename to be 0.
+ */
+ if (subr == DIF_SUBR_DIRNAME) {
+ ASSERT(lastdir == -1);
+ src = (uintptr_t)".";
+ lastdir = 0;
+ } else {
+ firstbase = 0;
+ }
+ }
+
+ if (subr == DIF_SUBR_DIRNAME) {
+ if (lastdir == -1) {
+ /*
+ * We know that we have a slash in the name --
+ * or lastdir would be set to 0, above. And
+ * because lastdir is -1, we know that this
+ * slash must be the first character. (That
+ * is, the full string must be of the form
+ * "/basename".) In this case, the last
+ * character of the directory name is 0.
+ */
+ lastdir = 0;
+ }
+
+ start = 0;
+ end = lastdir;
+ } else {
+ ASSERT(subr == DIF_SUBR_BASENAME);
+ ASSERT(firstbase != -1 && lastbase != -1);
+ start = firstbase;
+ end = lastbase;
+ }
+
+ for (i = start, j = 0; i <= end && j < size - 1; i++, j++)
+ dest[j] = dtrace_load8(src + i);
+
+ dest[j] = '\0';
+ regs[rd] = (uintptr_t)dest;
+ mstate->dtms_scratch_ptr += size;
+ break;
+ }
+
+ case DIF_SUBR_CLEANPATH: {
+ char *dest = (char *)mstate->dtms_scratch_ptr, c;
+ uint64_t size = state->dts_options[DTRACEOPT_STRSIZE];
+ uintptr_t src = tupregs[0].dttk_value;
+ int i = 0, j = 0;
+
+ if (!dtrace_strcanload(src, size, mstate, vstate)) {
+ regs[rd] = 0;
+ break;
+ }
+
+ if (!DTRACE_INSCRATCH(mstate, size)) {
+ DTRACE_CPUFLAG_SET(CPU_DTRACE_NOSCRATCH);
+ regs[rd] = 0;
+ break;
+ }
+
+ /*
+ * Move forward, loading each character.
+ */
+ do {
+ c = dtrace_load8(src + i++);
+next:
+ if (j + 5 >= size) /* 5 = strlen("/..c\0") */
+ break;
+
+ if (c != '/') {
+ dest[j++] = c;
+ continue;
+ }
+
+ c = dtrace_load8(src + i++);
+
+ if (c == '/') {
+ /*
+ * We have two slashes -- we can just advance
+ * to the next character.
+ */
+ goto next;
+ }
+
+ if (c != '.') {
+ /*
+ * This is not "." and it's not ".." -- we can
+ * just store the "/" and this character and
+ * drive on.
+ */
+ dest[j++] = '/';
+ dest[j++] = c;
+ continue;
+ }
+
+ c = dtrace_load8(src + i++);
+
+ if (c == '/') {
+ /*
+ * This is a "/./" component. We're not going
+ * to store anything in the destination buffer;
+ * we're just going to go to the next component.
+ */
+ goto next;
+ }
+
+ if (c != '.') {
+ /*
+ * This is not ".." -- we can just store the
+ * "/." and this character and continue
+ * processing.
+ */
+ dest[j++] = '/';
+ dest[j++] = '.';
+ dest[j++] = c;
+ continue;
+ }
+
+ c = dtrace_load8(src + i++);
+
+ if (c != '/' && c != '\0') {
+ /*
+ * This is not ".." -- it's "..[mumble]".
+ * We'll store the "/.." and this character
+ * and continue processing.
+ */
+ dest[j++] = '/';
+ dest[j++] = '.';
+ dest[j++] = '.';
+ dest[j++] = c;
+ continue;
+ }
+
+ /*
+ * This is "/../" or "/..\0". We need to back up
+ * our destination pointer until we find a "/".
+ */
+ i--;
+ while (j != 0 && dest[--j] != '/')
+ continue;
+
+ if (c == '\0')
+ dest[++j] = '/';
+ } while (c != '\0');
+
+ dest[j] = '\0';
+ regs[rd] = (uintptr_t)dest;
+ mstate->dtms_scratch_ptr += size;
+ break;
+ }
+
+ case DIF_SUBR_INET_NTOA:
+ case DIF_SUBR_INET_NTOA6:
+ case DIF_SUBR_INET_NTOP: {
+ size_t size;
+ int af, argi, i;
+ char *base, *end;
+
+ if (subr == DIF_SUBR_INET_NTOP) {
+ af = (int)tupregs[0].dttk_value;
+ argi = 1;
+ } else {
+ af = subr == DIF_SUBR_INET_NTOA ? AF_INET: AF_INET6;
+ argi = 0;
+ }
+
+ if (af == AF_INET) {
+ ipaddr_t ip4;
+ uint8_t *ptr8, val;
+
+ /*
+ * Safely load the IPv4 address.
+ */
+ ip4 = dtrace_load32(tupregs[argi].dttk_value);
+
+ /*
+ * Check an IPv4 string will fit in scratch.
+ */
+ size = INET_ADDRSTRLEN;
+ if (!DTRACE_INSCRATCH(mstate, size)) {
+ DTRACE_CPUFLAG_SET(CPU_DTRACE_NOSCRATCH);
+ regs[rd] = 0;
+ break;
+ }
+ base = (char *)mstate->dtms_scratch_ptr;
+ end = (char *)mstate->dtms_scratch_ptr + size - 1;
+
+ /*
+ * Stringify as a dotted decimal quad.
+ */
+ *end-- = '\0';
+ ptr8 = (uint8_t *)&ip4;
+ for (i = 3; i >= 0; i--) {
+ val = ptr8[i];
+
+ if (val == 0) {
+ *end-- = '0';
+ } else {
+ for (; val; val /= 10) {
+ *end-- = '0' + (val % 10);
+ }
+ }
+
+ if (i > 0)
+ *end-- = '.';
+ }
+ ASSERT(end + 1 >= base);
+
+ } else if (af == AF_INET6) {
+ struct in6_addr ip6;
+ int firstzero, tryzero, numzero, v6end;
+ uint16_t val;
+ const char digits[] = "0123456789abcdef";
+
+ /*
+ * Stringify using RFC 1884 convention 2 - 16 bit
+ * hexadecimal values with a zero-run compression.
+ * Lower case hexadecimal digits are used.
+ * eg, fe80::214:4fff:fe0b:76c8.
+ * The IPv4 embedded form is returned for inet_ntop,
+ * just the IPv4 string is returned for inet_ntoa6.
+ */
+
+ /*
+ * Safely load the IPv6 address.
+ */
+ dtrace_bcopy(
+ (void *)(uintptr_t)tupregs[argi].dttk_value,
+ (void *)(uintptr_t)&ip6, sizeof (struct in6_addr));
+
+ /*
+ * Check an IPv6 string will fit in scratch.
+ */
+ size = INET6_ADDRSTRLEN;
+ if (!DTRACE_INSCRATCH(mstate, size)) {
+ DTRACE_CPUFLAG_SET(CPU_DTRACE_NOSCRATCH);
+ regs[rd] = 0;
+ break;
+ }
+ base = (char *)mstate->dtms_scratch_ptr;
+ end = (char *)mstate->dtms_scratch_ptr + size - 1;
+ *end-- = '\0';
+
+ /*
+ * Find the longest run of 16 bit zero values
+ * for the single allowed zero compression - "::".
+ */
+ firstzero = -1;
+ tryzero = -1;
+ numzero = 1;
+ for (i = 0; i < sizeof (struct in6_addr); i++) {
+#if defined(sun)
+ if (ip6._S6_un._S6_u8[i] == 0 &&
+#else
+ if (ip6.__u6_addr.__u6_addr8[i] == 0 &&
+#endif
+ tryzero == -1 && i % 2 == 0) {
+ tryzero = i;
+ continue;
+ }
+
+ if (tryzero != -1 &&
+#if defined(sun)
+ (ip6._S6_un._S6_u8[i] != 0 ||
+#else
+ (ip6.__u6_addr.__u6_addr8[i] != 0 ||
+#endif
+ i == sizeof (struct in6_addr) - 1)) {
+
+ if (i - tryzero <= numzero) {
+ tryzero = -1;
+ continue;
+ }
+
+ firstzero = tryzero;
+ numzero = i - i % 2 - tryzero;
+ tryzero = -1;
+
+#if defined(sun)
+ if (ip6._S6_un._S6_u8[i] == 0 &&
+#else
+ if (ip6.__u6_addr.__u6_addr8[i] == 0 &&
+#endif
+ i == sizeof (struct in6_addr) - 1)
+ numzero += 2;
+ }
+ }
+ ASSERT(firstzero + numzero <= sizeof (struct in6_addr));
+
+ /*
+ * Check for an IPv4 embedded address.
+ */
+ v6end = sizeof (struct in6_addr) - 2;
+ if (IN6_IS_ADDR_V4MAPPED(&ip6) ||
+ IN6_IS_ADDR_V4COMPAT(&ip6)) {
+ for (i = sizeof (struct in6_addr) - 1;
+ i >= DTRACE_V4MAPPED_OFFSET; i--) {
+ ASSERT(end >= base);
+
+#if defined(sun)
+ val = ip6._S6_un._S6_u8[i];
+#else
+ val = ip6.__u6_addr.__u6_addr8[i];
+#endif
+
+ if (val == 0) {
+ *end-- = '0';
+ } else {
+ for (; val; val /= 10) {
+ *end-- = '0' + val % 10;
+ }
+ }
+
+ if (i > DTRACE_V4MAPPED_OFFSET)
+ *end-- = '.';
+ }
+
+ if (subr == DIF_SUBR_INET_NTOA6)
+ goto inetout;
+
+ /*
+ * Set v6end to skip the IPv4 address that
+ * we have already stringified.
+ */
+ v6end = 10;
+ }
+
+ /*
+ * Build the IPv6 string by working through the
+ * address in reverse.
+ */
+ for (i = v6end; i >= 0; i -= 2) {
+ ASSERT(end >= base);
+
+ if (i == firstzero + numzero - 2) {
+ *end-- = ':';
+ *end-- = ':';
+ i -= numzero - 2;
+ continue;
+ }
+
+ if (i < 14 && i != firstzero - 2)
+ *end-- = ':';
+
+#if defined(sun)
+ val = (ip6._S6_un._S6_u8[i] << 8) +
+ ip6._S6_un._S6_u8[i + 1];
+#else
+ val = (ip6.__u6_addr.__u6_addr8[i] << 8) +
+ ip6.__u6_addr.__u6_addr8[i + 1];
+#endif
+
+ if (val == 0) {
+ *end-- = '0';
+ } else {
+ for (; val; val /= 16) {
+ *end-- = digits[val % 16];
+ }
+ }
+ }
+ ASSERT(end + 1 >= base);
+
+ } else {
+ /*
+ * The user didn't use AH_INET or AH_INET6.
+ */
+ DTRACE_CPUFLAG_SET(CPU_DTRACE_ILLOP);
+ regs[rd] = 0;
+ break;
+ }
+
+inetout: regs[rd] = (uintptr_t)end + 1;
+ mstate->dtms_scratch_ptr += size;
+ break;
+ }
+
+ case DIF_SUBR_MEMREF: {
+ uintptr_t size = 2 * sizeof(uintptr_t);
+ uintptr_t *memref = (uintptr_t *) P2ROUNDUP(mstate->dtms_scratch_ptr, sizeof(uintptr_t));
+ size_t scratch_size = ((uintptr_t) memref - mstate->dtms_scratch_ptr) + size;
+
+ /* address and length */
+ memref[0] = tupregs[0].dttk_value;
+ memref[1] = tupregs[1].dttk_value;
+
+ regs[rd] = (uintptr_t) memref;
+ mstate->dtms_scratch_ptr += scratch_size;
+ break;
+ }
+
+ case DIF_SUBR_TYPEREF: {
+ uintptr_t size = 4 * sizeof(uintptr_t);
+ uintptr_t *typeref = (uintptr_t *) P2ROUNDUP(mstate->dtms_scratch_ptr, sizeof(uintptr_t));
+ size_t scratch_size = ((uintptr_t) typeref - mstate->dtms_scratch_ptr) + size;
+
+ /* address, num_elements, type_str, type_len */
+ typeref[0] = tupregs[0].dttk_value;
+ typeref[1] = tupregs[1].dttk_value;
+ typeref[2] = tupregs[2].dttk_value;
+ typeref[3] = tupregs[3].dttk_value;
+
+ regs[rd] = (uintptr_t) typeref;
+ mstate->dtms_scratch_ptr += scratch_size;
+ break;
+ }
+ }
+}
+
+/*
+ * Emulate the execution of DTrace IR instructions specified by the given
+ * DIF object. This function is deliberately void of assertions as all of
+ * the necessary checks are handled by a call to dtrace_difo_validate().
+ */
+static uint64_t
+dtrace_dif_emulate(dtrace_difo_t *difo, dtrace_mstate_t *mstate,
+ dtrace_vstate_t *vstate, dtrace_state_t *state)
+{
+ const dif_instr_t *text = difo->dtdo_buf;
+ const uint_t textlen = difo->dtdo_len;
+ const char *strtab = difo->dtdo_strtab;
+ const uint64_t *inttab = difo->dtdo_inttab;
+
+ uint64_t rval = 0;
+ dtrace_statvar_t *svar;
+ dtrace_dstate_t *dstate = &vstate->dtvs_dynvars;
+ dtrace_difv_t *v;
+ volatile uint16_t *flags = &cpu_core[curcpu].cpuc_dtrace_flags;
+ volatile uintptr_t *illval = &cpu_core[curcpu].cpuc_dtrace_illval;
+
+ dtrace_key_t tupregs[DIF_DTR_NREGS + 2]; /* +2 for thread and id */
+ uint64_t regs[DIF_DIR_NREGS];
+ uint64_t *tmp;
+
+ uint8_t cc_n = 0, cc_z = 0, cc_v = 0, cc_c = 0;
+ int64_t cc_r;
+ uint_t pc = 0, id, opc = 0;
+ uint8_t ttop = 0;
+ dif_instr_t instr;
+ uint_t r1, r2, rd;
+
+ /*
+ * We stash the current DIF object into the machine state: we need it
+ * for subsequent access checking.
+ */
+ mstate->dtms_difo = difo;
+
+ regs[DIF_REG_R0] = 0; /* %r0 is fixed at zero */
+
+ while (pc < textlen && !(*flags & CPU_DTRACE_FAULT)) {
+ opc = pc;
+
+ instr = text[pc++];
+ r1 = DIF_INSTR_R1(instr);
+ r2 = DIF_INSTR_R2(instr);
+ rd = DIF_INSTR_RD(instr);
+
+ switch (DIF_INSTR_OP(instr)) {
+ case DIF_OP_OR:
+ regs[rd] = regs[r1] | regs[r2];
+ break;
+ case DIF_OP_XOR:
+ regs[rd] = regs[r1] ^ regs[r2];
+ break;
+ case DIF_OP_AND:
+ regs[rd] = regs[r1] & regs[r2];
+ break;
+ case DIF_OP_SLL:
+ regs[rd] = regs[r1] << regs[r2];
+ break;
+ case DIF_OP_SRL:
+ regs[rd] = regs[r1] >> regs[r2];
+ break;
+ case DIF_OP_SUB:
+ regs[rd] = regs[r1] - regs[r2];
+ break;
+ case DIF_OP_ADD:
+ regs[rd] = regs[r1] + regs[r2];
+ break;
+ case DIF_OP_MUL:
+ regs[rd] = regs[r1] * regs[r2];
+ break;
+ case DIF_OP_SDIV:
+ if (regs[r2] == 0) {
+ regs[rd] = 0;
+ *flags |= CPU_DTRACE_DIVZERO;
+ } else {
+ regs[rd] = (int64_t)regs[r1] /
+ (int64_t)regs[r2];
+ }
+ break;
+
+ case DIF_OP_UDIV:
+ if (regs[r2] == 0) {
+ regs[rd] = 0;
+ *flags |= CPU_DTRACE_DIVZERO;
+ } else {
+ regs[rd] = regs[r1] / regs[r2];
+ }
+ break;
+
+ case DIF_OP_SREM:
+ if (regs[r2] == 0) {
+ regs[rd] = 0;
+ *flags |= CPU_DTRACE_DIVZERO;
+ } else {
+ regs[rd] = (int64_t)regs[r1] %
+ (int64_t)regs[r2];
+ }
+ break;
+
+ case DIF_OP_UREM:
+ if (regs[r2] == 0) {
+ regs[rd] = 0;
+ *flags |= CPU_DTRACE_DIVZERO;
+ } else {
+ regs[rd] = regs[r1] % regs[r2];
+ }
+ break;
+
+ case DIF_OP_NOT:
+ regs[rd] = ~regs[r1];
+ break;
+ case DIF_OP_MOV:
+ regs[rd] = regs[r1];
+ break;
+ case DIF_OP_CMP:
+ cc_r = regs[r1] - regs[r2];
+ cc_n = cc_r < 0;
+ cc_z = cc_r == 0;
+ cc_v = 0;
+ cc_c = regs[r1] < regs[r2];
+ break;
+ case DIF_OP_TST:
+ cc_n = cc_v = cc_c = 0;
+ cc_z = regs[r1] == 0;
+ break;
+ case DIF_OP_BA:
+ pc = DIF_INSTR_LABEL(instr);
+ break;
+ case DIF_OP_BE:
+ if (cc_z)
+ pc = DIF_INSTR_LABEL(instr);
+ break;
+ case DIF_OP_BNE:
+ if (cc_z == 0)
+ pc = DIF_INSTR_LABEL(instr);
+ break;
+ case DIF_OP_BG:
+ if ((cc_z | (cc_n ^ cc_v)) == 0)
+ pc = DIF_INSTR_LABEL(instr);
+ break;
+ case DIF_OP_BGU:
+ if ((cc_c | cc_z) == 0)
+ pc = DIF_INSTR_LABEL(instr);
+ break;
+ case DIF_OP_BGE:
+ if ((cc_n ^ cc_v) == 0)
+ pc = DIF_INSTR_LABEL(instr);
+ break;
+ case DIF_OP_BGEU:
+ if (cc_c == 0)
+ pc = DIF_INSTR_LABEL(instr);
+ break;
+ case DIF_OP_BL:
+ if (cc_n ^ cc_v)
+ pc = DIF_INSTR_LABEL(instr);
+ break;
+ case DIF_OP_BLU:
+ if (cc_c)
+ pc = DIF_INSTR_LABEL(instr);
+ break;
+ case DIF_OP_BLE:
+ if (cc_z | (cc_n ^ cc_v))
+ pc = DIF_INSTR_LABEL(instr);
+ break;
+ case DIF_OP_BLEU:
+ if (cc_c | cc_z)
+ pc = DIF_INSTR_LABEL(instr);
+ break;
+ case DIF_OP_RLDSB:
+ if (!dtrace_canstore(regs[r1], 1, mstate, vstate)) {
+ *flags |= CPU_DTRACE_KPRIV;
+ *illval = regs[r1];
+ break;
+ }
+ /*FALLTHROUGH*/
+ case DIF_OP_LDSB:
+ regs[rd] = (int8_t)dtrace_load8(regs[r1]);
+ break;
+ case DIF_OP_RLDSH:
+ if (!dtrace_canstore(regs[r1], 2, mstate, vstate)) {
+ *flags |= CPU_DTRACE_KPRIV;
+ *illval = regs[r1];
+ break;
+ }
+ /*FALLTHROUGH*/
+ case DIF_OP_LDSH:
+ regs[rd] = (int16_t)dtrace_load16(regs[r1]);
+ break;
+ case DIF_OP_RLDSW:
+ if (!dtrace_canstore(regs[r1], 4, mstate, vstate)) {
+ *flags |= CPU_DTRACE_KPRIV;
+ *illval = regs[r1];
+ break;
+ }
+ /*FALLTHROUGH*/
+ case DIF_OP_LDSW:
+ regs[rd] = (int32_t)dtrace_load32(regs[r1]);
+ break;
+ case DIF_OP_RLDUB:
+ if (!dtrace_canstore(regs[r1], 1, mstate, vstate)) {
+ *flags |= CPU_DTRACE_KPRIV;
+ *illval = regs[r1];
+ break;
+ }
+ /*FALLTHROUGH*/
+ case DIF_OP_LDUB:
+ regs[rd] = dtrace_load8(regs[r1]);
+ break;
+ case DIF_OP_RLDUH:
+ if (!dtrace_canstore(regs[r1], 2, mstate, vstate)) {
+ *flags |= CPU_DTRACE_KPRIV;
+ *illval = regs[r1];
+ break;
+ }
+ /*FALLTHROUGH*/
+ case DIF_OP_LDUH:
+ regs[rd] = dtrace_load16(regs[r1]);
+ break;
+ case DIF_OP_RLDUW:
+ if (!dtrace_canstore(regs[r1], 4, mstate, vstate)) {
+ *flags |= CPU_DTRACE_KPRIV;
+ *illval = regs[r1];
+ break;
+ }
+ /*FALLTHROUGH*/
+ case DIF_OP_LDUW:
+ regs[rd] = dtrace_load32(regs[r1]);
+ break;
+ case DIF_OP_RLDX:
+ if (!dtrace_canstore(regs[r1], 8, mstate, vstate)) {
+ *flags |= CPU_DTRACE_KPRIV;
+ *illval = regs[r1];
+ break;
+ }
+ /*FALLTHROUGH*/
+ case DIF_OP_LDX:
+ regs[rd] = dtrace_load64(regs[r1]);
+ break;
+ case DIF_OP_ULDSB:
+ regs[rd] = (int8_t)
+ dtrace_fuword8((void *)(uintptr_t)regs[r1]);
+ break;
+ case DIF_OP_ULDSH:
+ regs[rd] = (int16_t)
+ dtrace_fuword16((void *)(uintptr_t)regs[r1]);
+ break;
+ case DIF_OP_ULDSW:
+ regs[rd] = (int32_t)
+ dtrace_fuword32((void *)(uintptr_t)regs[r1]);
+ break;
+ case DIF_OP_ULDUB:
+ regs[rd] =
+ dtrace_fuword8((void *)(uintptr_t)regs[r1]);
+ break;
+ case DIF_OP_ULDUH:
+ regs[rd] =
+ dtrace_fuword16((void *)(uintptr_t)regs[r1]);
+ break;
+ case DIF_OP_ULDUW:
+ regs[rd] =
+ dtrace_fuword32((void *)(uintptr_t)regs[r1]);
+ break;
+ case DIF_OP_ULDX:
+ regs[rd] =
+ dtrace_fuword64((void *)(uintptr_t)regs[r1]);
+ break;
+ case DIF_OP_RET:
+ rval = regs[rd];
+ pc = textlen;
+ break;
+ case DIF_OP_NOP:
+ break;
+ case DIF_OP_SETX:
+ regs[rd] = inttab[DIF_INSTR_INTEGER(instr)];
+ break;
+ case DIF_OP_SETS:
+ regs[rd] = (uint64_t)(uintptr_t)
+ (strtab + DIF_INSTR_STRING(instr));
+ break;
+ case DIF_OP_SCMP: {
+ size_t sz = state->dts_options[DTRACEOPT_STRSIZE];
+ uintptr_t s1 = regs[r1];
+ uintptr_t s2 = regs[r2];
+
+ if (s1 != 0 &&
+ !dtrace_strcanload(s1, sz, mstate, vstate))
+ break;
+ if (s2 != 0 &&
+ !dtrace_strcanload(s2, sz, mstate, vstate))
+ break;
+
+ cc_r = dtrace_strncmp((char *)s1, (char *)s2, sz);
+
+ cc_n = cc_r < 0;
+ cc_z = cc_r == 0;
+ cc_v = cc_c = 0;
+ break;
+ }
+ case DIF_OP_LDGA:
+ regs[rd] = dtrace_dif_variable(mstate, state,
+ r1, regs[r2]);
+ break;
+ case DIF_OP_LDGS:
+ id = DIF_INSTR_VAR(instr);
+
+ if (id >= DIF_VAR_OTHER_UBASE) {
+ uintptr_t a;
+
+ id -= DIF_VAR_OTHER_UBASE;
+ svar = vstate->dtvs_globals[id];
+ ASSERT(svar != NULL);
+ v = &svar->dtsv_var;
+
+ if (!(v->dtdv_type.dtdt_flags & DIF_TF_BYREF)) {
+ regs[rd] = svar->dtsv_data;
+ break;
+ }
+
+ a = (uintptr_t)svar->dtsv_data;
+
+ if (*(uint8_t *)a == UINT8_MAX) {
+ /*
+ * If the 0th byte is set to UINT8_MAX
+ * then this is to be treated as a
+ * reference to a NULL variable.
+ */
+ regs[rd] = 0;
+ } else {
+ regs[rd] = a + sizeof (uint64_t);
+ }
+
+ break;
+ }
+
+ regs[rd] = dtrace_dif_variable(mstate, state, id, 0);
+ break;
+
+ case DIF_OP_STGS:
+ id = DIF_INSTR_VAR(instr);
+
+ ASSERT(id >= DIF_VAR_OTHER_UBASE);
+ id -= DIF_VAR_OTHER_UBASE;
+
+ svar = vstate->dtvs_globals[id];
+ ASSERT(svar != NULL);
+ v = &svar->dtsv_var;
+
+ if (v->dtdv_type.dtdt_flags & DIF_TF_BYREF) {
+ uintptr_t a = (uintptr_t)svar->dtsv_data;
+
+ ASSERT(a != 0);
+ ASSERT(svar->dtsv_size != 0);
+
+ if (regs[rd] == 0) {
+ *(uint8_t *)a = UINT8_MAX;
+ break;
+ } else {
+ *(uint8_t *)a = 0;
+ a += sizeof (uint64_t);
+ }
+ if (!dtrace_vcanload(
+ (void *)(uintptr_t)regs[rd], &v->dtdv_type,
+ mstate, vstate))
+ break;
+
+ dtrace_vcopy((void *)(uintptr_t)regs[rd],
+ (void *)a, &v->dtdv_type);
+ break;
+ }
+
+ svar->dtsv_data = regs[rd];
+ break;
+
+ case DIF_OP_LDTA:
+ /*
+ * There are no DTrace built-in thread-local arrays at
+ * present. This opcode is saved for future work.
+ */
+ *flags |= CPU_DTRACE_ILLOP;
+ regs[rd] = 0;
+ break;
+
+ case DIF_OP_LDLS:
+ id = DIF_INSTR_VAR(instr);
+
+ if (id < DIF_VAR_OTHER_UBASE) {
+ /*
+ * For now, this has no meaning.
+ */
+ regs[rd] = 0;
+ break;
+ }
+
+ id -= DIF_VAR_OTHER_UBASE;
+
+ ASSERT(id < vstate->dtvs_nlocals);
+ ASSERT(vstate->dtvs_locals != NULL);
+
+ svar = vstate->dtvs_locals[id];
+ ASSERT(svar != NULL);
+ v = &svar->dtsv_var;
+
+ if (v->dtdv_type.dtdt_flags & DIF_TF_BYREF) {
+ uintptr_t a = (uintptr_t)svar->dtsv_data;
+ size_t sz = v->dtdv_type.dtdt_size;
+
+ sz += sizeof (uint64_t);
+ ASSERT(svar->dtsv_size == NCPU * sz);
+ a += curcpu * sz;
+
+ if (*(uint8_t *)a == UINT8_MAX) {
+ /*
+ * If the 0th byte is set to UINT8_MAX
+ * then this is to be treated as a
+ * reference to a NULL variable.
+ */
+ regs[rd] = 0;
+ } else {
+ regs[rd] = a + sizeof (uint64_t);
+ }
+
+ break;
+ }
+
+ ASSERT(svar->dtsv_size == NCPU * sizeof (uint64_t));
+ tmp = (uint64_t *)(uintptr_t)svar->dtsv_data;
+ regs[rd] = tmp[curcpu];
+ break;
+
+ case DIF_OP_STLS:
+ id = DIF_INSTR_VAR(instr);
+
+ ASSERT(id >= DIF_VAR_OTHER_UBASE);
+ id -= DIF_VAR_OTHER_UBASE;
+ ASSERT(id < vstate->dtvs_nlocals);
+
+ ASSERT(vstate->dtvs_locals != NULL);
+ svar = vstate->dtvs_locals[id];
+ ASSERT(svar != NULL);
+ v = &svar->dtsv_var;
+
+ if (v->dtdv_type.dtdt_flags & DIF_TF_BYREF) {
+ uintptr_t a = (uintptr_t)svar->dtsv_data;
+ size_t sz = v->dtdv_type.dtdt_size;
+
+ sz += sizeof (uint64_t);
+ ASSERT(svar->dtsv_size == NCPU * sz);
+ a += curcpu * sz;
+
+ if (regs[rd] == 0) {
+ *(uint8_t *)a = UINT8_MAX;
+ break;
+ } else {
+ *(uint8_t *)a = 0;
+ a += sizeof (uint64_t);
+ }
+
+ if (!dtrace_vcanload(
+ (void *)(uintptr_t)regs[rd], &v->dtdv_type,
+ mstate, vstate))
+ break;
+
+ dtrace_vcopy((void *)(uintptr_t)regs[rd],
+ (void *)a, &v->dtdv_type);
+ break;
+ }
+
+ ASSERT(svar->dtsv_size == NCPU * sizeof (uint64_t));
+ tmp = (uint64_t *)(uintptr_t)svar->dtsv_data;
+ tmp[curcpu] = regs[rd];
+ break;
+
+ case DIF_OP_LDTS: {
+ dtrace_dynvar_t *dvar;
+ dtrace_key_t *key;
+
+ id = DIF_INSTR_VAR(instr);
+ ASSERT(id >= DIF_VAR_OTHER_UBASE);
+ id -= DIF_VAR_OTHER_UBASE;
+ v = &vstate->dtvs_tlocals[id];
+
+ key = &tupregs[DIF_DTR_NREGS];
+ key[0].dttk_value = (uint64_t)id;
+ key[0].dttk_size = 0;
+ DTRACE_TLS_THRKEY(key[1].dttk_value);
+ key[1].dttk_size = 0;
+
+ dvar = dtrace_dynvar(dstate, 2, key,
+ sizeof (uint64_t), DTRACE_DYNVAR_NOALLOC,
+ mstate, vstate);
+
+ if (dvar == NULL) {
+ regs[rd] = 0;
+ break;
+ }
+
+ if (v->dtdv_type.dtdt_flags & DIF_TF_BYREF) {
+ regs[rd] = (uint64_t)(uintptr_t)dvar->dtdv_data;
+ } else {
+ regs[rd] = *((uint64_t *)dvar->dtdv_data);
+ }
+
+ break;
+ }
+
+ case DIF_OP_STTS: {
+ dtrace_dynvar_t *dvar;
+ dtrace_key_t *key;
+
+ id = DIF_INSTR_VAR(instr);
+ ASSERT(id >= DIF_VAR_OTHER_UBASE);
+ id -= DIF_VAR_OTHER_UBASE;
+
+ key = &tupregs[DIF_DTR_NREGS];
+ key[0].dttk_value = (uint64_t)id;
+ key[0].dttk_size = 0;
+ DTRACE_TLS_THRKEY(key[1].dttk_value);
+ key[1].dttk_size = 0;
+ v = &vstate->dtvs_tlocals[id];
+
+ dvar = dtrace_dynvar(dstate, 2, key,
+ v->dtdv_type.dtdt_size > sizeof (uint64_t) ?
+ v->dtdv_type.dtdt_size : sizeof (uint64_t),
+ regs[rd] ? DTRACE_DYNVAR_ALLOC :
+ DTRACE_DYNVAR_DEALLOC, mstate, vstate);
+
+ /*
+ * Given that we're storing to thread-local data,
+ * we need to flush our predicate cache.
+ */
+ curthread->t_predcache = 0;
+
+ if (dvar == NULL)
+ break;
+
+ if (v->dtdv_type.dtdt_flags & DIF_TF_BYREF) {
+ if (!dtrace_vcanload(
+ (void *)(uintptr_t)regs[rd],
+ &v->dtdv_type, mstate, vstate))
+ break;
+
+ dtrace_vcopy((void *)(uintptr_t)regs[rd],
+ dvar->dtdv_data, &v->dtdv_type);
+ } else {
+ *((uint64_t *)dvar->dtdv_data) = regs[rd];
+ }
+
+ break;
+ }
+
+ case DIF_OP_SRA:
+ regs[rd] = (int64_t)regs[r1] >> regs[r2];
+ break;
+
+ case DIF_OP_CALL:
+ dtrace_dif_subr(DIF_INSTR_SUBR(instr), rd,
+ regs, tupregs, ttop, mstate, state);
+ break;
+
+ case DIF_OP_PUSHTR:
+ if (ttop == DIF_DTR_NREGS) {
+ *flags |= CPU_DTRACE_TUPOFLOW;
+ break;
+ }
+
+ if (r1 == DIF_TYPE_STRING) {
+ /*
+ * If this is a string type and the size is 0,
+ * we'll use the system-wide default string
+ * size. Note that we are _not_ looking at
+ * the value of the DTRACEOPT_STRSIZE option;
+ * had this been set, we would expect to have
+ * a non-zero size value in the "pushtr".
+ */
+ tupregs[ttop].dttk_size =
+ dtrace_strlen((char *)(uintptr_t)regs[rd],
+ regs[r2] ? regs[r2] :
+ dtrace_strsize_default) + 1;
+ } else {
+ tupregs[ttop].dttk_size = regs[r2];
+ }
+
+ tupregs[ttop++].dttk_value = regs[rd];
+ break;
+
+ case DIF_OP_PUSHTV:
+ if (ttop == DIF_DTR_NREGS) {
+ *flags |= CPU_DTRACE_TUPOFLOW;
+ break;
+ }
+
+ tupregs[ttop].dttk_value = regs[rd];
+ tupregs[ttop++].dttk_size = 0;
+ break;
+
+ case DIF_OP_POPTS:
+ if (ttop != 0)
+ ttop--;
+ break;
+
+ case DIF_OP_FLUSHTS:
+ ttop = 0;
+ break;
+
+ case DIF_OP_LDGAA:
+ case DIF_OP_LDTAA: {
+ dtrace_dynvar_t *dvar;
+ dtrace_key_t *key = tupregs;
+ uint_t nkeys = ttop;
+
+ id = DIF_INSTR_VAR(instr);
+ ASSERT(id >= DIF_VAR_OTHER_UBASE);
+ id -= DIF_VAR_OTHER_UBASE;
+
+ key[nkeys].dttk_value = (uint64_t)id;
+ key[nkeys++].dttk_size = 0;
+
+ if (DIF_INSTR_OP(instr) == DIF_OP_LDTAA) {
+ DTRACE_TLS_THRKEY(key[nkeys].dttk_value);
+ key[nkeys++].dttk_size = 0;
+ v = &vstate->dtvs_tlocals[id];
+ } else {
+ v = &vstate->dtvs_globals[id]->dtsv_var;
+ }
+
+ dvar = dtrace_dynvar(dstate, nkeys, key,
+ v->dtdv_type.dtdt_size > sizeof (uint64_t) ?
+ v->dtdv_type.dtdt_size : sizeof (uint64_t),
+ DTRACE_DYNVAR_NOALLOC, mstate, vstate);
+
+ if (dvar == NULL) {
+ regs[rd] = 0;
+ break;
+ }
+
+ if (v->dtdv_type.dtdt_flags & DIF_TF_BYREF) {
+ regs[rd] = (uint64_t)(uintptr_t)dvar->dtdv_data;
+ } else {
+ regs[rd] = *((uint64_t *)dvar->dtdv_data);
+ }
+
+ break;
+ }
+
+ case DIF_OP_STGAA:
+ case DIF_OP_STTAA: {
+ dtrace_dynvar_t *dvar;
+ dtrace_key_t *key = tupregs;
+ uint_t nkeys = ttop;
+
+ id = DIF_INSTR_VAR(instr);
+ ASSERT(id >= DIF_VAR_OTHER_UBASE);
+ id -= DIF_VAR_OTHER_UBASE;
+
+ key[nkeys].dttk_value = (uint64_t)id;
+ key[nkeys++].dttk_size = 0;
+
+ if (DIF_INSTR_OP(instr) == DIF_OP_STTAA) {
+ DTRACE_TLS_THRKEY(key[nkeys].dttk_value);
+ key[nkeys++].dttk_size = 0;
+ v = &vstate->dtvs_tlocals[id];
+ } else {
+ v = &vstate->dtvs_globals[id]->dtsv_var;
+ }
+
+ dvar = dtrace_dynvar(dstate, nkeys, key,
+ v->dtdv_type.dtdt_size > sizeof (uint64_t) ?
+ v->dtdv_type.dtdt_size : sizeof (uint64_t),
+ regs[rd] ? DTRACE_DYNVAR_ALLOC :
+ DTRACE_DYNVAR_DEALLOC, mstate, vstate);
+
+ if (dvar == NULL)
+ break;
+
+ if (v->dtdv_type.dtdt_flags & DIF_TF_BYREF) {
+ if (!dtrace_vcanload(
+ (void *)(uintptr_t)regs[rd], &v->dtdv_type,
+ mstate, vstate))
+ break;
+
+ dtrace_vcopy((void *)(uintptr_t)regs[rd],
+ dvar->dtdv_data, &v->dtdv_type);
+ } else {
+ *((uint64_t *)dvar->dtdv_data) = regs[rd];
+ }
+
+ break;
+ }
+
+ case DIF_OP_ALLOCS: {
+ uintptr_t ptr = P2ROUNDUP(mstate->dtms_scratch_ptr, 8);
+ size_t size = ptr - mstate->dtms_scratch_ptr + regs[r1];
+
+ /*
+ * Rounding up the user allocation size could have
+ * overflowed large, bogus allocations (like -1ULL) to
+ * 0.
+ */
+ if (size < regs[r1] ||
+ !DTRACE_INSCRATCH(mstate, size)) {
+ DTRACE_CPUFLAG_SET(CPU_DTRACE_NOSCRATCH);
+ regs[rd] = 0;
+ break;
+ }
+
+ dtrace_bzero((void *) mstate->dtms_scratch_ptr, size);
+ mstate->dtms_scratch_ptr += size;
+ regs[rd] = ptr;
+ break;
+ }
+
+ case DIF_OP_COPYS:
+ if (!dtrace_canstore(regs[rd], regs[r2],
+ mstate, vstate)) {
+ *flags |= CPU_DTRACE_BADADDR;
+ *illval = regs[rd];
+ break;
+ }
+
+ if (!dtrace_canload(regs[r1], regs[r2], mstate, vstate))
+ break;
+
+ dtrace_bcopy((void *)(uintptr_t)regs[r1],
+ (void *)(uintptr_t)regs[rd], (size_t)regs[r2]);
+ break;
+
+ case DIF_OP_STB:
+ if (!dtrace_canstore(regs[rd], 1, mstate, vstate)) {
+ *flags |= CPU_DTRACE_BADADDR;
+ *illval = regs[rd];
+ break;
+ }
+ *((uint8_t *)(uintptr_t)regs[rd]) = (uint8_t)regs[r1];
+ break;
+
+ case DIF_OP_STH:
+ if (!dtrace_canstore(regs[rd], 2, mstate, vstate)) {
+ *flags |= CPU_DTRACE_BADADDR;
+ *illval = regs[rd];
+ break;
+ }
+ if (regs[rd] & 1) {
+ *flags |= CPU_DTRACE_BADALIGN;
+ *illval = regs[rd];
+ break;
+ }
+ *((uint16_t *)(uintptr_t)regs[rd]) = (uint16_t)regs[r1];
+ break;
+
+ case DIF_OP_STW:
+ if (!dtrace_canstore(regs[rd], 4, mstate, vstate)) {
+ *flags |= CPU_DTRACE_BADADDR;
+ *illval = regs[rd];
+ break;
+ }
+ if (regs[rd] & 3) {
+ *flags |= CPU_DTRACE_BADALIGN;
+ *illval = regs[rd];
+ break;
+ }
+ *((uint32_t *)(uintptr_t)regs[rd]) = (uint32_t)regs[r1];
+ break;
+
+ case DIF_OP_STX:
+ if (!dtrace_canstore(regs[rd], 8, mstate, vstate)) {
+ *flags |= CPU_DTRACE_BADADDR;
+ *illval = regs[rd];
+ break;
+ }
+ if (regs[rd] & 7) {
+ *flags |= CPU_DTRACE_BADALIGN;
+ *illval = regs[rd];
+ break;
+ }
+ *((uint64_t *)(uintptr_t)regs[rd]) = regs[r1];
+ break;
+ }
+ }
+
+ if (!(*flags & CPU_DTRACE_FAULT))
+ return (rval);
+
+ mstate->dtms_fltoffs = opc * sizeof (dif_instr_t);
+ mstate->dtms_present |= DTRACE_MSTATE_FLTOFFS;
+
+ return (0);
+}
+
+static void
+dtrace_action_breakpoint(dtrace_ecb_t *ecb)
+{
+ dtrace_probe_t *probe = ecb->dte_probe;
+ dtrace_provider_t *prov = probe->dtpr_provider;
+ char c[DTRACE_FULLNAMELEN + 80], *str;
+ char *msg = "dtrace: breakpoint action at probe ";
+ char *ecbmsg = " (ecb ";
+ uintptr_t mask = (0xf << (sizeof (uintptr_t) * NBBY / 4));
+ uintptr_t val = (uintptr_t)ecb;
+ int shift = (sizeof (uintptr_t) * NBBY) - 4, i = 0;
+
+ if (dtrace_destructive_disallow)
+ return;
+
+ /*
+ * It's impossible to be taking action on the NULL probe.
+ */
+ ASSERT(probe != NULL);
+
+ /*
+ * This is a poor man's (destitute man's?) sprintf(): we want to
+ * print the provider name, module name, function name and name of
+ * the probe, along with the hex address of the ECB with the breakpoint
+ * action -- all of which we must place in the character buffer by
+ * hand.
+ */
+ while (*msg != '\0')
+ c[i++] = *msg++;
+
+ for (str = prov->dtpv_name; *str != '\0'; str++)
+ c[i++] = *str;
+ c[i++] = ':';
+
+ for (str = probe->dtpr_mod; *str != '\0'; str++)
+ c[i++] = *str;
+ c[i++] = ':';
+
+ for (str = probe->dtpr_func; *str != '\0'; str++)
+ c[i++] = *str;
+ c[i++] = ':';
+
+ for (str = probe->dtpr_name; *str != '\0'; str++)
+ c[i++] = *str;
+
+ while (*ecbmsg != '\0')
+ c[i++] = *ecbmsg++;
+
+ while (shift >= 0) {
+ mask = (uintptr_t)0xf << shift;
+
+ if (val >= ((uintptr_t)1 << shift))
+ c[i++] = "0123456789abcdef"[(val & mask) >> shift];
+ shift -= 4;
+ }
+
+ c[i++] = ')';
+ c[i] = '\0';
+
+#if defined(sun)
+ debug_enter(c);
+#else
+ kdb_enter_why(KDB_WHY_DTRACE, "breakpoint action");
+#endif
+}
+
+static void
+dtrace_action_panic(dtrace_ecb_t *ecb)
+{
+ dtrace_probe_t *probe = ecb->dte_probe;
+
+ /*
+ * It's impossible to be taking action on the NULL probe.
+ */
+ ASSERT(probe != NULL);
+
+ if (dtrace_destructive_disallow)
+ return;
+
+ if (dtrace_panicked != NULL)
+ return;
+
+ if (dtrace_casptr(&dtrace_panicked, NULL, curthread) != NULL)
+ return;
+
+ /*
+ * We won the right to panic. (We want to be sure that only one
+ * thread calls panic() from dtrace_probe(), and that panic() is
+ * called exactly once.)
+ */
+ dtrace_panic("dtrace: panic action at probe %s:%s:%s:%s (ecb %p)",
+ probe->dtpr_provider->dtpv_name, probe->dtpr_mod,
+ probe->dtpr_func, probe->dtpr_name, (void *)ecb);
+}
+
+static void
+dtrace_action_raise(uint64_t sig)
+{
+ if (dtrace_destructive_disallow)
+ return;
+
+ if (sig >= NSIG) {
+ DTRACE_CPUFLAG_SET(CPU_DTRACE_ILLOP);
+ return;
+ }
+
+#if defined(sun)
+ /*
+ * raise() has a queue depth of 1 -- we ignore all subsequent
+ * invocations of the raise() action.
+ */
+ if (curthread->t_dtrace_sig == 0)
+ curthread->t_dtrace_sig = (uint8_t)sig;
+
+ curthread->t_sig_check = 1;
+ aston(curthread);
+#else
+ struct proc *p = curproc;
+ PROC_LOCK(p);
+ psignal(p, sig);
+ PROC_UNLOCK(p);
+#endif
+}
+
+static void
+dtrace_action_stop(void)
+{
+ if (dtrace_destructive_disallow)
+ return;
+
+#if defined(sun)
+ if (!curthread->t_dtrace_stop) {
+ curthread->t_dtrace_stop = 1;
+ curthread->t_sig_check = 1;
+ aston(curthread);
+ }
+#else
+ struct proc *p = curproc;
+ PROC_LOCK(p);
+ psignal(p, SIGSTOP);
+ PROC_UNLOCK(p);
+#endif
+}
+
+static void
+dtrace_action_chill(dtrace_mstate_t *mstate, hrtime_t val)
+{
+ hrtime_t now;
+ volatile uint16_t *flags;
+#if defined(sun)
+ cpu_t *cpu = CPU;
+#else
+ cpu_t *cpu = &solaris_cpu[curcpu];
+#endif
+
+ if (dtrace_destructive_disallow)
+ return;
+
+ flags = (volatile uint16_t *)&cpu_core[cpu->cpu_id].cpuc_dtrace_flags;
+
+ now = dtrace_gethrtime();
+
+ if (now - cpu->cpu_dtrace_chillmark > dtrace_chill_interval) {
+ /*
+ * We need to advance the mark to the current time.
+ */
+ cpu->cpu_dtrace_chillmark = now;
+ cpu->cpu_dtrace_chilled = 0;
+ }
+
+ /*
+ * Now check to see if the requested chill time would take us over
+ * the maximum amount of time allowed in the chill interval. (Or
+ * worse, if the calculation itself induces overflow.)
+ */
+ if (cpu->cpu_dtrace_chilled + val > dtrace_chill_max ||
+ cpu->cpu_dtrace_chilled + val < cpu->cpu_dtrace_chilled) {
+ *flags |= CPU_DTRACE_ILLOP;
+ return;
+ }
+
+ while (dtrace_gethrtime() - now < val)
+ continue;
+
+ /*
+ * Normally, we assure that the value of the variable "timestamp" does
+ * not change within an ECB. The presence of chill() represents an
+ * exception to this rule, however.
+ */
+ mstate->dtms_present &= ~DTRACE_MSTATE_TIMESTAMP;
+ cpu->cpu_dtrace_chilled += val;
+}
+
+#if defined(sun)
+static void
+dtrace_action_ustack(dtrace_mstate_t *mstate, dtrace_state_t *state,
+ uint64_t *buf, uint64_t arg)
+{
+ int nframes = DTRACE_USTACK_NFRAMES(arg);
+ int strsize = DTRACE_USTACK_STRSIZE(arg);
+ uint64_t *pcs = &buf[1], *fps;
+ char *str = (char *)&pcs[nframes];
+ int size, offs = 0, i, j;
+ uintptr_t old = mstate->dtms_scratch_ptr, saved;
+ uint16_t *flags = &cpu_core[curcpu].cpuc_dtrace_flags;
+ char *sym;
+
+ /*
+ * Should be taking a faster path if string space has not been
+ * allocated.
+ */
+ ASSERT(strsize != 0);
+
+ /*
+ * We will first allocate some temporary space for the frame pointers.
+ */
+ fps = (uint64_t *)P2ROUNDUP(mstate->dtms_scratch_ptr, 8);
+ size = (uintptr_t)fps - mstate->dtms_scratch_ptr +
+ (nframes * sizeof (uint64_t));
+
+ if (!DTRACE_INSCRATCH(mstate, size)) {
+ /*
+ * Not enough room for our frame pointers -- need to indicate
+ * that we ran out of scratch space.
+ */
+ DTRACE_CPUFLAG_SET(CPU_DTRACE_NOSCRATCH);
+ return;
+ }
+
+ mstate->dtms_scratch_ptr += size;
+ saved = mstate->dtms_scratch_ptr;
+
+ /*
+ * Now get a stack with both program counters and frame pointers.
+ */
+ DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT);
+ dtrace_getufpstack(buf, fps, nframes + 1);
+ DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT);
+
+ /*
+ * If that faulted, we're cooked.
+ */
+ if (*flags & CPU_DTRACE_FAULT)
+ goto out;
+
+ /*
+ * Now we want to walk up the stack, calling the USTACK helper. For
+ * each iteration, we restore the scratch pointer.
+ */
+ for (i = 0; i < nframes; i++) {
+ mstate->dtms_scratch_ptr = saved;
+
+ if (offs >= strsize)
+ break;
+
+ sym = (char *)(uintptr_t)dtrace_helper(
+ DTRACE_HELPER_ACTION_USTACK,
+ mstate, state, pcs[i], fps[i]);
+
+ /*
+ * If we faulted while running the helper, we're going to
+ * clear the fault and null out the corresponding string.
+ */
+ if (*flags & CPU_DTRACE_FAULT) {
+ *flags &= ~CPU_DTRACE_FAULT;
+ str[offs++] = '\0';
+ continue;
+ }
+
+ if (sym == NULL) {
+ str[offs++] = '\0';
+ continue;
+ }
+
+ DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT);
+
+ /*
+ * Now copy in the string that the helper returned to us.
+ */
+ for (j = 0; offs + j < strsize; j++) {
+ if ((str[offs + j] = sym[j]) == '\0')
+ break;
+ }
+
+ DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT);
+
+ offs += j + 1;
+ }
+
+ if (offs >= strsize) {
+ /*
+ * If we didn't have room for all of the strings, we don't
+ * abort processing -- this needn't be a fatal error -- but we
+ * still want to increment a counter (dts_stkstroverflows) to
+ * allow this condition to be warned about. (If this is from
+ * a jstack() action, it is easily tuned via jstackstrsize.)
+ */
+ dtrace_error(&state->dts_stkstroverflows);
+ }
+
+ while (offs < strsize)
+ str[offs++] = '\0';
+
+out:
+ mstate->dtms_scratch_ptr = old;
+}
+#endif
+
+/*
+ * If you're looking for the epicenter of DTrace, you just found it. This
+ * is the function called by the provider to fire a probe -- from which all
+ * subsequent probe-context DTrace activity emanates.
+ */
+void
+dtrace_probe(dtrace_id_t id, uintptr_t arg0, uintptr_t arg1,
+ uintptr_t arg2, uintptr_t arg3, uintptr_t arg4)
+{
+ processorid_t cpuid;
+ dtrace_icookie_t cookie;
+ dtrace_probe_t *probe;
+ dtrace_mstate_t mstate;
+ dtrace_ecb_t *ecb;
+ dtrace_action_t *act;
+ intptr_t offs;
+ size_t size;
+ int vtime, onintr;
+ volatile uint16_t *flags;
+ hrtime_t now;
+
+#if defined(sun)
+ /*
+ * Kick out immediately if this CPU is still being born (in which case
+ * curthread will be set to -1) or the current thread can't allow
+ * probes in its current context.
+ */
+ if (((uintptr_t)curthread & 1) || (curthread->t_flag & T_DONTDTRACE))
+ return;
+#endif
+
+ cookie = dtrace_interrupt_disable();
+ probe = dtrace_probes[id - 1];
+ cpuid = curcpu;
+ onintr = CPU_ON_INTR(CPU);
+
+ if (!onintr && probe->dtpr_predcache != DTRACE_CACHEIDNONE &&
+ probe->dtpr_predcache == curthread->t_predcache) {
+ /*
+ * We have hit in the predicate cache; we know that
+ * this predicate would evaluate to be false.
+ */
+ dtrace_interrupt_enable(cookie);
+ return;
+ }
+
+#if defined(sun)
+ if (panic_quiesce) {
+#else
+ if (panicstr != NULL) {
+#endif
+ /*
+ * We don't trace anything if we're panicking.
+ */
+ dtrace_interrupt_enable(cookie);
+ return;
+ }
+
+ now = dtrace_gethrtime();
+ vtime = dtrace_vtime_references != 0;
+
+ if (vtime && curthread->t_dtrace_start)
+ curthread->t_dtrace_vtime += now - curthread->t_dtrace_start;
+
+ mstate.dtms_difo = NULL;
+ mstate.dtms_probe = probe;
+ mstate.dtms_strtok = 0;
+ mstate.dtms_arg[0] = arg0;
+ mstate.dtms_arg[1] = arg1;
+ mstate.dtms_arg[2] = arg2;
+ mstate.dtms_arg[3] = arg3;
+ mstate.dtms_arg[4] = arg4;
+
+ flags = (volatile uint16_t *)&cpu_core[cpuid].cpuc_dtrace_flags;
+
+ for (ecb = probe->dtpr_ecb; ecb != NULL; ecb = ecb->dte_next) {
+ dtrace_predicate_t *pred = ecb->dte_predicate;
+ dtrace_state_t *state = ecb->dte_state;
+ dtrace_buffer_t *buf = &state->dts_buffer[cpuid];
+ dtrace_buffer_t *aggbuf = &state->dts_aggbuffer[cpuid];
+ dtrace_vstate_t *vstate = &state->dts_vstate;
+ dtrace_provider_t *prov = probe->dtpr_provider;
+ int committed = 0;
+ caddr_t tomax;
+
+ /*
+ * A little subtlety with the following (seemingly innocuous)
+ * declaration of the automatic 'val': by looking at the
+ * code, you might think that it could be declared in the
+ * action processing loop, below. (That is, it's only used in
+ * the action processing loop.) However, it must be declared
+ * out of that scope because in the case of DIF expression
+ * arguments to aggregating actions, one iteration of the
+ * action loop will use the last iteration's value.
+ */
+ uint64_t val = 0;
+
+ mstate.dtms_present = DTRACE_MSTATE_ARGS | DTRACE_MSTATE_PROBE;
+ *flags &= ~CPU_DTRACE_ERROR;
+
+ if (prov == dtrace_provider) {
+ /*
+ * If dtrace itself is the provider of this probe,
+ * we're only going to continue processing the ECB if
+ * arg0 (the dtrace_state_t) is equal to the ECB's
+ * creating state. (This prevents disjoint consumers
+ * from seeing one another's metaprobes.)
+ */
+ if (arg0 != (uint64_t)(uintptr_t)state)
+ continue;
+ }
+
+ if (state->dts_activity != DTRACE_ACTIVITY_ACTIVE) {
+ /*
+ * We're not currently active. If our provider isn't
+ * the dtrace pseudo provider, we're not interested.
+ */
+ if (prov != dtrace_provider)
+ continue;
+
+ /*
+ * Now we must further check if we are in the BEGIN
+ * probe. If we are, we will only continue processing
+ * if we're still in WARMUP -- if one BEGIN enabling
+ * has invoked the exit() action, we don't want to
+ * evaluate subsequent BEGIN enablings.
+ */
+ if (probe->dtpr_id == dtrace_probeid_begin &&
+ state->dts_activity != DTRACE_ACTIVITY_WARMUP) {
+ ASSERT(state->dts_activity ==
+ DTRACE_ACTIVITY_DRAINING);
+ continue;
+ }
+ }
+
+ if (ecb->dte_cond) {
+ /*
+ * If the dte_cond bits indicate that this
+ * consumer is only allowed to see user-mode firings
+ * of this probe, call the provider's dtps_usermode()
+ * entry point to check that the probe was fired
+ * while in a user context. Skip this ECB if that's
+ * not the case.
+ */
+ if ((ecb->dte_cond & DTRACE_COND_USERMODE) &&
+ prov->dtpv_pops.dtps_usermode(prov->dtpv_arg,
+ probe->dtpr_id, probe->dtpr_arg) == 0)
+ continue;
+
+#if defined(sun)
+ /*
+ * This is more subtle than it looks. We have to be
+ * absolutely certain that CRED() isn't going to
+ * change out from under us so it's only legit to
+ * examine that structure if we're in constrained
+ * situations. Currently, the only times we'll this
+ * check is if a non-super-user has enabled the
+ * profile or syscall providers -- providers that
+ * allow visibility of all processes. For the
+ * profile case, the check above will ensure that
+ * we're examining a user context.
+ */
+ if (ecb->dte_cond & DTRACE_COND_OWNER) {
+ cred_t *cr;
+ cred_t *s_cr =
+ ecb->dte_state->dts_cred.dcr_cred;
+ proc_t *proc;
+
+ ASSERT(s_cr != NULL);
+
+ if ((cr = CRED()) == NULL ||
+ s_cr->cr_uid != cr->cr_uid ||
+ s_cr->cr_uid != cr->cr_ruid ||
+ s_cr->cr_uid != cr->cr_suid ||
+ s_cr->cr_gid != cr->cr_gid ||
+ s_cr->cr_gid != cr->cr_rgid ||
+ s_cr->cr_gid != cr->cr_sgid ||
+ (proc = ttoproc(curthread)) == NULL ||
+ (proc->p_flag & SNOCD))
+ continue;
+ }
+
+ if (ecb->dte_cond & DTRACE_COND_ZONEOWNER) {
+ cred_t *cr;
+ cred_t *s_cr =
+ ecb->dte_state->dts_cred.dcr_cred;
+
+ ASSERT(s_cr != NULL);
+
+ if ((cr = CRED()) == NULL ||
+ s_cr->cr_zone->zone_id !=
+ cr->cr_zone->zone_id)
+ continue;
+ }
+#endif
+ }
+
+ if (now - state->dts_alive > dtrace_deadman_timeout) {
+ /*
+ * We seem to be dead. Unless we (a) have kernel
+ * destructive permissions (b) have expicitly enabled
+ * destructive actions and (c) destructive actions have
+ * not been disabled, we're going to transition into
+ * the KILLED state, from which no further processing
+ * on this state will be performed.
+ */
+ if (!dtrace_priv_kernel_destructive(state) ||
+ !state->dts_cred.dcr_destructive ||
+ dtrace_destructive_disallow) {
+ void *activity = &state->dts_activity;
+ dtrace_activity_t current;
+
+ do {
+ current = state->dts_activity;
+ } while (dtrace_cas32(activity, current,
+ DTRACE_ACTIVITY_KILLED) != current);
+
+ continue;
+ }
+ }
+
+ if ((offs = dtrace_buffer_reserve(buf, ecb->dte_needed,
+ ecb->dte_alignment, state, &mstate)) < 0)
+ continue;
+
+ tomax = buf->dtb_tomax;
+ ASSERT(tomax != NULL);
+
+ if (ecb->dte_size != 0)
+ DTRACE_STORE(uint32_t, tomax, offs, ecb->dte_epid);
+
+ mstate.dtms_epid = ecb->dte_epid;
+ mstate.dtms_present |= DTRACE_MSTATE_EPID;
+
+ if (state->dts_cred.dcr_visible & DTRACE_CRV_KERNEL)
+ mstate.dtms_access = DTRACE_ACCESS_KERNEL;
+ else
+ mstate.dtms_access = 0;
+
+ if (pred != NULL) {
+ dtrace_difo_t *dp = pred->dtp_difo;
+ int rval;
+
+ rval = dtrace_dif_emulate(dp, &mstate, vstate, state);
+
+ if (!(*flags & CPU_DTRACE_ERROR) && !rval) {
+ dtrace_cacheid_t cid = probe->dtpr_predcache;
+
+ if (cid != DTRACE_CACHEIDNONE && !onintr) {
+ /*
+ * Update the predicate cache...
+ */
+ ASSERT(cid == pred->dtp_cacheid);
+ curthread->t_predcache = cid;
+ }
+
+ continue;
+ }
+ }
+
+ for (act = ecb->dte_action; !(*flags & CPU_DTRACE_ERROR) &&
+ act != NULL; act = act->dta_next) {
+ size_t valoffs;
+ dtrace_difo_t *dp;
+ dtrace_recdesc_t *rec = &act->dta_rec;
+
+ size = rec->dtrd_size;
+ valoffs = offs + rec->dtrd_offset;
+
+ if (DTRACEACT_ISAGG(act->dta_kind)) {
+ uint64_t v = 0xbad;
+ dtrace_aggregation_t *agg;
+
+ agg = (dtrace_aggregation_t *)act;
+
+ if ((dp = act->dta_difo) != NULL)
+ v = dtrace_dif_emulate(dp,
+ &mstate, vstate, state);
+
+ if (*flags & CPU_DTRACE_ERROR)
+ continue;
+
+ /*
+ * Note that we always pass the expression
+ * value from the previous iteration of the
+ * action loop. This value will only be used
+ * if there is an expression argument to the
+ * aggregating action, denoted by the
+ * dtag_hasarg field.
+ */
+ dtrace_aggregate(agg, buf,
+ offs, aggbuf, v, val);
+ continue;
+ }
+
+ switch (act->dta_kind) {
+ case DTRACEACT_STOP:
+ if (dtrace_priv_proc_destructive(state))
+ dtrace_action_stop();
+ continue;
+
+ case DTRACEACT_BREAKPOINT:
+ if (dtrace_priv_kernel_destructive(state))
+ dtrace_action_breakpoint(ecb);
+ continue;
+
+ case DTRACEACT_PANIC:
+ if (dtrace_priv_kernel_destructive(state))
+ dtrace_action_panic(ecb);
+ continue;
+
+ case DTRACEACT_STACK:
+ if (!dtrace_priv_kernel(state))
+ continue;
+
+ dtrace_getpcstack((pc_t *)(tomax + valoffs),
+ size / sizeof (pc_t), probe->dtpr_aframes,
+ DTRACE_ANCHORED(probe) ? NULL :
+ (uint32_t *)arg0);
+ continue;
+
+#if defined(sun)
+ case DTRACEACT_JSTACK:
+ case DTRACEACT_USTACK:
+ if (!dtrace_priv_proc(state))
+ continue;
+
+ /*
+ * See comment in DIF_VAR_PID.
+ */
+ if (DTRACE_ANCHORED(mstate.dtms_probe) &&
+ CPU_ON_INTR(CPU)) {
+ int depth = DTRACE_USTACK_NFRAMES(
+ rec->dtrd_arg) + 1;
+
+ dtrace_bzero((void *)(tomax + valoffs),
+ DTRACE_USTACK_STRSIZE(rec->dtrd_arg)
+ + depth * sizeof (uint64_t));
+
+ continue;
+ }
+
+ if (DTRACE_USTACK_STRSIZE(rec->dtrd_arg) != 0 &&
+ curproc->p_dtrace_helpers != NULL) {
+ /*
+ * This is the slow path -- we have
+ * allocated string space, and we're
+ * getting the stack of a process that
+ * has helpers. Call into a separate
+ * routine to perform this processing.
+ */
+ dtrace_action_ustack(&mstate, state,
+ (uint64_t *)(tomax + valoffs),
+ rec->dtrd_arg);
+ continue;
+ }
+
+ DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT);
+ dtrace_getupcstack((uint64_t *)
+ (tomax + valoffs),
+ DTRACE_USTACK_NFRAMES(rec->dtrd_arg) + 1);
+ DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT);
+ continue;
+#endif
+
+ default:
+ break;
+ }
+
+ dp = act->dta_difo;
+ ASSERT(dp != NULL);
+
+ val = dtrace_dif_emulate(dp, &mstate, vstate, state);
+
+ if (*flags & CPU_DTRACE_ERROR)
+ continue;
+
+ switch (act->dta_kind) {
+ case DTRACEACT_SPECULATE:
+ ASSERT(buf == &state->dts_buffer[cpuid]);
+ buf = dtrace_speculation_buffer(state,
+ cpuid, val);
+
+ if (buf == NULL) {
+ *flags |= CPU_DTRACE_DROP;
+ continue;
+ }
+
+ offs = dtrace_buffer_reserve(buf,
+ ecb->dte_needed, ecb->dte_alignment,
+ state, NULL);
+
+ if (offs < 0) {
+ *flags |= CPU_DTRACE_DROP;
+ continue;
+ }
+
+ tomax = buf->dtb_tomax;
+ ASSERT(tomax != NULL);
+
+ if (ecb->dte_size != 0)
+ DTRACE_STORE(uint32_t, tomax, offs,
+ ecb->dte_epid);
+ continue;
+
+ case DTRACEACT_PRINTM: {
+ /* The DIF returns a 'memref'. */
+ uintptr_t *memref = (uintptr_t *)(uintptr_t) val;
+
+ /* Get the size from the memref. */
+ size = memref[1];
+
+ /*
+ * Check if the size exceeds the allocated
+ * buffer size.
+ */
+ if (size + sizeof(uintptr_t) > dp->dtdo_rtype.dtdt_size) {
+ /* Flag a drop! */
+ *flags |= CPU_DTRACE_DROP;
+ continue;
+ }
+
+ /* Store the size in the buffer first. */
+ DTRACE_STORE(uintptr_t, tomax,
+ valoffs, size);
+
+ /*
+ * Offset the buffer address to the start
+ * of the data.
+ */
+ valoffs += sizeof(uintptr_t);
+
+ /*
+ * Reset to the memory address rather than
+ * the memref array, then let the BYREF
+ * code below do the work to store the
+ * memory data in the buffer.
+ */
+ val = memref[0];
+ break;
+ }
+
+ case DTRACEACT_PRINTT: {
+ /* The DIF returns a 'typeref'. */
+ uintptr_t *typeref = (uintptr_t *)(uintptr_t) val;
+ char c = '\0' + 1;
+ size_t s;
+
+ /*
+ * Get the type string length and round it
+ * up so that the data that follows is
+ * aligned for easy access.
+ */
+ size_t typs = strlen((char *) typeref[2]) + 1;
+ typs = roundup(typs, sizeof(uintptr_t));
+
+ /*
+ *Get the size from the typeref using the
+ * number of elements and the type size.
+ */
+ size = typeref[1] * typeref[3];
+
+ /*
+ * Check if the size exceeds the allocated
+ * buffer size.
+ */
+ if (size + typs + 2 * sizeof(uintptr_t) > dp->dtdo_rtype.dtdt_size) {
+ /* Flag a drop! */
+ *flags |= CPU_DTRACE_DROP;
+
+ }
+
+ /* Store the size in the buffer first. */
+ DTRACE_STORE(uintptr_t, tomax,
+ valoffs, size);
+ valoffs += sizeof(uintptr_t);
+
+ /* Store the type size in the buffer. */
+ DTRACE_STORE(uintptr_t, tomax,
+ valoffs, typeref[3]);
+ valoffs += sizeof(uintptr_t);
+
+ val = typeref[2];
+
+ for (s = 0; s < typs; s++) {
+ if (c != '\0')
+ c = dtrace_load8(val++);
+
+ DTRACE_STORE(uint8_t, tomax,
+ valoffs++, c);
+ }
+
+ /*
+ * Reset to the memory address rather than
+ * the typeref array, then let the BYREF
+ * code below do the work to store the
+ * memory data in the buffer.
+ */
+ val = typeref[0];
+ break;
+ }
+
+ case DTRACEACT_CHILL:
+ if (dtrace_priv_kernel_destructive(state))
+ dtrace_action_chill(&mstate, val);
+ continue;
+
+ case DTRACEACT_RAISE:
+ if (dtrace_priv_proc_destructive(state))
+ dtrace_action_raise(val);
+ continue;
+
+ case DTRACEACT_COMMIT:
+ ASSERT(!committed);
+
+ /*
+ * We need to commit our buffer state.
+ */
+ if (ecb->dte_size)
+ buf->dtb_offset = offs + ecb->dte_size;
+ buf = &state->dts_buffer[cpuid];
+ dtrace_speculation_commit(state, cpuid, val);
+ committed = 1;
+ continue;
+
+ case DTRACEACT_DISCARD:
+ dtrace_speculation_discard(state, cpuid, val);
+ continue;
+
+ case DTRACEACT_DIFEXPR:
+ case DTRACEACT_LIBACT:
+ case DTRACEACT_PRINTF:
+ case DTRACEACT_PRINTA:
+ case DTRACEACT_SYSTEM:
+ case DTRACEACT_FREOPEN:
+ break;
+
+ case DTRACEACT_SYM:
+ case DTRACEACT_MOD:
+ if (!dtrace_priv_kernel(state))
+ continue;
+ break;
+
+ case DTRACEACT_USYM:
+ case DTRACEACT_UMOD:
+ case DTRACEACT_UADDR: {
+#if defined(sun)
+ struct pid *pid = curthread->t_procp->p_pidp;
+#endif
+
+ if (!dtrace_priv_proc(state))
+ continue;
+
+ DTRACE_STORE(uint64_t, tomax,
+#if defined(sun)
+ valoffs, (uint64_t)pid->pid_id);
+#else
+ valoffs, (uint64_t) curproc->p_pid);
+#endif
+ DTRACE_STORE(uint64_t, tomax,
+ valoffs + sizeof (uint64_t), val);
+
+ continue;
+ }
+
+ case DTRACEACT_EXIT: {
+ /*
+ * For the exit action, we are going to attempt
+ * to atomically set our activity to be
+ * draining. If this fails (either because
+ * another CPU has beat us to the exit action,
+ * or because our current activity is something
+ * other than ACTIVE or WARMUP), we will
+ * continue. This assures that the exit action
+ * can be successfully recorded at most once
+ * when we're in the ACTIVE state. If we're
+ * encountering the exit() action while in
+ * COOLDOWN, however, we want to honor the new
+ * status code. (We know that we're the only
+ * thread in COOLDOWN, so there is no race.)
+ */
+ void *activity = &state->dts_activity;
+ dtrace_activity_t current = state->dts_activity;
+
+ if (current == DTRACE_ACTIVITY_COOLDOWN)
+ break;
+
+ if (current != DTRACE_ACTIVITY_WARMUP)
+ current = DTRACE_ACTIVITY_ACTIVE;
+
+ if (dtrace_cas32(activity, current,
+ DTRACE_ACTIVITY_DRAINING) != current) {
+ *flags |= CPU_DTRACE_DROP;
+ continue;
+ }
+
+ break;
+ }
+
+ default:
+ ASSERT(0);
+ }
+
+ if (dp->dtdo_rtype.dtdt_flags & DIF_TF_BYREF) {
+ uintptr_t end = valoffs + size;
+
+ if (!dtrace_vcanload((void *)(uintptr_t)val,
+ &dp->dtdo_rtype, &mstate, vstate))
+ continue;
+
+ /*
+ * If this is a string, we're going to only
+ * load until we find the zero byte -- after
+ * which we'll store zero bytes.
+ */
+ if (dp->dtdo_rtype.dtdt_kind ==
+ DIF_TYPE_STRING) {
+ char c = '\0' + 1;
+ int intuple = act->dta_intuple;
+ size_t s;
+
+ for (s = 0; s < size; s++) {
+ if (c != '\0')
+ c = dtrace_load8(val++);
+
+ DTRACE_STORE(uint8_t, tomax,
+ valoffs++, c);
+
+ if (c == '\0' && intuple)
+ break;
+ }
+
+ continue;
+ }
+
+ while (valoffs < end) {
+ DTRACE_STORE(uint8_t, tomax, valoffs++,
+ dtrace_load8(val++));
+ }
+
+ continue;
+ }
+
+ switch (size) {
+ case 0:
+ break;
+
+ case sizeof (uint8_t):
+ DTRACE_STORE(uint8_t, tomax, valoffs, val);
+ break;
+ case sizeof (uint16_t):
+ DTRACE_STORE(uint16_t, tomax, valoffs, val);
+ break;
+ case sizeof (uint32_t):
+ DTRACE_STORE(uint32_t, tomax, valoffs, val);
+ break;
+ case sizeof (uint64_t):
+ DTRACE_STORE(uint64_t, tomax, valoffs, val);
+ break;
+ default:
+ /*
+ * Any other size should have been returned by
+ * reference, not by value.
+ */
+ ASSERT(0);
+ break;
+ }
+ }
+
+ if (*flags & CPU_DTRACE_DROP)
+ continue;
+
+ if (*flags & CPU_DTRACE_FAULT) {
+ int ndx;
+ dtrace_action_t *err;
+
+ buf->dtb_errors++;
+
+ if (probe->dtpr_id == dtrace_probeid_error) {
+ /*
+ * There's nothing we can do -- we had an
+ * error on the error probe. We bump an
+ * error counter to at least indicate that
+ * this condition happened.
+ */
+ dtrace_error(&state->dts_dblerrors);
+ continue;
+ }
+
+ if (vtime) {
+ /*
+ * Before recursing on dtrace_probe(), we
+ * need to explicitly clear out our start
+ * time to prevent it from being accumulated
+ * into t_dtrace_vtime.
+ */
+ curthread->t_dtrace_start = 0;
+ }
+
+ /*
+ * Iterate over the actions to figure out which action
+ * we were processing when we experienced the error.
+ * Note that act points _past_ the faulting action; if
+ * act is ecb->dte_action, the fault was in the
+ * predicate, if it's ecb->dte_action->dta_next it's
+ * in action #1, and so on.
+ */
+ for (err = ecb->dte_action, ndx = 0;
+ err != act; err = err->dta_next, ndx++)
+ continue;
+
+ dtrace_probe_error(state, ecb->dte_epid, ndx,
+ (mstate.dtms_present & DTRACE_MSTATE_FLTOFFS) ?
+ mstate.dtms_fltoffs : -1, DTRACE_FLAGS2FLT(*flags),
+ cpu_core[cpuid].cpuc_dtrace_illval);
+
+ continue;
+ }
+
+ if (!committed)
+ buf->dtb_offset = offs + ecb->dte_size;
+ }
+
+ if (vtime)
+ curthread->t_dtrace_start = dtrace_gethrtime();
+
+ dtrace_interrupt_enable(cookie);
+}
+
+/*
+ * DTrace Probe Hashing Functions
+ *
+ * The functions in this section (and indeed, the functions in remaining
+ * sections) are not _called_ from probe context. (Any exceptions to this are
+ * marked with a "Note:".) Rather, they are called from elsewhere in the
+ * DTrace framework to look-up probes in, add probes to and remove probes from
+ * the DTrace probe hashes. (Each probe is hashed by each element of the
+ * probe tuple -- allowing for fast lookups, regardless of what was
+ * specified.)
+ */
+static uint_t
+dtrace_hash_str(const char *p)
+{
+ unsigned int g;
+ uint_t hval = 0;
+
+ while (*p) {
+ hval = (hval << 4) + *p++;
+ if ((g = (hval & 0xf0000000)) != 0)
+ hval ^= g >> 24;
+ hval &= ~g;
+ }
+ return (hval);
+}
+
+static dtrace_hash_t *
+dtrace_hash_create(uintptr_t stroffs, uintptr_t nextoffs, uintptr_t prevoffs)
+{
+ dtrace_hash_t *hash = kmem_zalloc(sizeof (dtrace_hash_t), KM_SLEEP);
+
+ hash->dth_stroffs = stroffs;
+ hash->dth_nextoffs = nextoffs;
+ hash->dth_prevoffs = prevoffs;
+
+ hash->dth_size = 1;
+ hash->dth_mask = hash->dth_size - 1;
+
+ hash->dth_tab = kmem_zalloc(hash->dth_size *
+ sizeof (dtrace_hashbucket_t *), KM_SLEEP);
+
+ return (hash);
+}
+
+static void
+dtrace_hash_destroy(dtrace_hash_t *hash)
+{
+#ifdef DEBUG
+ int i;
+
+ for (i = 0; i < hash->dth_size; i++)
+ ASSERT(hash->dth_tab[i] == NULL);
+#endif
+
+ kmem_free(hash->dth_tab,
+ hash->dth_size * sizeof (dtrace_hashbucket_t *));
+ kmem_free(hash, sizeof (dtrace_hash_t));
+}
+
+static void
+dtrace_hash_resize(dtrace_hash_t *hash)
+{
+ int size = hash->dth_size, i, ndx;
+ int new_size = hash->dth_size << 1;
+ int new_mask = new_size - 1;
+ dtrace_hashbucket_t **new_tab, *bucket, *next;
+
+ ASSERT((new_size & new_mask) == 0);
+
+ new_tab = kmem_zalloc(new_size * sizeof (void *), KM_SLEEP);
+
+ for (i = 0; i < size; i++) {
+ for (bucket = hash->dth_tab[i]; bucket != NULL; bucket = next) {
+ dtrace_probe_t *probe = bucket->dthb_chain;
+
+ ASSERT(probe != NULL);
+ ndx = DTRACE_HASHSTR(hash, probe) & new_mask;
+
+ next = bucket->dthb_next;
+ bucket->dthb_next = new_tab[ndx];
+ new_tab[ndx] = bucket;
+ }
+ }
+
+ kmem_free(hash->dth_tab, hash->dth_size * sizeof (void *));
+ hash->dth_tab = new_tab;
+ hash->dth_size = new_size;
+ hash->dth_mask = new_mask;
+}
+
+static void
+dtrace_hash_add(dtrace_hash_t *hash, dtrace_probe_t *new)
+{
+ int hashval = DTRACE_HASHSTR(hash, new);
+ int ndx = hashval & hash->dth_mask;
+ dtrace_hashbucket_t *bucket = hash->dth_tab[ndx];
+ dtrace_probe_t **nextp, **prevp;
+
+ for (; bucket != NULL; bucket = bucket->dthb_next) {
+ if (DTRACE_HASHEQ(hash, bucket->dthb_chain, new))
+ goto add;
+ }
+
+ if ((hash->dth_nbuckets >> 1) > hash->dth_size) {
+ dtrace_hash_resize(hash);
+ dtrace_hash_add(hash, new);
+ return;
+ }
+
+ bucket = kmem_zalloc(sizeof (dtrace_hashbucket_t), KM_SLEEP);
+ bucket->dthb_next = hash->dth_tab[ndx];
+ hash->dth_tab[ndx] = bucket;
+ hash->dth_nbuckets++;
+
+add:
+ nextp = DTRACE_HASHNEXT(hash, new);
+ ASSERT(*nextp == NULL && *(DTRACE_HASHPREV(hash, new)) == NULL);
+ *nextp = bucket->dthb_chain;
+
+ if (bucket->dthb_chain != NULL) {
+ prevp = DTRACE_HASHPREV(hash, bucket->dthb_chain);
+ ASSERT(*prevp == NULL);
+ *prevp = new;
+ }
+
+ bucket->dthb_chain = new;
+ bucket->dthb_len++;
+}
+
+static dtrace_probe_t *
+dtrace_hash_lookup(dtrace_hash_t *hash, dtrace_probe_t *template)
+{
+ int hashval = DTRACE_HASHSTR(hash, template);
+ int ndx = hashval & hash->dth_mask;
+ dtrace_hashbucket_t *bucket = hash->dth_tab[ndx];
+
+ for (; bucket != NULL; bucket = bucket->dthb_next) {
+ if (DTRACE_HASHEQ(hash, bucket->dthb_chain, template))
+ return (bucket->dthb_chain);
+ }
+
+ return (NULL);
+}
+
+static int
+dtrace_hash_collisions(dtrace_hash_t *hash, dtrace_probe_t *template)
+{
+ int hashval = DTRACE_HASHSTR(hash, template);
+ int ndx = hashval & hash->dth_mask;
+ dtrace_hashbucket_t *bucket = hash->dth_tab[ndx];
+
+ for (; bucket != NULL; bucket = bucket->dthb_next) {
+ if (DTRACE_HASHEQ(hash, bucket->dthb_chain, template))
+ return (bucket->dthb_len);
+ }
+
+ return (0);
+}
+
+static void
+dtrace_hash_remove(dtrace_hash_t *hash, dtrace_probe_t *probe)
+{
+ int ndx = DTRACE_HASHSTR(hash, probe) & hash->dth_mask;
+ dtrace_hashbucket_t *bucket = hash->dth_tab[ndx];
+
+ dtrace_probe_t **prevp = DTRACE_HASHPREV(hash, probe);
+ dtrace_probe_t **nextp = DTRACE_HASHNEXT(hash, probe);
+
+ /*
+ * Find the bucket that we're removing this probe from.
+ */
+ for (; bucket != NULL; bucket = bucket->dthb_next) {
+ if (DTRACE_HASHEQ(hash, bucket->dthb_chain, probe))
+ break;
+ }
+
+ ASSERT(bucket != NULL);
+
+ if (*prevp == NULL) {
+ if (*nextp == NULL) {
+ /*
+ * The removed probe was the only probe on this
+ * bucket; we need to remove the bucket.
+ */
+ dtrace_hashbucket_t *b = hash->dth_tab[ndx];
+
+ ASSERT(bucket->dthb_chain == probe);
+ ASSERT(b != NULL);
+
+ if (b == bucket) {
+ hash->dth_tab[ndx] = bucket->dthb_next;
+ } else {
+ while (b->dthb_next != bucket)
+ b = b->dthb_next;
+ b->dthb_next = bucket->dthb_next;
+ }
+
+ ASSERT(hash->dth_nbuckets > 0);
+ hash->dth_nbuckets--;
+ kmem_free(bucket, sizeof (dtrace_hashbucket_t));
+ return;
+ }
+
+ bucket->dthb_chain = *nextp;
+ } else {
+ *(DTRACE_HASHNEXT(hash, *prevp)) = *nextp;
+ }
+
+ if (*nextp != NULL)
+ *(DTRACE_HASHPREV(hash, *nextp)) = *prevp;
+}
+
+/*
+ * DTrace Utility Functions
+ *
+ * These are random utility functions that are _not_ called from probe context.
+ */
+static int
+dtrace_badattr(const dtrace_attribute_t *a)
+{
+ return (a->dtat_name > DTRACE_STABILITY_MAX ||
+ a->dtat_data > DTRACE_STABILITY_MAX ||
+ a->dtat_class > DTRACE_CLASS_MAX);
+}
+
+/*
+ * Return a duplicate copy of a string. If the specified string is NULL,
+ * this function returns a zero-length string.
+ */
+static char *
+dtrace_strdup(const char *str)
+{
+ char *new = kmem_zalloc((str != NULL ? strlen(str) : 0) + 1, KM_SLEEP);
+
+ if (str != NULL)
+ (void) strcpy(new, str);
+
+ return (new);
+}
+
+#define DTRACE_ISALPHA(c) \
+ (((c) >= 'a' && (c) <= 'z') || ((c) >= 'A' && (c) <= 'Z'))
+
+static int
+dtrace_badname(const char *s)
+{
+ char c;
+
+ if (s == NULL || (c = *s++) == '\0')
+ return (0);
+
+ if (!DTRACE_ISALPHA(c) && c != '-' && c != '_' && c != '.')
+ return (1);
+
+ while ((c = *s++) != '\0') {
+ if (!DTRACE_ISALPHA(c) && (c < '0' || c > '9') &&
+ c != '-' && c != '_' && c != '.' && c != '`')
+ return (1);
+ }
+
+ return (0);
+}
+
+static void
+dtrace_cred2priv(cred_t *cr, uint32_t *privp, uid_t *uidp, zoneid_t *zoneidp)
+{
+ uint32_t priv;
+
+#if defined(sun)
+ if (cr == NULL || PRIV_POLICY_ONLY(cr, PRIV_ALL, B_FALSE)) {
+ /*
+ * For DTRACE_PRIV_ALL, the uid and zoneid don't matter.
+ */
+ priv = DTRACE_PRIV_ALL;
+ } else {
+ *uidp = crgetuid(cr);
+ *zoneidp = crgetzoneid(cr);
+
+ priv = 0;
+ if (PRIV_POLICY_ONLY(cr, PRIV_DTRACE_KERNEL, B_FALSE))
+ priv |= DTRACE_PRIV_KERNEL | DTRACE_PRIV_USER;
+ else if (PRIV_POLICY_ONLY(cr, PRIV_DTRACE_USER, B_FALSE))
+ priv |= DTRACE_PRIV_USER;
+ if (PRIV_POLICY_ONLY(cr, PRIV_DTRACE_PROC, B_FALSE))
+ priv |= DTRACE_PRIV_PROC;
+ if (PRIV_POLICY_ONLY(cr, PRIV_PROC_OWNER, B_FALSE))
+ priv |= DTRACE_PRIV_OWNER;
+ if (PRIV_POLICY_ONLY(cr, PRIV_PROC_ZONE, B_FALSE))
+ priv |= DTRACE_PRIV_ZONEOWNER;
+ }
+#else
+ priv = DTRACE_PRIV_ALL;
+#endif
+
+ *privp = priv;
+}
+
+#ifdef DTRACE_ERRDEBUG
+static void
+dtrace_errdebug(const char *str)
+{
+ int hval = dtrace_hash_str(str) % DTRACE_ERRHASHSZ;
+ int occupied = 0;
+
+ mutex_enter(&dtrace_errlock);
+ dtrace_errlast = str;
+ dtrace_errthread = curthread;
+
+ while (occupied++ < DTRACE_ERRHASHSZ) {
+ if (dtrace_errhash[hval].dter_msg == str) {
+ dtrace_errhash[hval].dter_count++;
+ goto out;
+ }
+
+ if (dtrace_errhash[hval].dter_msg != NULL) {
+ hval = (hval + 1) % DTRACE_ERRHASHSZ;
+ continue;
+ }
+
+ dtrace_errhash[hval].dter_msg = str;
+ dtrace_errhash[hval].dter_count = 1;
+ goto out;
+ }
+
+ panic("dtrace: undersized error hash");
+out:
+ mutex_exit(&dtrace_errlock);
+}
+#endif
+
+/*
+ * DTrace Matching Functions
+ *
+ * These functions are used to match groups of probes, given some elements of
+ * a probe tuple, or some globbed expressions for elements of a probe tuple.
+ */
+static int
+dtrace_match_priv(const dtrace_probe_t *prp, uint32_t priv, uid_t uid,
+ zoneid_t zoneid)
+{
+ if (priv != DTRACE_PRIV_ALL) {
+ uint32_t ppriv = prp->dtpr_provider->dtpv_priv.dtpp_flags;
+ uint32_t match = priv & ppriv;
+
+ /*
+ * No PRIV_DTRACE_* privileges...
+ */
+ if ((priv & (DTRACE_PRIV_PROC | DTRACE_PRIV_USER |
+ DTRACE_PRIV_KERNEL)) == 0)
+ return (0);
+
+ /*
+ * No matching bits, but there were bits to match...
+ */
+ if (match == 0 && ppriv != 0)
+ return (0);
+
+ /*
+ * Need to have permissions to the process, but don't...
+ */
+ if (((ppriv & ~match) & DTRACE_PRIV_OWNER) != 0 &&
+ uid != prp->dtpr_provider->dtpv_priv.dtpp_uid) {
+ return (0);
+ }
+
+ /*
+ * Need to be in the same zone unless we possess the
+ * privilege to examine all zones.
+ */
+ if (((ppriv & ~match) & DTRACE_PRIV_ZONEOWNER) != 0 &&
+ zoneid != prp->dtpr_provider->dtpv_priv.dtpp_zoneid) {
+ return (0);
+ }
+ }
+
+ return (1);
+}
+
+/*
+ * dtrace_match_probe compares a dtrace_probe_t to a pre-compiled key, which
+ * consists of input pattern strings and an ops-vector to evaluate them.
+ * This function returns >0 for match, 0 for no match, and <0 for error.
+ */
+static int
+dtrace_match_probe(const dtrace_probe_t *prp, const dtrace_probekey_t *pkp,
+ uint32_t priv, uid_t uid, zoneid_t zoneid)
+{
+ dtrace_provider_t *pvp = prp->dtpr_provider;
+ int rv;
+
+ if (pvp->dtpv_defunct)
+ return (0);
+
+ if ((rv = pkp->dtpk_pmatch(pvp->dtpv_name, pkp->dtpk_prov, 0)) <= 0)
+ return (rv);
+
+ if ((rv = pkp->dtpk_mmatch(prp->dtpr_mod, pkp->dtpk_mod, 0)) <= 0)
+ return (rv);
+
+ if ((rv = pkp->dtpk_fmatch(prp->dtpr_func, pkp->dtpk_func, 0)) <= 0)
+ return (rv);
+
+ if ((rv = pkp->dtpk_nmatch(prp->dtpr_name, pkp->dtpk_name, 0)) <= 0)
+ return (rv);
+
+ if (dtrace_match_priv(prp, priv, uid, zoneid) == 0)
+ return (0);
+
+ return (rv);
+}
+
+/*
+ * dtrace_match_glob() is a safe kernel implementation of the gmatch(3GEN)
+ * interface for matching a glob pattern 'p' to an input string 's'. Unlike
+ * libc's version, the kernel version only applies to 8-bit ASCII strings.
+ * In addition, all of the recursion cases except for '*' matching have been
+ * unwound. For '*', we still implement recursive evaluation, but a depth
+ * counter is maintained and matching is aborted if we recurse too deep.
+ * The function returns 0 if no match, >0 if match, and <0 if recursion error.
+ */
+static int
+dtrace_match_glob(const char *s, const char *p, int depth)
+{
+ const char *olds;
+ char s1, c;
+ int gs;
+
+ if (depth > DTRACE_PROBEKEY_MAXDEPTH)
+ return (-1);
+
+ if (s == NULL)
+ s = ""; /* treat NULL as empty string */
+
+top:
+ olds = s;
+ s1 = *s++;
+
+ if (p == NULL)
+ return (0);
+
+ if ((c = *p++) == '\0')
+ return (s1 == '\0');
+
+ switch (c) {
+ case '[': {
+ int ok = 0, notflag = 0;
+ char lc = '\0';
+
+ if (s1 == '\0')
+ return (0);
+
+ if (*p == '!') {
+ notflag = 1;
+ p++;
+ }
+
+ if ((c = *p++) == '\0')
+ return (0);
+
+ do {
+ if (c == '-' && lc != '\0' && *p != ']') {
+ if ((c = *p++) == '\0')
+ return (0);
+ if (c == '\\' && (c = *p++) == '\0')
+ return (0);
+
+ if (notflag) {
+ if (s1 < lc || s1 > c)
+ ok++;
+ else
+ return (0);
+ } else if (lc <= s1 && s1 <= c)
+ ok++;
+
+ } else if (c == '\\' && (c = *p++) == '\0')
+ return (0);
+
+ lc = c; /* save left-hand 'c' for next iteration */
+
+ if (notflag) {
+ if (s1 != c)
+ ok++;
+ else
+ return (0);
+ } else if (s1 == c)
+ ok++;
+
+ if ((c = *p++) == '\0')
+ return (0);
+
+ } while (c != ']');
+
+ if (ok)
+ goto top;
+
+ return (0);
+ }
+
+ case '\\':
+ if ((c = *p++) == '\0')
+ return (0);
+ /*FALLTHRU*/
+
+ default:
+ if (c != s1)
+ return (0);
+ /*FALLTHRU*/
+
+ case '?':
+ if (s1 != '\0')
+ goto top;
+ return (0);
+
+ case '*':
+ while (*p == '*')
+ p++; /* consecutive *'s are identical to a single one */
+
+ if (*p == '\0')
+ return (1);
+
+ for (s = olds; *s != '\0'; s++) {
+ if ((gs = dtrace_match_glob(s, p, depth + 1)) != 0)
+ return (gs);
+ }
+
+ return (0);
+ }
+}
+
+/*ARGSUSED*/
+static int
+dtrace_match_string(const char *s, const char *p, int depth)
+{
+ return (s != NULL && strcmp(s, p) == 0);
+}
+
+/*ARGSUSED*/
+static int
+dtrace_match_nul(const char *s, const char *p, int depth)
+{
+ return (1); /* always match the empty pattern */
+}
+
+/*ARGSUSED*/
+static int
+dtrace_match_nonzero(const char *s, const char *p, int depth)
+{
+ return (s != NULL && s[0] != '\0');
+}
+
+static int
+dtrace_match(const dtrace_probekey_t *pkp, uint32_t priv, uid_t uid,
+ zoneid_t zoneid, int (*matched)(dtrace_probe_t *, void *), void *arg)
+{
+ dtrace_probe_t template, *probe;
+ dtrace_hash_t *hash = NULL;
+ int len, best = INT_MAX, nmatched = 0;
+ dtrace_id_t i;
+
+ ASSERT(MUTEX_HELD(&dtrace_lock));
+
+ /*
+ * If the probe ID is specified in the key, just lookup by ID and
+ * invoke the match callback once if a matching probe is found.
+ */
+ if (pkp->dtpk_id != DTRACE_IDNONE) {
+ if ((probe = dtrace_probe_lookup_id(pkp->dtpk_id)) != NULL &&
+ dtrace_match_probe(probe, pkp, priv, uid, zoneid) > 0) {
+ (void) (*matched)(probe, arg);
+ nmatched++;
+ }
+ return (nmatched);
+ }
+
+ template.dtpr_mod = (char *)pkp->dtpk_mod;
+ template.dtpr_func = (char *)pkp->dtpk_func;
+ template.dtpr_name = (char *)pkp->dtpk_name;
+
+ /*
+ * We want to find the most distinct of the module name, function
+ * name, and name. So for each one that is not a glob pattern or
+ * empty string, we perform a lookup in the corresponding hash and
+ * use the hash table with the fewest collisions to do our search.
+ */
+ if (pkp->dtpk_mmatch == &dtrace_match_string &&
+ (len = dtrace_hash_collisions(dtrace_bymod, &template)) < best) {
+ best = len;
+ hash = dtrace_bymod;
+ }
+
+ if (pkp->dtpk_fmatch == &dtrace_match_string &&
+ (len = dtrace_hash_collisions(dtrace_byfunc, &template)) < best) {
+ best = len;
+ hash = dtrace_byfunc;
+ }
+
+ if (pkp->dtpk_nmatch == &dtrace_match_string &&
+ (len = dtrace_hash_collisions(dtrace_byname, &template)) < best) {
+ best = len;
+ hash = dtrace_byname;
+ }
+
+ /*
+ * If we did not select a hash table, iterate over every probe and
+ * invoke our callback for each one that matches our input probe key.
+ */
+ if (hash == NULL) {
+ for (i = 0; i < dtrace_nprobes; i++) {
+ if ((probe = dtrace_probes[i]) == NULL ||
+ dtrace_match_probe(probe, pkp, priv, uid,
+ zoneid) <= 0)
+ continue;
+
+ nmatched++;
+
+ if ((*matched)(probe, arg) != DTRACE_MATCH_NEXT)
+ break;
+ }
+
+ return (nmatched);
+ }
+
+ /*
+ * If we selected a hash table, iterate over each probe of the same key
+ * name and invoke the callback for every probe that matches the other
+ * attributes of our input probe key.
+ */
+ for (probe = dtrace_hash_lookup(hash, &template); probe != NULL;
+ probe = *(DTRACE_HASHNEXT(hash, probe))) {
+
+ if (dtrace_match_probe(probe, pkp, priv, uid, zoneid) <= 0)
+ continue;
+
+ nmatched++;
+
+ if ((*matched)(probe, arg) != DTRACE_MATCH_NEXT)
+ break;
+ }
+
+ return (nmatched);
+}
+
+/*
+ * Return the function pointer dtrace_probecmp() should use to compare the
+ * specified pattern with a string. For NULL or empty patterns, we select
+ * dtrace_match_nul(). For glob pattern strings, we use dtrace_match_glob().
+ * For non-empty non-glob strings, we use dtrace_match_string().
+ */
+static dtrace_probekey_f *
+dtrace_probekey_func(const char *p)
+{
+ char c;
+
+ if (p == NULL || *p == '\0')
+ return (&dtrace_match_nul);
+
+ while ((c = *p++) != '\0') {
+ if (c == '[' || c == '?' || c == '*' || c == '\\')
+ return (&dtrace_match_glob);
+ }
+
+ return (&dtrace_match_string);
+}
+
+/*
+ * Build a probe comparison key for use with dtrace_match_probe() from the
+ * given probe description. By convention, a null key only matches anchored
+ * probes: if each field is the empty string, reset dtpk_fmatch to
+ * dtrace_match_nonzero().
+ */
+static void
+dtrace_probekey(dtrace_probedesc_t *pdp, dtrace_probekey_t *pkp)
+{
+ pkp->dtpk_prov = pdp->dtpd_provider;
+ pkp->dtpk_pmatch = dtrace_probekey_func(pdp->dtpd_provider);
+
+ pkp->dtpk_mod = pdp->dtpd_mod;
+ pkp->dtpk_mmatch = dtrace_probekey_func(pdp->dtpd_mod);
+
+ pkp->dtpk_func = pdp->dtpd_func;
+ pkp->dtpk_fmatch = dtrace_probekey_func(pdp->dtpd_func);
+
+ pkp->dtpk_name = pdp->dtpd_name;
+ pkp->dtpk_nmatch = dtrace_probekey_func(pdp->dtpd_name);
+
+ pkp->dtpk_id = pdp->dtpd_id;
+
+ if (pkp->dtpk_id == DTRACE_IDNONE &&
+ pkp->dtpk_pmatch == &dtrace_match_nul &&
+ pkp->dtpk_mmatch == &dtrace_match_nul &&
+ pkp->dtpk_fmatch == &dtrace_match_nul &&
+ pkp->dtpk_nmatch == &dtrace_match_nul)
+ pkp->dtpk_fmatch = &dtrace_match_nonzero;
+}
+
+/*
+ * DTrace Provider-to-Framework API Functions
+ *
+ * These functions implement much of the Provider-to-Framework API, as
+ * described in <sys/dtrace.h>. The parts of the API not in this section are
+ * the functions in the API for probe management (found below), and
+ * dtrace_probe() itself (found above).
+ */
+
+/*
+ * Register the calling provider with the DTrace framework. This should
+ * generally be called by DTrace providers in their attach(9E) entry point.
+ */
+int
+dtrace_register(const char *name, const dtrace_pattr_t *pap, uint32_t priv,
+ cred_t *cr, const dtrace_pops_t *pops, void *arg, dtrace_provider_id_t *idp)
+{
+ dtrace_provider_t *provider;
+
+ if (name == NULL || pap == NULL || pops == NULL || idp == NULL) {
+ cmn_err(CE_WARN, "failed to register provider '%s': invalid "
+ "arguments", name ? name : "<NULL>");
+ return (EINVAL);
+ }
+
+ if (name[0] == '\0' || dtrace_badname(name)) {
+ cmn_err(CE_WARN, "failed to register provider '%s': invalid "
+ "provider name", name);
+ return (EINVAL);
+ }
+
+ if ((pops->dtps_provide == NULL && pops->dtps_provide_module == NULL) ||
+ pops->dtps_enable == NULL || pops->dtps_disable == NULL ||
+ pops->dtps_destroy == NULL ||
+ ((pops->dtps_resume == NULL) != (pops->dtps_suspend == NULL))) {
+ cmn_err(CE_WARN, "failed to register provider '%s': invalid "
+ "provider ops", name);
+ return (EINVAL);
+ }
+
+ if (dtrace_badattr(&pap->dtpa_provider) ||
+ dtrace_badattr(&pap->dtpa_mod) ||
+ dtrace_badattr(&pap->dtpa_func) ||
+ dtrace_badattr(&pap->dtpa_name) ||
+ dtrace_badattr(&pap->dtpa_args)) {
+ cmn_err(CE_WARN, "failed to register provider '%s': invalid "
+ "provider attributes", name);
+ return (EINVAL);
+ }
+
+ if (priv & ~DTRACE_PRIV_ALL) {
+ cmn_err(CE_WARN, "failed to register provider '%s': invalid "
+ "privilege attributes", name);
+ return (EINVAL);
+ }
+
+ if ((priv & DTRACE_PRIV_KERNEL) &&
+ (priv & (DTRACE_PRIV_USER | DTRACE_PRIV_OWNER)) &&
+ pops->dtps_usermode == NULL) {
+ cmn_err(CE_WARN, "failed to register provider '%s': need "
+ "dtps_usermode() op for given privilege attributes", name);
+ return (EINVAL);
+ }
+
+ provider = kmem_zalloc(sizeof (dtrace_provider_t), KM_SLEEP);
+ provider->dtpv_name = kmem_alloc(strlen(name) + 1, KM_SLEEP);
+ (void) strcpy(provider->dtpv_name, name);
+
+ provider->dtpv_attr = *pap;
+ provider->dtpv_priv.dtpp_flags = priv;
+ if (cr != NULL) {
+ provider->dtpv_priv.dtpp_uid = crgetuid(cr);
+ provider->dtpv_priv.dtpp_zoneid = crgetzoneid(cr);
+ }
+ provider->dtpv_pops = *pops;
+
+ if (pops->dtps_provide == NULL) {
+ ASSERT(pops->dtps_provide_module != NULL);
+ provider->dtpv_pops.dtps_provide =
+ (void (*)(void *, dtrace_probedesc_t *))dtrace_nullop;
+ }
+
+ if (pops->dtps_provide_module == NULL) {
+ ASSERT(pops->dtps_provide != NULL);
+ provider->dtpv_pops.dtps_provide_module =
+ (void (*)(void *, modctl_t *))dtrace_nullop;
+ }
+
+ if (pops->dtps_suspend == NULL) {
+ ASSERT(pops->dtps_resume == NULL);
+ provider->dtpv_pops.dtps_suspend =
+ (void (*)(void *, dtrace_id_t, void *))dtrace_nullop;
+ provider->dtpv_pops.dtps_resume =
+ (void (*)(void *, dtrace_id_t, void *))dtrace_nullop;
+ }
+
+ provider->dtpv_arg = arg;
+ *idp = (dtrace_provider_id_t)provider;
+
+ if (pops == &dtrace_provider_ops) {
+ ASSERT(MUTEX_HELD(&dtrace_provider_lock));
+ ASSERT(MUTEX_HELD(&dtrace_lock));
+ ASSERT(dtrace_anon.dta_enabling == NULL);
+
+ /*
+ * We make sure that the DTrace provider is at the head of
+ * the provider chain.
+ */
+ provider->dtpv_next = dtrace_provider;
+ dtrace_provider = provider;
+ return (0);
+ }
+
+ mutex_enter(&dtrace_provider_lock);
+ mutex_enter(&dtrace_lock);
+
+ /*
+ * If there is at least one provider registered, we'll add this
+ * provider after the first provider.
+ */
+ if (dtrace_provider != NULL) {
+ provider->dtpv_next = dtrace_provider->dtpv_next;
+ dtrace_provider->dtpv_next = provider;
+ } else {
+ dtrace_provider = provider;
+ }
+
+ if (dtrace_retained != NULL) {
+ dtrace_enabling_provide(provider);
+
+ /*
+ * Now we need to call dtrace_enabling_matchall() -- which
+ * will acquire cpu_lock and dtrace_lock. We therefore need
+ * to drop all of our locks before calling into it...
+ */
+ mutex_exit(&dtrace_lock);
+ mutex_exit(&dtrace_provider_lock);
+ dtrace_enabling_matchall();
+
+ return (0);
+ }
+
+ mutex_exit(&dtrace_lock);
+ mutex_exit(&dtrace_provider_lock);
+
+ return (0);
+}
+
+/*
+ * Unregister the specified provider from the DTrace framework. This should
+ * generally be called by DTrace providers in their detach(9E) entry point.
+ */
+int
+dtrace_unregister(dtrace_provider_id_t id)
+{
+ dtrace_provider_t *old = (dtrace_provider_t *)id;
+ dtrace_provider_t *prev = NULL;
+ int i, self = 0;
+ dtrace_probe_t *probe, *first = NULL;
+
+ if (old->dtpv_pops.dtps_enable ==
+ (void (*)(void *, dtrace_id_t, void *))dtrace_nullop) {
+ /*
+ * If DTrace itself is the provider, we're called with locks
+ * already held.
+ */
+ ASSERT(old == dtrace_provider);
+#if defined(sun)
+ ASSERT(dtrace_devi != NULL);
+#endif
+ ASSERT(MUTEX_HELD(&dtrace_provider_lock));
+ ASSERT(MUTEX_HELD(&dtrace_lock));
+ self = 1;
+
+ if (dtrace_provider->dtpv_next != NULL) {
+ /*
+ * There's another provider here; return failure.
+ */
+ return (EBUSY);
+ }
+ } else {
+ mutex_enter(&dtrace_provider_lock);
+ mutex_enter(&mod_lock);
+ mutex_enter(&dtrace_lock);
+ }
+
+ /*
+ * If anyone has /dev/dtrace open, or if there are anonymous enabled
+ * probes, we refuse to let providers slither away, unless this
+ * provider has already been explicitly invalidated.
+ */
+ if (!old->dtpv_defunct &&
+ (dtrace_opens || (dtrace_anon.dta_state != NULL &&
+ dtrace_anon.dta_state->dts_necbs > 0))) {
+ if (!self) {
+ mutex_exit(&dtrace_lock);
+ mutex_exit(&mod_lock);
+ mutex_exit(&dtrace_provider_lock);
+ }
+ return (EBUSY);
+ }
+
+ /*
+ * Attempt to destroy the probes associated with this provider.
+ */
+ for (i = 0; i < dtrace_nprobes; i++) {
+ if ((probe = dtrace_probes[i]) == NULL)
+ continue;
+
+ if (probe->dtpr_provider != old)
+ continue;
+
+ if (probe->dtpr_ecb == NULL)
+ continue;
+
+ /*
+ * We have at least one ECB; we can't remove this provider.
+ */
+ if (!self) {
+ mutex_exit(&dtrace_lock);
+ mutex_exit(&mod_lock);
+ mutex_exit(&dtrace_provider_lock);
+ }
+ return (EBUSY);
+ }
+
+ /*
+ * All of the probes for this provider are disabled; we can safely
+ * remove all of them from their hash chains and from the probe array.
+ */
+ for (i = 0; i < dtrace_nprobes; i++) {
+ if ((probe = dtrace_probes[i]) == NULL)
+ continue;
+
+ if (probe->dtpr_provider != old)
+ continue;
+
+ dtrace_probes[i] = NULL;
+
+ dtrace_hash_remove(dtrace_bymod, probe);
+ dtrace_hash_remove(dtrace_byfunc, probe);
+ dtrace_hash_remove(dtrace_byname, probe);
+
+ if (first == NULL) {
+ first = probe;
+ probe->dtpr_nextmod = NULL;
+ } else {
+ probe->dtpr_nextmod = first;
+ first = probe;
+ }
+ }
+
+ /*
+ * The provider's probes have been removed from the hash chains and
+ * from the probe array. Now issue a dtrace_sync() to be sure that
+ * everyone has cleared out from any probe array processing.
+ */
+ dtrace_sync();
+
+ for (probe = first; probe != NULL; probe = first) {
+ first = probe->dtpr_nextmod;
+
+ old->dtpv_pops.dtps_destroy(old->dtpv_arg, probe->dtpr_id,
+ probe->dtpr_arg);
+ kmem_free(probe->dtpr_mod, strlen(probe->dtpr_mod) + 1);
+ kmem_free(probe->dtpr_func, strlen(probe->dtpr_func) + 1);
+ kmem_free(probe->dtpr_name, strlen(probe->dtpr_name) + 1);
+#if defined(sun)
+ vmem_free(dtrace_arena, (void *)(uintptr_t)(probe->dtpr_id), 1);
+#else
+ free_unr(dtrace_arena, probe->dtpr_id);
+#endif
+ kmem_free(probe, sizeof (dtrace_probe_t));
+ }
+
+ if ((prev = dtrace_provider) == old) {
+#if defined(sun)
+ ASSERT(self || dtrace_devi == NULL);
+ ASSERT(old->dtpv_next == NULL || dtrace_devi == NULL);
+#endif
+ dtrace_provider = old->dtpv_next;
+ } else {
+ while (prev != NULL && prev->dtpv_next != old)
+ prev = prev->dtpv_next;
+
+ if (prev == NULL) {
+ panic("attempt to unregister non-existent "
+ "dtrace provider %p\n", (void *)id);
+ }
+
+ prev->dtpv_next = old->dtpv_next;
+ }
+
+ if (!self) {
+ mutex_exit(&dtrace_lock);
+ mutex_exit(&mod_lock);
+ mutex_exit(&dtrace_provider_lock);
+ }
+
+ kmem_free(old->dtpv_name, strlen(old->dtpv_name) + 1);
+ kmem_free(old, sizeof (dtrace_provider_t));
+
+ return (0);
+}
+
+/*
+ * Invalidate the specified provider. All subsequent probe lookups for the
+ * specified provider will fail, but its probes will not be removed.
+ */
+void
+dtrace_invalidate(dtrace_provider_id_t id)
+{
+ dtrace_provider_t *pvp = (dtrace_provider_t *)id;
+
+ ASSERT(pvp->dtpv_pops.dtps_enable !=
+ (void (*)(void *, dtrace_id_t, void *))dtrace_nullop);
+
+ mutex_enter(&dtrace_provider_lock);
+ mutex_enter(&dtrace_lock);
+
+ pvp->dtpv_defunct = 1;
+
+ mutex_exit(&dtrace_lock);
+ mutex_exit(&dtrace_provider_lock);
+}
+
+/*
+ * Indicate whether or not DTrace has attached.
+ */
+int
+dtrace_attached(void)
+{
+ /*
+ * dtrace_provider will be non-NULL iff the DTrace driver has
+ * attached. (It's non-NULL because DTrace is always itself a
+ * provider.)
+ */
+ return (dtrace_provider != NULL);
+}
+
+/*
+ * Remove all the unenabled probes for the given provider. This function is
+ * not unlike dtrace_unregister(), except that it doesn't remove the provider
+ * -- just as many of its associated probes as it can.
+ */
+int
+dtrace_condense(dtrace_provider_id_t id)
+{
+ dtrace_provider_t *prov = (dtrace_provider_t *)id;
+ int i;
+ dtrace_probe_t *probe;
+
+ /*
+ * Make sure this isn't the dtrace provider itself.
+ */
+ ASSERT(prov->dtpv_pops.dtps_enable !=
+ (void (*)(void *, dtrace_id_t, void *))dtrace_nullop);
+
+ mutex_enter(&dtrace_provider_lock);
+ mutex_enter(&dtrace_lock);
+
+ /*
+ * Attempt to destroy the probes associated with this provider.
+ */
+ for (i = 0; i < dtrace_nprobes; i++) {
+ if ((probe = dtrace_probes[i]) == NULL)
+ continue;
+
+ if (probe->dtpr_provider != prov)
+ continue;
+
+ if (probe->dtpr_ecb != NULL)
+ continue;
+
+ dtrace_probes[i] = NULL;
+
+ dtrace_hash_remove(dtrace_bymod, probe);
+ dtrace_hash_remove(dtrace_byfunc, probe);
+ dtrace_hash_remove(dtrace_byname, probe);
+
+ prov->dtpv_pops.dtps_destroy(prov->dtpv_arg, i + 1,
+ probe->dtpr_arg);
+ kmem_free(probe->dtpr_mod, strlen(probe->dtpr_mod) + 1);
+ kmem_free(probe->dtpr_func, strlen(probe->dtpr_func) + 1);
+ kmem_free(probe->dtpr_name, strlen(probe->dtpr_name) + 1);
+ kmem_free(probe, sizeof (dtrace_probe_t));
+#if defined(sun)
+ vmem_free(dtrace_arena, (void *)((uintptr_t)i + 1), 1);
+#else
+ free_unr(dtrace_arena, i + 1);
+#endif
+ }
+
+ mutex_exit(&dtrace_lock);
+ mutex_exit(&dtrace_provider_lock);
+
+ return (0);
+}
+
+/*
+ * DTrace Probe Management Functions
+ *
+ * The functions in this section perform the DTrace probe management,
+ * including functions to create probes, look-up probes, and call into the
+ * providers to request that probes be provided. Some of these functions are
+ * in the Provider-to-Framework API; these functions can be identified by the
+ * fact that they are not declared "static".
+ */
+
+/*
+ * Create a probe with the specified module name, function name, and name.
+ */
+dtrace_id_t
+dtrace_probe_create(dtrace_provider_id_t prov, const char *mod,
+ const char *func, const char *name, int aframes, void *arg)
+{
+ dtrace_probe_t *probe, **probes;
+ dtrace_provider_t *provider = (dtrace_provider_t *)prov;
+ dtrace_id_t id;
+
+ if (provider == dtrace_provider) {
+ ASSERT(MUTEX_HELD(&dtrace_lock));
+ } else {
+ mutex_enter(&dtrace_lock);
+ }
+
+#if defined(sun)
+ id = (dtrace_id_t)(uintptr_t)vmem_alloc(dtrace_arena, 1,
+ VM_BESTFIT | VM_SLEEP);
+#else
+ id = alloc_unr(dtrace_arena);
+#endif
+ probe = kmem_zalloc(sizeof (dtrace_probe_t), KM_SLEEP);
+
+ probe->dtpr_id = id;
+ probe->dtpr_gen = dtrace_probegen++;
+ probe->dtpr_mod = dtrace_strdup(mod);
+ probe->dtpr_func = dtrace_strdup(func);
+ probe->dtpr_name = dtrace_strdup(name);
+ probe->dtpr_arg = arg;
+ probe->dtpr_aframes = aframes;
+ probe->dtpr_provider = provider;
+
+ dtrace_hash_add(dtrace_bymod, probe);
+ dtrace_hash_add(dtrace_byfunc, probe);
+ dtrace_hash_add(dtrace_byname, probe);
+
+ if (id - 1 >= dtrace_nprobes) {
+ size_t osize = dtrace_nprobes * sizeof (dtrace_probe_t *);
+ size_t nsize = osize << 1;
+
+ if (nsize == 0) {
+ ASSERT(osize == 0);
+ ASSERT(dtrace_probes == NULL);
+ nsize = sizeof (dtrace_probe_t *);
+ }
+
+ probes = kmem_zalloc(nsize, KM_SLEEP);
+
+ if (dtrace_probes == NULL) {
+ ASSERT(osize == 0);
+ dtrace_probes = probes;
+ dtrace_nprobes = 1;
+ } else {
+ dtrace_probe_t **oprobes = dtrace_probes;
+
+ bcopy(oprobes, probes, osize);
+ dtrace_membar_producer();
+ dtrace_probes = probes;
+
+ dtrace_sync();
+
+ /*
+ * All CPUs are now seeing the new probes array; we can
+ * safely free the old array.
+ */
+ kmem_free(oprobes, osize);
+ dtrace_nprobes <<= 1;
+ }
+
+ ASSERT(id - 1 < dtrace_nprobes);
+ }
+
+ ASSERT(dtrace_probes[id - 1] == NULL);
+ dtrace_probes[id - 1] = probe;
+
+ if (provider != dtrace_provider)
+ mutex_exit(&dtrace_lock);
+
+ return (id);
+}
+
+static dtrace_probe_t *
+dtrace_probe_lookup_id(dtrace_id_t id)
+{
+ ASSERT(MUTEX_HELD(&dtrace_lock));
+
+ if (id == 0 || id > dtrace_nprobes)
+ return (NULL);
+
+ return (dtrace_probes[id - 1]);
+}
+
+static int
+dtrace_probe_lookup_match(dtrace_probe_t *probe, void *arg)
+{
+ *((dtrace_id_t *)arg) = probe->dtpr_id;
+
+ return (DTRACE_MATCH_DONE);
+}
+
+/*
+ * Look up a probe based on provider and one or more of module name, function
+ * name and probe name.
+ */
+dtrace_id_t
+dtrace_probe_lookup(dtrace_provider_id_t prid, char *mod,
+ char *func, char *name)
+{
+ dtrace_probekey_t pkey;
+ dtrace_id_t id;
+ int match;
+
+ pkey.dtpk_prov = ((dtrace_provider_t *)prid)->dtpv_name;
+ pkey.dtpk_pmatch = &dtrace_match_string;
+ pkey.dtpk_mod = mod;
+ pkey.dtpk_mmatch = mod ? &dtrace_match_string : &dtrace_match_nul;
+ pkey.dtpk_func = func;
+ pkey.dtpk_fmatch = func ? &dtrace_match_string : &dtrace_match_nul;
+ pkey.dtpk_name = name;
+ pkey.dtpk_nmatch = name ? &dtrace_match_string : &dtrace_match_nul;
+ pkey.dtpk_id = DTRACE_IDNONE;
+
+ mutex_enter(&dtrace_lock);
+ match = dtrace_match(&pkey, DTRACE_PRIV_ALL, 0, 0,
+ dtrace_probe_lookup_match, &id);
+ mutex_exit(&dtrace_lock);
+
+ ASSERT(match == 1 || match == 0);
+ return (match ? id : 0);
+}
+
+/*
+ * Returns the probe argument associated with the specified probe.
+ */
+void *
+dtrace_probe_arg(dtrace_provider_id_t id, dtrace_id_t pid)
+{
+ dtrace_probe_t *probe;
+ void *rval = NULL;
+
+ mutex_enter(&dtrace_lock);
+
+ if ((probe = dtrace_probe_lookup_id(pid)) != NULL &&
+ probe->dtpr_provider == (dtrace_provider_t *)id)
+ rval = probe->dtpr_arg;
+
+ mutex_exit(&dtrace_lock);
+
+ return (rval);
+}
+
+/*
+ * Copy a probe into a probe description.
+ */
+static void
+dtrace_probe_description(const dtrace_probe_t *prp, dtrace_probedesc_t *pdp)
+{
+ bzero(pdp, sizeof (dtrace_probedesc_t));
+ pdp->dtpd_id = prp->dtpr_id;
+
+ (void) strncpy(pdp->dtpd_provider,
+ prp->dtpr_provider->dtpv_name, DTRACE_PROVNAMELEN - 1);
+
+ (void) strncpy(pdp->dtpd_mod, prp->dtpr_mod, DTRACE_MODNAMELEN - 1);
+ (void) strncpy(pdp->dtpd_func, prp->dtpr_func, DTRACE_FUNCNAMELEN - 1);
+ (void) strncpy(pdp->dtpd_name, prp->dtpr_name, DTRACE_NAMELEN - 1);
+}
+
+#if !defined(sun)
+static int
+dtrace_probe_provide_cb(linker_file_t lf, void *arg)
+{
+ dtrace_provider_t *prv = (dtrace_provider_t *) arg;
+
+ prv->dtpv_pops.dtps_provide_module(prv->dtpv_arg, lf);
+
+ return(0);
+}
+#endif
+
+
+/*
+ * Called to indicate that a probe -- or probes -- should be provided by a
+ * specfied provider. If the specified description is NULL, the provider will
+ * be told to provide all of its probes. (This is done whenever a new
+ * consumer comes along, or whenever a retained enabling is to be matched.) If
+ * the specified description is non-NULL, the provider is given the
+ * opportunity to dynamically provide the specified probe, allowing providers
+ * to support the creation of probes on-the-fly. (So-called _autocreated_
+ * probes.) If the provider is NULL, the operations will be applied to all
+ * providers; if the provider is non-NULL the operations will only be applied
+ * to the specified provider. The dtrace_provider_lock must be held, and the
+ * dtrace_lock must _not_ be held -- the provider's dtps_provide() operation
+ * will need to grab the dtrace_lock when it reenters the framework through
+ * dtrace_probe_lookup(), dtrace_probe_create(), etc.
+ */
+static void
+dtrace_probe_provide(dtrace_probedesc_t *desc, dtrace_provider_t *prv)
+{
+#if defined(sun)
+ modctl_t *ctl;
+#endif
+ int all = 0;
+
+ ASSERT(MUTEX_HELD(&dtrace_provider_lock));
+
+ if (prv == NULL) {
+ all = 1;
+ prv = dtrace_provider;
+ }
+
+ do {
+ /*
+ * First, call the blanket provide operation.
+ */
+ prv->dtpv_pops.dtps_provide(prv->dtpv_arg, desc);
+
+ /*
+ * Now call the per-module provide operation. We will grab
+ * mod_lock to prevent the list from being modified. Note
+ * that this also prevents the mod_busy bits from changing.
+ * (mod_busy can only be changed with mod_lock held.)
+ */
+ mutex_enter(&mod_lock);
+
+#if defined(sun)
+ ctl = &modules;
+ do {
+ if (ctl->mod_busy || ctl->mod_mp == NULL)
+ continue;
+
+ prv->dtpv_pops.dtps_provide_module(prv->dtpv_arg, ctl);
+
+ } while ((ctl = ctl->mod_next) != &modules);
+#else
+ (void) linker_file_foreach(dtrace_probe_provide_cb, prv);
+#endif
+
+ mutex_exit(&mod_lock);
+ } while (all && (prv = prv->dtpv_next) != NULL);
+}
+
+#if defined(sun)
+/*
+ * Iterate over each probe, and call the Framework-to-Provider API function
+ * denoted by offs.
+ */
+static void
+dtrace_probe_foreach(uintptr_t offs)
+{
+ dtrace_provider_t *prov;
+ void (*func)(void *, dtrace_id_t, void *);
+ dtrace_probe_t *probe;
+ dtrace_icookie_t cookie;
+ int i;
+
+ /*
+ * We disable interrupts to walk through the probe array. This is
+ * safe -- the dtrace_sync() in dtrace_unregister() assures that we
+ * won't see stale data.
+ */
+ cookie = dtrace_interrupt_disable();
+
+ for (i = 0; i < dtrace_nprobes; i++) {
+ if ((probe = dtrace_probes[i]) == NULL)
+ continue;
+
+ if (probe->dtpr_ecb == NULL) {
+ /*
+ * This probe isn't enabled -- don't call the function.
+ */
+ continue;
+ }
+
+ prov = probe->dtpr_provider;
+ func = *((void(**)(void *, dtrace_id_t, void *))
+ ((uintptr_t)&prov->dtpv_pops + offs));
+
+ func(prov->dtpv_arg, i + 1, probe->dtpr_arg);
+ }
+
+ dtrace_interrupt_enable(cookie);
+}
+#endif
+
+static int
+dtrace_probe_enable(dtrace_probedesc_t *desc, dtrace_enabling_t *enab)
+{
+ dtrace_probekey_t pkey;
+ uint32_t priv;
+ uid_t uid;
+ zoneid_t zoneid;
+
+ ASSERT(MUTEX_HELD(&dtrace_lock));
+ dtrace_ecb_create_cache = NULL;
+
+ if (desc == NULL) {
+ /*
+ * If we're passed a NULL description, we're being asked to
+ * create an ECB with a NULL probe.
+ */
+ (void) dtrace_ecb_create_enable(NULL, enab);
+ return (0);
+ }
+
+ dtrace_probekey(desc, &pkey);
+ dtrace_cred2priv(enab->dten_vstate->dtvs_state->dts_cred.dcr_cred,
+ &priv, &uid, &zoneid);
+
+ return (dtrace_match(&pkey, priv, uid, zoneid, dtrace_ecb_create_enable,
+ enab));
+}
+
+/*
+ * DTrace Helper Provider Functions
+ */
+static void
+dtrace_dofattr2attr(dtrace_attribute_t *attr, const dof_attr_t dofattr)
+{
+ attr->dtat_name = DOF_ATTR_NAME(dofattr);
+ attr->dtat_data = DOF_ATTR_DATA(dofattr);
+ attr->dtat_class = DOF_ATTR_CLASS(dofattr);
+}
+
+static void
+dtrace_dofprov2hprov(dtrace_helper_provdesc_t *hprov,
+ const dof_provider_t *dofprov, char *strtab)
+{
+ hprov->dthpv_provname = strtab + dofprov->dofpv_name;
+ dtrace_dofattr2attr(&hprov->dthpv_pattr.dtpa_provider,
+ dofprov->dofpv_provattr);
+ dtrace_dofattr2attr(&hprov->dthpv_pattr.dtpa_mod,
+ dofprov->dofpv_modattr);
+ dtrace_dofattr2attr(&hprov->dthpv_pattr.dtpa_func,
+ dofprov->dofpv_funcattr);
+ dtrace_dofattr2attr(&hprov->dthpv_pattr.dtpa_name,
+ dofprov->dofpv_nameattr);
+ dtrace_dofattr2attr(&hprov->dthpv_pattr.dtpa_args,
+ dofprov->dofpv_argsattr);
+}
+
+static void
+dtrace_helper_provide_one(dof_helper_t *dhp, dof_sec_t *sec, pid_t pid)
+{
+ uintptr_t daddr = (uintptr_t)dhp->dofhp_dof;
+ dof_hdr_t *dof = (dof_hdr_t *)daddr;
+ dof_sec_t *str_sec, *prb_sec, *arg_sec, *off_sec, *enoff_sec;
+ dof_provider_t *provider;
+ dof_probe_t *probe;
+ uint32_t *off, *enoff;
+ uint8_t *arg;
+ char *strtab;
+ uint_t i, nprobes;
+ dtrace_helper_provdesc_t dhpv;
+ dtrace_helper_probedesc_t dhpb;
+ dtrace_meta_t *meta = dtrace_meta_pid;
+ dtrace_mops_t *mops = &meta->dtm_mops;
+ void *parg;
+
+ provider = (dof_provider_t *)(uintptr_t)(daddr + sec->dofs_offset);
+ str_sec = (dof_sec_t *)(uintptr_t)(daddr + dof->dofh_secoff +
+ provider->dofpv_strtab * dof->dofh_secsize);
+ prb_sec = (dof_sec_t *)(uintptr_t)(daddr + dof->dofh_secoff +
+ provider->dofpv_probes * dof->dofh_secsize);
+ arg_sec = (dof_sec_t *)(uintptr_t)(daddr + dof->dofh_secoff +
+ provider->dofpv_prargs * dof->dofh_secsize);
+ off_sec = (dof_sec_t *)(uintptr_t)(daddr + dof->dofh_secoff +
+ provider->dofpv_proffs * dof->dofh_secsize);
+
+ strtab = (char *)(uintptr_t)(daddr + str_sec->dofs_offset);
+ off = (uint32_t *)(uintptr_t)(daddr + off_sec->dofs_offset);
+ arg = (uint8_t *)(uintptr_t)(daddr + arg_sec->dofs_offset);
+ enoff = NULL;
+
+ /*
+ * See dtrace_helper_provider_validate().
+ */
+ if (dof->dofh_ident[DOF_ID_VERSION] != DOF_VERSION_1 &&
+ provider->dofpv_prenoffs != DOF_SECT_NONE) {
+ enoff_sec = (dof_sec_t *)(uintptr_t)(daddr + dof->dofh_secoff +
+ provider->dofpv_prenoffs * dof->dofh_secsize);
+ enoff = (uint32_t *)(uintptr_t)(daddr + enoff_sec->dofs_offset);
+ }
+
+ nprobes = prb_sec->dofs_size / prb_sec->dofs_entsize;
+
+ /*
+ * Create the provider.
+ */
+ dtrace_dofprov2hprov(&dhpv, provider, strtab);
+
+ if ((parg = mops->dtms_provide_pid(meta->dtm_arg, &dhpv, pid)) == NULL)
+ return;
+
+ meta->dtm_count++;
+
+ /*
+ * Create the probes.
+ */
+ for (i = 0; i < nprobes; i++) {
+ probe = (dof_probe_t *)(uintptr_t)(daddr +
+ prb_sec->dofs_offset + i * prb_sec->dofs_entsize);
+
+ dhpb.dthpb_mod = dhp->dofhp_mod;
+ dhpb.dthpb_func = strtab + probe->dofpr_func;
+ dhpb.dthpb_name = strtab + probe->dofpr_name;
+ dhpb.dthpb_base = probe->dofpr_addr;
+ dhpb.dthpb_offs = off + probe->dofpr_offidx;
+ dhpb.dthpb_noffs = probe->dofpr_noffs;
+ if (enoff != NULL) {
+ dhpb.dthpb_enoffs = enoff + probe->dofpr_enoffidx;
+ dhpb.dthpb_nenoffs = probe->dofpr_nenoffs;
+ } else {
+ dhpb.dthpb_enoffs = NULL;
+ dhpb.dthpb_nenoffs = 0;
+ }
+ dhpb.dthpb_args = arg + probe->dofpr_argidx;
+ dhpb.dthpb_nargc = probe->dofpr_nargc;
+ dhpb.dthpb_xargc = probe->dofpr_xargc;
+ dhpb.dthpb_ntypes = strtab + probe->dofpr_nargv;
+ dhpb.dthpb_xtypes = strtab + probe->dofpr_xargv;
+
+ mops->dtms_create_probe(meta->dtm_arg, parg, &dhpb);
+ }
+}
+
+static void
+dtrace_helper_provide(dof_helper_t *dhp, pid_t pid)
+{
+ uintptr_t daddr = (uintptr_t)dhp->dofhp_dof;
+ dof_hdr_t *dof = (dof_hdr_t *)daddr;
+ int i;
+
+ ASSERT(MUTEX_HELD(&dtrace_meta_lock));
+
+ for (i = 0; i < dof->dofh_secnum; i++) {
+ dof_sec_t *sec = (dof_sec_t *)(uintptr_t)(daddr +
+ dof->dofh_secoff + i * dof->dofh_secsize);
+
+ if (sec->dofs_type != DOF_SECT_PROVIDER)
+ continue;
+
+ dtrace_helper_provide_one(dhp, sec, pid);
+ }
+
+ /*
+ * We may have just created probes, so we must now rematch against
+ * any retained enablings. Note that this call will acquire both
+ * cpu_lock and dtrace_lock; the fact that we are holding
+ * dtrace_meta_lock now is what defines the ordering with respect to
+ * these three locks.
+ */
+ dtrace_enabling_matchall();
+}
+
+#if defined(sun)
+static void
+dtrace_helper_provider_remove_one(dof_helper_t *dhp, dof_sec_t *sec, pid_t pid)
+{
+ uintptr_t daddr = (uintptr_t)dhp->dofhp_dof;
+ dof_hdr_t *dof = (dof_hdr_t *)daddr;
+ dof_sec_t *str_sec;
+ dof_provider_t *provider;
+ char *strtab;
+ dtrace_helper_provdesc_t dhpv;
+ dtrace_meta_t *meta = dtrace_meta_pid;
+ dtrace_mops_t *mops = &meta->dtm_mops;
+
+ provider = (dof_provider_t *)(uintptr_t)(daddr + sec->dofs_offset);
+ str_sec = (dof_sec_t *)(uintptr_t)(daddr + dof->dofh_secoff +
+ provider->dofpv_strtab * dof->dofh_secsize);
+
+ strtab = (char *)(uintptr_t)(daddr + str_sec->dofs_offset);
+
+ /*
+ * Create the provider.
+ */
+ dtrace_dofprov2hprov(&dhpv, provider, strtab);
+
+ mops->dtms_remove_pid(meta->dtm_arg, &dhpv, pid);
+
+ meta->dtm_count--;
+}
+
+static void
+dtrace_helper_provider_remove(dof_helper_t *dhp, pid_t pid)
+{
+ uintptr_t daddr = (uintptr_t)dhp->dofhp_dof;
+ dof_hdr_t *dof = (dof_hdr_t *)daddr;
+ int i;
+
+ ASSERT(MUTEX_HELD(&dtrace_meta_lock));
+
+ for (i = 0; i < dof->dofh_secnum; i++) {
+ dof_sec_t *sec = (dof_sec_t *)(uintptr_t)(daddr +
+ dof->dofh_secoff + i * dof->dofh_secsize);
+
+ if (sec->dofs_type != DOF_SECT_PROVIDER)
+ continue;
+
+ dtrace_helper_provider_remove_one(dhp, sec, pid);
+ }
+}
+#endif
+
+/*
+ * DTrace Meta Provider-to-Framework API Functions
+ *
+ * These functions implement the Meta Provider-to-Framework API, as described
+ * in <sys/dtrace.h>.
+ */
+int
+dtrace_meta_register(const char *name, const dtrace_mops_t *mops, void *arg,
+ dtrace_meta_provider_id_t *idp)
+{
+ dtrace_meta_t *meta;
+ dtrace_helpers_t *help, *next;
+ int i;
+
+ *idp = DTRACE_METAPROVNONE;
+
+ /*
+ * We strictly don't need the name, but we hold onto it for
+ * debuggability. All hail error queues!
+ */
+ if (name == NULL) {
+ cmn_err(CE_WARN, "failed to register meta-provider: "
+ "invalid name");
+ return (EINVAL);
+ }
+
+ if (mops == NULL ||
+ mops->dtms_create_probe == NULL ||
+ mops->dtms_provide_pid == NULL ||
+ mops->dtms_remove_pid == NULL) {
+ cmn_err(CE_WARN, "failed to register meta-register %s: "
+ "invalid ops", name);
+ return (EINVAL);
+ }
+
+ meta = kmem_zalloc(sizeof (dtrace_meta_t), KM_SLEEP);
+ meta->dtm_mops = *mops;
+ meta->dtm_name = kmem_alloc(strlen(name) + 1, KM_SLEEP);
+ (void) strcpy(meta->dtm_name, name);
+ meta->dtm_arg = arg;
+
+ mutex_enter(&dtrace_meta_lock);
+ mutex_enter(&dtrace_lock);
+
+ if (dtrace_meta_pid != NULL) {
+ mutex_exit(&dtrace_lock);
+ mutex_exit(&dtrace_meta_lock);
+ cmn_err(CE_WARN, "failed to register meta-register %s: "
+ "user-land meta-provider exists", name);
+ kmem_free(meta->dtm_name, strlen(meta->dtm_name) + 1);
+ kmem_free(meta, sizeof (dtrace_meta_t));
+ return (EINVAL);
+ }
+
+ dtrace_meta_pid = meta;
+ *idp = (dtrace_meta_provider_id_t)meta;
+
+ /*
+ * If there are providers and probes ready to go, pass them
+ * off to the new meta provider now.
+ */
+
+ help = dtrace_deferred_pid;
+ dtrace_deferred_pid = NULL;
+
+ mutex_exit(&dtrace_lock);
+
+ while (help != NULL) {
+ for (i = 0; i < help->dthps_nprovs; i++) {
+ dtrace_helper_provide(&help->dthps_provs[i]->dthp_prov,
+ help->dthps_pid);
+ }
+
+ next = help->dthps_next;
+ help->dthps_next = NULL;
+ help->dthps_prev = NULL;
+ help->dthps_deferred = 0;
+ help = next;
+ }
+
+ mutex_exit(&dtrace_meta_lock);
+
+ return (0);
+}
+
+int
+dtrace_meta_unregister(dtrace_meta_provider_id_t id)
+{
+ dtrace_meta_t **pp, *old = (dtrace_meta_t *)id;
+
+ mutex_enter(&dtrace_meta_lock);
+ mutex_enter(&dtrace_lock);
+
+ if (old == dtrace_meta_pid) {
+ pp = &dtrace_meta_pid;
+ } else {
+ panic("attempt to unregister non-existent "
+ "dtrace meta-provider %p\n", (void *)old);
+ }
+
+ if (old->dtm_count != 0) {
+ mutex_exit(&dtrace_lock);
+ mutex_exit(&dtrace_meta_lock);
+ return (EBUSY);
+ }
+
+ *pp = NULL;
+
+ mutex_exit(&dtrace_lock);
+ mutex_exit(&dtrace_meta_lock);
+
+ kmem_free(old->dtm_name, strlen(old->dtm_name) + 1);
+ kmem_free(old, sizeof (dtrace_meta_t));
+
+ return (0);
+}
+
+
+/*
+ * DTrace DIF Object Functions
+ */
+static int
+dtrace_difo_err(uint_t pc, const char *format, ...)
+{
+ if (dtrace_err_verbose) {
+ va_list alist;
+
+ (void) uprintf("dtrace DIF object error: [%u]: ", pc);
+ va_start(alist, format);
+ (void) vuprintf(format, alist);
+ va_end(alist);
+ }
+
+#ifdef DTRACE_ERRDEBUG
+ dtrace_errdebug(format);
+#endif
+ return (1);
+}
+
+/*
+ * Validate a DTrace DIF object by checking the IR instructions. The following
+ * rules are currently enforced by dtrace_difo_validate():
+ *
+ * 1. Each instruction must have a valid opcode
+ * 2. Each register, string, variable, or subroutine reference must be valid
+ * 3. No instruction can modify register %r0 (must be zero)
+ * 4. All instruction reserved bits must be set to zero
+ * 5. The last instruction must be a "ret" instruction
+ * 6. All branch targets must reference a valid instruction _after_ the branch
+ */
+static int
+dtrace_difo_validate(dtrace_difo_t *dp, dtrace_vstate_t *vstate, uint_t nregs,
+ cred_t *cr)
+{
+ int err = 0, i;
+ int (*efunc)(uint_t pc, const char *, ...) = dtrace_difo_err;
+ int kcheckload;
+ uint_t pc;
+
+ kcheckload = cr == NULL ||
+ (vstate->dtvs_state->dts_cred.dcr_visible & DTRACE_CRV_KERNEL) == 0;
+
+ dp->dtdo_destructive = 0;
+
+ for (pc = 0; pc < dp->dtdo_len && err == 0; pc++) {
+ dif_instr_t instr = dp->dtdo_buf[pc];
+
+ uint_t r1 = DIF_INSTR_R1(instr);
+ uint_t r2 = DIF_INSTR_R2(instr);
+ uint_t rd = DIF_INSTR_RD(instr);
+ uint_t rs = DIF_INSTR_RS(instr);
+ uint_t label = DIF_INSTR_LABEL(instr);
+ uint_t v = DIF_INSTR_VAR(instr);
+ uint_t subr = DIF_INSTR_SUBR(instr);
+ uint_t type = DIF_INSTR_TYPE(instr);
+ uint_t op = DIF_INSTR_OP(instr);
+
+ switch (op) {
+ case DIF_OP_OR:
+ case DIF_OP_XOR:
+ case DIF_OP_AND:
+ case DIF_OP_SLL:
+ case DIF_OP_SRL:
+ case DIF_OP_SRA:
+ case DIF_OP_SUB:
+ case DIF_OP_ADD:
+ case DIF_OP_MUL:
+ case DIF_OP_SDIV:
+ case DIF_OP_UDIV:
+ case DIF_OP_SREM:
+ case DIF_OP_UREM:
+ case DIF_OP_COPYS:
+ if (r1 >= nregs)
+ err += efunc(pc, "invalid register %u\n", r1);
+ if (r2 >= nregs)
+ err += efunc(pc, "invalid register %u\n", r2);
+ if (rd >= nregs)
+ err += efunc(pc, "invalid register %u\n", rd);
+ if (rd == 0)
+ err += efunc(pc, "cannot write to %r0\n");
+ break;
+ case DIF_OP_NOT:
+ case DIF_OP_MOV:
+ case DIF_OP_ALLOCS:
+ if (r1 >= nregs)
+ err += efunc(pc, "invalid register %u\n", r1);
+ if (r2 != 0)
+ err += efunc(pc, "non-zero reserved bits\n");
+ if (rd >= nregs)
+ err += efunc(pc, "invalid register %u\n", rd);
+ if (rd == 0)
+ err += efunc(pc, "cannot write to %r0\n");
+ break;
+ case DIF_OP_LDSB:
+ case DIF_OP_LDSH:
+ case DIF_OP_LDSW:
+ case DIF_OP_LDUB:
+ case DIF_OP_LDUH:
+ case DIF_OP_LDUW:
+ case DIF_OP_LDX:
+ if (r1 >= nregs)
+ err += efunc(pc, "invalid register %u\n", r1);
+ if (r2 != 0)
+ err += efunc(pc, "non-zero reserved bits\n");
+ if (rd >= nregs)
+ err += efunc(pc, "invalid register %u\n", rd);
+ if (rd == 0)
+ err += efunc(pc, "cannot write to %r0\n");
+ if (kcheckload)
+ dp->dtdo_buf[pc] = DIF_INSTR_LOAD(op +
+ DIF_OP_RLDSB - DIF_OP_LDSB, r1, rd);
+ break;
+ case DIF_OP_RLDSB:
+ case DIF_OP_RLDSH:
+ case DIF_OP_RLDSW:
+ case DIF_OP_RLDUB:
+ case DIF_OP_RLDUH:
+ case DIF_OP_RLDUW:
+ case DIF_OP_RLDX:
+ if (r1 >= nregs)
+ err += efunc(pc, "invalid register %u\n", r1);
+ if (r2 != 0)
+ err += efunc(pc, "non-zero reserved bits\n");
+ if (rd >= nregs)
+ err += efunc(pc, "invalid register %u\n", rd);
+ if (rd == 0)
+ err += efunc(pc, "cannot write to %r0\n");
+ break;
+ case DIF_OP_ULDSB:
+ case DIF_OP_ULDSH:
+ case DIF_OP_ULDSW:
+ case DIF_OP_ULDUB:
+ case DIF_OP_ULDUH:
+ case DIF_OP_ULDUW:
+ case DIF_OP_ULDX:
+ if (r1 >= nregs)
+ err += efunc(pc, "invalid register %u\n", r1);
+ if (r2 != 0)
+ err += efunc(pc, "non-zero reserved bits\n");
+ if (rd >= nregs)
+ err += efunc(pc, "invalid register %u\n", rd);
+ if (rd == 0)
+ err += efunc(pc, "cannot write to %r0\n");
+ break;
+ case DIF_OP_STB:
+ case DIF_OP_STH:
+ case DIF_OP_STW:
+ case DIF_OP_STX:
+ if (r1 >= nregs)
+ err += efunc(pc, "invalid register %u\n", r1);
+ if (r2 != 0)
+ err += efunc(pc, "non-zero reserved bits\n");
+ if (rd >= nregs)
+ err += efunc(pc, "invalid register %u\n", rd);
+ if (rd == 0)
+ err += efunc(pc, "cannot write to 0 address\n");
+ break;
+ case DIF_OP_CMP:
+ case DIF_OP_SCMP:
+ if (r1 >= nregs)
+ err += efunc(pc, "invalid register %u\n", r1);
+ if (r2 >= nregs)
+ err += efunc(pc, "invalid register %u\n", r2);
+ if (rd != 0)
+ err += efunc(pc, "non-zero reserved bits\n");
+ break;
+ case DIF_OP_TST:
+ if (r1 >= nregs)
+ err += efunc(pc, "invalid register %u\n", r1);
+ if (r2 != 0 || rd != 0)
+ err += efunc(pc, "non-zero reserved bits\n");
+ break;
+ case DIF_OP_BA:
+ case DIF_OP_BE:
+ case DIF_OP_BNE:
+ case DIF_OP_BG:
+ case DIF_OP_BGU:
+ case DIF_OP_BGE:
+ case DIF_OP_BGEU:
+ case DIF_OP_BL:
+ case DIF_OP_BLU:
+ case DIF_OP_BLE:
+ case DIF_OP_BLEU:
+ if (label >= dp->dtdo_len) {
+ err += efunc(pc, "invalid branch target %u\n",
+ label);
+ }
+ if (label <= pc) {
+ err += efunc(pc, "backward branch to %u\n",
+ label);
+ }
+ break;
+ case DIF_OP_RET:
+ if (r1 != 0 || r2 != 0)
+ err += efunc(pc, "non-zero reserved bits\n");
+ if (rd >= nregs)
+ err += efunc(pc, "invalid register %u\n", rd);
+ break;
+ case DIF_OP_NOP:
+ case DIF_OP_POPTS:
+ case DIF_OP_FLUSHTS:
+ if (r1 != 0 || r2 != 0 || rd != 0)
+ err += efunc(pc, "non-zero reserved bits\n");
+ break;
+ case DIF_OP_SETX:
+ if (DIF_INSTR_INTEGER(instr) >= dp->dtdo_intlen) {
+ err += efunc(pc, "invalid integer ref %u\n",
+ DIF_INSTR_INTEGER(instr));
+ }
+ if (rd >= nregs)
+ err += efunc(pc, "invalid register %u\n", rd);
+ if (rd == 0)
+ err += efunc(pc, "cannot write to %r0\n");
+ break;
+ case DIF_OP_SETS:
+ if (DIF_INSTR_STRING(instr) >= dp->dtdo_strlen) {
+ err += efunc(pc, "invalid string ref %u\n",
+ DIF_INSTR_STRING(instr));
+ }
+ if (rd >= nregs)
+ err += efunc(pc, "invalid register %u\n", rd);
+ if (rd == 0)
+ err += efunc(pc, "cannot write to %r0\n");
+ break;
+ case DIF_OP_LDGA:
+ case DIF_OP_LDTA:
+ if (r1 > DIF_VAR_ARRAY_MAX)
+ err += efunc(pc, "invalid array %u\n", r1);
+ if (r2 >= nregs)
+ err += efunc(pc, "invalid register %u\n", r2);
+ if (rd >= nregs)
+ err += efunc(pc, "invalid register %u\n", rd);
+ if (rd == 0)
+ err += efunc(pc, "cannot write to %r0\n");
+ break;
+ case DIF_OP_LDGS:
+ case DIF_OP_LDTS:
+ case DIF_OP_LDLS:
+ case DIF_OP_LDGAA:
+ case DIF_OP_LDTAA:
+ if (v < DIF_VAR_OTHER_MIN || v > DIF_VAR_OTHER_MAX)
+ err += efunc(pc, "invalid variable %u\n", v);
+ if (rd >= nregs)
+ err += efunc(pc, "invalid register %u\n", rd);
+ if (rd == 0)
+ err += efunc(pc, "cannot write to %r0\n");
+ break;
+ case DIF_OP_STGS:
+ case DIF_OP_STTS:
+ case DIF_OP_STLS:
+ case DIF_OP_STGAA:
+ case DIF_OP_STTAA:
+ if (v < DIF_VAR_OTHER_UBASE || v > DIF_VAR_OTHER_MAX)
+ err += efunc(pc, "invalid variable %u\n", v);
+ if (rs >= nregs)
+ err += efunc(pc, "invalid register %u\n", rd);
+ break;
+ case DIF_OP_CALL:
+ if (subr > DIF_SUBR_MAX)
+ err += efunc(pc, "invalid subr %u\n", subr);
+ if (rd >= nregs)
+ err += efunc(pc, "invalid register %u\n", rd);
+ if (rd == 0)
+ err += efunc(pc, "cannot write to %r0\n");
+
+ if (subr == DIF_SUBR_COPYOUT ||
+ subr == DIF_SUBR_COPYOUTSTR) {
+ dp->dtdo_destructive = 1;
+ }
+ break;
+ case DIF_OP_PUSHTR:
+ if (type != DIF_TYPE_STRING && type != DIF_TYPE_CTF)
+ err += efunc(pc, "invalid ref type %u\n", type);
+ if (r2 >= nregs)
+ err += efunc(pc, "invalid register %u\n", r2);
+ if (rs >= nregs)
+ err += efunc(pc, "invalid register %u\n", rs);
+ break;
+ case DIF_OP_PUSHTV:
+ if (type != DIF_TYPE_CTF)
+ err += efunc(pc, "invalid val type %u\n", type);
+ if (r2 >= nregs)
+ err += efunc(pc, "invalid register %u\n", r2);
+ if (rs >= nregs)
+ err += efunc(pc, "invalid register %u\n", rs);
+ break;
+ default:
+ err += efunc(pc, "invalid opcode %u\n",
+ DIF_INSTR_OP(instr));
+ }
+ }
+
+ if (dp->dtdo_len != 0 &&
+ DIF_INSTR_OP(dp->dtdo_buf[dp->dtdo_len - 1]) != DIF_OP_RET) {
+ err += efunc(dp->dtdo_len - 1,
+ "expected 'ret' as last DIF instruction\n");
+ }
+
+ if (!(dp->dtdo_rtype.dtdt_flags & DIF_TF_BYREF)) {
+ /*
+ * If we're not returning by reference, the size must be either
+ * 0 or the size of one of the base types.
+ */
+ switch (dp->dtdo_rtype.dtdt_size) {
+ case 0:
+ case sizeof (uint8_t):
+ case sizeof (uint16_t):
+ case sizeof (uint32_t):
+ case sizeof (uint64_t):
+ break;
+
+ default:
+ err += efunc(dp->dtdo_len - 1, "bad return size");
+ }
+ }
+
+ for (i = 0; i < dp->dtdo_varlen && err == 0; i++) {
+ dtrace_difv_t *v = &dp->dtdo_vartab[i], *existing = NULL;
+ dtrace_diftype_t *vt, *et;
+ uint_t id, ndx;
+
+ if (v->dtdv_scope != DIFV_SCOPE_GLOBAL &&
+ v->dtdv_scope != DIFV_SCOPE_THREAD &&
+ v->dtdv_scope != DIFV_SCOPE_LOCAL) {
+ err += efunc(i, "unrecognized variable scope %d\n",
+ v->dtdv_scope);
+ break;
+ }
+
+ if (v->dtdv_kind != DIFV_KIND_ARRAY &&
+ v->dtdv_kind != DIFV_KIND_SCALAR) {
+ err += efunc(i, "unrecognized variable type %d\n",
+ v->dtdv_kind);
+ break;
+ }
+
+ if ((id = v->dtdv_id) > DIF_VARIABLE_MAX) {
+ err += efunc(i, "%d exceeds variable id limit\n", id);
+ break;
+ }
+
+ if (id < DIF_VAR_OTHER_UBASE)
+ continue;
+
+ /*
+ * For user-defined variables, we need to check that this
+ * definition is identical to any previous definition that we
+ * encountered.
+ */
+ ndx = id - DIF_VAR_OTHER_UBASE;
+
+ switch (v->dtdv_scope) {
+ case DIFV_SCOPE_GLOBAL:
+ if (ndx < vstate->dtvs_nglobals) {
+ dtrace_statvar_t *svar;
+
+ if ((svar = vstate->dtvs_globals[ndx]) != NULL)
+ existing = &svar->dtsv_var;
+ }
+
+ break;
+
+ case DIFV_SCOPE_THREAD:
+ if (ndx < vstate->dtvs_ntlocals)
+ existing = &vstate->dtvs_tlocals[ndx];
+ break;
+
+ case DIFV_SCOPE_LOCAL:
+ if (ndx < vstate->dtvs_nlocals) {
+ dtrace_statvar_t *svar;
+
+ if ((svar = vstate->dtvs_locals[ndx]) != NULL)
+ existing = &svar->dtsv_var;
+ }
+
+ break;
+ }
+
+ vt = &v->dtdv_type;
+
+ if (vt->dtdt_flags & DIF_TF_BYREF) {
+ if (vt->dtdt_size == 0) {
+ err += efunc(i, "zero-sized variable\n");
+ break;
+ }
+
+ if (v->dtdv_scope == DIFV_SCOPE_GLOBAL &&
+ vt->dtdt_size > dtrace_global_maxsize) {
+ err += efunc(i, "oversized by-ref global\n");
+ break;
+ }
+ }
+
+ if (existing == NULL || existing->dtdv_id == 0)
+ continue;
+
+ ASSERT(existing->dtdv_id == v->dtdv_id);
+ ASSERT(existing->dtdv_scope == v->dtdv_scope);
+
+ if (existing->dtdv_kind != v->dtdv_kind)
+ err += efunc(i, "%d changed variable kind\n", id);
+
+ et = &existing->dtdv_type;
+
+ if (vt->dtdt_flags != et->dtdt_flags) {
+ err += efunc(i, "%d changed variable type flags\n", id);
+ break;
+ }
+
+ if (vt->dtdt_size != 0 && vt->dtdt_size != et->dtdt_size) {
+ err += efunc(i, "%d changed variable type size\n", id);
+ break;
+ }
+ }
+
+ return (err);
+}
+
+#if defined(sun)
+/*
+ * Validate a DTrace DIF object that it is to be used as a helper. Helpers
+ * are much more constrained than normal DIFOs. Specifically, they may
+ * not:
+ *
+ * 1. Make calls to subroutines other than copyin(), copyinstr() or
+ * miscellaneous string routines
+ * 2. Access DTrace variables other than the args[] array, and the
+ * curthread, pid, ppid, tid, execname, zonename, uid and gid variables.
+ * 3. Have thread-local variables.
+ * 4. Have dynamic variables.
+ */
+static int
+dtrace_difo_validate_helper(dtrace_difo_t *dp)
+{
+ int (*efunc)(uint_t pc, const char *, ...) = dtrace_difo_err;
+ int err = 0;
+ uint_t pc;
+
+ for (pc = 0; pc < dp->dtdo_len; pc++) {
+ dif_instr_t instr = dp->dtdo_buf[pc];
+
+ uint_t v = DIF_INSTR_VAR(instr);
+ uint_t subr = DIF_INSTR_SUBR(instr);
+ uint_t op = DIF_INSTR_OP(instr);
+
+ switch (op) {
+ case DIF_OP_OR:
+ case DIF_OP_XOR:
+ case DIF_OP_AND:
+ case DIF_OP_SLL:
+ case DIF_OP_SRL:
+ case DIF_OP_SRA:
+ case DIF_OP_SUB:
+ case DIF_OP_ADD:
+ case DIF_OP_MUL:
+ case DIF_OP_SDIV:
+ case DIF_OP_UDIV:
+ case DIF_OP_SREM:
+ case DIF_OP_UREM:
+ case DIF_OP_COPYS:
+ case DIF_OP_NOT:
+ case DIF_OP_MOV:
+ case DIF_OP_RLDSB:
+ case DIF_OP_RLDSH:
+ case DIF_OP_RLDSW:
+ case DIF_OP_RLDUB:
+ case DIF_OP_RLDUH:
+ case DIF_OP_RLDUW:
+ case DIF_OP_RLDX:
+ case DIF_OP_ULDSB:
+ case DIF_OP_ULDSH:
+ case DIF_OP_ULDSW:
+ case DIF_OP_ULDUB:
+ case DIF_OP_ULDUH:
+ case DIF_OP_ULDUW:
+ case DIF_OP_ULDX:
+ case DIF_OP_STB:
+ case DIF_OP_STH:
+ case DIF_OP_STW:
+ case DIF_OP_STX:
+ case DIF_OP_ALLOCS:
+ case DIF_OP_CMP:
+ case DIF_OP_SCMP:
+ case DIF_OP_TST:
+ case DIF_OP_BA:
+ case DIF_OP_BE:
+ case DIF_OP_BNE:
+ case DIF_OP_BG:
+ case DIF_OP_BGU:
+ case DIF_OP_BGE:
+ case DIF_OP_BGEU:
+ case DIF_OP_BL:
+ case DIF_OP_BLU:
+ case DIF_OP_BLE:
+ case DIF_OP_BLEU:
+ case DIF_OP_RET:
+ case DIF_OP_NOP:
+ case DIF_OP_POPTS:
+ case DIF_OP_FLUSHTS:
+ case DIF_OP_SETX:
+ case DIF_OP_SETS:
+ case DIF_OP_LDGA:
+ case DIF_OP_LDLS:
+ case DIF_OP_STGS:
+ case DIF_OP_STLS:
+ case DIF_OP_PUSHTR:
+ case DIF_OP_PUSHTV:
+ break;
+
+ case DIF_OP_LDGS:
+ if (v >= DIF_VAR_OTHER_UBASE)
+ break;
+
+ if (v >= DIF_VAR_ARG0 && v <= DIF_VAR_ARG9)
+ break;
+
+ if (v == DIF_VAR_CURTHREAD || v == DIF_VAR_PID ||
+ v == DIF_VAR_PPID || v == DIF_VAR_TID ||
+ v == DIF_VAR_EXECARGS ||
+ v == DIF_VAR_EXECNAME || v == DIF_VAR_ZONENAME ||
+ v == DIF_VAR_UID || v == DIF_VAR_GID)
+ break;
+
+ err += efunc(pc, "illegal variable %u\n", v);
+ break;
+
+ case DIF_OP_LDTA:
+ case DIF_OP_LDTS:
+ case DIF_OP_LDGAA:
+ case DIF_OP_LDTAA:
+ err += efunc(pc, "illegal dynamic variable load\n");
+ break;
+
+ case DIF_OP_STTS:
+ case DIF_OP_STGAA:
+ case DIF_OP_STTAA:
+ err += efunc(pc, "illegal dynamic variable store\n");
+ break;
+
+ case DIF_OP_CALL:
+ if (subr == DIF_SUBR_ALLOCA ||
+ subr == DIF_SUBR_BCOPY ||
+ subr == DIF_SUBR_COPYIN ||
+ subr == DIF_SUBR_COPYINTO ||
+ subr == DIF_SUBR_COPYINSTR ||
+ subr == DIF_SUBR_INDEX ||
+ subr == DIF_SUBR_INET_NTOA ||
+ subr == DIF_SUBR_INET_NTOA6 ||
+ subr == DIF_SUBR_INET_NTOP ||
+ subr == DIF_SUBR_LLTOSTR ||
+ subr == DIF_SUBR_RINDEX ||
+ subr == DIF_SUBR_STRCHR ||
+ subr == DIF_SUBR_STRJOIN ||
+ subr == DIF_SUBR_STRRCHR ||
+ subr == DIF_SUBR_STRSTR ||
+ subr == DIF_SUBR_HTONS ||
+ subr == DIF_SUBR_HTONL ||
+ subr == DIF_SUBR_HTONLL ||
+ subr == DIF_SUBR_NTOHS ||
+ subr == DIF_SUBR_NTOHL ||
+ subr == DIF_SUBR_NTOHLL ||
+ subr == DIF_SUBR_MEMREF ||
+ subr == DIF_SUBR_TYPEREF)
+ break;
+
+ err += efunc(pc, "invalid subr %u\n", subr);
+ break;
+
+ default:
+ err += efunc(pc, "invalid opcode %u\n",
+ DIF_INSTR_OP(instr));
+ }
+ }
+
+ return (err);
+}
+#endif
+
+/*
+ * Returns 1 if the expression in the DIF object can be cached on a per-thread
+ * basis; 0 if not.
+ */
+static int
+dtrace_difo_cacheable(dtrace_difo_t *dp)
+{
+ int i;
+
+ if (dp == NULL)
+ return (0);
+
+ for (i = 0; i < dp->dtdo_varlen; i++) {
+ dtrace_difv_t *v = &dp->dtdo_vartab[i];
+
+ if (v->dtdv_scope != DIFV_SCOPE_GLOBAL)
+ continue;
+
+ switch (v->dtdv_id) {
+ case DIF_VAR_CURTHREAD:
+ case DIF_VAR_PID:
+ case DIF_VAR_TID:
+ case DIF_VAR_EXECARGS:
+ case DIF_VAR_EXECNAME:
+ case DIF_VAR_ZONENAME:
+ break;
+
+ default:
+ return (0);
+ }
+ }
+
+ /*
+ * This DIF object may be cacheable. Now we need to look for any
+ * array loading instructions, any memory loading instructions, or
+ * any stores to thread-local variables.
+ */
+ for (i = 0; i < dp->dtdo_len; i++) {
+ uint_t op = DIF_INSTR_OP(dp->dtdo_buf[i]);
+
+ if ((op >= DIF_OP_LDSB && op <= DIF_OP_LDX) ||
+ (op >= DIF_OP_ULDSB && op <= DIF_OP_ULDX) ||
+ (op >= DIF_OP_RLDSB && op <= DIF_OP_RLDX) ||
+ op == DIF_OP_LDGA || op == DIF_OP_STTS)
+ return (0);
+ }
+
+ return (1);
+}
+
+static void
+dtrace_difo_hold(dtrace_difo_t *dp)
+{
+ int i;
+
+ ASSERT(MUTEX_HELD(&dtrace_lock));
+
+ dp->dtdo_refcnt++;
+ ASSERT(dp->dtdo_refcnt != 0);
+
+ /*
+ * We need to check this DIF object for references to the variable
+ * DIF_VAR_VTIMESTAMP.
+ */
+ for (i = 0; i < dp->dtdo_varlen; i++) {
+ dtrace_difv_t *v = &dp->dtdo_vartab[i];
+
+ if (v->dtdv_id != DIF_VAR_VTIMESTAMP)
+ continue;
+
+ if (dtrace_vtime_references++ == 0)
+ dtrace_vtime_enable();
+ }
+}
+
+/*
+ * This routine calculates the dynamic variable chunksize for a given DIF
+ * object. The calculation is not fool-proof, and can probably be tricked by
+ * malicious DIF -- but it works for all compiler-generated DIF. Because this
+ * calculation is likely imperfect, dtrace_dynvar() is able to gracefully fail
+ * if a dynamic variable size exceeds the chunksize.
+ */
+static void
+dtrace_difo_chunksize(dtrace_difo_t *dp, dtrace_vstate_t *vstate)
+{
+ uint64_t sval = 0;
+ dtrace_key_t tupregs[DIF_DTR_NREGS + 2]; /* +2 for thread and id */
+ const dif_instr_t *text = dp->dtdo_buf;
+ uint_t pc, srd = 0;
+ uint_t ttop = 0;
+ size_t size, ksize;
+ uint_t id, i;
+
+ for (pc = 0; pc < dp->dtdo_len; pc++) {
+ dif_instr_t instr = text[pc];
+ uint_t op = DIF_INSTR_OP(instr);
+ uint_t rd = DIF_INSTR_RD(instr);
+ uint_t r1 = DIF_INSTR_R1(instr);
+ uint_t nkeys = 0;
+ uchar_t scope = 0;
+
+ dtrace_key_t *key = tupregs;
+
+ switch (op) {
+ case DIF_OP_SETX:
+ sval = dp->dtdo_inttab[DIF_INSTR_INTEGER(instr)];
+ srd = rd;
+ continue;
+
+ case DIF_OP_STTS:
+ key = &tupregs[DIF_DTR_NREGS];
+ key[0].dttk_size = 0;
+ key[1].dttk_size = 0;
+ nkeys = 2;
+ scope = DIFV_SCOPE_THREAD;
+ break;
+
+ case DIF_OP_STGAA:
+ case DIF_OP_STTAA:
+ nkeys = ttop;
+
+ if (DIF_INSTR_OP(instr) == DIF_OP_STTAA)
+ key[nkeys++].dttk_size = 0;
+
+ key[nkeys++].dttk_size = 0;
+
+ if (op == DIF_OP_STTAA) {
+ scope = DIFV_SCOPE_THREAD;
+ } else {
+ scope = DIFV_SCOPE_GLOBAL;
+ }
+
+ break;
+
+ case DIF_OP_PUSHTR:
+ if (ttop == DIF_DTR_NREGS)
+ return;
+
+ if ((srd == 0 || sval == 0) && r1 == DIF_TYPE_STRING) {
+ /*
+ * If the register for the size of the "pushtr"
+ * is %r0 (or the value is 0) and the type is
+ * a string, we'll use the system-wide default
+ * string size.
+ */
+ tupregs[ttop++].dttk_size =
+ dtrace_strsize_default;
+ } else {
+ if (srd == 0)
+ return;
+
+ tupregs[ttop++].dttk_size = sval;
+ }
+
+ break;
+
+ case DIF_OP_PUSHTV:
+ if (ttop == DIF_DTR_NREGS)
+ return;
+
+ tupregs[ttop++].dttk_size = 0;
+ break;
+
+ case DIF_OP_FLUSHTS:
+ ttop = 0;
+ break;
+
+ case DIF_OP_POPTS:
+ if (ttop != 0)
+ ttop--;
+ break;
+ }
+
+ sval = 0;
+ srd = 0;
+
+ if (nkeys == 0)
+ continue;
+
+ /*
+ * We have a dynamic variable allocation; calculate its size.
+ */
+ for (ksize = 0, i = 0; i < nkeys; i++)
+ ksize += P2ROUNDUP(key[i].dttk_size, sizeof (uint64_t));
+
+ size = sizeof (dtrace_dynvar_t);
+ size += sizeof (dtrace_key_t) * (nkeys - 1);
+ size += ksize;
+
+ /*
+ * Now we need to determine the size of the stored data.
+ */
+ id = DIF_INSTR_VAR(instr);
+
+ for (i = 0; i < dp->dtdo_varlen; i++) {
+ dtrace_difv_t *v = &dp->dtdo_vartab[i];
+
+ if (v->dtdv_id == id && v->dtdv_scope == scope) {
+ size += v->dtdv_type.dtdt_size;
+ break;
+ }
+ }
+
+ if (i == dp->dtdo_varlen)
+ return;
+
+ /*
+ * We have the size. If this is larger than the chunk size
+ * for our dynamic variable state, reset the chunk size.
+ */
+ size = P2ROUNDUP(size, sizeof (uint64_t));
+
+ if (size > vstate->dtvs_dynvars.dtds_chunksize)
+ vstate->dtvs_dynvars.dtds_chunksize = size;
+ }
+}
+
+static void
+dtrace_difo_init(dtrace_difo_t *dp, dtrace_vstate_t *vstate)
+{
+ int i, oldsvars, osz, nsz, otlocals, ntlocals;
+ uint_t id;
+
+ ASSERT(MUTEX_HELD(&dtrace_lock));
+ ASSERT(dp->dtdo_buf != NULL && dp->dtdo_len != 0);
+
+ for (i = 0; i < dp->dtdo_varlen; i++) {
+ dtrace_difv_t *v = &dp->dtdo_vartab[i];
+ dtrace_statvar_t *svar, ***svarp = NULL;
+ size_t dsize = 0;
+ uint8_t scope = v->dtdv_scope;
+ int *np = NULL;
+
+ if ((id = v->dtdv_id) < DIF_VAR_OTHER_UBASE)
+ continue;
+
+ id -= DIF_VAR_OTHER_UBASE;
+
+ switch (scope) {
+ case DIFV_SCOPE_THREAD:
+ while (id >= (otlocals = vstate->dtvs_ntlocals)) {
+ dtrace_difv_t *tlocals;
+
+ if ((ntlocals = (otlocals << 1)) == 0)
+ ntlocals = 1;
+
+ osz = otlocals * sizeof (dtrace_difv_t);
+ nsz = ntlocals * sizeof (dtrace_difv_t);
+
+ tlocals = kmem_zalloc(nsz, KM_SLEEP);
+
+ if (osz != 0) {
+ bcopy(vstate->dtvs_tlocals,
+ tlocals, osz);
+ kmem_free(vstate->dtvs_tlocals, osz);
+ }
+
+ vstate->dtvs_tlocals = tlocals;
+ vstate->dtvs_ntlocals = ntlocals;
+ }
+
+ vstate->dtvs_tlocals[id] = *v;
+ continue;
+
+ case DIFV_SCOPE_LOCAL:
+ np = &vstate->dtvs_nlocals;
+ svarp = &vstate->dtvs_locals;
+
+ if (v->dtdv_type.dtdt_flags & DIF_TF_BYREF)
+ dsize = NCPU * (v->dtdv_type.dtdt_size +
+ sizeof (uint64_t));
+ else
+ dsize = NCPU * sizeof (uint64_t);
+
+ break;
+
+ case DIFV_SCOPE_GLOBAL:
+ np = &vstate->dtvs_nglobals;
+ svarp = &vstate->dtvs_globals;
+
+ if (v->dtdv_type.dtdt_flags & DIF_TF_BYREF)
+ dsize = v->dtdv_type.dtdt_size +
+ sizeof (uint64_t);
+
+ break;
+
+ default:
+ ASSERT(0);
+ }
+
+ while (id >= (oldsvars = *np)) {
+ dtrace_statvar_t **statics;
+ int newsvars, oldsize, newsize;
+
+ if ((newsvars = (oldsvars << 1)) == 0)
+ newsvars = 1;
+
+ oldsize = oldsvars * sizeof (dtrace_statvar_t *);
+ newsize = newsvars * sizeof (dtrace_statvar_t *);
+
+ statics = kmem_zalloc(newsize, KM_SLEEP);
+
+ if (oldsize != 0) {
+ bcopy(*svarp, statics, oldsize);
+ kmem_free(*svarp, oldsize);
+ }
+
+ *svarp = statics;
+ *np = newsvars;
+ }
+
+ if ((svar = (*svarp)[id]) == NULL) {
+ svar = kmem_zalloc(sizeof (dtrace_statvar_t), KM_SLEEP);
+ svar->dtsv_var = *v;
+
+ if ((svar->dtsv_size = dsize) != 0) {
+ svar->dtsv_data = (uint64_t)(uintptr_t)
+ kmem_zalloc(dsize, KM_SLEEP);
+ }
+
+ (*svarp)[id] = svar;
+ }
+
+ svar->dtsv_refcnt++;
+ }
+
+ dtrace_difo_chunksize(dp, vstate);
+ dtrace_difo_hold(dp);
+}
+
+#if defined(sun)
+static dtrace_difo_t *
+dtrace_difo_duplicate(dtrace_difo_t *dp, dtrace_vstate_t *vstate)
+{
+ dtrace_difo_t *new;
+ size_t sz;
+
+ ASSERT(dp->dtdo_buf != NULL);
+ ASSERT(dp->dtdo_refcnt != 0);
+
+ new = kmem_zalloc(sizeof (dtrace_difo_t), KM_SLEEP);
+
+ ASSERT(dp->dtdo_buf != NULL);
+ sz = dp->dtdo_len * sizeof (dif_instr_t);
+ new->dtdo_buf = kmem_alloc(sz, KM_SLEEP);
+ bcopy(dp->dtdo_buf, new->dtdo_buf, sz);
+ new->dtdo_len = dp->dtdo_len;
+
+ if (dp->dtdo_strtab != NULL) {
+ ASSERT(dp->dtdo_strlen != 0);
+ new->dtdo_strtab = kmem_alloc(dp->dtdo_strlen, KM_SLEEP);
+ bcopy(dp->dtdo_strtab, new->dtdo_strtab, dp->dtdo_strlen);
+ new->dtdo_strlen = dp->dtdo_strlen;
+ }
+
+ if (dp->dtdo_inttab != NULL) {
+ ASSERT(dp->dtdo_intlen != 0);
+ sz = dp->dtdo_intlen * sizeof (uint64_t);
+ new->dtdo_inttab = kmem_alloc(sz, KM_SLEEP);
+ bcopy(dp->dtdo_inttab, new->dtdo_inttab, sz);
+ new->dtdo_intlen = dp->dtdo_intlen;
+ }
+
+ if (dp->dtdo_vartab != NULL) {
+ ASSERT(dp->dtdo_varlen != 0);
+ sz = dp->dtdo_varlen * sizeof (dtrace_difv_t);
+ new->dtdo_vartab = kmem_alloc(sz, KM_SLEEP);
+ bcopy(dp->dtdo_vartab, new->dtdo_vartab, sz);
+ new->dtdo_varlen = dp->dtdo_varlen;
+ }
+
+ dtrace_difo_init(new, vstate);
+ return (new);
+}
+#endif
+
+static void
+dtrace_difo_destroy(dtrace_difo_t *dp, dtrace_vstate_t *vstate)
+{
+ int i;
+
+ ASSERT(dp->dtdo_refcnt == 0);
+
+ for (i = 0; i < dp->dtdo_varlen; i++) {
+ dtrace_difv_t *v = &dp->dtdo_vartab[i];
+ dtrace_statvar_t *svar, **svarp = NULL;
+ uint_t id;
+ uint8_t scope = v->dtdv_scope;
+ int *np = NULL;
+
+ switch (scope) {
+ case DIFV_SCOPE_THREAD:
+ continue;
+
+ case DIFV_SCOPE_LOCAL:
+ np = &vstate->dtvs_nlocals;
+ svarp = vstate->dtvs_locals;
+ break;
+
+ case DIFV_SCOPE_GLOBAL:
+ np = &vstate->dtvs_nglobals;
+ svarp = vstate->dtvs_globals;
+ break;
+
+ default:
+ ASSERT(0);
+ }
+
+ if ((id = v->dtdv_id) < DIF_VAR_OTHER_UBASE)
+ continue;
+
+ id -= DIF_VAR_OTHER_UBASE;
+ ASSERT(id < *np);
+
+ svar = svarp[id];
+ ASSERT(svar != NULL);
+ ASSERT(svar->dtsv_refcnt > 0);
+
+ if (--svar->dtsv_refcnt > 0)
+ continue;
+
+ if (svar->dtsv_size != 0) {
+ ASSERT(svar->dtsv_data != 0);
+ kmem_free((void *)(uintptr_t)svar->dtsv_data,
+ svar->dtsv_size);
+ }
+
+ kmem_free(svar, sizeof (dtrace_statvar_t));
+ svarp[id] = NULL;
+ }
+
+ if (dp->dtdo_buf != NULL)
+ kmem_free(dp->dtdo_buf, dp->dtdo_len * sizeof (dif_instr_t));
+ if (dp->dtdo_inttab != NULL)
+ kmem_free(dp->dtdo_inttab, dp->dtdo_intlen * sizeof (uint64_t));
+ if (dp->dtdo_strtab != NULL)
+ kmem_free(dp->dtdo_strtab, dp->dtdo_strlen);
+ if (dp->dtdo_vartab != NULL)
+ kmem_free(dp->dtdo_vartab, dp->dtdo_varlen * sizeof (dtrace_difv_t));
+
+ kmem_free(dp, sizeof (dtrace_difo_t));
+}
+
+static void
+dtrace_difo_release(dtrace_difo_t *dp, dtrace_vstate_t *vstate)
+{
+ int i;
+
+ ASSERT(MUTEX_HELD(&dtrace_lock));
+ ASSERT(dp->dtdo_refcnt != 0);
+
+ for (i = 0; i < dp->dtdo_varlen; i++) {
+ dtrace_difv_t *v = &dp->dtdo_vartab[i];
+
+ if (v->dtdv_id != DIF_VAR_VTIMESTAMP)
+ continue;
+
+ ASSERT(dtrace_vtime_references > 0);
+ if (--dtrace_vtime_references == 0)
+ dtrace_vtime_disable();
+ }
+
+ if (--dp->dtdo_refcnt == 0)
+ dtrace_difo_destroy(dp, vstate);
+}
+
+/*
+ * DTrace Format Functions
+ */
+static uint16_t
+dtrace_format_add(dtrace_state_t *state, char *str)
+{
+ char *fmt, **new;
+ uint16_t ndx, len = strlen(str) + 1;
+
+ fmt = kmem_zalloc(len, KM_SLEEP);
+ bcopy(str, fmt, len);
+
+ for (ndx = 0; ndx < state->dts_nformats; ndx++) {
+ if (state->dts_formats[ndx] == NULL) {
+ state->dts_formats[ndx] = fmt;
+ return (ndx + 1);
+ }
+ }
+
+ if (state->dts_nformats == USHRT_MAX) {
+ /*
+ * This is only likely if a denial-of-service attack is being
+ * attempted. As such, it's okay to fail silently here.
+ */
+ kmem_free(fmt, len);
+ return (0);
+ }
+
+ /*
+ * For simplicity, we always resize the formats array to be exactly the
+ * number of formats.
+ */
+ ndx = state->dts_nformats++;
+ new = kmem_alloc((ndx + 1) * sizeof (char *), KM_SLEEP);
+
+ if (state->dts_formats != NULL) {
+ ASSERT(ndx != 0);
+ bcopy(state->dts_formats, new, ndx * sizeof (char *));
+ kmem_free(state->dts_formats, ndx * sizeof (char *));
+ }
+
+ state->dts_formats = new;
+ state->dts_formats[ndx] = fmt;
+
+ return (ndx + 1);
+}
+
+static void
+dtrace_format_remove(dtrace_state_t *state, uint16_t format)
+{
+ char *fmt;
+
+ ASSERT(state->dts_formats != NULL);
+ ASSERT(format <= state->dts_nformats);
+ ASSERT(state->dts_formats[format - 1] != NULL);
+
+ fmt = state->dts_formats[format - 1];
+ kmem_free(fmt, strlen(fmt) + 1);
+ state->dts_formats[format - 1] = NULL;
+}
+
+static void
+dtrace_format_destroy(dtrace_state_t *state)
+{
+ int i;
+
+ if (state->dts_nformats == 0) {
+ ASSERT(state->dts_formats == NULL);
+ return;
+ }
+
+ ASSERT(state->dts_formats != NULL);
+
+ for (i = 0; i < state->dts_nformats; i++) {
+ char *fmt = state->dts_formats[i];
+
+ if (fmt == NULL)
+ continue;
+
+ kmem_free(fmt, strlen(fmt) + 1);
+ }
+
+ kmem_free(state->dts_formats, state->dts_nformats * sizeof (char *));
+ state->dts_nformats = 0;
+ state->dts_formats = NULL;
+}
+
+/*
+ * DTrace Predicate Functions
+ */
+static dtrace_predicate_t *
+dtrace_predicate_create(dtrace_difo_t *dp)
+{
+ dtrace_predicate_t *pred;
+
+ ASSERT(MUTEX_HELD(&dtrace_lock));
+ ASSERT(dp->dtdo_refcnt != 0);
+
+ pred = kmem_zalloc(sizeof (dtrace_predicate_t), KM_SLEEP);
+ pred->dtp_difo = dp;
+ pred->dtp_refcnt = 1;
+
+ if (!dtrace_difo_cacheable(dp))
+ return (pred);
+
+ if (dtrace_predcache_id == DTRACE_CACHEIDNONE) {
+ /*
+ * This is only theoretically possible -- we have had 2^32
+ * cacheable predicates on this machine. We cannot allow any
+ * more predicates to become cacheable: as unlikely as it is,
+ * there may be a thread caching a (now stale) predicate cache
+ * ID. (N.B.: the temptation is being successfully resisted to
+ * have this cmn_err() "Holy shit -- we executed this code!")
+ */
+ return (pred);
+ }
+
+ pred->dtp_cacheid = dtrace_predcache_id++;
+
+ return (pred);
+}
+
+static void
+dtrace_predicate_hold(dtrace_predicate_t *pred)
+{
+ ASSERT(MUTEX_HELD(&dtrace_lock));
+ ASSERT(pred->dtp_difo != NULL && pred->dtp_difo->dtdo_refcnt != 0);
+ ASSERT(pred->dtp_refcnt > 0);
+
+ pred->dtp_refcnt++;
+}
+
+static void
+dtrace_predicate_release(dtrace_predicate_t *pred, dtrace_vstate_t *vstate)
+{
+ dtrace_difo_t *dp = pred->dtp_difo;
+
+ ASSERT(MUTEX_HELD(&dtrace_lock));
+ ASSERT(dp != NULL && dp->dtdo_refcnt != 0);
+ ASSERT(pred->dtp_refcnt > 0);
+
+ if (--pred->dtp_refcnt == 0) {
+ dtrace_difo_release(pred->dtp_difo, vstate);
+ kmem_free(pred, sizeof (dtrace_predicate_t));
+ }
+}
+
+/*
+ * DTrace Action Description Functions
+ */
+static dtrace_actdesc_t *
+dtrace_actdesc_create(dtrace_actkind_t kind, uint32_t ntuple,
+ uint64_t uarg, uint64_t arg)
+{
+ dtrace_actdesc_t *act;
+
+#if defined(sun)
+ ASSERT(!DTRACEACT_ISPRINTFLIKE(kind) || (arg != NULL &&
+ arg >= KERNELBASE) || (arg == NULL && kind == DTRACEACT_PRINTA));
+#endif
+
+ act = kmem_zalloc(sizeof (dtrace_actdesc_t), KM_SLEEP);
+ act->dtad_kind = kind;
+ act->dtad_ntuple = ntuple;
+ act->dtad_uarg = uarg;
+ act->dtad_arg = arg;
+ act->dtad_refcnt = 1;
+
+ return (act);
+}
+
+static void
+dtrace_actdesc_hold(dtrace_actdesc_t *act)
+{
+ ASSERT(act->dtad_refcnt >= 1);
+ act->dtad_refcnt++;
+}
+
+static void
+dtrace_actdesc_release(dtrace_actdesc_t *act, dtrace_vstate_t *vstate)
+{
+ dtrace_actkind_t kind = act->dtad_kind;
+ dtrace_difo_t *dp;
+
+ ASSERT(act->dtad_refcnt >= 1);
+
+ if (--act->dtad_refcnt != 0)
+ return;
+
+ if ((dp = act->dtad_difo) != NULL)
+ dtrace_difo_release(dp, vstate);
+
+ if (DTRACEACT_ISPRINTFLIKE(kind)) {
+ char *str = (char *)(uintptr_t)act->dtad_arg;
+
+#if defined(sun)
+ ASSERT((str != NULL && (uintptr_t)str >= KERNELBASE) ||
+ (str == NULL && act->dtad_kind == DTRACEACT_PRINTA));
+#endif
+
+ if (str != NULL)
+ kmem_free(str, strlen(str) + 1);
+ }
+
+ kmem_free(act, sizeof (dtrace_actdesc_t));
+}
+
+/*
+ * DTrace ECB Functions
+ */
+static dtrace_ecb_t *
+dtrace_ecb_add(dtrace_state_t *state, dtrace_probe_t *probe)
+{
+ dtrace_ecb_t *ecb;
+ dtrace_epid_t epid;
+
+ ASSERT(MUTEX_HELD(&dtrace_lock));
+
+ ecb = kmem_zalloc(sizeof (dtrace_ecb_t), KM_SLEEP);
+ ecb->dte_predicate = NULL;
+ ecb->dte_probe = probe;
+
+ /*
+ * The default size is the size of the default action: recording
+ * the epid.
+ */
+ ecb->dte_size = ecb->dte_needed = sizeof (dtrace_epid_t);
+ ecb->dte_alignment = sizeof (dtrace_epid_t);
+
+ epid = state->dts_epid++;
+
+ if (epid - 1 >= state->dts_necbs) {
+ dtrace_ecb_t **oecbs = state->dts_ecbs, **ecbs;
+ int necbs = state->dts_necbs << 1;
+
+ ASSERT(epid == state->dts_necbs + 1);
+
+ if (necbs == 0) {
+ ASSERT(oecbs == NULL);
+ necbs = 1;
+ }
+
+ ecbs = kmem_zalloc(necbs * sizeof (*ecbs), KM_SLEEP);
+
+ if (oecbs != NULL)
+ bcopy(oecbs, ecbs, state->dts_necbs * sizeof (*ecbs));
+
+ dtrace_membar_producer();
+ state->dts_ecbs = ecbs;
+
+ if (oecbs != NULL) {
+ /*
+ * If this state is active, we must dtrace_sync()
+ * before we can free the old dts_ecbs array: we're
+ * coming in hot, and there may be active ring
+ * buffer processing (which indexes into the dts_ecbs
+ * array) on another CPU.
+ */
+ if (state->dts_activity != DTRACE_ACTIVITY_INACTIVE)
+ dtrace_sync();
+
+ kmem_free(oecbs, state->dts_necbs * sizeof (*ecbs));
+ }
+
+ dtrace_membar_producer();
+ state->dts_necbs = necbs;
+ }
+
+ ecb->dte_state = state;
+
+ ASSERT(state->dts_ecbs[epid - 1] == NULL);
+ dtrace_membar_producer();
+ state->dts_ecbs[(ecb->dte_epid = epid) - 1] = ecb;
+
+ return (ecb);
+}
+
+static void
+dtrace_ecb_enable(dtrace_ecb_t *ecb)
+{
+ dtrace_probe_t *probe = ecb->dte_probe;
+
+ ASSERT(MUTEX_HELD(&cpu_lock));
+ ASSERT(MUTEX_HELD(&dtrace_lock));
+ ASSERT(ecb->dte_next == NULL);
+
+ if (probe == NULL) {
+ /*
+ * This is the NULL probe -- there's nothing to do.
+ */
+ return;
+ }
+
+ if (probe->dtpr_ecb == NULL) {
+ dtrace_provider_t *prov = probe->dtpr_provider;
+
+ /*
+ * We're the first ECB on this probe.
+ */
+ probe->dtpr_ecb = probe->dtpr_ecb_last = ecb;
+
+ if (ecb->dte_predicate != NULL)
+ probe->dtpr_predcache = ecb->dte_predicate->dtp_cacheid;
+
+ prov->dtpv_pops.dtps_enable(prov->dtpv_arg,
+ probe->dtpr_id, probe->dtpr_arg);
+ } else {
+ /*
+ * This probe is already active. Swing the last pointer to
+ * point to the new ECB, and issue a dtrace_sync() to assure
+ * that all CPUs have seen the change.
+ */
+ ASSERT(probe->dtpr_ecb_last != NULL);
+ probe->dtpr_ecb_last->dte_next = ecb;
+ probe->dtpr_ecb_last = ecb;
+ probe->dtpr_predcache = 0;
+
+ dtrace_sync();
+ }
+}
+
+static void
+dtrace_ecb_resize(dtrace_ecb_t *ecb)
+{
+ uint32_t maxalign = sizeof (dtrace_epid_t);
+ uint32_t align = sizeof (uint8_t), offs, diff;
+ dtrace_action_t *act;
+ int wastuple = 0;
+ uint32_t aggbase = UINT32_MAX;
+ dtrace_state_t *state = ecb->dte_state;
+
+ /*
+ * If we record anything, we always record the epid. (And we always
+ * record it first.)
+ */
+ offs = sizeof (dtrace_epid_t);
+ ecb->dte_size = ecb->dte_needed = sizeof (dtrace_epid_t);
+
+ for (act = ecb->dte_action; act != NULL; act = act->dta_next) {
+ dtrace_recdesc_t *rec = &act->dta_rec;
+
+ if ((align = rec->dtrd_alignment) > maxalign)
+ maxalign = align;
+
+ if (!wastuple && act->dta_intuple) {
+ /*
+ * This is the first record in a tuple. Align the
+ * offset to be at offset 4 in an 8-byte aligned
+ * block.
+ */
+ diff = offs + sizeof (dtrace_aggid_t);
+
+ if ((diff = (diff & (sizeof (uint64_t) - 1))))
+ offs += sizeof (uint64_t) - diff;
+
+ aggbase = offs - sizeof (dtrace_aggid_t);
+ ASSERT(!(aggbase & (sizeof (uint64_t) - 1)));
+ }
+
+ /*LINTED*/
+ if (rec->dtrd_size != 0 && (diff = (offs & (align - 1)))) {
+ /*
+ * The current offset is not properly aligned; align it.
+ */
+ offs += align - diff;
+ }
+
+ rec->dtrd_offset = offs;
+
+ if (offs + rec->dtrd_size > ecb->dte_needed) {
+ ecb->dte_needed = offs + rec->dtrd_size;
+
+ if (ecb->dte_needed > state->dts_needed)
+ state->dts_needed = ecb->dte_needed;
+ }
+
+ if (DTRACEACT_ISAGG(act->dta_kind)) {
+ dtrace_aggregation_t *agg = (dtrace_aggregation_t *)act;
+ dtrace_action_t *first = agg->dtag_first, *prev;
+
+ ASSERT(rec->dtrd_size != 0 && first != NULL);
+ ASSERT(wastuple);
+ ASSERT(aggbase != UINT32_MAX);
+
+ agg->dtag_base = aggbase;
+
+ while ((prev = first->dta_prev) != NULL &&
+ DTRACEACT_ISAGG(prev->dta_kind)) {
+ agg = (dtrace_aggregation_t *)prev;
+ first = agg->dtag_first;
+ }
+
+ if (prev != NULL) {
+ offs = prev->dta_rec.dtrd_offset +
+ prev->dta_rec.dtrd_size;
+ } else {
+ offs = sizeof (dtrace_epid_t);
+ }
+ wastuple = 0;
+ } else {
+ if (!act->dta_intuple)
+ ecb->dte_size = offs + rec->dtrd_size;
+
+ offs += rec->dtrd_size;
+ }
+
+ wastuple = act->dta_intuple;
+ }
+
+ if ((act = ecb->dte_action) != NULL &&
+ !(act->dta_kind == DTRACEACT_SPECULATE && act->dta_next == NULL) &&
+ ecb->dte_size == sizeof (dtrace_epid_t)) {
+ /*
+ * If the size is still sizeof (dtrace_epid_t), then all
+ * actions store no data; set the size to 0.
+ */
+ ecb->dte_alignment = maxalign;
+ ecb->dte_size = 0;
+
+ /*
+ * If the needed space is still sizeof (dtrace_epid_t), then
+ * all actions need no additional space; set the needed
+ * size to 0.
+ */
+ if (ecb->dte_needed == sizeof (dtrace_epid_t))
+ ecb->dte_needed = 0;
+
+ return;
+ }
+
+ /*
+ * Set our alignment, and make sure that the dte_size and dte_needed
+ * are aligned to the size of an EPID.
+ */
+ ecb->dte_alignment = maxalign;
+ ecb->dte_size = (ecb->dte_size + (sizeof (dtrace_epid_t) - 1)) &
+ ~(sizeof (dtrace_epid_t) - 1);
+ ecb->dte_needed = (ecb->dte_needed + (sizeof (dtrace_epid_t) - 1)) &
+ ~(sizeof (dtrace_epid_t) - 1);
+ ASSERT(ecb->dte_size <= ecb->dte_needed);
+}
+
+static dtrace_action_t *
+dtrace_ecb_aggregation_create(dtrace_ecb_t *ecb, dtrace_actdesc_t *desc)
+{
+ dtrace_aggregation_t *agg;
+ size_t size = sizeof (uint64_t);
+ int ntuple = desc->dtad_ntuple;
+ dtrace_action_t *act;
+ dtrace_recdesc_t *frec;
+ dtrace_aggid_t aggid;
+ dtrace_state_t *state = ecb->dte_state;
+
+ agg = kmem_zalloc(sizeof (dtrace_aggregation_t), KM_SLEEP);
+ agg->dtag_ecb = ecb;
+
+ ASSERT(DTRACEACT_ISAGG(desc->dtad_kind));
+
+ switch (desc->dtad_kind) {
+ case DTRACEAGG_MIN:
+ agg->dtag_initial = INT64_MAX;
+ agg->dtag_aggregate = dtrace_aggregate_min;
+ break;
+
+ case DTRACEAGG_MAX:
+ agg->dtag_initial = INT64_MIN;
+ agg->dtag_aggregate = dtrace_aggregate_max;
+ break;
+
+ case DTRACEAGG_COUNT:
+ agg->dtag_aggregate = dtrace_aggregate_count;
+ break;
+
+ case DTRACEAGG_QUANTIZE:
+ agg->dtag_aggregate = dtrace_aggregate_quantize;
+ size = (((sizeof (uint64_t) * NBBY) - 1) * 2 + 1) *
+ sizeof (uint64_t);
+ break;
+
+ case DTRACEAGG_LQUANTIZE: {
+ uint16_t step = DTRACE_LQUANTIZE_STEP(desc->dtad_arg);
+ uint16_t levels = DTRACE_LQUANTIZE_LEVELS(desc->dtad_arg);
+
+ agg->dtag_initial = desc->dtad_arg;
+ agg->dtag_aggregate = dtrace_aggregate_lquantize;
+
+ if (step == 0 || levels == 0)
+ goto err;
+
+ size = levels * sizeof (uint64_t) + 3 * sizeof (uint64_t);
+ break;
+ }
+
+ case DTRACEAGG_AVG:
+ agg->dtag_aggregate = dtrace_aggregate_avg;
+ size = sizeof (uint64_t) * 2;
+ break;
+
+ case DTRACEAGG_STDDEV:
+ agg->dtag_aggregate = dtrace_aggregate_stddev;
+ size = sizeof (uint64_t) * 4;
+ break;
+
+ case DTRACEAGG_SUM:
+ agg->dtag_aggregate = dtrace_aggregate_sum;
+ break;
+
+ default:
+ goto err;
+ }
+
+ agg->dtag_action.dta_rec.dtrd_size = size;
+
+ if (ntuple == 0)
+ goto err;
+
+ /*
+ * We must make sure that we have enough actions for the n-tuple.
+ */
+ for (act = ecb->dte_action_last; act != NULL; act = act->dta_prev) {
+ if (DTRACEACT_ISAGG(act->dta_kind))
+ break;
+
+ if (--ntuple == 0) {
+ /*
+ * This is the action with which our n-tuple begins.
+ */
+ agg->dtag_first = act;
+ goto success;
+ }
+ }
+
+ /*
+ * This n-tuple is short by ntuple elements. Return failure.
+ */
+ ASSERT(ntuple != 0);
+err:
+ kmem_free(agg, sizeof (dtrace_aggregation_t));
+ return (NULL);
+
+success:
+ /*
+ * If the last action in the tuple has a size of zero, it's actually
+ * an expression argument for the aggregating action.
+ */
+ ASSERT(ecb->dte_action_last != NULL);
+ act = ecb->dte_action_last;
+
+ if (act->dta_kind == DTRACEACT_DIFEXPR) {
+ ASSERT(act->dta_difo != NULL);
+
+ if (act->dta_difo->dtdo_rtype.dtdt_size == 0)
+ agg->dtag_hasarg = 1;
+ }
+
+ /*
+ * We need to allocate an id for this aggregation.
+ */
+#if defined(sun)
+ aggid = (dtrace_aggid_t)(uintptr_t)vmem_alloc(state->dts_aggid_arena, 1,
+ VM_BESTFIT | VM_SLEEP);
+#else
+ aggid = alloc_unr(state->dts_aggid_arena);
+#endif
+
+ if (aggid - 1 >= state->dts_naggregations) {
+ dtrace_aggregation_t **oaggs = state->dts_aggregations;
+ dtrace_aggregation_t **aggs;
+ int naggs = state->dts_naggregations << 1;
+ int onaggs = state->dts_naggregations;
+
+ ASSERT(aggid == state->dts_naggregations + 1);
+
+ if (naggs == 0) {
+ ASSERT(oaggs == NULL);
+ naggs = 1;
+ }
+
+ aggs = kmem_zalloc(naggs * sizeof (*aggs), KM_SLEEP);
+
+ if (oaggs != NULL) {
+ bcopy(oaggs, aggs, onaggs * sizeof (*aggs));
+ kmem_free(oaggs, onaggs * sizeof (*aggs));
+ }
+
+ state->dts_aggregations = aggs;
+ state->dts_naggregations = naggs;
+ }
+
+ ASSERT(state->dts_aggregations[aggid - 1] == NULL);
+ state->dts_aggregations[(agg->dtag_id = aggid) - 1] = agg;
+
+ frec = &agg->dtag_first->dta_rec;
+ if (frec->dtrd_alignment < sizeof (dtrace_aggid_t))
+ frec->dtrd_alignment = sizeof (dtrace_aggid_t);
+
+ for (act = agg->dtag_first; act != NULL; act = act->dta_next) {
+ ASSERT(!act->dta_intuple);
+ act->dta_intuple = 1;
+ }
+
+ return (&agg->dtag_action);
+}
+
+static void
+dtrace_ecb_aggregation_destroy(dtrace_ecb_t *ecb, dtrace_action_t *act)
+{
+ dtrace_aggregation_t *agg = (dtrace_aggregation_t *)act;
+ dtrace_state_t *state = ecb->dte_state;
+ dtrace_aggid_t aggid = agg->dtag_id;
+
+ ASSERT(DTRACEACT_ISAGG(act->dta_kind));
+#if defined(sun)
+ vmem_free(state->dts_aggid_arena, (void *)(uintptr_t)aggid, 1);
+#else
+ free_unr(state->dts_aggid_arena, aggid);
+#endif
+
+ ASSERT(state->dts_aggregations[aggid - 1] == agg);
+ state->dts_aggregations[aggid - 1] = NULL;
+
+ kmem_free(agg, sizeof (dtrace_aggregation_t));
+}
+
+static int
+dtrace_ecb_action_add(dtrace_ecb_t *ecb, dtrace_actdesc_t *desc)
+{
+ dtrace_action_t *action, *last;
+ dtrace_difo_t *dp = desc->dtad_difo;
+ uint32_t size = 0, align = sizeof (uint8_t), mask;
+ uint16_t format = 0;
+ dtrace_recdesc_t *rec;
+ dtrace_state_t *state = ecb->dte_state;
+ dtrace_optval_t *opt = state->dts_options, nframes = 0, strsize;
+ uint64_t arg = desc->dtad_arg;
+
+ ASSERT(MUTEX_HELD(&dtrace_lock));
+ ASSERT(ecb->dte_action == NULL || ecb->dte_action->dta_refcnt == 1);
+
+ if (DTRACEACT_ISAGG(desc->dtad_kind)) {
+ /*
+ * If this is an aggregating action, there must be neither
+ * a speculate nor a commit on the action chain.
+ */
+ dtrace_action_t *act;
+
+ for (act = ecb->dte_action; act != NULL; act = act->dta_next) {
+ if (act->dta_kind == DTRACEACT_COMMIT)
+ return (EINVAL);
+
+ if (act->dta_kind == DTRACEACT_SPECULATE)
+ return (EINVAL);
+ }
+
+ action = dtrace_ecb_aggregation_create(ecb, desc);
+
+ if (action == NULL)
+ return (EINVAL);
+ } else {
+ if (DTRACEACT_ISDESTRUCTIVE(desc->dtad_kind) ||
+ (desc->dtad_kind == DTRACEACT_DIFEXPR &&
+ dp != NULL && dp->dtdo_destructive)) {
+ state->dts_destructive = 1;
+ }
+
+ switch (desc->dtad_kind) {
+ case DTRACEACT_PRINTF:
+ case DTRACEACT_PRINTA:
+ case DTRACEACT_SYSTEM:
+ case DTRACEACT_FREOPEN:
+ /*
+ * We know that our arg is a string -- turn it into a
+ * format.
+ */
+ if (arg == 0) {
+ ASSERT(desc->dtad_kind == DTRACEACT_PRINTA);
+ format = 0;
+ } else {
+ ASSERT(arg != 0);
+#if defined(sun)
+ ASSERT(arg > KERNELBASE);
+#endif
+ format = dtrace_format_add(state,
+ (char *)(uintptr_t)arg);
+ }
+
+ /*FALLTHROUGH*/
+ case DTRACEACT_LIBACT:
+ case DTRACEACT_DIFEXPR:
+ if (dp == NULL)
+ return (EINVAL);
+
+ if ((size = dp->dtdo_rtype.dtdt_size) != 0)
+ break;
+
+ if (dp->dtdo_rtype.dtdt_kind == DIF_TYPE_STRING) {
+ if (!(dp->dtdo_rtype.dtdt_flags & DIF_TF_BYREF))
+ return (EINVAL);
+
+ size = opt[DTRACEOPT_STRSIZE];
+ }
+
+ break;
+
+ case DTRACEACT_STACK:
+ if ((nframes = arg) == 0) {
+ nframes = opt[DTRACEOPT_STACKFRAMES];
+ ASSERT(nframes > 0);
+ arg = nframes;
+ }
+
+ size = nframes * sizeof (pc_t);
+ break;
+
+ case DTRACEACT_JSTACK:
+ if ((strsize = DTRACE_USTACK_STRSIZE(arg)) == 0)
+ strsize = opt[DTRACEOPT_JSTACKSTRSIZE];
+
+ if ((nframes = DTRACE_USTACK_NFRAMES(arg)) == 0)
+ nframes = opt[DTRACEOPT_JSTACKFRAMES];
+
+ arg = DTRACE_USTACK_ARG(nframes, strsize);
+
+ /*FALLTHROUGH*/
+ case DTRACEACT_USTACK:
+ if (desc->dtad_kind != DTRACEACT_JSTACK &&
+ (nframes = DTRACE_USTACK_NFRAMES(arg)) == 0) {
+ strsize = DTRACE_USTACK_STRSIZE(arg);
+ nframes = opt[DTRACEOPT_USTACKFRAMES];
+ ASSERT(nframes > 0);
+ arg = DTRACE_USTACK_ARG(nframes, strsize);
+ }
+
+ /*
+ * Save a slot for the pid.
+ */
+ size = (nframes + 1) * sizeof (uint64_t);
+ size += DTRACE_USTACK_STRSIZE(arg);
+ size = P2ROUNDUP(size, (uint32_t)(sizeof (uintptr_t)));
+
+ break;
+
+ case DTRACEACT_SYM:
+ case DTRACEACT_MOD:
+ if (dp == NULL || ((size = dp->dtdo_rtype.dtdt_size) !=
+ sizeof (uint64_t)) ||
+ (dp->dtdo_rtype.dtdt_flags & DIF_TF_BYREF))
+ return (EINVAL);
+ break;
+
+ case DTRACEACT_USYM:
+ case DTRACEACT_UMOD:
+ case DTRACEACT_UADDR:
+ if (dp == NULL ||
+ (dp->dtdo_rtype.dtdt_size != sizeof (uint64_t)) ||
+ (dp->dtdo_rtype.dtdt_flags & DIF_TF_BYREF))
+ return (EINVAL);
+
+ /*
+ * We have a slot for the pid, plus a slot for the
+ * argument. To keep things simple (aligned with
+ * bitness-neutral sizing), we store each as a 64-bit
+ * quantity.
+ */
+ size = 2 * sizeof (uint64_t);
+ break;
+
+ case DTRACEACT_STOP:
+ case DTRACEACT_BREAKPOINT:
+ case DTRACEACT_PANIC:
+ break;
+
+ case DTRACEACT_CHILL:
+ case DTRACEACT_DISCARD:
+ case DTRACEACT_RAISE:
+ if (dp == NULL)
+ return (EINVAL);
+ break;
+
+ case DTRACEACT_EXIT:
+ if (dp == NULL ||
+ (size = dp->dtdo_rtype.dtdt_size) != sizeof (int) ||
+ (dp->dtdo_rtype.dtdt_flags & DIF_TF_BYREF))
+ return (EINVAL);
+ break;
+
+ case DTRACEACT_SPECULATE:
+ if (ecb->dte_size > sizeof (dtrace_epid_t))
+ return (EINVAL);
+
+ if (dp == NULL)
+ return (EINVAL);
+
+ state->dts_speculates = 1;
+ break;
+
+ case DTRACEACT_PRINTM:
+ size = dp->dtdo_rtype.dtdt_size;
+ break;
+
+ case DTRACEACT_PRINTT:
+ size = dp->dtdo_rtype.dtdt_size;
+ break;
+
+ case DTRACEACT_COMMIT: {
+ dtrace_action_t *act = ecb->dte_action;
+
+ for (; act != NULL; act = act->dta_next) {
+ if (act->dta_kind == DTRACEACT_COMMIT)
+ return (EINVAL);
+ }
+
+ if (dp == NULL)
+ return (EINVAL);
+ break;
+ }
+
+ default:
+ return (EINVAL);
+ }
+
+ if (size != 0 || desc->dtad_kind == DTRACEACT_SPECULATE) {
+ /*
+ * If this is a data-storing action or a speculate,
+ * we must be sure that there isn't a commit on the
+ * action chain.
+ */
+ dtrace_action_t *act = ecb->dte_action;
+
+ for (; act != NULL; act = act->dta_next) {
+ if (act->dta_kind == DTRACEACT_COMMIT)
+ return (EINVAL);
+ }
+ }
+
+ action = kmem_zalloc(sizeof (dtrace_action_t), KM_SLEEP);
+ action->dta_rec.dtrd_size = size;
+ }
+
+ action->dta_refcnt = 1;
+ rec = &action->dta_rec;
+ size = rec->dtrd_size;
+
+ for (mask = sizeof (uint64_t) - 1; size != 0 && mask > 0; mask >>= 1) {
+ if (!(size & mask)) {
+ align = mask + 1;
+ break;
+ }
+ }
+
+ action->dta_kind = desc->dtad_kind;
+
+ if ((action->dta_difo = dp) != NULL)
+ dtrace_difo_hold(dp);
+
+ rec->dtrd_action = action->dta_kind;
+ rec->dtrd_arg = arg;
+ rec->dtrd_uarg = desc->dtad_uarg;
+ rec->dtrd_alignment = (uint16_t)align;
+ rec->dtrd_format = format;
+
+ if ((last = ecb->dte_action_last) != NULL) {
+ ASSERT(ecb->dte_action != NULL);
+ action->dta_prev = last;
+ last->dta_next = action;
+ } else {
+ ASSERT(ecb->dte_action == NULL);
+ ecb->dte_action = action;
+ }
+
+ ecb->dte_action_last = action;
+
+ return (0);
+}
+
+static void
+dtrace_ecb_action_remove(dtrace_ecb_t *ecb)
+{
+ dtrace_action_t *act = ecb->dte_action, *next;
+ dtrace_vstate_t *vstate = &ecb->dte_state->dts_vstate;
+ dtrace_difo_t *dp;
+ uint16_t format;
+
+ if (act != NULL && act->dta_refcnt > 1) {
+ ASSERT(act->dta_next == NULL || act->dta_next->dta_refcnt == 1);
+ act->dta_refcnt--;
+ } else {
+ for (; act != NULL; act = next) {
+ next = act->dta_next;
+ ASSERT(next != NULL || act == ecb->dte_action_last);
+ ASSERT(act->dta_refcnt == 1);
+
+ if ((format = act->dta_rec.dtrd_format) != 0)
+ dtrace_format_remove(ecb->dte_state, format);
+
+ if ((dp = act->dta_difo) != NULL)
+ dtrace_difo_release(dp, vstate);
+
+ if (DTRACEACT_ISAGG(act->dta_kind)) {
+ dtrace_ecb_aggregation_destroy(ecb, act);
+ } else {
+ kmem_free(act, sizeof (dtrace_action_t));
+ }
+ }
+ }
+
+ ecb->dte_action = NULL;
+ ecb->dte_action_last = NULL;
+ ecb->dte_size = sizeof (dtrace_epid_t);
+}
+
+static void
+dtrace_ecb_disable(dtrace_ecb_t *ecb)
+{
+ /*
+ * We disable the ECB by removing it from its probe.
+ */
+ dtrace_ecb_t *pecb, *prev = NULL;
+ dtrace_probe_t *probe = ecb->dte_probe;
+
+ ASSERT(MUTEX_HELD(&dtrace_lock));
+
+ if (probe == NULL) {
+ /*
+ * This is the NULL probe; there is nothing to disable.
+ */
+ return;
+ }
+
+ for (pecb = probe->dtpr_ecb; pecb != NULL; pecb = pecb->dte_next) {
+ if (pecb == ecb)
+ break;
+ prev = pecb;
+ }
+
+ ASSERT(pecb != NULL);
+
+ if (prev == NULL) {
+ probe->dtpr_ecb = ecb->dte_next;
+ } else {
+ prev->dte_next = ecb->dte_next;
+ }
+
+ if (ecb == probe->dtpr_ecb_last) {
+ ASSERT(ecb->dte_next == NULL);
+ probe->dtpr_ecb_last = prev;
+ }
+
+ /*
+ * The ECB has been disconnected from the probe; now sync to assure
+ * that all CPUs have seen the change before returning.
+ */
+ dtrace_sync();
+
+ if (probe->dtpr_ecb == NULL) {
+ /*
+ * That was the last ECB on the probe; clear the predicate
+ * cache ID for the probe, disable it and sync one more time
+ * to assure that we'll never hit it again.
+ */
+ dtrace_provider_t *prov = probe->dtpr_provider;
+
+ ASSERT(ecb->dte_next == NULL);
+ ASSERT(probe->dtpr_ecb_last == NULL);
+ probe->dtpr_predcache = DTRACE_CACHEIDNONE;
+ prov->dtpv_pops.dtps_disable(prov->dtpv_arg,
+ probe->dtpr_id, probe->dtpr_arg);
+ dtrace_sync();
+ } else {
+ /*
+ * There is at least one ECB remaining on the probe. If there
+ * is _exactly_ one, set the probe's predicate cache ID to be
+ * the predicate cache ID of the remaining ECB.
+ */
+ ASSERT(probe->dtpr_ecb_last != NULL);
+ ASSERT(probe->dtpr_predcache == DTRACE_CACHEIDNONE);
+
+ if (probe->dtpr_ecb == probe->dtpr_ecb_last) {
+ dtrace_predicate_t *p = probe->dtpr_ecb->dte_predicate;
+
+ ASSERT(probe->dtpr_ecb->dte_next == NULL);
+
+ if (p != NULL)
+ probe->dtpr_predcache = p->dtp_cacheid;
+ }
+
+ ecb->dte_next = NULL;
+ }
+}
+
+static void
+dtrace_ecb_destroy(dtrace_ecb_t *ecb)
+{
+ dtrace_state_t *state = ecb->dte_state;
+ dtrace_vstate_t *vstate = &state->dts_vstate;
+ dtrace_predicate_t *pred;
+ dtrace_epid_t epid = ecb->dte_epid;
+
+ ASSERT(MUTEX_HELD(&dtrace_lock));
+ ASSERT(ecb->dte_next == NULL);
+ ASSERT(ecb->dte_probe == NULL || ecb->dte_probe->dtpr_ecb != ecb);
+
+ if ((pred = ecb->dte_predicate) != NULL)
+ dtrace_predicate_release(pred, vstate);
+
+ dtrace_ecb_action_remove(ecb);
+
+ ASSERT(state->dts_ecbs[epid - 1] == ecb);
+ state->dts_ecbs[epid - 1] = NULL;
+
+ kmem_free(ecb, sizeof (dtrace_ecb_t));
+}
+
+static dtrace_ecb_t *
+dtrace_ecb_create(dtrace_state_t *state, dtrace_probe_t *probe,
+ dtrace_enabling_t *enab)
+{
+ dtrace_ecb_t *ecb;
+ dtrace_predicate_t *pred;
+ dtrace_actdesc_t *act;
+ dtrace_provider_t *prov;
+ dtrace_ecbdesc_t *desc = enab->dten_current;
+
+ ASSERT(MUTEX_HELD(&dtrace_lock));
+ ASSERT(state != NULL);
+
+ ecb = dtrace_ecb_add(state, probe);
+ ecb->dte_uarg = desc->dted_uarg;
+
+ if ((pred = desc->dted_pred.dtpdd_predicate) != NULL) {
+ dtrace_predicate_hold(pred);
+ ecb->dte_predicate = pred;
+ }
+
+ if (probe != NULL) {
+ /*
+ * If the provider shows more leg than the consumer is old
+ * enough to see, we need to enable the appropriate implicit
+ * predicate bits to prevent the ecb from activating at
+ * revealing times.
+ *
+ * Providers specifying DTRACE_PRIV_USER at register time
+ * are stating that they need the /proc-style privilege
+ * model to be enforced, and this is what DTRACE_COND_OWNER
+ * and DTRACE_COND_ZONEOWNER will then do at probe time.
+ */
+ prov = probe->dtpr_provider;
+ if (!(state->dts_cred.dcr_visible & DTRACE_CRV_ALLPROC) &&
+ (prov->dtpv_priv.dtpp_flags & DTRACE_PRIV_USER))
+ ecb->dte_cond |= DTRACE_COND_OWNER;
+
+ if (!(state->dts_cred.dcr_visible & DTRACE_CRV_ALLZONE) &&
+ (prov->dtpv_priv.dtpp_flags & DTRACE_PRIV_USER))
+ ecb->dte_cond |= DTRACE_COND_ZONEOWNER;
+
+ /*
+ * If the provider shows us kernel innards and the user
+ * is lacking sufficient privilege, enable the
+ * DTRACE_COND_USERMODE implicit predicate.
+ */
+ if (!(state->dts_cred.dcr_visible & DTRACE_CRV_KERNEL) &&
+ (prov->dtpv_priv.dtpp_flags & DTRACE_PRIV_KERNEL))
+ ecb->dte_cond |= DTRACE_COND_USERMODE;
+ }
+
+ if (dtrace_ecb_create_cache != NULL) {
+ /*
+ * If we have a cached ecb, we'll use its action list instead
+ * of creating our own (saving both time and space).
+ */
+ dtrace_ecb_t *cached = dtrace_ecb_create_cache;
+ dtrace_action_t *act = cached->dte_action;
+
+ if (act != NULL) {
+ ASSERT(act->dta_refcnt > 0);
+ act->dta_refcnt++;
+ ecb->dte_action = act;
+ ecb->dte_action_last = cached->dte_action_last;
+ ecb->dte_needed = cached->dte_needed;
+ ecb->dte_size = cached->dte_size;
+ ecb->dte_alignment = cached->dte_alignment;
+ }
+
+ return (ecb);
+ }
+
+ for (act = desc->dted_action; act != NULL; act = act->dtad_next) {
+ if ((enab->dten_error = dtrace_ecb_action_add(ecb, act)) != 0) {
+ dtrace_ecb_destroy(ecb);
+ return (NULL);
+ }
+ }
+
+ dtrace_ecb_resize(ecb);
+
+ return (dtrace_ecb_create_cache = ecb);
+}
+
+static int
+dtrace_ecb_create_enable(dtrace_probe_t *probe, void *arg)
+{
+ dtrace_ecb_t *ecb;
+ dtrace_enabling_t *enab = arg;
+ dtrace_state_t *state = enab->dten_vstate->dtvs_state;
+
+ ASSERT(state != NULL);
+
+ if (probe != NULL && probe->dtpr_gen < enab->dten_probegen) {
+ /*
+ * This probe was created in a generation for which this
+ * enabling has previously created ECBs; we don't want to
+ * enable it again, so just kick out.
+ */
+ return (DTRACE_MATCH_NEXT);
+ }
+
+ if ((ecb = dtrace_ecb_create(state, probe, enab)) == NULL)
+ return (DTRACE_MATCH_DONE);
+
+ dtrace_ecb_enable(ecb);
+ return (DTRACE_MATCH_NEXT);
+}
+
+static dtrace_ecb_t *
+dtrace_epid2ecb(dtrace_state_t *state, dtrace_epid_t id)
+{
+ dtrace_ecb_t *ecb;
+
+ ASSERT(MUTEX_HELD(&dtrace_lock));
+
+ if (id == 0 || id > state->dts_necbs)
+ return (NULL);
+
+ ASSERT(state->dts_necbs > 0 && state->dts_ecbs != NULL);
+ ASSERT((ecb = state->dts_ecbs[id - 1]) == NULL || ecb->dte_epid == id);
+
+ return (state->dts_ecbs[id - 1]);
+}
+
+static dtrace_aggregation_t *
+dtrace_aggid2agg(dtrace_state_t *state, dtrace_aggid_t id)
+{
+ dtrace_aggregation_t *agg;
+
+ ASSERT(MUTEX_HELD(&dtrace_lock));
+
+ if (id == 0 || id > state->dts_naggregations)
+ return (NULL);
+
+ ASSERT(state->dts_naggregations > 0 && state->dts_aggregations != NULL);
+ ASSERT((agg = state->dts_aggregations[id - 1]) == NULL ||
+ agg->dtag_id == id);
+
+ return (state->dts_aggregations[id - 1]);
+}
+
+/*
+ * DTrace Buffer Functions
+ *
+ * The following functions manipulate DTrace buffers. Most of these functions
+ * are called in the context of establishing or processing consumer state;
+ * exceptions are explicitly noted.
+ */
+
+/*
+ * Note: called from cross call context. This function switches the two
+ * buffers on a given CPU. The atomicity of this operation is assured by
+ * disabling interrupts while the actual switch takes place; the disabling of
+ * interrupts serializes the execution with any execution of dtrace_probe() on
+ * the same CPU.
+ */
+static void
+dtrace_buffer_switch(dtrace_buffer_t *buf)
+{
+ caddr_t tomax = buf->dtb_tomax;
+ caddr_t xamot = buf->dtb_xamot;
+ dtrace_icookie_t cookie;
+
+ ASSERT(!(buf->dtb_flags & DTRACEBUF_NOSWITCH));
+ ASSERT(!(buf->dtb_flags & DTRACEBUF_RING));
+
+ cookie = dtrace_interrupt_disable();
+ buf->dtb_tomax = xamot;
+ buf->dtb_xamot = tomax;
+ buf->dtb_xamot_drops = buf->dtb_drops;
+ buf->dtb_xamot_offset = buf->dtb_offset;
+ buf->dtb_xamot_errors = buf->dtb_errors;
+ buf->dtb_xamot_flags = buf->dtb_flags;
+ buf->dtb_offset = 0;
+ buf->dtb_drops = 0;
+ buf->dtb_errors = 0;
+ buf->dtb_flags &= ~(DTRACEBUF_ERROR | DTRACEBUF_DROPPED);
+ dtrace_interrupt_enable(cookie);
+}
+
+/*
+ * Note: called from cross call context. This function activates a buffer
+ * on a CPU. As with dtrace_buffer_switch(), the atomicity of the operation
+ * is guaranteed by the disabling of interrupts.
+ */
+static void
+dtrace_buffer_activate(dtrace_state_t *state)
+{
+ dtrace_buffer_t *buf;
+ dtrace_icookie_t cookie = dtrace_interrupt_disable();
+
+ buf = &state->dts_buffer[curcpu];
+
+ if (buf->dtb_tomax != NULL) {
+ /*
+ * We might like to assert that the buffer is marked inactive,
+ * but this isn't necessarily true: the buffer for the CPU
+ * that processes the BEGIN probe has its buffer activated
+ * manually. In this case, we take the (harmless) action
+ * re-clearing the bit INACTIVE bit.
+ */
+ buf->dtb_flags &= ~DTRACEBUF_INACTIVE;
+ }
+
+ dtrace_interrupt_enable(cookie);
+}
+
+static int
+dtrace_buffer_alloc(dtrace_buffer_t *bufs, size_t size, int flags,
+ processorid_t cpu)
+{
+#if defined(sun)
+ cpu_t *cp;
+#else
+ struct pcpu *cp;
+#endif
+ dtrace_buffer_t *buf;
+
+#if defined(sun)
+ ASSERT(MUTEX_HELD(&cpu_lock));
+ ASSERT(MUTEX_HELD(&dtrace_lock));
+
+ if (size > dtrace_nonroot_maxsize &&
+ !PRIV_POLICY_CHOICE(CRED(), PRIV_ALL, B_FALSE))
+ return (EFBIG);
+
+ cp = cpu_list;
+
+ do {
+ if (cpu != DTRACE_CPUALL && cpu != cp->cpu_id)
+ continue;
+
+ buf = &bufs[cp->cpu_id];
+
+ /*
+ * If there is already a buffer allocated for this CPU, it
+ * is only possible that this is a DR event. In this case,
+ */
+ if (buf->dtb_tomax != NULL) {
+ ASSERT(buf->dtb_size == size);
+ continue;
+ }
+
+ ASSERT(buf->dtb_xamot == NULL);
+
+ if ((buf->dtb_tomax = kmem_zalloc(size, KM_NOSLEEP)) == NULL)
+ goto err;
+
+ buf->dtb_size = size;
+ buf->dtb_flags = flags;
+ buf->dtb_offset = 0;
+ buf->dtb_drops = 0;
+
+ if (flags & DTRACEBUF_NOSWITCH)
+ continue;
+
+ if ((buf->dtb_xamot = kmem_zalloc(size, KM_NOSLEEP)) == NULL)
+ goto err;
+ } while ((cp = cp->cpu_next) != cpu_list);
+
+ return (0);
+
+err:
+ cp = cpu_list;
+
+ do {
+ if (cpu != DTRACE_CPUALL && cpu != cp->cpu_id)
+ continue;
+
+ buf = &bufs[cp->cpu_id];
+
+ if (buf->dtb_xamot != NULL) {
+ ASSERT(buf->dtb_tomax != NULL);
+ ASSERT(buf->dtb_size == size);
+ kmem_free(buf->dtb_xamot, size);
+ }
+
+ if (buf->dtb_tomax != NULL) {
+ ASSERT(buf->dtb_size == size);
+ kmem_free(buf->dtb_tomax, size);
+ }
+
+ buf->dtb_tomax = NULL;
+ buf->dtb_xamot = NULL;
+ buf->dtb_size = 0;
+ } while ((cp = cp->cpu_next) != cpu_list);
+
+ return (ENOMEM);
+#else
+ int i;
+
+#if defined(__amd64__)
+ /*
+ * FreeBSD isn't good at limiting the amount of memory we
+ * ask to malloc, so let's place a limit here before trying
+ * to do something that might well end in tears at bedtime.
+ */
+ if (size > physmem * PAGE_SIZE / (128 * (mp_maxid + 1)))
+ return(ENOMEM);
+#endif
+
+ ASSERT(MUTEX_HELD(&dtrace_lock));
+ for (i = 0; i <= mp_maxid; i++) {
+ if ((cp = pcpu_find(i)) == NULL)
+ continue;
+
+ if (cpu != DTRACE_CPUALL && cpu != i)
+ continue;
+
+ buf = &bufs[i];
+
+ /*
+ * If there is already a buffer allocated for this CPU, it
+ * is only possible that this is a DR event. In this case,
+ * the buffer size must match our specified size.
+ */
+ if (buf->dtb_tomax != NULL) {
+ ASSERT(buf->dtb_size == size);
+ continue;
+ }
+
+ ASSERT(buf->dtb_xamot == NULL);
+
+ if ((buf->dtb_tomax = kmem_zalloc(size, KM_NOSLEEP)) == NULL)
+ goto err;
+
+ buf->dtb_size = size;
+ buf->dtb_flags = flags;
+ buf->dtb_offset = 0;
+ buf->dtb_drops = 0;
+
+ if (flags & DTRACEBUF_NOSWITCH)
+ continue;
+
+ if ((buf->dtb_xamot = kmem_zalloc(size, KM_NOSLEEP)) == NULL)
+ goto err;
+ }
+
+ return (0);
+
+err:
+ /*
+ * Error allocating memory, so free the buffers that were
+ * allocated before the failed allocation.
+ */
+ for (i = 0; i <= mp_maxid; i++) {
+ if ((cp = pcpu_find(i)) == NULL)
+ continue;
+
+ if (cpu != DTRACE_CPUALL && cpu != i)
+ continue;
+
+ buf = &bufs[i];
+
+ if (buf->dtb_xamot != NULL) {
+ ASSERT(buf->dtb_tomax != NULL);
+ ASSERT(buf->dtb_size == size);
+ kmem_free(buf->dtb_xamot, size);
+ }
+
+ if (buf->dtb_tomax != NULL) {
+ ASSERT(buf->dtb_size == size);
+ kmem_free(buf->dtb_tomax, size);
+ }
+
+ buf->dtb_tomax = NULL;
+ buf->dtb_xamot = NULL;
+ buf->dtb_size = 0;
+
+ }
+
+ return (ENOMEM);
+#endif
+}
+
+/*
+ * Note: called from probe context. This function just increments the drop
+ * count on a buffer. It has been made a function to allow for the
+ * possibility of understanding the source of mysterious drop counts. (A
+ * problem for which one may be particularly disappointed that DTrace cannot
+ * be used to understand DTrace.)
+ */
+static void
+dtrace_buffer_drop(dtrace_buffer_t *buf)
+{
+ buf->dtb_drops++;
+}
+
+/*
+ * Note: called from probe context. This function is called to reserve space
+ * in a buffer. If mstate is non-NULL, sets the scratch base and size in the
+ * mstate. Returns the new offset in the buffer, or a negative value if an
+ * error has occurred.
+ */
+static intptr_t
+dtrace_buffer_reserve(dtrace_buffer_t *buf, size_t needed, size_t align,
+ dtrace_state_t *state, dtrace_mstate_t *mstate)
+{
+ intptr_t offs = buf->dtb_offset, soffs;
+ intptr_t woffs;
+ caddr_t tomax;
+ size_t total;
+
+ if (buf->dtb_flags & DTRACEBUF_INACTIVE)
+ return (-1);
+
+ if ((tomax = buf->dtb_tomax) == NULL) {
+ dtrace_buffer_drop(buf);
+ return (-1);
+ }
+
+ if (!(buf->dtb_flags & (DTRACEBUF_RING | DTRACEBUF_FILL))) {
+ while (offs & (align - 1)) {
+ /*
+ * Assert that our alignment is off by a number which
+ * is itself sizeof (uint32_t) aligned.
+ */
+ ASSERT(!((align - (offs & (align - 1))) &
+ (sizeof (uint32_t) - 1)));
+ DTRACE_STORE(uint32_t, tomax, offs, DTRACE_EPIDNONE);
+ offs += sizeof (uint32_t);
+ }
+
+ if ((soffs = offs + needed) > buf->dtb_size) {
+ dtrace_buffer_drop(buf);
+ return (-1);
+ }
+
+ if (mstate == NULL)
+ return (offs);
+
+ mstate->dtms_scratch_base = (uintptr_t)tomax + soffs;
+ mstate->dtms_scratch_size = buf->dtb_size - soffs;
+ mstate->dtms_scratch_ptr = mstate->dtms_scratch_base;
+
+ return (offs);
+ }
+
+ if (buf->dtb_flags & DTRACEBUF_FILL) {
+ if (state->dts_activity != DTRACE_ACTIVITY_COOLDOWN &&
+ (buf->dtb_flags & DTRACEBUF_FULL))
+ return (-1);
+ goto out;
+ }
+
+ total = needed + (offs & (align - 1));
+
+ /*
+ * For a ring buffer, life is quite a bit more complicated. Before
+ * we can store any padding, we need to adjust our wrapping offset.
+ * (If we've never before wrapped or we're not about to, no adjustment
+ * is required.)
+ */
+ if ((buf->dtb_flags & DTRACEBUF_WRAPPED) ||
+ offs + total > buf->dtb_size) {
+ woffs = buf->dtb_xamot_offset;
+
+ if (offs + total > buf->dtb_size) {
+ /*
+ * We can't fit in the end of the buffer. First, a
+ * sanity check that we can fit in the buffer at all.
+ */
+ if (total > buf->dtb_size) {
+ dtrace_buffer_drop(buf);
+ return (-1);
+ }
+
+ /*
+ * We're going to be storing at the top of the buffer,
+ * so now we need to deal with the wrapped offset. We
+ * only reset our wrapped offset to 0 if it is
+ * currently greater than the current offset. If it
+ * is less than the current offset, it is because a
+ * previous allocation induced a wrap -- but the
+ * allocation didn't subsequently take the space due
+ * to an error or false predicate evaluation. In this
+ * case, we'll just leave the wrapped offset alone: if
+ * the wrapped offset hasn't been advanced far enough
+ * for this allocation, it will be adjusted in the
+ * lower loop.
+ */
+ if (buf->dtb_flags & DTRACEBUF_WRAPPED) {
+ if (woffs >= offs)
+ woffs = 0;
+ } else {
+ woffs = 0;
+ }
+
+ /*
+ * Now we know that we're going to be storing to the
+ * top of the buffer and that there is room for us
+ * there. We need to clear the buffer from the current
+ * offset to the end (there may be old gunk there).
+ */
+ while (offs < buf->dtb_size)
+ tomax[offs++] = 0;
+
+ /*
+ * We need to set our offset to zero. And because we
+ * are wrapping, we need to set the bit indicating as
+ * much. We can also adjust our needed space back
+ * down to the space required by the ECB -- we know
+ * that the top of the buffer is aligned.
+ */
+ offs = 0;
+ total = needed;
+ buf->dtb_flags |= DTRACEBUF_WRAPPED;
+ } else {
+ /*
+ * There is room for us in the buffer, so we simply
+ * need to check the wrapped offset.
+ */
+ if (woffs < offs) {
+ /*
+ * The wrapped offset is less than the offset.
+ * This can happen if we allocated buffer space
+ * that induced a wrap, but then we didn't
+ * subsequently take the space due to an error
+ * or false predicate evaluation. This is
+ * okay; we know that _this_ allocation isn't
+ * going to induce a wrap. We still can't
+ * reset the wrapped offset to be zero,
+ * however: the space may have been trashed in
+ * the previous failed probe attempt. But at
+ * least the wrapped offset doesn't need to
+ * be adjusted at all...
+ */
+ goto out;
+ }
+ }
+
+ while (offs + total > woffs) {
+ dtrace_epid_t epid = *(uint32_t *)(tomax + woffs);
+ size_t size;
+
+ if (epid == DTRACE_EPIDNONE) {
+ size = sizeof (uint32_t);
+ } else {
+ ASSERT(epid <= state->dts_necbs);
+ ASSERT(state->dts_ecbs[epid - 1] != NULL);
+
+ size = state->dts_ecbs[epid - 1]->dte_size;
+ }
+
+ ASSERT(woffs + size <= buf->dtb_size);
+ ASSERT(size != 0);
+
+ if (woffs + size == buf->dtb_size) {
+ /*
+ * We've reached the end of the buffer; we want
+ * to set the wrapped offset to 0 and break
+ * out. However, if the offs is 0, then we're
+ * in a strange edge-condition: the amount of
+ * space that we want to reserve plus the size
+ * of the record that we're overwriting is
+ * greater than the size of the buffer. This
+ * is problematic because if we reserve the
+ * space but subsequently don't consume it (due
+ * to a failed predicate or error) the wrapped
+ * offset will be 0 -- yet the EPID at offset 0
+ * will not be committed. This situation is
+ * relatively easy to deal with: if we're in
+ * this case, the buffer is indistinguishable
+ * from one that hasn't wrapped; we need only
+ * finish the job by clearing the wrapped bit,
+ * explicitly setting the offset to be 0, and
+ * zero'ing out the old data in the buffer.
+ */
+ if (offs == 0) {
+ buf->dtb_flags &= ~DTRACEBUF_WRAPPED;
+ buf->dtb_offset = 0;
+ woffs = total;
+
+ while (woffs < buf->dtb_size)
+ tomax[woffs++] = 0;
+ }
+
+ woffs = 0;
+ break;
+ }
+
+ woffs += size;
+ }
+
+ /*
+ * We have a wrapped offset. It may be that the wrapped offset
+ * has become zero -- that's okay.
+ */
+ buf->dtb_xamot_offset = woffs;
+ }
+
+out:
+ /*
+ * Now we can plow the buffer with any necessary padding.
+ */
+ while (offs & (align - 1)) {
+ /*
+ * Assert that our alignment is off by a number which
+ * is itself sizeof (uint32_t) aligned.
+ */
+ ASSERT(!((align - (offs & (align - 1))) &
+ (sizeof (uint32_t) - 1)));
+ DTRACE_STORE(uint32_t, tomax, offs, DTRACE_EPIDNONE);
+ offs += sizeof (uint32_t);
+ }
+
+ if (buf->dtb_flags & DTRACEBUF_FILL) {
+ if (offs + needed > buf->dtb_size - state->dts_reserve) {
+ buf->dtb_flags |= DTRACEBUF_FULL;
+ return (-1);
+ }
+ }
+
+ if (mstate == NULL)
+ return (offs);
+
+ /*
+ * For ring buffers and fill buffers, the scratch space is always
+ * the inactive buffer.
+ */
+ mstate->dtms_scratch_base = (uintptr_t)buf->dtb_xamot;
+ mstate->dtms_scratch_size = buf->dtb_size;
+ mstate->dtms_scratch_ptr = mstate->dtms_scratch_base;
+
+ return (offs);
+}
+
+static void
+dtrace_buffer_polish(dtrace_buffer_t *buf)
+{
+ ASSERT(buf->dtb_flags & DTRACEBUF_RING);
+ ASSERT(MUTEX_HELD(&dtrace_lock));
+
+ if (!(buf->dtb_flags & DTRACEBUF_WRAPPED))
+ return;
+
+ /*
+ * We need to polish the ring buffer. There are three cases:
+ *
+ * - The first (and presumably most common) is that there is no gap
+ * between the buffer offset and the wrapped offset. In this case,
+ * there is nothing in the buffer that isn't valid data; we can
+ * mark the buffer as polished and return.
+ *
+ * - The second (less common than the first but still more common
+ * than the third) is that there is a gap between the buffer offset
+ * and the wrapped offset, and the wrapped offset is larger than the
+ * buffer offset. This can happen because of an alignment issue, or
+ * can happen because of a call to dtrace_buffer_reserve() that
+ * didn't subsequently consume the buffer space. In this case,
+ * we need to zero the data from the buffer offset to the wrapped
+ * offset.
+ *
+ * - The third (and least common) is that there is a gap between the
+ * buffer offset and the wrapped offset, but the wrapped offset is
+ * _less_ than the buffer offset. This can only happen because a
+ * call to dtrace_buffer_reserve() induced a wrap, but the space
+ * was not subsequently consumed. In this case, we need to zero the
+ * space from the offset to the end of the buffer _and_ from the
+ * top of the buffer to the wrapped offset.
+ */
+ if (buf->dtb_offset < buf->dtb_xamot_offset) {
+ bzero(buf->dtb_tomax + buf->dtb_offset,
+ buf->dtb_xamot_offset - buf->dtb_offset);
+ }
+
+ if (buf->dtb_offset > buf->dtb_xamot_offset) {
+ bzero(buf->dtb_tomax + buf->dtb_offset,
+ buf->dtb_size - buf->dtb_offset);
+ bzero(buf->dtb_tomax, buf->dtb_xamot_offset);
+ }
+}
+
+static void
+dtrace_buffer_free(dtrace_buffer_t *bufs)
+{
+ int i;
+
+ for (i = 0; i < NCPU; i++) {
+ dtrace_buffer_t *buf = &bufs[i];
+
+ if (buf->dtb_tomax == NULL) {
+ ASSERT(buf->dtb_xamot == NULL);
+ ASSERT(buf->dtb_size == 0);
+ continue;
+ }
+
+ if (buf->dtb_xamot != NULL) {
+ ASSERT(!(buf->dtb_flags & DTRACEBUF_NOSWITCH));
+ kmem_free(buf->dtb_xamot, buf->dtb_size);
+ }
+
+ kmem_free(buf->dtb_tomax, buf->dtb_size);
+ buf->dtb_size = 0;
+ buf->dtb_tomax = NULL;
+ buf->dtb_xamot = NULL;
+ }
+}
+
+/*
+ * DTrace Enabling Functions
+ */
+static dtrace_enabling_t *
+dtrace_enabling_create(dtrace_vstate_t *vstate)
+{
+ dtrace_enabling_t *enab;
+
+ enab = kmem_zalloc(sizeof (dtrace_enabling_t), KM_SLEEP);
+ enab->dten_vstate = vstate;
+
+ return (enab);
+}
+
+static void
+dtrace_enabling_add(dtrace_enabling_t *enab, dtrace_ecbdesc_t *ecb)
+{
+ dtrace_ecbdesc_t **ndesc;
+ size_t osize, nsize;
+
+ /*
+ * We can't add to enablings after we've enabled them, or after we've
+ * retained them.
+ */
+ ASSERT(enab->dten_probegen == 0);
+ ASSERT(enab->dten_next == NULL && enab->dten_prev == NULL);
+
+ if (enab->dten_ndesc < enab->dten_maxdesc) {
+ enab->dten_desc[enab->dten_ndesc++] = ecb;
+ return;
+ }
+
+ osize = enab->dten_maxdesc * sizeof (dtrace_enabling_t *);
+
+ if (enab->dten_maxdesc == 0) {
+ enab->dten_maxdesc = 1;
+ } else {
+ enab->dten_maxdesc <<= 1;
+ }
+
+ ASSERT(enab->dten_ndesc < enab->dten_maxdesc);
+
+ nsize = enab->dten_maxdesc * sizeof (dtrace_enabling_t *);
+ ndesc = kmem_zalloc(nsize, KM_SLEEP);
+ bcopy(enab->dten_desc, ndesc, osize);
+ if (enab->dten_desc != NULL)
+ kmem_free(enab->dten_desc, osize);
+
+ enab->dten_desc = ndesc;
+ enab->dten_desc[enab->dten_ndesc++] = ecb;
+}
+
+static void
+dtrace_enabling_addlike(dtrace_enabling_t *enab, dtrace_ecbdesc_t *ecb,
+ dtrace_probedesc_t *pd)
+{
+ dtrace_ecbdesc_t *new;
+ dtrace_predicate_t *pred;
+ dtrace_actdesc_t *act;
+
+ /*
+ * We're going to create a new ECB description that matches the
+ * specified ECB in every way, but has the specified probe description.
+ */
+ new = kmem_zalloc(sizeof (dtrace_ecbdesc_t), KM_SLEEP);
+
+ if ((pred = ecb->dted_pred.dtpdd_predicate) != NULL)
+ dtrace_predicate_hold(pred);
+
+ for (act = ecb->dted_action; act != NULL; act = act->dtad_next)
+ dtrace_actdesc_hold(act);
+
+ new->dted_action = ecb->dted_action;
+ new->dted_pred = ecb->dted_pred;
+ new->dted_probe = *pd;
+ new->dted_uarg = ecb->dted_uarg;
+
+ dtrace_enabling_add(enab, new);
+}
+
+static void
+dtrace_enabling_dump(dtrace_enabling_t *enab)
+{
+ int i;
+
+ for (i = 0; i < enab->dten_ndesc; i++) {
+ dtrace_probedesc_t *desc = &enab->dten_desc[i]->dted_probe;
+
+ cmn_err(CE_NOTE, "enabling probe %d (%s:%s:%s:%s)", i,
+ desc->dtpd_provider, desc->dtpd_mod,
+ desc->dtpd_func, desc->dtpd_name);
+ }
+}
+
+static void
+dtrace_enabling_destroy(dtrace_enabling_t *enab)
+{
+ int i;
+ dtrace_ecbdesc_t *ep;
+ dtrace_vstate_t *vstate = enab->dten_vstate;
+
+ ASSERT(MUTEX_HELD(&dtrace_lock));
+
+ for (i = 0; i < enab->dten_ndesc; i++) {
+ dtrace_actdesc_t *act, *next;
+ dtrace_predicate_t *pred;
+
+ ep = enab->dten_desc[i];
+
+ if ((pred = ep->dted_pred.dtpdd_predicate) != NULL)
+ dtrace_predicate_release(pred, vstate);
+
+ for (act = ep->dted_action; act != NULL; act = next) {
+ next = act->dtad_next;
+ dtrace_actdesc_release(act, vstate);
+ }
+
+ kmem_free(ep, sizeof (dtrace_ecbdesc_t));
+ }
+
+ if (enab->dten_desc != NULL)
+ kmem_free(enab->dten_desc,
+ enab->dten_maxdesc * sizeof (dtrace_enabling_t *));
+
+ /*
+ * If this was a retained enabling, decrement the dts_nretained count
+ * and take it off of the dtrace_retained list.
+ */
+ if (enab->dten_prev != NULL || enab->dten_next != NULL ||
+ dtrace_retained == enab) {
+ ASSERT(enab->dten_vstate->dtvs_state != NULL);
+ ASSERT(enab->dten_vstate->dtvs_state->dts_nretained > 0);
+ enab->dten_vstate->dtvs_state->dts_nretained--;
+ }
+
+ if (enab->dten_prev == NULL) {
+ if (dtrace_retained == enab) {
+ dtrace_retained = enab->dten_next;
+
+ if (dtrace_retained != NULL)
+ dtrace_retained->dten_prev = NULL;
+ }
+ } else {
+ ASSERT(enab != dtrace_retained);
+ ASSERT(dtrace_retained != NULL);
+ enab->dten_prev->dten_next = enab->dten_next;
+ }
+
+ if (enab->dten_next != NULL) {
+ ASSERT(dtrace_retained != NULL);
+ enab->dten_next->dten_prev = enab->dten_prev;
+ }
+
+ kmem_free(enab, sizeof (dtrace_enabling_t));
+}
+
+static int
+dtrace_enabling_retain(dtrace_enabling_t *enab)
+{
+ dtrace_state_t *state;
+
+ ASSERT(MUTEX_HELD(&dtrace_lock));
+ ASSERT(enab->dten_next == NULL && enab->dten_prev == NULL);
+ ASSERT(enab->dten_vstate != NULL);
+
+ state = enab->dten_vstate->dtvs_state;
+ ASSERT(state != NULL);
+
+ /*
+ * We only allow each state to retain dtrace_retain_max enablings.
+ */
+ if (state->dts_nretained >= dtrace_retain_max)
+ return (ENOSPC);
+
+ state->dts_nretained++;
+
+ if (dtrace_retained == NULL) {
+ dtrace_retained = enab;
+ return (0);
+ }
+
+ enab->dten_next = dtrace_retained;
+ dtrace_retained->dten_prev = enab;
+ dtrace_retained = enab;
+
+ return (0);
+}
+
+static int
+dtrace_enabling_replicate(dtrace_state_t *state, dtrace_probedesc_t *match,
+ dtrace_probedesc_t *create)
+{
+ dtrace_enabling_t *new, *enab;
+ int found = 0, err = ENOENT;
+
+ ASSERT(MUTEX_HELD(&dtrace_lock));
+ ASSERT(strlen(match->dtpd_provider) < DTRACE_PROVNAMELEN);
+ ASSERT(strlen(match->dtpd_mod) < DTRACE_MODNAMELEN);
+ ASSERT(strlen(match->dtpd_func) < DTRACE_FUNCNAMELEN);
+ ASSERT(strlen(match->dtpd_name) < DTRACE_NAMELEN);
+
+ new = dtrace_enabling_create(&state->dts_vstate);
+
+ /*
+ * Iterate over all retained enablings, looking for enablings that
+ * match the specified state.
+ */
+ for (enab = dtrace_retained; enab != NULL; enab = enab->dten_next) {
+ int i;
+
+ /*
+ * dtvs_state can only be NULL for helper enablings -- and
+ * helper enablings can't be retained.
+ */
+ ASSERT(enab->dten_vstate->dtvs_state != NULL);
+
+ if (enab->dten_vstate->dtvs_state != state)
+ continue;
+
+ /*
+ * Now iterate over each probe description; we're looking for
+ * an exact match to the specified probe description.
+ */
+ for (i = 0; i < enab->dten_ndesc; i++) {
+ dtrace_ecbdesc_t *ep = enab->dten_desc[i];
+ dtrace_probedesc_t *pd = &ep->dted_probe;
+
+ if (strcmp(pd->dtpd_provider, match->dtpd_provider))
+ continue;
+
+ if (strcmp(pd->dtpd_mod, match->dtpd_mod))
+ continue;
+
+ if (strcmp(pd->dtpd_func, match->dtpd_func))
+ continue;
+
+ if (strcmp(pd->dtpd_name, match->dtpd_name))
+ continue;
+
+ /*
+ * We have a winning probe! Add it to our growing
+ * enabling.
+ */
+ found = 1;
+ dtrace_enabling_addlike(new, ep, create);
+ }
+ }
+
+ if (!found || (err = dtrace_enabling_retain(new)) != 0) {
+ dtrace_enabling_destroy(new);
+ return (err);
+ }
+
+ return (0);
+}
+
+static void
+dtrace_enabling_retract(dtrace_state_t *state)
+{
+ dtrace_enabling_t *enab, *next;
+
+ ASSERT(MUTEX_HELD(&dtrace_lock));
+
+ /*
+ * Iterate over all retained enablings, destroy the enablings retained
+ * for the specified state.
+ */
+ for (enab = dtrace_retained; enab != NULL; enab = next) {
+ next = enab->dten_next;
+
+ /*
+ * dtvs_state can only be NULL for helper enablings -- and
+ * helper enablings can't be retained.
+ */
+ ASSERT(enab->dten_vstate->dtvs_state != NULL);
+
+ if (enab->dten_vstate->dtvs_state == state) {
+ ASSERT(state->dts_nretained > 0);
+ dtrace_enabling_destroy(enab);
+ }
+ }
+
+ ASSERT(state->dts_nretained == 0);
+}
+
+static int
+dtrace_enabling_match(dtrace_enabling_t *enab, int *nmatched)
+{
+ int i = 0;
+ int matched = 0;
+
+ ASSERT(MUTEX_HELD(&cpu_lock));
+ ASSERT(MUTEX_HELD(&dtrace_lock));
+
+ for (i = 0; i < enab->dten_ndesc; i++) {
+ dtrace_ecbdesc_t *ep = enab->dten_desc[i];
+
+ enab->dten_current = ep;
+ enab->dten_error = 0;
+
+ matched += dtrace_probe_enable(&ep->dted_probe, enab);
+
+ if (enab->dten_error != 0) {
+ /*
+ * If we get an error half-way through enabling the
+ * probes, we kick out -- perhaps with some number of
+ * them enabled. Leaving enabled probes enabled may
+ * be slightly confusing for user-level, but we expect
+ * that no one will attempt to actually drive on in
+ * the face of such errors. If this is an anonymous
+ * enabling (indicated with a NULL nmatched pointer),
+ * we cmn_err() a message. We aren't expecting to
+ * get such an error -- such as it can exist at all,
+ * it would be a result of corrupted DOF in the driver
+ * properties.
+ */
+ if (nmatched == NULL) {
+ cmn_err(CE_WARN, "dtrace_enabling_match() "
+ "error on %p: %d", (void *)ep,
+ enab->dten_error);
+ }
+
+ return (enab->dten_error);
+ }
+ }
+
+ enab->dten_probegen = dtrace_probegen;
+ if (nmatched != NULL)
+ *nmatched = matched;
+
+ return (0);
+}
+
+static void
+dtrace_enabling_matchall(void)
+{
+ dtrace_enabling_t *enab;
+
+ mutex_enter(&cpu_lock);
+ mutex_enter(&dtrace_lock);
+
+ /*
+ * Iterate over all retained enablings to see if any probes match
+ * against them. We only perform this operation on enablings for which
+ * we have sufficient permissions by virtue of being in the global zone
+ * or in the same zone as the DTrace client. Because we can be called
+ * after dtrace_detach() has been called, we cannot assert that there
+ * are retained enablings. We can safely load from dtrace_retained,
+ * however: the taskq_destroy() at the end of dtrace_detach() will
+ * block pending our completion.
+ */
+ for (enab = dtrace_retained; enab != NULL; enab = enab->dten_next) {
+#if defined(sun)
+ cred_t *cr = enab->dten_vstate->dtvs_state->dts_cred.dcr_cred;
+
+ if (INGLOBALZONE(curproc) || getzoneid() == crgetzoneid(cr))
+#endif
+ (void) dtrace_enabling_match(enab, NULL);
+ }
+
+ mutex_exit(&dtrace_lock);
+ mutex_exit(&cpu_lock);
+}
+
+/*
+ * If an enabling is to be enabled without having matched probes (that is, if
+ * dtrace_state_go() is to be called on the underlying dtrace_state_t), the
+ * enabling must be _primed_ by creating an ECB for every ECB description.
+ * This must be done to assure that we know the number of speculations, the
+ * number of aggregations, the minimum buffer size needed, etc. before we
+ * transition out of DTRACE_ACTIVITY_INACTIVE. To do this without actually
+ * enabling any probes, we create ECBs for every ECB decription, but with a
+ * NULL probe -- which is exactly what this function does.
+ */
+static void
+dtrace_enabling_prime(dtrace_state_t *state)
+{
+ dtrace_enabling_t *enab;
+ int i;
+
+ for (enab = dtrace_retained; enab != NULL; enab = enab->dten_next) {
+ ASSERT(enab->dten_vstate->dtvs_state != NULL);
+
+ if (enab->dten_vstate->dtvs_state != state)
+ continue;
+
+ /*
+ * We don't want to prime an enabling more than once, lest
+ * we allow a malicious user to induce resource exhaustion.
+ * (The ECBs that result from priming an enabling aren't
+ * leaked -- but they also aren't deallocated until the
+ * consumer state is destroyed.)
+ */
+ if (enab->dten_primed)
+ continue;
+
+ for (i = 0; i < enab->dten_ndesc; i++) {
+ enab->dten_current = enab->dten_desc[i];
+ (void) dtrace_probe_enable(NULL, enab);
+ }
+
+ enab->dten_primed = 1;
+ }
+}
+
+/*
+ * Called to indicate that probes should be provided due to retained
+ * enablings. This is implemented in terms of dtrace_probe_provide(), but it
+ * must take an initial lap through the enabling calling the dtps_provide()
+ * entry point explicitly to allow for autocreated probes.
+ */
+static void
+dtrace_enabling_provide(dtrace_provider_t *prv)
+{
+ int i, all = 0;
+ dtrace_probedesc_t desc;
+
+ ASSERT(MUTEX_HELD(&dtrace_lock));
+ ASSERT(MUTEX_HELD(&dtrace_provider_lock));
+
+ if (prv == NULL) {
+ all = 1;
+ prv = dtrace_provider;
+ }
+
+ do {
+ dtrace_enabling_t *enab = dtrace_retained;
+ void *parg = prv->dtpv_arg;
+
+ for (; enab != NULL; enab = enab->dten_next) {
+ for (i = 0; i < enab->dten_ndesc; i++) {
+ desc = enab->dten_desc[i]->dted_probe;
+ mutex_exit(&dtrace_lock);
+ prv->dtpv_pops.dtps_provide(parg, &desc);
+ mutex_enter(&dtrace_lock);
+ }
+ }
+ } while (all && (prv = prv->dtpv_next) != NULL);
+
+ mutex_exit(&dtrace_lock);
+ dtrace_probe_provide(NULL, all ? NULL : prv);
+ mutex_enter(&dtrace_lock);
+}
+
+/*
+ * DTrace DOF Functions
+ */
+/*ARGSUSED*/
+static void
+dtrace_dof_error(dof_hdr_t *dof, const char *str)
+{
+ if (dtrace_err_verbose)
+ cmn_err(CE_WARN, "failed to process DOF: %s", str);
+
+#ifdef DTRACE_ERRDEBUG
+ dtrace_errdebug(str);
+#endif
+}
+
+/*
+ * Create DOF out of a currently enabled state. Right now, we only create
+ * DOF containing the run-time options -- but this could be expanded to create
+ * complete DOF representing the enabled state.
+ */
+static dof_hdr_t *
+dtrace_dof_create(dtrace_state_t *state)
+{
+ dof_hdr_t *dof;
+ dof_sec_t *sec;
+ dof_optdesc_t *opt;
+ int i, len = sizeof (dof_hdr_t) +
+ roundup(sizeof (dof_sec_t), sizeof (uint64_t)) +
+ sizeof (dof_optdesc_t) * DTRACEOPT_MAX;
+
+ ASSERT(MUTEX_HELD(&dtrace_lock));
+
+ dof = kmem_zalloc(len, KM_SLEEP);
+ dof->dofh_ident[DOF_ID_MAG0] = DOF_MAG_MAG0;
+ dof->dofh_ident[DOF_ID_MAG1] = DOF_MAG_MAG1;
+ dof->dofh_ident[DOF_ID_MAG2] = DOF_MAG_MAG2;
+ dof->dofh_ident[DOF_ID_MAG3] = DOF_MAG_MAG3;
+
+ dof->dofh_ident[DOF_ID_MODEL] = DOF_MODEL_NATIVE;
+ dof->dofh_ident[DOF_ID_ENCODING] = DOF_ENCODE_NATIVE;
+ dof->dofh_ident[DOF_ID_VERSION] = DOF_VERSION;
+ dof->dofh_ident[DOF_ID_DIFVERS] = DIF_VERSION;
+ dof->dofh_ident[DOF_ID_DIFIREG] = DIF_DIR_NREGS;
+ dof->dofh_ident[DOF_ID_DIFTREG] = DIF_DTR_NREGS;
+
+ dof->dofh_flags = 0;
+ dof->dofh_hdrsize = sizeof (dof_hdr_t);
+ dof->dofh_secsize = sizeof (dof_sec_t);
+ dof->dofh_secnum = 1; /* only DOF_SECT_OPTDESC */
+ dof->dofh_secoff = sizeof (dof_hdr_t);
+ dof->dofh_loadsz = len;
+ dof->dofh_filesz = len;
+ dof->dofh_pad = 0;
+
+ /*
+ * Fill in the option section header...
+ */
+ sec = (dof_sec_t *)((uintptr_t)dof + sizeof (dof_hdr_t));
+ sec->dofs_type = DOF_SECT_OPTDESC;
+ sec->dofs_align = sizeof (uint64_t);
+ sec->dofs_flags = DOF_SECF_LOAD;
+ sec->dofs_entsize = sizeof (dof_optdesc_t);
+
+ opt = (dof_optdesc_t *)((uintptr_t)sec +
+ roundup(sizeof (dof_sec_t), sizeof (uint64_t)));
+
+ sec->dofs_offset = (uintptr_t)opt - (uintptr_t)dof;
+ sec->dofs_size = sizeof (dof_optdesc_t) * DTRACEOPT_MAX;
+
+ for (i = 0; i < DTRACEOPT_MAX; i++) {
+ opt[i].dofo_option = i;
+ opt[i].dofo_strtab = DOF_SECIDX_NONE;
+ opt[i].dofo_value = state->dts_options[i];
+ }
+
+ return (dof);
+}
+
+static dof_hdr_t *
+dtrace_dof_copyin(uintptr_t uarg, int *errp)
+{
+ dof_hdr_t hdr, *dof;
+
+ ASSERT(!MUTEX_HELD(&dtrace_lock));
+
+ /*
+ * First, we're going to copyin() the sizeof (dof_hdr_t).
+ */
+ if (copyin((void *)uarg, &hdr, sizeof (hdr)) != 0) {
+ dtrace_dof_error(NULL, "failed to copyin DOF header");
+ *errp = EFAULT;
+ return (NULL);
+ }
+
+ /*
+ * Now we'll allocate the entire DOF and copy it in -- provided
+ * that the length isn't outrageous.
+ */
+ if (hdr.dofh_loadsz >= dtrace_dof_maxsize) {
+ dtrace_dof_error(&hdr, "load size exceeds maximum");
+ *errp = E2BIG;
+ return (NULL);
+ }
+
+ if (hdr.dofh_loadsz < sizeof (hdr)) {
+ dtrace_dof_error(&hdr, "invalid load size");
+ *errp = EINVAL;
+ return (NULL);
+ }
+
+ dof = kmem_alloc(hdr.dofh_loadsz, KM_SLEEP);
+
+ if (copyin((void *)uarg, dof, hdr.dofh_loadsz) != 0) {
+ kmem_free(dof, hdr.dofh_loadsz);
+ *errp = EFAULT;
+ return (NULL);
+ }
+
+ return (dof);
+}
+
+#if !defined(sun)
+static __inline uchar_t
+dtrace_dof_char(char c) {
+ switch (c) {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ return (c - '0');
+ case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F':
+ return (c - 'A' + 10);
+ case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f':
+ return (c - 'a' + 10);
+ }
+ /* Should not reach here. */
+ return (0);
+}
+#endif
+
+static dof_hdr_t *
+dtrace_dof_property(const char *name)
+{
+ uchar_t *buf;
+ uint64_t loadsz;
+ unsigned int len, i;
+ dof_hdr_t *dof;
+
+#if defined(sun)
+ /*
+ * Unfortunately, array of values in .conf files are always (and
+ * only) interpreted to be integer arrays. We must read our DOF
+ * as an integer array, and then squeeze it into a byte array.
+ */
+ if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dtrace_devi, 0,
+ (char *)name, (int **)&buf, &len) != DDI_PROP_SUCCESS)
+ return (NULL);
+
+ for (i = 0; i < len; i++)
+ buf[i] = (uchar_t)(((int *)buf)[i]);
+
+ if (len < sizeof (dof_hdr_t)) {
+ ddi_prop_free(buf);
+ dtrace_dof_error(NULL, "truncated header");
+ return (NULL);
+ }
+
+ if (len < (loadsz = ((dof_hdr_t *)buf)->dofh_loadsz)) {
+ ddi_prop_free(buf);
+ dtrace_dof_error(NULL, "truncated DOF");
+ return (NULL);
+ }
+
+ if (loadsz >= dtrace_dof_maxsize) {
+ ddi_prop_free(buf);
+ dtrace_dof_error(NULL, "oversized DOF");
+ return (NULL);
+ }
+
+ dof = kmem_alloc(loadsz, KM_SLEEP);
+ bcopy(buf, dof, loadsz);
+ ddi_prop_free(buf);
+#else
+ char *p;
+ char *p_env;
+
+ if ((p_env = getenv(name)) == NULL)
+ return (NULL);
+
+ len = strlen(p_env) / 2;
+
+ buf = kmem_alloc(len, KM_SLEEP);
+
+ dof = (dof_hdr_t *) buf;
+
+ p = p_env;
+
+ for (i = 0; i < len; i++) {
+ buf[i] = (dtrace_dof_char(p[0]) << 4) |
+ dtrace_dof_char(p[1]);
+ p += 2;
+ }
+
+ freeenv(p_env);
+
+ if (len < sizeof (dof_hdr_t)) {
+ kmem_free(buf, 0);
+ dtrace_dof_error(NULL, "truncated header");
+ return (NULL);
+ }
+
+ if (len < (loadsz = dof->dofh_loadsz)) {
+ kmem_free(buf, 0);
+ dtrace_dof_error(NULL, "truncated DOF");
+ return (NULL);
+ }
+
+ if (loadsz >= dtrace_dof_maxsize) {
+ kmem_free(buf, 0);
+ dtrace_dof_error(NULL, "oversized DOF");
+ return (NULL);
+ }
+#endif
+
+ return (dof);
+}
+
+static void
+dtrace_dof_destroy(dof_hdr_t *dof)
+{
+ kmem_free(dof, dof->dofh_loadsz);
+}
+
+/*
+ * Return the dof_sec_t pointer corresponding to a given section index. If the
+ * index is not valid, dtrace_dof_error() is called and NULL is returned. If
+ * a type other than DOF_SECT_NONE is specified, the header is checked against
+ * this type and NULL is returned if the types do not match.
+ */
+static dof_sec_t *
+dtrace_dof_sect(dof_hdr_t *dof, uint32_t type, dof_secidx_t i)
+{
+ dof_sec_t *sec = (dof_sec_t *)(uintptr_t)
+ ((uintptr_t)dof + dof->dofh_secoff + i * dof->dofh_secsize);
+
+ if (i >= dof->dofh_secnum) {
+ dtrace_dof_error(dof, "referenced section index is invalid");
+ return (NULL);
+ }
+
+ if (!(sec->dofs_flags & DOF_SECF_LOAD)) {
+ dtrace_dof_error(dof, "referenced section is not loadable");
+ return (NULL);
+ }
+
+ if (type != DOF_SECT_NONE && type != sec->dofs_type) {
+ dtrace_dof_error(dof, "referenced section is the wrong type");
+ return (NULL);
+ }
+
+ return (sec);
+}
+
+static dtrace_probedesc_t *
+dtrace_dof_probedesc(dof_hdr_t *dof, dof_sec_t *sec, dtrace_probedesc_t *desc)
+{
+ dof_probedesc_t *probe;
+ dof_sec_t *strtab;
+ uintptr_t daddr = (uintptr_t)dof;
+ uintptr_t str;
+ size_t size;
+
+ if (sec->dofs_type != DOF_SECT_PROBEDESC) {
+ dtrace_dof_error(dof, "invalid probe section");
+ return (NULL);
+ }
+
+ if (sec->dofs_align != sizeof (dof_secidx_t)) {
+ dtrace_dof_error(dof, "bad alignment in probe description");
+ return (NULL);
+ }
+
+ if (sec->dofs_offset + sizeof (dof_probedesc_t) > dof->dofh_loadsz) {
+ dtrace_dof_error(dof, "truncated probe description");
+ return (NULL);
+ }
+
+ probe = (dof_probedesc_t *)(uintptr_t)(daddr + sec->dofs_offset);
+ strtab = dtrace_dof_sect(dof, DOF_SECT_STRTAB, probe->dofp_strtab);
+
+ if (strtab == NULL)
+ return (NULL);
+
+ str = daddr + strtab->dofs_offset;
+ size = strtab->dofs_size;
+
+ if (probe->dofp_provider >= strtab->dofs_size) {
+ dtrace_dof_error(dof, "corrupt probe provider");
+ return (NULL);
+ }
+
+ (void) strncpy(desc->dtpd_provider,
+ (char *)(str + probe->dofp_provider),
+ MIN(DTRACE_PROVNAMELEN - 1, size - probe->dofp_provider));
+
+ if (probe->dofp_mod >= strtab->dofs_size) {
+ dtrace_dof_error(dof, "corrupt probe module");
+ return (NULL);
+ }
+
+ (void) strncpy(desc->dtpd_mod, (char *)(str + probe->dofp_mod),
+ MIN(DTRACE_MODNAMELEN - 1, size - probe->dofp_mod));
+
+ if (probe->dofp_func >= strtab->dofs_size) {
+ dtrace_dof_error(dof, "corrupt probe function");
+ return (NULL);
+ }
+
+ (void) strncpy(desc->dtpd_func, (char *)(str + probe->dofp_func),
+ MIN(DTRACE_FUNCNAMELEN - 1, size - probe->dofp_func));
+
+ if (probe->dofp_name >= strtab->dofs_size) {
+ dtrace_dof_error(dof, "corrupt probe name");
+ return (NULL);
+ }
+
+ (void) strncpy(desc->dtpd_name, (char *)(str + probe->dofp_name),
+ MIN(DTRACE_NAMELEN - 1, size - probe->dofp_name));
+
+ return (desc);
+}
+
+static dtrace_difo_t *
+dtrace_dof_difo(dof_hdr_t *dof, dof_sec_t *sec, dtrace_vstate_t *vstate,
+ cred_t *cr)
+{
+ dtrace_difo_t *dp;
+ size_t ttl = 0;
+ dof_difohdr_t *dofd;
+ uintptr_t daddr = (uintptr_t)dof;
+ size_t max = dtrace_difo_maxsize;
+ int i, l, n;
+
+ static const struct {
+ int section;
+ int bufoffs;
+ int lenoffs;
+ int entsize;
+ int align;
+ const char *msg;
+ } difo[] = {
+ { DOF_SECT_DIF, offsetof(dtrace_difo_t, dtdo_buf),
+ offsetof(dtrace_difo_t, dtdo_len), sizeof (dif_instr_t),
+ sizeof (dif_instr_t), "multiple DIF sections" },
+
+ { DOF_SECT_INTTAB, offsetof(dtrace_difo_t, dtdo_inttab),
+ offsetof(dtrace_difo_t, dtdo_intlen), sizeof (uint64_t),
+ sizeof (uint64_t), "multiple integer tables" },
+
+ { DOF_SECT_STRTAB, offsetof(dtrace_difo_t, dtdo_strtab),
+ offsetof(dtrace_difo_t, dtdo_strlen), 0,
+ sizeof (char), "multiple string tables" },
+
+ { DOF_SECT_VARTAB, offsetof(dtrace_difo_t, dtdo_vartab),
+ offsetof(dtrace_difo_t, dtdo_varlen), sizeof (dtrace_difv_t),
+ sizeof (uint_t), "multiple variable tables" },
+
+ { DOF_SECT_NONE, 0, 0, 0, 0, NULL }
+ };
+
+ if (sec->dofs_type != DOF_SECT_DIFOHDR) {
+ dtrace_dof_error(dof, "invalid DIFO header section");
+ return (NULL);
+ }
+
+ if (sec->dofs_align != sizeof (dof_secidx_t)) {
+ dtrace_dof_error(dof, "bad alignment in DIFO header");
+ return (NULL);
+ }
+
+ if (sec->dofs_size < sizeof (dof_difohdr_t) ||
+ sec->dofs_size % sizeof (dof_secidx_t)) {
+ dtrace_dof_error(dof, "bad size in DIFO header");
+ return (NULL);
+ }
+
+ dofd = (dof_difohdr_t *)(uintptr_t)(daddr + sec->dofs_offset);
+ n = (sec->dofs_size - sizeof (*dofd)) / sizeof (dof_secidx_t) + 1;
+
+ dp = kmem_zalloc(sizeof (dtrace_difo_t), KM_SLEEP);
+ dp->dtdo_rtype = dofd->dofd_rtype;
+
+ for (l = 0; l < n; l++) {
+ dof_sec_t *subsec;
+ void **bufp;
+ uint32_t *lenp;
+
+ if ((subsec = dtrace_dof_sect(dof, DOF_SECT_NONE,
+ dofd->dofd_links[l])) == NULL)
+ goto err; /* invalid section link */
+
+ if (ttl + subsec->dofs_size > max) {
+ dtrace_dof_error(dof, "exceeds maximum size");
+ goto err;
+ }
+
+ ttl += subsec->dofs_size;
+
+ for (i = 0; difo[i].section != DOF_SECT_NONE; i++) {
+ if (subsec->dofs_type != difo[i].section)
+ continue;
+
+ if (!(subsec->dofs_flags & DOF_SECF_LOAD)) {
+ dtrace_dof_error(dof, "section not loaded");
+ goto err;
+ }
+
+ if (subsec->dofs_align != difo[i].align) {
+ dtrace_dof_error(dof, "bad alignment");
+ goto err;
+ }
+
+ bufp = (void **)((uintptr_t)dp + difo[i].bufoffs);
+ lenp = (uint32_t *)((uintptr_t)dp + difo[i].lenoffs);
+
+ if (*bufp != NULL) {
+ dtrace_dof_error(dof, difo[i].msg);
+ goto err;
+ }
+
+ if (difo[i].entsize != subsec->dofs_entsize) {
+ dtrace_dof_error(dof, "entry size mismatch");
+ goto err;
+ }
+
+ if (subsec->dofs_entsize != 0 &&
+ (subsec->dofs_size % subsec->dofs_entsize) != 0) {
+ dtrace_dof_error(dof, "corrupt entry size");
+ goto err;
+ }
+
+ *lenp = subsec->dofs_size;
+ *bufp = kmem_alloc(subsec->dofs_size, KM_SLEEP);
+ bcopy((char *)(uintptr_t)(daddr + subsec->dofs_offset),
+ *bufp, subsec->dofs_size);
+
+ if (subsec->dofs_entsize != 0)
+ *lenp /= subsec->dofs_entsize;
+
+ break;
+ }
+
+ /*
+ * If we encounter a loadable DIFO sub-section that is not
+ * known to us, assume this is a broken program and fail.
+ */
+ if (difo[i].section == DOF_SECT_NONE &&
+ (subsec->dofs_flags & DOF_SECF_LOAD)) {
+ dtrace_dof_error(dof, "unrecognized DIFO subsection");
+ goto err;
+ }
+ }
+
+ if (dp->dtdo_buf == NULL) {
+ /*
+ * We can't have a DIF object without DIF text.
+ */
+ dtrace_dof_error(dof, "missing DIF text");
+ goto err;
+ }
+
+ /*
+ * Before we validate the DIF object, run through the variable table
+ * looking for the strings -- if any of their size are under, we'll set
+ * their size to be the system-wide default string size. Note that
+ * this should _not_ happen if the "strsize" option has been set --
+ * in this case, the compiler should have set the size to reflect the
+ * setting of the option.
+ */
+ for (i = 0; i < dp->dtdo_varlen; i++) {
+ dtrace_difv_t *v = &dp->dtdo_vartab[i];
+ dtrace_diftype_t *t = &v->dtdv_type;
+
+ if (v->dtdv_id < DIF_VAR_OTHER_UBASE)
+ continue;
+
+ if (t->dtdt_kind == DIF_TYPE_STRING && t->dtdt_size == 0)
+ t->dtdt_size = dtrace_strsize_default;
+ }
+
+ if (dtrace_difo_validate(dp, vstate, DIF_DIR_NREGS, cr) != 0)
+ goto err;
+
+ dtrace_difo_init(dp, vstate);
+ return (dp);
+
+err:
+ kmem_free(dp->dtdo_buf, dp->dtdo_len * sizeof (dif_instr_t));
+ kmem_free(dp->dtdo_inttab, dp->dtdo_intlen * sizeof (uint64_t));
+ kmem_free(dp->dtdo_strtab, dp->dtdo_strlen);
+ kmem_free(dp->dtdo_vartab, dp->dtdo_varlen * sizeof (dtrace_difv_t));
+
+ kmem_free(dp, sizeof (dtrace_difo_t));
+ return (NULL);
+}
+
+static dtrace_predicate_t *
+dtrace_dof_predicate(dof_hdr_t *dof, dof_sec_t *sec, dtrace_vstate_t *vstate,
+ cred_t *cr)
+{
+ dtrace_difo_t *dp;
+
+ if ((dp = dtrace_dof_difo(dof, sec, vstate, cr)) == NULL)
+ return (NULL);
+
+ return (dtrace_predicate_create(dp));
+}
+
+static dtrace_actdesc_t *
+dtrace_dof_actdesc(dof_hdr_t *dof, dof_sec_t *sec, dtrace_vstate_t *vstate,
+ cred_t *cr)
+{
+ dtrace_actdesc_t *act, *first = NULL, *last = NULL, *next;
+ dof_actdesc_t *desc;
+ dof_sec_t *difosec;
+ size_t offs;
+ uintptr_t daddr = (uintptr_t)dof;
+ uint64_t arg;
+ dtrace_actkind_t kind;
+
+ if (sec->dofs_type != DOF_SECT_ACTDESC) {
+ dtrace_dof_error(dof, "invalid action section");
+ return (NULL);
+ }
+
+ if (sec->dofs_offset + sizeof (dof_actdesc_t) > dof->dofh_loadsz) {
+ dtrace_dof_error(dof, "truncated action description");
+ return (NULL);
+ }
+
+ if (sec->dofs_align != sizeof (uint64_t)) {
+ dtrace_dof_error(dof, "bad alignment in action description");
+ return (NULL);
+ }
+
+ if (sec->dofs_size < sec->dofs_entsize) {
+ dtrace_dof_error(dof, "section entry size exceeds total size");
+ return (NULL);
+ }
+
+ if (sec->dofs_entsize != sizeof (dof_actdesc_t)) {
+ dtrace_dof_error(dof, "bad entry size in action description");
+ return (NULL);
+ }
+
+ if (sec->dofs_size / sec->dofs_entsize > dtrace_actions_max) {
+ dtrace_dof_error(dof, "actions exceed dtrace_actions_max");
+ return (NULL);
+ }
+
+ for (offs = 0; offs < sec->dofs_size; offs += sec->dofs_entsize) {
+ desc = (dof_actdesc_t *)(daddr +
+ (uintptr_t)sec->dofs_offset + offs);
+ kind = (dtrace_actkind_t)desc->dofa_kind;
+
+ if (DTRACEACT_ISPRINTFLIKE(kind) &&
+ (kind != DTRACEACT_PRINTA ||
+ desc->dofa_strtab != DOF_SECIDX_NONE)) {
+ dof_sec_t *strtab;
+ char *str, *fmt;
+ uint64_t i;
+
+ /*
+ * printf()-like actions must have a format string.
+ */
+ if ((strtab = dtrace_dof_sect(dof,
+ DOF_SECT_STRTAB, desc->dofa_strtab)) == NULL)
+ goto err;
+
+ str = (char *)((uintptr_t)dof +
+ (uintptr_t)strtab->dofs_offset);
+
+ for (i = desc->dofa_arg; i < strtab->dofs_size; i++) {
+ if (str[i] == '\0')
+ break;
+ }
+
+ if (i >= strtab->dofs_size) {
+ dtrace_dof_error(dof, "bogus format string");
+ goto err;
+ }
+
+ if (i == desc->dofa_arg) {
+ dtrace_dof_error(dof, "empty format string");
+ goto err;
+ }
+
+ i -= desc->dofa_arg;
+ fmt = kmem_alloc(i + 1, KM_SLEEP);
+ bcopy(&str[desc->dofa_arg], fmt, i + 1);
+ arg = (uint64_t)(uintptr_t)fmt;
+ } else {
+ if (kind == DTRACEACT_PRINTA) {
+ ASSERT(desc->dofa_strtab == DOF_SECIDX_NONE);
+ arg = 0;
+ } else {
+ arg = desc->dofa_arg;
+ }
+ }
+
+ act = dtrace_actdesc_create(kind, desc->dofa_ntuple,
+ desc->dofa_uarg, arg);
+
+ if (last != NULL) {
+ last->dtad_next = act;
+ } else {
+ first = act;
+ }
+
+ last = act;
+
+ if (desc->dofa_difo == DOF_SECIDX_NONE)
+ continue;
+
+ if ((difosec = dtrace_dof_sect(dof,
+ DOF_SECT_DIFOHDR, desc->dofa_difo)) == NULL)
+ goto err;
+
+ act->dtad_difo = dtrace_dof_difo(dof, difosec, vstate, cr);
+
+ if (act->dtad_difo == NULL)
+ goto err;
+ }
+
+ ASSERT(first != NULL);
+ return (first);
+
+err:
+ for (act = first; act != NULL; act = next) {
+ next = act->dtad_next;
+ dtrace_actdesc_release(act, vstate);
+ }
+
+ return (NULL);
+}
+
+static dtrace_ecbdesc_t *
+dtrace_dof_ecbdesc(dof_hdr_t *dof, dof_sec_t *sec, dtrace_vstate_t *vstate,
+ cred_t *cr)
+{
+ dtrace_ecbdesc_t *ep;
+ dof_ecbdesc_t *ecb;
+ dtrace_probedesc_t *desc;
+ dtrace_predicate_t *pred = NULL;
+
+ if (sec->dofs_size < sizeof (dof_ecbdesc_t)) {
+ dtrace_dof_error(dof, "truncated ECB description");
+ return (NULL);
+ }
+
+ if (sec->dofs_align != sizeof (uint64_t)) {
+ dtrace_dof_error(dof, "bad alignment in ECB description");
+ return (NULL);
+ }
+
+ ecb = (dof_ecbdesc_t *)((uintptr_t)dof + (uintptr_t)sec->dofs_offset);
+ sec = dtrace_dof_sect(dof, DOF_SECT_PROBEDESC, ecb->dofe_probes);
+
+ if (sec == NULL)
+ return (NULL);
+
+ ep = kmem_zalloc(sizeof (dtrace_ecbdesc_t), KM_SLEEP);
+ ep->dted_uarg = ecb->dofe_uarg;
+ desc = &ep->dted_probe;
+
+ if (dtrace_dof_probedesc(dof, sec, desc) == NULL)
+ goto err;
+
+ if (ecb->dofe_pred != DOF_SECIDX_NONE) {
+ if ((sec = dtrace_dof_sect(dof,
+ DOF_SECT_DIFOHDR, ecb->dofe_pred)) == NULL)
+ goto err;
+
+ if ((pred = dtrace_dof_predicate(dof, sec, vstate, cr)) == NULL)
+ goto err;
+
+ ep->dted_pred.dtpdd_predicate = pred;
+ }
+
+ if (ecb->dofe_actions != DOF_SECIDX_NONE) {
+ if ((sec = dtrace_dof_sect(dof,
+ DOF_SECT_ACTDESC, ecb->dofe_actions)) == NULL)
+ goto err;
+
+ ep->dted_action = dtrace_dof_actdesc(dof, sec, vstate, cr);
+
+ if (ep->dted_action == NULL)
+ goto err;
+ }
+
+ return (ep);
+
+err:
+ if (pred != NULL)
+ dtrace_predicate_release(pred, vstate);
+ kmem_free(ep, sizeof (dtrace_ecbdesc_t));
+ return (NULL);
+}
+
+/*
+ * Apply the relocations from the specified 'sec' (a DOF_SECT_URELHDR) to the
+ * specified DOF. At present, this amounts to simply adding 'ubase' to the
+ * site of any user SETX relocations to account for load object base address.
+ * In the future, if we need other relocations, this function can be extended.
+ */
+static int
+dtrace_dof_relocate(dof_hdr_t *dof, dof_sec_t *sec, uint64_t ubase)
+{
+ uintptr_t daddr = (uintptr_t)dof;
+ dof_relohdr_t *dofr =
+ (dof_relohdr_t *)(uintptr_t)(daddr + sec->dofs_offset);
+ dof_sec_t *ss, *rs, *ts;
+ dof_relodesc_t *r;
+ uint_t i, n;
+
+ if (sec->dofs_size < sizeof (dof_relohdr_t) ||
+ sec->dofs_align != sizeof (dof_secidx_t)) {
+ dtrace_dof_error(dof, "invalid relocation header");
+ return (-1);
+ }
+
+ ss = dtrace_dof_sect(dof, DOF_SECT_STRTAB, dofr->dofr_strtab);
+ rs = dtrace_dof_sect(dof, DOF_SECT_RELTAB, dofr->dofr_relsec);
+ ts = dtrace_dof_sect(dof, DOF_SECT_NONE, dofr->dofr_tgtsec);
+
+ if (ss == NULL || rs == NULL || ts == NULL)
+ return (-1); /* dtrace_dof_error() has been called already */
+
+ if (rs->dofs_entsize < sizeof (dof_relodesc_t) ||
+ rs->dofs_align != sizeof (uint64_t)) {
+ dtrace_dof_error(dof, "invalid relocation section");
+ return (-1);
+ }
+
+ r = (dof_relodesc_t *)(uintptr_t)(daddr + rs->dofs_offset);
+ n = rs->dofs_size / rs->dofs_entsize;
+
+ for (i = 0; i < n; i++) {
+ uintptr_t taddr = daddr + ts->dofs_offset + r->dofr_offset;
+
+ switch (r->dofr_type) {
+ case DOF_RELO_NONE:
+ break;
+ case DOF_RELO_SETX:
+ if (r->dofr_offset >= ts->dofs_size || r->dofr_offset +
+ sizeof (uint64_t) > ts->dofs_size) {
+ dtrace_dof_error(dof, "bad relocation offset");
+ return (-1);
+ }
+
+ if (!IS_P2ALIGNED(taddr, sizeof (uint64_t))) {
+ dtrace_dof_error(dof, "misaligned setx relo");
+ return (-1);
+ }
+
+ *(uint64_t *)taddr += ubase;
+ break;
+ default:
+ dtrace_dof_error(dof, "invalid relocation type");
+ return (-1);
+ }
+
+ r = (dof_relodesc_t *)((uintptr_t)r + rs->dofs_entsize);
+ }
+
+ return (0);
+}
+
+/*
+ * The dof_hdr_t passed to dtrace_dof_slurp() should be a partially validated
+ * header: it should be at the front of a memory region that is at least
+ * sizeof (dof_hdr_t) in size -- and then at least dof_hdr.dofh_loadsz in
+ * size. It need not be validated in any other way.
+ */
+static int
+dtrace_dof_slurp(dof_hdr_t *dof, dtrace_vstate_t *vstate, cred_t *cr,
+ dtrace_enabling_t **enabp, uint64_t ubase, int noprobes)
+{
+ uint64_t len = dof->dofh_loadsz, seclen;
+ uintptr_t daddr = (uintptr_t)dof;
+ dtrace_ecbdesc_t *ep;
+ dtrace_enabling_t *enab;
+ uint_t i;
+
+ ASSERT(MUTEX_HELD(&dtrace_lock));
+ ASSERT(dof->dofh_loadsz >= sizeof (dof_hdr_t));
+
+ /*
+ * Check the DOF header identification bytes. In addition to checking
+ * valid settings, we also verify that unused bits/bytes are zeroed so
+ * we can use them later without fear of regressing existing binaries.
+ */
+ if (bcmp(&dof->dofh_ident[DOF_ID_MAG0],
+ DOF_MAG_STRING, DOF_MAG_STRLEN) != 0) {
+ dtrace_dof_error(dof, "DOF magic string mismatch");
+ return (-1);
+ }
+
+ if (dof->dofh_ident[DOF_ID_MODEL] != DOF_MODEL_ILP32 &&
+ dof->dofh_ident[DOF_ID_MODEL] != DOF_MODEL_LP64) {
+ dtrace_dof_error(dof, "DOF has invalid data model");
+ return (-1);
+ }
+
+ if (dof->dofh_ident[DOF_ID_ENCODING] != DOF_ENCODE_NATIVE) {
+ dtrace_dof_error(dof, "DOF encoding mismatch");
+ return (-1);
+ }
+
+ if (dof->dofh_ident[DOF_ID_VERSION] != DOF_VERSION_1 &&
+ dof->dofh_ident[DOF_ID_VERSION] != DOF_VERSION_2) {
+ dtrace_dof_error(dof, "DOF version mismatch");
+ return (-1);
+ }
+
+ if (dof->dofh_ident[DOF_ID_DIFVERS] != DIF_VERSION_2) {
+ dtrace_dof_error(dof, "DOF uses unsupported instruction set");
+ return (-1);
+ }
+
+ if (dof->dofh_ident[DOF_ID_DIFIREG] > DIF_DIR_NREGS) {
+ dtrace_dof_error(dof, "DOF uses too many integer registers");
+ return (-1);
+ }
+
+ if (dof->dofh_ident[DOF_ID_DIFTREG] > DIF_DTR_NREGS) {
+ dtrace_dof_error(dof, "DOF uses too many tuple registers");
+ return (-1);
+ }
+
+ for (i = DOF_ID_PAD; i < DOF_ID_SIZE; i++) {
+ if (dof->dofh_ident[i] != 0) {
+ dtrace_dof_error(dof, "DOF has invalid ident byte set");
+ return (-1);
+ }
+ }
+
+ if (dof->dofh_flags & ~DOF_FL_VALID) {
+ dtrace_dof_error(dof, "DOF has invalid flag bits set");
+ return (-1);
+ }
+
+ if (dof->dofh_secsize == 0) {
+ dtrace_dof_error(dof, "zero section header size");
+ return (-1);
+ }
+
+ /*
+ * Check that the section headers don't exceed the amount of DOF
+ * data. Note that we cast the section size and number of sections
+ * to uint64_t's to prevent possible overflow in the multiplication.
+ */
+ seclen = (uint64_t)dof->dofh_secnum * (uint64_t)dof->dofh_secsize;
+
+ if (dof->dofh_secoff > len || seclen > len ||
+ dof->dofh_secoff + seclen > len) {
+ dtrace_dof_error(dof, "truncated section headers");
+ return (-1);
+ }
+
+ if (!IS_P2ALIGNED(dof->dofh_secoff, sizeof (uint64_t))) {
+ dtrace_dof_error(dof, "misaligned section headers");
+ return (-1);
+ }
+
+ if (!IS_P2ALIGNED(dof->dofh_secsize, sizeof (uint64_t))) {
+ dtrace_dof_error(dof, "misaligned section size");
+ return (-1);
+ }
+
+ /*
+ * Take an initial pass through the section headers to be sure that
+ * the headers don't have stray offsets. If the 'noprobes' flag is
+ * set, do not permit sections relating to providers, probes, or args.
+ */
+ for (i = 0; i < dof->dofh_secnum; i++) {
+ dof_sec_t *sec = (dof_sec_t *)(daddr +
+ (uintptr_t)dof->dofh_secoff + i * dof->dofh_secsize);
+
+ if (noprobes) {
+ switch (sec->dofs_type) {
+ case DOF_SECT_PROVIDER:
+ case DOF_SECT_PROBES:
+ case DOF_SECT_PRARGS:
+ case DOF_SECT_PROFFS:
+ dtrace_dof_error(dof, "illegal sections "
+ "for enabling");
+ return (-1);
+ }
+ }
+
+ if (!(sec->dofs_flags & DOF_SECF_LOAD))
+ continue; /* just ignore non-loadable sections */
+
+ if (sec->dofs_align & (sec->dofs_align - 1)) {
+ dtrace_dof_error(dof, "bad section alignment");
+ return (-1);
+ }
+
+ if (sec->dofs_offset & (sec->dofs_align - 1)) {
+ dtrace_dof_error(dof, "misaligned section");
+ return (-1);
+ }
+
+ if (sec->dofs_offset > len || sec->dofs_size > len ||
+ sec->dofs_offset + sec->dofs_size > len) {
+ dtrace_dof_error(dof, "corrupt section header");
+ return (-1);
+ }
+
+ if (sec->dofs_type == DOF_SECT_STRTAB && *((char *)daddr +
+ sec->dofs_offset + sec->dofs_size - 1) != '\0') {
+ dtrace_dof_error(dof, "non-terminating string table");
+ return (-1);
+ }
+ }
+
+ /*
+ * Take a second pass through the sections and locate and perform any
+ * relocations that are present. We do this after the first pass to
+ * be sure that all sections have had their headers validated.
+ */
+ for (i = 0; i < dof->dofh_secnum; i++) {
+ dof_sec_t *sec = (dof_sec_t *)(daddr +
+ (uintptr_t)dof->dofh_secoff + i * dof->dofh_secsize);
+
+ if (!(sec->dofs_flags & DOF_SECF_LOAD))
+ continue; /* skip sections that are not loadable */
+
+ switch (sec->dofs_type) {
+ case DOF_SECT_URELHDR:
+ if (dtrace_dof_relocate(dof, sec, ubase) != 0)
+ return (-1);
+ break;
+ }
+ }
+
+ if ((enab = *enabp) == NULL)
+ enab = *enabp = dtrace_enabling_create(vstate);
+
+ for (i = 0; i < dof->dofh_secnum; i++) {
+ dof_sec_t *sec = (dof_sec_t *)(daddr +
+ (uintptr_t)dof->dofh_secoff + i * dof->dofh_secsize);
+
+ if (sec->dofs_type != DOF_SECT_ECBDESC)
+ continue;
+
+ if ((ep = dtrace_dof_ecbdesc(dof, sec, vstate, cr)) == NULL) {
+ dtrace_enabling_destroy(enab);
+ *enabp = NULL;
+ return (-1);
+ }
+
+ dtrace_enabling_add(enab, ep);
+ }
+
+ return (0);
+}
+
+/*
+ * Process DOF for any options. This routine assumes that the DOF has been
+ * at least processed by dtrace_dof_slurp().
+ */
+static int
+dtrace_dof_options(dof_hdr_t *dof, dtrace_state_t *state)
+{
+ int i, rval;
+ uint32_t entsize;
+ size_t offs;
+ dof_optdesc_t *desc;
+
+ for (i = 0; i < dof->dofh_secnum; i++) {
+ dof_sec_t *sec = (dof_sec_t *)((uintptr_t)dof +
+ (uintptr_t)dof->dofh_secoff + i * dof->dofh_secsize);
+
+ if (sec->dofs_type != DOF_SECT_OPTDESC)
+ continue;
+
+ if (sec->dofs_align != sizeof (uint64_t)) {
+ dtrace_dof_error(dof, "bad alignment in "
+ "option description");
+ return (EINVAL);
+ }
+
+ if ((entsize = sec->dofs_entsize) == 0) {
+ dtrace_dof_error(dof, "zeroed option entry size");
+ return (EINVAL);
+ }
+
+ if (entsize < sizeof (dof_optdesc_t)) {
+ dtrace_dof_error(dof, "bad option entry size");
+ return (EINVAL);
+ }
+
+ for (offs = 0; offs < sec->dofs_size; offs += entsize) {
+ desc = (dof_optdesc_t *)((uintptr_t)dof +
+ (uintptr_t)sec->dofs_offset + offs);
+
+ if (desc->dofo_strtab != DOF_SECIDX_NONE) {
+ dtrace_dof_error(dof, "non-zero option string");
+ return (EINVAL);
+ }
+
+ if (desc->dofo_value == DTRACEOPT_UNSET) {
+ dtrace_dof_error(dof, "unset option");
+ return (EINVAL);
+ }
+
+ if ((rval = dtrace_state_option(state,
+ desc->dofo_option, desc->dofo_value)) != 0) {
+ dtrace_dof_error(dof, "rejected option");
+ return (rval);
+ }
+ }
+ }
+
+ return (0);
+}
+
+/*
+ * DTrace Consumer State Functions
+ */
+static int
+dtrace_dstate_init(dtrace_dstate_t *dstate, size_t size)
+{
+ size_t hashsize, maxper, min, chunksize = dstate->dtds_chunksize;
+ void *base;
+ uintptr_t limit;
+ dtrace_dynvar_t *dvar, *next, *start;
+ int i;
+
+ ASSERT(MUTEX_HELD(&dtrace_lock));
+ ASSERT(dstate->dtds_base == NULL && dstate->dtds_percpu == NULL);
+
+ bzero(dstate, sizeof (dtrace_dstate_t));
+
+ if ((dstate->dtds_chunksize = chunksize) == 0)
+ dstate->dtds_chunksize = DTRACE_DYNVAR_CHUNKSIZE;
+
+ if (size < (min = dstate->dtds_chunksize + sizeof (dtrace_dynhash_t)))
+ size = min;
+
+ if ((base = kmem_zalloc(size, KM_NOSLEEP)) == NULL)
+ return (ENOMEM);
+
+ dstate->dtds_size = size;
+ dstate->dtds_base = base;
+ dstate->dtds_percpu = kmem_cache_alloc(dtrace_state_cache, KM_SLEEP);
+ bzero(dstate->dtds_percpu, NCPU * sizeof (dtrace_dstate_percpu_t));
+
+ hashsize = size / (dstate->dtds_chunksize + sizeof (dtrace_dynhash_t));
+
+ if (hashsize != 1 && (hashsize & 1))
+ hashsize--;
+
+ dstate->dtds_hashsize = hashsize;
+ dstate->dtds_hash = dstate->dtds_base;
+
+ /*
+ * Set all of our hash buckets to point to the single sink, and (if
+ * it hasn't already been set), set the sink's hash value to be the
+ * sink sentinel value. The sink is needed for dynamic variable
+ * lookups to know that they have iterated over an entire, valid hash
+ * chain.
+ */
+ for (i = 0; i < hashsize; i++)
+ dstate->dtds_hash[i].dtdh_chain = &dtrace_dynhash_sink;
+
+ if (dtrace_dynhash_sink.dtdv_hashval != DTRACE_DYNHASH_SINK)
+ dtrace_dynhash_sink.dtdv_hashval = DTRACE_DYNHASH_SINK;
+
+ /*
+ * Determine number of active CPUs. Divide free list evenly among
+ * active CPUs.
+ */
+ start = (dtrace_dynvar_t *)
+ ((uintptr_t)base + hashsize * sizeof (dtrace_dynhash_t));
+ limit = (uintptr_t)base + size;
+
+ maxper = (limit - (uintptr_t)start) / NCPU;
+ maxper = (maxper / dstate->dtds_chunksize) * dstate->dtds_chunksize;
+
+ for (i = 0; i < NCPU; i++) {
+#if !defined(sun)
+ if (CPU_ABSENT(i))
+ continue;
+#endif
+ dstate->dtds_percpu[i].dtdsc_free = dvar = start;
+
+ /*
+ * If we don't even have enough chunks to make it once through
+ * NCPUs, we're just going to allocate everything to the first
+ * CPU. And if we're on the last CPU, we're going to allocate
+ * whatever is left over. In either case, we set the limit to
+ * be the limit of the dynamic variable space.
+ */
+ if (maxper == 0 || i == NCPU - 1) {
+ limit = (uintptr_t)base + size;
+ start = NULL;
+ } else {
+ limit = (uintptr_t)start + maxper;
+ start = (dtrace_dynvar_t *)limit;
+ }
+
+ ASSERT(limit <= (uintptr_t)base + size);
+
+ for (;;) {
+ next = (dtrace_dynvar_t *)((uintptr_t)dvar +
+ dstate->dtds_chunksize);
+
+ if ((uintptr_t)next + dstate->dtds_chunksize >= limit)
+ break;
+
+ dvar->dtdv_next = next;
+ dvar = next;
+ }
+
+ if (maxper == 0)
+ break;
+ }
+
+ return (0);
+}
+
+static void
+dtrace_dstate_fini(dtrace_dstate_t *dstate)
+{
+ ASSERT(MUTEX_HELD(&cpu_lock));
+
+ if (dstate->dtds_base == NULL)
+ return;
+
+ kmem_free(dstate->dtds_base, dstate->dtds_size);
+ kmem_cache_free(dtrace_state_cache, dstate->dtds_percpu);
+}
+
+static void
+dtrace_vstate_fini(dtrace_vstate_t *vstate)
+{
+ /*
+ * Logical XOR, where are you?
+ */
+ ASSERT((vstate->dtvs_nglobals == 0) ^ (vstate->dtvs_globals != NULL));
+
+ if (vstate->dtvs_nglobals > 0) {
+ kmem_free(vstate->dtvs_globals, vstate->dtvs_nglobals *
+ sizeof (dtrace_statvar_t *));
+ }
+
+ if (vstate->dtvs_ntlocals > 0) {
+ kmem_free(vstate->dtvs_tlocals, vstate->dtvs_ntlocals *
+ sizeof (dtrace_difv_t));
+ }
+
+ ASSERT((vstate->dtvs_nlocals == 0) ^ (vstate->dtvs_locals != NULL));
+
+ if (vstate->dtvs_nlocals > 0) {
+ kmem_free(vstate->dtvs_locals, vstate->dtvs_nlocals *
+ sizeof (dtrace_statvar_t *));
+ }
+}
+
+#if defined(sun)
+static void
+dtrace_state_clean(dtrace_state_t *state)
+{
+ if (state->dts_activity == DTRACE_ACTIVITY_INACTIVE)
+ return;
+
+ dtrace_dynvar_clean(&state->dts_vstate.dtvs_dynvars);
+ dtrace_speculation_clean(state);
+}
+
+static void
+dtrace_state_deadman(dtrace_state_t *state)
+{
+ hrtime_t now;
+
+ dtrace_sync();
+
+ now = dtrace_gethrtime();
+
+ if (state != dtrace_anon.dta_state &&
+ now - state->dts_laststatus >= dtrace_deadman_user)
+ return;
+
+ /*
+ * We must be sure that dts_alive never appears to be less than the
+ * value upon entry to dtrace_state_deadman(), and because we lack a
+ * dtrace_cas64(), we cannot store to it atomically. We thus instead
+ * store INT64_MAX to it, followed by a memory barrier, followed by
+ * the new value. This assures that dts_alive never appears to be
+ * less than its true value, regardless of the order in which the
+ * stores to the underlying storage are issued.
+ */
+ state->dts_alive = INT64_MAX;
+ dtrace_membar_producer();
+ state->dts_alive = now;
+}
+#else
+static void
+dtrace_state_clean(void *arg)
+{
+ dtrace_state_t *state = arg;
+ dtrace_optval_t *opt = state->dts_options;
+
+ if (state->dts_activity == DTRACE_ACTIVITY_INACTIVE)
+ return;
+
+ dtrace_dynvar_clean(&state->dts_vstate.dtvs_dynvars);
+ dtrace_speculation_clean(state);
+
+ callout_reset(&state->dts_cleaner, hz * opt[DTRACEOPT_CLEANRATE] / NANOSEC,
+ dtrace_state_clean, state);
+}
+
+static void
+dtrace_state_deadman(void *arg)
+{
+ dtrace_state_t *state = arg;
+ hrtime_t now;
+
+ dtrace_sync();
+
+ dtrace_debug_output();
+
+ now = dtrace_gethrtime();
+
+ if (state != dtrace_anon.dta_state &&
+ now - state->dts_laststatus >= dtrace_deadman_user)
+ return;
+
+ /*
+ * We must be sure that dts_alive never appears to be less than the
+ * value upon entry to dtrace_state_deadman(), and because we lack a
+ * dtrace_cas64(), we cannot store to it atomically. We thus instead
+ * store INT64_MAX to it, followed by a memory barrier, followed by
+ * the new value. This assures that dts_alive never appears to be
+ * less than its true value, regardless of the order in which the
+ * stores to the underlying storage are issued.
+ */
+ state->dts_alive = INT64_MAX;
+ dtrace_membar_producer();
+ state->dts_alive = now;
+
+ callout_reset(&state->dts_deadman, hz * dtrace_deadman_interval / NANOSEC,
+ dtrace_state_deadman, state);
+}
+#endif
+
+static dtrace_state_t *
+#if defined(sun)
+dtrace_state_create(dev_t *devp, cred_t *cr)
+#else
+dtrace_state_create(struct cdev *dev)
+#endif
+{
+#if defined(sun)
+ minor_t minor;
+ major_t major;
+#else
+ cred_t *cr = NULL;
+ int m = 0;
+#endif
+ char c[30];
+ dtrace_state_t *state;
+ dtrace_optval_t *opt;
+ int bufsize = NCPU * sizeof (dtrace_buffer_t), i;
+
+ ASSERT(MUTEX_HELD(&dtrace_lock));
+ ASSERT(MUTEX_HELD(&cpu_lock));
+
+#if defined(sun)
+ minor = (minor_t)(uintptr_t)vmem_alloc(dtrace_minor, 1,
+ VM_BESTFIT | VM_SLEEP);
+
+ if (ddi_soft_state_zalloc(dtrace_softstate, minor) != DDI_SUCCESS) {
+ vmem_free(dtrace_minor, (void *)(uintptr_t)minor, 1);
+ return (NULL);
+ }
+
+ state = ddi_get_soft_state(dtrace_softstate, minor);
+#else
+ if (dev != NULL) {
+ cr = dev->si_cred;
+ m = minor(dev);
+ }
+
+ /* Allocate memory for the state. */
+ state = kmem_zalloc(sizeof(dtrace_state_t), KM_SLEEP);
+#endif
+
+ state->dts_epid = DTRACE_EPIDNONE + 1;
+
+ (void) snprintf(c, sizeof (c), "dtrace_aggid_%d", m);
+#if defined(sun)
+ state->dts_aggid_arena = vmem_create(c, (void *)1, UINT32_MAX, 1,
+ NULL, NULL, NULL, 0, VM_SLEEP | VMC_IDENTIFIER);
+
+ if (devp != NULL) {
+ major = getemajor(*devp);
+ } else {
+ major = ddi_driver_major(dtrace_devi);
+ }
+
+ state->dts_dev = makedevice(major, minor);
+
+ if (devp != NULL)
+ *devp = state->dts_dev;
+#else
+ state->dts_aggid_arena = new_unrhdr(1, INT_MAX, &dtrace_unr_mtx);
+ state->dts_dev = dev;
+#endif
+
+ /*
+ * We allocate NCPU buffers. On the one hand, this can be quite
+ * a bit of memory per instance (nearly 36K on a Starcat). On the
+ * other hand, it saves an additional memory reference in the probe
+ * path.
+ */
+ state->dts_buffer = kmem_zalloc(bufsize, KM_SLEEP);
+ state->dts_aggbuffer = kmem_zalloc(bufsize, KM_SLEEP);
+
+#if defined(sun)
+ state->dts_cleaner = CYCLIC_NONE;
+ state->dts_deadman = CYCLIC_NONE;
+#else
+ callout_init(&state->dts_cleaner, CALLOUT_MPSAFE);
+ callout_init(&state->dts_deadman, CALLOUT_MPSAFE);
+#endif
+ state->dts_vstate.dtvs_state = state;
+
+ for (i = 0; i < DTRACEOPT_MAX; i++)
+ state->dts_options[i] = DTRACEOPT_UNSET;
+
+ /*
+ * Set the default options.
+ */
+ opt = state->dts_options;
+ opt[DTRACEOPT_BUFPOLICY] = DTRACEOPT_BUFPOLICY_SWITCH;
+ opt[DTRACEOPT_BUFRESIZE] = DTRACEOPT_BUFRESIZE_AUTO;
+ opt[DTRACEOPT_NSPEC] = dtrace_nspec_default;
+ opt[DTRACEOPT_SPECSIZE] = dtrace_specsize_default;
+ opt[DTRACEOPT_CPU] = (dtrace_optval_t)DTRACE_CPUALL;
+ opt[DTRACEOPT_STRSIZE] = dtrace_strsize_default;
+ opt[DTRACEOPT_STACKFRAMES] = dtrace_stackframes_default;
+ opt[DTRACEOPT_USTACKFRAMES] = dtrace_ustackframes_default;
+ opt[DTRACEOPT_CLEANRATE] = dtrace_cleanrate_default;
+ opt[DTRACEOPT_AGGRATE] = dtrace_aggrate_default;
+ opt[DTRACEOPT_SWITCHRATE] = dtrace_switchrate_default;
+ opt[DTRACEOPT_STATUSRATE] = dtrace_statusrate_default;
+ opt[DTRACEOPT_JSTACKFRAMES] = dtrace_jstackframes_default;
+ opt[DTRACEOPT_JSTACKSTRSIZE] = dtrace_jstackstrsize_default;
+
+ state->dts_activity = DTRACE_ACTIVITY_INACTIVE;
+
+ /*
+ * Depending on the user credentials, we set flag bits which alter probe
+ * visibility or the amount of destructiveness allowed. In the case of
+ * actual anonymous tracing, or the possession of all privileges, all of
+ * the normal checks are bypassed.
+ */
+ if (cr == NULL || PRIV_POLICY_ONLY(cr, PRIV_ALL, B_FALSE)) {
+ state->dts_cred.dcr_visible = DTRACE_CRV_ALL;
+ state->dts_cred.dcr_action = DTRACE_CRA_ALL;
+ } else {
+ /*
+ * Set up the credentials for this instantiation. We take a
+ * hold on the credential to prevent it from disappearing on
+ * us; this in turn prevents the zone_t referenced by this
+ * credential from disappearing. This means that we can
+ * examine the credential and the zone from probe context.
+ */
+ crhold(cr);
+ state->dts_cred.dcr_cred = cr;
+
+ /*
+ * CRA_PROC means "we have *some* privilege for dtrace" and
+ * unlocks the use of variables like pid, zonename, etc.
+ */
+ if (PRIV_POLICY_ONLY(cr, PRIV_DTRACE_USER, B_FALSE) ||
+ PRIV_POLICY_ONLY(cr, PRIV_DTRACE_PROC, B_FALSE)) {
+ state->dts_cred.dcr_action |= DTRACE_CRA_PROC;
+ }
+
+ /*
+ * dtrace_user allows use of syscall and profile providers.
+ * If the user also has proc_owner and/or proc_zone, we
+ * extend the scope to include additional visibility and
+ * destructive power.
+ */
+ if (PRIV_POLICY_ONLY(cr, PRIV_DTRACE_USER, B_FALSE)) {
+ if (PRIV_POLICY_ONLY(cr, PRIV_PROC_OWNER, B_FALSE)) {
+ state->dts_cred.dcr_visible |=
+ DTRACE_CRV_ALLPROC;
+
+ state->dts_cred.dcr_action |=
+ DTRACE_CRA_PROC_DESTRUCTIVE_ALLUSER;
+ }
+
+ if (PRIV_POLICY_ONLY(cr, PRIV_PROC_ZONE, B_FALSE)) {
+ state->dts_cred.dcr_visible |=
+ DTRACE_CRV_ALLZONE;
+
+ state->dts_cred.dcr_action |=
+ DTRACE_CRA_PROC_DESTRUCTIVE_ALLZONE;
+ }
+
+ /*
+ * If we have all privs in whatever zone this is,
+ * we can do destructive things to processes which
+ * have altered credentials.
+ */
+#if defined(sun)
+ if (priv_isequalset(priv_getset(cr, PRIV_EFFECTIVE),
+ cr->cr_zone->zone_privset)) {
+ state->dts_cred.dcr_action |=
+ DTRACE_CRA_PROC_DESTRUCTIVE_CREDCHG;
+ }
+#endif
+ }
+
+ /*
+ * Holding the dtrace_kernel privilege also implies that
+ * the user has the dtrace_user privilege from a visibility
+ * perspective. But without further privileges, some
+ * destructive actions are not available.
+ */
+ if (PRIV_POLICY_ONLY(cr, PRIV_DTRACE_KERNEL, B_FALSE)) {
+ /*
+ * Make all probes in all zones visible. However,
+ * this doesn't mean that all actions become available
+ * to all zones.
+ */
+ state->dts_cred.dcr_visible |= DTRACE_CRV_KERNEL |
+ DTRACE_CRV_ALLPROC | DTRACE_CRV_ALLZONE;
+
+ state->dts_cred.dcr_action |= DTRACE_CRA_KERNEL |
+ DTRACE_CRA_PROC;
+ /*
+ * Holding proc_owner means that destructive actions
+ * for *this* zone are allowed.
+ */
+ if (PRIV_POLICY_ONLY(cr, PRIV_PROC_OWNER, B_FALSE))
+ state->dts_cred.dcr_action |=
+ DTRACE_CRA_PROC_DESTRUCTIVE_ALLUSER;
+
+ /*
+ * Holding proc_zone means that destructive actions
+ * for this user/group ID in all zones is allowed.
+ */
+ if (PRIV_POLICY_ONLY(cr, PRIV_PROC_ZONE, B_FALSE))
+ state->dts_cred.dcr_action |=
+ DTRACE_CRA_PROC_DESTRUCTIVE_ALLZONE;
+
+#if defined(sun)
+ /*
+ * If we have all privs in whatever zone this is,
+ * we can do destructive things to processes which
+ * have altered credentials.
+ */
+ if (priv_isequalset(priv_getset(cr, PRIV_EFFECTIVE),
+ cr->cr_zone->zone_privset)) {
+ state->dts_cred.dcr_action |=
+ DTRACE_CRA_PROC_DESTRUCTIVE_CREDCHG;
+ }
+#endif
+ }
+
+ /*
+ * Holding the dtrace_proc privilege gives control over fasttrap
+ * and pid providers. We need to grant wider destructive
+ * privileges in the event that the user has proc_owner and/or
+ * proc_zone.
+ */
+ if (PRIV_POLICY_ONLY(cr, PRIV_DTRACE_PROC, B_FALSE)) {
+ if (PRIV_POLICY_ONLY(cr, PRIV_PROC_OWNER, B_FALSE))
+ state->dts_cred.dcr_action |=
+ DTRACE_CRA_PROC_DESTRUCTIVE_ALLUSER;
+
+ if (PRIV_POLICY_ONLY(cr, PRIV_PROC_ZONE, B_FALSE))
+ state->dts_cred.dcr_action |=
+ DTRACE_CRA_PROC_DESTRUCTIVE_ALLZONE;
+ }
+ }
+
+ return (state);
+}
+
+static int
+dtrace_state_buffer(dtrace_state_t *state, dtrace_buffer_t *buf, int which)
+{
+ dtrace_optval_t *opt = state->dts_options, size;
+ processorid_t cpu = 0;;
+ int flags = 0, rval;
+
+ ASSERT(MUTEX_HELD(&dtrace_lock));
+ ASSERT(MUTEX_HELD(&cpu_lock));
+ ASSERT(which < DTRACEOPT_MAX);
+ ASSERT(state->dts_activity == DTRACE_ACTIVITY_INACTIVE ||
+ (state == dtrace_anon.dta_state &&
+ state->dts_activity == DTRACE_ACTIVITY_ACTIVE));
+
+ if (opt[which] == DTRACEOPT_UNSET || opt[which] == 0)
+ return (0);
+
+ if (opt[DTRACEOPT_CPU] != DTRACEOPT_UNSET)
+ cpu = opt[DTRACEOPT_CPU];
+
+ if (which == DTRACEOPT_SPECSIZE)
+ flags |= DTRACEBUF_NOSWITCH;
+
+ if (which == DTRACEOPT_BUFSIZE) {
+ if (opt[DTRACEOPT_BUFPOLICY] == DTRACEOPT_BUFPOLICY_RING)
+ flags |= DTRACEBUF_RING;
+
+ if (opt[DTRACEOPT_BUFPOLICY] == DTRACEOPT_BUFPOLICY_FILL)
+ flags |= DTRACEBUF_FILL;
+
+ if (state != dtrace_anon.dta_state ||
+ state->dts_activity != DTRACE_ACTIVITY_ACTIVE)
+ flags |= DTRACEBUF_INACTIVE;
+ }
+
+ for (size = opt[which]; size >= sizeof (uint64_t); size >>= 1) {
+ /*
+ * The size must be 8-byte aligned. If the size is not 8-byte
+ * aligned, drop it down by the difference.
+ */
+ if (size & (sizeof (uint64_t) - 1))
+ size -= size & (sizeof (uint64_t) - 1);
+
+ if (size < state->dts_reserve) {
+ /*
+ * Buffers always must be large enough to accommodate
+ * their prereserved space. We return E2BIG instead
+ * of ENOMEM in this case to allow for user-level
+ * software to differentiate the cases.
+ */
+ return (E2BIG);
+ }
+
+ rval = dtrace_buffer_alloc(buf, size, flags, cpu);
+
+ if (rval != ENOMEM) {
+ opt[which] = size;
+ return (rval);
+ }
+
+ if (opt[DTRACEOPT_BUFRESIZE] == DTRACEOPT_BUFRESIZE_MANUAL)
+ return (rval);
+ }
+
+ return (ENOMEM);
+}
+
+static int
+dtrace_state_buffers(dtrace_state_t *state)
+{
+ dtrace_speculation_t *spec = state->dts_speculations;
+ int rval, i;
+
+ if ((rval = dtrace_state_buffer(state, state->dts_buffer,
+ DTRACEOPT_BUFSIZE)) != 0)
+ return (rval);
+
+ if ((rval = dtrace_state_buffer(state, state->dts_aggbuffer,
+ DTRACEOPT_AGGSIZE)) != 0)
+ return (rval);
+
+ for (i = 0; i < state->dts_nspeculations; i++) {
+ if ((rval = dtrace_state_buffer(state,
+ spec[i].dtsp_buffer, DTRACEOPT_SPECSIZE)) != 0)
+ return (rval);
+ }
+
+ return (0);
+}
+
+static void
+dtrace_state_prereserve(dtrace_state_t *state)
+{
+ dtrace_ecb_t *ecb;
+ dtrace_probe_t *probe;
+
+ state->dts_reserve = 0;
+
+ if (state->dts_options[DTRACEOPT_BUFPOLICY] != DTRACEOPT_BUFPOLICY_FILL)
+ return;
+
+ /*
+ * If our buffer policy is a "fill" buffer policy, we need to set the
+ * prereserved space to be the space required by the END probes.
+ */
+ probe = dtrace_probes[dtrace_probeid_end - 1];
+ ASSERT(probe != NULL);
+
+ for (ecb = probe->dtpr_ecb; ecb != NULL; ecb = ecb->dte_next) {
+ if (ecb->dte_state != state)
+ continue;
+
+ state->dts_reserve += ecb->dte_needed + ecb->dte_alignment;
+ }
+}
+
+static int
+dtrace_state_go(dtrace_state_t *state, processorid_t *cpu)
+{
+ dtrace_optval_t *opt = state->dts_options, sz, nspec;
+ dtrace_speculation_t *spec;
+ dtrace_buffer_t *buf;
+#if defined(sun)
+ cyc_handler_t hdlr;
+ cyc_time_t when;
+#endif
+ int rval = 0, i, bufsize = NCPU * sizeof (dtrace_buffer_t);
+ dtrace_icookie_t cookie;
+
+ mutex_enter(&cpu_lock);
+ mutex_enter(&dtrace_lock);
+
+ if (state->dts_activity != DTRACE_ACTIVITY_INACTIVE) {
+ rval = EBUSY;
+ goto out;
+ }
+
+ /*
+ * Before we can perform any checks, we must prime all of the
+ * retained enablings that correspond to this state.
+ */
+ dtrace_enabling_prime(state);
+
+ if (state->dts_destructive && !state->dts_cred.dcr_destructive) {
+ rval = EACCES;
+ goto out;
+ }
+
+ dtrace_state_prereserve(state);
+
+ /*
+ * Now we want to do is try to allocate our speculations.
+ * We do not automatically resize the number of speculations; if
+ * this fails, we will fail the operation.
+ */
+ nspec = opt[DTRACEOPT_NSPEC];
+ ASSERT(nspec != DTRACEOPT_UNSET);
+
+ if (nspec > INT_MAX) {
+ rval = ENOMEM;
+ goto out;
+ }
+
+ spec = kmem_zalloc(nspec * sizeof (dtrace_speculation_t), KM_NOSLEEP);
+
+ if (spec == NULL) {
+ rval = ENOMEM;
+ goto out;
+ }
+
+ state->dts_speculations = spec;
+ state->dts_nspeculations = (int)nspec;
+
+ for (i = 0; i < nspec; i++) {
+ if ((buf = kmem_zalloc(bufsize, KM_NOSLEEP)) == NULL) {
+ rval = ENOMEM;
+ goto err;
+ }
+
+ spec[i].dtsp_buffer = buf;
+ }
+
+ if (opt[DTRACEOPT_GRABANON] != DTRACEOPT_UNSET) {
+ if (dtrace_anon.dta_state == NULL) {
+ rval = ENOENT;
+ goto out;
+ }
+
+ if (state->dts_necbs != 0) {
+ rval = EALREADY;
+ goto out;
+ }
+
+ state->dts_anon = dtrace_anon_grab();
+ ASSERT(state->dts_anon != NULL);
+ state = state->dts_anon;
+
+ /*
+ * We want "grabanon" to be set in the grabbed state, so we'll
+ * copy that option value from the grabbing state into the
+ * grabbed state.
+ */
+ state->dts_options[DTRACEOPT_GRABANON] =
+ opt[DTRACEOPT_GRABANON];
+
+ *cpu = dtrace_anon.dta_beganon;
+
+ /*
+ * If the anonymous state is active (as it almost certainly
+ * is if the anonymous enabling ultimately matched anything),
+ * we don't allow any further option processing -- but we
+ * don't return failure.
+ */
+ if (state->dts_activity != DTRACE_ACTIVITY_INACTIVE)
+ goto out;
+ }
+
+ if (opt[DTRACEOPT_AGGSIZE] != DTRACEOPT_UNSET &&
+ opt[DTRACEOPT_AGGSIZE] != 0) {
+ if (state->dts_aggregations == NULL) {
+ /*
+ * We're not going to create an aggregation buffer
+ * because we don't have any ECBs that contain
+ * aggregations -- set this option to 0.
+ */
+ opt[DTRACEOPT_AGGSIZE] = 0;
+ } else {
+ /*
+ * If we have an aggregation buffer, we must also have
+ * a buffer to use as scratch.
+ */
+ if (opt[DTRACEOPT_BUFSIZE] == DTRACEOPT_UNSET ||
+ opt[DTRACEOPT_BUFSIZE] < state->dts_needed) {
+ opt[DTRACEOPT_BUFSIZE] = state->dts_needed;
+ }
+ }
+ }
+
+ if (opt[DTRACEOPT_SPECSIZE] != DTRACEOPT_UNSET &&
+ opt[DTRACEOPT_SPECSIZE] != 0) {
+ if (!state->dts_speculates) {
+ /*
+ * We're not going to create speculation buffers
+ * because we don't have any ECBs that actually
+ * speculate -- set the speculation size to 0.
+ */
+ opt[DTRACEOPT_SPECSIZE] = 0;
+ }
+ }
+
+ /*
+ * The bare minimum size for any buffer that we're actually going to
+ * do anything to is sizeof (uint64_t).
+ */
+ sz = sizeof (uint64_t);
+
+ if ((state->dts_needed != 0 && opt[DTRACEOPT_BUFSIZE] < sz) ||
+ (state->dts_speculates && opt[DTRACEOPT_SPECSIZE] < sz) ||
+ (state->dts_aggregations != NULL && opt[DTRACEOPT_AGGSIZE] < sz)) {
+ /*
+ * A buffer size has been explicitly set to 0 (or to a size
+ * that will be adjusted to 0) and we need the space -- we
+ * need to return failure. We return ENOSPC to differentiate
+ * it from failing to allocate a buffer due to failure to meet
+ * the reserve (for which we return E2BIG).
+ */
+ rval = ENOSPC;
+ goto out;
+ }
+
+ if ((rval = dtrace_state_buffers(state)) != 0)
+ goto err;
+
+ if ((sz = opt[DTRACEOPT_DYNVARSIZE]) == DTRACEOPT_UNSET)
+ sz = dtrace_dstate_defsize;
+
+ do {
+ rval = dtrace_dstate_init(&state->dts_vstate.dtvs_dynvars, sz);
+
+ if (rval == 0)
+ break;
+
+ if (opt[DTRACEOPT_BUFRESIZE] == DTRACEOPT_BUFRESIZE_MANUAL)
+ goto err;
+ } while (sz >>= 1);
+
+ opt[DTRACEOPT_DYNVARSIZE] = sz;
+
+ if (rval != 0)
+ goto err;
+
+ if (opt[DTRACEOPT_STATUSRATE] > dtrace_statusrate_max)
+ opt[DTRACEOPT_STATUSRATE] = dtrace_statusrate_max;
+
+ if (opt[DTRACEOPT_CLEANRATE] == 0)
+ opt[DTRACEOPT_CLEANRATE] = dtrace_cleanrate_max;
+
+ if (opt[DTRACEOPT_CLEANRATE] < dtrace_cleanrate_min)
+ opt[DTRACEOPT_CLEANRATE] = dtrace_cleanrate_min;
+
+ if (opt[DTRACEOPT_CLEANRATE] > dtrace_cleanrate_max)
+ opt[DTRACEOPT_CLEANRATE] = dtrace_cleanrate_max;
+
+ state->dts_alive = state->dts_laststatus = dtrace_gethrtime();
+#if defined(sun)
+ hdlr.cyh_func = (cyc_func_t)dtrace_state_clean;
+ hdlr.cyh_arg = state;
+ hdlr.cyh_level = CY_LOW_LEVEL;
+
+ when.cyt_when = 0;
+ when.cyt_interval = opt[DTRACEOPT_CLEANRATE];
+
+ state->dts_cleaner = cyclic_add(&hdlr, &when);
+
+ hdlr.cyh_func = (cyc_func_t)dtrace_state_deadman;
+ hdlr.cyh_arg = state;
+ hdlr.cyh_level = CY_LOW_LEVEL;
+
+ when.cyt_when = 0;
+ when.cyt_interval = dtrace_deadman_interval;
+
+ state->dts_deadman = cyclic_add(&hdlr, &when);
+#else
+ callout_reset(&state->dts_cleaner, hz * opt[DTRACEOPT_CLEANRATE] / NANOSEC,
+ dtrace_state_clean, state);
+ callout_reset(&state->dts_deadman, hz * dtrace_deadman_interval / NANOSEC,
+ dtrace_state_deadman, state);
+#endif
+
+ state->dts_activity = DTRACE_ACTIVITY_WARMUP;
+
+ /*
+ * Now it's time to actually fire the BEGIN probe. We need to disable
+ * interrupts here both to record the CPU on which we fired the BEGIN
+ * probe (the data from this CPU will be processed first at user
+ * level) and to manually activate the buffer for this CPU.
+ */
+ cookie = dtrace_interrupt_disable();
+ *cpu = curcpu;
+ ASSERT(state->dts_buffer[*cpu].dtb_flags & DTRACEBUF_INACTIVE);
+ state->dts_buffer[*cpu].dtb_flags &= ~DTRACEBUF_INACTIVE;
+
+ dtrace_probe(dtrace_probeid_begin,
+ (uint64_t)(uintptr_t)state, 0, 0, 0, 0);
+ dtrace_interrupt_enable(cookie);
+ /*
+ * We may have had an exit action from a BEGIN probe; only change our
+ * state to ACTIVE if we're still in WARMUP.
+ */
+ ASSERT(state->dts_activity == DTRACE_ACTIVITY_WARMUP ||
+ state->dts_activity == DTRACE_ACTIVITY_DRAINING);
+
+ if (state->dts_activity == DTRACE_ACTIVITY_WARMUP)
+ state->dts_activity = DTRACE_ACTIVITY_ACTIVE;
+
+ /*
+ * Regardless of whether or not now we're in ACTIVE or DRAINING, we
+ * want each CPU to transition its principal buffer out of the
+ * INACTIVE state. Doing this assures that no CPU will suddenly begin
+ * processing an ECB halfway down a probe's ECB chain; all CPUs will
+ * atomically transition from processing none of a state's ECBs to
+ * processing all of them.
+ */
+ dtrace_xcall(DTRACE_CPUALL,
+ (dtrace_xcall_t)dtrace_buffer_activate, state);
+ goto out;
+
+err:
+ dtrace_buffer_free(state->dts_buffer);
+ dtrace_buffer_free(state->dts_aggbuffer);
+
+ if ((nspec = state->dts_nspeculations) == 0) {
+ ASSERT(state->dts_speculations == NULL);
+ goto out;
+ }
+
+ spec = state->dts_speculations;
+ ASSERT(spec != NULL);
+
+ for (i = 0; i < state->dts_nspeculations; i++) {
+ if ((buf = spec[i].dtsp_buffer) == NULL)
+ break;
+
+ dtrace_buffer_free(buf);
+ kmem_free(buf, bufsize);
+ }
+
+ kmem_free(spec, nspec * sizeof (dtrace_speculation_t));
+ state->dts_nspeculations = 0;
+ state->dts_speculations = NULL;
+
+out:
+ mutex_exit(&dtrace_lock);
+ mutex_exit(&cpu_lock);
+
+ return (rval);
+}
+
+static int
+dtrace_state_stop(dtrace_state_t *state, processorid_t *cpu)
+{
+ dtrace_icookie_t cookie;
+
+ ASSERT(MUTEX_HELD(&dtrace_lock));
+
+ if (state->dts_activity != DTRACE_ACTIVITY_ACTIVE &&
+ state->dts_activity != DTRACE_ACTIVITY_DRAINING)
+ return (EINVAL);
+
+ /*
+ * We'll set the activity to DTRACE_ACTIVITY_DRAINING, and issue a sync
+ * to be sure that every CPU has seen it. See below for the details
+ * on why this is done.
+ */
+ state->dts_activity = DTRACE_ACTIVITY_DRAINING;
+ dtrace_sync();
+
+ /*
+ * By this point, it is impossible for any CPU to be still processing
+ * with DTRACE_ACTIVITY_ACTIVE. We can thus set our activity to
+ * DTRACE_ACTIVITY_COOLDOWN and know that we're not racing with any
+ * other CPU in dtrace_buffer_reserve(). This allows dtrace_probe()
+ * and callees to know that the activity is DTRACE_ACTIVITY_COOLDOWN
+ * iff we're in the END probe.
+ */
+ state->dts_activity = DTRACE_ACTIVITY_COOLDOWN;
+ dtrace_sync();
+ ASSERT(state->dts_activity == DTRACE_ACTIVITY_COOLDOWN);
+
+ /*
+ * Finally, we can release the reserve and call the END probe. We
+ * disable interrupts across calling the END probe to allow us to
+ * return the CPU on which we actually called the END probe. This
+ * allows user-land to be sure that this CPU's principal buffer is
+ * processed last.
+ */
+ state->dts_reserve = 0;
+
+ cookie = dtrace_interrupt_disable();
+ *cpu = curcpu;
+ dtrace_probe(dtrace_probeid_end,
+ (uint64_t)(uintptr_t)state, 0, 0, 0, 0);
+ dtrace_interrupt_enable(cookie);
+
+ state->dts_activity = DTRACE_ACTIVITY_STOPPED;
+ dtrace_sync();
+
+ return (0);
+}
+
+static int
+dtrace_state_option(dtrace_state_t *state, dtrace_optid_t option,
+ dtrace_optval_t val)
+{
+ ASSERT(MUTEX_HELD(&dtrace_lock));
+
+ if (state->dts_activity != DTRACE_ACTIVITY_INACTIVE)
+ return (EBUSY);
+
+ if (option >= DTRACEOPT_MAX)
+ return (EINVAL);
+
+ if (option != DTRACEOPT_CPU && val < 0)
+ return (EINVAL);
+
+ switch (option) {
+ case DTRACEOPT_DESTRUCTIVE:
+ if (dtrace_destructive_disallow)
+ return (EACCES);
+
+ state->dts_cred.dcr_destructive = 1;
+ break;
+
+ case DTRACEOPT_BUFSIZE:
+ case DTRACEOPT_DYNVARSIZE:
+ case DTRACEOPT_AGGSIZE:
+ case DTRACEOPT_SPECSIZE:
+ case DTRACEOPT_STRSIZE:
+ if (val < 0)
+ return (EINVAL);
+
+ if (val >= LONG_MAX) {
+ /*
+ * If this is an otherwise negative value, set it to
+ * the highest multiple of 128m less than LONG_MAX.
+ * Technically, we're adjusting the size without
+ * regard to the buffer resizing policy, but in fact,
+ * this has no effect -- if we set the buffer size to
+ * ~LONG_MAX and the buffer policy is ultimately set to
+ * be "manual", the buffer allocation is guaranteed to
+ * fail, if only because the allocation requires two
+ * buffers. (We set the the size to the highest
+ * multiple of 128m because it ensures that the size
+ * will remain a multiple of a megabyte when
+ * repeatedly halved -- all the way down to 15m.)
+ */
+ val = LONG_MAX - (1 << 27) + 1;
+ }
+ }
+
+ state->dts_options[option] = val;
+
+ return (0);
+}
+
+static void
+dtrace_state_destroy(dtrace_state_t *state)
+{
+ dtrace_ecb_t *ecb;
+ dtrace_vstate_t *vstate = &state->dts_vstate;
+#if defined(sun)
+ minor_t minor = getminor(state->dts_dev);
+#endif
+ int i, bufsize = NCPU * sizeof (dtrace_buffer_t);
+ dtrace_speculation_t *spec = state->dts_speculations;
+ int nspec = state->dts_nspeculations;
+ uint32_t match;
+
+ ASSERT(MUTEX_HELD(&dtrace_lock));
+ ASSERT(MUTEX_HELD(&cpu_lock));
+
+ /*
+ * First, retract any retained enablings for this state.
+ */
+ dtrace_enabling_retract(state);
+ ASSERT(state->dts_nretained == 0);
+
+ if (state->dts_activity == DTRACE_ACTIVITY_ACTIVE ||
+ state->dts_activity == DTRACE_ACTIVITY_DRAINING) {
+ /*
+ * We have managed to come into dtrace_state_destroy() on a
+ * hot enabling -- almost certainly because of a disorderly
+ * shutdown of a consumer. (That is, a consumer that is
+ * exiting without having called dtrace_stop().) In this case,
+ * we're going to set our activity to be KILLED, and then
+ * issue a sync to be sure that everyone is out of probe
+ * context before we start blowing away ECBs.
+ */
+ state->dts_activity = DTRACE_ACTIVITY_KILLED;
+ dtrace_sync();
+ }
+
+ /*
+ * Release the credential hold we took in dtrace_state_create().
+ */
+ if (state->dts_cred.dcr_cred != NULL)
+ crfree(state->dts_cred.dcr_cred);
+
+ /*
+ * Now we can safely disable and destroy any enabled probes. Because
+ * any DTRACE_PRIV_KERNEL probes may actually be slowing our progress
+ * (especially if they're all enabled), we take two passes through the
+ * ECBs: in the first, we disable just DTRACE_PRIV_KERNEL probes, and
+ * in the second we disable whatever is left over.
+ */
+ for (match = DTRACE_PRIV_KERNEL; ; match = 0) {
+ for (i = 0; i < state->dts_necbs; i++) {
+ if ((ecb = state->dts_ecbs[i]) == NULL)
+ continue;
+
+ if (match && ecb->dte_probe != NULL) {
+ dtrace_probe_t *probe = ecb->dte_probe;
+ dtrace_provider_t *prov = probe->dtpr_provider;
+
+ if (!(prov->dtpv_priv.dtpp_flags & match))
+ continue;
+ }
+
+ dtrace_ecb_disable(ecb);
+ dtrace_ecb_destroy(ecb);
+ }
+
+ if (!match)
+ break;
+ }
+
+ /*
+ * Before we free the buffers, perform one more sync to assure that
+ * every CPU is out of probe context.
+ */
+ dtrace_sync();
+
+ dtrace_buffer_free(state->dts_buffer);
+ dtrace_buffer_free(state->dts_aggbuffer);
+
+ for (i = 0; i < nspec; i++)
+ dtrace_buffer_free(spec[i].dtsp_buffer);
+
+#if defined(sun)
+ if (state->dts_cleaner != CYCLIC_NONE)
+ cyclic_remove(state->dts_cleaner);
+
+ if (state->dts_deadman != CYCLIC_NONE)
+ cyclic_remove(state->dts_deadman);
+#else
+ callout_drain(&state->dts_cleaner);
+ callout_drain(&state->dts_deadman);
+#endif
+
+ dtrace_dstate_fini(&vstate->dtvs_dynvars);
+ dtrace_vstate_fini(vstate);
+ if (state->dts_ecbs != NULL)
+ kmem_free(state->dts_ecbs, state->dts_necbs * sizeof (dtrace_ecb_t *));
+
+ if (state->dts_aggregations != NULL) {
+#ifdef DEBUG
+ for (i = 0; i < state->dts_naggregations; i++)
+ ASSERT(state->dts_aggregations[i] == NULL);
+#endif
+ ASSERT(state->dts_naggregations > 0);
+ kmem_free(state->dts_aggregations,
+ state->dts_naggregations * sizeof (dtrace_aggregation_t *));
+ }
+
+ kmem_free(state->dts_buffer, bufsize);
+ kmem_free(state->dts_aggbuffer, bufsize);
+
+ for (i = 0; i < nspec; i++)
+ kmem_free(spec[i].dtsp_buffer, bufsize);
+
+ if (spec != NULL)
+ kmem_free(spec, nspec * sizeof (dtrace_speculation_t));
+
+ dtrace_format_destroy(state);
+
+ if (state->dts_aggid_arena != NULL) {
+#if defined(sun)
+ vmem_destroy(state->dts_aggid_arena);
+#else
+ delete_unrhdr(state->dts_aggid_arena);
+#endif
+ state->dts_aggid_arena = NULL;
+ }
+#if defined(sun)
+ ddi_soft_state_free(dtrace_softstate, minor);
+ vmem_free(dtrace_minor, (void *)(uintptr_t)minor, 1);
+#endif
+}
+
+/*
+ * DTrace Anonymous Enabling Functions
+ */
+static dtrace_state_t *
+dtrace_anon_grab(void)
+{
+ dtrace_state_t *state;
+
+ ASSERT(MUTEX_HELD(&dtrace_lock));
+
+ if ((state = dtrace_anon.dta_state) == NULL) {
+ ASSERT(dtrace_anon.dta_enabling == NULL);
+ return (NULL);
+ }
+
+ ASSERT(dtrace_anon.dta_enabling != NULL);
+ ASSERT(dtrace_retained != NULL);
+
+ dtrace_enabling_destroy(dtrace_anon.dta_enabling);
+ dtrace_anon.dta_enabling = NULL;
+ dtrace_anon.dta_state = NULL;
+
+ return (state);
+}
+
+static void
+dtrace_anon_property(void)
+{
+ int i, rv;
+ dtrace_state_t *state;
+ dof_hdr_t *dof;
+ char c[32]; /* enough for "dof-data-" + digits */
+
+ ASSERT(MUTEX_HELD(&dtrace_lock));
+ ASSERT(MUTEX_HELD(&cpu_lock));
+
+ for (i = 0; ; i++) {
+ (void) snprintf(c, sizeof (c), "dof-data-%d", i);
+
+ dtrace_err_verbose = 1;
+
+ if ((dof = dtrace_dof_property(c)) == NULL) {
+ dtrace_err_verbose = 0;
+ break;
+ }
+
+#if defined(sun)
+ /*
+ * We want to create anonymous state, so we need to transition
+ * the kernel debugger to indicate that DTrace is active. If
+ * this fails (e.g. because the debugger has modified text in
+ * some way), we won't continue with the processing.
+ */
+ if (kdi_dtrace_set(KDI_DTSET_DTRACE_ACTIVATE) != 0) {
+ cmn_err(CE_NOTE, "kernel debugger active; anonymous "
+ "enabling ignored.");
+ dtrace_dof_destroy(dof);
+ break;
+ }
+#endif
+
+ /*
+ * If we haven't allocated an anonymous state, we'll do so now.
+ */
+ if ((state = dtrace_anon.dta_state) == NULL) {
+#if defined(sun)
+ state = dtrace_state_create(NULL, NULL);
+#else
+ state = dtrace_state_create(NULL);
+#endif
+ dtrace_anon.dta_state = state;
+
+ if (state == NULL) {
+ /*
+ * This basically shouldn't happen: the only
+ * failure mode from dtrace_state_create() is a
+ * failure of ddi_soft_state_zalloc() that
+ * itself should never happen. Still, the
+ * interface allows for a failure mode, and
+ * we want to fail as gracefully as possible:
+ * we'll emit an error message and cease
+ * processing anonymous state in this case.
+ */
+ cmn_err(CE_WARN, "failed to create "
+ "anonymous state");
+ dtrace_dof_destroy(dof);
+ break;
+ }
+ }
+
+ rv = dtrace_dof_slurp(dof, &state->dts_vstate, CRED(),
+ &dtrace_anon.dta_enabling, 0, B_TRUE);
+
+ if (rv == 0)
+ rv = dtrace_dof_options(dof, state);
+
+ dtrace_err_verbose = 0;
+ dtrace_dof_destroy(dof);
+
+ if (rv != 0) {
+ /*
+ * This is malformed DOF; chuck any anonymous state
+ * that we created.
+ */
+ ASSERT(dtrace_anon.dta_enabling == NULL);
+ dtrace_state_destroy(state);
+ dtrace_anon.dta_state = NULL;
+ break;
+ }
+
+ ASSERT(dtrace_anon.dta_enabling != NULL);
+ }
+
+ if (dtrace_anon.dta_enabling != NULL) {
+ int rval;
+
+ /*
+ * dtrace_enabling_retain() can only fail because we are
+ * trying to retain more enablings than are allowed -- but
+ * we only have one anonymous enabling, and we are guaranteed
+ * to be allowed at least one retained enabling; we assert
+ * that dtrace_enabling_retain() returns success.
+ */
+ rval = dtrace_enabling_retain(dtrace_anon.dta_enabling);
+ ASSERT(rval == 0);
+
+ dtrace_enabling_dump(dtrace_anon.dta_enabling);
+ }
+}
+
+#if defined(sun)
+/*
+ * DTrace Helper Functions
+ */
+static void
+dtrace_helper_trace(dtrace_helper_action_t *helper,
+ dtrace_mstate_t *mstate, dtrace_vstate_t *vstate, int where)
+{
+ uint32_t size, next, nnext, i;
+ dtrace_helptrace_t *ent;
+ uint16_t flags = cpu_core[curcpu].cpuc_dtrace_flags;
+
+ if (!dtrace_helptrace_enabled)
+ return;
+
+ ASSERT(vstate->dtvs_nlocals <= dtrace_helptrace_nlocals);
+
+ /*
+ * What would a tracing framework be without its own tracing
+ * framework? (Well, a hell of a lot simpler, for starters...)
+ */
+ size = sizeof (dtrace_helptrace_t) + dtrace_helptrace_nlocals *
+ sizeof (uint64_t) - sizeof (uint64_t);
+
+ /*
+ * Iterate until we can allocate a slot in the trace buffer.
+ */
+ do {
+ next = dtrace_helptrace_next;
+
+ if (next + size < dtrace_helptrace_bufsize) {
+ nnext = next + size;
+ } else {
+ nnext = size;
+ }
+ } while (dtrace_cas32(&dtrace_helptrace_next, next, nnext) != next);
+
+ /*
+ * We have our slot; fill it in.
+ */
+ if (nnext == size)
+ next = 0;
+
+ ent = (dtrace_helptrace_t *)&dtrace_helptrace_buffer[next];
+ ent->dtht_helper = helper;
+ ent->dtht_where = where;
+ ent->dtht_nlocals = vstate->dtvs_nlocals;
+
+ ent->dtht_fltoffs = (mstate->dtms_present & DTRACE_MSTATE_FLTOFFS) ?
+ mstate->dtms_fltoffs : -1;
+ ent->dtht_fault = DTRACE_FLAGS2FLT(flags);
+ ent->dtht_illval = cpu_core[curcpu].cpuc_dtrace_illval;
+
+ for (i = 0; i < vstate->dtvs_nlocals; i++) {
+ dtrace_statvar_t *svar;
+
+ if ((svar = vstate->dtvs_locals[i]) == NULL)
+ continue;
+
+ ASSERT(svar->dtsv_size >= NCPU * sizeof (uint64_t));
+ ent->dtht_locals[i] =
+ ((uint64_t *)(uintptr_t)svar->dtsv_data)[curcpu];
+ }
+}
+#endif
+
+#if defined(sun)
+static uint64_t
+dtrace_helper(int which, dtrace_mstate_t *mstate,
+ dtrace_state_t *state, uint64_t arg0, uint64_t arg1)
+{
+ uint16_t *flags = &cpu_core[curcpu].cpuc_dtrace_flags;
+ uint64_t sarg0 = mstate->dtms_arg[0];
+ uint64_t sarg1 = mstate->dtms_arg[1];
+ uint64_t rval;
+ dtrace_helpers_t *helpers = curproc->p_dtrace_helpers;
+ dtrace_helper_action_t *helper;
+ dtrace_vstate_t *vstate;
+ dtrace_difo_t *pred;
+ int i, trace = dtrace_helptrace_enabled;
+
+ ASSERT(which >= 0 && which < DTRACE_NHELPER_ACTIONS);
+
+ if (helpers == NULL)
+ return (0);
+
+ if ((helper = helpers->dthps_actions[which]) == NULL)
+ return (0);
+
+ vstate = &helpers->dthps_vstate;
+ mstate->dtms_arg[0] = arg0;
+ mstate->dtms_arg[1] = arg1;
+
+ /*
+ * Now iterate over each helper. If its predicate evaluates to 'true',
+ * we'll call the corresponding actions. Note that the below calls
+ * to dtrace_dif_emulate() may set faults in machine state. This is
+ * okay: our caller (the outer dtrace_dif_emulate()) will simply plow
+ * the stored DIF offset with its own (which is the desired behavior).
+ * Also, note the calls to dtrace_dif_emulate() may allocate scratch
+ * from machine state; this is okay, too.
+ */
+ for (; helper != NULL; helper = helper->dtha_next) {
+ if ((pred = helper->dtha_predicate) != NULL) {
+ if (trace)
+ dtrace_helper_trace(helper, mstate, vstate, 0);
+
+ if (!dtrace_dif_emulate(pred, mstate, vstate, state))
+ goto next;
+
+ if (*flags & CPU_DTRACE_FAULT)
+ goto err;
+ }
+
+ for (i = 0; i < helper->dtha_nactions; i++) {
+ if (trace)
+ dtrace_helper_trace(helper,
+ mstate, vstate, i + 1);
+
+ rval = dtrace_dif_emulate(helper->dtha_actions[i],
+ mstate, vstate, state);
+
+ if (*flags & CPU_DTRACE_FAULT)
+ goto err;
+ }
+
+next:
+ if (trace)
+ dtrace_helper_trace(helper, mstate, vstate,
+ DTRACE_HELPTRACE_NEXT);
+ }
+
+ if (trace)
+ dtrace_helper_trace(helper, mstate, vstate,
+ DTRACE_HELPTRACE_DONE);
+
+ /*
+ * Restore the arg0 that we saved upon entry.
+ */
+ mstate->dtms_arg[0] = sarg0;
+ mstate->dtms_arg[1] = sarg1;
+
+ return (rval);
+
+err:
+ if (trace)
+ dtrace_helper_trace(helper, mstate, vstate,
+ DTRACE_HELPTRACE_ERR);
+
+ /*
+ * Restore the arg0 that we saved upon entry.
+ */
+ mstate->dtms_arg[0] = sarg0;
+ mstate->dtms_arg[1] = sarg1;
+
+ return (0);
+}
+
+static void
+dtrace_helper_action_destroy(dtrace_helper_action_t *helper,
+ dtrace_vstate_t *vstate)
+{
+ int i;
+
+ if (helper->dtha_predicate != NULL)
+ dtrace_difo_release(helper->dtha_predicate, vstate);
+
+ for (i = 0; i < helper->dtha_nactions; i++) {
+ ASSERT(helper->dtha_actions[i] != NULL);
+ dtrace_difo_release(helper->dtha_actions[i], vstate);
+ }
+
+ kmem_free(helper->dtha_actions,
+ helper->dtha_nactions * sizeof (dtrace_difo_t *));
+ kmem_free(helper, sizeof (dtrace_helper_action_t));
+}
+
+static int
+dtrace_helper_destroygen(int gen)
+{
+ proc_t *p = curproc;
+ dtrace_helpers_t *help = p->p_dtrace_helpers;
+ dtrace_vstate_t *vstate;
+ int i;
+
+ ASSERT(MUTEX_HELD(&dtrace_lock));
+
+ if (help == NULL || gen > help->dthps_generation)
+ return (EINVAL);
+
+ vstate = &help->dthps_vstate;
+
+ for (i = 0; i < DTRACE_NHELPER_ACTIONS; i++) {
+ dtrace_helper_action_t *last = NULL, *h, *next;
+
+ for (h = help->dthps_actions[i]; h != NULL; h = next) {
+ next = h->dtha_next;
+
+ if (h->dtha_generation == gen) {
+ if (last != NULL) {
+ last->dtha_next = next;
+ } else {
+ help->dthps_actions[i] = next;
+ }
+
+ dtrace_helper_action_destroy(h, vstate);
+ } else {
+ last = h;
+ }
+ }
+ }
+
+ /*
+ * Interate until we've cleared out all helper providers with the
+ * given generation number.
+ */
+ for (;;) {
+ dtrace_helper_provider_t *prov;
+
+ /*
+ * Look for a helper provider with the right generation. We
+ * have to start back at the beginning of the list each time
+ * because we drop dtrace_lock. It's unlikely that we'll make
+ * more than two passes.
+ */
+ for (i = 0; i < help->dthps_nprovs; i++) {
+ prov = help->dthps_provs[i];
+
+ if (prov->dthp_generation == gen)
+ break;
+ }
+
+ /*
+ * If there were no matches, we're done.
+ */
+ if (i == help->dthps_nprovs)
+ break;
+
+ /*
+ * Move the last helper provider into this slot.
+ */
+ help->dthps_nprovs--;
+ help->dthps_provs[i] = help->dthps_provs[help->dthps_nprovs];
+ help->dthps_provs[help->dthps_nprovs] = NULL;
+
+ mutex_exit(&dtrace_lock);
+
+ /*
+ * If we have a meta provider, remove this helper provider.
+ */
+ mutex_enter(&dtrace_meta_lock);
+ if (dtrace_meta_pid != NULL) {
+ ASSERT(dtrace_deferred_pid == NULL);
+ dtrace_helper_provider_remove(&prov->dthp_prov,
+ p->p_pid);
+ }
+ mutex_exit(&dtrace_meta_lock);
+
+ dtrace_helper_provider_destroy(prov);
+
+ mutex_enter(&dtrace_lock);
+ }
+
+ return (0);
+}
+#endif
+
+#if defined(sun)
+static int
+dtrace_helper_validate(dtrace_helper_action_t *helper)
+{
+ int err = 0, i;
+ dtrace_difo_t *dp;
+
+ if ((dp = helper->dtha_predicate) != NULL)
+ err += dtrace_difo_validate_helper(dp);
+
+ for (i = 0; i < helper->dtha_nactions; i++)
+ err += dtrace_difo_validate_helper(helper->dtha_actions[i]);
+
+ return (err == 0);
+}
+#endif
+
+#if defined(sun)
+static int
+dtrace_helper_action_add(int which, dtrace_ecbdesc_t *ep)
+{
+ dtrace_helpers_t *help;
+ dtrace_helper_action_t *helper, *last;
+ dtrace_actdesc_t *act;
+ dtrace_vstate_t *vstate;
+ dtrace_predicate_t *pred;
+ int count = 0, nactions = 0, i;
+
+ if (which < 0 || which >= DTRACE_NHELPER_ACTIONS)
+ return (EINVAL);
+
+ help = curproc->p_dtrace_helpers;
+ last = help->dthps_actions[which];
+ vstate = &help->dthps_vstate;
+
+ for (count = 0; last != NULL; last = last->dtha_next) {
+ count++;
+ if (last->dtha_next == NULL)
+ break;
+ }
+
+ /*
+ * If we already have dtrace_helper_actions_max helper actions for this
+ * helper action type, we'll refuse to add a new one.
+ */
+ if (count >= dtrace_helper_actions_max)
+ return (ENOSPC);
+
+ helper = kmem_zalloc(sizeof (dtrace_helper_action_t), KM_SLEEP);
+ helper->dtha_generation = help->dthps_generation;
+
+ if ((pred = ep->dted_pred.dtpdd_predicate) != NULL) {
+ ASSERT(pred->dtp_difo != NULL);
+ dtrace_difo_hold(pred->dtp_difo);
+ helper->dtha_predicate = pred->dtp_difo;
+ }
+
+ for (act = ep->dted_action; act != NULL; act = act->dtad_next) {
+ if (act->dtad_kind != DTRACEACT_DIFEXPR)
+ goto err;
+
+ if (act->dtad_difo == NULL)
+ goto err;
+
+ nactions++;
+ }
+
+ helper->dtha_actions = kmem_zalloc(sizeof (dtrace_difo_t *) *
+ (helper->dtha_nactions = nactions), KM_SLEEP);
+
+ for (act = ep->dted_action, i = 0; act != NULL; act = act->dtad_next) {
+ dtrace_difo_hold(act->dtad_difo);
+ helper->dtha_actions[i++] = act->dtad_difo;
+ }
+
+ if (!dtrace_helper_validate(helper))
+ goto err;
+
+ if (last == NULL) {
+ help->dthps_actions[which] = helper;
+ } else {
+ last->dtha_next = helper;
+ }
+
+ if (vstate->dtvs_nlocals > dtrace_helptrace_nlocals) {
+ dtrace_helptrace_nlocals = vstate->dtvs_nlocals;
+ dtrace_helptrace_next = 0;
+ }
+
+ return (0);
+err:
+ dtrace_helper_action_destroy(helper, vstate);
+ return (EINVAL);
+}
+
+static void
+dtrace_helper_provider_register(proc_t *p, dtrace_helpers_t *help,
+ dof_helper_t *dofhp)
+{
+ ASSERT(MUTEX_NOT_HELD(&dtrace_lock));
+
+ mutex_enter(&dtrace_meta_lock);
+ mutex_enter(&dtrace_lock);
+
+ if (!dtrace_attached() || dtrace_meta_pid == NULL) {
+ /*
+ * If the dtrace module is loaded but not attached, or if
+ * there aren't isn't a meta provider registered to deal with
+ * these provider descriptions, we need to postpone creating
+ * the actual providers until later.
+ */
+
+ if (help->dthps_next == NULL && help->dthps_prev == NULL &&
+ dtrace_deferred_pid != help) {
+ help->dthps_deferred = 1;
+ help->dthps_pid = p->p_pid;
+ help->dthps_next = dtrace_deferred_pid;
+ help->dthps_prev = NULL;
+ if (dtrace_deferred_pid != NULL)
+ dtrace_deferred_pid->dthps_prev = help;
+ dtrace_deferred_pid = help;
+ }
+
+ mutex_exit(&dtrace_lock);
+
+ } else if (dofhp != NULL) {
+ /*
+ * If the dtrace module is loaded and we have a particular
+ * helper provider description, pass that off to the
+ * meta provider.
+ */
+
+ mutex_exit(&dtrace_lock);
+
+ dtrace_helper_provide(dofhp, p->p_pid);
+
+ } else {
+ /*
+ * Otherwise, just pass all the helper provider descriptions
+ * off to the meta provider.
+ */
+
+ int i;
+ mutex_exit(&dtrace_lock);
+
+ for (i = 0; i < help->dthps_nprovs; i++) {
+ dtrace_helper_provide(&help->dthps_provs[i]->dthp_prov,
+ p->p_pid);
+ }
+ }
+
+ mutex_exit(&dtrace_meta_lock);
+}
+
+static int
+dtrace_helper_provider_add(dof_helper_t *dofhp, int gen)
+{
+ dtrace_helpers_t *help;
+ dtrace_helper_provider_t *hprov, **tmp_provs;
+ uint_t tmp_maxprovs, i;
+
+ ASSERT(MUTEX_HELD(&dtrace_lock));
+
+ help = curproc->p_dtrace_helpers;
+ ASSERT(help != NULL);
+
+ /*
+ * If we already have dtrace_helper_providers_max helper providers,
+ * we're refuse to add a new one.
+ */
+ if (help->dthps_nprovs >= dtrace_helper_providers_max)
+ return (ENOSPC);
+
+ /*
+ * Check to make sure this isn't a duplicate.
+ */
+ for (i = 0; i < help->dthps_nprovs; i++) {
+ if (dofhp->dofhp_addr ==
+ help->dthps_provs[i]->dthp_prov.dofhp_addr)
+ return (EALREADY);
+ }
+
+ hprov = kmem_zalloc(sizeof (dtrace_helper_provider_t), KM_SLEEP);
+ hprov->dthp_prov = *dofhp;
+ hprov->dthp_ref = 1;
+ hprov->dthp_generation = gen;
+
+ /*
+ * Allocate a bigger table for helper providers if it's already full.
+ */
+ if (help->dthps_maxprovs == help->dthps_nprovs) {
+ tmp_maxprovs = help->dthps_maxprovs;
+ tmp_provs = help->dthps_provs;
+
+ if (help->dthps_maxprovs == 0)
+ help->dthps_maxprovs = 2;
+ else
+ help->dthps_maxprovs *= 2;
+ if (help->dthps_maxprovs > dtrace_helper_providers_max)
+ help->dthps_maxprovs = dtrace_helper_providers_max;
+
+ ASSERT(tmp_maxprovs < help->dthps_maxprovs);
+
+ help->dthps_provs = kmem_zalloc(help->dthps_maxprovs *
+ sizeof (dtrace_helper_provider_t *), KM_SLEEP);
+
+ if (tmp_provs != NULL) {
+ bcopy(tmp_provs, help->dthps_provs, tmp_maxprovs *
+ sizeof (dtrace_helper_provider_t *));
+ kmem_free(tmp_provs, tmp_maxprovs *
+ sizeof (dtrace_helper_provider_t *));
+ }
+ }
+
+ help->dthps_provs[help->dthps_nprovs] = hprov;
+ help->dthps_nprovs++;
+
+ return (0);
+}
+
+static void
+dtrace_helper_provider_destroy(dtrace_helper_provider_t *hprov)
+{
+ mutex_enter(&dtrace_lock);
+
+ if (--hprov->dthp_ref == 0) {
+ dof_hdr_t *dof;
+ mutex_exit(&dtrace_lock);
+ dof = (dof_hdr_t *)(uintptr_t)hprov->dthp_prov.dofhp_dof;
+ dtrace_dof_destroy(dof);
+ kmem_free(hprov, sizeof (dtrace_helper_provider_t));
+ } else {
+ mutex_exit(&dtrace_lock);
+ }
+}
+
+static int
+dtrace_helper_provider_validate(dof_hdr_t *dof, dof_sec_t *sec)
+{
+ uintptr_t daddr = (uintptr_t)dof;
+ dof_sec_t *str_sec, *prb_sec, *arg_sec, *off_sec, *enoff_sec;
+ dof_provider_t *provider;
+ dof_probe_t *probe;
+ uint8_t *arg;
+ char *strtab, *typestr;
+ dof_stridx_t typeidx;
+ size_t typesz;
+ uint_t nprobes, j, k;
+
+ ASSERT(sec->dofs_type == DOF_SECT_PROVIDER);
+
+ if (sec->dofs_offset & (sizeof (uint_t) - 1)) {
+ dtrace_dof_error(dof, "misaligned section offset");
+ return (-1);
+ }
+
+ /*
+ * The section needs to be large enough to contain the DOF provider
+ * structure appropriate for the given version.
+ */
+ if (sec->dofs_size <
+ ((dof->dofh_ident[DOF_ID_VERSION] == DOF_VERSION_1) ?
+ offsetof(dof_provider_t, dofpv_prenoffs) :
+ sizeof (dof_provider_t))) {
+ dtrace_dof_error(dof, "provider section too small");
+ return (-1);
+ }
+
+ provider = (dof_provider_t *)(uintptr_t)(daddr + sec->dofs_offset);
+ str_sec = dtrace_dof_sect(dof, DOF_SECT_STRTAB, provider->dofpv_strtab);
+ prb_sec = dtrace_dof_sect(dof, DOF_SECT_PROBES, provider->dofpv_probes);
+ arg_sec = dtrace_dof_sect(dof, DOF_SECT_PRARGS, provider->dofpv_prargs);
+ off_sec = dtrace_dof_sect(dof, DOF_SECT_PROFFS, provider->dofpv_proffs);
+
+ if (str_sec == NULL || prb_sec == NULL ||
+ arg_sec == NULL || off_sec == NULL)
+ return (-1);
+
+ enoff_sec = NULL;
+
+ if (dof->dofh_ident[DOF_ID_VERSION] != DOF_VERSION_1 &&
+ provider->dofpv_prenoffs != DOF_SECT_NONE &&
+ (enoff_sec = dtrace_dof_sect(dof, DOF_SECT_PRENOFFS,
+ provider->dofpv_prenoffs)) == NULL)
+ return (-1);
+
+ strtab = (char *)(uintptr_t)(daddr + str_sec->dofs_offset);
+
+ if (provider->dofpv_name >= str_sec->dofs_size ||
+ strlen(strtab + provider->dofpv_name) >= DTRACE_PROVNAMELEN) {
+ dtrace_dof_error(dof, "invalid provider name");
+ return (-1);
+ }
+
+ if (prb_sec->dofs_entsize == 0 ||
+ prb_sec->dofs_entsize > prb_sec->dofs_size) {
+ dtrace_dof_error(dof, "invalid entry size");
+ return (-1);
+ }
+
+ if (prb_sec->dofs_entsize & (sizeof (uintptr_t) - 1)) {
+ dtrace_dof_error(dof, "misaligned entry size");
+ return (-1);
+ }
+
+ if (off_sec->dofs_entsize != sizeof (uint32_t)) {
+ dtrace_dof_error(dof, "invalid entry size");
+ return (-1);
+ }
+
+ if (off_sec->dofs_offset & (sizeof (uint32_t) - 1)) {
+ dtrace_dof_error(dof, "misaligned section offset");
+ return (-1);
+ }
+
+ if (arg_sec->dofs_entsize != sizeof (uint8_t)) {
+ dtrace_dof_error(dof, "invalid entry size");
+ return (-1);
+ }
+
+ arg = (uint8_t *)(uintptr_t)(daddr + arg_sec->dofs_offset);
+
+ nprobes = prb_sec->dofs_size / prb_sec->dofs_entsize;
+
+ /*
+ * Take a pass through the probes to check for errors.
+ */
+ for (j = 0; j < nprobes; j++) {
+ probe = (dof_probe_t *)(uintptr_t)(daddr +
+ prb_sec->dofs_offset + j * prb_sec->dofs_entsize);
+
+ if (probe->dofpr_func >= str_sec->dofs_size) {
+ dtrace_dof_error(dof, "invalid function name");
+ return (-1);
+ }
+
+ if (strlen(strtab + probe->dofpr_func) >= DTRACE_FUNCNAMELEN) {
+ dtrace_dof_error(dof, "function name too long");
+ return (-1);
+ }
+
+ if (probe->dofpr_name >= str_sec->dofs_size ||
+ strlen(strtab + probe->dofpr_name) >= DTRACE_NAMELEN) {
+ dtrace_dof_error(dof, "invalid probe name");
+ return (-1);
+ }
+
+ /*
+ * The offset count must not wrap the index, and the offsets
+ * must also not overflow the section's data.
+ */
+ if (probe->dofpr_offidx + probe->dofpr_noffs <
+ probe->dofpr_offidx ||
+ (probe->dofpr_offidx + probe->dofpr_noffs) *
+ off_sec->dofs_entsize > off_sec->dofs_size) {
+ dtrace_dof_error(dof, "invalid probe offset");
+ return (-1);
+ }
+
+ if (dof->dofh_ident[DOF_ID_VERSION] != DOF_VERSION_1) {
+ /*
+ * If there's no is-enabled offset section, make sure
+ * there aren't any is-enabled offsets. Otherwise
+ * perform the same checks as for probe offsets
+ * (immediately above).
+ */
+ if (enoff_sec == NULL) {
+ if (probe->dofpr_enoffidx != 0 ||
+ probe->dofpr_nenoffs != 0) {
+ dtrace_dof_error(dof, "is-enabled "
+ "offsets with null section");
+ return (-1);
+ }
+ } else if (probe->dofpr_enoffidx +
+ probe->dofpr_nenoffs < probe->dofpr_enoffidx ||
+ (probe->dofpr_enoffidx + probe->dofpr_nenoffs) *
+ enoff_sec->dofs_entsize > enoff_sec->dofs_size) {
+ dtrace_dof_error(dof, "invalid is-enabled "
+ "offset");
+ return (-1);
+ }
+
+ if (probe->dofpr_noffs + probe->dofpr_nenoffs == 0) {
+ dtrace_dof_error(dof, "zero probe and "
+ "is-enabled offsets");
+ return (-1);
+ }
+ } else if (probe->dofpr_noffs == 0) {
+ dtrace_dof_error(dof, "zero probe offsets");
+ return (-1);
+ }
+
+ if (probe->dofpr_argidx + probe->dofpr_xargc <
+ probe->dofpr_argidx ||
+ (probe->dofpr_argidx + probe->dofpr_xargc) *
+ arg_sec->dofs_entsize > arg_sec->dofs_size) {
+ dtrace_dof_error(dof, "invalid args");
+ return (-1);
+ }
+
+ typeidx = probe->dofpr_nargv;
+ typestr = strtab + probe->dofpr_nargv;
+ for (k = 0; k < probe->dofpr_nargc; k++) {
+ if (typeidx >= str_sec->dofs_size) {
+ dtrace_dof_error(dof, "bad "
+ "native argument type");
+ return (-1);
+ }
+
+ typesz = strlen(typestr) + 1;
+ if (typesz > DTRACE_ARGTYPELEN) {
+ dtrace_dof_error(dof, "native "
+ "argument type too long");
+ return (-1);
+ }
+ typeidx += typesz;
+ typestr += typesz;
+ }
+
+ typeidx = probe->dofpr_xargv;
+ typestr = strtab + probe->dofpr_xargv;
+ for (k = 0; k < probe->dofpr_xargc; k++) {
+ if (arg[probe->dofpr_argidx + k] > probe->dofpr_nargc) {
+ dtrace_dof_error(dof, "bad "
+ "native argument index");
+ return (-1);
+ }
+
+ if (typeidx >= str_sec->dofs_size) {
+ dtrace_dof_error(dof, "bad "
+ "translated argument type");
+ return (-1);
+ }
+
+ typesz = strlen(typestr) + 1;
+ if (typesz > DTRACE_ARGTYPELEN) {
+ dtrace_dof_error(dof, "translated argument "
+ "type too long");
+ return (-1);
+ }
+
+ typeidx += typesz;
+ typestr += typesz;
+ }
+ }
+
+ return (0);
+}
+
+static int
+dtrace_helper_slurp(dof_hdr_t *dof, dof_helper_t *dhp)
+{
+ dtrace_helpers_t *help;
+ dtrace_vstate_t *vstate;
+ dtrace_enabling_t *enab = NULL;
+ int i, gen, rv, nhelpers = 0, nprovs = 0, destroy = 1;
+ uintptr_t daddr = (uintptr_t)dof;
+
+ ASSERT(MUTEX_HELD(&dtrace_lock));
+
+ if ((help = curproc->p_dtrace_helpers) == NULL)
+ help = dtrace_helpers_create(curproc);
+
+ vstate = &help->dthps_vstate;
+
+ if ((rv = dtrace_dof_slurp(dof, vstate, NULL, &enab,
+ dhp != NULL ? dhp->dofhp_addr : 0, B_FALSE)) != 0) {
+ dtrace_dof_destroy(dof);
+ return (rv);
+ }
+
+ /*
+ * Look for helper providers and validate their descriptions.
+ */
+ if (dhp != NULL) {
+ for (i = 0; i < dof->dofh_secnum; i++) {
+ dof_sec_t *sec = (dof_sec_t *)(uintptr_t)(daddr +
+ dof->dofh_secoff + i * dof->dofh_secsize);
+
+ if (sec->dofs_type != DOF_SECT_PROVIDER)
+ continue;
+
+ if (dtrace_helper_provider_validate(dof, sec) != 0) {
+ dtrace_enabling_destroy(enab);
+ dtrace_dof_destroy(dof);
+ return (-1);
+ }
+
+ nprovs++;
+ }
+ }
+
+ /*
+ * Now we need to walk through the ECB descriptions in the enabling.
+ */
+ for (i = 0; i < enab->dten_ndesc; i++) {
+ dtrace_ecbdesc_t *ep = enab->dten_desc[i];
+ dtrace_probedesc_t *desc = &ep->dted_probe;
+
+ if (strcmp(desc->dtpd_provider, "dtrace") != 0)
+ continue;
+
+ if (strcmp(desc->dtpd_mod, "helper") != 0)
+ continue;
+
+ if (strcmp(desc->dtpd_func, "ustack") != 0)
+ continue;
+
+ if ((rv = dtrace_helper_action_add(DTRACE_HELPER_ACTION_USTACK,
+ ep)) != 0) {
+ /*
+ * Adding this helper action failed -- we are now going
+ * to rip out the entire generation and return failure.
+ */
+ (void) dtrace_helper_destroygen(help->dthps_generation);
+ dtrace_enabling_destroy(enab);
+ dtrace_dof_destroy(dof);
+ return (-1);
+ }
+
+ nhelpers++;
+ }
+
+ if (nhelpers < enab->dten_ndesc)
+ dtrace_dof_error(dof, "unmatched helpers");
+
+ gen = help->dthps_generation++;
+ dtrace_enabling_destroy(enab);
+
+ if (dhp != NULL && nprovs > 0) {
+ dhp->dofhp_dof = (uint64_t)(uintptr_t)dof;
+ if (dtrace_helper_provider_add(dhp, gen) == 0) {
+ mutex_exit(&dtrace_lock);
+ dtrace_helper_provider_register(curproc, help, dhp);
+ mutex_enter(&dtrace_lock);
+
+ destroy = 0;
+ }
+ }
+
+ if (destroy)
+ dtrace_dof_destroy(dof);
+
+ return (gen);
+}
+
+static dtrace_helpers_t *
+dtrace_helpers_create(proc_t *p)
+{
+ dtrace_helpers_t *help;
+
+ ASSERT(MUTEX_HELD(&dtrace_lock));
+ ASSERT(p->p_dtrace_helpers == NULL);
+
+ help = kmem_zalloc(sizeof (dtrace_helpers_t), KM_SLEEP);
+ help->dthps_actions = kmem_zalloc(sizeof (dtrace_helper_action_t *) *
+ DTRACE_NHELPER_ACTIONS, KM_SLEEP);
+
+ p->p_dtrace_helpers = help;
+ dtrace_helpers++;
+
+ return (help);
+}
+
+static void
+dtrace_helpers_destroy(void)
+{
+ dtrace_helpers_t *help;
+ dtrace_vstate_t *vstate;
+ proc_t *p = curproc;
+ int i;
+
+ mutex_enter(&dtrace_lock);
+
+ ASSERT(p->p_dtrace_helpers != NULL);
+ ASSERT(dtrace_helpers > 0);
+
+ help = p->p_dtrace_helpers;
+ vstate = &help->dthps_vstate;
+
+ /*
+ * We're now going to lose the help from this process.
+ */
+ p->p_dtrace_helpers = NULL;
+ dtrace_sync();
+
+ /*
+ * Destory the helper actions.
+ */
+ for (i = 0; i < DTRACE_NHELPER_ACTIONS; i++) {
+ dtrace_helper_action_t *h, *next;
+
+ for (h = help->dthps_actions[i]; h != NULL; h = next) {
+ next = h->dtha_next;
+ dtrace_helper_action_destroy(h, vstate);
+ h = next;
+ }
+ }
+
+ mutex_exit(&dtrace_lock);
+
+ /*
+ * Destroy the helper providers.
+ */
+ if (help->dthps_maxprovs > 0) {
+ mutex_enter(&dtrace_meta_lock);
+ if (dtrace_meta_pid != NULL) {
+ ASSERT(dtrace_deferred_pid == NULL);
+
+ for (i = 0; i < help->dthps_nprovs; i++) {
+ dtrace_helper_provider_remove(
+ &help->dthps_provs[i]->dthp_prov, p->p_pid);
+ }
+ } else {
+ mutex_enter(&dtrace_lock);
+ ASSERT(help->dthps_deferred == 0 ||
+ help->dthps_next != NULL ||
+ help->dthps_prev != NULL ||
+ help == dtrace_deferred_pid);
+
+ /*
+ * Remove the helper from the deferred list.
+ */
+ if (help->dthps_next != NULL)
+ help->dthps_next->dthps_prev = help->dthps_prev;
+ if (help->dthps_prev != NULL)
+ help->dthps_prev->dthps_next = help->dthps_next;
+ if (dtrace_deferred_pid == help) {
+ dtrace_deferred_pid = help->dthps_next;
+ ASSERT(help->dthps_prev == NULL);
+ }
+
+ mutex_exit(&dtrace_lock);
+ }
+
+ mutex_exit(&dtrace_meta_lock);
+
+ for (i = 0; i < help->dthps_nprovs; i++) {
+ dtrace_helper_provider_destroy(help->dthps_provs[i]);
+ }
+
+ kmem_free(help->dthps_provs, help->dthps_maxprovs *
+ sizeof (dtrace_helper_provider_t *));
+ }
+
+ mutex_enter(&dtrace_lock);
+
+ dtrace_vstate_fini(&help->dthps_vstate);
+ kmem_free(help->dthps_actions,
+ sizeof (dtrace_helper_action_t *) * DTRACE_NHELPER_ACTIONS);
+ kmem_free(help, sizeof (dtrace_helpers_t));
+
+ --dtrace_helpers;
+ mutex_exit(&dtrace_lock);
+}
+
+static void
+dtrace_helpers_duplicate(proc_t *from, proc_t *to)
+{
+ dtrace_helpers_t *help, *newhelp;
+ dtrace_helper_action_t *helper, *new, *last;
+ dtrace_difo_t *dp;
+ dtrace_vstate_t *vstate;
+ int i, j, sz, hasprovs = 0;
+
+ mutex_enter(&dtrace_lock);
+ ASSERT(from->p_dtrace_helpers != NULL);
+ ASSERT(dtrace_helpers > 0);
+
+ help = from->p_dtrace_helpers;
+ newhelp = dtrace_helpers_create(to);
+ ASSERT(to->p_dtrace_helpers != NULL);
+
+ newhelp->dthps_generation = help->dthps_generation;
+ vstate = &newhelp->dthps_vstate;
+
+ /*
+ * Duplicate the helper actions.
+ */
+ for (i = 0; i < DTRACE_NHELPER_ACTIONS; i++) {
+ if ((helper = help->dthps_actions[i]) == NULL)
+ continue;
+
+ for (last = NULL; helper != NULL; helper = helper->dtha_next) {
+ new = kmem_zalloc(sizeof (dtrace_helper_action_t),
+ KM_SLEEP);
+ new->dtha_generation = helper->dtha_generation;
+
+ if ((dp = helper->dtha_predicate) != NULL) {
+ dp = dtrace_difo_duplicate(dp, vstate);
+ new->dtha_predicate = dp;
+ }
+
+ new->dtha_nactions = helper->dtha_nactions;
+ sz = sizeof (dtrace_difo_t *) * new->dtha_nactions;
+ new->dtha_actions = kmem_alloc(sz, KM_SLEEP);
+
+ for (j = 0; j < new->dtha_nactions; j++) {
+ dtrace_difo_t *dp = helper->dtha_actions[j];
+
+ ASSERT(dp != NULL);
+ dp = dtrace_difo_duplicate(dp, vstate);
+ new->dtha_actions[j] = dp;
+ }
+
+ if (last != NULL) {
+ last->dtha_next = new;
+ } else {
+ newhelp->dthps_actions[i] = new;
+ }
+
+ last = new;
+ }
+ }
+
+ /*
+ * Duplicate the helper providers and register them with the
+ * DTrace framework.
+ */
+ if (help->dthps_nprovs > 0) {
+ newhelp->dthps_nprovs = help->dthps_nprovs;
+ newhelp->dthps_maxprovs = help->dthps_nprovs;
+ newhelp->dthps_provs = kmem_alloc(newhelp->dthps_nprovs *
+ sizeof (dtrace_helper_provider_t *), KM_SLEEP);
+ for (i = 0; i < newhelp->dthps_nprovs; i++) {
+ newhelp->dthps_provs[i] = help->dthps_provs[i];
+ newhelp->dthps_provs[i]->dthp_ref++;
+ }
+
+ hasprovs = 1;
+ }
+
+ mutex_exit(&dtrace_lock);
+
+ if (hasprovs)
+ dtrace_helper_provider_register(to, newhelp, NULL);
+}
+#endif
+
+#if defined(sun)
+/*
+ * DTrace Hook Functions
+ */
+static void
+dtrace_module_loaded(modctl_t *ctl)
+{
+ dtrace_provider_t *prv;
+
+ mutex_enter(&dtrace_provider_lock);
+ mutex_enter(&mod_lock);
+
+ ASSERT(ctl->mod_busy);
+
+ /*
+ * We're going to call each providers per-module provide operation
+ * specifying only this module.
+ */
+ for (prv = dtrace_provider; prv != NULL; prv = prv->dtpv_next)
+ prv->dtpv_pops.dtps_provide_module(prv->dtpv_arg, ctl);
+
+ mutex_exit(&mod_lock);
+ mutex_exit(&dtrace_provider_lock);
+
+ /*
+ * If we have any retained enablings, we need to match against them.
+ * Enabling probes requires that cpu_lock be held, and we cannot hold
+ * cpu_lock here -- it is legal for cpu_lock to be held when loading a
+ * module. (In particular, this happens when loading scheduling
+ * classes.) So if we have any retained enablings, we need to dispatch
+ * our task queue to do the match for us.
+ */
+ mutex_enter(&dtrace_lock);
+
+ if (dtrace_retained == NULL) {
+ mutex_exit(&dtrace_lock);
+ return;
+ }
+
+ (void) taskq_dispatch(dtrace_taskq,
+ (task_func_t *)dtrace_enabling_matchall, NULL, TQ_SLEEP);
+
+ mutex_exit(&dtrace_lock);
+
+ /*
+ * And now, for a little heuristic sleaze: in general, we want to
+ * match modules as soon as they load. However, we cannot guarantee
+ * this, because it would lead us to the lock ordering violation
+ * outlined above. The common case, of course, is that cpu_lock is
+ * _not_ held -- so we delay here for a clock tick, hoping that that's
+ * long enough for the task queue to do its work. If it's not, it's
+ * not a serious problem -- it just means that the module that we
+ * just loaded may not be immediately instrumentable.
+ */
+ delay(1);
+}
+
+static void
+dtrace_module_unloaded(modctl_t *ctl)
+{
+ dtrace_probe_t template, *probe, *first, *next;
+ dtrace_provider_t *prov;
+
+ template.dtpr_mod = ctl->mod_modname;
+
+ mutex_enter(&dtrace_provider_lock);
+ mutex_enter(&mod_lock);
+ mutex_enter(&dtrace_lock);
+
+ if (dtrace_bymod == NULL) {
+ /*
+ * The DTrace module is loaded (obviously) but not attached;
+ * we don't have any work to do.
+ */
+ mutex_exit(&dtrace_provider_lock);
+ mutex_exit(&mod_lock);
+ mutex_exit(&dtrace_lock);
+ return;
+ }
+
+ for (probe = first = dtrace_hash_lookup(dtrace_bymod, &template);
+ probe != NULL; probe = probe->dtpr_nextmod) {
+ if (probe->dtpr_ecb != NULL) {
+ mutex_exit(&dtrace_provider_lock);
+ mutex_exit(&mod_lock);
+ mutex_exit(&dtrace_lock);
+
+ /*
+ * This shouldn't _actually_ be possible -- we're
+ * unloading a module that has an enabled probe in it.
+ * (It's normally up to the provider to make sure that
+ * this can't happen.) However, because dtps_enable()
+ * doesn't have a failure mode, there can be an
+ * enable/unload race. Upshot: we don't want to
+ * assert, but we're not going to disable the
+ * probe, either.
+ */
+ if (dtrace_err_verbose) {
+ cmn_err(CE_WARN, "unloaded module '%s' had "
+ "enabled probes", ctl->mod_modname);
+ }
+
+ return;
+ }
+ }
+
+ probe = first;
+
+ for (first = NULL; probe != NULL; probe = next) {
+ ASSERT(dtrace_probes[probe->dtpr_id - 1] == probe);
+
+ dtrace_probes[probe->dtpr_id - 1] = NULL;
+
+ next = probe->dtpr_nextmod;
+ dtrace_hash_remove(dtrace_bymod, probe);
+ dtrace_hash_remove(dtrace_byfunc, probe);
+ dtrace_hash_remove(dtrace_byname, probe);
+
+ if (first == NULL) {
+ first = probe;
+ probe->dtpr_nextmod = NULL;
+ } else {
+ probe->dtpr_nextmod = first;
+ first = probe;
+ }
+ }
+
+ /*
+ * We've removed all of the module's probes from the hash chains and
+ * from the probe array. Now issue a dtrace_sync() to be sure that
+ * everyone has cleared out from any probe array processing.
+ */
+ dtrace_sync();
+
+ for (probe = first; probe != NULL; probe = first) {
+ first = probe->dtpr_nextmod;
+ prov = probe->dtpr_provider;
+ prov->dtpv_pops.dtps_destroy(prov->dtpv_arg, probe->dtpr_id,
+ probe->dtpr_arg);
+ kmem_free(probe->dtpr_mod, strlen(probe->dtpr_mod) + 1);
+ kmem_free(probe->dtpr_func, strlen(probe->dtpr_func) + 1);
+ kmem_free(probe->dtpr_name, strlen(probe->dtpr_name) + 1);
+ vmem_free(dtrace_arena, (void *)(uintptr_t)probe->dtpr_id, 1);
+ kmem_free(probe, sizeof (dtrace_probe_t));
+ }
+
+ mutex_exit(&dtrace_lock);
+ mutex_exit(&mod_lock);
+ mutex_exit(&dtrace_provider_lock);
+}
+
+static void
+dtrace_suspend(void)
+{
+ dtrace_probe_foreach(offsetof(dtrace_pops_t, dtps_suspend));
+}
+
+static void
+dtrace_resume(void)
+{
+ dtrace_probe_foreach(offsetof(dtrace_pops_t, dtps_resume));
+}
+#endif
+
+static int
+dtrace_cpu_setup(cpu_setup_t what, processorid_t cpu)
+{
+ ASSERT(MUTEX_HELD(&cpu_lock));
+ mutex_enter(&dtrace_lock);
+
+ switch (what) {
+ case CPU_CONFIG: {
+ dtrace_state_t *state;
+ dtrace_optval_t *opt, rs, c;
+
+ /*
+ * For now, we only allocate a new buffer for anonymous state.
+ */
+ if ((state = dtrace_anon.dta_state) == NULL)
+ break;
+
+ if (state->dts_activity != DTRACE_ACTIVITY_ACTIVE)
+ break;
+
+ opt = state->dts_options;
+ c = opt[DTRACEOPT_CPU];
+
+ if (c != DTRACE_CPUALL && c != DTRACEOPT_UNSET && c != cpu)
+ break;
+
+ /*
+ * Regardless of what the actual policy is, we're going to
+ * temporarily set our resize policy to be manual. We're
+ * also going to temporarily set our CPU option to denote
+ * the newly configured CPU.
+ */
+ rs = opt[DTRACEOPT_BUFRESIZE];
+ opt[DTRACEOPT_BUFRESIZE] = DTRACEOPT_BUFRESIZE_MANUAL;
+ opt[DTRACEOPT_CPU] = (dtrace_optval_t)cpu;
+
+ (void) dtrace_state_buffers(state);
+
+ opt[DTRACEOPT_BUFRESIZE] = rs;
+ opt[DTRACEOPT_CPU] = c;
+
+ break;
+ }
+
+ case CPU_UNCONFIG:
+ /*
+ * We don't free the buffer in the CPU_UNCONFIG case. (The
+ * buffer will be freed when the consumer exits.)
+ */
+ break;
+
+ default:
+ break;
+ }
+
+ mutex_exit(&dtrace_lock);
+ return (0);
+}
+
+#if defined(sun)
+static void
+dtrace_cpu_setup_initial(processorid_t cpu)
+{
+ (void) dtrace_cpu_setup(CPU_CONFIG, cpu);
+}
+#endif
+
+static void
+dtrace_toxrange_add(uintptr_t base, uintptr_t limit)
+{
+ if (dtrace_toxranges >= dtrace_toxranges_max) {
+ int osize, nsize;
+ dtrace_toxrange_t *range;
+
+ osize = dtrace_toxranges_max * sizeof (dtrace_toxrange_t);
+
+ if (osize == 0) {
+ ASSERT(dtrace_toxrange == NULL);
+ ASSERT(dtrace_toxranges_max == 0);
+ dtrace_toxranges_max = 1;
+ } else {
+ dtrace_toxranges_max <<= 1;
+ }
+
+ nsize = dtrace_toxranges_max * sizeof (dtrace_toxrange_t);
+ range = kmem_zalloc(nsize, KM_SLEEP);
+
+ if (dtrace_toxrange != NULL) {
+ ASSERT(osize != 0);
+ bcopy(dtrace_toxrange, range, osize);
+ kmem_free(dtrace_toxrange, osize);
+ }
+
+ dtrace_toxrange = range;
+ }
+
+ ASSERT(dtrace_toxrange[dtrace_toxranges].dtt_base == 0);
+ ASSERT(dtrace_toxrange[dtrace_toxranges].dtt_limit == 0);
+
+ dtrace_toxrange[dtrace_toxranges].dtt_base = base;
+ dtrace_toxrange[dtrace_toxranges].dtt_limit = limit;
+ dtrace_toxranges++;
+}
+
+/*
+ * DTrace Driver Cookbook Functions
+ */
+#if defined(sun)
+/*ARGSUSED*/
+static int
+dtrace_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
+{
+ dtrace_provider_id_t id;
+ dtrace_state_t *state = NULL;
+ dtrace_enabling_t *enab;
+
+ mutex_enter(&cpu_lock);
+ mutex_enter(&dtrace_provider_lock);
+ mutex_enter(&dtrace_lock);
+
+ if (ddi_soft_state_init(&dtrace_softstate,
+ sizeof (dtrace_state_t), 0) != 0) {
+ cmn_err(CE_NOTE, "/dev/dtrace failed to initialize soft state");
+ mutex_exit(&cpu_lock);
+ mutex_exit(&dtrace_provider_lock);
+ mutex_exit(&dtrace_lock);
+ return (DDI_FAILURE);
+ }
+
+ if (ddi_create_minor_node(devi, DTRACEMNR_DTRACE, S_IFCHR,
+ DTRACEMNRN_DTRACE, DDI_PSEUDO, NULL) == DDI_FAILURE ||
+ ddi_create_minor_node(devi, DTRACEMNR_HELPER, S_IFCHR,
+ DTRACEMNRN_HELPER, DDI_PSEUDO, NULL) == DDI_FAILURE) {
+ cmn_err(CE_NOTE, "/dev/dtrace couldn't create minor nodes");
+ ddi_remove_minor_node(devi, NULL);
+ ddi_soft_state_fini(&dtrace_softstate);
+ mutex_exit(&cpu_lock);
+ mutex_exit(&dtrace_provider_lock);
+ mutex_exit(&dtrace_lock);
+ return (DDI_FAILURE);
+ }
+
+ ddi_report_dev(devi);
+ dtrace_devi = devi;
+
+ dtrace_modload = dtrace_module_loaded;
+ dtrace_modunload = dtrace_module_unloaded;
+ dtrace_cpu_init = dtrace_cpu_setup_initial;
+ dtrace_helpers_cleanup = dtrace_helpers_destroy;
+ dtrace_helpers_fork = dtrace_helpers_duplicate;
+ dtrace_cpustart_init = dtrace_suspend;
+ dtrace_cpustart_fini = dtrace_resume;
+ dtrace_debugger_init = dtrace_suspend;
+ dtrace_debugger_fini = dtrace_resume;
+
+ register_cpu_setup_func((cpu_setup_func_t *)dtrace_cpu_setup, NULL);
+
+ ASSERT(MUTEX_HELD(&cpu_lock));
+
+ dtrace_arena = vmem_create("dtrace", (void *)1, UINT32_MAX, 1,
+ NULL, NULL, NULL, 0, VM_SLEEP | VMC_IDENTIFIER);
+ dtrace_minor = vmem_create("dtrace_minor", (void *)DTRACEMNRN_CLONE,
+ UINT32_MAX - DTRACEMNRN_CLONE, 1, NULL, NULL, NULL, 0,
+ VM_SLEEP | VMC_IDENTIFIER);
+ dtrace_taskq = taskq_create("dtrace_taskq", 1, maxclsyspri,
+ 1, INT_MAX, 0);
+
+ dtrace_state_cache = kmem_cache_create("dtrace_state_cache",
+ sizeof (dtrace_dstate_percpu_t) * NCPU, DTRACE_STATE_ALIGN,
+ NULL, NULL, NULL, NULL, NULL, 0);
+
+ ASSERT(MUTEX_HELD(&cpu_lock));
+ dtrace_bymod = dtrace_hash_create(offsetof(dtrace_probe_t, dtpr_mod),
+ offsetof(dtrace_probe_t, dtpr_nextmod),
+ offsetof(dtrace_probe_t, dtpr_prevmod));
+
+ dtrace_byfunc = dtrace_hash_create(offsetof(dtrace_probe_t, dtpr_func),
+ offsetof(dtrace_probe_t, dtpr_nextfunc),
+ offsetof(dtrace_probe_t, dtpr_prevfunc));
+
+ dtrace_byname = dtrace_hash_create(offsetof(dtrace_probe_t, dtpr_name),
+ offsetof(dtrace_probe_t, dtpr_nextname),
+ offsetof(dtrace_probe_t, dtpr_prevname));
+
+ if (dtrace_retain_max < 1) {
+ cmn_err(CE_WARN, "illegal value (%lu) for dtrace_retain_max; "
+ "setting to 1", dtrace_retain_max);
+ dtrace_retain_max = 1;
+ }
+
+ /*
+ * Now discover our toxic ranges.
+ */
+ dtrace_toxic_ranges(dtrace_toxrange_add);
+
+ /*
+ * Before we register ourselves as a provider to our own framework,
+ * we would like to assert that dtrace_provider is NULL -- but that's
+ * not true if we were loaded as a dependency of a DTrace provider.
+ * Once we've registered, we can assert that dtrace_provider is our
+ * pseudo provider.
+ */
+ (void) dtrace_register("dtrace", &dtrace_provider_attr,
+ DTRACE_PRIV_NONE, 0, &dtrace_provider_ops, NULL, &id);
+
+ ASSERT(dtrace_provider != NULL);
+ ASSERT((dtrace_provider_id_t)dtrace_provider == id);
+
+ dtrace_probeid_begin = dtrace_probe_create((dtrace_provider_id_t)
+ dtrace_provider, NULL, NULL, "BEGIN", 0, NULL);
+ dtrace_probeid_end = dtrace_probe_create((dtrace_provider_id_t)
+ dtrace_provider, NULL, NULL, "END", 0, NULL);
+ dtrace_probeid_error = dtrace_probe_create((dtrace_provider_id_t)
+ dtrace_provider, NULL, NULL, "ERROR", 1, NULL);
+
+ dtrace_anon_property();
+ mutex_exit(&cpu_lock);
+
+ /*
+ * If DTrace helper tracing is enabled, we need to allocate the
+ * trace buffer and initialize the values.
+ */
+ if (dtrace_helptrace_enabled) {
+ ASSERT(dtrace_helptrace_buffer == NULL);
+ dtrace_helptrace_buffer =
+ kmem_zalloc(dtrace_helptrace_bufsize, KM_SLEEP);
+ dtrace_helptrace_next = 0;
+ }
+
+ /*
+ * If there are already providers, we must ask them to provide their
+ * probes, and then match any anonymous enabling against them. Note
+ * that there should be no other retained enablings at this time:
+ * the only retained enablings at this time should be the anonymous
+ * enabling.
+ */
+ if (dtrace_anon.dta_enabling != NULL) {
+ ASSERT(dtrace_retained == dtrace_anon.dta_enabling);
+
+ dtrace_enabling_provide(NULL);
+ state = dtrace_anon.dta_state;
+
+ /*
+ * We couldn't hold cpu_lock across the above call to
+ * dtrace_enabling_provide(), but we must hold it to actually
+ * enable the probes. We have to drop all of our locks, pick
+ * up cpu_lock, and regain our locks before matching the
+ * retained anonymous enabling.
+ */
+ mutex_exit(&dtrace_lock);
+ mutex_exit(&dtrace_provider_lock);
+
+ mutex_enter(&cpu_lock);
+ mutex_enter(&dtrace_provider_lock);
+ mutex_enter(&dtrace_lock);
+
+ if ((enab = dtrace_anon.dta_enabling) != NULL)
+ (void) dtrace_enabling_match(enab, NULL);
+
+ mutex_exit(&cpu_lock);
+ }
+
+ mutex_exit(&dtrace_lock);
+ mutex_exit(&dtrace_provider_lock);
+
+ if (state != NULL) {
+ /*
+ * If we created any anonymous state, set it going now.
+ */
+ (void) dtrace_state_go(state, &dtrace_anon.dta_beganon);
+ }
+
+ return (DDI_SUCCESS);
+}
+#endif
+
+#if !defined(sun)
+static void
+dtrace_dtr(void *data __unused)
+{
+}
+#endif
+
+/*ARGSUSED*/
+static int
+#if defined(sun)
+dtrace_open(dev_t *devp, int flag, int otyp, cred_t *cred_p)
+#else
+dtrace_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
+#endif
+{
+ dtrace_state_t *state;
+ uint32_t priv;
+ uid_t uid;
+ zoneid_t zoneid;
+
+#if defined(sun)
+ if (getminor(*devp) == DTRACEMNRN_HELPER)
+ return (0);
+
+ /*
+ * If this wasn't an open with the "helper" minor, then it must be
+ * the "dtrace" minor.
+ */
+ ASSERT(getminor(*devp) == DTRACEMNRN_DTRACE);
+#else
+ cred_t *cred_p = NULL;
+
+ cred_p = dev->si_cred;
+#endif
+
+ /*
+ * If no DTRACE_PRIV_* bits are set in the credential, then the
+ * caller lacks sufficient permission to do anything with DTrace.
+ */
+ dtrace_cred2priv(cred_p, &priv, &uid, &zoneid);
+ if (priv == DTRACE_PRIV_NONE) {
+ return (EACCES);
+ }
+
+ /*
+ * Ask all providers to provide all their probes.
+ */
+ mutex_enter(&dtrace_provider_lock);
+ dtrace_probe_provide(NULL, NULL);
+ mutex_exit(&dtrace_provider_lock);
+
+ mutex_enter(&cpu_lock);
+ mutex_enter(&dtrace_lock);
+ dtrace_opens++;
+ dtrace_membar_producer();
+
+#if defined(sun)
+ /*
+ * If the kernel debugger is active (that is, if the kernel debugger
+ * modified text in some way), we won't allow the open.
+ */
+ if (kdi_dtrace_set(KDI_DTSET_DTRACE_ACTIVATE) != 0) {
+ dtrace_opens--;
+ mutex_exit(&cpu_lock);
+ mutex_exit(&dtrace_lock);
+ return (EBUSY);
+ }
+
+ state = dtrace_state_create(devp, cred_p);
+#else
+ state = dtrace_state_create(dev);
+ devfs_set_cdevpriv(state, dtrace_dtr);
+#endif
+
+ mutex_exit(&cpu_lock);
+
+ if (state == NULL) {
+#if defined(sun)
+ if (--dtrace_opens == 0)
+ (void) kdi_dtrace_set(KDI_DTSET_DTRACE_DEACTIVATE);
+#else
+ --dtrace_opens;
+#endif
+ mutex_exit(&dtrace_lock);
+ return (EAGAIN);
+ }
+
+ mutex_exit(&dtrace_lock);
+
+ return (0);
+}
+
+/*ARGSUSED*/
+static int
+#if defined(sun)
+dtrace_close(dev_t dev, int flag, int otyp, cred_t *cred_p)
+#else
+dtrace_close(struct cdev *dev, int flags, int fmt __unused, struct thread *td)
+#endif
+{
+#if defined(sun)
+ minor_t minor = getminor(dev);
+ dtrace_state_t *state;
+
+ if (minor == DTRACEMNRN_HELPER)
+ return (0);
+
+ state = ddi_get_soft_state(dtrace_softstate, minor);
+#else
+ dtrace_state_t *state;
+ devfs_get_cdevpriv((void **) &state);
+
+#endif
+
+ mutex_enter(&cpu_lock);
+ mutex_enter(&dtrace_lock);
+
+ if (state != NULL) {
+ if (state->dts_anon) {
+ /*
+ * There is anonymous state. Destroy that first.
+ */
+ ASSERT(dtrace_anon.dta_state == NULL);
+ dtrace_state_destroy(state->dts_anon);
+ }
+
+ dtrace_state_destroy(state);
+
+#if !defined(sun)
+ kmem_free(state, 0);
+ devfs_clear_cdevpriv();
+#endif
+ }
+
+ ASSERT(dtrace_opens > 0);
+#if defined(sun)
+ if (--dtrace_opens == 0)
+ (void) kdi_dtrace_set(KDI_DTSET_DTRACE_DEACTIVATE);
+#else
+ --dtrace_opens;
+#endif
+
+ mutex_exit(&dtrace_lock);
+ mutex_exit(&cpu_lock);
+
+ return (0);
+}
+
+#if defined(sun)
+/*ARGSUSED*/
+static int
+dtrace_ioctl_helper(int cmd, intptr_t arg, int *rv)
+{
+ int rval;
+ dof_helper_t help, *dhp = NULL;
+
+ switch (cmd) {
+ case DTRACEHIOC_ADDDOF:
+ if (copyin((void *)arg, &help, sizeof (help)) != 0) {
+ dtrace_dof_error(NULL, "failed to copyin DOF helper");
+ return (EFAULT);
+ }
+
+ dhp = &help;
+ arg = (intptr_t)help.dofhp_dof;
+ /*FALLTHROUGH*/
+
+ case DTRACEHIOC_ADD: {
+ dof_hdr_t *dof = dtrace_dof_copyin(arg, &rval);
+
+ if (dof == NULL)
+ return (rval);
+
+ mutex_enter(&dtrace_lock);
+
+ /*
+ * dtrace_helper_slurp() takes responsibility for the dof --
+ * it may free it now or it may save it and free it later.
+ */
+ if ((rval = dtrace_helper_slurp(dof, dhp)) != -1) {
+ *rv = rval;
+ rval = 0;
+ } else {
+ rval = EINVAL;
+ }
+
+ mutex_exit(&dtrace_lock);
+ return (rval);
+ }
+
+ case DTRACEHIOC_REMOVE: {
+ mutex_enter(&dtrace_lock);
+ rval = dtrace_helper_destroygen(arg);
+ mutex_exit(&dtrace_lock);
+
+ return (rval);
+ }
+
+ default:
+ break;
+ }
+
+ return (ENOTTY);
+}
+
+/*ARGSUSED*/
+static int
+dtrace_ioctl(dev_t dev, int cmd, intptr_t arg, int md, cred_t *cr, int *rv)
+{
+ minor_t minor = getminor(dev);
+ dtrace_state_t *state;
+ int rval;
+
+ if (minor == DTRACEMNRN_HELPER)
+ return (dtrace_ioctl_helper(cmd, arg, rv));
+
+ state = ddi_get_soft_state(dtrace_softstate, minor);
+
+ if (state->dts_anon) {
+ ASSERT(dtrace_anon.dta_state == NULL);
+ state = state->dts_anon;
+ }
+
+ switch (cmd) {
+ case DTRACEIOC_PROVIDER: {
+ dtrace_providerdesc_t pvd;
+ dtrace_provider_t *pvp;
+
+ if (copyin((void *)arg, &pvd, sizeof (pvd)) != 0)
+ return (EFAULT);
+
+ pvd.dtvd_name[DTRACE_PROVNAMELEN - 1] = '\0';
+ mutex_enter(&dtrace_provider_lock);
+
+ for (pvp = dtrace_provider; pvp != NULL; pvp = pvp->dtpv_next) {
+ if (strcmp(pvp->dtpv_name, pvd.dtvd_name) == 0)
+ break;
+ }
+
+ mutex_exit(&dtrace_provider_lock);
+
+ if (pvp == NULL)
+ return (ESRCH);
+
+ bcopy(&pvp->dtpv_priv, &pvd.dtvd_priv, sizeof (dtrace_ppriv_t));
+ bcopy(&pvp->dtpv_attr, &pvd.dtvd_attr, sizeof (dtrace_pattr_t));
+
+ if (copyout(&pvd, (void *)arg, sizeof (pvd)) != 0)
+ return (EFAULT);
+
+ return (0);
+ }
+
+ case DTRACEIOC_EPROBE: {
+ dtrace_eprobedesc_t epdesc;
+ dtrace_ecb_t *ecb;
+ dtrace_action_t *act;
+ void *buf;
+ size_t size;
+ uintptr_t dest;
+ int nrecs;
+
+ if (copyin((void *)arg, &epdesc, sizeof (epdesc)) != 0)
+ return (EFAULT);
+
+ mutex_enter(&dtrace_lock);
+
+ if ((ecb = dtrace_epid2ecb(state, epdesc.dtepd_epid)) == NULL) {
+ mutex_exit(&dtrace_lock);
+ return (EINVAL);
+ }
+
+ if (ecb->dte_probe == NULL) {
+ mutex_exit(&dtrace_lock);
+ return (EINVAL);
+ }
+
+ epdesc.dtepd_probeid = ecb->dte_probe->dtpr_id;
+ epdesc.dtepd_uarg = ecb->dte_uarg;
+ epdesc.dtepd_size = ecb->dte_size;
+
+ nrecs = epdesc.dtepd_nrecs;
+ epdesc.dtepd_nrecs = 0;
+ for (act = ecb->dte_action; act != NULL; act = act->dta_next) {
+ if (DTRACEACT_ISAGG(act->dta_kind) || act->dta_intuple)
+ continue;
+
+ epdesc.dtepd_nrecs++;
+ }
+
+ /*
+ * Now that we have the size, we need to allocate a temporary
+ * buffer in which to store the complete description. We need
+ * the temporary buffer to be able to drop dtrace_lock()
+ * across the copyout(), below.
+ */
+ size = sizeof (dtrace_eprobedesc_t) +
+ (epdesc.dtepd_nrecs * sizeof (dtrace_recdesc_t));
+
+ buf = kmem_alloc(size, KM_SLEEP);
+ dest = (uintptr_t)buf;
+
+ bcopy(&epdesc, (void *)dest, sizeof (epdesc));
+ dest += offsetof(dtrace_eprobedesc_t, dtepd_rec[0]);
+
+ for (act = ecb->dte_action; act != NULL; act = act->dta_next) {
+ if (DTRACEACT_ISAGG(act->dta_kind) || act->dta_intuple)
+ continue;
+
+ if (nrecs-- == 0)
+ break;
+
+ bcopy(&act->dta_rec, (void *)dest,
+ sizeof (dtrace_recdesc_t));
+ dest += sizeof (dtrace_recdesc_t);
+ }
+
+ mutex_exit(&dtrace_lock);
+
+ if (copyout(buf, (void *)arg, dest - (uintptr_t)buf) != 0) {
+ kmem_free(buf, size);
+ return (EFAULT);
+ }
+
+ kmem_free(buf, size);
+ return (0);
+ }
+
+ case DTRACEIOC_AGGDESC: {
+ dtrace_aggdesc_t aggdesc;
+ dtrace_action_t *act;
+ dtrace_aggregation_t *agg;
+ int nrecs;
+ uint32_t offs;
+ dtrace_recdesc_t *lrec;
+ void *buf;
+ size_t size;
+ uintptr_t dest;
+
+ if (copyin((void *)arg, &aggdesc, sizeof (aggdesc)) != 0)
+ return (EFAULT);
+
+ mutex_enter(&dtrace_lock);
+
+ if ((agg = dtrace_aggid2agg(state, aggdesc.dtagd_id)) == NULL) {
+ mutex_exit(&dtrace_lock);
+ return (EINVAL);
+ }
+
+ aggdesc.dtagd_epid = agg->dtag_ecb->dte_epid;
+
+ nrecs = aggdesc.dtagd_nrecs;
+ aggdesc.dtagd_nrecs = 0;
+
+ offs = agg->dtag_base;
+ lrec = &agg->dtag_action.dta_rec;
+ aggdesc.dtagd_size = lrec->dtrd_offset + lrec->dtrd_size - offs;
+
+ for (act = agg->dtag_first; ; act = act->dta_next) {
+ ASSERT(act->dta_intuple ||
+ DTRACEACT_ISAGG(act->dta_kind));
+
+ /*
+ * If this action has a record size of zero, it
+ * denotes an argument to the aggregating action.
+ * Because the presence of this record doesn't (or
+ * shouldn't) affect the way the data is interpreted,
+ * we don't copy it out to save user-level the
+ * confusion of dealing with a zero-length record.
+ */
+ if (act->dta_rec.dtrd_size == 0) {
+ ASSERT(agg->dtag_hasarg);
+ continue;
+ }
+
+ aggdesc.dtagd_nrecs++;
+
+ if (act == &agg->dtag_action)
+ break;
+ }
+
+ /*
+ * Now that we have the size, we need to allocate a temporary
+ * buffer in which to store the complete description. We need
+ * the temporary buffer to be able to drop dtrace_lock()
+ * across the copyout(), below.
+ */
+ size = sizeof (dtrace_aggdesc_t) +
+ (aggdesc.dtagd_nrecs * sizeof (dtrace_recdesc_t));
+
+ buf = kmem_alloc(size, KM_SLEEP);
+ dest = (uintptr_t)buf;
+
+ bcopy(&aggdesc, (void *)dest, sizeof (aggdesc));
+ dest += offsetof(dtrace_aggdesc_t, dtagd_rec[0]);
+
+ for (act = agg->dtag_first; ; act = act->dta_next) {
+ dtrace_recdesc_t rec = act->dta_rec;
+
+ /*
+ * See the comment in the above loop for why we pass
+ * over zero-length records.
+ */
+ if (rec.dtrd_size == 0) {
+ ASSERT(agg->dtag_hasarg);
+ continue;
+ }
+
+ if (nrecs-- == 0)
+ break;
+
+ rec.dtrd_offset -= offs;
+ bcopy(&rec, (void *)dest, sizeof (rec));
+ dest += sizeof (dtrace_recdesc_t);
+
+ if (act == &agg->dtag_action)
+ break;
+ }
+
+ mutex_exit(&dtrace_lock);
+
+ if (copyout(buf, (void *)arg, dest - (uintptr_t)buf) != 0) {
+ kmem_free(buf, size);
+ return (EFAULT);
+ }
+
+ kmem_free(buf, size);
+ return (0);
+ }
+
+ case DTRACEIOC_ENABLE: {
+ dof_hdr_t *dof;
+ dtrace_enabling_t *enab = NULL;
+ dtrace_vstate_t *vstate;
+ int err = 0;
+
+ *rv = 0;
+
+ /*
+ * If a NULL argument has been passed, we take this as our
+ * cue to reevaluate our enablings.
+ */
+ if (arg == NULL) {
+ dtrace_enabling_matchall();
+
+ return (0);
+ }
+
+ if ((dof = dtrace_dof_copyin(arg, &rval)) == NULL)
+ return (rval);
+
+ mutex_enter(&cpu_lock);
+ mutex_enter(&dtrace_lock);
+ vstate = &state->dts_vstate;
+
+ if (state->dts_activity != DTRACE_ACTIVITY_INACTIVE) {
+ mutex_exit(&dtrace_lock);
+ mutex_exit(&cpu_lock);
+ dtrace_dof_destroy(dof);
+ return (EBUSY);
+ }
+
+ if (dtrace_dof_slurp(dof, vstate, cr, &enab, 0, B_TRUE) != 0) {
+ mutex_exit(&dtrace_lock);
+ mutex_exit(&cpu_lock);
+ dtrace_dof_destroy(dof);
+ return (EINVAL);
+ }
+
+ if ((rval = dtrace_dof_options(dof, state)) != 0) {
+ dtrace_enabling_destroy(enab);
+ mutex_exit(&dtrace_lock);
+ mutex_exit(&cpu_lock);
+ dtrace_dof_destroy(dof);
+ return (rval);
+ }
+
+ if ((err = dtrace_enabling_match(enab, rv)) == 0) {
+ err = dtrace_enabling_retain(enab);
+ } else {
+ dtrace_enabling_destroy(enab);
+ }
+
+ mutex_exit(&cpu_lock);
+ mutex_exit(&dtrace_lock);
+ dtrace_dof_destroy(dof);
+
+ return (err);
+ }
+
+ case DTRACEIOC_REPLICATE: {
+ dtrace_repldesc_t desc;
+ dtrace_probedesc_t *match = &desc.dtrpd_match;
+ dtrace_probedesc_t *create = &desc.dtrpd_create;
+ int err;
+
+ if (copyin((void *)arg, &desc, sizeof (desc)) != 0)
+ return (EFAULT);
+
+ match->dtpd_provider[DTRACE_PROVNAMELEN - 1] = '\0';
+ match->dtpd_mod[DTRACE_MODNAMELEN - 1] = '\0';
+ match->dtpd_func[DTRACE_FUNCNAMELEN - 1] = '\0';
+ match->dtpd_name[DTRACE_NAMELEN - 1] = '\0';
+
+ create->dtpd_provider[DTRACE_PROVNAMELEN - 1] = '\0';
+ create->dtpd_mod[DTRACE_MODNAMELEN - 1] = '\0';
+ create->dtpd_func[DTRACE_FUNCNAMELEN - 1] = '\0';
+ create->dtpd_name[DTRACE_NAMELEN - 1] = '\0';
+
+ mutex_enter(&dtrace_lock);
+ err = dtrace_enabling_replicate(state, match, create);
+ mutex_exit(&dtrace_lock);
+
+ return (err);
+ }
+
+ case DTRACEIOC_PROBEMATCH:
+ case DTRACEIOC_PROBES: {
+ dtrace_probe_t *probe = NULL;
+ dtrace_probedesc_t desc;
+ dtrace_probekey_t pkey;
+ dtrace_id_t i;
+ int m = 0;
+ uint32_t priv;
+ uid_t uid;
+ zoneid_t zoneid;
+
+ if (copyin((void *)arg, &desc, sizeof (desc)) != 0)
+ return (EFAULT);
+
+ desc.dtpd_provider[DTRACE_PROVNAMELEN - 1] = '\0';
+ desc.dtpd_mod[DTRACE_MODNAMELEN - 1] = '\0';
+ desc.dtpd_func[DTRACE_FUNCNAMELEN - 1] = '\0';
+ desc.dtpd_name[DTRACE_NAMELEN - 1] = '\0';
+
+ /*
+ * Before we attempt to match this probe, we want to give
+ * all providers the opportunity to provide it.
+ */
+ if (desc.dtpd_id == DTRACE_IDNONE) {
+ mutex_enter(&dtrace_provider_lock);
+ dtrace_probe_provide(&desc, NULL);
+ mutex_exit(&dtrace_provider_lock);
+ desc.dtpd_id++;
+ }
+
+ if (cmd == DTRACEIOC_PROBEMATCH) {
+ dtrace_probekey(&desc, &pkey);
+ pkey.dtpk_id = DTRACE_IDNONE;
+ }
+
+ dtrace_cred2priv(cr, &priv, &uid, &zoneid);
+
+ mutex_enter(&dtrace_lock);
+
+ if (cmd == DTRACEIOC_PROBEMATCH) {
+ for (i = desc.dtpd_id; i <= dtrace_nprobes; i++) {
+ if ((probe = dtrace_probes[i - 1]) != NULL &&
+ (m = dtrace_match_probe(probe, &pkey,
+ priv, uid, zoneid)) != 0)
+ break;
+ }
+
+ if (m < 0) {
+ mutex_exit(&dtrace_lock);
+ return (EINVAL);
+ }
+
+ } else {
+ for (i = desc.dtpd_id; i <= dtrace_nprobes; i++) {
+ if ((probe = dtrace_probes[i - 1]) != NULL &&
+ dtrace_match_priv(probe, priv, uid, zoneid))
+ break;
+ }
+ }
+
+ if (probe == NULL) {
+ mutex_exit(&dtrace_lock);
+ return (ESRCH);
+ }
+
+ dtrace_probe_description(probe, &desc);
+ mutex_exit(&dtrace_lock);
+
+ if (copyout(&desc, (void *)arg, sizeof (desc)) != 0)
+ return (EFAULT);
+
+ return (0);
+ }
+
+ case DTRACEIOC_PROBEARG: {
+ dtrace_argdesc_t desc;
+ dtrace_probe_t *probe;
+ dtrace_provider_t *prov;
+
+ if (copyin((void *)arg, &desc, sizeof (desc)) != 0)
+ return (EFAULT);
+
+ if (desc.dtargd_id == DTRACE_IDNONE)
+ return (EINVAL);
+
+ if (desc.dtargd_ndx == DTRACE_ARGNONE)
+ return (EINVAL);
+
+ mutex_enter(&dtrace_provider_lock);
+ mutex_enter(&mod_lock);
+ mutex_enter(&dtrace_lock);
+
+ if (desc.dtargd_id > dtrace_nprobes) {
+ mutex_exit(&dtrace_lock);
+ mutex_exit(&mod_lock);
+ mutex_exit(&dtrace_provider_lock);
+ return (EINVAL);
+ }
+
+ if ((probe = dtrace_probes[desc.dtargd_id - 1]) == NULL) {
+ mutex_exit(&dtrace_lock);
+ mutex_exit(&mod_lock);
+ mutex_exit(&dtrace_provider_lock);
+ return (EINVAL);
+ }
+
+ mutex_exit(&dtrace_lock);
+
+ prov = probe->dtpr_provider;
+
+ if (prov->dtpv_pops.dtps_getargdesc == NULL) {
+ /*
+ * There isn't any typed information for this probe.
+ * Set the argument number to DTRACE_ARGNONE.
+ */
+ desc.dtargd_ndx = DTRACE_ARGNONE;
+ } else {
+ desc.dtargd_native[0] = '\0';
+ desc.dtargd_xlate[0] = '\0';
+ desc.dtargd_mapping = desc.dtargd_ndx;
+
+ prov->dtpv_pops.dtps_getargdesc(prov->dtpv_arg,
+ probe->dtpr_id, probe->dtpr_arg, &desc);
+ }
+
+ mutex_exit(&mod_lock);
+ mutex_exit(&dtrace_provider_lock);
+
+ if (copyout(&desc, (void *)arg, sizeof (desc)) != 0)
+ return (EFAULT);
+
+ return (0);
+ }
+
+ case DTRACEIOC_GO: {
+ processorid_t cpuid;
+ rval = dtrace_state_go(state, &cpuid);
+
+ if (rval != 0)
+ return (rval);
+
+ if (copyout(&cpuid, (void *)arg, sizeof (cpuid)) != 0)
+ return (EFAULT);
+
+ return (0);
+ }
+
+ case DTRACEIOC_STOP: {
+ processorid_t cpuid;
+
+ mutex_enter(&dtrace_lock);
+ rval = dtrace_state_stop(state, &cpuid);
+ mutex_exit(&dtrace_lock);
+
+ if (rval != 0)
+ return (rval);
+
+ if (copyout(&cpuid, (void *)arg, sizeof (cpuid)) != 0)
+ return (EFAULT);
+
+ return (0);
+ }
+
+ case DTRACEIOC_DOFGET: {
+ dof_hdr_t hdr, *dof;
+ uint64_t len;
+
+ if (copyin((void *)arg, &hdr, sizeof (hdr)) != 0)
+ return (EFAULT);
+
+ mutex_enter(&dtrace_lock);
+ dof = dtrace_dof_create(state);
+ mutex_exit(&dtrace_lock);
+
+ len = MIN(hdr.dofh_loadsz, dof->dofh_loadsz);
+ rval = copyout(dof, (void *)arg, len);
+ dtrace_dof_destroy(dof);
+
+ return (rval == 0 ? 0 : EFAULT);
+ }
+
+ case DTRACEIOC_AGGSNAP:
+ case DTRACEIOC_BUFSNAP: {
+ dtrace_bufdesc_t desc;
+ caddr_t cached;
+ dtrace_buffer_t *buf;
+
+ if (copyin((void *)arg, &desc, sizeof (desc)) != 0)
+ return (EFAULT);
+
+ if (desc.dtbd_cpu < 0 || desc.dtbd_cpu >= NCPU)
+ return (EINVAL);
+
+ mutex_enter(&dtrace_lock);
+
+ if (cmd == DTRACEIOC_BUFSNAP) {
+ buf = &state->dts_buffer[desc.dtbd_cpu];
+ } else {
+ buf = &state->dts_aggbuffer[desc.dtbd_cpu];
+ }
+
+ if (buf->dtb_flags & (DTRACEBUF_RING | DTRACEBUF_FILL)) {
+ size_t sz = buf->dtb_offset;
+
+ if (state->dts_activity != DTRACE_ACTIVITY_STOPPED) {
+ mutex_exit(&dtrace_lock);
+ return (EBUSY);
+ }
+
+ /*
+ * If this buffer has already been consumed, we're
+ * going to indicate that there's nothing left here
+ * to consume.
+ */
+ if (buf->dtb_flags & DTRACEBUF_CONSUMED) {
+ mutex_exit(&dtrace_lock);
+
+ desc.dtbd_size = 0;
+ desc.dtbd_drops = 0;
+ desc.dtbd_errors = 0;
+ desc.dtbd_oldest = 0;
+ sz = sizeof (desc);
+
+ if (copyout(&desc, (void *)arg, sz) != 0)
+ return (EFAULT);
+
+ return (0);
+ }
+
+ /*
+ * If this is a ring buffer that has wrapped, we want
+ * to copy the whole thing out.
+ */
+ if (buf->dtb_flags & DTRACEBUF_WRAPPED) {
+ dtrace_buffer_polish(buf);
+ sz = buf->dtb_size;
+ }
+
+ if (copyout(buf->dtb_tomax, desc.dtbd_data, sz) != 0) {
+ mutex_exit(&dtrace_lock);
+ return (EFAULT);
+ }
+
+ desc.dtbd_size = sz;
+ desc.dtbd_drops = buf->dtb_drops;
+ desc.dtbd_errors = buf->dtb_errors;
+ desc.dtbd_oldest = buf->dtb_xamot_offset;
+
+ mutex_exit(&dtrace_lock);
+
+ if (copyout(&desc, (void *)arg, sizeof (desc)) != 0)
+ return (EFAULT);
+
+ buf->dtb_flags |= DTRACEBUF_CONSUMED;
+
+ return (0);
+ }
+
+ if (buf->dtb_tomax == NULL) {
+ ASSERT(buf->dtb_xamot == NULL);
+ mutex_exit(&dtrace_lock);
+ return (ENOENT);
+ }
+
+ cached = buf->dtb_tomax;
+ ASSERT(!(buf->dtb_flags & DTRACEBUF_NOSWITCH));
+
+ dtrace_xcall(desc.dtbd_cpu,
+ (dtrace_xcall_t)dtrace_buffer_switch, buf);
+
+ state->dts_errors += buf->dtb_xamot_errors;
+
+ /*
+ * If the buffers did not actually switch, then the cross call
+ * did not take place -- presumably because the given CPU is
+ * not in the ready set. If this is the case, we'll return
+ * ENOENT.
+ */
+ if (buf->dtb_tomax == cached) {
+ ASSERT(buf->dtb_xamot != cached);
+ mutex_exit(&dtrace_lock);
+ return (ENOENT);
+ }
+
+ ASSERT(cached == buf->dtb_xamot);
+
+ /*
+ * We have our snapshot; now copy it out.
+ */
+ if (copyout(buf->dtb_xamot, desc.dtbd_data,
+ buf->dtb_xamot_offset) != 0) {
+ mutex_exit(&dtrace_lock);
+ return (EFAULT);
+ }
+
+ desc.dtbd_size = buf->dtb_xamot_offset;
+ desc.dtbd_drops = buf->dtb_xamot_drops;
+ desc.dtbd_errors = buf->dtb_xamot_errors;
+ desc.dtbd_oldest = 0;
+
+ mutex_exit(&dtrace_lock);
+
+ /*
+ * Finally, copy out the buffer description.
+ */
+ if (copyout(&desc, (void *)arg, sizeof (desc)) != 0)
+ return (EFAULT);
+
+ return (0);
+ }
+
+ case DTRACEIOC_CONF: {
+ dtrace_conf_t conf;
+
+ bzero(&conf, sizeof (conf));
+ conf.dtc_difversion = DIF_VERSION;
+ conf.dtc_difintregs = DIF_DIR_NREGS;
+ conf.dtc_diftupregs = DIF_DTR_NREGS;
+ conf.dtc_ctfmodel = CTF_MODEL_NATIVE;
+
+ if (copyout(&conf, (void *)arg, sizeof (conf)) != 0)
+ return (EFAULT);
+
+ return (0);
+ }
+
+ case DTRACEIOC_STATUS: {
+ dtrace_status_t stat;
+ dtrace_dstate_t *dstate;
+ int i, j;
+ uint64_t nerrs;
+
+ /*
+ * See the comment in dtrace_state_deadman() for the reason
+ * for setting dts_laststatus to INT64_MAX before setting
+ * it to the correct value.
+ */
+ state->dts_laststatus = INT64_MAX;
+ dtrace_membar_producer();
+ state->dts_laststatus = dtrace_gethrtime();
+
+ bzero(&stat, sizeof (stat));
+
+ mutex_enter(&dtrace_lock);
+
+ if (state->dts_activity == DTRACE_ACTIVITY_INACTIVE) {
+ mutex_exit(&dtrace_lock);
+ return (ENOENT);
+ }
+
+ if (state->dts_activity == DTRACE_ACTIVITY_DRAINING)
+ stat.dtst_exiting = 1;
+
+ nerrs = state->dts_errors;
+ dstate = &state->dts_vstate.dtvs_dynvars;
+
+ for (i = 0; i < NCPU; i++) {
+ dtrace_dstate_percpu_t *dcpu = &dstate->dtds_percpu[i];
+
+ stat.dtst_dyndrops += dcpu->dtdsc_drops;
+ stat.dtst_dyndrops_dirty += dcpu->dtdsc_dirty_drops;
+ stat.dtst_dyndrops_rinsing += dcpu->dtdsc_rinsing_drops;
+
+ if (state->dts_buffer[i].dtb_flags & DTRACEBUF_FULL)
+ stat.dtst_filled++;
+
+ nerrs += state->dts_buffer[i].dtb_errors;
+
+ for (j = 0; j < state->dts_nspeculations; j++) {
+ dtrace_speculation_t *spec;
+ dtrace_buffer_t *buf;
+
+ spec = &state->dts_speculations[j];
+ buf = &spec->dtsp_buffer[i];
+ stat.dtst_specdrops += buf->dtb_xamot_drops;
+ }
+ }
+
+ stat.dtst_specdrops_busy = state->dts_speculations_busy;
+ stat.dtst_specdrops_unavail = state->dts_speculations_unavail;
+ stat.dtst_stkstroverflows = state->dts_stkstroverflows;
+ stat.dtst_dblerrors = state->dts_dblerrors;
+ stat.dtst_killed =
+ (state->dts_activity == DTRACE_ACTIVITY_KILLED);
+ stat.dtst_errors = nerrs;
+
+ mutex_exit(&dtrace_lock);
+
+ if (copyout(&stat, (void *)arg, sizeof (stat)) != 0)
+ return (EFAULT);
+
+ return (0);
+ }
+
+ case DTRACEIOC_FORMAT: {
+ dtrace_fmtdesc_t fmt;
+ char *str;
+ int len;
+
+ if (copyin((void *)arg, &fmt, sizeof (fmt)) != 0)
+ return (EFAULT);
+
+ mutex_enter(&dtrace_lock);
+
+ if (fmt.dtfd_format == 0 ||
+ fmt.dtfd_format > state->dts_nformats) {
+ mutex_exit(&dtrace_lock);
+ return (EINVAL);
+ }
+
+ /*
+ * Format strings are allocated contiguously and they are
+ * never freed; if a format index is less than the number
+ * of formats, we can assert that the format map is non-NULL
+ * and that the format for the specified index is non-NULL.
+ */
+ ASSERT(state->dts_formats != NULL);
+ str = state->dts_formats[fmt.dtfd_format - 1];
+ ASSERT(str != NULL);
+
+ len = strlen(str) + 1;
+
+ if (len > fmt.dtfd_length) {
+ fmt.dtfd_length = len;
+
+ if (copyout(&fmt, (void *)arg, sizeof (fmt)) != 0) {
+ mutex_exit(&dtrace_lock);
+ return (EINVAL);
+ }
+ } else {
+ if (copyout(str, fmt.dtfd_string, len) != 0) {
+ mutex_exit(&dtrace_lock);
+ return (EINVAL);
+ }
+ }
+
+ mutex_exit(&dtrace_lock);
+ return (0);
+ }
+
+ default:
+ break;
+ }
+
+ return (ENOTTY);
+}
+
+/*ARGSUSED*/
+static int
+dtrace_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+ dtrace_state_t *state;
+
+ switch (cmd) {
+ case DDI_DETACH:
+ break;
+
+ case DDI_SUSPEND:
+ return (DDI_SUCCESS);
+
+ default:
+ return (DDI_FAILURE);
+ }
+
+ mutex_enter(&cpu_lock);
+ mutex_enter(&dtrace_provider_lock);
+ mutex_enter(&dtrace_lock);
+
+ ASSERT(dtrace_opens == 0);
+
+ if (dtrace_helpers > 0) {
+ mutex_exit(&dtrace_provider_lock);
+ mutex_exit(&dtrace_lock);
+ mutex_exit(&cpu_lock);
+ return (DDI_FAILURE);
+ }
+
+ if (dtrace_unregister((dtrace_provider_id_t)dtrace_provider) != 0) {
+ mutex_exit(&dtrace_provider_lock);
+ mutex_exit(&dtrace_lock);
+ mutex_exit(&cpu_lock);
+ return (DDI_FAILURE);
+ }
+
+ dtrace_provider = NULL;
+
+ if ((state = dtrace_anon_grab()) != NULL) {
+ /*
+ * If there were ECBs on this state, the provider should
+ * have not been allowed to detach; assert that there is
+ * none.
+ */
+ ASSERT(state->dts_necbs == 0);
+ dtrace_state_destroy(state);
+
+ /*
+ * If we're being detached with anonymous state, we need to
+ * indicate to the kernel debugger that DTrace is now inactive.
+ */
+ (void) kdi_dtrace_set(KDI_DTSET_DTRACE_DEACTIVATE);
+ }
+
+ bzero(&dtrace_anon, sizeof (dtrace_anon_t));
+ unregister_cpu_setup_func((cpu_setup_func_t *)dtrace_cpu_setup, NULL);
+ dtrace_cpu_init = NULL;
+ dtrace_helpers_cleanup = NULL;
+ dtrace_helpers_fork = NULL;
+ dtrace_cpustart_init = NULL;
+ dtrace_cpustart_fini = NULL;
+ dtrace_debugger_init = NULL;
+ dtrace_debugger_fini = NULL;
+ dtrace_modload = NULL;
+ dtrace_modunload = NULL;
+
+ mutex_exit(&cpu_lock);
+
+ if (dtrace_helptrace_enabled) {
+ kmem_free(dtrace_helptrace_buffer, dtrace_helptrace_bufsize);
+ dtrace_helptrace_buffer = NULL;
+ }
+
+ kmem_free(dtrace_probes, dtrace_nprobes * sizeof (dtrace_probe_t *));
+ dtrace_probes = NULL;
+ dtrace_nprobes = 0;
+
+ dtrace_hash_destroy(dtrace_bymod);
+ dtrace_hash_destroy(dtrace_byfunc);
+ dtrace_hash_destroy(dtrace_byname);
+ dtrace_bymod = NULL;
+ dtrace_byfunc = NULL;
+ dtrace_byname = NULL;
+
+ kmem_cache_destroy(dtrace_state_cache);
+ vmem_destroy(dtrace_minor);
+ vmem_destroy(dtrace_arena);
+
+ if (dtrace_toxrange != NULL) {
+ kmem_free(dtrace_toxrange,
+ dtrace_toxranges_max * sizeof (dtrace_toxrange_t));
+ dtrace_toxrange = NULL;
+ dtrace_toxranges = 0;
+ dtrace_toxranges_max = 0;
+ }
+
+ ddi_remove_minor_node(dtrace_devi, NULL);
+ dtrace_devi = NULL;
+
+ ddi_soft_state_fini(&dtrace_softstate);
+
+ ASSERT(dtrace_vtime_references == 0);
+ ASSERT(dtrace_opens == 0);
+ ASSERT(dtrace_retained == NULL);
+
+ mutex_exit(&dtrace_lock);
+ mutex_exit(&dtrace_provider_lock);
+
+ /*
+ * We don't destroy the task queue until after we have dropped our
+ * locks (taskq_destroy() may block on running tasks). To prevent
+ * attempting to do work after we have effectively detached but before
+ * the task queue has been destroyed, all tasks dispatched via the
+ * task queue must check that DTrace is still attached before
+ * performing any operation.
+ */
+ taskq_destroy(dtrace_taskq);
+ dtrace_taskq = NULL;
+
+ return (DDI_SUCCESS);
+}
+#endif
+
+#if defined(sun)
+/*ARGSUSED*/
+static int
+dtrace_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
+{
+ int error;
+
+ switch (infocmd) {
+ case DDI_INFO_DEVT2DEVINFO:
+ *result = (void *)dtrace_devi;
+ error = DDI_SUCCESS;
+ break;
+ case DDI_INFO_DEVT2INSTANCE:
+ *result = (void *)0;
+ error = DDI_SUCCESS;
+ break;
+ default:
+ error = DDI_FAILURE;
+ }
+ return (error);
+}
+#endif
+
+#if defined(sun)
+static struct cb_ops dtrace_cb_ops = {
+ dtrace_open, /* open */
+ dtrace_close, /* close */
+ nulldev, /* strategy */
+ nulldev, /* print */
+ nodev, /* dump */
+ nodev, /* read */
+ nodev, /* write */
+ dtrace_ioctl, /* ioctl */
+ nodev, /* devmap */
+ nodev, /* mmap */
+ nodev, /* segmap */
+ nochpoll, /* poll */
+ ddi_prop_op, /* cb_prop_op */
+ 0, /* streamtab */
+ D_NEW | D_MP /* Driver compatibility flag */
+};
+
+static struct dev_ops dtrace_ops = {
+ DEVO_REV, /* devo_rev */
+ 0, /* refcnt */
+ dtrace_info, /* get_dev_info */
+ nulldev, /* identify */
+ nulldev, /* probe */
+ dtrace_attach, /* attach */
+ dtrace_detach, /* detach */
+ nodev, /* reset */
+ &dtrace_cb_ops, /* driver operations */
+ NULL, /* bus operations */
+ nodev /* dev power */
+};
+
+static struct modldrv modldrv = {
+ &mod_driverops, /* module type (this is a pseudo driver) */
+ "Dynamic Tracing", /* name of module */
+ &dtrace_ops, /* driver ops */
+};
+
+static struct modlinkage modlinkage = {
+ MODREV_1,
+ (void *)&modldrv,
+ NULL
+};
+
+int
+_init(void)
+{
+ return (mod_install(&modlinkage));
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&modlinkage, modinfop));
+}
+
+int
+_fini(void)
+{
+ return (mod_remove(&modlinkage));
+}
+#else
+
+static d_ioctl_t dtrace_ioctl;
+static void dtrace_load(void *);
+static int dtrace_unload(void);
+static struct cdev *dtrace_dev;
+
+void dtrace_invop_init(void);
+void dtrace_invop_uninit(void);
+
+static struct cdevsw dtrace_cdevsw = {
+ .d_version = D_VERSION,
+ .d_flags = D_TRACKCLOSE,
+ .d_close = dtrace_close,
+ .d_ioctl = dtrace_ioctl,
+ .d_open = dtrace_open,
+ .d_name = "dtrace",
+};
+
+#include <dtrace_anon.c>
+#include <dtrace_ioctl.c>
+#include <dtrace_load.c>
+#include <dtrace_modevent.c>
+#include <dtrace_sysctl.c>
+#include <dtrace_unload.c>
+#include <dtrace_vtime.c>
+#include <dtrace_hacks.c>
+#include <dtrace_isa.c>
+
+SYSINIT(dtrace_load, SI_SUB_DTRACE, SI_ORDER_FIRST, dtrace_load, NULL);
+SYSUNINIT(dtrace_unload, SI_SUB_DTRACE, SI_ORDER_FIRST, dtrace_unload, NULL);
+SYSINIT(dtrace_anon_init, SI_SUB_DTRACE_ANON, SI_ORDER_FIRST, dtrace_anon_init, NULL);
+
+DEV_MODULE(dtrace, dtrace_modevent, NULL);
+MODULE_VERSION(dtrace, 1);
+MODULE_DEPEND(dtrace, cyclic, 1, 1, 1);
+MODULE_DEPEND(dtrace, opensolaris, 1, 1, 1);
+#endif
diff --git a/sys/cddl/contrib/opensolaris/uts/common/dtrace/fasttrap.c b/sys/cddl/contrib/opensolaris/uts/common/dtrace/fasttrap.c
new file mode 100644
index 000000000000..45839cbb8f94
--- /dev/null
+++ b/sys/cddl/contrib/opensolaris/uts/common/dtrace/fasttrap.c
@@ -0,0 +1,2376 @@
+/*
+ * 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 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/atomic.h>
+#include <sys/errno.h>
+#include <sys/stat.h>
+#include <sys/modctl.h>
+#include <sys/conf.h>
+#include <sys/systm.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/cpuvar.h>
+#include <sys/kmem.h>
+#include <sys/strsubr.h>
+#include <sys/fasttrap.h>
+#include <sys/fasttrap_impl.h>
+#include <sys/fasttrap_isa.h>
+#include <sys/dtrace.h>
+#include <sys/dtrace_impl.h>
+#include <sys/sysmacros.h>
+#include <sys/proc.h>
+#include <sys/priv.h>
+#include <sys/policy.h>
+#include <util/qsort.h>
+
+/*
+ * User-Land Trap-Based Tracing
+ * ----------------------------
+ *
+ * The fasttrap provider allows DTrace consumers to instrument any user-level
+ * instruction to gather data; this includes probes with semantic
+ * signifigance like entry and return as well as simple offsets into the
+ * function. While the specific techniques used are very ISA specific, the
+ * methodology is generalizable to any architecture.
+ *
+ *
+ * The General Methodology
+ * -----------------------
+ *
+ * With the primary goal of tracing every user-land instruction and the
+ * limitation that we can't trust user space so don't want to rely on much
+ * information there, we begin by replacing the instructions we want to trace
+ * with trap instructions. Each instruction we overwrite is saved into a hash
+ * table keyed by process ID and pc address. When we enter the kernel due to
+ * this trap instruction, we need the effects of the replaced instruction to
+ * appear to have occurred before we proceed with the user thread's
+ * execution.
+ *
+ * Each user level thread is represented by a ulwp_t structure which is
+ * always easily accessible through a register. The most basic way to produce
+ * the effects of the instruction we replaced is to copy that instruction out
+ * to a bit of scratch space reserved in the user thread's ulwp_t structure
+ * (a sort of kernel-private thread local storage), set the PC to that
+ * scratch space and single step. When we reenter the kernel after single
+ * stepping the instruction we must then adjust the PC to point to what would
+ * normally be the next instruction. Of course, special care must be taken
+ * for branches and jumps, but these represent such a small fraction of any
+ * instruction set that writing the code to emulate these in the kernel is
+ * not too difficult.
+ *
+ * Return probes may require several tracepoints to trace every return site,
+ * and, conversely, each tracepoint may activate several probes (the entry
+ * and offset 0 probes, for example). To solve this muliplexing problem,
+ * tracepoints contain lists of probes to activate and probes contain lists
+ * of tracepoints to enable. If a probe is activated, it adds its ID to
+ * existing tracepoints or creates new ones as necessary.
+ *
+ * Most probes are activated _before_ the instruction is executed, but return
+ * probes are activated _after_ the effects of the last instruction of the
+ * function are visible. Return probes must be fired _after_ we have
+ * single-stepped the instruction whereas all other probes are fired
+ * beforehand.
+ *
+ *
+ * Lock Ordering
+ * -------------
+ *
+ * The lock ordering below -- both internally and with respect to the DTrace
+ * framework -- is a little tricky and bears some explanation. Each provider
+ * has a lock (ftp_mtx) that protects its members including reference counts
+ * for enabled probes (ftp_rcount), consumers actively creating probes
+ * (ftp_ccount) and USDT consumers (ftp_mcount); all three prevent a provider
+ * from being freed. A provider is looked up by taking the bucket lock for the
+ * provider hash table, and is returned with its lock held. The provider lock
+ * may be taken in functions invoked by the DTrace framework, but may not be
+ * held while calling functions in the DTrace framework.
+ *
+ * To ensure consistency over multiple calls to the DTrace framework, the
+ * creation lock (ftp_cmtx) should be held. Naturally, the creation lock may
+ * not be taken when holding the provider lock as that would create a cyclic
+ * lock ordering. In situations where one would naturally take the provider
+ * lock and then the creation lock, we instead up a reference count to prevent
+ * the provider from disappearing, drop the provider lock, and acquire the
+ * creation lock.
+ *
+ * Briefly:
+ * bucket lock before provider lock
+ * DTrace before provider lock
+ * creation lock before DTrace
+ * never hold the provider lock and creation lock simultaneously
+ */
+
+static dev_info_t *fasttrap_devi;
+static dtrace_meta_provider_id_t fasttrap_meta_id;
+
+static timeout_id_t fasttrap_timeout;
+static kmutex_t fasttrap_cleanup_mtx;
+static uint_t fasttrap_cleanup_work;
+
+/*
+ * Generation count on modifications to the global tracepoint lookup table.
+ */
+static volatile uint64_t fasttrap_mod_gen;
+
+/*
+ * When the fasttrap provider is loaded, fasttrap_max is set to either
+ * FASTTRAP_MAX_DEFAULT or the value for fasttrap-max-probes in the
+ * fasttrap.conf file. Each time a probe is created, fasttrap_total is
+ * incremented by the number of tracepoints that may be associated with that
+ * probe; fasttrap_total is capped at fasttrap_max.
+ */
+#define FASTTRAP_MAX_DEFAULT 250000
+static uint32_t fasttrap_max;
+static uint32_t fasttrap_total;
+
+
+#define FASTTRAP_TPOINTS_DEFAULT_SIZE 0x4000
+#define FASTTRAP_PROVIDERS_DEFAULT_SIZE 0x100
+#define FASTTRAP_PROCS_DEFAULT_SIZE 0x100
+
+#define FASTTRAP_PID_NAME "pid"
+
+fasttrap_hash_t fasttrap_tpoints;
+static fasttrap_hash_t fasttrap_provs;
+static fasttrap_hash_t fasttrap_procs;
+
+static uint64_t fasttrap_pid_count; /* pid ref count */
+static kmutex_t fasttrap_count_mtx; /* lock on ref count */
+
+#define FASTTRAP_ENABLE_FAIL 1
+#define FASTTRAP_ENABLE_PARTIAL 2
+
+static int fasttrap_tracepoint_enable(proc_t *, fasttrap_probe_t *, uint_t);
+static void fasttrap_tracepoint_disable(proc_t *, fasttrap_probe_t *, uint_t);
+
+static fasttrap_provider_t *fasttrap_provider_lookup(pid_t, const char *,
+ const dtrace_pattr_t *);
+static void fasttrap_provider_retire(pid_t, const char *, int);
+static void fasttrap_provider_free(fasttrap_provider_t *);
+
+static fasttrap_proc_t *fasttrap_proc_lookup(pid_t);
+static void fasttrap_proc_release(fasttrap_proc_t *);
+
+#define FASTTRAP_PROVS_INDEX(pid, name) \
+ ((fasttrap_hash_str(name) + (pid)) & fasttrap_provs.fth_mask)
+
+#define FASTTRAP_PROCS_INDEX(pid) ((pid) & fasttrap_procs.fth_mask)
+
+static int
+fasttrap_highbit(ulong_t i)
+{
+ int h = 1;
+
+ if (i == 0)
+ return (0);
+#ifdef _LP64
+ if (i & 0xffffffff00000000ul) {
+ h += 32; i >>= 32;
+ }
+#endif
+ if (i & 0xffff0000) {
+ h += 16; i >>= 16;
+ }
+ if (i & 0xff00) {
+ h += 8; i >>= 8;
+ }
+ if (i & 0xf0) {
+ h += 4; i >>= 4;
+ }
+ if (i & 0xc) {
+ h += 2; i >>= 2;
+ }
+ if (i & 0x2) {
+ h += 1;
+ }
+ return (h);
+}
+
+static uint_t
+fasttrap_hash_str(const char *p)
+{
+ unsigned int g;
+ uint_t hval = 0;
+
+ while (*p) {
+ hval = (hval << 4) + *p++;
+ if ((g = (hval & 0xf0000000)) != 0)
+ hval ^= g >> 24;
+ hval &= ~g;
+ }
+ return (hval);
+}
+
+void
+fasttrap_sigtrap(proc_t *p, kthread_t *t, uintptr_t pc)
+{
+ sigqueue_t *sqp = kmem_zalloc(sizeof (sigqueue_t), KM_SLEEP);
+
+ sqp->sq_info.si_signo = SIGTRAP;
+ sqp->sq_info.si_code = TRAP_DTRACE;
+ sqp->sq_info.si_addr = (caddr_t)pc;
+
+ mutex_enter(&p->p_lock);
+ sigaddqa(p, t, sqp);
+ mutex_exit(&p->p_lock);
+
+ if (t != NULL)
+ aston(t);
+}
+
+/*
+ * This function ensures that no threads are actively using the memory
+ * associated with probes that were formerly live.
+ */
+static void
+fasttrap_mod_barrier(uint64_t gen)
+{
+ int i;
+
+ if (gen < fasttrap_mod_gen)
+ return;
+
+ fasttrap_mod_gen++;
+
+ for (i = 0; i < NCPU; i++) {
+ mutex_enter(&cpu_core[i].cpuc_pid_lock);
+ mutex_exit(&cpu_core[i].cpuc_pid_lock);
+ }
+}
+
+/*
+ * This is the timeout's callback for cleaning up the providers and their
+ * probes.
+ */
+/*ARGSUSED*/
+static void
+fasttrap_pid_cleanup_cb(void *data)
+{
+ fasttrap_provider_t **fpp, *fp;
+ fasttrap_bucket_t *bucket;
+ dtrace_provider_id_t provid;
+ int i, later;
+
+ static volatile int in = 0;
+ ASSERT(in == 0);
+ in = 1;
+
+ mutex_enter(&fasttrap_cleanup_mtx);
+ while (fasttrap_cleanup_work) {
+ fasttrap_cleanup_work = 0;
+ mutex_exit(&fasttrap_cleanup_mtx);
+
+ later = 0;
+
+ /*
+ * Iterate over all the providers trying to remove the marked
+ * ones. If a provider is marked but not retired, we just
+ * have to take a crack at removing it -- it's no big deal if
+ * we can't.
+ */
+ for (i = 0; i < fasttrap_provs.fth_nent; i++) {
+ bucket = &fasttrap_provs.fth_table[i];
+ mutex_enter(&bucket->ftb_mtx);
+ fpp = (fasttrap_provider_t **)&bucket->ftb_data;
+
+ while ((fp = *fpp) != NULL) {
+ if (!fp->ftp_marked) {
+ fpp = &fp->ftp_next;
+ continue;
+ }
+
+ mutex_enter(&fp->ftp_mtx);
+
+ /*
+ * If this provider has consumers actively
+ * creating probes (ftp_ccount) or is a USDT
+ * provider (ftp_mcount), we can't unregister
+ * or even condense.
+ */
+ if (fp->ftp_ccount != 0 ||
+ fp->ftp_mcount != 0) {
+ mutex_exit(&fp->ftp_mtx);
+ fp->ftp_marked = 0;
+ continue;
+ }
+
+ if (!fp->ftp_retired || fp->ftp_rcount != 0)
+ fp->ftp_marked = 0;
+
+ mutex_exit(&fp->ftp_mtx);
+
+ /*
+ * If we successfully unregister this
+ * provider we can remove it from the hash
+ * chain and free the memory. If our attempt
+ * to unregister fails and this is a retired
+ * provider, increment our flag to try again
+ * pretty soon. If we've consumed more than
+ * half of our total permitted number of
+ * probes call dtrace_condense() to try to
+ * clean out the unenabled probes.
+ */
+ provid = fp->ftp_provid;
+ if (dtrace_unregister(provid) != 0) {
+ if (fasttrap_total > fasttrap_max / 2)
+ (void) dtrace_condense(provid);
+ later += fp->ftp_marked;
+ fpp = &fp->ftp_next;
+ } else {
+ *fpp = fp->ftp_next;
+ fasttrap_provider_free(fp);
+ }
+ }
+ mutex_exit(&bucket->ftb_mtx);
+ }
+
+ mutex_enter(&fasttrap_cleanup_mtx);
+ }
+
+ ASSERT(fasttrap_timeout != 0);
+
+ /*
+ * If we were unable to remove a retired provider, try again after
+ * a second. This situation can occur in certain circumstances where
+ * providers cannot be unregistered even though they have no probes
+ * enabled because of an execution of dtrace -l or something similar.
+ * If the timeout has been disabled (set to 1 because we're trying
+ * to detach), we set fasttrap_cleanup_work to ensure that we'll
+ * get a chance to do that work if and when the timeout is reenabled
+ * (if detach fails).
+ */
+ if (later > 0 && fasttrap_timeout != (timeout_id_t)1)
+ fasttrap_timeout = timeout(&fasttrap_pid_cleanup_cb, NULL, hz);
+ else if (later > 0)
+ fasttrap_cleanup_work = 1;
+ else
+ fasttrap_timeout = 0;
+
+ mutex_exit(&fasttrap_cleanup_mtx);
+ in = 0;
+}
+
+/*
+ * Activates the asynchronous cleanup mechanism.
+ */
+static void
+fasttrap_pid_cleanup(void)
+{
+ mutex_enter(&fasttrap_cleanup_mtx);
+ fasttrap_cleanup_work = 1;
+ if (fasttrap_timeout == 0)
+ fasttrap_timeout = timeout(&fasttrap_pid_cleanup_cb, NULL, 1);
+ mutex_exit(&fasttrap_cleanup_mtx);
+}
+
+/*
+ * This is called from cfork() via dtrace_fasttrap_fork(). The child
+ * process's address space is (roughly) a copy of the parent process's so
+ * we have to remove all the instrumentation we had previously enabled in the
+ * parent.
+ */
+static void
+fasttrap_fork(proc_t *p, proc_t *cp)
+{
+ pid_t ppid = p->p_pid;
+ int i;
+
+ ASSERT(curproc == p);
+ ASSERT(p->p_proc_flag & P_PR_LOCK);
+ ASSERT(p->p_dtrace_count > 0);
+ ASSERT(cp->p_dtrace_count == 0);
+
+ /*
+ * This would be simpler and faster if we maintained per-process
+ * hash tables of enabled tracepoints. It could, however, potentially
+ * slow down execution of a tracepoint since we'd need to go
+ * through two levels of indirection. In the future, we should
+ * consider either maintaining per-process ancillary lists of
+ * enabled tracepoints or hanging a pointer to a per-process hash
+ * table of enabled tracepoints off the proc structure.
+ */
+
+ /*
+ * We don't have to worry about the child process disappearing
+ * because we're in fork().
+ */
+ mutex_enter(&cp->p_lock);
+ sprlock_proc(cp);
+ mutex_exit(&cp->p_lock);
+
+ /*
+ * Iterate over every tracepoint looking for ones that belong to the
+ * parent process, and remove each from the child process.
+ */
+ for (i = 0; i < fasttrap_tpoints.fth_nent; i++) {
+ fasttrap_tracepoint_t *tp;
+ fasttrap_bucket_t *bucket = &fasttrap_tpoints.fth_table[i];
+
+ mutex_enter(&bucket->ftb_mtx);
+ for (tp = bucket->ftb_data; tp != NULL; tp = tp->ftt_next) {
+ if (tp->ftt_pid == ppid &&
+ tp->ftt_proc->ftpc_acount != 0) {
+ int ret = fasttrap_tracepoint_remove(cp, tp);
+ ASSERT(ret == 0);
+
+ /*
+ * The count of active providers can only be
+ * decremented (i.e. to zero) during exec,
+ * exit, and removal of a meta provider so it
+ * should be impossible to drop the count
+ * mid-fork.
+ */
+ ASSERT(tp->ftt_proc->ftpc_acount != 0);
+ }
+ }
+ mutex_exit(&bucket->ftb_mtx);
+ }
+
+ mutex_enter(&cp->p_lock);
+ sprunlock(cp);
+}
+
+/*
+ * This is called from proc_exit() or from exec_common() if p_dtrace_probes
+ * is set on the proc structure to indicate that there is a pid provider
+ * associated with this process.
+ */
+static void
+fasttrap_exec_exit(proc_t *p)
+{
+ ASSERT(p == curproc);
+ ASSERT(MUTEX_HELD(&p->p_lock));
+
+ mutex_exit(&p->p_lock);
+
+ /*
+ * We clean up the pid provider for this process here; user-land
+ * static probes are handled by the meta-provider remove entry point.
+ */
+ fasttrap_provider_retire(p->p_pid, FASTTRAP_PID_NAME, 0);
+
+ mutex_enter(&p->p_lock);
+}
+
+
+/*ARGSUSED*/
+static void
+fasttrap_pid_provide(void *arg, const dtrace_probedesc_t *desc)
+{
+ /*
+ * There are no "default" pid probes.
+ */
+}
+
+static int
+fasttrap_tracepoint_enable(proc_t *p, fasttrap_probe_t *probe, uint_t index)
+{
+ fasttrap_tracepoint_t *tp, *new_tp = NULL;
+ fasttrap_bucket_t *bucket;
+ fasttrap_id_t *id;
+ pid_t pid;
+ uintptr_t pc;
+
+ ASSERT(index < probe->ftp_ntps);
+
+ pid = probe->ftp_pid;
+ pc = probe->ftp_tps[index].fit_tp->ftt_pc;
+ id = &probe->ftp_tps[index].fit_id;
+
+ ASSERT(probe->ftp_tps[index].fit_tp->ftt_pid == pid);
+
+ ASSERT(!(p->p_flag & SVFORK));
+
+ /*
+ * Before we make any modifications, make sure we've imposed a barrier
+ * on the generation in which this probe was last modified.
+ */
+ fasttrap_mod_barrier(probe->ftp_gen);
+
+ bucket = &fasttrap_tpoints.fth_table[FASTTRAP_TPOINTS_INDEX(pid, pc)];
+
+ /*
+ * If the tracepoint has already been enabled, just add our id to the
+ * list of interested probes. This may be our second time through
+ * this path in which case we'll have constructed the tracepoint we'd
+ * like to install. If we can't find a match, and have an allocated
+ * tracepoint ready to go, enable that one now.
+ *
+ * A tracepoint whose process is defunct is also considered defunct.
+ */
+again:
+ mutex_enter(&bucket->ftb_mtx);
+ for (tp = bucket->ftb_data; tp != NULL; tp = tp->ftt_next) {
+ /*
+ * Note that it's safe to access the active count on the
+ * associated proc structure because we know that at least one
+ * provider (this one) will still be around throughout this
+ * operation.
+ */
+ if (tp->ftt_pid != pid || tp->ftt_pc != pc ||
+ tp->ftt_proc->ftpc_acount == 0)
+ continue;
+
+ /*
+ * Now that we've found a matching tracepoint, it would be
+ * a decent idea to confirm that the tracepoint is still
+ * enabled and the trap instruction hasn't been overwritten.
+ * Since this is a little hairy, we'll punt for now.
+ */
+
+ /*
+ * This can't be the first interested probe. We don't have
+ * to worry about another thread being in the midst of
+ * deleting this tracepoint (which would be the only valid
+ * reason for a tracepoint to have no interested probes)
+ * since we're holding P_PR_LOCK for this process.
+ */
+ ASSERT(tp->ftt_ids != NULL || tp->ftt_retids != NULL);
+
+ switch (id->fti_ptype) {
+ case DTFTP_ENTRY:
+ case DTFTP_OFFSETS:
+ case DTFTP_IS_ENABLED:
+ id->fti_next = tp->ftt_ids;
+ membar_producer();
+ tp->ftt_ids = id;
+ membar_producer();
+ break;
+
+ case DTFTP_RETURN:
+ case DTFTP_POST_OFFSETS:
+ id->fti_next = tp->ftt_retids;
+ membar_producer();
+ tp->ftt_retids = id;
+ membar_producer();
+ break;
+
+ default:
+ ASSERT(0);
+ }
+
+ mutex_exit(&bucket->ftb_mtx);
+
+ if (new_tp != NULL) {
+ new_tp->ftt_ids = NULL;
+ new_tp->ftt_retids = NULL;
+ }
+
+ return (0);
+ }
+
+ /*
+ * If we have a good tracepoint ready to go, install it now while
+ * we have the lock held and no one can screw with us.
+ */
+ if (new_tp != NULL) {
+ int rc = 0;
+
+ new_tp->ftt_next = bucket->ftb_data;
+ membar_producer();
+ bucket->ftb_data = new_tp;
+ membar_producer();
+ mutex_exit(&bucket->ftb_mtx);
+
+ /*
+ * Activate the tracepoint in the ISA-specific manner.
+ * If this fails, we need to report the failure, but
+ * indicate that this tracepoint must still be disabled
+ * by calling fasttrap_tracepoint_disable().
+ */
+ if (fasttrap_tracepoint_install(p, new_tp) != 0)
+ rc = FASTTRAP_ENABLE_PARTIAL;
+
+ /*
+ * Increment the count of the number of tracepoints active in
+ * the victim process.
+ */
+ ASSERT(p->p_proc_flag & P_PR_LOCK);
+ p->p_dtrace_count++;
+
+ return (rc);
+ }
+
+ mutex_exit(&bucket->ftb_mtx);
+
+ /*
+ * Initialize the tracepoint that's been preallocated with the probe.
+ */
+ new_tp = probe->ftp_tps[index].fit_tp;
+
+ ASSERT(new_tp->ftt_pid == pid);
+ ASSERT(new_tp->ftt_pc == pc);
+ ASSERT(new_tp->ftt_proc == probe->ftp_prov->ftp_proc);
+ ASSERT(new_tp->ftt_ids == NULL);
+ ASSERT(new_tp->ftt_retids == NULL);
+
+ switch (id->fti_ptype) {
+ case DTFTP_ENTRY:
+ case DTFTP_OFFSETS:
+ case DTFTP_IS_ENABLED:
+ id->fti_next = NULL;
+ new_tp->ftt_ids = id;
+ break;
+
+ case DTFTP_RETURN:
+ case DTFTP_POST_OFFSETS:
+ id->fti_next = NULL;
+ new_tp->ftt_retids = id;
+ break;
+
+ default:
+ ASSERT(0);
+ }
+
+ /*
+ * If the ISA-dependent initialization goes to plan, go back to the
+ * beginning and try to install this freshly made tracepoint.
+ */
+ if (fasttrap_tracepoint_init(p, new_tp, pc, id->fti_ptype) == 0)
+ goto again;
+
+ new_tp->ftt_ids = NULL;
+ new_tp->ftt_retids = NULL;
+
+ return (FASTTRAP_ENABLE_FAIL);
+}
+
+static void
+fasttrap_tracepoint_disable(proc_t *p, fasttrap_probe_t *probe, uint_t index)
+{
+ fasttrap_bucket_t *bucket;
+ fasttrap_provider_t *provider = probe->ftp_prov;
+ fasttrap_tracepoint_t **pp, *tp;
+ fasttrap_id_t *id, **idp;
+ pid_t pid;
+ uintptr_t pc;
+
+ ASSERT(index < probe->ftp_ntps);
+
+ pid = probe->ftp_pid;
+ pc = probe->ftp_tps[index].fit_tp->ftt_pc;
+ id = &probe->ftp_tps[index].fit_id;
+
+ ASSERT(probe->ftp_tps[index].fit_tp->ftt_pid == pid);
+
+ /*
+ * Find the tracepoint and make sure that our id is one of the
+ * ones registered with it.
+ */
+ bucket = &fasttrap_tpoints.fth_table[FASTTRAP_TPOINTS_INDEX(pid, pc)];
+ mutex_enter(&bucket->ftb_mtx);
+ for (tp = bucket->ftb_data; tp != NULL; tp = tp->ftt_next) {
+ if (tp->ftt_pid == pid && tp->ftt_pc == pc &&
+ tp->ftt_proc == provider->ftp_proc)
+ break;
+ }
+
+ /*
+ * If we somehow lost this tracepoint, we're in a world of hurt.
+ */
+ ASSERT(tp != NULL);
+
+ switch (id->fti_ptype) {
+ case DTFTP_ENTRY:
+ case DTFTP_OFFSETS:
+ case DTFTP_IS_ENABLED:
+ ASSERT(tp->ftt_ids != NULL);
+ idp = &tp->ftt_ids;
+ break;
+
+ case DTFTP_RETURN:
+ case DTFTP_POST_OFFSETS:
+ ASSERT(tp->ftt_retids != NULL);
+ idp = &tp->ftt_retids;
+ break;
+
+ default:
+ ASSERT(0);
+ }
+
+ while ((*idp)->fti_probe != probe) {
+ idp = &(*idp)->fti_next;
+ ASSERT(*idp != NULL);
+ }
+
+ id = *idp;
+ *idp = id->fti_next;
+ membar_producer();
+
+ ASSERT(id->fti_probe == probe);
+
+ /*
+ * If there are other registered enablings of this tracepoint, we're
+ * all done, but if this was the last probe assocated with this
+ * this tracepoint, we need to remove and free it.
+ */
+ if (tp->ftt_ids != NULL || tp->ftt_retids != NULL) {
+
+ /*
+ * If the current probe's tracepoint is in use, swap it
+ * for an unused tracepoint.
+ */
+ if (tp == probe->ftp_tps[index].fit_tp) {
+ fasttrap_probe_t *tmp_probe;
+ fasttrap_tracepoint_t **tmp_tp;
+ uint_t tmp_index;
+
+ if (tp->ftt_ids != NULL) {
+ tmp_probe = tp->ftt_ids->fti_probe;
+ /* LINTED - alignment */
+ tmp_index = FASTTRAP_ID_INDEX(tp->ftt_ids);
+ tmp_tp = &tmp_probe->ftp_tps[tmp_index].fit_tp;
+ } else {
+ tmp_probe = tp->ftt_retids->fti_probe;
+ /* LINTED - alignment */
+ tmp_index = FASTTRAP_ID_INDEX(tp->ftt_retids);
+ tmp_tp = &tmp_probe->ftp_tps[tmp_index].fit_tp;
+ }
+
+ ASSERT(*tmp_tp != NULL);
+ ASSERT(*tmp_tp != probe->ftp_tps[index].fit_tp);
+ ASSERT((*tmp_tp)->ftt_ids == NULL);
+ ASSERT((*tmp_tp)->ftt_retids == NULL);
+
+ probe->ftp_tps[index].fit_tp = *tmp_tp;
+ *tmp_tp = tp;
+ }
+
+ mutex_exit(&bucket->ftb_mtx);
+
+ /*
+ * Tag the modified probe with the generation in which it was
+ * changed.
+ */
+ probe->ftp_gen = fasttrap_mod_gen;
+ return;
+ }
+
+ mutex_exit(&bucket->ftb_mtx);
+
+ /*
+ * We can't safely remove the tracepoint from the set of active
+ * tracepoints until we've actually removed the fasttrap instruction
+ * from the process's text. We can, however, operate on this
+ * tracepoint secure in the knowledge that no other thread is going to
+ * be looking at it since we hold P_PR_LOCK on the process if it's
+ * live or we hold the provider lock on the process if it's dead and
+ * gone.
+ */
+
+ /*
+ * We only need to remove the actual instruction if we're looking
+ * at an existing process
+ */
+ if (p != NULL) {
+ /*
+ * If we fail to restore the instruction we need to kill
+ * this process since it's in a completely unrecoverable
+ * state.
+ */
+ if (fasttrap_tracepoint_remove(p, tp) != 0)
+ fasttrap_sigtrap(p, NULL, pc);
+
+ /*
+ * Decrement the count of the number of tracepoints active
+ * in the victim process.
+ */
+ ASSERT(p->p_proc_flag & P_PR_LOCK);
+ p->p_dtrace_count--;
+ }
+
+ /*
+ * Remove the probe from the hash table of active tracepoints.
+ */
+ mutex_enter(&bucket->ftb_mtx);
+ pp = (fasttrap_tracepoint_t **)&bucket->ftb_data;
+ ASSERT(*pp != NULL);
+ while (*pp != tp) {
+ pp = &(*pp)->ftt_next;
+ ASSERT(*pp != NULL);
+ }
+
+ *pp = tp->ftt_next;
+ membar_producer();
+
+ mutex_exit(&bucket->ftb_mtx);
+
+ /*
+ * Tag the modified probe with the generation in which it was changed.
+ */
+ probe->ftp_gen = fasttrap_mod_gen;
+}
+
+static void
+fasttrap_enable_callbacks(void)
+{
+ /*
+ * We don't have to play the rw lock game here because we're
+ * providing something rather than taking something away --
+ * we can be sure that no threads have tried to follow this
+ * function pointer yet.
+ */
+ mutex_enter(&fasttrap_count_mtx);
+ if (fasttrap_pid_count == 0) {
+ ASSERT(dtrace_pid_probe_ptr == NULL);
+ ASSERT(dtrace_return_probe_ptr == NULL);
+ dtrace_pid_probe_ptr = &fasttrap_pid_probe;
+ dtrace_return_probe_ptr = &fasttrap_return_probe;
+ }
+ ASSERT(dtrace_pid_probe_ptr == &fasttrap_pid_probe);
+ ASSERT(dtrace_return_probe_ptr == &fasttrap_return_probe);
+ fasttrap_pid_count++;
+ mutex_exit(&fasttrap_count_mtx);
+}
+
+static void
+fasttrap_disable_callbacks(void)
+{
+ ASSERT(MUTEX_HELD(&cpu_lock));
+
+ mutex_enter(&fasttrap_count_mtx);
+ ASSERT(fasttrap_pid_count > 0);
+ fasttrap_pid_count--;
+ if (fasttrap_pid_count == 0) {
+ cpu_t *cur, *cpu = CPU;
+
+ for (cur = cpu->cpu_next_onln; cur != cpu;
+ cur = cur->cpu_next_onln) {
+ rw_enter(&cur->cpu_ft_lock, RW_WRITER);
+ }
+
+ dtrace_pid_probe_ptr = NULL;
+ dtrace_return_probe_ptr = NULL;
+
+ for (cur = cpu->cpu_next_onln; cur != cpu;
+ cur = cur->cpu_next_onln) {
+ rw_exit(&cur->cpu_ft_lock);
+ }
+ }
+ mutex_exit(&fasttrap_count_mtx);
+}
+
+/*ARGSUSED*/
+static void
+fasttrap_pid_enable(void *arg, dtrace_id_t id, void *parg)
+{
+ fasttrap_probe_t *probe = parg;
+ proc_t *p;
+ int i, rc;
+
+ ASSERT(probe != NULL);
+ ASSERT(!probe->ftp_enabled);
+ ASSERT(id == probe->ftp_id);
+ ASSERT(MUTEX_HELD(&cpu_lock));
+
+ /*
+ * Increment the count of enabled probes on this probe's provider;
+ * the provider can't go away while the probe still exists. We
+ * must increment this even if we aren't able to properly enable
+ * this probe.
+ */
+ mutex_enter(&probe->ftp_prov->ftp_mtx);
+ probe->ftp_prov->ftp_rcount++;
+ mutex_exit(&probe->ftp_prov->ftp_mtx);
+
+ /*
+ * If this probe's provider is retired (meaning it was valid in a
+ * previously exec'ed incarnation of this address space), bail out. The
+ * provider can't go away while we're in this code path.
+ */
+ if (probe->ftp_prov->ftp_retired)
+ return;
+
+ /*
+ * If we can't find the process, it may be that we're in the context of
+ * a fork in which the traced process is being born and we're copying
+ * USDT probes. Otherwise, the process is gone so bail.
+ */
+ if ((p = sprlock(probe->ftp_pid)) == NULL) {
+ if ((curproc->p_flag & SFORKING) == 0)
+ return;
+
+ mutex_enter(&pidlock);
+ p = prfind(probe->ftp_pid);
+
+ /*
+ * Confirm that curproc is indeed forking the process in which
+ * we're trying to enable probes.
+ */
+ ASSERT(p != NULL);
+ ASSERT(p->p_parent == curproc);
+ ASSERT(p->p_stat == SIDL);
+
+ mutex_enter(&p->p_lock);
+ mutex_exit(&pidlock);
+
+ sprlock_proc(p);
+ }
+
+ ASSERT(!(p->p_flag & SVFORK));
+ mutex_exit(&p->p_lock);
+
+ /*
+ * We have to enable the trap entry point before any user threads have
+ * the chance to execute the trap instruction we're about to place
+ * in their process's text.
+ */
+ fasttrap_enable_callbacks();
+
+ /*
+ * Enable all the tracepoints and add this probe's id to each
+ * tracepoint's list of active probes.
+ */
+ for (i = 0; i < probe->ftp_ntps; i++) {
+ if ((rc = fasttrap_tracepoint_enable(p, probe, i)) != 0) {
+ /*
+ * If enabling the tracepoint failed completely,
+ * we don't have to disable it; if the failure
+ * was only partial we must disable it.
+ */
+ if (rc == FASTTRAP_ENABLE_FAIL)
+ i--;
+ else
+ ASSERT(rc == FASTTRAP_ENABLE_PARTIAL);
+
+ /*
+ * Back up and pull out all the tracepoints we've
+ * created so far for this probe.
+ */
+ while (i >= 0) {
+ fasttrap_tracepoint_disable(p, probe, i);
+ i--;
+ }
+
+ mutex_enter(&p->p_lock);
+ sprunlock(p);
+
+ /*
+ * Since we're not actually enabling this probe,
+ * drop our reference on the trap table entry.
+ */
+ fasttrap_disable_callbacks();
+ return;
+ }
+ }
+
+ mutex_enter(&p->p_lock);
+ sprunlock(p);
+
+ probe->ftp_enabled = 1;
+}
+
+/*ARGSUSED*/
+static void
+fasttrap_pid_disable(void *arg, dtrace_id_t id, void *parg)
+{
+ fasttrap_probe_t *probe = parg;
+ fasttrap_provider_t *provider = probe->ftp_prov;
+ proc_t *p;
+ int i, whack = 0;
+
+ ASSERT(id == probe->ftp_id);
+
+ /*
+ * We won't be able to acquire a /proc-esque lock on the process
+ * iff the process is dead and gone. In this case, we rely on the
+ * provider lock as a point of mutual exclusion to prevent other
+ * DTrace consumers from disabling this probe.
+ */
+ if ((p = sprlock(probe->ftp_pid)) != NULL) {
+ ASSERT(!(p->p_flag & SVFORK));
+ mutex_exit(&p->p_lock);
+ }
+
+ mutex_enter(&provider->ftp_mtx);
+
+ /*
+ * Disable all the associated tracepoints (for fully enabled probes).
+ */
+ if (probe->ftp_enabled) {
+ for (i = 0; i < probe->ftp_ntps; i++) {
+ fasttrap_tracepoint_disable(p, probe, i);
+ }
+ }
+
+ ASSERT(provider->ftp_rcount > 0);
+ provider->ftp_rcount--;
+
+ if (p != NULL) {
+ /*
+ * Even though we may not be able to remove it entirely, we
+ * mark this retired provider to get a chance to remove some
+ * of the associated probes.
+ */
+ if (provider->ftp_retired && !provider->ftp_marked)
+ whack = provider->ftp_marked = 1;
+ mutex_exit(&provider->ftp_mtx);
+
+ mutex_enter(&p->p_lock);
+ sprunlock(p);
+ } else {
+ /*
+ * If the process is dead, we're just waiting for the
+ * last probe to be disabled to be able to free it.
+ */
+ if (provider->ftp_rcount == 0 && !provider->ftp_marked)
+ whack = provider->ftp_marked = 1;
+ mutex_exit(&provider->ftp_mtx);
+ }
+
+ if (whack)
+ fasttrap_pid_cleanup();
+
+ if (!probe->ftp_enabled)
+ return;
+
+ probe->ftp_enabled = 0;
+
+ ASSERT(MUTEX_HELD(&cpu_lock));
+ fasttrap_disable_callbacks();
+}
+
+/*ARGSUSED*/
+static void
+fasttrap_pid_getargdesc(void *arg, dtrace_id_t id, void *parg,
+ dtrace_argdesc_t *desc)
+{
+ fasttrap_probe_t *probe = parg;
+ char *str;
+ int i, ndx;
+
+ desc->dtargd_native[0] = '\0';
+ desc->dtargd_xlate[0] = '\0';
+
+ if (probe->ftp_prov->ftp_retired != 0 ||
+ desc->dtargd_ndx >= probe->ftp_nargs) {
+ desc->dtargd_ndx = DTRACE_ARGNONE;
+ return;
+ }
+
+ ndx = (probe->ftp_argmap != NULL) ?
+ probe->ftp_argmap[desc->dtargd_ndx] : desc->dtargd_ndx;
+
+ str = probe->ftp_ntypes;
+ for (i = 0; i < ndx; i++) {
+ str += strlen(str) + 1;
+ }
+
+ ASSERT(strlen(str + 1) < sizeof (desc->dtargd_native));
+ (void) strcpy(desc->dtargd_native, str);
+
+ if (probe->ftp_xtypes == NULL)
+ return;
+
+ str = probe->ftp_xtypes;
+ for (i = 0; i < desc->dtargd_ndx; i++) {
+ str += strlen(str) + 1;
+ }
+
+ ASSERT(strlen(str + 1) < sizeof (desc->dtargd_xlate));
+ (void) strcpy(desc->dtargd_xlate, str);
+}
+
+/*ARGSUSED*/
+static void
+fasttrap_pid_destroy(void *arg, dtrace_id_t id, void *parg)
+{
+ fasttrap_probe_t *probe = parg;
+ int i;
+ size_t size;
+
+ ASSERT(probe != NULL);
+ ASSERT(!probe->ftp_enabled);
+ ASSERT(fasttrap_total >= probe->ftp_ntps);
+
+ atomic_add_32(&fasttrap_total, -probe->ftp_ntps);
+ size = offsetof(fasttrap_probe_t, ftp_tps[probe->ftp_ntps]);
+
+ if (probe->ftp_gen + 1 >= fasttrap_mod_gen)
+ fasttrap_mod_barrier(probe->ftp_gen);
+
+ for (i = 0; i < probe->ftp_ntps; i++) {
+ kmem_free(probe->ftp_tps[i].fit_tp,
+ sizeof (fasttrap_tracepoint_t));
+ }
+
+ kmem_free(probe, size);
+}
+
+
+static const dtrace_pattr_t pid_attr = {
+{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_ISA },
+{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
+{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
+{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_ISA },
+{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
+};
+
+static dtrace_pops_t pid_pops = {
+ fasttrap_pid_provide,
+ NULL,
+ fasttrap_pid_enable,
+ fasttrap_pid_disable,
+ NULL,
+ NULL,
+ fasttrap_pid_getargdesc,
+ fasttrap_pid_getarg,
+ NULL,
+ fasttrap_pid_destroy
+};
+
+static dtrace_pops_t usdt_pops = {
+ fasttrap_pid_provide,
+ NULL,
+ fasttrap_pid_enable,
+ fasttrap_pid_disable,
+ NULL,
+ NULL,
+ fasttrap_pid_getargdesc,
+ fasttrap_usdt_getarg,
+ NULL,
+ fasttrap_pid_destroy
+};
+
+static fasttrap_proc_t *
+fasttrap_proc_lookup(pid_t pid)
+{
+ fasttrap_bucket_t *bucket;
+ fasttrap_proc_t *fprc, *new_fprc;
+
+ bucket = &fasttrap_procs.fth_table[FASTTRAP_PROCS_INDEX(pid)];
+ mutex_enter(&bucket->ftb_mtx);
+
+ for (fprc = bucket->ftb_data; fprc != NULL; fprc = fprc->ftpc_next) {
+ if (fprc->ftpc_pid == pid && fprc->ftpc_acount != 0) {
+ mutex_enter(&fprc->ftpc_mtx);
+ mutex_exit(&bucket->ftb_mtx);
+ fprc->ftpc_rcount++;
+ atomic_add_64(&fprc->ftpc_acount, 1);
+ ASSERT(fprc->ftpc_acount <= fprc->ftpc_rcount);
+ mutex_exit(&fprc->ftpc_mtx);
+
+ return (fprc);
+ }
+ }
+
+ /*
+ * Drop the bucket lock so we don't try to perform a sleeping
+ * allocation under it.
+ */
+ mutex_exit(&bucket->ftb_mtx);
+
+ new_fprc = kmem_zalloc(sizeof (fasttrap_proc_t), KM_SLEEP);
+ new_fprc->ftpc_pid = pid;
+ new_fprc->ftpc_rcount = 1;
+ new_fprc->ftpc_acount = 1;
+
+ mutex_enter(&bucket->ftb_mtx);
+
+ /*
+ * Take another lap through the list to make sure a proc hasn't
+ * been created for this pid while we weren't under the bucket lock.
+ */
+ for (fprc = bucket->ftb_data; fprc != NULL; fprc = fprc->ftpc_next) {
+ if (fprc->ftpc_pid == pid && fprc->ftpc_acount != 0) {
+ mutex_enter(&fprc->ftpc_mtx);
+ mutex_exit(&bucket->ftb_mtx);
+ fprc->ftpc_rcount++;
+ atomic_add_64(&fprc->ftpc_acount, 1);
+ ASSERT(fprc->ftpc_acount <= fprc->ftpc_rcount);
+ mutex_exit(&fprc->ftpc_mtx);
+
+ kmem_free(new_fprc, sizeof (fasttrap_proc_t));
+
+ return (fprc);
+ }
+ }
+
+ new_fprc->ftpc_next = bucket->ftb_data;
+ bucket->ftb_data = new_fprc;
+
+ mutex_exit(&bucket->ftb_mtx);
+
+ return (new_fprc);
+}
+
+static void
+fasttrap_proc_release(fasttrap_proc_t *proc)
+{
+ fasttrap_bucket_t *bucket;
+ fasttrap_proc_t *fprc, **fprcp;
+ pid_t pid = proc->ftpc_pid;
+
+ mutex_enter(&proc->ftpc_mtx);
+
+ ASSERT(proc->ftpc_rcount != 0);
+ ASSERT(proc->ftpc_acount <= proc->ftpc_rcount);
+
+ if (--proc->ftpc_rcount != 0) {
+ mutex_exit(&proc->ftpc_mtx);
+ return;
+ }
+
+ mutex_exit(&proc->ftpc_mtx);
+
+ /*
+ * There should definitely be no live providers associated with this
+ * process at this point.
+ */
+ ASSERT(proc->ftpc_acount == 0);
+
+ bucket = &fasttrap_procs.fth_table[FASTTRAP_PROCS_INDEX(pid)];
+ mutex_enter(&bucket->ftb_mtx);
+
+ fprcp = (fasttrap_proc_t **)&bucket->ftb_data;
+ while ((fprc = *fprcp) != NULL) {
+ if (fprc == proc)
+ break;
+
+ fprcp = &fprc->ftpc_next;
+ }
+
+ /*
+ * Something strange has happened if we can't find the proc.
+ */
+ ASSERT(fprc != NULL);
+
+ *fprcp = fprc->ftpc_next;
+
+ mutex_exit(&bucket->ftb_mtx);
+
+ kmem_free(fprc, sizeof (fasttrap_proc_t));
+}
+
+/*
+ * Lookup a fasttrap-managed provider based on its name and associated pid.
+ * If the pattr argument is non-NULL, this function instantiates the provider
+ * if it doesn't exist otherwise it returns NULL. The provider is returned
+ * with its lock held.
+ */
+static fasttrap_provider_t *
+fasttrap_provider_lookup(pid_t pid, const char *name,
+ const dtrace_pattr_t *pattr)
+{
+ fasttrap_provider_t *fp, *new_fp = NULL;
+ fasttrap_bucket_t *bucket;
+ char provname[DTRACE_PROVNAMELEN];
+ proc_t *p;
+ cred_t *cred;
+
+ ASSERT(strlen(name) < sizeof (fp->ftp_name));
+ ASSERT(pattr != NULL);
+
+ bucket = &fasttrap_provs.fth_table[FASTTRAP_PROVS_INDEX(pid, name)];
+ mutex_enter(&bucket->ftb_mtx);
+
+ /*
+ * Take a lap through the list and return the match if we find it.
+ */
+ for (fp = bucket->ftb_data; fp != NULL; fp = fp->ftp_next) {
+ if (fp->ftp_pid == pid && strcmp(fp->ftp_name, name) == 0 &&
+ !fp->ftp_retired) {
+ mutex_enter(&fp->ftp_mtx);
+ mutex_exit(&bucket->ftb_mtx);
+ return (fp);
+ }
+ }
+
+ /*
+ * Drop the bucket lock so we don't try to perform a sleeping
+ * allocation under it.
+ */
+ mutex_exit(&bucket->ftb_mtx);
+
+ /*
+ * Make sure the process exists, isn't a child created as the result
+ * of a vfork(2), and isn't a zombie (but may be in fork).
+ */
+ mutex_enter(&pidlock);
+ if ((p = prfind(pid)) == NULL) {
+ mutex_exit(&pidlock);
+ return (NULL);
+ }
+ mutex_enter(&p->p_lock);
+ mutex_exit(&pidlock);
+ if (p->p_flag & (SVFORK | SEXITING)) {
+ mutex_exit(&p->p_lock);
+ return (NULL);
+ }
+
+ /*
+ * Increment p_dtrace_probes so that the process knows to inform us
+ * when it exits or execs. fasttrap_provider_free() decrements this
+ * when we're done with this provider.
+ */
+ p->p_dtrace_probes++;
+
+ /*
+ * Grab the credentials for this process so we have
+ * something to pass to dtrace_register().
+ */
+ mutex_enter(&p->p_crlock);
+ crhold(p->p_cred);
+ cred = p->p_cred;
+ mutex_exit(&p->p_crlock);
+ mutex_exit(&p->p_lock);
+
+ new_fp = kmem_zalloc(sizeof (fasttrap_provider_t), KM_SLEEP);
+ new_fp->ftp_pid = pid;
+ new_fp->ftp_proc = fasttrap_proc_lookup(pid);
+
+ ASSERT(new_fp->ftp_proc != NULL);
+
+ mutex_enter(&bucket->ftb_mtx);
+
+ /*
+ * Take another lap through the list to make sure a provider hasn't
+ * been created for this pid while we weren't under the bucket lock.
+ */
+ for (fp = bucket->ftb_data; fp != NULL; fp = fp->ftp_next) {
+ if (fp->ftp_pid == pid && strcmp(fp->ftp_name, name) == 0 &&
+ !fp->ftp_retired) {
+ mutex_enter(&fp->ftp_mtx);
+ mutex_exit(&bucket->ftb_mtx);
+ fasttrap_provider_free(new_fp);
+ crfree(cred);
+ return (fp);
+ }
+ }
+
+ (void) strcpy(new_fp->ftp_name, name);
+
+ /*
+ * Fail and return NULL if either the provider name is too long
+ * or we fail to register this new provider with the DTrace
+ * framework. Note that this is the only place we ever construct
+ * the full provider name -- we keep it in pieces in the provider
+ * structure.
+ */
+ if (snprintf(provname, sizeof (provname), "%s%u", name, (uint_t)pid) >=
+ sizeof (provname) ||
+ dtrace_register(provname, pattr,
+ DTRACE_PRIV_PROC | DTRACE_PRIV_OWNER | DTRACE_PRIV_ZONEOWNER, cred,
+ pattr == &pid_attr ? &pid_pops : &usdt_pops, new_fp,
+ &new_fp->ftp_provid) != 0) {
+ mutex_exit(&bucket->ftb_mtx);
+ fasttrap_provider_free(new_fp);
+ crfree(cred);
+ return (NULL);
+ }
+
+ new_fp->ftp_next = bucket->ftb_data;
+ bucket->ftb_data = new_fp;
+
+ mutex_enter(&new_fp->ftp_mtx);
+ mutex_exit(&bucket->ftb_mtx);
+
+ crfree(cred);
+ return (new_fp);
+}
+
+static void
+fasttrap_provider_free(fasttrap_provider_t *provider)
+{
+ pid_t pid = provider->ftp_pid;
+ proc_t *p;
+
+ /*
+ * There need to be no associated enabled probes, no consumers
+ * creating probes, and no meta providers referencing this provider.
+ */
+ ASSERT(provider->ftp_rcount == 0);
+ ASSERT(provider->ftp_ccount == 0);
+ ASSERT(provider->ftp_mcount == 0);
+
+ /*
+ * If this provider hasn't been retired, we need to explicitly drop the
+ * count of active providers on the associated process structure.
+ */
+ if (!provider->ftp_retired) {
+ atomic_add_64(&provider->ftp_proc->ftpc_acount, -1);
+ ASSERT(provider->ftp_proc->ftpc_acount <
+ provider->ftp_proc->ftpc_rcount);
+ }
+
+ fasttrap_proc_release(provider->ftp_proc);
+
+ kmem_free(provider, sizeof (fasttrap_provider_t));
+
+ /*
+ * Decrement p_dtrace_probes on the process whose provider we're
+ * freeing. We don't have to worry about clobbering somone else's
+ * modifications to it because we have locked the bucket that
+ * corresponds to this process's hash chain in the provider hash
+ * table. Don't sweat it if we can't find the process.
+ */
+ mutex_enter(&pidlock);
+ if ((p = prfind(pid)) == NULL) {
+ mutex_exit(&pidlock);
+ return;
+ }
+
+ mutex_enter(&p->p_lock);
+ mutex_exit(&pidlock);
+
+ p->p_dtrace_probes--;
+ mutex_exit(&p->p_lock);
+}
+
+static void
+fasttrap_provider_retire(pid_t pid, const char *name, int mprov)
+{
+ fasttrap_provider_t *fp;
+ fasttrap_bucket_t *bucket;
+ dtrace_provider_id_t provid;
+
+ ASSERT(strlen(name) < sizeof (fp->ftp_name));
+
+ bucket = &fasttrap_provs.fth_table[FASTTRAP_PROVS_INDEX(pid, name)];
+ mutex_enter(&bucket->ftb_mtx);
+
+ for (fp = bucket->ftb_data; fp != NULL; fp = fp->ftp_next) {
+ if (fp->ftp_pid == pid && strcmp(fp->ftp_name, name) == 0 &&
+ !fp->ftp_retired)
+ break;
+ }
+
+ if (fp == NULL) {
+ mutex_exit(&bucket->ftb_mtx);
+ return;
+ }
+
+ mutex_enter(&fp->ftp_mtx);
+ ASSERT(!mprov || fp->ftp_mcount > 0);
+ if (mprov && --fp->ftp_mcount != 0) {
+ mutex_exit(&fp->ftp_mtx);
+ mutex_exit(&bucket->ftb_mtx);
+ return;
+ }
+
+ /*
+ * Mark the provider to be removed in our post-processing step, mark it
+ * retired, and drop the active count on its proc. Marking it indicates
+ * that we should try to remove it; setting the retired flag indicates
+ * that we're done with this provider; dropping the active the proc
+ * releases our hold, and when this reaches zero (as it will during
+ * exit or exec) the proc and associated providers become defunct.
+ *
+ * We obviously need to take the bucket lock before the provider lock
+ * to perform the lookup, but we need to drop the provider lock
+ * before calling into the DTrace framework since we acquire the
+ * provider lock in callbacks invoked from the DTrace framework. The
+ * bucket lock therefore protects the integrity of the provider hash
+ * table.
+ */
+ atomic_add_64(&fp->ftp_proc->ftpc_acount, -1);
+ ASSERT(fp->ftp_proc->ftpc_acount < fp->ftp_proc->ftpc_rcount);
+
+ fp->ftp_retired = 1;
+ fp->ftp_marked = 1;
+ provid = fp->ftp_provid;
+ mutex_exit(&fp->ftp_mtx);
+
+ /*
+ * We don't have to worry about invalidating the same provider twice
+ * since fasttrap_provider_lookup() will ignore provider that have
+ * been marked as retired.
+ */
+ dtrace_invalidate(provid);
+
+ mutex_exit(&bucket->ftb_mtx);
+
+ fasttrap_pid_cleanup();
+}
+
+static int
+fasttrap_uint32_cmp(const void *ap, const void *bp)
+{
+ return (*(const uint32_t *)ap - *(const uint32_t *)bp);
+}
+
+static int
+fasttrap_uint64_cmp(const void *ap, const void *bp)
+{
+ return (*(const uint64_t *)ap - *(const uint64_t *)bp);
+}
+
+static int
+fasttrap_add_probe(fasttrap_probe_spec_t *pdata)
+{
+ fasttrap_provider_t *provider;
+ fasttrap_probe_t *pp;
+ fasttrap_tracepoint_t *tp;
+ char *name;
+ int i, aframes, whack;
+
+ /*
+ * There needs to be at least one desired trace point.
+ */
+ if (pdata->ftps_noffs == 0)
+ return (EINVAL);
+
+ switch (pdata->ftps_type) {
+ case DTFTP_ENTRY:
+ name = "entry";
+ aframes = FASTTRAP_ENTRY_AFRAMES;
+ break;
+ case DTFTP_RETURN:
+ name = "return";
+ aframes = FASTTRAP_RETURN_AFRAMES;
+ break;
+ case DTFTP_OFFSETS:
+ name = NULL;
+ break;
+ default:
+ return (EINVAL);
+ }
+
+ if ((provider = fasttrap_provider_lookup(pdata->ftps_pid,
+ FASTTRAP_PID_NAME, &pid_attr)) == NULL)
+ return (ESRCH);
+
+ /*
+ * Increment this reference count to indicate that a consumer is
+ * actively adding a new probe associated with this provider. This
+ * prevents the provider from being deleted -- we'll need to check
+ * for pending deletions when we drop this reference count.
+ */
+ provider->ftp_ccount++;
+ mutex_exit(&provider->ftp_mtx);
+
+ /*
+ * Grab the creation lock to ensure consistency between calls to
+ * dtrace_probe_lookup() and dtrace_probe_create() in the face of
+ * other threads creating probes. We must drop the provider lock
+ * before taking this lock to avoid a three-way deadlock with the
+ * DTrace framework.
+ */
+ mutex_enter(&provider->ftp_cmtx);
+
+ if (name == NULL) {
+ for (i = 0; i < pdata->ftps_noffs; i++) {
+ char name_str[17];
+
+ (void) sprintf(name_str, "%llx",
+ (unsigned long long)pdata->ftps_offs[i]);
+
+ if (dtrace_probe_lookup(provider->ftp_provid,
+ pdata->ftps_mod, pdata->ftps_func, name_str) != 0)
+ continue;
+
+ atomic_add_32(&fasttrap_total, 1);
+
+ if (fasttrap_total > fasttrap_max) {
+ atomic_add_32(&fasttrap_total, -1);
+ goto no_mem;
+ }
+
+ pp = kmem_zalloc(sizeof (fasttrap_probe_t), KM_SLEEP);
+
+ pp->ftp_prov = provider;
+ pp->ftp_faddr = pdata->ftps_pc;
+ pp->ftp_fsize = pdata->ftps_size;
+ pp->ftp_pid = pdata->ftps_pid;
+ pp->ftp_ntps = 1;
+
+ tp = kmem_zalloc(sizeof (fasttrap_tracepoint_t),
+ KM_SLEEP);
+
+ tp->ftt_proc = provider->ftp_proc;
+ tp->ftt_pc = pdata->ftps_offs[i] + pdata->ftps_pc;
+ tp->ftt_pid = pdata->ftps_pid;
+
+ pp->ftp_tps[0].fit_tp = tp;
+ pp->ftp_tps[0].fit_id.fti_probe = pp;
+ pp->ftp_tps[0].fit_id.fti_ptype = pdata->ftps_type;
+
+ pp->ftp_id = dtrace_probe_create(provider->ftp_provid,
+ pdata->ftps_mod, pdata->ftps_func, name_str,
+ FASTTRAP_OFFSET_AFRAMES, pp);
+ }
+
+ } else if (dtrace_probe_lookup(provider->ftp_provid, pdata->ftps_mod,
+ pdata->ftps_func, name) == 0) {
+ atomic_add_32(&fasttrap_total, pdata->ftps_noffs);
+
+ if (fasttrap_total > fasttrap_max) {
+ atomic_add_32(&fasttrap_total, -pdata->ftps_noffs);
+ goto no_mem;
+ }
+
+ /*
+ * Make sure all tracepoint program counter values are unique.
+ * We later assume that each probe has exactly one tracepoint
+ * for a given pc.
+ */
+ qsort(pdata->ftps_offs, pdata->ftps_noffs,
+ sizeof (uint64_t), fasttrap_uint64_cmp);
+ for (i = 1; i < pdata->ftps_noffs; i++) {
+ if (pdata->ftps_offs[i] > pdata->ftps_offs[i - 1])
+ continue;
+
+ atomic_add_32(&fasttrap_total, -pdata->ftps_noffs);
+ goto no_mem;
+ }
+
+ ASSERT(pdata->ftps_noffs > 0);
+ pp = kmem_zalloc(offsetof(fasttrap_probe_t,
+ ftp_tps[pdata->ftps_noffs]), KM_SLEEP);
+
+ pp->ftp_prov = provider;
+ pp->ftp_faddr = pdata->ftps_pc;
+ pp->ftp_fsize = pdata->ftps_size;
+ pp->ftp_pid = pdata->ftps_pid;
+ pp->ftp_ntps = pdata->ftps_noffs;
+
+ for (i = 0; i < pdata->ftps_noffs; i++) {
+ tp = kmem_zalloc(sizeof (fasttrap_tracepoint_t),
+ KM_SLEEP);
+
+ tp->ftt_proc = provider->ftp_proc;
+ tp->ftt_pc = pdata->ftps_offs[i] + pdata->ftps_pc;
+ tp->ftt_pid = pdata->ftps_pid;
+
+ pp->ftp_tps[i].fit_tp = tp;
+ pp->ftp_tps[i].fit_id.fti_probe = pp;
+ pp->ftp_tps[i].fit_id.fti_ptype = pdata->ftps_type;
+ }
+
+ pp->ftp_id = dtrace_probe_create(provider->ftp_provid,
+ pdata->ftps_mod, pdata->ftps_func, name, aframes, pp);
+ }
+
+ mutex_exit(&provider->ftp_cmtx);
+
+ /*
+ * We know that the provider is still valid since we incremented the
+ * creation reference count. If someone tried to clean up this provider
+ * while we were using it (e.g. because the process called exec(2) or
+ * exit(2)), take note of that and try to clean it up now.
+ */
+ mutex_enter(&provider->ftp_mtx);
+ provider->ftp_ccount--;
+ whack = provider->ftp_retired;
+ mutex_exit(&provider->ftp_mtx);
+
+ if (whack)
+ fasttrap_pid_cleanup();
+
+ return (0);
+
+no_mem:
+ /*
+ * If we've exhausted the allowable resources, we'll try to remove
+ * this provider to free some up. This is to cover the case where
+ * the user has accidentally created many more probes than was
+ * intended (e.g. pid123:::).
+ */
+ mutex_exit(&provider->ftp_cmtx);
+ mutex_enter(&provider->ftp_mtx);
+ provider->ftp_ccount--;
+ provider->ftp_marked = 1;
+ mutex_exit(&provider->ftp_mtx);
+
+ fasttrap_pid_cleanup();
+
+ return (ENOMEM);
+}
+
+/*ARGSUSED*/
+static void *
+fasttrap_meta_provide(void *arg, dtrace_helper_provdesc_t *dhpv, pid_t pid)
+{
+ fasttrap_provider_t *provider;
+
+ /*
+ * A 32-bit unsigned integer (like a pid for example) can be
+ * expressed in 10 or fewer decimal digits. Make sure that we'll
+ * have enough space for the provider name.
+ */
+ if (strlen(dhpv->dthpv_provname) + 10 >=
+ sizeof (provider->ftp_name)) {
+ cmn_err(CE_WARN, "failed to instantiate provider %s: "
+ "name too long to accomodate pid", dhpv->dthpv_provname);
+ return (NULL);
+ }
+
+ /*
+ * Don't let folks spoof the true pid provider.
+ */
+ if (strcmp(dhpv->dthpv_provname, FASTTRAP_PID_NAME) == 0) {
+ cmn_err(CE_WARN, "failed to instantiate provider %s: "
+ "%s is an invalid name", dhpv->dthpv_provname,
+ FASTTRAP_PID_NAME);
+ return (NULL);
+ }
+
+ /*
+ * The highest stability class that fasttrap supports is ISA; cap
+ * the stability of the new provider accordingly.
+ */
+ if (dhpv->dthpv_pattr.dtpa_provider.dtat_class > DTRACE_CLASS_ISA)
+ dhpv->dthpv_pattr.dtpa_provider.dtat_class = DTRACE_CLASS_ISA;
+ if (dhpv->dthpv_pattr.dtpa_mod.dtat_class > DTRACE_CLASS_ISA)
+ dhpv->dthpv_pattr.dtpa_mod.dtat_class = DTRACE_CLASS_ISA;
+ if (dhpv->dthpv_pattr.dtpa_func.dtat_class > DTRACE_CLASS_ISA)
+ dhpv->dthpv_pattr.dtpa_func.dtat_class = DTRACE_CLASS_ISA;
+ if (dhpv->dthpv_pattr.dtpa_name.dtat_class > DTRACE_CLASS_ISA)
+ dhpv->dthpv_pattr.dtpa_name.dtat_class = DTRACE_CLASS_ISA;
+ if (dhpv->dthpv_pattr.dtpa_args.dtat_class > DTRACE_CLASS_ISA)
+ dhpv->dthpv_pattr.dtpa_args.dtat_class = DTRACE_CLASS_ISA;
+
+ if ((provider = fasttrap_provider_lookup(pid, dhpv->dthpv_provname,
+ &dhpv->dthpv_pattr)) == NULL) {
+ cmn_err(CE_WARN, "failed to instantiate provider %s for "
+ "process %u", dhpv->dthpv_provname, (uint_t)pid);
+ return (NULL);
+ }
+
+ /*
+ * Up the meta provider count so this provider isn't removed until
+ * the meta provider has been told to remove it.
+ */
+ provider->ftp_mcount++;
+
+ mutex_exit(&provider->ftp_mtx);
+
+ return (provider);
+}
+
+/*ARGSUSED*/
+static void
+fasttrap_meta_create_probe(void *arg, void *parg,
+ dtrace_helper_probedesc_t *dhpb)
+{
+ fasttrap_provider_t *provider = parg;
+ fasttrap_probe_t *pp;
+ fasttrap_tracepoint_t *tp;
+ int i, j;
+ uint32_t ntps;
+
+ /*
+ * Since the meta provider count is non-zero we don't have to worry
+ * about this provider disappearing.
+ */
+ ASSERT(provider->ftp_mcount > 0);
+
+ /*
+ * The offsets must be unique.
+ */
+ qsort(dhpb->dthpb_offs, dhpb->dthpb_noffs, sizeof (uint32_t),
+ fasttrap_uint32_cmp);
+ for (i = 1; i < dhpb->dthpb_noffs; i++) {
+ if (dhpb->dthpb_base + dhpb->dthpb_offs[i] <=
+ dhpb->dthpb_base + dhpb->dthpb_offs[i - 1])
+ return;
+ }
+
+ qsort(dhpb->dthpb_enoffs, dhpb->dthpb_nenoffs, sizeof (uint32_t),
+ fasttrap_uint32_cmp);
+ for (i = 1; i < dhpb->dthpb_nenoffs; i++) {
+ if (dhpb->dthpb_base + dhpb->dthpb_enoffs[i] <=
+ dhpb->dthpb_base + dhpb->dthpb_enoffs[i - 1])
+ return;
+ }
+
+ /*
+ * Grab the creation lock to ensure consistency between calls to
+ * dtrace_probe_lookup() and dtrace_probe_create() in the face of
+ * other threads creating probes.
+ */
+ mutex_enter(&provider->ftp_cmtx);
+
+ if (dtrace_probe_lookup(provider->ftp_provid, dhpb->dthpb_mod,
+ dhpb->dthpb_func, dhpb->dthpb_name) != 0) {
+ mutex_exit(&provider->ftp_cmtx);
+ return;
+ }
+
+ ntps = dhpb->dthpb_noffs + dhpb->dthpb_nenoffs;
+ ASSERT(ntps > 0);
+
+ atomic_add_32(&fasttrap_total, ntps);
+
+ if (fasttrap_total > fasttrap_max) {
+ atomic_add_32(&fasttrap_total, -ntps);
+ mutex_exit(&provider->ftp_cmtx);
+ return;
+ }
+
+ pp = kmem_zalloc(offsetof(fasttrap_probe_t, ftp_tps[ntps]), KM_SLEEP);
+
+ pp->ftp_prov = provider;
+ pp->ftp_pid = provider->ftp_pid;
+ pp->ftp_ntps = ntps;
+ pp->ftp_nargs = dhpb->dthpb_xargc;
+ pp->ftp_xtypes = dhpb->dthpb_xtypes;
+ pp->ftp_ntypes = dhpb->dthpb_ntypes;
+
+ /*
+ * First create a tracepoint for each actual point of interest.
+ */
+ for (i = 0; i < dhpb->dthpb_noffs; i++) {
+ tp = kmem_zalloc(sizeof (fasttrap_tracepoint_t), KM_SLEEP);
+
+ tp->ftt_proc = provider->ftp_proc;
+ tp->ftt_pc = dhpb->dthpb_base + dhpb->dthpb_offs[i];
+ tp->ftt_pid = provider->ftp_pid;
+
+ pp->ftp_tps[i].fit_tp = tp;
+ pp->ftp_tps[i].fit_id.fti_probe = pp;
+#ifdef __sparc
+ pp->ftp_tps[i].fit_id.fti_ptype = DTFTP_POST_OFFSETS;
+#else
+ pp->ftp_tps[i].fit_id.fti_ptype = DTFTP_OFFSETS;
+#endif
+ }
+
+ /*
+ * Then create a tracepoint for each is-enabled point.
+ */
+ for (j = 0; i < ntps; i++, j++) {
+ tp = kmem_zalloc(sizeof (fasttrap_tracepoint_t), KM_SLEEP);
+
+ tp->ftt_proc = provider->ftp_proc;
+ tp->ftt_pc = dhpb->dthpb_base + dhpb->dthpb_enoffs[j];
+ tp->ftt_pid = provider->ftp_pid;
+
+ pp->ftp_tps[i].fit_tp = tp;
+ pp->ftp_tps[i].fit_id.fti_probe = pp;
+ pp->ftp_tps[i].fit_id.fti_ptype = DTFTP_IS_ENABLED;
+ }
+
+ /*
+ * If the arguments are shuffled around we set the argument remapping
+ * table. Later, when the probe fires, we only remap the arguments
+ * if the table is non-NULL.
+ */
+ for (i = 0; i < dhpb->dthpb_xargc; i++) {
+ if (dhpb->dthpb_args[i] != i) {
+ pp->ftp_argmap = dhpb->dthpb_args;
+ break;
+ }
+ }
+
+ /*
+ * The probe is fully constructed -- register it with DTrace.
+ */
+ pp->ftp_id = dtrace_probe_create(provider->ftp_provid, dhpb->dthpb_mod,
+ dhpb->dthpb_func, dhpb->dthpb_name, FASTTRAP_OFFSET_AFRAMES, pp);
+
+ mutex_exit(&provider->ftp_cmtx);
+}
+
+/*ARGSUSED*/
+static void
+fasttrap_meta_remove(void *arg, dtrace_helper_provdesc_t *dhpv, pid_t pid)
+{
+ /*
+ * Clean up the USDT provider. There may be active consumers of the
+ * provider busy adding probes, no damage will actually befall the
+ * provider until that count has dropped to zero. This just puts
+ * the provider on death row.
+ */
+ fasttrap_provider_retire(pid, dhpv->dthpv_provname, 1);
+}
+
+static dtrace_mops_t fasttrap_mops = {
+ fasttrap_meta_create_probe,
+ fasttrap_meta_provide,
+ fasttrap_meta_remove
+};
+
+/*ARGSUSED*/
+static int
+fasttrap_open(dev_t *devp, int flag, int otyp, cred_t *cred_p)
+{
+ return (0);
+}
+
+/*ARGSUSED*/
+static int
+fasttrap_ioctl(dev_t dev, int cmd, intptr_t arg, int md, cred_t *cr, int *rv)
+{
+ if (!dtrace_attached())
+ return (EAGAIN);
+
+ if (cmd == FASTTRAPIOC_MAKEPROBE) {
+ fasttrap_probe_spec_t *uprobe = (void *)arg;
+ fasttrap_probe_spec_t *probe;
+ uint64_t noffs;
+ size_t size;
+ int ret;
+ char *c;
+
+ if (copyin(&uprobe->ftps_noffs, &noffs,
+ sizeof (uprobe->ftps_noffs)))
+ return (EFAULT);
+
+ /*
+ * Probes must have at least one tracepoint.
+ */
+ if (noffs == 0)
+ return (EINVAL);
+
+ size = sizeof (fasttrap_probe_spec_t) +
+ sizeof (probe->ftps_offs[0]) * (noffs - 1);
+
+ if (size > 1024 * 1024)
+ return (ENOMEM);
+
+ probe = kmem_alloc(size, KM_SLEEP);
+
+ if (copyin(uprobe, probe, size) != 0) {
+ kmem_free(probe, size);
+ return (EFAULT);
+ }
+
+ /*
+ * Verify that the function and module strings contain no
+ * funny characters.
+ */
+ for (c = &probe->ftps_func[0]; *c != '\0'; c++) {
+ if (*c < 0x20 || 0x7f <= *c) {
+ ret = EINVAL;
+ goto err;
+ }
+ }
+
+ for (c = &probe->ftps_mod[0]; *c != '\0'; c++) {
+ if (*c < 0x20 || 0x7f <= *c) {
+ ret = EINVAL;
+ goto err;
+ }
+ }
+
+ if (!PRIV_POLICY_CHOICE(cr, PRIV_ALL, B_FALSE)) {
+ proc_t *p;
+ pid_t pid = probe->ftps_pid;
+
+ mutex_enter(&pidlock);
+ /*
+ * Report an error if the process doesn't exist
+ * or is actively being birthed.
+ */
+ if ((p = prfind(pid)) == NULL || p->p_stat == SIDL) {
+ mutex_exit(&pidlock);
+ return (ESRCH);
+ }
+ mutex_enter(&p->p_lock);
+ mutex_exit(&pidlock);
+
+ if ((ret = priv_proc_cred_perm(cr, p, NULL,
+ VREAD | VWRITE)) != 0) {
+ mutex_exit(&p->p_lock);
+ return (ret);
+ }
+
+ mutex_exit(&p->p_lock);
+ }
+
+ ret = fasttrap_add_probe(probe);
+err:
+ kmem_free(probe, size);
+
+ return (ret);
+
+ } else if (cmd == FASTTRAPIOC_GETINSTR) {
+ fasttrap_instr_query_t instr;
+ fasttrap_tracepoint_t *tp;
+ uint_t index;
+ int ret;
+
+ if (copyin((void *)arg, &instr, sizeof (instr)) != 0)
+ return (EFAULT);
+
+ if (!PRIV_POLICY_CHOICE(cr, PRIV_ALL, B_FALSE)) {
+ proc_t *p;
+ pid_t pid = instr.ftiq_pid;
+
+ mutex_enter(&pidlock);
+ /*
+ * Report an error if the process doesn't exist
+ * or is actively being birthed.
+ */
+ if ((p = prfind(pid)) == NULL || p->p_stat == SIDL) {
+ mutex_exit(&pidlock);
+ return (ESRCH);
+ }
+ mutex_enter(&p->p_lock);
+ mutex_exit(&pidlock);
+
+ if ((ret = priv_proc_cred_perm(cr, p, NULL,
+ VREAD)) != 0) {
+ mutex_exit(&p->p_lock);
+ return (ret);
+ }
+
+ mutex_exit(&p->p_lock);
+ }
+
+ index = FASTTRAP_TPOINTS_INDEX(instr.ftiq_pid, instr.ftiq_pc);
+
+ mutex_enter(&fasttrap_tpoints.fth_table[index].ftb_mtx);
+ tp = fasttrap_tpoints.fth_table[index].ftb_data;
+ while (tp != NULL) {
+ if (instr.ftiq_pid == tp->ftt_pid &&
+ instr.ftiq_pc == tp->ftt_pc &&
+ tp->ftt_proc->ftpc_acount != 0)
+ break;
+
+ tp = tp->ftt_next;
+ }
+
+ if (tp == NULL) {
+ mutex_exit(&fasttrap_tpoints.fth_table[index].ftb_mtx);
+ return (ENOENT);
+ }
+
+ bcopy(&tp->ftt_instr, &instr.ftiq_instr,
+ sizeof (instr.ftiq_instr));
+ mutex_exit(&fasttrap_tpoints.fth_table[index].ftb_mtx);
+
+ if (copyout(&instr, (void *)arg, sizeof (instr)) != 0)
+ return (EFAULT);
+
+ return (0);
+ }
+
+ return (EINVAL);
+}
+
+static struct cb_ops fasttrap_cb_ops = {
+ fasttrap_open, /* open */
+ nodev, /* close */
+ nulldev, /* strategy */
+ nulldev, /* print */
+ nodev, /* dump */
+ nodev, /* read */
+ nodev, /* write */
+ fasttrap_ioctl, /* ioctl */
+ nodev, /* devmap */
+ nodev, /* mmap */
+ nodev, /* segmap */
+ nochpoll, /* poll */
+ ddi_prop_op, /* cb_prop_op */
+ 0, /* streamtab */
+ D_NEW | D_MP /* Driver compatibility flag */
+};
+
+/*ARGSUSED*/
+static int
+fasttrap_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
+{
+ int error;
+
+ switch (infocmd) {
+ case DDI_INFO_DEVT2DEVINFO:
+ *result = (void *)fasttrap_devi;
+ error = DDI_SUCCESS;
+ break;
+ case DDI_INFO_DEVT2INSTANCE:
+ *result = (void *)0;
+ error = DDI_SUCCESS;
+ break;
+ default:
+ error = DDI_FAILURE;
+ }
+ return (error);
+}
+
+static int
+fasttrap_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
+{
+ ulong_t nent;
+
+ switch (cmd) {
+ case DDI_ATTACH:
+ break;
+ case DDI_RESUME:
+ return (DDI_SUCCESS);
+ default:
+ return (DDI_FAILURE);
+ }
+
+ if (ddi_create_minor_node(devi, "fasttrap", S_IFCHR, 0,
+ DDI_PSEUDO, NULL) == DDI_FAILURE) {
+ ddi_remove_minor_node(devi, NULL);
+ return (DDI_FAILURE);
+ }
+
+ ddi_report_dev(devi);
+ fasttrap_devi = devi;
+
+ /*
+ * Install our hooks into fork(2), exec(2), and exit(2).
+ */
+ dtrace_fasttrap_fork_ptr = &fasttrap_fork;
+ dtrace_fasttrap_exit_ptr = &fasttrap_exec_exit;
+ dtrace_fasttrap_exec_ptr = &fasttrap_exec_exit;
+
+ fasttrap_max = ddi_getprop(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS,
+ "fasttrap-max-probes", FASTTRAP_MAX_DEFAULT);
+ fasttrap_total = 0;
+
+ /*
+ * Conjure up the tracepoints hashtable...
+ */
+ nent = ddi_getprop(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS,
+ "fasttrap-hash-size", FASTTRAP_TPOINTS_DEFAULT_SIZE);
+
+ if (nent == 0 || nent > 0x1000000)
+ nent = FASTTRAP_TPOINTS_DEFAULT_SIZE;
+
+ if ((nent & (nent - 1)) == 0)
+ fasttrap_tpoints.fth_nent = nent;
+ else
+ fasttrap_tpoints.fth_nent = 1 << fasttrap_highbit(nent);
+ ASSERT(fasttrap_tpoints.fth_nent > 0);
+ fasttrap_tpoints.fth_mask = fasttrap_tpoints.fth_nent - 1;
+ fasttrap_tpoints.fth_table = kmem_zalloc(fasttrap_tpoints.fth_nent *
+ sizeof (fasttrap_bucket_t), KM_SLEEP);
+
+ /*
+ * ... and the providers hash table...
+ */
+ nent = FASTTRAP_PROVIDERS_DEFAULT_SIZE;
+ if ((nent & (nent - 1)) == 0)
+ fasttrap_provs.fth_nent = nent;
+ else
+ fasttrap_provs.fth_nent = 1 << fasttrap_highbit(nent);
+ ASSERT(fasttrap_provs.fth_nent > 0);
+ fasttrap_provs.fth_mask = fasttrap_provs.fth_nent - 1;
+ fasttrap_provs.fth_table = kmem_zalloc(fasttrap_provs.fth_nent *
+ sizeof (fasttrap_bucket_t), KM_SLEEP);
+
+ /*
+ * ... and the procs hash table.
+ */
+ nent = FASTTRAP_PROCS_DEFAULT_SIZE;
+ if ((nent & (nent - 1)) == 0)
+ fasttrap_procs.fth_nent = nent;
+ else
+ fasttrap_procs.fth_nent = 1 << fasttrap_highbit(nent);
+ ASSERT(fasttrap_procs.fth_nent > 0);
+ fasttrap_procs.fth_mask = fasttrap_procs.fth_nent - 1;
+ fasttrap_procs.fth_table = kmem_zalloc(fasttrap_procs.fth_nent *
+ sizeof (fasttrap_bucket_t), KM_SLEEP);
+
+ (void) dtrace_meta_register("fasttrap", &fasttrap_mops, NULL,
+ &fasttrap_meta_id);
+
+ return (DDI_SUCCESS);
+}
+
+static int
+fasttrap_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
+{
+ int i, fail = 0;
+ timeout_id_t tmp;
+
+ switch (cmd) {
+ case DDI_DETACH:
+ break;
+ case DDI_SUSPEND:
+ return (DDI_SUCCESS);
+ default:
+ return (DDI_FAILURE);
+ }
+
+ /*
+ * Unregister the meta-provider to make sure no new fasttrap-
+ * managed providers come along while we're trying to close up
+ * shop. If we fail to detach, we'll need to re-register as a
+ * meta-provider. We can fail to unregister as a meta-provider
+ * if providers we manage still exist.
+ */
+ if (fasttrap_meta_id != DTRACE_METAPROVNONE &&
+ dtrace_meta_unregister(fasttrap_meta_id) != 0)
+ return (DDI_FAILURE);
+
+ /*
+ * Prevent any new timeouts from running by setting fasttrap_timeout
+ * to a non-zero value, and wait for the current timeout to complete.
+ */
+ mutex_enter(&fasttrap_cleanup_mtx);
+ fasttrap_cleanup_work = 0;
+
+ while (fasttrap_timeout != (timeout_id_t)1) {
+ tmp = fasttrap_timeout;
+ fasttrap_timeout = (timeout_id_t)1;
+
+ if (tmp != 0) {
+ mutex_exit(&fasttrap_cleanup_mtx);
+ (void) untimeout(tmp);
+ mutex_enter(&fasttrap_cleanup_mtx);
+ }
+ }
+
+ fasttrap_cleanup_work = 0;
+ mutex_exit(&fasttrap_cleanup_mtx);
+
+ /*
+ * Iterate over all of our providers. If there's still a process
+ * that corresponds to that pid, fail to detach.
+ */
+ for (i = 0; i < fasttrap_provs.fth_nent; i++) {
+ fasttrap_provider_t **fpp, *fp;
+ fasttrap_bucket_t *bucket = &fasttrap_provs.fth_table[i];
+
+ mutex_enter(&bucket->ftb_mtx);
+ fpp = (fasttrap_provider_t **)&bucket->ftb_data;
+ while ((fp = *fpp) != NULL) {
+ /*
+ * Acquire and release the lock as a simple way of
+ * waiting for any other consumer to finish with
+ * this provider. A thread must first acquire the
+ * bucket lock so there's no chance of another thread
+ * blocking on the provider's lock.
+ */
+ mutex_enter(&fp->ftp_mtx);
+ mutex_exit(&fp->ftp_mtx);
+
+ if (dtrace_unregister(fp->ftp_provid) != 0) {
+ fail = 1;
+ fpp = &fp->ftp_next;
+ } else {
+ *fpp = fp->ftp_next;
+ fasttrap_provider_free(fp);
+ }
+ }
+
+ mutex_exit(&bucket->ftb_mtx);
+ }
+
+ if (fail) {
+ uint_t work;
+ /*
+ * If we're failing to detach, we need to unblock timeouts
+ * and start a new timeout if any work has accumulated while
+ * we've been unsuccessfully trying to detach.
+ */
+ mutex_enter(&fasttrap_cleanup_mtx);
+ fasttrap_timeout = 0;
+ work = fasttrap_cleanup_work;
+ mutex_exit(&fasttrap_cleanup_mtx);
+
+ if (work)
+ fasttrap_pid_cleanup();
+
+ (void) dtrace_meta_register("fasttrap", &fasttrap_mops, NULL,
+ &fasttrap_meta_id);
+
+ return (DDI_FAILURE);
+ }
+
+#ifdef DEBUG
+ mutex_enter(&fasttrap_count_mtx);
+ ASSERT(fasttrap_pid_count == 0);
+ mutex_exit(&fasttrap_count_mtx);
+#endif
+
+ kmem_free(fasttrap_tpoints.fth_table,
+ fasttrap_tpoints.fth_nent * sizeof (fasttrap_bucket_t));
+ fasttrap_tpoints.fth_nent = 0;
+
+ kmem_free(fasttrap_provs.fth_table,
+ fasttrap_provs.fth_nent * sizeof (fasttrap_bucket_t));
+ fasttrap_provs.fth_nent = 0;
+
+ kmem_free(fasttrap_procs.fth_table,
+ fasttrap_procs.fth_nent * sizeof (fasttrap_bucket_t));
+ fasttrap_procs.fth_nent = 0;
+
+ /*
+ * We know there are no tracepoints in any process anywhere in
+ * the system so there is no process which has its p_dtrace_count
+ * greater than zero, therefore we know that no thread can actively
+ * be executing code in fasttrap_fork(). Similarly for p_dtrace_probes
+ * and fasttrap_exec() and fasttrap_exit().
+ */
+ ASSERT(dtrace_fasttrap_fork_ptr == &fasttrap_fork);
+ dtrace_fasttrap_fork_ptr = NULL;
+
+ ASSERT(dtrace_fasttrap_exec_ptr == &fasttrap_exec_exit);
+ dtrace_fasttrap_exec_ptr = NULL;
+
+ ASSERT(dtrace_fasttrap_exit_ptr == &fasttrap_exec_exit);
+ dtrace_fasttrap_exit_ptr = NULL;
+
+ ddi_remove_minor_node(devi, NULL);
+
+ return (DDI_SUCCESS);
+}
+
+static struct dev_ops fasttrap_ops = {
+ DEVO_REV, /* devo_rev */
+ 0, /* refcnt */
+ fasttrap_info, /* get_dev_info */
+ nulldev, /* identify */
+ nulldev, /* probe */
+ fasttrap_attach, /* attach */
+ fasttrap_detach, /* detach */
+ nodev, /* reset */
+ &fasttrap_cb_ops, /* driver operations */
+ NULL, /* bus operations */
+ nodev /* dev power */
+};
+
+/*
+ * Module linkage information for the kernel.
+ */
+static struct modldrv modldrv = {
+ &mod_driverops, /* module type (this is a pseudo driver) */
+ "Fasttrap Tracing", /* name of module */
+ &fasttrap_ops, /* driver ops */
+};
+
+static struct modlinkage modlinkage = {
+ MODREV_1,
+ (void *)&modldrv,
+ NULL
+};
+
+int
+_init(void)
+{
+ return (mod_install(&modlinkage));
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&modlinkage, modinfop));
+}
+
+int
+_fini(void)
+{
+ return (mod_remove(&modlinkage));
+}
diff --git a/sys/cddl/contrib/opensolaris/uts/common/dtrace/lockstat.c b/sys/cddl/contrib/opensolaris/uts/common/dtrace/lockstat.c
new file mode 100644
index 000000000000..3eb76a061d32
--- /dev/null
+++ b/sys/cddl/contrib/opensolaris/uts/common/dtrace/lockstat.c
@@ -0,0 +1,341 @@
+/*
+ * 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 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/open.h>
+#include <sys/file.h>
+#include <sys/conf.h>
+#include <sys/modctl.h>
+#include <sys/cmn_err.h>
+#include <sys/bitmap.h>
+#include <sys/debug.h>
+#include <sys/kmem.h>
+#include <sys/errno.h>
+#include <sys/sysmacros.h>
+#include <sys/lockstat.h>
+#include <sys/atomic.h>
+#include <sys/dtrace.h>
+
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+
+typedef struct lockstat_probe {
+ const char *lsp_func;
+ const char *lsp_name;
+ int lsp_probe;
+ dtrace_id_t lsp_id;
+} lockstat_probe_t;
+
+lockstat_probe_t lockstat_probes[] =
+{
+ { LS_MUTEX_ENTER, LSA_ACQUIRE, LS_MUTEX_ENTER_ACQUIRE },
+ { LS_MUTEX_ENTER, LSA_BLOCK, LS_MUTEX_ENTER_BLOCK },
+ { LS_MUTEX_ENTER, LSA_SPIN, LS_MUTEX_ENTER_SPIN },
+ { LS_MUTEX_EXIT, LSA_RELEASE, LS_MUTEX_EXIT_RELEASE },
+ { LS_MUTEX_DESTROY, LSA_RELEASE, LS_MUTEX_DESTROY_RELEASE },
+ { LS_MUTEX_TRYENTER, LSA_ACQUIRE, LS_MUTEX_TRYENTER_ACQUIRE },
+ { LS_LOCK_SET, LSS_ACQUIRE, LS_LOCK_SET_ACQUIRE },
+ { LS_LOCK_SET, LSS_SPIN, LS_LOCK_SET_SPIN },
+ { LS_LOCK_SET_SPL, LSS_ACQUIRE, LS_LOCK_SET_SPL_ACQUIRE },
+ { LS_LOCK_SET_SPL, LSS_SPIN, LS_LOCK_SET_SPL_SPIN },
+ { LS_LOCK_TRY, LSS_ACQUIRE, LS_LOCK_TRY_ACQUIRE },
+ { LS_LOCK_CLEAR, LSS_RELEASE, LS_LOCK_CLEAR_RELEASE },
+ { LS_LOCK_CLEAR_SPLX, LSS_RELEASE, LS_LOCK_CLEAR_SPLX_RELEASE },
+ { LS_CLOCK_UNLOCK, LSS_RELEASE, LS_CLOCK_UNLOCK_RELEASE },
+ { LS_RW_ENTER, LSR_ACQUIRE, LS_RW_ENTER_ACQUIRE },
+ { LS_RW_ENTER, LSR_BLOCK, LS_RW_ENTER_BLOCK },
+ { LS_RW_EXIT, LSR_RELEASE, LS_RW_EXIT_RELEASE },
+ { LS_RW_TRYENTER, LSR_ACQUIRE, LS_RW_TRYENTER_ACQUIRE },
+ { LS_RW_TRYUPGRADE, LSR_UPGRADE, LS_RW_TRYUPGRADE_UPGRADE },
+ { LS_RW_DOWNGRADE, LSR_DOWNGRADE, LS_RW_DOWNGRADE_DOWNGRADE },
+ { LS_THREAD_LOCK, LST_SPIN, LS_THREAD_LOCK_SPIN },
+ { LS_THREAD_LOCK_HIGH, LST_SPIN, LS_THREAD_LOCK_HIGH_SPIN },
+ { NULL }
+};
+
+static dev_info_t *lockstat_devi; /* saved in xxattach() for xxinfo() */
+static kmutex_t lockstat_test; /* for testing purposes only */
+static dtrace_provider_id_t lockstat_id;
+
+/*ARGSUSED*/
+static void
+lockstat_enable(void *arg, dtrace_id_t id, void *parg)
+{
+ lockstat_probe_t *probe = parg;
+
+ ASSERT(!lockstat_probemap[probe->lsp_probe]);
+
+ lockstat_probemap[probe->lsp_probe] = id;
+ membar_producer();
+
+ lockstat_hot_patch();
+ membar_producer();
+
+ /*
+ * Immediately generate a record for the lockstat_test mutex
+ * to verify that the mutex hot-patch code worked as expected.
+ */
+ mutex_enter(&lockstat_test);
+ mutex_exit(&lockstat_test);
+}
+
+/*ARGSUSED*/
+static void
+lockstat_disable(void *arg, dtrace_id_t id, void *parg)
+{
+ lockstat_probe_t *probe = parg;
+ int i;
+
+ ASSERT(lockstat_probemap[probe->lsp_probe]);
+
+ lockstat_probemap[probe->lsp_probe] = 0;
+ lockstat_hot_patch();
+ membar_producer();
+
+ /*
+ * See if we have any probes left enabled.
+ */
+ for (i = 0; i < LS_NPROBES; i++) {
+ if (lockstat_probemap[i]) {
+ /*
+ * This probe is still enabled. We don't need to deal
+ * with waiting for all threads to be out of the
+ * lockstat critical sections; just return.
+ */
+ return;
+ }
+ }
+
+ /*
+ * The delay() here isn't as cheesy as you might think. We don't
+ * want to busy-loop in the kernel, so we have to give up the
+ * CPU between calls to lockstat_active_threads(); that much is
+ * obvious. But the reason it's a do..while loop rather than a
+ * while loop is subtle. The memory barrier above guarantees that
+ * no threads will enter the lockstat code from this point forward.
+ * However, another thread could already be executing lockstat code
+ * without our knowledge if the update to its t_lockstat field hasn't
+ * cleared its CPU's store buffer. Delaying for one clock tick
+ * guarantees that either (1) the thread will have *ample* time to
+ * complete its work, or (2) the thread will be preempted, in which
+ * case it will have to grab and release a dispatcher lock, which
+ * will flush that CPU's store buffer. Either way we're covered.
+ */
+ do {
+ delay(1);
+ } while (lockstat_active_threads());
+}
+
+/*ARGSUSED*/
+static int
+lockstat_open(dev_t *devp, int flag, int otyp, cred_t *cred_p)
+{
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+lockstat_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
+{
+ int error;
+
+ switch (infocmd) {
+ case DDI_INFO_DEVT2DEVINFO:
+ *result = (void *) lockstat_devi;
+ error = DDI_SUCCESS;
+ break;
+ case DDI_INFO_DEVT2INSTANCE:
+ *result = (void *)0;
+ error = DDI_SUCCESS;
+ break;
+ default:
+ error = DDI_FAILURE;
+ }
+ return (error);
+}
+
+/*ARGSUSED*/
+static void
+lockstat_provide(void *arg, const dtrace_probedesc_t *desc)
+{
+ int i = 0;
+
+ for (i = 0; lockstat_probes[i].lsp_func != NULL; i++) {
+ lockstat_probe_t *probe = &lockstat_probes[i];
+
+ if (dtrace_probe_lookup(lockstat_id, "genunix",
+ probe->lsp_func, probe->lsp_name) != 0)
+ continue;
+
+ ASSERT(!probe->lsp_id);
+ probe->lsp_id = dtrace_probe_create(lockstat_id,
+ "genunix", probe->lsp_func, probe->lsp_name,
+ 1, probe);
+ }
+}
+
+/*ARGSUSED*/
+static void
+lockstat_destroy(void *arg, dtrace_id_t id, void *parg)
+{
+ lockstat_probe_t *probe = parg;
+
+ ASSERT(!lockstat_probemap[probe->lsp_probe]);
+ probe->lsp_id = 0;
+}
+
+static dtrace_pattr_t lockstat_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_UNKNOWN },
+{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
+{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
+};
+
+static dtrace_pops_t lockstat_pops = {
+ lockstat_provide,
+ NULL,
+ lockstat_enable,
+ lockstat_disable,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ lockstat_destroy
+};
+
+static int
+lockstat_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
+{
+ switch (cmd) {
+ case DDI_ATTACH:
+ break;
+ case DDI_RESUME:
+ return (DDI_SUCCESS);
+ default:
+ return (DDI_FAILURE);
+ }
+
+ if (ddi_create_minor_node(devi, "lockstat", S_IFCHR, 0,
+ DDI_PSEUDO, 0) == DDI_FAILURE ||
+ dtrace_register("lockstat", &lockstat_attr, DTRACE_PRIV_KERNEL,
+ NULL, &lockstat_pops, NULL, &lockstat_id) != 0) {
+ ddi_remove_minor_node(devi, NULL);
+ return (DDI_FAILURE);
+ }
+
+ lockstat_probe = dtrace_probe;
+ membar_producer();
+
+ ddi_report_dev(devi);
+ lockstat_devi = devi;
+ return (DDI_SUCCESS);
+}
+
+static int
+lockstat_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
+{
+ switch (cmd) {
+ case DDI_DETACH:
+ break;
+ case DDI_SUSPEND:
+ return (DDI_SUCCESS);
+ default:
+ return (DDI_FAILURE);
+ }
+
+ if (dtrace_unregister(lockstat_id) != 0)
+ return (DDI_FAILURE);
+
+ ddi_remove_minor_node(devi, NULL);
+ return (DDI_SUCCESS);
+}
+
+/*
+ * Configuration data structures
+ */
+static struct cb_ops lockstat_cb_ops = {
+ lockstat_open, /* open */
+ nodev, /* close */
+ nulldev, /* strategy */
+ nulldev, /* print */
+ nodev, /* dump */
+ nodev, /* read */
+ nodev, /* write */
+ nodev, /* ioctl */
+ nodev, /* devmap */
+ nodev, /* mmap */
+ nodev, /* segmap */
+ nochpoll, /* poll */
+ ddi_prop_op, /* cb_prop_op */
+ 0, /* streamtab */
+ D_MP | D_NEW /* Driver compatibility flag */
+};
+
+static struct dev_ops lockstat_ops = {
+ DEVO_REV, /* devo_rev, */
+ 0, /* refcnt */
+ lockstat_info, /* getinfo */
+ nulldev, /* identify */
+ nulldev, /* probe */
+ lockstat_attach, /* attach */
+ lockstat_detach, /* detach */
+ nulldev, /* reset */
+ &lockstat_cb_ops, /* cb_ops */
+ NULL, /* bus_ops */
+};
+
+static struct modldrv modldrv = {
+ &mod_driverops, /* Type of module. This one is a driver */
+ "Lock Statistics %I%", /* name of module */
+ &lockstat_ops, /* driver ops */
+};
+
+static struct modlinkage modlinkage = {
+ MODREV_1, (void *)&modldrv, NULL
+};
+
+int
+_init(void)
+{
+ return (mod_install(&modlinkage));
+}
+
+int
+_fini(void)
+{
+ return (mod_remove(&modlinkage));
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&modlinkage, modinfop));
+}
diff --git a/sys/cddl/contrib/opensolaris/uts/common/dtrace/profile.c b/sys/cddl/contrib/opensolaris/uts/common/dtrace/profile.c
new file mode 100644
index 000000000000..8de919a851a2
--- /dev/null
+++ b/sys/cddl/contrib/opensolaris/uts/common/dtrace/profile.c
@@ -0,0 +1,576 @@
+/*
+ * 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.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/errno.h>
+#include <sys/stat.h>
+#include <sys/modctl.h>
+#include <sys/conf.h>
+#include <sys/systm.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/cpuvar.h>
+#include <sys/kmem.h>
+#include <sys/strsubr.h>
+#include <sys/dtrace.h>
+#include <sys/cyclic.h>
+#include <sys/atomic.h>
+
+static dev_info_t *profile_devi;
+static dtrace_provider_id_t profile_id;
+
+/*
+ * Regardless of platform, the stack frames look like this in the case of the
+ * profile provider:
+ *
+ * profile_fire
+ * cyclic_expire
+ * cyclic_fire
+ * [ cbe ]
+ * [ interrupt code ]
+ *
+ * On x86, there are five frames from the generic interrupt code; further, the
+ * interrupted instruction appears as its own stack frame, giving us a total of
+ * 10.
+ *
+ * On SPARC, the picture is further complicated because the compiler
+ * optimizes away tail-calls -- so the following frames are optimized away:
+ *
+ * profile_fire
+ * cyclic_expire
+ *
+ * This gives three frames. However, on DEBUG kernels, the cyclic_expire
+ * frame cannot be tail-call eliminated, yielding four frames in this case.
+ *
+ * All of the above constraints lead to the mess below. Yes, the profile
+ * provider should ideally figure this out on-the-fly by hitting one of its own
+ * probes and then walking its own stack trace. This is complicated, however,
+ * and the static definition doesn't seem to be overly brittle. Still, we
+ * allow for a manual override in case we get it completely wrong.
+ */
+#ifdef __x86
+#define PROF_ARTIFICIAL_FRAMES 10
+#else
+#ifdef __sparc
+#ifdef DEBUG
+#define PROF_ARTIFICIAL_FRAMES 4
+#else
+#define PROF_ARTIFICIAL_FRAMES 3
+#endif
+#endif
+#endif
+
+#define PROF_NAMELEN 15
+
+#define PROF_PROFILE 0
+#define PROF_TICK 1
+#define PROF_PREFIX_PROFILE "profile-"
+#define PROF_PREFIX_TICK "tick-"
+
+typedef struct profile_probe {
+ char prof_name[PROF_NAMELEN];
+ dtrace_id_t prof_id;
+ int prof_kind;
+ hrtime_t prof_interval;
+ cyclic_id_t prof_cyclic;
+} profile_probe_t;
+
+typedef struct profile_probe_percpu {
+ hrtime_t profc_expected;
+ hrtime_t profc_interval;
+ profile_probe_t *profc_probe;
+} profile_probe_percpu_t;
+
+hrtime_t profile_interval_min = NANOSEC / 5000; /* 5000 hz */
+int profile_aframes = 0; /* override */
+
+static int profile_rates[] = {
+ 97, 199, 499, 997, 1999,
+ 4001, 4999, 0, 0, 0,
+ 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0
+};
+
+static int profile_ticks[] = {
+ 1, 10, 100, 500, 1000,
+ 5000, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0
+};
+
+/*
+ * profile_max defines the upper bound on the number of profile probes that
+ * can exist (this is to prevent malicious or clumsy users from exhausing
+ * system resources by creating a slew of profile probes). At mod load time,
+ * this gets its value from PROFILE_MAX_DEFAULT or profile-max-probes if it's
+ * present in the profile.conf file.
+ */
+#define PROFILE_MAX_DEFAULT 1000 /* default max. number of probes */
+static uint32_t profile_max; /* maximum number of profile probes */
+static uint32_t profile_total; /* current number of profile probes */
+
+static void
+profile_fire(void *arg)
+{
+ profile_probe_percpu_t *pcpu = arg;
+ profile_probe_t *prof = pcpu->profc_probe;
+ hrtime_t late;
+
+ late = dtrace_gethrtime() - pcpu->profc_expected;
+ pcpu->profc_expected += pcpu->profc_interval;
+
+ dtrace_probe(prof->prof_id, CPU->cpu_profile_pc,
+ CPU->cpu_profile_upc, late, 0, 0);
+}
+
+static void
+profile_tick(void *arg)
+{
+ profile_probe_t *prof = arg;
+
+ dtrace_probe(prof->prof_id, CPU->cpu_profile_pc,
+ CPU->cpu_profile_upc, 0, 0, 0);
+}
+
+static void
+profile_create(hrtime_t interval, const char *name, int kind)
+{
+ profile_probe_t *prof;
+ int nr_frames = PROF_ARTIFICIAL_FRAMES + dtrace_mach_aframes();
+
+ if (profile_aframes)
+ nr_frames = profile_aframes;
+
+ if (interval < profile_interval_min)
+ return;
+
+ if (dtrace_probe_lookup(profile_id, NULL, NULL, name) != 0)
+ return;
+
+ atomic_add_32(&profile_total, 1);
+ if (profile_total > profile_max) {
+ atomic_add_32(&profile_total, -1);
+ return;
+ }
+
+ prof = kmem_zalloc(sizeof (profile_probe_t), KM_SLEEP);
+ (void) strcpy(prof->prof_name, name);
+ prof->prof_interval = interval;
+ prof->prof_cyclic = CYCLIC_NONE;
+ prof->prof_kind = kind;
+ prof->prof_id = dtrace_probe_create(profile_id,
+ NULL, NULL, name, nr_frames, prof);
+}
+
+/*ARGSUSED*/
+static void
+profile_provide(void *arg, const dtrace_probedesc_t *desc)
+{
+ int i, j, rate, kind;
+ hrtime_t val = 0, mult = 1, len;
+ const char *name, *suffix = NULL;
+
+ const struct {
+ char *prefix;
+ int kind;
+ } types[] = {
+ { PROF_PREFIX_PROFILE, PROF_PROFILE },
+ { PROF_PREFIX_TICK, PROF_TICK },
+ { NULL, NULL }
+ };
+
+ const struct {
+ char *name;
+ hrtime_t mult;
+ } suffixes[] = {
+ { "ns", NANOSEC / NANOSEC },
+ { "nsec", NANOSEC / NANOSEC },
+ { "us", NANOSEC / MICROSEC },
+ { "usec", NANOSEC / MICROSEC },
+ { "ms", NANOSEC / MILLISEC },
+ { "msec", NANOSEC / MILLISEC },
+ { "s", NANOSEC / SEC },
+ { "sec", NANOSEC / SEC },
+ { "m", NANOSEC * (hrtime_t)60 },
+ { "min", NANOSEC * (hrtime_t)60 },
+ { "h", NANOSEC * (hrtime_t)(60 * 60) },
+ { "hour", NANOSEC * (hrtime_t)(60 * 60) },
+ { "d", NANOSEC * (hrtime_t)(24 * 60 * 60) },
+ { "day", NANOSEC * (hrtime_t)(24 * 60 * 60) },
+ { "hz", 0 },
+ { NULL }
+ };
+
+ if (desc == NULL) {
+ char n[PROF_NAMELEN];
+
+ /*
+ * If no description was provided, provide all of our probes.
+ */
+ for (i = 0; i < sizeof (profile_rates) / sizeof (int); i++) {
+ if ((rate = profile_rates[i]) == 0)
+ continue;
+
+ (void) snprintf(n, PROF_NAMELEN, "%s%d",
+ PROF_PREFIX_PROFILE, rate);
+ profile_create(NANOSEC / rate, n, PROF_PROFILE);
+ }
+
+ for (i = 0; i < sizeof (profile_ticks) / sizeof (int); i++) {
+ if ((rate = profile_ticks[i]) == 0)
+ continue;
+
+ (void) snprintf(n, PROF_NAMELEN, "%s%d",
+ PROF_PREFIX_TICK, rate);
+ profile_create(NANOSEC / rate, n, PROF_TICK);
+ }
+
+ return;
+ }
+
+ name = desc->dtpd_name;
+
+ for (i = 0; types[i].prefix != NULL; i++) {
+ len = strlen(types[i].prefix);
+
+ if (strncmp(name, types[i].prefix, len) != 0)
+ continue;
+ break;
+ }
+
+ if (types[i].prefix == NULL)
+ return;
+
+ kind = types[i].kind;
+ j = strlen(name) - len;
+
+ /*
+ * We need to start before any time suffix.
+ */
+ for (j = strlen(name); j >= len; j--) {
+ if (name[j] >= '0' && name[j] <= '9')
+ break;
+ suffix = &name[j];
+ }
+
+ ASSERT(suffix != NULL);
+
+ /*
+ * Now determine the numerical value present in the probe name.
+ */
+ for (; j >= len; j--) {
+ if (name[j] < '0' || name[j] > '9')
+ return;
+
+ val += (name[j] - '0') * mult;
+ mult *= (hrtime_t)10;
+ }
+
+ if (val == 0)
+ return;
+
+ /*
+ * Look-up the suffix to determine the multiplier.
+ */
+ for (i = 0, mult = 0; suffixes[i].name != NULL; i++) {
+ if (strcasecmp(suffixes[i].name, suffix) == 0) {
+ mult = suffixes[i].mult;
+ break;
+ }
+ }
+
+ if (suffixes[i].name == NULL && *suffix != '\0')
+ return;
+
+ if (mult == 0) {
+ /*
+ * The default is frequency-per-second.
+ */
+ val = NANOSEC / val;
+ } else {
+ val *= mult;
+ }
+
+ profile_create(val, name, kind);
+}
+
+/*ARGSUSED*/
+static void
+profile_destroy(void *arg, dtrace_id_t id, void *parg)
+{
+ profile_probe_t *prof = parg;
+
+ ASSERT(prof->prof_cyclic == CYCLIC_NONE);
+ kmem_free(prof, sizeof (profile_probe_t));
+
+ ASSERT(profile_total >= 1);
+ atomic_add_32(&profile_total, -1);
+}
+
+/*ARGSUSED*/
+static void
+profile_online(void *arg, cpu_t *cpu, cyc_handler_t *hdlr, cyc_time_t *when)
+{
+ profile_probe_t *prof = arg;
+ profile_probe_percpu_t *pcpu;
+
+ pcpu = kmem_zalloc(sizeof (profile_probe_percpu_t), KM_SLEEP);
+ pcpu->profc_probe = prof;
+
+ hdlr->cyh_func = profile_fire;
+ hdlr->cyh_arg = pcpu;
+ hdlr->cyh_level = CY_HIGH_LEVEL;
+
+ when->cyt_interval = prof->prof_interval;
+ when->cyt_when = dtrace_gethrtime() + when->cyt_interval;
+
+ pcpu->profc_expected = when->cyt_when;
+ pcpu->profc_interval = when->cyt_interval;
+}
+
+/*ARGSUSED*/
+static void
+profile_offline(void *arg, cpu_t *cpu, void *oarg)
+{
+ profile_probe_percpu_t *pcpu = oarg;
+
+ ASSERT(pcpu->profc_probe == arg);
+ kmem_free(pcpu, sizeof (profile_probe_percpu_t));
+}
+
+/*ARGSUSED*/
+static void
+profile_enable(void *arg, dtrace_id_t id, void *parg)
+{
+ profile_probe_t *prof = parg;
+ cyc_omni_handler_t omni;
+ cyc_handler_t hdlr;
+ cyc_time_t when;
+
+ ASSERT(prof->prof_interval != 0);
+ ASSERT(MUTEX_HELD(&cpu_lock));
+
+ if (prof->prof_kind == PROF_TICK) {
+ hdlr.cyh_func = profile_tick;
+ hdlr.cyh_arg = prof;
+ hdlr.cyh_level = CY_HIGH_LEVEL;
+
+ when.cyt_interval = prof->prof_interval;
+ when.cyt_when = dtrace_gethrtime() + when.cyt_interval;
+ } else {
+ ASSERT(prof->prof_kind == PROF_PROFILE);
+ omni.cyo_online = profile_online;
+ omni.cyo_offline = profile_offline;
+ omni.cyo_arg = prof;
+ }
+
+ if (prof->prof_kind == PROF_TICK) {
+ prof->prof_cyclic = cyclic_add(&hdlr, &when);
+ } else {
+ prof->prof_cyclic = cyclic_add_omni(&omni);
+ }
+}
+
+/*ARGSUSED*/
+static void
+profile_disable(void *arg, dtrace_id_t id, void *parg)
+{
+ profile_probe_t *prof = parg;
+
+ ASSERT(prof->prof_cyclic != CYCLIC_NONE);
+ ASSERT(MUTEX_HELD(&cpu_lock));
+
+ cyclic_remove(prof->prof_cyclic);
+ prof->prof_cyclic = CYCLIC_NONE;
+}
+
+/*ARGSUSED*/
+static int
+profile_usermode(void *arg, dtrace_id_t id, void *parg)
+{
+ return (CPU->cpu_profile_pc == 0);
+}
+
+static dtrace_pattr_t profile_attr = {
+{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
+{ DTRACE_STABILITY_UNSTABLE, DTRACE_STABILITY_UNSTABLE, DTRACE_CLASS_UNKNOWN },
+{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
+{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
+{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
+};
+
+static dtrace_pops_t profile_pops = {
+ profile_provide,
+ NULL,
+ profile_enable,
+ profile_disable,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ profile_usermode,
+ profile_destroy
+};
+
+static int
+profile_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
+{
+ switch (cmd) {
+ case DDI_ATTACH:
+ break;
+ case DDI_RESUME:
+ return (DDI_SUCCESS);
+ default:
+ return (DDI_FAILURE);
+ }
+
+ if (ddi_create_minor_node(devi, "profile", S_IFCHR, 0,
+ DDI_PSEUDO, NULL) == DDI_FAILURE ||
+ dtrace_register("profile", &profile_attr,
+ DTRACE_PRIV_KERNEL | DTRACE_PRIV_USER, NULL,
+ &profile_pops, NULL, &profile_id) != 0) {
+ ddi_remove_minor_node(devi, NULL);
+ return (DDI_FAILURE);
+ }
+
+ profile_max = ddi_getprop(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS,
+ "profile-max-probes", PROFILE_MAX_DEFAULT);
+
+ ddi_report_dev(devi);
+ profile_devi = devi;
+ return (DDI_SUCCESS);
+}
+
+static int
+profile_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
+{
+ switch (cmd) {
+ case DDI_DETACH:
+ break;
+ case DDI_SUSPEND:
+ return (DDI_SUCCESS);
+ default:
+ return (DDI_FAILURE);
+ }
+
+ if (dtrace_unregister(profile_id) != 0)
+ return (DDI_FAILURE);
+
+ ddi_remove_minor_node(devi, NULL);
+ return (DDI_SUCCESS);
+}
+
+/*ARGSUSED*/
+static int
+profile_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
+{
+ int error;
+
+ switch (infocmd) {
+ case DDI_INFO_DEVT2DEVINFO:
+ *result = (void *)profile_devi;
+ error = DDI_SUCCESS;
+ break;
+ case DDI_INFO_DEVT2INSTANCE:
+ *result = (void *)0;
+ error = DDI_SUCCESS;
+ break;
+ default:
+ error = DDI_FAILURE;
+ }
+ return (error);
+}
+
+/*ARGSUSED*/
+static int
+profile_open(dev_t *devp, int flag, int otyp, cred_t *cred_p)
+{
+ return (0);
+}
+
+static struct cb_ops profile_cb_ops = {
+ profile_open, /* open */
+ nodev, /* close */
+ nulldev, /* strategy */
+ nulldev, /* print */
+ nodev, /* dump */
+ nodev, /* read */
+ nodev, /* write */
+ nodev, /* ioctl */
+ nodev, /* devmap */
+ nodev, /* mmap */
+ nodev, /* segmap */
+ nochpoll, /* poll */
+ ddi_prop_op, /* cb_prop_op */
+ 0, /* streamtab */
+ D_NEW | D_MP /* Driver compatibility flag */
+};
+
+static struct dev_ops profile_ops = {
+ DEVO_REV, /* devo_rev, */
+ 0, /* refcnt */
+ profile_info, /* get_dev_info */
+ nulldev, /* identify */
+ nulldev, /* probe */
+ profile_attach, /* attach */
+ profile_detach, /* detach */
+ nodev, /* reset */
+ &profile_cb_ops, /* driver operations */
+ NULL, /* bus operations */
+ nodev /* dev power */
+};
+
+/*
+ * Module linkage information for the kernel.
+ */
+static struct modldrv modldrv = {
+ &mod_driverops, /* module type (this is a pseudo driver) */
+ "Profile Interrupt Tracing", /* name of module */
+ &profile_ops, /* driver ops */
+};
+
+static struct modlinkage modlinkage = {
+ MODREV_1,
+ (void *)&modldrv,
+ NULL
+};
+
+int
+_init(void)
+{
+ return (mod_install(&modlinkage));
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&modlinkage, modinfop));
+}
+
+int
+_fini(void)
+{
+ return (mod_remove(&modlinkage));
+}
diff --git a/sys/cddl/contrib/opensolaris/uts/common/dtrace/sdt_subr.c b/sys/cddl/contrib/opensolaris/uts/common/dtrace/sdt_subr.c
new file mode 100644
index 000000000000..66ff8a92a01b
--- /dev/null
+++ b/sys/cddl/contrib/opensolaris/uts/common/dtrace/sdt_subr.c
@@ -0,0 +1,888 @@
+/*
+ * 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 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/sdt_impl.h>
+
+static dtrace_pattr_t vtrace_attr = {
+{ DTRACE_STABILITY_UNSTABLE, DTRACE_STABILITY_UNSTABLE, DTRACE_CLASS_ISA },
+{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
+{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
+{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
+{ DTRACE_STABILITY_UNSTABLE, DTRACE_STABILITY_UNSTABLE, DTRACE_CLASS_ISA },
+};
+
+static dtrace_pattr_t info_attr = {
+{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_ISA },
+{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
+{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
+{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_ISA },
+{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_ISA },
+};
+
+static dtrace_pattr_t fpu_attr = {
+{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_ISA },
+{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
+{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
+{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_CPU },
+{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_ISA },
+};
+
+static dtrace_pattr_t fsinfo_attr = {
+{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_ISA },
+{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
+{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
+{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
+{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_ISA },
+};
+
+static dtrace_pattr_t stab_attr = {
+{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_ISA },
+{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
+{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
+{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_ISA },
+{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_ISA },
+};
+
+static dtrace_pattr_t sdt_attr = {
+{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_ISA },
+{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
+{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
+{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_ISA },
+{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_ISA },
+};
+
+static dtrace_pattr_t xpv_attr = {
+{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_PLATFORM },
+{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
+{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
+{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_PLATFORM },
+{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_PLATFORM },
+};
+
+sdt_provider_t sdt_providers[] = {
+ { "vtrace", "__vtrace_", &vtrace_attr, 0 },
+ { "sysinfo", "__cpu_sysinfo_", &info_attr, 0 },
+ { "vminfo", "__cpu_vminfo_", &info_attr, 0 },
+ { "fpuinfo", "__fpuinfo_", &fpu_attr, 0 },
+ { "sched", "__sched_", &stab_attr, 0 },
+ { "proc", "__proc_", &stab_attr, 0 },
+ { "io", "__io_", &stab_attr, 0 },
+ { "mib", "__mib_", &stab_attr, 0 },
+ { "fsinfo", "__fsinfo_", &fsinfo_attr, 0 },
+ { "nfsv3", "__nfsv3_", &stab_attr, 0 },
+ { "nfsv4", "__nfsv4_", &stab_attr, 0 },
+ { "xpv", "__xpv_", &xpv_attr, 0 },
+ { "sysevent", "__sysevent_", &stab_attr, 0 },
+ { "sdt", NULL, &sdt_attr, 0 },
+ { NULL }
+};
+
+sdt_argdesc_t sdt_args[] = {
+ { "sched", "wakeup", 0, 0, "kthread_t *", "lwpsinfo_t *" },
+ { "sched", "wakeup", 1, 0, "kthread_t *", "psinfo_t *" },
+ { "sched", "dequeue", 0, 0, "kthread_t *", "lwpsinfo_t *" },
+ { "sched", "dequeue", 1, 0, "kthread_t *", "psinfo_t *" },
+ { "sched", "dequeue", 2, 1, "disp_t *", "cpuinfo_t *" },
+ { "sched", "enqueue", 0, 0, "kthread_t *", "lwpsinfo_t *" },
+ { "sched", "enqueue", 1, 0, "kthread_t *", "psinfo_t *" },
+ { "sched", "enqueue", 2, 1, "disp_t *", "cpuinfo_t *" },
+ { "sched", "enqueue", 3, 2, "int" },
+ { "sched", "off-cpu", 0, 0, "kthread_t *", "lwpsinfo_t *" },
+ { "sched", "off-cpu", 1, 0, "kthread_t *", "psinfo_t *" },
+ { "sched", "tick", 0, 0, "kthread_t *", "lwpsinfo_t *" },
+ { "sched", "tick", 1, 0, "kthread_t *", "psinfo_t *" },
+ { "sched", "change-pri", 0, 0, "kthread_t *", "lwpsinfo_t *" },
+ { "sched", "change-pri", 1, 0, "kthread_t *", "psinfo_t *" },
+ { "sched", "change-pri", 2, 1, "pri_t" },
+ { "sched", "schedctl-nopreempt", 0, 0, "kthread_t *", "lwpsinfo_t *" },
+ { "sched", "schedctl-nopreempt", 1, 0, "kthread_t *", "psinfo_t *" },
+ { "sched", "schedctl-nopreempt", 2, 1, "int" },
+ { "sched", "schedctl-preempt", 0, 0, "kthread_t *", "lwpsinfo_t *" },
+ { "sched", "schedctl-preempt", 1, 0, "kthread_t *", "psinfo_t *" },
+ { "sched", "schedctl-yield", 0, 0, "int" },
+ { "sched", "surrender", 0, 0, "kthread_t *", "lwpsinfo_t *" },
+ { "sched", "surrender", 1, 0, "kthread_t *", "psinfo_t *" },
+ { "sched", "cpucaps-sleep", 0, 0, "kthread_t *", "lwpsinfo_t *" },
+ { "sched", "cpucaps-sleep", 1, 0, "kthread_t *", "psinfo_t *" },
+ { "sched", "cpucaps-wakeup", 0, 0, "kthread_t *", "lwpsinfo_t *" },
+ { "sched", "cpucaps-wakeup", 1, 0, "kthread_t *", "psinfo_t *" },
+
+ { "proc", "create", 0, 0, "proc_t *", "psinfo_t *" },
+ { "proc", "exec", 0, 0, "string" },
+ { "proc", "exec-failure", 0, 0, "int" },
+ { "proc", "exit", 0, 0, "int" },
+ { "proc", "fault", 0, 0, "int" },
+ { "proc", "fault", 1, 1, "siginfo_t *" },
+ { "proc", "lwp-create", 0, 0, "kthread_t *", "lwpsinfo_t *" },
+ { "proc", "lwp-create", 1, 0, "kthread_t *", "psinfo_t *" },
+ { "proc", "signal-clear", 0, 0, "int" },
+ { "proc", "signal-clear", 1, 1, "siginfo_t *" },
+ { "proc", "signal-discard", 0, 0, "kthread_t *", "lwpsinfo_t *" },
+ { "proc", "signal-discard", 1, 1, "proc_t *", "psinfo_t *" },
+ { "proc", "signal-discard", 2, 2, "int" },
+ { "proc", "signal-handle", 0, 0, "int" },
+ { "proc", "signal-handle", 1, 1, "siginfo_t *" },
+ { "proc", "signal-handle", 2, 2, "void (*)(void)" },
+ { "proc", "signal-send", 0, 0, "kthread_t *", "lwpsinfo_t *" },
+ { "proc", "signal-send", 1, 0, "kthread_t *", "psinfo_t *" },
+ { "proc", "signal-send", 2, 1, "int" },
+
+ { "io", "start", 0, 0, "buf_t *", "bufinfo_t *" },
+ { "io", "start", 1, 0, "buf_t *", "devinfo_t *" },
+ { "io", "start", 2, 0, "buf_t *", "fileinfo_t *" },
+ { "io", "done", 0, 0, "buf_t *", "bufinfo_t *" },
+ { "io", "done", 1, 0, "buf_t *", "devinfo_t *" },
+ { "io", "done", 2, 0, "buf_t *", "fileinfo_t *" },
+ { "io", "wait-start", 0, 0, "buf_t *", "bufinfo_t *" },
+ { "io", "wait-start", 1, 0, "buf_t *", "devinfo_t *" },
+ { "io", "wait-start", 2, 0, "buf_t *", "fileinfo_t *" },
+ { "io", "wait-done", 0, 0, "buf_t *", "bufinfo_t *" },
+ { "io", "wait-done", 1, 0, "buf_t *", "devinfo_t *" },
+ { "io", "wait-done", 2, 0, "buf_t *", "fileinfo_t *" },
+
+ { "mib", NULL, 0, 0, "int" },
+
+ { "fsinfo", NULL, 0, 0, "vnode_t *", "fileinfo_t *" },
+ { "fsinfo", NULL, 1, 1, "int", "int" },
+
+ { "nfsv3", "op-getattr-start", 0, 0, "struct svc_req *",
+ "conninfo_t *" },
+ { "nfsv3", "op-getattr-start", 1, 1, "nfsv3oparg_t *",
+ "nfsv3opinfo_t *" },
+ { "nfsv3", "op-getattr-start", 2, 3, "GETATTR3args *" },
+ { "nfsv3", "op-getattr-done", 0, 0, "struct svc_req *",
+ "conninfo_t *" },
+ { "nfsv3", "op-getattr-done", 1, 1, "nfsv3oparg_t *",
+ "nfsv3opinfo_t *" },
+ { "nfsv3", "op-getattr-done", 2, 3, "GETATTR3res *" },
+ { "nfsv3", "op-setattr-start", 0, 0, "struct svc_req *",
+ "conninfo_t *" },
+ { "nfsv3", "op-setattr-start", 1, 1, "nfsv3oparg_t *",
+ "nfsv3opinfo_t *" },
+ { "nfsv3", "op-setattr-start", 2, 3, "SETATTR3args *" },
+ { "nfsv3", "op-setattr-done", 0, 0, "struct svc_req *",
+ "conninfo_t *" },
+ { "nfsv3", "op-setattr-done", 1, 1, "nfsv3oparg_t *",
+ "nfsv3opinfo_t *" },
+ { "nfsv3", "op-setattr-done", 2, 3, "SETATTR3res *" },
+ { "nfsv3", "op-lookup-start", 0, 0, "struct svc_req *",
+ "conninfo_t *" },
+ { "nfsv3", "op-lookup-start", 1, 1, "nfsv3oparg_t *",
+ "nfsv3opinfo_t *" },
+ { "nfsv3", "op-lookup-start", 2, 3, "LOOKUP3args *" },
+ { "nfsv3", "op-lookup-done", 0, 0, "struct svc_req *",
+ "conninfo_t *" },
+ { "nfsv3", "op-lookup-done", 1, 1, "nfsv3oparg_t *",
+ "nfsv3opinfo_t *" },
+ { "nfsv3", "op-lookup-done", 2, 3, "LOOKUP3res *" },
+ { "nfsv3", "op-access-start", 0, 0, "struct svc_req *",
+ "conninfo_t *" },
+ { "nfsv3", "op-access-start", 1, 1, "nfsv3oparg_t *",
+ "nfsv3opinfo_t *" },
+ { "nfsv3", "op-access-start", 2, 3, "ACCESS3args *" },
+ { "nfsv3", "op-access-done", 0, 0, "struct svc_req *",
+ "conninfo_t *" },
+ { "nfsv3", "op-access-done", 1, 1, "nfsv3oparg_t *",
+ "nfsv3opinfo_t *" },
+ { "nfsv3", "op-access-done", 2, 3, "ACCESS3res *" },
+ { "nfsv3", "op-commit-start", 0, 0, "struct svc_req *",
+ "conninfo_t *" },
+ { "nfsv3", "op-commit-start", 1, 1, "nfsv3oparg_t *",
+ "nfsv3opinfo_t *" },
+ { "nfsv3", "op-commit-start", 2, 3, "COMMIT3args *" },
+ { "nfsv3", "op-commit-done", 0, 0, "struct svc_req *",
+ "conninfo_t *" },
+ { "nfsv3", "op-commit-done", 1, 1, "nfsv3oparg_t *",
+ "nfsv3opinfo_t *" },
+ { "nfsv3", "op-commit-done", 2, 3, "COMMIT3res *" },
+ { "nfsv3", "op-create-start", 0, 0, "struct svc_req *",
+ "conninfo_t *" },
+ { "nfsv3", "op-create-start", 1, 1, "nfsv3oparg_t *",
+ "nfsv3opinfo_t *" },
+ { "nfsv3", "op-create-start", 2, 3, "CREATE3args *" },
+ { "nfsv3", "op-create-done", 0, 0, "struct svc_req *",
+ "conninfo_t *" },
+ { "nfsv3", "op-create-done", 1, 1, "nfsv3oparg_t *",
+ "nfsv3opinfo_t *" },
+ { "nfsv3", "op-create-done", 2, 3, "CREATE3res *" },
+ { "nfsv3", "op-fsinfo-start", 0, 0, "struct svc_req *",
+ "conninfo_t *" },
+ { "nfsv3", "op-fsinfo-start", 1, 1, "nfsv3oparg_t *",
+ "nfsv3opinfo_t *" },
+ { "nfsv3", "op-fsinfo-start", 2, 3, "FSINFO3args *" },
+ { "nfsv3", "op-fsinfo-done", 0, 0, "struct svc_req *",
+ "conninfo_t *" },
+ { "nfsv3", "op-fsinfo-done", 1, 1, "nfsv3oparg_t *",
+ "nfsv3opinfo_t *" },
+ { "nfsv3", "op-fsinfo-done", 2, 3, "FSINFO3res *" },
+ { "nfsv3", "op-fsstat-start", 0, 0, "struct svc_req *",
+ "conninfo_t *" },
+ { "nfsv3", "op-fsstat-start", 1, 1, "nfsv3oparg_t *",
+ "nfsv3opinfo_t *" },
+ { "nfsv3", "op-fsstat-start", 2, 3, "FSSTAT3args *" },
+ { "nfsv3", "op-fsstat-done", 0, 0, "struct svc_req *",
+ "conninfo_t *" },
+ { "nfsv3", "op-fsstat-done", 1, 1, "nfsv3oparg_t *",
+ "nfsv3opinfo_t *" },
+ { "nfsv3", "op-fsstat-done", 2, 3, "FSSTAT3res *" },
+ { "nfsv3", "op-link-start", 0, 0, "struct svc_req *",
+ "conninfo_t *" },
+ { "nfsv3", "op-link-start", 1, 1, "nfsv3oparg_t *",
+ "nfsv3opinfo_t *" },
+ { "nfsv3", "op-link-start", 2, 3, "LINK3args *" },
+ { "nfsv3", "op-link-done", 0, 0, "struct svc_req *",
+ "conninfo_t *" },
+ { "nfsv3", "op-link-done", 1, 1, "nfsv3oparg_t *",
+ "nfsv3opinfo_t *" },
+ { "nfsv3", "op-link-done", 2, 3, "LINK3res *" },
+ { "nfsv3", "op-mkdir-start", 0, 0, "struct svc_req *",
+ "conninfo_t *" },
+ { "nfsv3", "op-mkdir-start", 1, 1, "nfsv3oparg_t *",
+ "nfsv3opinfo_t *" },
+ { "nfsv3", "op-mkdir-start", 2, 3, "MKDIR3args *" },
+ { "nfsv3", "op-mkdir-done", 0, 0, "struct svc_req *",
+ "conninfo_t *" },
+ { "nfsv3", "op-mkdir-done", 1, 1, "nfsv3oparg_t *",
+ "nfsv3opinfo_t *" },
+ { "nfsv3", "op-mkdir-done", 2, 3, "MKDIR3res *" },
+ { "nfsv3", "op-mknod-start", 0, 0, "struct svc_req *",
+ "conninfo_t *" },
+ { "nfsv3", "op-mknod-start", 1, 1, "nfsv3oparg_t *",
+ "nfsv3opinfo_t *" },
+ { "nfsv3", "op-mknod-start", 2, 3, "MKNOD3args *" },
+ { "nfsv3", "op-mknod-done", 0, 0, "struct svc_req *",
+ "conninfo_t *" },
+ { "nfsv3", "op-mknod-done", 1, 1, "nfsv3oparg_t *",
+ "nfsv3opinfo_t *" },
+ { "nfsv3", "op-mknod-done", 2, 3, "MKNOD3res *" },
+ { "nfsv3", "op-null-start", 0, 0, "struct svc_req *",
+ "conninfo_t *" },
+ { "nfsv3", "op-null-start", 1, 1, "nfsv3oparg_t *",
+ "nfsv3opinfo_t *" },
+ { "nfsv3", "op-null-done", 0, 0, "struct svc_req *",
+ "conninfo_t *" },
+ { "nfsv3", "op-null-done", 1, 1, "nfsv3oparg_t *",
+ "nfsv3opinfo_t *" },
+ { "nfsv3", "op-pathconf-start", 0, 0, "struct svc_req *",
+ "conninfo_t *" },
+ { "nfsv3", "op-pathconf-start", 1, 1, "nfsv3oparg_t *",
+ "nfsv3opinfo_t *" },
+ { "nfsv3", "op-pathconf-start", 2, 3, "PATHCONF3args *" },
+ { "nfsv3", "op-pathconf-done", 0, 0, "struct svc_req *",
+ "conninfo_t *" },
+ { "nfsv3", "op-pathconf-done", 1, 1, "nfsv3oparg_t *",
+ "nfsv3opinfo_t *" },
+ { "nfsv3", "op-pathconf-done", 2, 3, "PATHCONF3res *" },
+ { "nfsv3", "op-read-start", 0, 0, "struct svc_req *",
+ "conninfo_t *" },
+ { "nfsv3", "op-read-start", 1, 1, "nfsv3oparg_t *",
+ "nfsv3opinfo_t *" },
+ { "nfsv3", "op-read-start", 2, 3, "READ3args *" },
+ { "nfsv3", "op-read-done", 0, 0, "struct svc_req *",
+ "conninfo_t *" },
+ { "nfsv3", "op-read-done", 1, 1, "nfsv3oparg_t *",
+ "nfsv3opinfo_t *" },
+ { "nfsv3", "op-read-done", 2, 3, "READ3res *" },
+ { "nfsv3", "op-readdir-start", 0, 0, "struct svc_req *",
+ "conninfo_t *" },
+ { "nfsv3", "op-readdir-start", 1, 1, "nfsv3oparg_t *",
+ "nfsv3opinfo_t *" },
+ { "nfsv3", "op-readdir-start", 2, 3, "READDIR3args *" },
+ { "nfsv3", "op-readdir-done", 0, 0, "struct svc_req *",
+ "conninfo_t *" },
+ { "nfsv3", "op-readdir-done", 1, 1, "nfsv3oparg_t *",
+ "nfsv3opinfo_t *" },
+ { "nfsv3", "op-readdir-done", 2, 3, "READDIR3res *" },
+ { "nfsv3", "op-readdirplus-start", 0, 0, "struct svc_req *",
+ "conninfo_t *" },
+ { "nfsv3", "op-readdirplus-start", 1, 1, "nfsv3oparg_t *",
+ "nfsv3opinfo_t *" },
+ { "nfsv3", "op-readdirplus-start", 2, 3, "READDIRPLUS3args *" },
+ { "nfsv3", "op-readdirplus-done", 0, 0, "struct svc_req *",
+ "conninfo_t *" },
+ { "nfsv3", "op-readdirplus-done", 1, 1, "nfsv3oparg_t *",
+ "nfsv3opinfo_t *" },
+ { "nfsv3", "op-readdirplus-done", 2, 3, "READDIRPLUS3res *" },
+ { "nfsv3", "op-readlink-start", 0, 0, "struct svc_req *",
+ "conninfo_t *" },
+ { "nfsv3", "op-readlink-start", 1, 1, "nfsv3oparg_t *",
+ "nfsv3opinfo_t *" },
+ { "nfsv3", "op-readlink-start", 2, 3, "READLINK3args *" },
+ { "nfsv3", "op-readlink-done", 0, 0, "struct svc_req *",
+ "conninfo_t *" },
+ { "nfsv3", "op-readlink-done", 1, 1, "nfsv3oparg_t *",
+ "nfsv3opinfo_t *" },
+ { "nfsv3", "op-readlink-done", 2, 3, "READLINK3res *" },
+ { "nfsv3", "op-remove-start", 0, 0, "struct svc_req *",
+ "conninfo_t *" },
+ { "nfsv3", "op-remove-start", 1, 1, "nfsv3oparg_t *",
+ "nfsv3opinfo_t *" },
+ { "nfsv3", "op-remove-start", 2, 3, "REMOVE3args *" },
+ { "nfsv3", "op-remove-done", 0, 0, "struct svc_req *",
+ "conninfo_t *" },
+ { "nfsv3", "op-remove-done", 1, 1, "nfsv3oparg_t *",
+ "nfsv3opinfo_t *" },
+ { "nfsv3", "op-remove-done", 2, 3, "REMOVE3res *" },
+ { "nfsv3", "op-rename-start", 0, 0, "struct svc_req *",
+ "conninfo_t *" },
+ { "nfsv3", "op-rename-start", 1, 1, "nfsv3oparg_t *",
+ "nfsv3opinfo_t *" },
+ { "nfsv3", "op-rename-start", 2, 3, "RENAME3args *" },
+ { "nfsv3", "op-rename-done", 0, 0, "struct svc_req *",
+ "conninfo_t *" },
+ { "nfsv3", "op-rename-done", 1, 1, "nfsv3oparg_t *",
+ "nfsv3opinfo_t *" },
+ { "nfsv3", "op-rename-done", 2, 3, "RENAME3res *" },
+ { "nfsv3", "op-rmdir-start", 0, 0, "struct svc_req *",
+ "conninfo_t *" },
+ { "nfsv3", "op-rmdir-start", 1, 1, "nfsv3oparg_t *",
+ "nfsv3opinfo_t *" },
+ { "nfsv3", "op-rmdir-start", 2, 3, "RMDIR3args *" },
+ { "nfsv3", "op-rmdir-done", 0, 0, "struct svc_req *",
+ "conninfo_t *" },
+ { "nfsv3", "op-rmdir-done", 1, 1, "nfsv3oparg_t *",
+ "nfsv3opinfo_t *" },
+ { "nfsv3", "op-rmdir-done", 2, 3, "RMDIR3res *" },
+ { "nfsv3", "op-setattr-start", 0, 0, "struct svc_req *",
+ "conninfo_t *" },
+ { "nfsv3", "op-setattr-start", 1, 1, "nfsv3oparg_t *",
+ "nfsv3opinfo_t *" },
+ { "nfsv3", "op-setattr-start", 2, 3, "SETATTR3args *" },
+ { "nfsv3", "op-setattr-done", 0, 0, "struct svc_req *",
+ "conninfo_t *" },
+ { "nfsv3", "op-setattr-done", 1, 1, "nfsv3oparg_t *",
+ "nfsv3opinfo_t *" },
+ { "nfsv3", "op-setattr-done", 2, 3, "SETATTR3res *" },
+ { "nfsv3", "op-symlink-start", 0, 0, "struct svc_req *",
+ "conninfo_t *" },
+ { "nfsv3", "op-symlink-start", 1, 1, "nfsv3oparg_t *",
+ "nfsv3opinfo_t *" },
+ { "nfsv3", "op-symlink-start", 2, 3, "SYMLINK3args *" },
+ { "nfsv3", "op-symlink-done", 0, 0, "struct svc_req *",
+ "conninfo_t *" },
+ { "nfsv3", "op-symlink-done", 1, 1, "nfsv3oparg_t *",
+ "nfsv3opinfo_t *" },
+ { "nfsv3", "op-symlink-done", 2, 3, "SYMLINK3res *" },
+ { "nfsv3", "op-write-start", 0, 0, "struct svc_req *",
+ "conninfo_t *" },
+ { "nfsv3", "op-write-start", 1, 1, "nfsv3oparg_t *",
+ "nfsv3opinfo_t *" },
+ { "nfsv3", "op-write-start", 2, 3, "WRITE3args *" },
+ { "nfsv3", "op-write-done", 0, 0, "struct svc_req *",
+ "conninfo_t *" },
+ { "nfsv3", "op-write-done", 1, 1, "nfsv3oparg_t *",
+ "nfsv3opinfo_t *" },
+ { "nfsv3", "op-write-done", 2, 3, "WRITE3res *" },
+
+ { "nfsv4", "null-start", 0, 0, "struct svc_req *", "conninfo_t *" },
+ { "nfsv4", "null-done", 0, 0, "struct svc_req *", "conninfo_t *" },
+ { "nfsv4", "compound-start", 0, 0, "struct compound_state *",
+ "conninfo_t *" },
+ { "nfsv4", "compound-start", 1, 0, "struct compound_state *",
+ "nfsv4opinfo_t *" },
+ { "nfsv4", "compound-start", 2, 1, "COMPOUND4args *" },
+ { "nfsv4", "compound-done", 0, 0, "struct compound_state *",
+ "conninfo_t *" },
+ { "nfsv4", "compound-done", 1, 0, "struct compound_state *",
+ "nfsv4opinfo_t *" },
+ { "nfsv4", "compound-done", 2, 1, "COMPOUND4res *" },
+ { "nfsv4", "op-access-start", 0, 0, "struct compound_state *",
+ "conninfo_t *"},
+ { "nfsv4", "op-access-start", 1, 0, "struct compound_state *",
+ "nfsv4opinfo_t *" },
+ { "nfsv4", "op-access-start", 2, 1, "ACCESS4args *" },
+ { "nfsv4", "op-access-done", 0, 0, "struct compound_state *",
+ "conninfo_t *" },
+ { "nfsv4", "op-access-done", 1, 0, "struct compound_state *",
+ "nfsv4opinfo_t *" },
+ { "nfsv4", "op-access-done", 2, 1, "ACCESS4res *" },
+ { "nfsv4", "op-close-start", 0, 0, "struct compound_state *",
+ "conninfo_t *" },
+ { "nfsv4", "op-close-start", 1, 0, "struct compound_state *",
+ "nfsv4opinfo_t *" },
+ { "nfsv4", "op-close-start", 2, 1, "CLOSE4args *" },
+ { "nfsv4", "op-close-done", 0, 0, "struct compound_state *",
+ "conninfo_t *" },
+ { "nfsv4", "op-close-done", 1, 0, "struct compound_state *",
+ "nfsv4opinfo_t *" },
+ { "nfsv4", "op-close-done", 2, 1, "CLOSE4res *" },
+ { "nfsv4", "op-commit-start", 0, 0, "struct compound_state *",
+ "conninfo_t *" },
+ { "nfsv4", "op-commit-start", 1, 0, "struct compound_state *",
+ "nfsv4opinfo_t *" },
+ { "nfsv4", "op-commit-start", 2, 1, "COMMIT4args *" },
+ { "nfsv4", "op-commit-done", 0, 0, "struct compound_state *",
+ "conninfo_t *" },
+ { "nfsv4", "op-commit-done", 1, 0, "struct compound_state *",
+ "nfsv4opinfo_t *" },
+ { "nfsv4", "op-commit-done", 2, 1, "COMMIT4res *" },
+ { "nfsv4", "op-create-start", 0, 0, "struct compound_state *",
+ "conninfo_t *" },
+ { "nfsv4", "op-create-start", 1, 0, "struct compound_state *",
+ "nfsv4opinfo_t *" },
+ { "nfsv4", "op-create-start", 2, 1, "CREATE4args *" },
+ { "nfsv4", "op-create-done", 0, 0, "struct compound_state *",
+ "conninfo_t *" },
+ { "nfsv4", "op-create-done", 1, 0, "struct compound_state *",
+ "nfsv4opinfo_t *" },
+ { "nfsv4", "op-create-done", 2, 1, "CREATE4res *" },
+ { "nfsv4", "op-delegpurge-start", 0, 0, "struct compound_state *",
+ "conninfo_t *" },
+ { "nfsv4", "op-delegpurge-start", 1, 0, "struct compound_state *",
+ "nfsv4opinfo_t *" },
+ { "nfsv4", "op-delegpurge-start", 2, 1, "DELEGPURGE4args *" },
+ { "nfsv4", "op-delegpurge-done", 0, 0, "struct compound_state *",
+ "conninfo_t *" },
+ { "nfsv4", "op-delegpurge-done", 1, 0, "struct compound_state *",
+ "nfsv4opinfo_t *" },
+ { "nfsv4", "op-delegpurge-done", 2, 1, "DELEGPURGE4res *" },
+ { "nfsv4", "op-delegreturn-start", 0, 0, "struct compound_state *",
+ "conninfo_t *" },
+ { "nfsv4", "op-delegreturn-start", 1, 0, "struct compound_state *",
+ "nfsv4opinfo_t *" },
+ { "nfsv4", "op-delegreturn-start", 2, 1, "DELEGRETURN4args *" },
+ { "nfsv4", "op-delegreturn-done", 0, 0, "struct compound_state *",
+ "conninfo_t *" },
+ { "nfsv4", "op-delegreturn-done", 1, 0, "struct compound_state *",
+ "nfsv4opinfo_t *" },
+ { "nfsv4", "op-delegreturn-done", 2, 1, "DELEGRETURN4res *" },
+ { "nfsv4", "op-getattr-start", 0, 0, "struct compound_state *",
+ "conninfo_t *" },
+ { "nfsv4", "op-getattr-start", 1, 0, "struct compound_state *",
+ "nfsv4opinfo_t *" },
+ { "nfsv4", "op-getattr-start", 2, 1, "GETATTR4args *" },
+ { "nfsv4", "op-getattr-done", 0, 0, "struct compound_state *",
+ "conninfo_t *" },
+ { "nfsv4", "op-getattr-done", 1, 0, "struct compound_state *",
+ "nfsv4opinfo_t *" },
+ { "nfsv4", "op-getattr-done", 2, 1, "GETATTR4res *" },
+ { "nfsv4", "op-getfh-start", 0, 0, "struct compound_state *",
+ "conninfo_t *" },
+ { "nfsv4", "op-getfh-start", 1, 0, "struct compound_state *",
+ "nfsv4opinfo_t *" },
+ { "nfsv4", "op-getfh-done", 0, 0, "struct compound_state *",
+ "conninfo_t *" },
+ { "nfsv4", "op-getfh-done", 1, 0, "struct compound_state *",
+ "nfsv4opinfo_t *" },
+ { "nfsv4", "op-getfh-done", 2, 1, "GETFH4res *" },
+ { "nfsv4", "op-link-start", 0, 0, "struct compound_state *",
+ "conninfo_t *" },
+ { "nfsv4", "op-link-start", 1, 0, "struct compound_state *",
+ "nfsv4opinfo_t *" },
+ { "nfsv4", "op-link-start", 2, 1, "LINK4args *" },
+ { "nfsv4", "op-link-done", 0, 0, "struct compound_state *",
+ "conninfo_t *" },
+ { "nfsv4", "op-link-done", 1, 0, "struct compound_state *",
+ "nfsv4opinfo_t *" },
+ { "nfsv4", "op-link-done", 2, 1, "LINK4res *" },
+ { "nfsv4", "op-lock-start", 0, 0, "struct compound_state *",
+ "conninfo_t *" },
+ { "nfsv4", "op-lock-start", 1, 0, "struct compound_state *",
+ "nfsv4opinfo_t *" },
+ { "nfsv4", "op-lock-start", 2, 1, "LOCK4args *" },
+ { "nfsv4", "op-lock-done", 0, 0, "struct compound_state *",
+ "conninfo_t *" },
+ { "nfsv4", "op-lock-done", 1, 0, "struct compound_state *",
+ "nfsv4opinfo_t *" },
+ { "nfsv4", "op-lock-done", 2, 1, "LOCK4res *" },
+ { "nfsv4", "op-lockt-start", 0, 0, "struct compound_state *",
+ "conninfo_t *" },
+ { "nfsv4", "op-lockt-start", 1, 0, "struct compound_state *",
+ "nfsv4opinfo_t *" },
+ { "nfsv4", "op-lockt-start", 2, 1, "LOCKT4args *" },
+ { "nfsv4", "op-lockt-done", 0, 0, "struct compound_state *",
+ "conninfo_t *" },
+ { "nfsv4", "op-lockt-done", 1, 0, "struct compound_state *",
+ "nfsv4opinfo_t *" },
+ { "nfsv4", "op-lockt-done", 2, 1, "LOCKT4res *" },
+ { "nfsv4", "op-locku-start", 0, 0, "struct compound_state *",
+ "conninfo_t *" },
+ { "nfsv4", "op-locku-start", 1, 0, "struct compound_state *",
+ "nfsv4opinfo_t *" },
+ { "nfsv4", "op-locku-start", 2, 1, "LOCKU4args *" },
+ { "nfsv4", "op-locku-done", 0, 0, "struct compound_state *",
+ "conninfo_t *" },
+ { "nfsv4", "op-locku-done", 1, 0, "struct compound_state *",
+ "nfsv4opinfo_t *" },
+ { "nfsv4", "op-locku-done", 2, 1, "LOCKU4res *" },
+ { "nfsv4", "op-lookup-start", 0, 0, "struct compound_state *",
+ "conninfo_t *" },
+ { "nfsv4", "op-lookup-start", 1, 0, "struct compound_state *",
+ "nfsv4opinfo_t *" },
+ { "nfsv4", "op-lookup-start", 2, 1, "LOOKUP4args *" },
+ { "nfsv4", "op-lookup-done", 0, 0, "struct compound_state *",
+ "conninfo_t *" },
+ { "nfsv4", "op-lookup-done", 1, 0, "struct compound_state *",
+ "nfsv4opinfo_t *" },
+ { "nfsv4", "op-lookup-done", 2, 1, "LOOKUP4res *" },
+ { "nfsv4", "op-lookupp-start", 0, 0, "struct compound_state *",
+ "conninfo_t *" },
+ { "nfsv4", "op-lookupp-start", 1, 0, "struct compound_state *",
+ "nfsv4opinfo_t *" },
+ { "nfsv4", "op-lookupp-done", 0, 0, "struct compound_state *",
+ "conninfo_t *" },
+ { "nfsv4", "op-lookupp-done", 1, 0, "struct compound_state *",
+ "nfsv4opinfo_t *" },
+ { "nfsv4", "op-lookupp-done", 2, 1, "LOOKUPP4res *" },
+ { "nfsv4", "op-nverify-start", 0, 0, "struct compound_state *",
+ "conninfo_t *" },
+ { "nfsv4", "op-nverify-start", 1, 0, "struct compound_state *",
+ "nfsv4opinfo_t *" },
+ { "nfsv4", "op-nverify-start", 2, 1, "NVERIFY4args *" },
+ { "nfsv4", "op-nverify-done", 0, 0, "struct compound_state *",
+ "conninfo_t *" },
+ { "nfsv4", "op-nverify-done", 1, 0, "struct compound_state *",
+ "nfsv4opinfo_t *" },
+ { "nfsv4", "op-nverify-done", 2, 1, "NVERIFY4res *" },
+ { "nfsv4", "op-open-start", 0, 0, "struct compound_state *",
+ "conninfo_t *" },
+ { "nfsv4", "op-open-start", 1, 0, "struct compound_state *",
+ "nfsv4opinfo_t *" },
+ { "nfsv4", "op-open-start", 2, 1, "OPEN4args *" },
+ { "nfsv4", "op-open-done", 0, 0, "struct compound_state *",
+ "conninfo_t *" },
+ { "nfsv4", "op-open-done", 1, 0, "struct compound_state *",
+ "nfsv4opinfo_t *" },
+ { "nfsv4", "op-open-done", 2, 1, "OPEN4res *" },
+ { "nfsv4", "op-open-confirm-start", 0, 0, "struct compound_state *",
+ "conninfo_t *" },
+ { "nfsv4", "op-open-confirm-start", 1, 0, "struct compound_state *",
+ "nfsv4opinfo_t *" },
+ { "nfsv4", "op-open-confirm-start", 2, 1, "OPEN_CONFIRM4args *" },
+ { "nfsv4", "op-open-confirm-done", 0, 0, "struct compound_state *",
+ "conninfo_t *" },
+ { "nfsv4", "op-open-confirm-done", 1, 0, "struct compound_state *",
+ "nfsv4opinfo_t *" },
+ { "nfsv4", "op-open-confirm-done", 2, 1, "OPEN_CONFIRM4res *" },
+ { "nfsv4", "op-open-downgrade-start", 0, 0, "struct compound_state *",
+ "conninfo_t *" },
+ { "nfsv4", "op-open-downgrade-start", 1, 0, "struct compound_state *",
+ "nfsv4opinfo_t *" },
+ { "nfsv4", "op-open-downgrade-start", 2, 1, "OPEN_DOWNGRADE4args *" },
+ { "nfsv4", "op-open-downgrade-done", 0, 0, "struct compound_state *",
+ "conninfo_t *" },
+ { "nfsv4", "op-open-downgrade-done", 1, 0, "struct compound_state *",
+ "nfsv4opinfo_t *" },
+ { "nfsv4", "op-open-downgrade-done", 2, 1, "OPEN_DOWNGRADE4res *" },
+ { "nfsv4", "op-openattr-start", 0, 0, "struct compound_state *",
+ "conninfo_t *" },
+ { "nfsv4", "op-openattr-start", 1, 0, "struct compound_state *",
+ "nfsv4opinfo_t *" },
+ { "nfsv4", "op-openattr-start", 2, 1, "OPENATTR4args *" },
+ { "nfsv4", "op-openattr-done", 0, 0, "struct compound_state *",
+ "conninfo_t *" },
+ { "nfsv4", "op-openattr-done", 1, 0, "struct compound_state *",
+ "nfsv4opinfo_t *" },
+ { "nfsv4", "op-openattr-done", 2, 1, "OPENATTR4res *" },
+ { "nfsv4", "op-putfh-start", 0, 0, "struct compound_state *",
+ "conninfo_t *" },
+ { "nfsv4", "op-putfh-start", 1, 0, "struct compound_state *",
+ "nfsv4opinfo_t *" },
+ { "nfsv4", "op-putfh-start", 2, 1, "PUTFH4args *" },
+ { "nfsv4", "op-putfh-done", 0, 0, "struct compound_state *",
+ "conninfo_t *" },
+ { "nfsv4", "op-putfh-done", 1, 0, "struct compound_state *",
+ "nfsv4opinfo_t *" },
+ { "nfsv4", "op-putfh-done", 2, 1, "PUTFH4res *" },
+ { "nfsv4", "op-putpubfh-start", 0, 0, "struct compound_state *",
+ "conninfo_t *" },
+ { "nfsv4", "op-putpubfh-start", 1, 0, "struct compound_state *",
+ "nfsv4opinfo_t *" },
+ { "nfsv4", "op-putpubfh-done", 0, 0, "struct compound_state *",
+ "conninfo_t *" },
+ { "nfsv4", "op-putpubfh-done", 1, 0, "struct compound_state *",
+ "nfsv4opinfo_t *" },
+ { "nfsv4", "op-putpubfh-done", 2, 1, "PUTPUBFH4res *" },
+ { "nfsv4", "op-putrootfh-start", 0, 0, "struct compound_state *",
+ "conninfo_t *" },
+ { "nfsv4", "op-putrootfh-start", 1, 0, "struct compound_state *",
+ "nfsv4opinfo_t *" },
+ { "nfsv4", "op-putrootfh-done", 0, 0, "struct compound_state *",
+ "conninfo_t *" },
+ { "nfsv4", "op-putrootfh-done", 1, 0, "struct compound_state *",
+ "nfsv4opinfo_t *" },
+ { "nfsv4", "op-putrootfh-done", 2, 1, "PUTROOTFH4res *" },
+ { "nfsv4", "op-read-start", 0, 0, "struct compound_state *",
+ "conninfo_t *" },
+ { "nfsv4", "op-read-start", 1, 0, "struct compound_state *",
+ "nfsv4opinfo_t *" },
+ { "nfsv4", "op-read-start", 2, 1, "READ4args *" },
+ { "nfsv4", "op-read-done", 0, 0, "struct compound_state *",
+ "conninfo_t *" },
+ { "nfsv4", "op-read-done", 1, 0, "struct compound_state *",
+ "nfsv4opinfo_t *" },
+ { "nfsv4", "op-read-done", 2, 1, "READ4res *" },
+ { "nfsv4", "op-readdir-start", 0, 0, "struct compound_state *",
+ "conninfo_t *" },
+ { "nfsv4", "op-readdir-start", 1, 0, "struct compound_state *",
+ "nfsv4opinfo_t *" },
+ { "nfsv4", "op-readdir-start", 2, 1, "READDIR4args *" },
+ { "nfsv4", "op-readdir-done", 0, 0, "struct compound_state *",
+ "conninfo_t *" },
+ { "nfsv4", "op-readdir-done", 1, 0, "struct compound_state *",
+ "nfsv4opinfo_t *" },
+ { "nfsv4", "op-readdir-done", 2, 1, "READDIR4res *" },
+ { "nfsv4", "op-readlink-start", 0, 0, "struct compound_state *",
+ "conninfo_t *" },
+ { "nfsv4", "op-readlink-start", 1, 0, "struct compound_state *",
+ "nfsv4opinfo_t *" },
+ { "nfsv4", "op-readlink-done", 0, 0, "struct compound_state *",
+ "conninfo_t *" },
+ { "nfsv4", "op-readlink-done", 1, 0, "struct compound_state *",
+ "nfsv4opinfo_t *" },
+ { "nfsv4", "op-readlink-done", 2, 1, "READLINK4res *" },
+ { "nfsv4", "op-release-lockowner-start", 0, 0,
+ "struct compound_state *", "conninfo_t *" },
+ { "nfsv4", "op-release-lockowner-start", 1, 0,
+ "struct compound_state *", "nfsv4opinfo_t *" },
+ { "nfsv4", "op-release-lockowner-start", 2, 1,
+ "RELEASE_LOCKOWNER4args *" },
+ { "nfsv4", "op-release-lockowner-done", 0, 0,
+ "struct compound_state *", "conninfo_t *" },
+ { "nfsv4", "op-release-lockowner-done", 1, 0,
+ "struct compound_state *", "nfsv4opinfo_t *" },
+ { "nfsv4", "op-release-lockowner-done", 2, 1,
+ "RELEASE_LOCKOWNER4res *" },
+ { "nfsv4", "op-remove-start", 0, 0, "struct compound_state *",
+ "conninfo_t *" },
+ { "nfsv4", "op-remove-start", 1, 0, "struct compound_state *",
+ "nfsv4opinfo_t *" },
+ { "nfsv4", "op-remove-start", 2, 1, "REMOVE4args *" },
+ { "nfsv4", "op-remove-done", 0, 0, "struct compound_state *",
+ "conninfo_t *" },
+ { "nfsv4", "op-remove-done", 1, 0, "struct compound_state *",
+ "nfsv4opinfo_t *" },
+ { "nfsv4", "op-remove-done", 2, 1, "REMOVE4res *" },
+ { "nfsv4", "op-rename-start", 0, 0, "struct compound_state *",
+ "conninfo_t *" },
+ { "nfsv4", "op-rename-start", 1, 0, "struct compound_state *",
+ "nfsv4opinfo_t *" },
+ { "nfsv4", "op-rename-start", 2, 1, "RENAME4args *" },
+ { "nfsv4", "op-rename-done", 0, 0, "struct compound_state *",
+ "conninfo_t *" },
+ { "nfsv4", "op-rename-done", 1, 0, "struct compound_state *",
+ "nfsv4opinfo_t *" },
+ { "nfsv4", "op-rename-done", 2, 1, "RENAME4res *" },
+ { "nfsv4", "op-renew-start", 0, 0, "struct compound_state *",
+ "conninfo_t *" },
+ { "nfsv4", "op-renew-start", 1, 0, "struct compound_state *",
+ "nfsv4opinfo_t *" },
+ { "nfsv4", "op-renew-start", 2, 1, "RENEW4args *" },
+ { "nfsv4", "op-renew-done", 0, 0, "struct compound_state *",
+ "conninfo_t *" },
+ { "nfsv4", "op-renew-done", 1, 0, "struct compound_state *",
+ "nfsv4opinfo_t *" },
+ { "nfsv4", "op-renew-done", 2, 1, "RENEW4res *" },
+ { "nfsv4", "op-restorefh-start", 0, 0, "struct compound_state *",
+ "conninfo_t *" },
+ { "nfsv4", "op-restorefh-start", 1, 0, "struct compound_state *",
+ "nfsv4opinfo_t *" },
+ { "nfsv4", "op-restorefh-done", 0, 0, "struct compound_state *",
+ "conninfo_t *" },
+ { "nfsv4", "op-restorefh-done", 1, 0, "struct compound_state *",
+ "nfsv4opinfo_t *" },
+ { "nfsv4", "op-restorefh-done", 2, 1, "RESTOREFH4res *" },
+ { "nfsv4", "op-savefh-start", 0, 0, "struct compound_state *",
+ "conninfo_t *" },
+ { "nfsv4", "op-savefh-start", 1, 0, "struct compound_state *",
+ "nfsv4opinfo_t *" },
+ { "nfsv4", "op-savefh-done", 0, 0, "struct compound_state *",
+ "conninfo_t *" },
+ { "nfsv4", "op-savefh-done", 1, 0, "struct compound_state *",
+ "nfsv4opinfo_t *" },
+ { "nfsv4", "op-savefh-done", 2, 1, "SAVEFH4res *" },
+ { "nfsv4", "op-secinfo-start", 0, 0, "struct compound_state *",
+ "conninfo_t *" },
+ { "nfsv4", "op-secinfo-start", 1, 0, "struct compound_state *",
+ "nfsv4opinfo_t *" },
+ { "nfsv4", "op-secinfo-start", 2, 1, "SECINFO4args *" },
+ { "nfsv4", "op-secinfo-done", 0, 0, "struct compound_state *",
+ "conninfo_t *" },
+ { "nfsv4", "op-secinfo-done", 1, 0, "struct compound_state *",
+ "nfsv4opinfo_t *" },
+ { "nfsv4", "op-secinfo-done", 2, 1, "SECINFO4res *" },
+ { "nfsv4", "op-setattr-start", 0, 0, "struct compound_state *",
+ "conninfo_t *" },
+ { "nfsv4", "op-setattr-start", 1, 0, "struct compound_state *",
+ "nfsv4opinfo_t *" },
+ { "nfsv4", "op-setattr-start", 2, 1, "SETATTR4args *" },
+ { "nfsv4", "op-setattr-done", 0, 0, "struct compound_state *",
+ "conninfo_t *" },
+ { "nfsv4", "op-setattr-done", 1, 0, "struct compound_state *",
+ "nfsv4opinfo_t *" },
+ { "nfsv4", "op-setattr-done", 2, 1, "SETATTR4res *" },
+ { "nfsv4", "op-setclientid-start", 0, 0, "struct compound_state *",
+ "conninfo_t *" },
+ { "nfsv4", "op-setclientid-start", 1, 0, "struct compound_state *",
+ "nfsv4opinfo_t *" },
+ { "nfsv4", "op-setclientid-start", 2, 1, "SETCLIENTID4args *" },
+ { "nfsv4", "op-setclientid-done", 0, 0, "struct compound_state *",
+ "conninfo_t *" },
+ { "nfsv4", "op-setclientid-done", 1, 0, "struct compound_state *",
+ "nfsv4opinfo_t *" },
+ { "nfsv4", "op-setclientid-done", 2, 1, "SETCLIENTID4res *" },
+ { "nfsv4", "op-setclientid-confirm-start", 0, 0,
+ "struct compound_state *", "conninfo_t *" },
+ { "nfsv4", "op-setclientid-confirm-start", 1, 0,
+ "struct compound_state *", "nfsv4opinfo_t *" },
+ { "nfsv4", "op-setclientid-confirm-start", 2, 1,
+ "SETCLIENTID_CONFIRM4args *" },
+ { "nfsv4", "op-setclientid-confirm-done", 0, 0,
+ "struct compound_state *", "conninfo_t *" },
+ { "nfsv4", "op-setclientid-confirm-done", 1, 0,
+ "struct compound_state *", "nfsv4opinfo_t *" },
+ { "nfsv4", "op-setclientid-confirm-done", 2, 1,
+ "SETCLIENTID_CONFIRM4res *" },
+ { "nfsv4", "op-verify-start", 0, 0, "struct compound_state *",
+ "conninfo_t *" },
+ { "nfsv4", "op-verify-start", 1, 0, "struct compound_state *",
+ "nfsv4opinfo_t *" },
+ { "nfsv4", "op-verify-start", 2, 1, "VERIFY4args *" },
+ { "nfsv4", "op-verify-done", 0, 0, "struct compound_state *",
+ "conninfo_t *" },
+ { "nfsv4", "op-verify-done", 1, 0, "struct compound_state *",
+ "nfsv4opinfo_t *" },
+ { "nfsv4", "op-verify-done", 2, 1, "VERIFY4res *" },
+ { "nfsv4", "op-write-start", 0, 0, "struct compound_state *",
+ "conninfo_t *" },
+ { "nfsv4", "op-write-start", 1, 0, "struct compound_state *",
+ "nfsv4opinfo_t *" },
+ { "nfsv4", "op-write-start", 2, 1, "WRITE4args *" },
+ { "nfsv4", "op-write-done", 0, 0, "struct compound_state *",
+ "conninfo_t *" },
+ { "nfsv4", "op-write-done", 1, 0, "struct compound_state *",
+ "nfsv4opinfo_t *" },
+ { "nfsv4", "op-write-done", 2, 1, "WRITE4res *" },
+ { "nfsv4", "cb-recall-start", 0, 0, "rfs4_client_t *",
+ "conninfo_t *" },
+ { "nfsv4", "cb-recall-start", 1, 1, "rfs4_deleg_state_t *",
+ "nfsv4cbinfo_t *" },
+ { "nfsv4", "cb-recall-start", 2, 2, "CB_RECALL4args *" },
+ { "nfsv4", "cb-recall-done", 0, 0, "rfs4_client_t *",
+ "conninfo_t *" },
+ { "nfsv4", "cb-recall-done", 1, 1, "rfs4_deleg_state_t *",
+ "nfsv4cbinfo_t *" },
+ { "nfsv4", "cb-recall-done", 2, 2, "CB_RECALL4res *" },
+
+ { "sysevent", "post", 0, 0, "evch_bind_t *", "syseventchaninfo_t *" },
+ { "sysevent", "post", 1, 1, "sysevent_impl_t *", "syseventinfo_t *" },
+
+ { "xpv", "add-to-physmap-end", 0, 0, "int" },
+ { "xpv", "add-to-physmap-start", 0, 0, "domid_t" },
+ { "xpv", "add-to-physmap-start", 1, 1, "uint_t" },
+ { "xpv", "add-to-physmap-start", 2, 2, "ulong_t" },
+ { "xpv", "add-to-physmap-start", 3, 3, "ulong_t" },
+ { "xpv", "decrease-reservation-end", 0, 0, "int" },
+ { "xpv", "decrease-reservation-start", 0, 0, "domid_t" },
+ { "xpv", "decrease-reservation-start", 1, 1, "ulong_t" },
+ { "xpv", "decrease-reservation-start", 2, 2, "uint_t" },
+ { "xpv", "decrease-reservation-start", 3, 3, "ulong_t *" },
+ { "xpv", "dom-create-start", 0, 0, "xen_domctl_t *" },
+ { "xpv", "dom-destroy-start", 0, 0, "domid_t" },
+ { "xpv", "dom-pause-start", 0, 0, "domid_t" },
+ { "xpv", "dom-unpause-start", 0, 0, "domid_t" },
+ { "xpv", "dom-create-end", 0, 0, "int" },
+ { "xpv", "dom-destroy-end", 0, 0, "int" },
+ { "xpv", "dom-pause-end", 0, 0, "int" },
+ { "xpv", "dom-unpause-end", 0, 0, "int" },
+ { "xpv", "evtchn-op-end", 0, 0, "int" },
+ { "xpv", "evtchn-op-start", 0, 0, "int" },
+ { "xpv", "evtchn-op-start", 1, 1, "void *" },
+ { "xpv", "increase-reservation-end", 0, 0, "int" },
+ { "xpv", "increase-reservation-start", 0, 0, "domid_t" },
+ { "xpv", "increase-reservation-start", 1, 1, "ulong_t" },
+ { "xpv", "increase-reservation-start", 2, 2, "uint_t" },
+ { "xpv", "increase-reservation-start", 3, 3, "ulong_t *" },
+ { "xpv", "mmap-end", 0, 0, "int" },
+ { "xpv", "mmap-entry", 0, 0, "ulong_t" },
+ { "xpv", "mmap-entry", 1, 1, "ulong_t" },
+ { "xpv", "mmap-entry", 2, 2, "ulong_t" },
+ { "xpv", "mmap-start", 0, 0, "domid_t" },
+ { "xpv", "mmap-start", 1, 1, "int" },
+ { "xpv", "mmap-start", 2, 2, "privcmd_mmap_entry_t *" },
+ { "xpv", "mmapbatch-end", 0, 0, "int" },
+ { "xpv", "mmapbatch-end", 1, 1, "struct seg *" },
+ { "xpv", "mmapbatch-end", 2, 2, "caddr_t" },
+ { "xpv", "mmapbatch-start", 0, 0, "domid_t" },
+ { "xpv", "mmapbatch-start", 1, 1, "int" },
+ { "xpv", "mmapbatch-start", 2, 2, "caddr_t" },
+ { "xpv", "mmu-ext-op-end", 0, 0, "int" },
+ { "xpv", "mmu-ext-op-start", 0, 0, "int" },
+ { "xpv", "mmu-ext-op-start", 1, 1, "struct mmuext_op *" },
+ { "xpv", "mmu-update-start", 0, 0, "int" },
+ { "xpv", "mmu-update-start", 1, 1, "int" },
+ { "xpv", "mmu-update-start", 2, 2, "mmu_update_t *" },
+ { "xpv", "mmu-update-end", 0, 0, "int" },
+ { "xpv", "populate-physmap-end", 0, 0, "int" },
+ { "xpv", "populate-physmap-start", 0, 0, "domid_t" },
+ { "xpv", "populate-physmap-start", 1, 1, "ulong_t" },
+ { "xpv", "populate-physmap-start", 2, 2, "ulong_t *" },
+ { "xpv", "set-memory-map-end", 0, 0, "int" },
+ { "xpv", "set-memory-map-start", 0, 0, "domid_t" },
+ { "xpv", "set-memory-map-start", 1, 1, "int" },
+ { "xpv", "set-memory-map-start", 2, 2, "struct xen_memory_map *" },
+ { "xpv", "setvcpucontext-end", 0, 0, "int" },
+ { "xpv", "setvcpucontext-start", 0, 0, "domid_t" },
+ { "xpv", "setvcpucontext-start", 1, 1, "vcpu_guest_context_t *" },
+ { NULL }
+};
+
+/*ARGSUSED*/
+void
+sdt_getargdesc(void *arg, dtrace_id_t id, void *parg, dtrace_argdesc_t *desc)
+{
+ sdt_probe_t *sdp = parg;
+ int i;
+
+ desc->dtargd_native[0] = '\0';
+ desc->dtargd_xlate[0] = '\0';
+
+ for (i = 0; sdt_args[i].sda_provider != NULL; i++) {
+ sdt_argdesc_t *a = &sdt_args[i];
+
+ if (strcmp(sdp->sdp_provider->sdtp_name, a->sda_provider) != 0)
+ continue;
+
+ if (a->sda_name != NULL &&
+ strcmp(sdp->sdp_name, a->sda_name) != 0)
+ continue;
+
+ if (desc->dtargd_ndx != a->sda_ndx)
+ continue;
+
+ if (a->sda_native != NULL)
+ (void) strcpy(desc->dtargd_native, a->sda_native);
+
+ if (a->sda_xlate != NULL)
+ (void) strcpy(desc->dtargd_xlate, a->sda_xlate);
+
+ desc->dtargd_mapping = a->sda_mapping;
+ return;
+ }
+
+ desc->dtargd_ndx = DTRACE_ARGNONE;
+}
diff --git a/sys/cddl/contrib/opensolaris/uts/common/dtrace/systrace.c b/sys/cddl/contrib/opensolaris/uts/common/dtrace/systrace.c
new file mode 100644
index 000000000000..be14660b04c0
--- /dev/null
+++ b/sys/cddl/contrib/opensolaris/uts/common/dtrace/systrace.c
@@ -0,0 +1,373 @@
+/*
+ * 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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/dtrace.h>
+#include <sys/systrace.h>
+#include <sys/stat.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/atomic.h>
+
+#define SYSTRACE_ARTIFICIAL_FRAMES 1
+
+#define SYSTRACE_SHIFT 16
+#define SYSTRACE_ISENTRY(x) ((int)(x) >> SYSTRACE_SHIFT)
+#define SYSTRACE_SYSNUM(x) ((int)(x) & ((1 << SYSTRACE_SHIFT) - 1))
+#define SYSTRACE_ENTRY(id) ((1 << SYSTRACE_SHIFT) | (id))
+#define SYSTRACE_RETURN(id) (id)
+
+#if ((1 << SYSTRACE_SHIFT) <= NSYSCALL)
+#error 1 << SYSTRACE_SHIFT must exceed number of system calls
+#endif
+
+static dev_info_t *systrace_devi;
+static dtrace_provider_id_t systrace_id;
+
+static void
+systrace_init(struct sysent *actual, systrace_sysent_t **interposed)
+{
+ systrace_sysent_t *sysent = *interposed;
+ int i;
+
+ if (sysent == NULL) {
+ *interposed = sysent = kmem_zalloc(sizeof (systrace_sysent_t) *
+ NSYSCALL, KM_SLEEP);
+ }
+
+ for (i = 0; i < NSYSCALL; i++) {
+ struct sysent *a = &actual[i];
+ systrace_sysent_t *s = &sysent[i];
+
+ if (LOADABLE_SYSCALL(a) && !LOADED_SYSCALL(a))
+ continue;
+
+ if (a->sy_callc == dtrace_systrace_syscall)
+ continue;
+
+#ifdef _SYSCALL32_IMPL
+ if (a->sy_callc == dtrace_systrace_syscall32)
+ continue;
+#endif
+
+ s->stsy_underlying = a->sy_callc;
+ }
+}
+
+/*ARGSUSED*/
+static void
+systrace_provide(void *arg, const dtrace_probedesc_t *desc)
+{
+ int i;
+
+ if (desc != NULL)
+ return;
+
+ systrace_init(sysent, &systrace_sysent);
+#ifdef _SYSCALL32_IMPL
+ systrace_init(sysent32, &systrace_sysent32);
+#endif
+
+ for (i = 0; i < NSYSCALL; i++) {
+ if (systrace_sysent[i].stsy_underlying == NULL)
+ continue;
+
+ if (dtrace_probe_lookup(systrace_id, NULL,
+ syscallnames[i], "entry") != 0)
+ continue;
+
+ (void) dtrace_probe_create(systrace_id, NULL, syscallnames[i],
+ "entry", SYSTRACE_ARTIFICIAL_FRAMES,
+ (void *)((uintptr_t)SYSTRACE_ENTRY(i)));
+ (void) dtrace_probe_create(systrace_id, NULL, syscallnames[i],
+ "return", SYSTRACE_ARTIFICIAL_FRAMES,
+ (void *)((uintptr_t)SYSTRACE_RETURN(i)));
+
+ systrace_sysent[i].stsy_entry = DTRACE_IDNONE;
+ systrace_sysent[i].stsy_return = DTRACE_IDNONE;
+#ifdef _SYSCALL32_IMPL
+ systrace_sysent32[i].stsy_entry = DTRACE_IDNONE;
+ systrace_sysent32[i].stsy_return = DTRACE_IDNONE;
+#endif
+ }
+}
+
+/*ARGSUSED*/
+static void
+systrace_destroy(void *arg, dtrace_id_t id, void *parg)
+{
+ int sysnum = SYSTRACE_SYSNUM((uintptr_t)parg);
+
+ /*
+ * There's nothing to do here but assert that we have actually been
+ * disabled.
+ */
+ if (SYSTRACE_ISENTRY((uintptr_t)parg)) {
+ ASSERT(systrace_sysent[sysnum].stsy_entry == DTRACE_IDNONE);
+#ifdef _SYSCALL32_IMPL
+ ASSERT(systrace_sysent32[sysnum].stsy_entry == DTRACE_IDNONE);
+#endif
+ } else {
+ ASSERT(systrace_sysent[sysnum].stsy_return == DTRACE_IDNONE);
+#ifdef _SYSCALL32_IMPL
+ ASSERT(systrace_sysent32[sysnum].stsy_return == DTRACE_IDNONE);
+#endif
+ }
+}
+
+/*ARGSUSED*/
+static void
+systrace_enable(void *arg, dtrace_id_t id, void *parg)
+{
+ int sysnum = SYSTRACE_SYSNUM((uintptr_t)parg);
+ int enabled = (systrace_sysent[sysnum].stsy_entry != DTRACE_IDNONE ||
+ systrace_sysent[sysnum].stsy_return != DTRACE_IDNONE);
+
+ if (SYSTRACE_ISENTRY((uintptr_t)parg)) {
+ systrace_sysent[sysnum].stsy_entry = id;
+#ifdef _SYSCALL32_IMPL
+ systrace_sysent32[sysnum].stsy_entry = id;
+#endif
+ } else {
+ systrace_sysent[sysnum].stsy_return = id;
+#ifdef _SYSCALL32_IMPL
+ systrace_sysent32[sysnum].stsy_return = id;
+#endif
+ }
+
+ if (enabled) {
+ ASSERT(sysent[sysnum].sy_callc == dtrace_systrace_syscall);
+ return;
+ }
+
+ (void) casptr(&sysent[sysnum].sy_callc,
+ (void *)systrace_sysent[sysnum].stsy_underlying,
+ (void *)dtrace_systrace_syscall);
+#ifdef _SYSCALL32_IMPL
+ (void) casptr(&sysent32[sysnum].sy_callc,
+ (void *)systrace_sysent32[sysnum].stsy_underlying,
+ (void *)dtrace_systrace_syscall32);
+#endif
+}
+
+/*ARGSUSED*/
+static void
+systrace_disable(void *arg, dtrace_id_t id, void *parg)
+{
+ int sysnum = SYSTRACE_SYSNUM((uintptr_t)parg);
+ int disable = (systrace_sysent[sysnum].stsy_entry == DTRACE_IDNONE ||
+ systrace_sysent[sysnum].stsy_return == DTRACE_IDNONE);
+
+ if (disable) {
+ (void) casptr(&sysent[sysnum].sy_callc,
+ (void *)dtrace_systrace_syscall,
+ (void *)systrace_sysent[sysnum].stsy_underlying);
+
+#ifdef _SYSCALL32_IMPL
+ (void) casptr(&sysent32[sysnum].sy_callc,
+ (void *)dtrace_systrace_syscall32,
+ (void *)systrace_sysent32[sysnum].stsy_underlying);
+#endif
+ }
+
+ if (SYSTRACE_ISENTRY((uintptr_t)parg)) {
+ systrace_sysent[sysnum].stsy_entry = DTRACE_IDNONE;
+#ifdef _SYSCALL32_IMPL
+ systrace_sysent32[sysnum].stsy_entry = DTRACE_IDNONE;
+#endif
+ } else {
+ systrace_sysent[sysnum].stsy_return = DTRACE_IDNONE;
+#ifdef _SYSCALL32_IMPL
+ systrace_sysent32[sysnum].stsy_return = DTRACE_IDNONE;
+#endif
+ }
+}
+
+static dtrace_pattr_t systrace_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 systrace_pops = {
+ systrace_provide,
+ NULL,
+ systrace_enable,
+ systrace_disable,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ systrace_destroy
+};
+
+static int
+systrace_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
+{
+ switch (cmd) {
+ case DDI_ATTACH:
+ break;
+ case DDI_RESUME:
+ return (DDI_SUCCESS);
+ default:
+ return (DDI_FAILURE);
+ }
+
+ systrace_probe = (void (*)())dtrace_probe;
+ membar_enter();
+
+ if (ddi_create_minor_node(devi, "systrace", S_IFCHR, 0,
+ DDI_PSEUDO, NULL) == DDI_FAILURE ||
+ dtrace_register("syscall", &systrace_attr, DTRACE_PRIV_USER, NULL,
+ &systrace_pops, NULL, &systrace_id) != 0) {
+ systrace_probe = systrace_stub;
+ ddi_remove_minor_node(devi, NULL);
+ return (DDI_FAILURE);
+ }
+
+ ddi_report_dev(devi);
+ systrace_devi = devi;
+
+ return (DDI_SUCCESS);
+}
+
+static int
+systrace_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
+{
+ switch (cmd) {
+ case DDI_DETACH:
+ break;
+ case DDI_SUSPEND:
+ return (DDI_SUCCESS);
+ default:
+ return (DDI_FAILURE);
+ }
+
+ if (dtrace_unregister(systrace_id) != 0)
+ return (DDI_FAILURE);
+
+ ddi_remove_minor_node(devi, NULL);
+ systrace_probe = systrace_stub;
+ return (DDI_SUCCESS);
+}
+
+/*ARGSUSED*/
+static int
+systrace_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
+{
+ int error;
+
+ switch (infocmd) {
+ case DDI_INFO_DEVT2DEVINFO:
+ *result = (void *)systrace_devi;
+ error = DDI_SUCCESS;
+ break;
+ case DDI_INFO_DEVT2INSTANCE:
+ *result = (void *)0;
+ error = DDI_SUCCESS;
+ break;
+ default:
+ error = DDI_FAILURE;
+ }
+ return (error);
+}
+
+/*ARGSUSED*/
+static int
+systrace_open(dev_t *devp, int flag, int otyp, cred_t *cred_p)
+{
+ return (0);
+}
+
+static struct cb_ops systrace_cb_ops = {
+ systrace_open, /* open */
+ nodev, /* close */
+ nulldev, /* strategy */
+ nulldev, /* print */
+ nodev, /* dump */
+ nodev, /* read */
+ nodev, /* write */
+ nodev, /* ioctl */
+ nodev, /* devmap */
+ nodev, /* mmap */
+ nodev, /* segmap */
+ nochpoll, /* poll */
+ ddi_prop_op, /* cb_prop_op */
+ 0, /* streamtab */
+ D_NEW | D_MP /* Driver compatibility flag */
+};
+
+static struct dev_ops systrace_ops = {
+ DEVO_REV, /* devo_rev, */
+ 0, /* refcnt */
+ systrace_info, /* get_dev_info */
+ nulldev, /* identify */
+ nulldev, /* probe */
+ systrace_attach, /* attach */
+ systrace_detach, /* detach */
+ nodev, /* reset */
+ &systrace_cb_ops, /* driver operations */
+ NULL, /* bus operations */
+ nodev /* dev power */
+};
+
+/*
+ * Module linkage information for the kernel.
+ */
+static struct modldrv modldrv = {
+ &mod_driverops, /* module type (this is a pseudo driver) */
+ "System Call Tracing", /* name of module */
+ &systrace_ops, /* driver ops */
+};
+
+static struct modlinkage modlinkage = {
+ MODREV_1,
+ (void *)&modldrv,
+ NULL
+};
+
+int
+_init(void)
+{
+ return (mod_install(&modlinkage));
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&modlinkage, modinfop));
+}
+
+int
+_fini(void)
+{
+ return (mod_remove(&modlinkage));
+}
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/arc.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/arc.c
index 55cb12407188..420f802f360d 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/arc.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/arc.c
@@ -1148,7 +1148,7 @@ arc_evict(arc_state_t *state, int64_t bytes, boolean_t recycle,
/* prefetch buffers have a minimum lifespan */
if (HDR_IO_IN_PROGRESS(ab) ||
(ab->b_flags & (ARC_PREFETCH|ARC_INDIRECT) &&
- lbolt - ab->b_arc_access < arc_min_prefetch_lifespan)) {
+ LBOLT - ab->b_arc_access < arc_min_prefetch_lifespan)) {
skipped++;
continue;
}
@@ -1439,7 +1439,7 @@ arc_reclaim_needed(void)
return (1);
#endif
#else
- if (kmem_used() > (kmem_size() * 4) / 5)
+ if (kmem_used() > (kmem_size() * 3) / 4)
return (1);
#endif
@@ -1525,7 +1525,7 @@ arc_reclaim_thread(void *dummy __unused)
}
/* reset the growth delay for every reclaim */
- growtime = lbolt + (arc_grow_retry * hz);
+ growtime = LBOLT + (arc_grow_retry * hz);
ASSERT(growtime > 0);
if (zfs_needfree && last_reclaim == ARC_RECLAIM_CONS) {
@@ -1538,7 +1538,7 @@ arc_reclaim_thread(void *dummy __unused)
last_reclaim = ARC_RECLAIM_AGGR;
}
arc_kmem_reap_now(last_reclaim);
- } else if ((growtime > 0) && ((growtime - lbolt) <= 0)) {
+ } else if ((growtime > 0) && ((growtime - LBOLT) <= 0)) {
arc_no_grow = FALSE;
}
@@ -1757,7 +1757,7 @@ arc_access(arc_buf_hdr_t *buf, kmutex_t *hash_lock)
*/
ASSERT(buf->b_arc_access == 0);
- buf->b_arc_access = lbolt;
+ buf->b_arc_access = LBOLT;
DTRACE_PROBE1(new_state__mru, arc_buf_hdr_t *, buf);
arc_change_state(arc_mru, buf, hash_lock);
@@ -1781,7 +1781,7 @@ arc_access(arc_buf_hdr_t *buf, kmutex_t *hash_lock)
buf->b_flags &= ~ARC_PREFETCH;
ARCSTAT_BUMP(arcstat_mru_hits);
}
- buf->b_arc_access = lbolt;
+ buf->b_arc_access = LBOLT;
return;
}
@@ -1790,13 +1790,13 @@ arc_access(arc_buf_hdr_t *buf, kmutex_t *hash_lock)
* but it is still in the cache. Move it to the MFU
* state.
*/
- if (lbolt > buf->b_arc_access + ARC_MINTIME) {
+ if (LBOLT > buf->b_arc_access + ARC_MINTIME) {
/*
* More than 125ms have passed since we
* instantiated this buffer. Move it to the
* most frequently used state.
*/
- buf->b_arc_access = lbolt;
+ buf->b_arc_access = LBOLT;
DTRACE_PROBE1(new_state__mfu, arc_buf_hdr_t *, buf);
arc_change_state(arc_mfu, buf, hash_lock);
}
@@ -1819,7 +1819,7 @@ arc_access(arc_buf_hdr_t *buf, kmutex_t *hash_lock)
DTRACE_PROBE1(new_state__mfu, arc_buf_hdr_t *, buf);
}
- buf->b_arc_access = lbolt;
+ buf->b_arc_access = LBOLT;
arc_change_state(new_state, buf, hash_lock);
ARCSTAT_BUMP(arcstat_mru_ghost_hits);
@@ -1842,7 +1842,7 @@ arc_access(arc_buf_hdr_t *buf, kmutex_t *hash_lock)
mutex_exit(&arc_mfu->arcs_mtx);
}
ARCSTAT_BUMP(arcstat_mfu_hits);
- buf->b_arc_access = lbolt;
+ buf->b_arc_access = LBOLT;
} else if (buf->b_state == arc_mfu_ghost) {
arc_state_t *new_state = arc_mfu;
/*
@@ -1860,7 +1860,7 @@ arc_access(arc_buf_hdr_t *buf, kmutex_t *hash_lock)
new_state = arc_mru;
}
- buf->b_arc_access = lbolt;
+ buf->b_arc_access = LBOLT;
DTRACE_PROBE1(new_state__mfu, arc_buf_hdr_t *, buf);
arc_change_state(new_state, buf, hash_lock);
@@ -2729,7 +2729,7 @@ arc_init(void)
arc_c_max = (arc_c * 8) - (1<<30);
else
arc_c_max = arc_c_min;
- arc_c_max = MAX(arc_c * 6, arc_c_max);
+ arc_c_max = MAX(arc_c * 5, arc_c_max);
#ifdef _KERNEL
/*
* Allow the tunables to override our calculations if they are
@@ -2800,16 +2800,17 @@ arc_init(void)
arc_dead = FALSE;
#ifdef _KERNEL
- /* Warn about ZFS memory requirements. */
+ /* Warn about ZFS memory and address space requirements. */
if (((uint64_t)physmem * PAGESIZE) < (256 + 128 + 64) * (1 << 20)) {
printf("ZFS WARNING: Recommended minimum RAM size is 512MB; "
"expect unstable behavior.\n");
- } else if (kmem_size() < 256 * (1 << 20)) {
- printf("ZFS WARNING: Recommended minimum kmem_size is 256MB; "
+ }
+ if (kmem_size() < 512 * (1 << 20)) {
+ printf("ZFS WARNING: Recommended minimum kmem_size is 512MB; "
"expect unstable behavior.\n");
- printf(" Consider tuning vm.kmem_size or "
- "vm.kmem_size_min\n");
- printf(" in /boot/loader.conf.\n");
+ printf(" Consider tuning vm.kmem_size and "
+ "vm.kmem_size_max\n");
+ printf(" in /boot/loader.conf.\n");
}
#endif
}
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_zfetch.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_zfetch.c
index 78d625cd41bd..b25cc898c37d 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_zfetch.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_zfetch.c
@@ -196,7 +196,7 @@ dmu_zfetch_dofetch(zfetch_t *zf, zstream_t *zs)
break;
}
zs->zst_ph_offset = prefetch_tail;
- zs->zst_last = lbolt;
+ zs->zst_last = LBOLT;
}
/*
@@ -517,7 +517,7 @@ dmu_zfetch_stream_reclaim(zfetch_t *zf)
for (zs = list_head(&zf->zf_stream); zs;
zs = list_next(&zf->zf_stream, zs)) {
- if (((lbolt - zs->zst_last) / hz) > zfetch_min_sec_reap)
+ if (((LBOLT - zs->zst_last) / hz) > zfetch_min_sec_reap)
break;
}
@@ -639,7 +639,7 @@ dmu_zfetch(zfetch_t *zf, uint64_t offset, uint64_t size, int prefetched)
newstream->zst_ph_offset = zst.zst_len + zst.zst_offset;
newstream->zst_cap = zst.zst_len;
newstream->zst_direction = ZFETCH_FORWARD;
- newstream->zst_last = lbolt;
+ newstream->zst_last = LBOLT;
mutex_init(&newstream->zst_lock, NULL, MUTEX_DEFAULT, NULL);
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/refcount.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/refcount.c
index 411ed46e13d7..a2f4614fed87 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/refcount.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/refcount.c
@@ -42,7 +42,7 @@ static kmem_cache_t *reference_cache;
static kmem_cache_t *reference_history_cache;
void
-refcount_init(void)
+refcount_sysinit(void)
{
reference_cache = kmem_cache_create("reference_cache",
sizeof (reference_t), 0, NULL, NULL, NULL, NULL, NULL, 0);
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa_misc.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa_misc.c
index 5da1f96c85c5..1e1f0ee93068 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa_misc.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa_misc.c
@@ -1103,7 +1103,7 @@ spa_init(int mode)
spa_mode = mode;
- refcount_init();
+ refcount_sysinit();
unique_init();
zio_init();
dmu_init();
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/refcount.h b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/refcount.h
index 4de1cae12fe7..c64c6627f783 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/refcount.h
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/refcount.h
@@ -28,6 +28,7 @@
#pragma ident "%Z%%M% %I% %E% SMI"
+#include_next <sys/refcount.h>
#include <sys/list.h>
#include <sys/zfs_context.h>
@@ -70,7 +71,7 @@ int64_t refcount_remove(refcount_t *rc, void *holder_tag);
int64_t refcount_add_many(refcount_t *rc, uint64_t number, void *holder_tag);
int64_t refcount_remove_many(refcount_t *rc, uint64_t number, void *holder_tag);
-void refcount_init(void);
+void refcount_sysinit(void);
void refcount_fini(void);
#else /* DEBUG */
@@ -91,7 +92,7 @@ typedef struct refcount {
#define refcount_remove_many(rc, number, holder) \
atomic_add_64_nv(&(rc)->rc_count, -number)
-#define refcount_init()
+#define refcount_sysinit()
#define refcount_fini()
#endif /* DEBUG */
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c
index c9424be1995f..8699922ccf09 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c
@@ -1824,3 +1824,4 @@ static moduledata_t zfs_mod = {
0
};
DECLARE_MODULE(zfsctrl, zfs_mod, SI_SUB_VFS, SI_ORDER_ANY);
+MODULE_DEPEND(zfsctrl, opensolaris, 1, 1, 1);
diff --git a/sys/cddl/contrib/opensolaris/uts/common/rpc/xdr.c b/sys/cddl/contrib/opensolaris/uts/common/rpc/xdr.c
index 65b73b6181a9..e9346681774c 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/rpc/xdr.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/rpc/xdr.c
@@ -54,11 +54,13 @@
#pragma weak xdr_int64_t = xdr_longlong_t
#pragma weak xdr_uint64_t = xdr_u_longlong_t
+#if defined(sun)
#if !defined(_BIG_ENDIAN) && !defined(_LITTLE_ENDIAN)
#error "Exactly one of _BIG_ENDIAN or _LITTLE_ENDIAN must be defined"
#elif defined(_BIG_ENDIAN) && defined(_LITTLE_ENDIAN)
#error "Only one of _BIG_ENDIAN or _LITTLE_ENDIAN may be defined"
#endif
+#endif
/*
* constants specific to the xdr "protocol"
@@ -174,12 +176,12 @@ bool_t
xdr_longlong_t(XDR *xdrs, longlong_t *hp)
{
if (xdrs->x_op == XDR_ENCODE) {
-#if defined(_LITTLE_ENDIAN)
+#if BYTE_ORDER == _LITTLE_ENDIAN
if (XDR_PUTINT32(xdrs, (int32_t *)((char *)hp +
BYTES_PER_XDR_UNIT)) == TRUE) {
return (XDR_PUTINT32(xdrs, (int32_t *)hp));
}
-#elif defined(_BIG_ENDIAN)
+#else
if (XDR_PUTINT32(xdrs, (int32_t *)hp) == TRUE) {
return (XDR_PUTINT32(xdrs, (int32_t *)((char *)hp +
BYTES_PER_XDR_UNIT)));
@@ -189,12 +191,12 @@ xdr_longlong_t(XDR *xdrs, longlong_t *hp)
}
if (xdrs->x_op == XDR_DECODE) {
-#if defined(_LITTLE_ENDIAN)
+#if BYTE_ORDER == _LITTLE_ENDIAN
if (XDR_GETINT32(xdrs, (int32_t *)((char *)hp +
BYTES_PER_XDR_UNIT)) == TRUE) {
return (XDR_GETINT32(xdrs, (int32_t *)hp));
}
-#elif defined(_BIG_ENDIAN)
+#else
if (XDR_GETINT32(xdrs, (int32_t *)hp) == TRUE) {
return (XDR_GETINT32(xdrs, (int32_t *)((char *)hp +
BYTES_PER_XDR_UNIT)));
@@ -213,12 +215,12 @@ xdr_u_longlong_t(XDR *xdrs, u_longlong_t *hp)
{
if (xdrs->x_op == XDR_ENCODE) {
-#if defined(_LITTLE_ENDIAN)
+#if BYTE_ORDER == _LITTLE_ENDIAN
if (XDR_PUTINT32(xdrs, (int32_t *)((char *)hp +
BYTES_PER_XDR_UNIT)) == TRUE) {
return (XDR_PUTINT32(xdrs, (int32_t *)hp));
}
-#elif defined(_BIG_ENDIAN)
+#else
if (XDR_PUTINT32(xdrs, (int32_t *)hp) == TRUE) {
return (XDR_PUTINT32(xdrs, (int32_t *)((char *)hp +
BYTES_PER_XDR_UNIT)));
@@ -228,12 +230,12 @@ xdr_u_longlong_t(XDR *xdrs, u_longlong_t *hp)
}
if (xdrs->x_op == XDR_DECODE) {
-#if defined(_LITTLE_ENDIAN)
+#if BYTE_ORDER == _LITTLE_ENDIAN
if (XDR_GETINT32(xdrs, (int32_t *)((char *)hp +
BYTES_PER_XDR_UNIT)) == TRUE) {
return (XDR_GETINT32(xdrs, (int32_t *)hp));
}
-#elif defined(_BIG_ENDIAN)
+#else
if (XDR_GETINT32(xdrs, (int32_t *)hp) == TRUE) {
return (XDR_GETINT32(xdrs, (int32_t *)((char *)hp +
BYTES_PER_XDR_UNIT)));
diff --git a/sys/cddl/contrib/opensolaris/uts/common/sys/cmn_err.h b/sys/cddl/contrib/opensolaris/uts/common/sys/cmn_err.h
new file mode 100644
index 000000000000..e710d8e5c30b
--- /dev/null
+++ b/sys/cddl/contrib/opensolaris/uts/common/sys/cmn_err.h
@@ -0,0 +1,128 @@
+/*
+ * 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 (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _SYS_CMN_ERR_H
+#define _SYS_CMN_ERR_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#if defined(_KERNEL) && !defined(_ASM)
+#include <sys/va_list.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Common error handling severity levels */
+
+#define CE_CONT 0 /* continuation */
+#define CE_NOTE 1 /* notice */
+#define CE_WARN 2 /* warning */
+#define CE_PANIC 3 /* panic */
+#define CE_IGNORE 4 /* print nothing */
+
+#ifndef _ASM
+
+#ifdef _KERNEL
+
+/*PRINTFLIKE2*/
+extern void cmn_err(int, const char *, ...)
+ __KPRINTFLIKE(2);
+#pragma rarely_called(cmn_err)
+
+extern void vzcmn_err(zoneid_t, int, const char *, __va_list)
+ __KVPRINTFLIKE(3);
+#pragma rarely_called(vzcmn_err)
+
+extern void vcmn_err(int, const char *, __va_list)
+ __KVPRINTFLIKE(2);
+#pragma rarely_called(vcmn_err)
+
+/*PRINTFLIKE3*/
+extern void zcmn_err(zoneid_t, int, const char *, ...)
+ __KPRINTFLIKE(3);
+#pragma rarely_called(zcmn_err)
+
+/*PRINTFLIKE1*/
+extern void printf(const char *, ...)
+ __KPRINTFLIKE(1);
+#pragma rarely_called(printf)
+
+extern void vzprintf(zoneid_t, const char *, __va_list)
+ __KVPRINTFLIKE(2);
+#pragma rarely_called(vzprintf)
+
+/*PRINTFLIKE2*/
+extern void zprintf(zoneid_t, const char *, ...)
+ __KPRINTFLIKE(2);
+#pragma rarely_called(zprintf)
+
+extern void vprintf(const char *, __va_list)
+ __KVPRINTFLIKE(1);
+#pragma rarely_called(vprintf)
+
+/*PRINTFLIKE1*/
+extern void uprintf(const char *, ...)
+ __KPRINTFLIKE(1);
+#pragma rarely_called(uprintf)
+
+extern void vuprintf(const char *, __va_list)
+ __KVPRINTFLIKE(1);
+#pragma rarely_called(vuprintf)
+
+/*PRINTFLIKE3*/
+extern size_t snprintf(char *, size_t, const char *, ...)
+ __KPRINTFLIKE(3);
+extern size_t vsnprintf(char *, size_t, const char *, __va_list)
+ __KVPRINTFLIKE(3);
+/*PRINTFLIKE2*/
+extern char *sprintf(char *, const char *, ...)
+ __KPRINTFLIKE(2);
+extern char *vsprintf(char *, const char *, __va_list)
+ __KVPRINTFLIKE(2);
+
+/*PRINTFLIKE1*/
+extern void panic(const char *, ...)
+ __KPRINTFLIKE(1) __NORETURN;
+#pragma rarely_called(panic)
+
+extern void vpanic(const char *, __va_list)
+ __KVPRINTFLIKE(1) __NORETURN;
+#pragma rarely_called(vpanic)
+
+#endif /* _KERNEL */
+#endif /* !_ASM */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_CMN_ERR_H */
diff --git a/sys/cddl/contrib/opensolaris/uts/common/sys/cpupart.h b/sys/cddl/contrib/opensolaris/uts/common/sys/cpupart.h
new file mode 100644
index 000000000000..b9e0da4e1993
--- /dev/null
+++ b/sys/cddl/contrib/opensolaris/uts/common/sys/cpupart.h
@@ -0,0 +1,162 @@
+/*
+ * 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.
+ */
+
+#ifndef _SYS_CPUPART_H
+#define _SYS_CPUPART_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <sys/processor.h>
+#include <sys/cpuvar.h>
+#include <sys/disp.h>
+#include <sys/pset.h>
+#include <sys/lgrp.h>
+#include <sys/lgrp_user.h>
+#include <sys/pg.h>
+#include <sys/bitset.h>
+#include <sys/time.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef _KERNEL
+
+typedef int cpupartid_t;
+
+/*
+ * Special partition id.
+ */
+#define CP_DEFAULT 0
+
+/*
+ * Flags for cpupart_list()
+ */
+#define CP_ALL 0 /* return all cpu partitions */
+#define CP_NONEMPTY 1 /* return only non-empty ones */
+
+#if defined(_MACHDEP)
+struct mach_cpupart {
+ cpuset_t mc_haltset;
+};
+
+extern struct mach_cpupart cp_default_mach;
+#else
+struct mach_cpupart;
+#endif
+
+typedef struct cpupart {
+ disp_t cp_kp_queue; /* partition-wide kpreempt queue */
+ cpupartid_t cp_id; /* partition ID */
+ int cp_ncpus; /* number of online processors */
+ struct cpupart *cp_next; /* next partition in list */
+ struct cpupart *cp_prev; /* previous partition in list */
+ struct cpu *cp_cpulist; /* processor list */
+ struct kstat *cp_kstat; /* per-partition statistics */
+
+ /*
+ * cp_nrunnable and cp_nrunning are used to calculate load average.
+ */
+ uint_t cp_nrunnable; /* current # of runnable threads */
+ uint_t cp_nrunning; /* current # of running threads */
+
+ /*
+ * cp_updates, cp_nrunnable_cum, cp_nwaiting_cum, and cp_hp_avenrun
+ * are used to generate kstat information on an as-needed basis.
+ */
+ uint64_t cp_updates; /* number of statistics updates */
+ uint64_t cp_nrunnable_cum; /* cum. # of runnable threads */
+ uint64_t cp_nwaiting_cum; /* cum. # of waiting threads */
+
+ struct loadavg_s cp_loadavg; /* cpupart loadavg */
+
+ klgrpset_t cp_lgrpset; /* set of lgroups on which this */
+ /* partition has cpus */
+ lpl_t *cp_lgrploads; /* table of load averages for this */
+ /* partition, indexed by lgrp ID */
+ int cp_nlgrploads; /* size of cp_lgrploads table */
+ uint64_t cp_hp_avenrun[3]; /* high-precision load average */
+ uint_t cp_attr; /* bitmask of attributes */
+ lgrp_gen_t cp_gen; /* generation number */
+ lgrp_id_t cp_lgrp_hint; /* last home lgroup chosen */
+ bitset_t cp_cmt_pgs; /* CMT PGs represented */
+
+ struct mach_cpupart *cp_mach; /* mach-specific */
+} cpupart_t;
+
+typedef struct cpupart_kstat {
+ kstat_named_t cpk_updates; /* number of updates */
+ kstat_named_t cpk_runnable; /* cum # of runnable threads */
+ kstat_named_t cpk_waiting; /* cum # waiting for I/O */
+ kstat_named_t cpk_ncpus; /* current # of CPUs */
+ kstat_named_t cpk_avenrun_1min; /* 1-minute load average */
+ kstat_named_t cpk_avenrun_5min; /* 5-minute load average */
+ kstat_named_t cpk_avenrun_15min; /* 15-minute load average */
+} cpupart_kstat_t;
+
+/*
+ * Macro to obtain the maximum run priority for the global queue associated
+ * with given cpu partition.
+ */
+#define CP_MAXRUNPRI(cp) ((cp)->cp_kp_queue.disp_maxrunpri)
+
+/*
+ * This macro is used to determine if the given thread must surrender
+ * CPU to higher priority runnable threads on one of its dispatch queues.
+ * This should really be defined in <sys/disp.h> but it is not because
+ * including <sys/cpupart.h> there would cause recursive includes.
+ */
+#define DISP_MUST_SURRENDER(t) \
+ ((DISP_MAXRUNPRI(t) > DISP_PRIO(t)) || \
+ (CP_MAXRUNPRI(t->t_cpupart) > DISP_PRIO(t)))
+
+extern cpupart_t cp_default;
+extern cpupart_t *cp_list_head;
+extern uint_t cp_numparts;
+extern uint_t cp_numparts_nonempty;
+
+extern void cpupart_initialize_default();
+extern cpupart_t *cpupart_find(psetid_t);
+extern int cpupart_create(psetid_t *);
+extern int cpupart_destroy(psetid_t);
+extern psetid_t cpupart_query_cpu(cpu_t *);
+extern int cpupart_attach_cpu(psetid_t, cpu_t *, int);
+extern int cpupart_get_cpus(psetid_t *, processorid_t *, uint_t *);
+extern int cpupart_bind_thread(kthread_id_t, psetid_t, int, void *,
+ void *);
+extern void cpupart_kpqalloc(pri_t);
+extern int cpupart_get_loadavg(psetid_t, int *, int);
+extern uint_t cpupart_list(psetid_t *, uint_t, int);
+extern int cpupart_setattr(psetid_t, uint_t);
+extern int cpupart_getattr(psetid_t, uint_t *);
+
+#endif /* _KERNEL */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_CPUPART_H */
diff --git a/sys/cddl/contrib/opensolaris/uts/common/sys/cpuvar.h b/sys/cddl/contrib/opensolaris/uts/common/sys/cpuvar.h
new file mode 100644
index 000000000000..cd0d0278662c
--- /dev/null
+++ b/sys/cddl/contrib/opensolaris/uts/common/sys/cpuvar.h
@@ -0,0 +1,737 @@
+/*
+ * 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 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _SYS_CPUVAR_H
+#define _SYS_CPUVAR_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/thread.h>
+#include <sys/sysinfo.h> /* has cpu_stat_t definition */
+#include <sys/disp.h>
+#include <sys/processor.h>
+
+#if (defined(_KERNEL) || defined(_KMEMUSER)) && defined(_MACHDEP)
+#include <sys/machcpuvar.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/file.h>
+#include <sys/bitmap.h>
+#include <sys/rwlock.h>
+#include <sys/msacct.h>
+#if defined(__GNUC__) && defined(_ASM_INLINES) && defined(_KERNEL) && \
+ (defined(__i386) || defined(__amd64))
+#include <asm/cpuvar.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct squeue_set_s;
+
+#define CPU_CACHE_COHERENCE_SIZE 64
+#define S_LOADAVG_SZ 11
+#define S_MOVAVG_SZ 10
+
+struct loadavg_s {
+ int lg_cur; /* current loadavg entry */
+ unsigned int lg_len; /* number entries recorded */
+ hrtime_t lg_total; /* used to temporarily hold load totals */
+ hrtime_t lg_loads[S_LOADAVG_SZ]; /* table of recorded entries */
+};
+
+/*
+ * For fast event tracing.
+ */
+struct ftrace_record;
+typedef struct ftrace_data {
+ int ftd_state; /* ftrace flags */
+ kmutex_t ftd_unused; /* ftrace buffer lock, unused */
+ struct ftrace_record *ftd_cur; /* current record */
+ struct ftrace_record *ftd_first; /* first record */
+ struct ftrace_record *ftd_last; /* last record */
+} ftrace_data_t;
+
+struct cyc_cpu;
+struct nvlist;
+
+/*
+ * Per-CPU data.
+ *
+ * Be careful adding new members: if they are not the same in all modules (e.g.
+ * change size depending on a #define), CTF uniquification can fail to work
+ * properly. Furthermore, this is transitive in that it applies recursively to
+ * all types pointed to by cpu_t.
+ */
+typedef struct cpu {
+ processorid_t cpu_id; /* CPU number */
+ processorid_t cpu_seqid; /* sequential CPU id (0..ncpus-1) */
+ volatile cpu_flag_t cpu_flags; /* flags indicating CPU state */
+ struct cpu *cpu_self; /* pointer to itself */
+ kthread_t *cpu_thread; /* current thread */
+ kthread_t *cpu_idle_thread; /* idle thread for this CPU */
+ kthread_t *cpu_pause_thread; /* pause thread for this CPU */
+ klwp_id_t cpu_lwp; /* current lwp (if any) */
+ klwp_id_t cpu_fpowner; /* currently loaded fpu owner */
+ struct cpupart *cpu_part; /* partition with this CPU */
+ struct lgrp_ld *cpu_lpl; /* pointer to this cpu's load */
+ int cpu_cache_offset; /* see kmem.c for details */
+
+ /*
+ * Links to other CPUs. It is safe to walk these lists if
+ * one of the following is true:
+ * - cpu_lock held
+ * - preemption disabled via kpreempt_disable
+ * - PIL >= DISP_LEVEL
+ * - acting thread is an interrupt thread
+ * - all other CPUs are paused
+ */
+ struct cpu *cpu_next; /* next existing CPU */
+ struct cpu *cpu_prev; /* prev existing CPU */
+ struct cpu *cpu_next_onln; /* next online (enabled) CPU */
+ struct cpu *cpu_prev_onln; /* prev online (enabled) CPU */
+ struct cpu *cpu_next_part; /* next CPU in partition */
+ struct cpu *cpu_prev_part; /* prev CPU in partition */
+ struct cpu *cpu_next_lgrp; /* next CPU in latency group */
+ struct cpu *cpu_prev_lgrp; /* prev CPU in latency group */
+ struct cpu *cpu_next_lpl; /* next CPU in lgrp partition */
+ struct cpu *cpu_prev_lpl;
+
+ struct cpu_pg *cpu_pg; /* cpu's processor groups */
+
+ void *cpu_reserved[4]; /* reserved for future use */
+
+ /*
+ * Scheduling variables.
+ */
+ disp_t *cpu_disp; /* dispatch queue data */
+ /*
+ * Note that cpu_disp is set before the CPU is added to the system
+ * and is never modified. Hence, no additional locking is needed
+ * beyond what's necessary to access the cpu_t structure.
+ */
+ char cpu_runrun; /* scheduling flag - set to preempt */
+ char cpu_kprunrun; /* force kernel preemption */
+ pri_t cpu_chosen_level; /* priority at which cpu */
+ /* was chosen for scheduling */
+ kthread_t *cpu_dispthread; /* thread selected for dispatch */
+ disp_lock_t cpu_thread_lock; /* dispatcher lock on current thread */
+ uint8_t cpu_disp_flags; /* flags used by dispatcher */
+ /*
+ * The following field is updated when ever the cpu_dispthread
+ * changes. Also in places, where the current thread(cpu_dispthread)
+ * priority changes. This is used in disp_lowpri_cpu()
+ */
+ pri_t cpu_dispatch_pri; /* priority of cpu_dispthread */
+ clock_t cpu_last_swtch; /* last time switched to new thread */
+
+ /*
+ * Interrupt data.
+ */
+ caddr_t cpu_intr_stack; /* interrupt stack */
+ kthread_t *cpu_intr_thread; /* interrupt thread list */
+ uint_t cpu_intr_actv; /* interrupt levels active (bitmask) */
+ int cpu_base_spl; /* priority for highest rupt active */
+
+ /*
+ * Statistics.
+ */
+ cpu_stats_t cpu_stats; /* per-CPU statistics */
+ struct kstat *cpu_info_kstat; /* kstat for cpu info */
+
+ uintptr_t cpu_profile_pc; /* kernel PC in profile interrupt */
+ uintptr_t cpu_profile_upc; /* user PC in profile interrupt */
+ uintptr_t cpu_profile_pil; /* PIL when profile interrupted */
+
+ ftrace_data_t cpu_ftrace; /* per cpu ftrace data */
+
+ clock_t cpu_deadman_lbolt; /* used by deadman() */
+ uint_t cpu_deadman_countdown; /* used by deadman() */
+
+ kmutex_t cpu_cpc_ctxlock; /* protects context for idle thread */
+ kcpc_ctx_t *cpu_cpc_ctx; /* performance counter context */
+
+ /*
+ * Configuration information for the processor_info system call.
+ */
+ processor_info_t cpu_type_info; /* config info */
+ time_t cpu_state_begin; /* when CPU entered current state */
+ char cpu_cpr_flags; /* CPR related info */
+ struct cyc_cpu *cpu_cyclic; /* per cpu cyclic subsystem data */
+ struct squeue_set_s *cpu_squeue_set; /* per cpu squeue set */
+ struct nvlist *cpu_props; /* pool-related properties */
+
+ krwlock_t cpu_ft_lock; /* DTrace: fasttrap lock */
+ uintptr_t cpu_dtrace_caller; /* DTrace: caller, if any */
+ hrtime_t cpu_dtrace_chillmark; /* DTrace: chill mark time */
+ hrtime_t cpu_dtrace_chilled; /* DTrace: total chill time */
+ volatile uint16_t cpu_mstate; /* cpu microstate */
+ volatile uint16_t cpu_mstate_gen; /* generation counter */
+ volatile hrtime_t cpu_mstate_start; /* cpu microstate start time */
+ volatile hrtime_t cpu_acct[NCMSTATES]; /* cpu microstate data */
+ hrtime_t cpu_intracct[NCMSTATES]; /* interrupt mstate data */
+ hrtime_t cpu_waitrq; /* cpu run-queue wait time */
+ struct loadavg_s cpu_loadavg; /* loadavg info for this cpu */
+
+ char *cpu_idstr; /* for printing and debugging */
+ char *cpu_brandstr; /* for printing */
+
+ /*
+ * Sum of all device interrupt weights that are currently directed at
+ * this cpu. Cleared at start of interrupt redistribution.
+ */
+ int32_t cpu_intr_weight;
+ void *cpu_vm_data;
+
+ struct cpu_physid *cpu_physid; /* physical associations */
+
+ uint64_t cpu_curr_clock; /* current clock freq in Hz */
+ char *cpu_supp_freqs; /* supported freqs in Hz */
+
+ /*
+ * Interrupt load factor used by dispatcher & softcall
+ */
+ hrtime_t cpu_intrlast; /* total interrupt time (nsec) */
+ int cpu_intrload; /* interrupt load factor (0-99%) */
+
+ /*
+ * New members must be added /before/ this member, as the CTF tools
+ * rely on this being the last field before cpu_m, so they can
+ * correctly calculate the offset when synthetically adding the cpu_m
+ * member in objects that do not have it. This fixup is required for
+ * uniquification to work correctly.
+ */
+ uintptr_t cpu_m_pad;
+
+#if (defined(_KERNEL) || defined(_KMEMUSER)) && defined(_MACHDEP)
+ struct machcpu cpu_m; /* per architecture info */
+#endif
+} cpu_t;
+
+/*
+ * The cpu_core structure consists of per-CPU state available in any context.
+ * On some architectures, this may mean that the page(s) containing the
+ * NCPU-sized array of cpu_core structures must be locked in the TLB -- it
+ * is up to the platform to assure that this is performed properly. Note that
+ * the structure is sized to avoid false sharing.
+ */
+#define CPUC_SIZE (sizeof (uint16_t) + sizeof (uintptr_t) + \
+ sizeof (kmutex_t))
+#define CPUC_PADSIZE CPU_CACHE_COHERENCE_SIZE - CPUC_SIZE
+
+typedef struct cpu_core {
+ uint16_t cpuc_dtrace_flags; /* DTrace flags */
+ uint8_t cpuc_pad[CPUC_PADSIZE]; /* padding */
+ uintptr_t cpuc_dtrace_illval; /* DTrace illegal value */
+ kmutex_t cpuc_pid_lock; /* DTrace pid provider lock */
+} cpu_core_t;
+
+#ifdef _KERNEL
+extern cpu_core_t cpu_core[];
+#endif /* _KERNEL */
+
+/*
+ * CPU_ON_INTR() macro. Returns non-zero if currently on interrupt stack.
+ * Note that this isn't a test for a high PIL. For example, cpu_intr_actv
+ * does not get updated when we go through sys_trap from TL>0 at high PIL.
+ * getpil() should be used instead to check for PIL levels.
+ */
+#define CPU_ON_INTR(cpup) ((cpup)->cpu_intr_actv >> (LOCK_LEVEL + 1))
+
+#if defined(_KERNEL) || defined(_KMEMUSER)
+
+#define INTR_STACK_SIZE MAX(DEFAULTSTKSZ, PAGESIZE)
+
+/* MEMBERS PROTECTED BY "atomicity": cpu_flags */
+
+/*
+ * Flags in the CPU structure.
+ *
+ * These are protected by cpu_lock (except during creation).
+ *
+ * Offlined-CPUs have three stages of being offline:
+ *
+ * CPU_ENABLE indicates that the CPU is participating in I/O interrupts
+ * that can be directed at a number of different CPUs. If CPU_ENABLE
+ * is off, the CPU will not be given interrupts that can be sent elsewhere,
+ * but will still get interrupts from devices associated with that CPU only,
+ * and from other CPUs.
+ *
+ * CPU_OFFLINE indicates that the dispatcher should not allow any threads
+ * other than interrupt threads to run on that CPU. A CPU will not have
+ * CPU_OFFLINE set if there are any bound threads (besides interrupts).
+ *
+ * CPU_QUIESCED is set if p_offline was able to completely turn idle the
+ * CPU and it will not have to run interrupt threads. In this case it'll
+ * stay in the idle loop until CPU_QUIESCED is turned off.
+ *
+ * CPU_FROZEN is used only by CPR to mark CPUs that have been successfully
+ * suspended (in the suspend path), or have yet to be resumed (in the resume
+ * case).
+ *
+ * On some platforms CPUs can be individually powered off.
+ * The following flags are set for powered off CPUs: CPU_QUIESCED,
+ * CPU_OFFLINE, and CPU_POWEROFF. The following flags are cleared:
+ * CPU_RUNNING, CPU_READY, CPU_EXISTS, and CPU_ENABLE.
+ */
+#define CPU_RUNNING 0x001 /* CPU running */
+#define CPU_READY 0x002 /* CPU ready for cross-calls */
+#define CPU_QUIESCED 0x004 /* CPU will stay in idle */
+#define CPU_EXISTS 0x008 /* CPU is configured */
+#define CPU_ENABLE 0x010 /* CPU enabled for interrupts */
+#define CPU_OFFLINE 0x020 /* CPU offline via p_online */
+#define CPU_POWEROFF 0x040 /* CPU is powered off */
+#define CPU_FROZEN 0x080 /* CPU is frozen via CPR suspend */
+#define CPU_SPARE 0x100 /* CPU offline available for use */
+#define CPU_FAULTED 0x200 /* CPU offline diagnosed faulty */
+
+#define FMT_CPU_FLAGS \
+ "\20\12fault\11spare\10frozen" \
+ "\7poweroff\6offline\5enable\4exist\3quiesced\2ready\1run"
+
+#define CPU_ACTIVE(cpu) (((cpu)->cpu_flags & CPU_OFFLINE) == 0)
+
+/*
+ * Flags for cpu_offline(), cpu_faulted(), and cpu_spare().
+ */
+#define CPU_FORCED 0x0001 /* Force CPU offline */
+
+/*
+ * DTrace flags.
+ */
+#define CPU_DTRACE_NOFAULT 0x0001 /* Don't fault */
+#define CPU_DTRACE_DROP 0x0002 /* Drop this ECB */
+#define CPU_DTRACE_BADADDR 0x0004 /* DTrace fault: bad address */
+#define CPU_DTRACE_BADALIGN 0x0008 /* DTrace fault: bad alignment */
+#define CPU_DTRACE_DIVZERO 0x0010 /* DTrace fault: divide by zero */
+#define CPU_DTRACE_ILLOP 0x0020 /* DTrace fault: illegal operation */
+#define CPU_DTRACE_NOSCRATCH 0x0040 /* DTrace fault: out of scratch */
+#define CPU_DTRACE_KPRIV 0x0080 /* DTrace fault: bad kernel access */
+#define CPU_DTRACE_UPRIV 0x0100 /* DTrace fault: bad user access */
+#define CPU_DTRACE_TUPOFLOW 0x0200 /* DTrace fault: tuple stack overflow */
+#if defined(__sparc)
+#define CPU_DTRACE_FAKERESTORE 0x0400 /* pid provider hint to getreg */
+#endif
+#define CPU_DTRACE_ENTRY 0x0800 /* pid provider hint to ustack() */
+#define CPU_DTRACE_BADSTACK 0x1000 /* DTrace fault: bad stack */
+
+#define CPU_DTRACE_FAULT (CPU_DTRACE_BADADDR | CPU_DTRACE_BADALIGN | \
+ CPU_DTRACE_DIVZERO | CPU_DTRACE_ILLOP | \
+ CPU_DTRACE_NOSCRATCH | CPU_DTRACE_KPRIV | \
+ CPU_DTRACE_UPRIV | CPU_DTRACE_TUPOFLOW | \
+ CPU_DTRACE_BADSTACK)
+#define CPU_DTRACE_ERROR (CPU_DTRACE_FAULT | CPU_DTRACE_DROP)
+
+/*
+ * Dispatcher flags
+ * These flags must be changed only by the current CPU.
+ */
+#define CPU_DISP_DONTSTEAL 0x01 /* CPU undergoing context swtch */
+#define CPU_DISP_HALTED 0x02 /* CPU halted waiting for interrupt */
+
+
+#endif /* _KERNEL || _KMEMUSER */
+
+#if (defined(_KERNEL) || defined(_KMEMUSER)) && defined(_MACHDEP)
+
+/*
+ * Macros for manipulating sets of CPUs as a bitmap. Note that this
+ * bitmap may vary in size depending on the maximum CPU id a specific
+ * platform supports. This may be different than the number of CPUs
+ * the platform supports, since CPU ids can be sparse. We define two
+ * sets of macros; one for platforms where the maximum CPU id is less
+ * than the number of bits in a single word (32 in a 32-bit kernel,
+ * 64 in a 64-bit kernel), and one for platforms that require bitmaps
+ * of more than one word.
+ */
+
+#define CPUSET_WORDS BT_BITOUL(NCPU)
+#define CPUSET_NOTINSET ((uint_t)-1)
+
+#if CPUSET_WORDS > 1
+
+typedef struct cpuset {
+ ulong_t cpub[CPUSET_WORDS];
+} cpuset_t;
+
+/*
+ * Private functions for manipulating cpusets that do not fit in a
+ * single word. These should not be used directly; instead the
+ * CPUSET_* macros should be used so the code will be portable
+ * across different definitions of NCPU.
+ */
+extern void cpuset_all(cpuset_t *);
+extern void cpuset_all_but(cpuset_t *, uint_t);
+extern int cpuset_isnull(cpuset_t *);
+extern int cpuset_cmp(cpuset_t *, cpuset_t *);
+extern void cpuset_only(cpuset_t *, uint_t);
+extern uint_t cpuset_find(cpuset_t *);
+extern void cpuset_bounds(cpuset_t *, uint_t *, uint_t *);
+
+#define CPUSET_ALL(set) cpuset_all(&(set))
+#define CPUSET_ALL_BUT(set, cpu) cpuset_all_but(&(set), cpu)
+#define CPUSET_ONLY(set, cpu) cpuset_only(&(set), cpu)
+#define CPU_IN_SET(set, cpu) BT_TEST((set).cpub, cpu)
+#define CPUSET_ADD(set, cpu) BT_SET((set).cpub, cpu)
+#define CPUSET_DEL(set, cpu) BT_CLEAR((set).cpub, cpu)
+#define CPUSET_ISNULL(set) cpuset_isnull(&(set))
+#define CPUSET_ISEQUAL(set1, set2) cpuset_cmp(&(set1), &(set2))
+
+/*
+ * Find one CPU in the cpuset.
+ * Sets "cpu" to the id of the found CPU, or CPUSET_NOTINSET if no cpu
+ * could be found. (i.e. empty set)
+ */
+#define CPUSET_FIND(set, cpu) { \
+ cpu = cpuset_find(&(set)); \
+}
+
+/*
+ * Determine the smallest and largest CPU id in the set. Returns
+ * CPUSET_NOTINSET in smallest and largest when set is empty.
+ */
+#define CPUSET_BOUNDS(set, smallest, largest) { \
+ cpuset_bounds(&(set), &(smallest), &(largest)); \
+}
+
+/*
+ * Atomic cpuset operations
+ * These are safe to use for concurrent cpuset manipulations.
+ * "xdel" and "xadd" are exclusive operations, that set "result" to "0"
+ * if the add or del was successful, or "-1" if not successful.
+ * (e.g. attempting to add a cpu to a cpuset that's already there, or
+ * deleting a cpu that's not in the cpuset)
+ */
+
+#define CPUSET_ATOMIC_DEL(set, cpu) BT_ATOMIC_CLEAR((set).cpub, (cpu))
+#define CPUSET_ATOMIC_ADD(set, cpu) BT_ATOMIC_SET((set).cpub, (cpu))
+
+#define CPUSET_ATOMIC_XADD(set, cpu, result) \
+ BT_ATOMIC_SET_EXCL((set).cpub, cpu, result)
+
+#define CPUSET_ATOMIC_XDEL(set, cpu, result) \
+ BT_ATOMIC_CLEAR_EXCL((set).cpub, cpu, result)
+
+
+#define CPUSET_OR(set1, set2) { \
+ int _i; \
+ for (_i = 0; _i < CPUSET_WORDS; _i++) \
+ (set1).cpub[_i] |= (set2).cpub[_i]; \
+}
+
+#define CPUSET_XOR(set1, set2) { \
+ int _i; \
+ for (_i = 0; _i < CPUSET_WORDS; _i++) \
+ (set1).cpub[_i] ^= (set2).cpub[_i]; \
+}
+
+#define CPUSET_AND(set1, set2) { \
+ int _i; \
+ for (_i = 0; _i < CPUSET_WORDS; _i++) \
+ (set1).cpub[_i] &= (set2).cpub[_i]; \
+}
+
+#define CPUSET_ZERO(set) { \
+ int _i; \
+ for (_i = 0; _i < CPUSET_WORDS; _i++) \
+ (set).cpub[_i] = 0; \
+}
+
+#elif CPUSET_WORDS == 1
+
+typedef ulong_t cpuset_t; /* a set of CPUs */
+
+#define CPUSET(cpu) (1UL << (cpu))
+
+#define CPUSET_ALL(set) ((void)((set) = ~0UL))
+#define CPUSET_ALL_BUT(set, cpu) ((void)((set) = ~CPUSET(cpu)))
+#define CPUSET_ONLY(set, cpu) ((void)((set) = CPUSET(cpu)))
+#define CPU_IN_SET(set, cpu) ((set) & CPUSET(cpu))
+#define CPUSET_ADD(set, cpu) ((void)((set) |= CPUSET(cpu)))
+#define CPUSET_DEL(set, cpu) ((void)((set) &= ~CPUSET(cpu)))
+#define CPUSET_ISNULL(set) ((set) == 0)
+#define CPUSET_ISEQUAL(set1, set2) ((set1) == (set2))
+#define CPUSET_OR(set1, set2) ((void)((set1) |= (set2)))
+#define CPUSET_XOR(set1, set2) ((void)((set1) ^= (set2)))
+#define CPUSET_AND(set1, set2) ((void)((set1) &= (set2)))
+#define CPUSET_ZERO(set) ((void)((set) = 0))
+
+#define CPUSET_FIND(set, cpu) { \
+ cpu = (uint_t)(lowbit(set) - 1); \
+}
+
+#define CPUSET_BOUNDS(set, smallest, largest) { \
+ smallest = (uint_t)(lowbit(set) - 1); \
+ largest = (uint_t)(highbit(set) - 1); \
+}
+
+#define CPUSET_ATOMIC_DEL(set, cpu) atomic_and_long(&(set), ~CPUSET(cpu))
+#define CPUSET_ATOMIC_ADD(set, cpu) atomic_or_long(&(set), CPUSET(cpu))
+
+#define CPUSET_ATOMIC_XADD(set, cpu, result) \
+ { result = atomic_set_long_excl(&(set), (cpu)); }
+
+#define CPUSET_ATOMIC_XDEL(set, cpu, result) \
+ { result = atomic_clear_long_excl(&(set), (cpu)); }
+
+#else /* CPUSET_WORDS <= 0 */
+
+#error NCPU is undefined or invalid
+
+#endif /* CPUSET_WORDS */
+
+extern cpuset_t cpu_seqid_inuse;
+
+#endif /* (_KERNEL || _KMEMUSER) && _MACHDEP */
+
+#define CPU_CPR_OFFLINE 0x0
+#define CPU_CPR_ONLINE 0x1
+#define CPU_CPR_IS_OFFLINE(cpu) (((cpu)->cpu_cpr_flags & CPU_CPR_ONLINE) == 0)
+#define CPU_CPR_IS_ONLINE(cpu) ((cpu)->cpu_cpr_flags & CPU_CPR_ONLINE)
+#define CPU_SET_CPR_FLAGS(cpu, flag) ((cpu)->cpu_cpr_flags |= flag)
+
+#if defined(_KERNEL) || defined(_KMEMUSER)
+
+extern struct cpu *cpu[]; /* indexed by CPU number */
+extern cpu_t *cpu_list; /* list of CPUs */
+extern cpu_t *cpu_active; /* list of active CPUs */
+extern int ncpus; /* number of CPUs present */
+extern int ncpus_online; /* number of CPUs not quiesced */
+extern int max_ncpus; /* max present before ncpus is known */
+extern int boot_max_ncpus; /* like max_ncpus but for real */
+extern processorid_t max_cpuid; /* maximum CPU number */
+extern struct cpu *cpu_inmotion; /* offline or partition move target */
+extern cpu_t *clock_cpu_list;
+
+#if defined(__i386) || defined(__amd64)
+extern struct cpu *curcpup(void);
+#define CPU (curcpup()) /* Pointer to current CPU */
+#else
+#define CPU (curthread->t_cpu) /* Pointer to current CPU */
+#endif
+
+/*
+ * CPU_CURRENT indicates to thread_affinity_set to use CPU->cpu_id
+ * as the target and to grab cpu_lock instead of requiring the caller
+ * to grab it.
+ */
+#define CPU_CURRENT -3
+
+/*
+ * Per-CPU statistics
+ *
+ * cpu_stats_t contains numerous system and VM-related statistics, in the form
+ * of gauges or monotonically-increasing event occurrence counts.
+ */
+
+#define CPU_STATS_ENTER_K() kpreempt_disable()
+#define CPU_STATS_EXIT_K() kpreempt_enable()
+
+#define CPU_STATS_ADD_K(class, stat, amount) \
+ { kpreempt_disable(); /* keep from switching CPUs */\
+ CPU_STATS_ADDQ(CPU, class, stat, amount); \
+ kpreempt_enable(); \
+ }
+
+#define CPU_STATS_ADDQ(cp, class, stat, amount) { \
+ extern void __dtrace_probe___cpu_##class##info_##stat(uint_t, \
+ uint64_t *, cpu_t *); \
+ uint64_t *stataddr = &((cp)->cpu_stats.class.stat); \
+ __dtrace_probe___cpu_##class##info_##stat((amount), \
+ stataddr, cp); \
+ *(stataddr) += (amount); \
+}
+
+#define CPU_STATS(cp, stat) \
+ ((cp)->cpu_stats.stat)
+
+#endif /* _KERNEL || _KMEMUSER */
+
+/*
+ * CPU support routines.
+ */
+#if defined(_KERNEL) && defined(__STDC__) /* not for genassym.c */
+
+struct zone;
+
+void cpu_list_init(cpu_t *);
+void cpu_add_unit(cpu_t *);
+void cpu_del_unit(int cpuid);
+void cpu_add_active(cpu_t *);
+void cpu_kstat_init(cpu_t *);
+void cpu_visibility_add(cpu_t *, struct zone *);
+void cpu_visibility_remove(cpu_t *, struct zone *);
+void cpu_visibility_configure(cpu_t *, struct zone *);
+void cpu_visibility_unconfigure(cpu_t *, struct zone *);
+void cpu_visibility_online(cpu_t *, struct zone *);
+void cpu_visibility_offline(cpu_t *, struct zone *);
+void cpu_create_intrstat(cpu_t *);
+void cpu_delete_intrstat(cpu_t *);
+int cpu_kstat_intrstat_update(kstat_t *, int);
+void cpu_intr_swtch_enter(kthread_t *);
+void cpu_intr_swtch_exit(kthread_t *);
+
+void mbox_lock_init(void); /* initialize cross-call locks */
+void mbox_init(int cpun); /* initialize cross-calls */
+void poke_cpu(int cpun); /* interrupt another CPU (to preempt) */
+
+/*
+ * values for safe_list. Pause state that CPUs are in.
+ */
+#define PAUSE_IDLE 0 /* normal state */
+#define PAUSE_READY 1 /* paused thread ready to spl */
+#define PAUSE_WAIT 2 /* paused thread is spl-ed high */
+#define PAUSE_DIE 3 /* tell pause thread to leave */
+#define PAUSE_DEAD 4 /* pause thread has left */
+
+void mach_cpu_pause(volatile char *);
+
+void pause_cpus(cpu_t *off_cp);
+void start_cpus(void);
+int cpus_paused(void);
+
+void cpu_pause_init(void);
+cpu_t *cpu_get(processorid_t cpun); /* get the CPU struct associated */
+
+int cpu_online(cpu_t *cp); /* take cpu online */
+int cpu_offline(cpu_t *cp, int flags); /* take cpu offline */
+int cpu_spare(cpu_t *cp, int flags); /* take cpu to spare */
+int cpu_faulted(cpu_t *cp, int flags); /* take cpu to faulted */
+int cpu_poweron(cpu_t *cp); /* take powered-off cpu to offline */
+int cpu_poweroff(cpu_t *cp); /* take offline cpu to powered-off */
+
+cpu_t *cpu_intr_next(cpu_t *cp); /* get next online CPU taking intrs */
+int cpu_intr_count(cpu_t *cp); /* count # of CPUs handling intrs */
+int cpu_intr_on(cpu_t *cp); /* CPU taking I/O interrupts? */
+void cpu_intr_enable(cpu_t *cp); /* enable I/O interrupts */
+int cpu_intr_disable(cpu_t *cp); /* disable I/O interrupts */
+void cpu_intr_alloc(cpu_t *cp, int n); /* allocate interrupt threads */
+
+/*
+ * Routines for checking CPU states.
+ */
+int cpu_is_online(cpu_t *); /* check if CPU is online */
+int cpu_is_nointr(cpu_t *); /* check if CPU can service intrs */
+int cpu_is_active(cpu_t *); /* check if CPU can run threads */
+int cpu_is_offline(cpu_t *); /* check if CPU is offline */
+int cpu_is_poweredoff(cpu_t *); /* check if CPU is powered off */
+
+int cpu_flagged_online(cpu_flag_t); /* flags show CPU is online */
+int cpu_flagged_nointr(cpu_flag_t); /* flags show CPU not handling intrs */
+int cpu_flagged_active(cpu_flag_t); /* flags show CPU scheduling threads */
+int cpu_flagged_offline(cpu_flag_t); /* flags show CPU is offline */
+int cpu_flagged_poweredoff(cpu_flag_t); /* flags show CPU is powered off */
+
+/*
+ * The processor_info(2) state of a CPU is a simplified representation suitable
+ * for use by an application program. Kernel subsystems should utilize the
+ * internal per-CPU state as given by the cpu_flags member of the cpu structure,
+ * as this information may include platform- or architecture-specific state
+ * critical to a subsystem's disposition of a particular CPU.
+ */
+void cpu_set_state(cpu_t *); /* record/timestamp current state */
+int cpu_get_state(cpu_t *); /* get current cpu state */
+const char *cpu_get_state_str(cpu_t *); /* get current cpu state as string */
+
+
+void cpu_set_supp_freqs(cpu_t *, const char *); /* set the CPU supported */
+ /* frequencies */
+
+int cpu_configure(int);
+int cpu_unconfigure(int);
+void cpu_destroy_bound_threads(cpu_t *cp);
+
+extern int cpu_bind_thread(kthread_t *tp, processorid_t bind,
+ processorid_t *obind, int *error);
+extern int cpu_unbind(processorid_t cpu_id, boolean_t force);
+extern void thread_affinity_set(kthread_t *t, int cpu_id);
+extern void thread_affinity_clear(kthread_t *t);
+extern void affinity_set(int cpu_id);
+extern void affinity_clear(void);
+extern void init_cpu_mstate(struct cpu *, int);
+extern void term_cpu_mstate(struct cpu *);
+extern void new_cpu_mstate(int, hrtime_t);
+extern void get_cpu_mstate(struct cpu *, hrtime_t *);
+extern void thread_nomigrate(void);
+extern void thread_allowmigrate(void);
+extern void weakbinding_stop(void);
+extern void weakbinding_start(void);
+
+/*
+ * The following routines affect the CPUs participation in interrupt processing,
+ * if that is applicable on the architecture. This only affects interrupts
+ * which aren't directed at the processor (not cross calls).
+ *
+ * cpu_disable_intr returns non-zero if interrupts were previously enabled.
+ */
+int cpu_disable_intr(struct cpu *cp); /* stop issuing interrupts to cpu */
+void cpu_enable_intr(struct cpu *cp); /* start issuing interrupts to cpu */
+
+/*
+ * The mutex cpu_lock protects cpu_flags for all CPUs, as well as the ncpus
+ * and ncpus_online counts.
+ */
+extern kmutex_t cpu_lock; /* lock protecting CPU data */
+
+typedef enum {
+ CPU_INIT,
+ CPU_CONFIG,
+ CPU_UNCONFIG,
+ CPU_ON,
+ CPU_OFF,
+ CPU_CPUPART_IN,
+ CPU_CPUPART_OUT
+} cpu_setup_t;
+
+typedef int cpu_setup_func_t(cpu_setup_t, int, void *);
+
+/*
+ * Routines used to register interest in cpu's being added to or removed
+ * from the system.
+ */
+extern void register_cpu_setup_func(cpu_setup_func_t *, void *);
+extern void unregister_cpu_setup_func(cpu_setup_func_t *, void *);
+extern void cpu_state_change_notify(int, cpu_setup_t);
+
+/*
+ * Create various strings that describe the given CPU for the
+ * processor_info system call and configuration-related kstats.
+ */
+#define CPU_IDSTRLEN 100
+
+extern void init_cpu_info(struct cpu *);
+extern void cpu_vm_data_init(struct cpu *);
+extern void cpu_vm_data_destroy(struct cpu *);
+
+#endif /* _KERNEL */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_CPUVAR_H */
diff --git a/sys/cddl/contrib/opensolaris/uts/common/sys/ctf.h b/sys/cddl/contrib/opensolaris/uts/common/sys/ctf.h
new file mode 100644
index 000000000000..2d1987be459f
--- /dev/null
+++ b/sys/cddl/contrib/opensolaris/uts/common/sys/ctf.h
@@ -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
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _CTF_H
+#define _CTF_H
+
+#if defined(sun)
+#pragma ident "%Z%%M% %I% %E% SMI"
+#endif
+
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * CTF - Compact ANSI-C Type Format
+ *
+ * This file format can be used to compactly represent the information needed
+ * by a debugger to interpret the ANSI-C types used by a given program.
+ * Traditionally, this kind of information is generated by the compiler when
+ * invoked with the -g flag and is stored in "stabs" strings or in the more
+ * modern DWARF format. CTF provides a representation of only the information
+ * that is relevant to debugging a complex, optimized C program such as the
+ * operating system kernel in a form that is significantly more compact than
+ * the equivalent stabs or DWARF representation. The format is data-model
+ * independent, so consumers do not need different code depending on whether
+ * they are 32-bit or 64-bit programs. CTF assumes that a standard ELF symbol
+ * table is available for use in the debugger, and uses the structure and data
+ * of the symbol table to avoid storing redundant information. The CTF data
+ * may be compressed on disk or in memory, indicated by a bit in the header.
+ * CTF may be interpreted in a raw disk file, or it may be stored in an ELF
+ * section, typically named .SUNW_ctf. Data structures are aligned so that
+ * a raw CTF file or CTF ELF section may be manipulated using mmap(2).
+ *
+ * The CTF file or section itself has the following structure:
+ *
+ * +--------+--------+---------+----------+-------+--------+
+ * | file | type | data | function | data | string |
+ * | header | labels | objects | info | types | table |
+ * +--------+--------+---------+----------+-------+--------+
+ *
+ * The file header stores a magic number and version information, encoding
+ * flags, and the byte offset of each of the sections relative to the end of the
+ * header itself. If the CTF data has been uniquified against another set of
+ * CTF data, a reference to that data also appears in the the header. This
+ * reference is the name of the label corresponding to the types uniquified
+ * against.
+ *
+ * Following the header is a list of labels, used to group the types included in
+ * the data types section. Each label is accompanied by a type ID i. A given
+ * label refers to the group of types whose IDs are in the range [0, i].
+ *
+ * Data object and function records are stored in the same order as they appear
+ * in the corresponding symbol table, except that symbols marked SHN_UNDEF are
+ * not stored and symbols that have no type data are padded out with zeroes.
+ * For each data object, the type ID (a small integer) is recorded. For each
+ * function, the type ID of the return type and argument types is recorded.
+ *
+ * The data types section is a list of variable size records that represent each
+ * type, in order by their ID. The types themselves form a directed graph,
+ * where each node may contain one or more outgoing edges to other type nodes,
+ * denoted by their ID.
+ *
+ * Strings are recorded as a string table ID (0 or 1) and a byte offset into the
+ * string table. String table 0 is the internal CTF string table. String table
+ * 1 is the external string table, which is the string table associated with the
+ * ELF symbol table for this object. CTF does not record any strings that are
+ * already in the symbol table, and the CTF string table does not contain any
+ * duplicated strings.
+ *
+ * If the CTF data has been merged with another parent CTF object, some outgoing
+ * edges may refer to type nodes that exist in another CTF object. The debugger
+ * and libctf library are responsible for connecting the appropriate objects
+ * together so that the full set of types can be explored and manipulated.
+ */
+
+#define CTF_MAX_TYPE 0xffff /* max type identifier value */
+#define CTF_MAX_NAME 0x7fffffff /* max offset into a string table */
+#define CTF_MAX_VLEN 0x3ff /* max struct, union, enum members or args */
+#define CTF_MAX_INTOFF 0xff /* max offset of intrinsic value in bits */
+#define CTF_MAX_INTBITS 0xffff /* max size of an intrinsic in bits */
+
+/* See ctf_type_t */
+#define CTF_MAX_SIZE 0xfffe /* max size of a type in bytes */
+#define CTF_LSIZE_SENT 0xffff /* sentinel for ctt_size */
+#define CTF_MAX_LSIZE UINT64_MAX
+
+typedef struct ctf_preamble {
+ ushort_t ctp_magic; /* magic number (CTF_MAGIC) */
+ uchar_t ctp_version; /* data format version number (CTF_VERSION) */
+ uchar_t ctp_flags; /* flags (see below) */
+} ctf_preamble_t;
+
+typedef struct ctf_header {
+ ctf_preamble_t cth_preamble;
+ uint_t cth_parlabel; /* ref to name of parent lbl uniq'd against */
+ uint_t cth_parname; /* ref to basename of parent */
+ uint_t cth_lbloff; /* offset of label section */
+ uint_t cth_objtoff; /* offset of object section */
+ uint_t cth_funcoff; /* offset of function section */
+ uint_t cth_typeoff; /* offset of type section */
+ uint_t cth_stroff; /* offset of string section */
+ uint_t cth_strlen; /* length of string section in bytes */
+} ctf_header_t;
+
+#define cth_magic cth_preamble.ctp_magic
+#define cth_version cth_preamble.ctp_version
+#define cth_flags cth_preamble.ctp_flags
+
+#ifdef CTF_OLD_VERSIONS
+
+typedef struct ctf_header_v1 {
+ ctf_preamble_t cth_preamble;
+ uint_t cth_objtoff;
+ uint_t cth_funcoff;
+ uint_t cth_typeoff;
+ uint_t cth_stroff;
+ uint_t cth_strlen;
+} ctf_header_v1_t;
+
+#endif /* CTF_OLD_VERSIONS */
+
+#define CTF_MAGIC 0xcff1 /* magic number identifying header */
+
+/* data format version number */
+#define CTF_VERSION_1 1
+#define CTF_VERSION_2 2
+#define CTF_VERSION CTF_VERSION_2 /* current version */
+
+#define CTF_F_COMPRESS 0x1 /* data buffer is compressed */
+
+typedef struct ctf_lblent {
+ uint_t ctl_label; /* ref to name of label */
+ uint_t ctl_typeidx; /* last type associated with this label */
+} ctf_lblent_t;
+
+typedef struct ctf_stype {
+ uint_t ctt_name; /* reference to name in string table */
+ ushort_t ctt_info; /* encoded kind, variant length (see below) */
+ union {
+ ushort_t _size; /* size of entire type in bytes */
+ ushort_t _type; /* reference to another type */
+ } _u;
+} ctf_stype_t;
+
+/*
+ * type sizes, measured in bytes, come in two flavors. 99% of them fit within
+ * (USHRT_MAX - 1), and thus can be stored in the ctt_size member of a
+ * ctf_stype_t. The maximum value for these sizes is CTF_MAX_SIZE. The sizes
+ * larger than CTF_MAX_SIZE must be stored in the ctt_lsize member of a
+ * ctf_type_t. Use of this member is indicated by the presence of
+ * CTF_LSIZE_SENT in ctt_size.
+ */
+typedef struct ctf_type {
+ uint_t ctt_name; /* reference to name in string table */
+ ushort_t ctt_info; /* encoded kind, variant length (see below) */
+ union {
+ ushort_t _size; /* always CTF_LSIZE_SENT */
+ ushort_t _type; /* do not use */
+ } _u;
+ uint_t ctt_lsizehi; /* high 32 bits of type size in bytes */
+ uint_t ctt_lsizelo; /* low 32 bits of type size in bytes */
+} ctf_type_t;
+
+#define ctt_size _u._size /* for fundamental types that have a size */
+#define ctt_type _u._type /* for types that reference another type */
+
+/*
+ * The following macros compose and decompose values for ctt_info and
+ * ctt_name, as well as other structures that contain name references.
+ *
+ * ------------------------
+ * ctt_info: | kind | isroot | vlen |
+ * ------------------------
+ * 15 11 10 9 0
+ *
+ * kind = CTF_INFO_KIND(c.ctt_info); <-- CTF_K_* value (see below)
+ * vlen = CTF_INFO_VLEN(c.ctt_info); <-- length of variable data list
+ *
+ * stid = CTF_NAME_STID(c.ctt_name); <-- string table id number (0 or 1)
+ * offset = CTF_NAME_OFFSET(c.ctt_name); <-- string table byte offset
+ *
+ * c.ctt_info = CTF_TYPE_INFO(kind, vlen);
+ * c.ctt_name = CTF_TYPE_NAME(stid, offset);
+ */
+
+#define CTF_INFO_KIND(info) (((info) & 0xf800) >> 11)
+#define CTF_INFO_ISROOT(info) (((info) & 0x0400) >> 10)
+#define CTF_INFO_VLEN(info) (((info) & CTF_MAX_VLEN))
+
+#define CTF_NAME_STID(name) ((name) >> 31)
+#define CTF_NAME_OFFSET(name) ((name) & 0x7fffffff)
+
+#define CTF_TYPE_INFO(kind, isroot, vlen) \
+ (((kind) << 11) | (((isroot) ? 1 : 0) << 10) | ((vlen) & CTF_MAX_VLEN))
+
+#define CTF_TYPE_NAME(stid, offset) \
+ (((stid) << 31) | ((offset) & 0x7fffffff))
+
+#define CTF_TYPE_ISPARENT(id) ((id) < 0x8000)
+#define CTF_TYPE_ISCHILD(id) ((id) > 0x7fff)
+
+#define CTF_TYPE_TO_INDEX(id) ((id) & 0x7fff)
+#define CTF_INDEX_TO_TYPE(id, child) ((child) ? ((id) | 0x8000) : (id))
+#define CTF_PARENT_SHIFT 15
+
+#define CTF_STRTAB_0 0 /* symbolic define for string table id 0 */
+#define CTF_STRTAB_1 1 /* symbolic define for string table id 1 */
+
+#define CTF_TYPE_LSIZE(cttp) \
+ (((uint64_t)(cttp)->ctt_lsizehi) << 32 | (cttp)->ctt_lsizelo)
+#define CTF_SIZE_TO_LSIZE_HI(size) ((uint32_t)((uint64_t)(size) >> 32))
+#define CTF_SIZE_TO_LSIZE_LO(size) ((uint32_t)(size))
+
+#ifdef CTF_OLD_VERSIONS
+
+#define CTF_INFO_KIND_V1(info) (((info) & 0xf000) >> 12)
+#define CTF_INFO_ISROOT_V1(info) (((info) & 0x0800) >> 11)
+#define CTF_INFO_VLEN_V1(info) (((info) & 0x07ff))
+
+#define CTF_TYPE_INFO_V1(kind, isroot, vlen) \
+ (((kind) << 12) | (((isroot) ? 1 : 0) << 11) | ((vlen) & 0x07ff))
+
+#endif /* CTF_OLD_VERSIONS */
+
+/*
+ * Values for CTF_TYPE_KIND(). If the kind has an associated data list,
+ * CTF_INFO_VLEN() will extract the number of elements in the list, and
+ * the type of each element is shown in the comments below.
+ */
+#define CTF_K_UNKNOWN 0 /* unknown type (used for padding) */
+#define CTF_K_INTEGER 1 /* variant data is CTF_INT_DATA() (see below) */
+#define CTF_K_FLOAT 2 /* variant data is CTF_FP_DATA() (see below) */
+#define CTF_K_POINTER 3 /* ctt_type is referenced type */
+#define CTF_K_ARRAY 4 /* variant data is single ctf_array_t */
+#define CTF_K_FUNCTION 5 /* ctt_type is return type, variant data is */
+ /* list of argument types (ushort_t's) */
+#define CTF_K_STRUCT 6 /* variant data is list of ctf_member_t's */
+#define CTF_K_UNION 7 /* variant data is list of ctf_member_t's */
+#define CTF_K_ENUM 8 /* variant data is list of ctf_enum_t's */
+#define CTF_K_FORWARD 9 /* no additional data; ctt_name is tag */
+#define CTF_K_TYPEDEF 10 /* ctt_type is referenced type */
+#define CTF_K_VOLATILE 11 /* ctt_type is base type */
+#define CTF_K_CONST 12 /* ctt_type is base type */
+#define CTF_K_RESTRICT 13 /* ctt_type is base type */
+
+#define CTF_K_MAX 31 /* Maximum possible CTF_K_* value */
+
+/*
+ * Values for ctt_type when kind is CTF_K_INTEGER. The flags, offset in bits,
+ * and size in bits are encoded as a single word using the following macros.
+ */
+#define CTF_INT_ENCODING(data) (((data) & 0xff000000) >> 24)
+#define CTF_INT_OFFSET(data) (((data) & 0x00ff0000) >> 16)
+#define CTF_INT_BITS(data) (((data) & 0x0000ffff))
+
+#define CTF_INT_DATA(encoding, offset, bits) \
+ (((encoding) << 24) | ((offset) << 16) | (bits))
+
+#define CTF_INT_SIGNED 0x01 /* integer is signed (otherwise unsigned) */
+#define CTF_INT_CHAR 0x02 /* character display format */
+#define CTF_INT_BOOL 0x04 /* boolean display format */
+#define CTF_INT_VARARGS 0x08 /* varargs display format */
+
+/*
+ * Values for ctt_type when kind is CTF_K_FLOAT. The encoding, offset in bits,
+ * and size in bits are encoded as a single word using the following macros.
+ */
+#define CTF_FP_ENCODING(data) (((data) & 0xff000000) >> 24)
+#define CTF_FP_OFFSET(data) (((data) & 0x00ff0000) >> 16)
+#define CTF_FP_BITS(data) (((data) & 0x0000ffff))
+
+#define CTF_FP_DATA(encoding, offset, bits) \
+ (((encoding) << 24) | ((offset) << 16) | (bits))
+
+#define CTF_FP_SINGLE 1 /* IEEE 32-bit float encoding */
+#define CTF_FP_DOUBLE 2 /* IEEE 64-bit float encoding */
+#define CTF_FP_CPLX 3 /* Complex encoding */
+#define CTF_FP_DCPLX 4 /* Double complex encoding */
+#define CTF_FP_LDCPLX 5 /* Long double complex encoding */
+#define CTF_FP_LDOUBLE 6 /* Long double encoding */
+#define CTF_FP_INTRVL 7 /* Interval (2x32-bit) encoding */
+#define CTF_FP_DINTRVL 8 /* Double interval (2x64-bit) encoding */
+#define CTF_FP_LDINTRVL 9 /* Long double interval (2x128-bit) encoding */
+#define CTF_FP_IMAGRY 10 /* Imaginary (32-bit) encoding */
+#define CTF_FP_DIMAGRY 11 /* Long imaginary (64-bit) encoding */
+#define CTF_FP_LDIMAGRY 12 /* Long double imaginary (128-bit) encoding */
+
+#define CTF_FP_MAX 12 /* Maximum possible CTF_FP_* value */
+
+typedef struct ctf_array {
+ ushort_t cta_contents; /* reference to type of array contents */
+ ushort_t cta_index; /* reference to type of array index */
+ uint_t cta_nelems; /* number of elements */
+} ctf_array_t;
+
+/*
+ * Most structure members have bit offsets that can be expressed using a
+ * short. Some don't. ctf_member_t is used for structs which cannot
+ * contain any of these large offsets, whereas ctf_lmember_t is used in the
+ * latter case. If ctt_size for a given struct is >= 8192 bytes, all members
+ * will be stored as type ctf_lmember_t.
+ */
+
+#define CTF_LSTRUCT_THRESH 8192
+
+typedef struct ctf_member {
+ uint_t ctm_name; /* reference to name in string table */
+ ushort_t ctm_type; /* reference to type of member */
+ ushort_t ctm_offset; /* offset of this member in bits */
+} ctf_member_t;
+
+typedef struct ctf_lmember {
+ uint_t ctlm_name; /* reference to name in string table */
+ ushort_t ctlm_type; /* reference to type of member */
+ ushort_t ctlm_pad; /* padding */
+ uint_t ctlm_offsethi; /* high 32 bits of member offset in bits */
+ uint_t ctlm_offsetlo; /* low 32 bits of member offset in bits */
+} ctf_lmember_t;
+
+#define CTF_LMEM_OFFSET(ctlmp) \
+ (((uint64_t)(ctlmp)->ctlm_offsethi) << 32 | (ctlmp)->ctlm_offsetlo)
+#define CTF_OFFSET_TO_LMEMHI(offset) ((uint32_t)((uint64_t)(offset) >> 32))
+#define CTF_OFFSET_TO_LMEMLO(offset) ((uint32_t)(offset))
+
+typedef struct ctf_enum {
+ uint_t cte_name; /* reference to name in string table */
+ int cte_value; /* value associated with this name */
+} ctf_enum_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _CTF_H */
diff --git a/sys/cddl/contrib/opensolaris/uts/common/sys/ctf_api.h b/sys/cddl/contrib/opensolaris/uts/common/sys/ctf_api.h
new file mode 100644
index 000000000000..cd4caaa6f971
--- /dev/null
+++ b/sys/cddl/contrib/opensolaris/uts/common/sys/ctf_api.h
@@ -0,0 +1,245 @@
+/*
+ * 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.
+ */
+
+/*
+ * This header file defines the interfaces available from the CTF debugger
+ * library, libctf, and an equivalent kernel module. This API can be used by
+ * a debugger to operate on data in the Compact ANSI-C Type Format (CTF).
+ * This is NOT a public interface, although it may eventually become one in
+ * the fullness of time after we gain more experience with the interfaces.
+ *
+ * In the meantime, be aware that any program linked with this API in this
+ * release of Solaris is almost guaranteed to break in the next release.
+ *
+ * In short, do not user this header file or the CTF routines for any purpose.
+ */
+
+#ifndef _CTF_API_H
+#define _CTF_API_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/elf.h>
+#include <sys/ctf.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Clients can open one or more CTF containers and obtain a pointer to an
+ * opaque ctf_file_t. Types are identified by an opaque ctf_id_t token.
+ * These opaque definitions allow libctf to evolve without breaking clients.
+ */
+typedef struct ctf_file ctf_file_t;
+typedef long ctf_id_t;
+
+/*
+ * If the debugger needs to provide the CTF library with a set of raw buffers
+ * for use as the CTF data, symbol table, and string table, it can do so by
+ * filling in ctf_sect_t structures and passing them to ctf_bufopen():
+ */
+typedef struct ctf_sect {
+ char *cts_name; /* section name (if any) */
+ ulong_t cts_type; /* section type (ELF SHT_... value) */
+ ulong_t cts_flags; /* section flags (ELF SHF_... value) */
+#if defined(sun)
+ const void *cts_data; /* pointer to section data */
+#else
+ void *cts_data; /* pointer to section data */
+#endif
+ size_t cts_size; /* size of data in bytes */
+ size_t cts_entsize; /* size of each section entry (symtab only) */
+ off64_t cts_offset; /* file offset of this section (if any) */
+} ctf_sect_t;
+
+/*
+ * Encoding information for integers, floating-point values, and certain other
+ * intrinsics can be obtained by calling ctf_type_encoding(), below. The flags
+ * field will contain values appropriate for the type defined in <sys/ctf.h>.
+ */
+typedef struct ctf_encoding {
+ uint_t cte_format; /* data format (CTF_INT_* or CTF_FP_* flags) */
+ uint_t cte_offset; /* offset of value in bits */
+ uint_t cte_bits; /* size of storage in bits */
+} ctf_encoding_t;
+
+typedef struct ctf_membinfo {
+ ctf_id_t ctm_type; /* type of struct or union member */
+ ulong_t ctm_offset; /* offset of member in bits */
+} ctf_membinfo_t;
+
+typedef struct ctf_arinfo {
+ ctf_id_t ctr_contents; /* type of array contents */
+ ctf_id_t ctr_index; /* type of array index */
+ uint_t ctr_nelems; /* number of elements */
+} ctf_arinfo_t;
+
+typedef struct ctf_funcinfo {
+ ctf_id_t ctc_return; /* function return type */
+ uint_t ctc_argc; /* number of typed arguments to function */
+ uint_t ctc_flags; /* function attributes (see below) */
+} ctf_funcinfo_t;
+
+typedef struct ctf_lblinfo {
+ ctf_id_t ctb_typeidx; /* last type associated with the label */
+} ctf_lblinfo_t;
+
+#define CTF_FUNC_VARARG 0x1 /* function arguments end with varargs */
+
+/*
+ * Functions that return integer status or a ctf_id_t use the following value
+ * to indicate failure. ctf_errno() can be used to obtain an error code.
+ */
+#define CTF_ERR (-1L)
+
+/*
+ * The CTF data model is inferred to be the caller's data model or the data
+ * model of the given object, unless ctf_setmodel() is explicitly called.
+ */
+#define CTF_MODEL_ILP32 1 /* object data model is ILP32 */
+#define CTF_MODEL_LP64 2 /* object data model is LP64 */
+#ifdef _LP64
+#define CTF_MODEL_NATIVE CTF_MODEL_LP64
+#else
+#define CTF_MODEL_NATIVE CTF_MODEL_ILP32
+#endif
+
+/*
+ * Dynamic CTF containers can be created using ctf_create(). The ctf_add_*
+ * routines can be used to add new definitions to the dynamic container.
+ * New types are labeled as root or non-root to determine whether they are
+ * visible at the top-level program scope when subsequently doing a lookup.
+ */
+#define CTF_ADD_NONROOT 0 /* type only visible in nested scope */
+#define CTF_ADD_ROOT 1 /* type visible at top-level scope */
+
+/*
+ * These typedefs are used to define the signature for callback functions
+ * that can be used with the iteration and visit functions below:
+ */
+typedef int ctf_visit_f(const char *, ctf_id_t, ulong_t, int, void *);
+typedef int ctf_member_f(const char *, ctf_id_t, ulong_t, void *);
+typedef int ctf_enum_f(const char *, int, void *);
+typedef int ctf_type_f(ctf_id_t, void *);
+typedef int ctf_label_f(const char *, const ctf_lblinfo_t *, void *);
+
+extern ctf_file_t *ctf_bufopen(const ctf_sect_t *, const ctf_sect_t *,
+ const ctf_sect_t *, int *);
+extern ctf_file_t *ctf_fdopen(int, int *);
+extern ctf_file_t *ctf_open(const char *, int *);
+extern ctf_file_t *ctf_create(int *);
+extern void ctf_close(ctf_file_t *);
+
+extern ctf_file_t *ctf_parent_file(ctf_file_t *);
+extern const char *ctf_parent_name(ctf_file_t *);
+
+extern int ctf_import(ctf_file_t *, ctf_file_t *);
+extern int ctf_setmodel(ctf_file_t *, int);
+extern int ctf_getmodel(ctf_file_t *);
+
+extern void ctf_setspecific(ctf_file_t *, void *);
+extern void *ctf_getspecific(ctf_file_t *);
+
+extern int ctf_errno(ctf_file_t *);
+extern const char *ctf_errmsg(int);
+extern int ctf_version(int);
+
+extern int ctf_func_info(ctf_file_t *, ulong_t, ctf_funcinfo_t *);
+extern int ctf_func_args(ctf_file_t *, ulong_t, uint_t, ctf_id_t *);
+
+extern ctf_id_t ctf_lookup_by_name(ctf_file_t *, const char *);
+extern ctf_id_t ctf_lookup_by_symbol(ctf_file_t *, ulong_t);
+
+extern ctf_id_t ctf_type_resolve(ctf_file_t *, ctf_id_t);
+extern ssize_t ctf_type_lname(ctf_file_t *, ctf_id_t, char *, size_t);
+extern char *ctf_type_name(ctf_file_t *, ctf_id_t, char *, size_t);
+extern ssize_t ctf_type_size(ctf_file_t *, ctf_id_t);
+extern ssize_t ctf_type_align(ctf_file_t *, ctf_id_t);
+extern int ctf_type_kind(ctf_file_t *, ctf_id_t);
+extern ctf_id_t ctf_type_reference(ctf_file_t *, ctf_id_t);
+extern ctf_id_t ctf_type_pointer(ctf_file_t *, ctf_id_t);
+extern int ctf_type_encoding(ctf_file_t *, ctf_id_t, ctf_encoding_t *);
+extern int ctf_type_visit(ctf_file_t *, ctf_id_t, ctf_visit_f *, void *);
+extern int ctf_type_cmp(ctf_file_t *, ctf_id_t, ctf_file_t *, ctf_id_t);
+extern int ctf_type_compat(ctf_file_t *, ctf_id_t, ctf_file_t *, ctf_id_t);
+
+extern int ctf_member_info(ctf_file_t *, ctf_id_t, const char *,
+ ctf_membinfo_t *);
+extern int ctf_array_info(ctf_file_t *, ctf_id_t, ctf_arinfo_t *);
+
+extern const char *ctf_enum_name(ctf_file_t *, ctf_id_t, int);
+extern int ctf_enum_value(ctf_file_t *, ctf_id_t, const char *, int *);
+
+extern const char *ctf_label_topmost(ctf_file_t *);
+extern int ctf_label_info(ctf_file_t *, const char *, ctf_lblinfo_t *);
+
+extern int ctf_member_iter(ctf_file_t *, ctf_id_t, ctf_member_f *, void *);
+extern int ctf_enum_iter(ctf_file_t *, ctf_id_t, ctf_enum_f *, void *);
+extern int ctf_type_iter(ctf_file_t *, ctf_type_f *, void *);
+extern int ctf_label_iter(ctf_file_t *, ctf_label_f *, void *);
+
+extern ctf_id_t ctf_add_array(ctf_file_t *, uint_t, const ctf_arinfo_t *);
+extern ctf_id_t ctf_add_const(ctf_file_t *, uint_t, ctf_id_t);
+extern ctf_id_t ctf_add_enum(ctf_file_t *, uint_t, const char *);
+extern ctf_id_t ctf_add_float(ctf_file_t *, uint_t,
+ const char *, const ctf_encoding_t *);
+extern ctf_id_t ctf_add_forward(ctf_file_t *, uint_t, const char *, uint_t);
+extern ctf_id_t ctf_add_function(ctf_file_t *, uint_t,
+ const ctf_funcinfo_t *, const ctf_id_t *);
+extern ctf_id_t ctf_add_integer(ctf_file_t *, uint_t,
+ const char *, const ctf_encoding_t *);
+extern ctf_id_t ctf_add_pointer(ctf_file_t *, uint_t, ctf_id_t);
+extern ctf_id_t ctf_add_type(ctf_file_t *, ctf_file_t *, ctf_id_t);
+extern ctf_id_t ctf_add_typedef(ctf_file_t *, uint_t, const char *, ctf_id_t);
+extern ctf_id_t ctf_add_restrict(ctf_file_t *, uint_t, ctf_id_t);
+extern ctf_id_t ctf_add_struct(ctf_file_t *, uint_t, const char *);
+extern ctf_id_t ctf_add_union(ctf_file_t *, uint_t, const char *);
+extern ctf_id_t ctf_add_volatile(ctf_file_t *, uint_t, ctf_id_t);
+
+extern int ctf_add_enumerator(ctf_file_t *, ctf_id_t, const char *, int);
+extern int ctf_add_member(ctf_file_t *, ctf_id_t, const char *, ctf_id_t);
+
+extern int ctf_set_array(ctf_file_t *, ctf_id_t, const ctf_arinfo_t *);
+
+extern int ctf_update(ctf_file_t *);
+extern int ctf_discard(ctf_file_t *);
+extern int ctf_write(ctf_file_t *, int);
+
+#ifdef _KERNEL
+
+struct module;
+extern ctf_file_t *ctf_modopen(struct module *, int *);
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _CTF_API_H */
diff --git a/sys/cddl/contrib/opensolaris/uts/common/sys/debug.h b/sys/cddl/contrib/opensolaris/uts/common/sys/debug.h
index c87c8848bc03..432e6be94dd5 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/sys/debug.h
+++ b/sys/cddl/contrib/opensolaris/uts/common/sys/debug.h
@@ -49,7 +49,7 @@ extern "C" {
#if defined(__STDC__)
extern int assfail(const char *, const char *, int);
#define VERIFY(EX) ((void)((EX) || assfail(#EX, __FILE__, __LINE__)))
-#if DEBUG
+#ifdef DEBUG
#define ASSERT(EX) VERIFY(EX)
#else
#define ASSERT(x) ((void)0)
@@ -57,7 +57,7 @@ extern int assfail(const char *, const char *, int);
#else /* defined(__STDC__) */
extern int assfail();
#define VERIFY(EX) ((void)((EX) || assfail("EX", __FILE__, __LINE__)))
-#if DEBUG
+#ifdef DEBUG
#define ASSERT(EX) VERIFY(EX)
#else
#define ASSERT(x) ((void)0)
@@ -97,7 +97,7 @@ _NOTE(CONSTCOND) } while (0)
#define VERIFY3S(x, y, z) VERIFY3_IMPL(x, y, z, int64_t)
#define VERIFY3U(x, y, z) VERIFY3_IMPL(x, y, z, uint64_t)
#define VERIFY3P(x, y, z) VERIFY3_IMPL(x, y, z, uintptr_t)
-#if DEBUG
+#ifdef DEBUG
#define ASSERT3S(x, y, z) VERIFY3S(x, y, z)
#define ASSERT3U(x, y, z) VERIFY3U(x, y, z)
#define ASSERT3P(x, y, z) VERIFY3P(x, y, z)
diff --git a/sys/cddl/contrib/opensolaris/uts/common/sys/dtrace.h b/sys/cddl/contrib/opensolaris/uts/common/sys/dtrace.h
new file mode 100644
index 000000000000..82b97a373682
--- /dev/null
+++ b/sys/cddl/contrib/opensolaris/uts/common/sys/dtrace.h
@@ -0,0 +1,2309 @@
+/*
+ * 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.
+ */
+
+#ifndef _SYS_DTRACE_H
+#define _SYS_DTRACE_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * DTrace Dynamic Tracing Software: Kernel Interfaces
+ *
+ * Note: The contents of this file are private to the implementation of the
+ * Solaris system and DTrace subsystem and are subject to change at any time
+ * without notice. Applications and drivers using these interfaces will fail
+ * to run on future releases. These interfaces should not be used for any
+ * purpose except those expressly outlined in dtrace(7D) and libdtrace(3LIB).
+ * Please refer to the "Solaris Dynamic Tracing Guide" for more information.
+ */
+
+#ifndef _ASM
+
+#include <sys/types.h>
+#include <sys/modctl.h>
+#include <sys/processor.h>
+#if defined(sun)
+#include <sys/systm.h>
+#else
+#include <sys/param.h>
+#include <sys/linker.h>
+#include <sys/ioccom.h>
+#include <sys/ucred.h>
+typedef int model_t;
+#endif
+#include <sys/ctf_api.h>
+#include <sys/cyclic.h>
+#if defined(sun)
+#include <sys/int_limits.h>
+#else
+#include <sys/stdint.h>
+#endif
+
+/*
+ * DTrace Universal Constants and Typedefs
+ */
+#define DTRACE_CPUALL -1 /* all CPUs */
+#define DTRACE_IDNONE 0 /* invalid probe identifier */
+#define DTRACE_EPIDNONE 0 /* invalid enabled probe identifier */
+#define DTRACE_AGGIDNONE 0 /* invalid aggregation identifier */
+#define DTRACE_AGGVARIDNONE 0 /* invalid aggregation variable ID */
+#define DTRACE_CACHEIDNONE 0 /* invalid predicate cache */
+#define DTRACE_PROVNONE 0 /* invalid provider identifier */
+#define DTRACE_METAPROVNONE 0 /* invalid meta-provider identifier */
+#define DTRACE_ARGNONE -1 /* invalid argument index */
+
+#define DTRACE_PROVNAMELEN 64
+#define DTRACE_MODNAMELEN 64
+#define DTRACE_FUNCNAMELEN 128
+#define DTRACE_NAMELEN 64
+#define DTRACE_FULLNAMELEN (DTRACE_PROVNAMELEN + DTRACE_MODNAMELEN + \
+ DTRACE_FUNCNAMELEN + DTRACE_NAMELEN + 4)
+#define DTRACE_ARGTYPELEN 128
+
+typedef uint32_t dtrace_id_t; /* probe identifier */
+typedef uint32_t dtrace_epid_t; /* enabled probe identifier */
+typedef uint32_t dtrace_aggid_t; /* aggregation identifier */
+typedef int64_t dtrace_aggvarid_t; /* aggregation variable identifier */
+typedef uint16_t dtrace_actkind_t; /* action kind */
+typedef int64_t dtrace_optval_t; /* option value */
+typedef uint32_t dtrace_cacheid_t; /* predicate cache identifier */
+
+typedef enum dtrace_probespec {
+ DTRACE_PROBESPEC_NONE = -1,
+ DTRACE_PROBESPEC_PROVIDER = 0,
+ DTRACE_PROBESPEC_MOD,
+ DTRACE_PROBESPEC_FUNC,
+ DTRACE_PROBESPEC_NAME
+} dtrace_probespec_t;
+
+/*
+ * DTrace Intermediate Format (DIF)
+ *
+ * The following definitions describe the DTrace Intermediate Format (DIF), a
+ * a RISC-like instruction set and program encoding used to represent
+ * predicates and actions that can be bound to DTrace probes. The constants
+ * below defining the number of available registers are suggested minimums; the
+ * compiler should use DTRACEIOC_CONF to dynamically obtain the number of
+ * registers provided by the current DTrace implementation.
+ */
+#define DIF_VERSION_1 1 /* DIF version 1: Solaris 10 Beta */
+#define DIF_VERSION_2 2 /* DIF version 2: Solaris 10 FCS */
+#define DIF_VERSION DIF_VERSION_2 /* latest DIF instruction set version */
+#define DIF_DIR_NREGS 8 /* number of DIF integer registers */
+#define DIF_DTR_NREGS 8 /* number of DIF tuple registers */
+
+#define DIF_OP_OR 1 /* or r1, r2, rd */
+#define DIF_OP_XOR 2 /* xor r1, r2, rd */
+#define DIF_OP_AND 3 /* and r1, r2, rd */
+#define DIF_OP_SLL 4 /* sll r1, r2, rd */
+#define DIF_OP_SRL 5 /* srl r1, r2, rd */
+#define DIF_OP_SUB 6 /* sub r1, r2, rd */
+#define DIF_OP_ADD 7 /* add r1, r2, rd */
+#define DIF_OP_MUL 8 /* mul r1, r2, rd */
+#define DIF_OP_SDIV 9 /* sdiv r1, r2, rd */
+#define DIF_OP_UDIV 10 /* udiv r1, r2, rd */
+#define DIF_OP_SREM 11 /* srem r1, r2, rd */
+#define DIF_OP_UREM 12 /* urem r1, r2, rd */
+#define DIF_OP_NOT 13 /* not r1, rd */
+#define DIF_OP_MOV 14 /* mov r1, rd */
+#define DIF_OP_CMP 15 /* cmp r1, r2 */
+#define DIF_OP_TST 16 /* tst r1 */
+#define DIF_OP_BA 17 /* ba label */
+#define DIF_OP_BE 18 /* be label */
+#define DIF_OP_BNE 19 /* bne label */
+#define DIF_OP_BG 20 /* bg label */
+#define DIF_OP_BGU 21 /* bgu label */
+#define DIF_OP_BGE 22 /* bge label */
+#define DIF_OP_BGEU 23 /* bgeu label */
+#define DIF_OP_BL 24 /* bl label */
+#define DIF_OP_BLU 25 /* blu label */
+#define DIF_OP_BLE 26 /* ble label */
+#define DIF_OP_BLEU 27 /* bleu label */
+#define DIF_OP_LDSB 28 /* ldsb [r1], rd */
+#define DIF_OP_LDSH 29 /* ldsh [r1], rd */
+#define DIF_OP_LDSW 30 /* ldsw [r1], rd */
+#define DIF_OP_LDUB 31 /* ldub [r1], rd */
+#define DIF_OP_LDUH 32 /* lduh [r1], rd */
+#define DIF_OP_LDUW 33 /* lduw [r1], rd */
+#define DIF_OP_LDX 34 /* ldx [r1], rd */
+#define DIF_OP_RET 35 /* ret rd */
+#define DIF_OP_NOP 36 /* nop */
+#define DIF_OP_SETX 37 /* setx intindex, rd */
+#define DIF_OP_SETS 38 /* sets strindex, rd */
+#define DIF_OP_SCMP 39 /* scmp r1, r2 */
+#define DIF_OP_LDGA 40 /* ldga var, ri, rd */
+#define DIF_OP_LDGS 41 /* ldgs var, rd */
+#define DIF_OP_STGS 42 /* stgs var, rs */
+#define DIF_OP_LDTA 43 /* ldta var, ri, rd */
+#define DIF_OP_LDTS 44 /* ldts var, rd */
+#define DIF_OP_STTS 45 /* stts var, rs */
+#define DIF_OP_SRA 46 /* sra r1, r2, rd */
+#define DIF_OP_CALL 47 /* call subr, rd */
+#define DIF_OP_PUSHTR 48 /* pushtr type, rs, rr */
+#define DIF_OP_PUSHTV 49 /* pushtv type, rs, rv */
+#define DIF_OP_POPTS 50 /* popts */
+#define DIF_OP_FLUSHTS 51 /* flushts */
+#define DIF_OP_LDGAA 52 /* ldgaa var, rd */
+#define DIF_OP_LDTAA 53 /* ldtaa var, rd */
+#define DIF_OP_STGAA 54 /* stgaa var, rs */
+#define DIF_OP_STTAA 55 /* sttaa var, rs */
+#define DIF_OP_LDLS 56 /* ldls var, rd */
+#define DIF_OP_STLS 57 /* stls var, rs */
+#define DIF_OP_ALLOCS 58 /* allocs r1, rd */
+#define DIF_OP_COPYS 59 /* copys r1, r2, rd */
+#define DIF_OP_STB 60 /* stb r1, [rd] */
+#define DIF_OP_STH 61 /* sth r1, [rd] */
+#define DIF_OP_STW 62 /* stw r1, [rd] */
+#define DIF_OP_STX 63 /* stx r1, [rd] */
+#define DIF_OP_ULDSB 64 /* uldsb [r1], rd */
+#define DIF_OP_ULDSH 65 /* uldsh [r1], rd */
+#define DIF_OP_ULDSW 66 /* uldsw [r1], rd */
+#define DIF_OP_ULDUB 67 /* uldub [r1], rd */
+#define DIF_OP_ULDUH 68 /* ulduh [r1], rd */
+#define DIF_OP_ULDUW 69 /* ulduw [r1], rd */
+#define DIF_OP_ULDX 70 /* uldx [r1], rd */
+#define DIF_OP_RLDSB 71 /* rldsb [r1], rd */
+#define DIF_OP_RLDSH 72 /* rldsh [r1], rd */
+#define DIF_OP_RLDSW 73 /* rldsw [r1], rd */
+#define DIF_OP_RLDUB 74 /* rldub [r1], rd */
+#define DIF_OP_RLDUH 75 /* rlduh [r1], rd */
+#define DIF_OP_RLDUW 76 /* rlduw [r1], rd */
+#define DIF_OP_RLDX 77 /* rldx [r1], rd */
+#define DIF_OP_XLATE 78 /* xlate xlrindex, rd */
+#define DIF_OP_XLARG 79 /* xlarg xlrindex, rd */
+
+#define DIF_INTOFF_MAX 0xffff /* highest integer table offset */
+#define DIF_STROFF_MAX 0xffff /* highest string table offset */
+#define DIF_REGISTER_MAX 0xff /* highest register number */
+#define DIF_VARIABLE_MAX 0xffff /* highest variable identifier */
+#define DIF_SUBROUTINE_MAX 0xffff /* highest subroutine code */
+
+#define DIF_VAR_ARRAY_MIN 0x0000 /* lowest numbered array variable */
+#define DIF_VAR_ARRAY_UBASE 0x0080 /* lowest user-defined array */
+#define DIF_VAR_ARRAY_MAX 0x00ff /* highest numbered array variable */
+
+#define DIF_VAR_OTHER_MIN 0x0100 /* lowest numbered scalar or assc */
+#define DIF_VAR_OTHER_UBASE 0x0500 /* lowest user-defined scalar or assc */
+#define DIF_VAR_OTHER_MAX 0xffff /* highest numbered scalar or assc */
+
+#define DIF_VAR_ARGS 0x0000 /* arguments array */
+#define DIF_VAR_REGS 0x0001 /* registers array */
+#define DIF_VAR_UREGS 0x0002 /* user registers array */
+#define DIF_VAR_CURTHREAD 0x0100 /* thread pointer */
+#define DIF_VAR_TIMESTAMP 0x0101 /* timestamp */
+#define DIF_VAR_VTIMESTAMP 0x0102 /* virtual timestamp */
+#define DIF_VAR_IPL 0x0103 /* interrupt priority level */
+#define DIF_VAR_EPID 0x0104 /* enabled probe ID */
+#define DIF_VAR_ID 0x0105 /* probe ID */
+#define DIF_VAR_ARG0 0x0106 /* first argument */
+#define DIF_VAR_ARG1 0x0107 /* second argument */
+#define DIF_VAR_ARG2 0x0108 /* third argument */
+#define DIF_VAR_ARG3 0x0109 /* fourth argument */
+#define DIF_VAR_ARG4 0x010a /* fifth argument */
+#define DIF_VAR_ARG5 0x010b /* sixth argument */
+#define DIF_VAR_ARG6 0x010c /* seventh argument */
+#define DIF_VAR_ARG7 0x010d /* eighth argument */
+#define DIF_VAR_ARG8 0x010e /* ninth argument */
+#define DIF_VAR_ARG9 0x010f /* tenth argument */
+#define DIF_VAR_STACKDEPTH 0x0110 /* stack depth */
+#define DIF_VAR_CALLER 0x0111 /* caller */
+#define DIF_VAR_PROBEPROV 0x0112 /* probe provider */
+#define DIF_VAR_PROBEMOD 0x0113 /* probe module */
+#define DIF_VAR_PROBEFUNC 0x0114 /* probe function */
+#define DIF_VAR_PROBENAME 0x0115 /* probe name */
+#define DIF_VAR_PID 0x0116 /* process ID */
+#define DIF_VAR_TID 0x0117 /* (per-process) thread ID */
+#define DIF_VAR_EXECNAME 0x0118 /* name of executable */
+#define DIF_VAR_ZONENAME 0x0119 /* zone name associated with process */
+#define DIF_VAR_WALLTIMESTAMP 0x011a /* wall-clock timestamp */
+#define DIF_VAR_USTACKDEPTH 0x011b /* user-land stack depth */
+#define DIF_VAR_UCALLER 0x011c /* user-level caller */
+#define DIF_VAR_PPID 0x011d /* parent process ID */
+#define DIF_VAR_UID 0x011e /* process user ID */
+#define DIF_VAR_GID 0x011f /* process group ID */
+#define DIF_VAR_ERRNO 0x0120 /* thread errno */
+#define DIF_VAR_EXECARGS 0x0121 /* process arguments */
+
+#define DIF_SUBR_RAND 0
+#define DIF_SUBR_MUTEX_OWNED 1
+#define DIF_SUBR_MUTEX_OWNER 2
+#define DIF_SUBR_MUTEX_TYPE_ADAPTIVE 3
+#define DIF_SUBR_MUTEX_TYPE_SPIN 4
+#define DIF_SUBR_RW_READ_HELD 5
+#define DIF_SUBR_RW_WRITE_HELD 6
+#define DIF_SUBR_RW_ISWRITER 7
+#define DIF_SUBR_COPYIN 8
+#define DIF_SUBR_COPYINSTR 9
+#define DIF_SUBR_SPECULATION 10
+#define DIF_SUBR_PROGENYOF 11
+#define DIF_SUBR_STRLEN 12
+#define DIF_SUBR_COPYOUT 13
+#define DIF_SUBR_COPYOUTSTR 14
+#define DIF_SUBR_ALLOCA 15
+#define DIF_SUBR_BCOPY 16
+#define DIF_SUBR_COPYINTO 17
+#define DIF_SUBR_MSGDSIZE 18
+#define DIF_SUBR_MSGSIZE 19
+#define DIF_SUBR_GETMAJOR 20
+#define DIF_SUBR_GETMINOR 21
+#define DIF_SUBR_DDI_PATHNAME 22
+#define DIF_SUBR_STRJOIN 23
+#define DIF_SUBR_LLTOSTR 24
+#define DIF_SUBR_BASENAME 25
+#define DIF_SUBR_DIRNAME 26
+#define DIF_SUBR_CLEANPATH 27
+#define DIF_SUBR_STRCHR 28
+#define DIF_SUBR_STRRCHR 29
+#define DIF_SUBR_STRSTR 30
+#define DIF_SUBR_STRTOK 31
+#define DIF_SUBR_SUBSTR 32
+#define DIF_SUBR_INDEX 33
+#define DIF_SUBR_RINDEX 34
+#define DIF_SUBR_HTONS 35
+#define DIF_SUBR_HTONL 36
+#define DIF_SUBR_HTONLL 37
+#define DIF_SUBR_NTOHS 38
+#define DIF_SUBR_NTOHL 39
+#define DIF_SUBR_NTOHLL 40
+#define DIF_SUBR_INET_NTOP 41
+#define DIF_SUBR_INET_NTOA 42
+#define DIF_SUBR_INET_NTOA6 43
+#define DIF_SUBR_MEMREF 44
+#define DIF_SUBR_TYPEREF 45
+#define DIF_SUBR_SX_SHARED_HELD 46
+#define DIF_SUBR_SX_EXCLUSIVE_HELD 47
+#define DIF_SUBR_SX_ISEXCLUSIVE 48
+
+#define DIF_SUBR_MAX 48 /* max subroutine value */
+
+typedef uint32_t dif_instr_t;
+
+#define DIF_INSTR_OP(i) (((i) >> 24) & 0xff)
+#define DIF_INSTR_R1(i) (((i) >> 16) & 0xff)
+#define DIF_INSTR_R2(i) (((i) >> 8) & 0xff)
+#define DIF_INSTR_RD(i) ((i) & 0xff)
+#define DIF_INSTR_RS(i) ((i) & 0xff)
+#define DIF_INSTR_LABEL(i) ((i) & 0xffffff)
+#define DIF_INSTR_VAR(i) (((i) >> 8) & 0xffff)
+#define DIF_INSTR_INTEGER(i) (((i) >> 8) & 0xffff)
+#define DIF_INSTR_STRING(i) (((i) >> 8) & 0xffff)
+#define DIF_INSTR_SUBR(i) (((i) >> 8) & 0xffff)
+#define DIF_INSTR_TYPE(i) (((i) >> 16) & 0xff)
+#define DIF_INSTR_XLREF(i) (((i) >> 8) & 0xffff)
+
+#define DIF_INSTR_FMT(op, r1, r2, d) \
+ (((op) << 24) | ((r1) << 16) | ((r2) << 8) | (d))
+
+#define DIF_INSTR_NOT(r1, d) (DIF_INSTR_FMT(DIF_OP_NOT, r1, 0, d))
+#define DIF_INSTR_MOV(r1, d) (DIF_INSTR_FMT(DIF_OP_MOV, r1, 0, d))
+#define DIF_INSTR_CMP(op, r1, r2) (DIF_INSTR_FMT(op, r1, r2, 0))
+#define DIF_INSTR_TST(r1) (DIF_INSTR_FMT(DIF_OP_TST, r1, 0, 0))
+#define DIF_INSTR_BRANCH(op, label) (((op) << 24) | (label))
+#define DIF_INSTR_LOAD(op, r1, d) (DIF_INSTR_FMT(op, r1, 0, d))
+#define DIF_INSTR_STORE(op, r1, d) (DIF_INSTR_FMT(op, r1, 0, d))
+#define DIF_INSTR_SETX(i, d) ((DIF_OP_SETX << 24) | ((i) << 8) | (d))
+#define DIF_INSTR_SETS(s, d) ((DIF_OP_SETS << 24) | ((s) << 8) | (d))
+#define DIF_INSTR_RET(d) (DIF_INSTR_FMT(DIF_OP_RET, 0, 0, d))
+#define DIF_INSTR_NOP (DIF_OP_NOP << 24)
+#define DIF_INSTR_LDA(op, v, r, d) (DIF_INSTR_FMT(op, v, r, d))
+#define DIF_INSTR_LDV(op, v, d) (((op) << 24) | ((v) << 8) | (d))
+#define DIF_INSTR_STV(op, v, rs) (((op) << 24) | ((v) << 8) | (rs))
+#define DIF_INSTR_CALL(s, d) ((DIF_OP_CALL << 24) | ((s) << 8) | (d))
+#define DIF_INSTR_PUSHTS(op, t, r2, rs) (DIF_INSTR_FMT(op, t, r2, rs))
+#define DIF_INSTR_POPTS (DIF_OP_POPTS << 24)
+#define DIF_INSTR_FLUSHTS (DIF_OP_FLUSHTS << 24)
+#define DIF_INSTR_ALLOCS(r1, d) (DIF_INSTR_FMT(DIF_OP_ALLOCS, r1, 0, d))
+#define DIF_INSTR_COPYS(r1, r2, d) (DIF_INSTR_FMT(DIF_OP_COPYS, r1, r2, d))
+#define DIF_INSTR_XLATE(op, r, d) (((op) << 24) | ((r) << 8) | (d))
+
+#define DIF_REG_R0 0 /* %r0 is always set to zero */
+
+/*
+ * A DTrace Intermediate Format Type (DIF Type) is used to represent the types
+ * of variables, function and associative array arguments, and the return type
+ * for each DIF object (shown below). It contains a description of the type,
+ * its size in bytes, and a module identifier.
+ */
+typedef struct dtrace_diftype {
+ uint8_t dtdt_kind; /* type kind (see below) */
+ uint8_t dtdt_ckind; /* type kind in CTF */
+ uint8_t dtdt_flags; /* type flags (see below) */
+ uint8_t dtdt_pad; /* reserved for future use */
+ uint32_t dtdt_size; /* type size in bytes (unless string) */
+} dtrace_diftype_t;
+
+#define DIF_TYPE_CTF 0 /* type is a CTF type */
+#define DIF_TYPE_STRING 1 /* type is a D string */
+
+#define DIF_TF_BYREF 0x1 /* type is passed by reference */
+
+/*
+ * A DTrace Intermediate Format variable record is used to describe each of the
+ * variables referenced by a given DIF object. It contains an integer variable
+ * identifier along with variable scope and properties, as shown below. The
+ * size of this structure must be sizeof (int) aligned.
+ */
+typedef struct dtrace_difv {
+ uint32_t dtdv_name; /* variable name index in dtdo_strtab */
+ uint32_t dtdv_id; /* variable reference identifier */
+ uint8_t dtdv_kind; /* variable kind (see below) */
+ uint8_t dtdv_scope; /* variable scope (see below) */
+ uint16_t dtdv_flags; /* variable flags (see below) */
+ dtrace_diftype_t dtdv_type; /* variable type (see above) */
+} dtrace_difv_t;
+
+#define DIFV_KIND_ARRAY 0 /* variable is an array of quantities */
+#define DIFV_KIND_SCALAR 1 /* variable is a scalar quantity */
+
+#define DIFV_SCOPE_GLOBAL 0 /* variable has global scope */
+#define DIFV_SCOPE_THREAD 1 /* variable has thread scope */
+#define DIFV_SCOPE_LOCAL 2 /* variable has local scope */
+
+#define DIFV_F_REF 0x1 /* variable is referenced by DIFO */
+#define DIFV_F_MOD 0x2 /* variable is written by DIFO */
+
+/*
+ * DTrace Actions
+ *
+ * The upper byte determines the class of the action; the low bytes determines
+ * the specific action within that class. The classes of actions are as
+ * follows:
+ *
+ * [ no class ] <= May record process- or kernel-related data
+ * DTRACEACT_PROC <= Only records process-related data
+ * DTRACEACT_PROC_DESTRUCTIVE <= Potentially destructive to processes
+ * DTRACEACT_KERNEL <= Only records kernel-related data
+ * DTRACEACT_KERNEL_DESTRUCTIVE <= Potentially destructive to the kernel
+ * DTRACEACT_SPECULATIVE <= Speculation-related action
+ * DTRACEACT_AGGREGATION <= Aggregating action
+ */
+#define DTRACEACT_NONE 0 /* no action */
+#define DTRACEACT_DIFEXPR 1 /* action is DIF expression */
+#define DTRACEACT_EXIT 2 /* exit() action */
+#define DTRACEACT_PRINTF 3 /* printf() action */
+#define DTRACEACT_PRINTA 4 /* printa() action */
+#define DTRACEACT_LIBACT 5 /* library-controlled action */
+#define DTRACEACT_PRINTM 6 /* printm() action */
+#define DTRACEACT_PRINTT 7 /* printt() action */
+
+#define DTRACEACT_PROC 0x0100
+#define DTRACEACT_USTACK (DTRACEACT_PROC + 1)
+#define DTRACEACT_JSTACK (DTRACEACT_PROC + 2)
+#define DTRACEACT_USYM (DTRACEACT_PROC + 3)
+#define DTRACEACT_UMOD (DTRACEACT_PROC + 4)
+#define DTRACEACT_UADDR (DTRACEACT_PROC + 5)
+
+#define DTRACEACT_PROC_DESTRUCTIVE 0x0200
+#define DTRACEACT_STOP (DTRACEACT_PROC_DESTRUCTIVE + 1)
+#define DTRACEACT_RAISE (DTRACEACT_PROC_DESTRUCTIVE + 2)
+#define DTRACEACT_SYSTEM (DTRACEACT_PROC_DESTRUCTIVE + 3)
+#define DTRACEACT_FREOPEN (DTRACEACT_PROC_DESTRUCTIVE + 4)
+
+#define DTRACEACT_PROC_CONTROL 0x0300
+
+#define DTRACEACT_KERNEL 0x0400
+#define DTRACEACT_STACK (DTRACEACT_KERNEL + 1)
+#define DTRACEACT_SYM (DTRACEACT_KERNEL + 2)
+#define DTRACEACT_MOD (DTRACEACT_KERNEL + 3)
+
+#define DTRACEACT_KERNEL_DESTRUCTIVE 0x0500
+#define DTRACEACT_BREAKPOINT (DTRACEACT_KERNEL_DESTRUCTIVE + 1)
+#define DTRACEACT_PANIC (DTRACEACT_KERNEL_DESTRUCTIVE + 2)
+#define DTRACEACT_CHILL (DTRACEACT_KERNEL_DESTRUCTIVE + 3)
+
+#define DTRACEACT_SPECULATIVE 0x0600
+#define DTRACEACT_SPECULATE (DTRACEACT_SPECULATIVE + 1)
+#define DTRACEACT_COMMIT (DTRACEACT_SPECULATIVE + 2)
+#define DTRACEACT_DISCARD (DTRACEACT_SPECULATIVE + 3)
+
+#define DTRACEACT_CLASS(x) ((x) & 0xff00)
+
+#define DTRACEACT_ISDESTRUCTIVE(x) \
+ (DTRACEACT_CLASS(x) == DTRACEACT_PROC_DESTRUCTIVE || \
+ DTRACEACT_CLASS(x) == DTRACEACT_KERNEL_DESTRUCTIVE)
+
+#define DTRACEACT_ISSPECULATIVE(x) \
+ (DTRACEACT_CLASS(x) == DTRACEACT_SPECULATIVE)
+
+#define DTRACEACT_ISPRINTFLIKE(x) \
+ ((x) == DTRACEACT_PRINTF || (x) == DTRACEACT_PRINTA || \
+ (x) == DTRACEACT_SYSTEM || (x) == DTRACEACT_FREOPEN)
+
+/*
+ * DTrace Aggregating Actions
+ *
+ * These are functions f(x) for which the following is true:
+ *
+ * f(f(x_0) U f(x_1) U ... U f(x_n)) = f(x_0 U x_1 U ... U x_n)
+ *
+ * where x_n is a set of arbitrary data. Aggregating actions are in their own
+ * DTrace action class, DTTRACEACT_AGGREGATION. The macros provided here allow
+ * for easier processing of the aggregation argument and data payload for a few
+ * aggregating actions (notably: quantize(), lquantize(), and ustack()).
+ */
+#define DTRACEACT_AGGREGATION 0x0700
+#define DTRACEAGG_COUNT (DTRACEACT_AGGREGATION + 1)
+#define DTRACEAGG_MIN (DTRACEACT_AGGREGATION + 2)
+#define DTRACEAGG_MAX (DTRACEACT_AGGREGATION + 3)
+#define DTRACEAGG_AVG (DTRACEACT_AGGREGATION + 4)
+#define DTRACEAGG_SUM (DTRACEACT_AGGREGATION + 5)
+#define DTRACEAGG_STDDEV (DTRACEACT_AGGREGATION + 6)
+#define DTRACEAGG_QUANTIZE (DTRACEACT_AGGREGATION + 7)
+#define DTRACEAGG_LQUANTIZE (DTRACEACT_AGGREGATION + 8)
+
+#define DTRACEACT_ISAGG(x) \
+ (DTRACEACT_CLASS(x) == DTRACEACT_AGGREGATION)
+
+#define DTRACE_QUANTIZE_NBUCKETS \
+ (((sizeof (uint64_t) * NBBY) - 1) * 2 + 1)
+
+#define DTRACE_QUANTIZE_ZEROBUCKET ((sizeof (uint64_t) * NBBY) - 1)
+
+#define DTRACE_QUANTIZE_BUCKETVAL(buck) \
+ (int64_t)((buck) < DTRACE_QUANTIZE_ZEROBUCKET ? \
+ -(1LL << (DTRACE_QUANTIZE_ZEROBUCKET - 1 - (buck))) : \
+ (buck) == DTRACE_QUANTIZE_ZEROBUCKET ? 0 : \
+ 1LL << ((buck) - DTRACE_QUANTIZE_ZEROBUCKET - 1))
+
+#define DTRACE_LQUANTIZE_STEPSHIFT 48
+#define DTRACE_LQUANTIZE_STEPMASK ((uint64_t)UINT16_MAX << 48)
+#define DTRACE_LQUANTIZE_LEVELSHIFT 32
+#define DTRACE_LQUANTIZE_LEVELMASK ((uint64_t)UINT16_MAX << 32)
+#define DTRACE_LQUANTIZE_BASESHIFT 0
+#define DTRACE_LQUANTIZE_BASEMASK UINT32_MAX
+
+#define DTRACE_LQUANTIZE_STEP(x) \
+ (uint16_t)(((x) & DTRACE_LQUANTIZE_STEPMASK) >> \
+ DTRACE_LQUANTIZE_STEPSHIFT)
+
+#define DTRACE_LQUANTIZE_LEVELS(x) \
+ (uint16_t)(((x) & DTRACE_LQUANTIZE_LEVELMASK) >> \
+ DTRACE_LQUANTIZE_LEVELSHIFT)
+
+#define DTRACE_LQUANTIZE_BASE(x) \
+ (int32_t)(((x) & DTRACE_LQUANTIZE_BASEMASK) >> \
+ DTRACE_LQUANTIZE_BASESHIFT)
+
+#define DTRACE_USTACK_NFRAMES(x) (uint32_t)((x) & UINT32_MAX)
+#define DTRACE_USTACK_STRSIZE(x) (uint32_t)((x) >> 32)
+#define DTRACE_USTACK_ARG(x, y) \
+ ((((uint64_t)(y)) << 32) | ((x) & UINT32_MAX))
+
+#ifndef _LP64
+#if BYTE_ORDER == _BIG_ENDIAN
+#define DTRACE_PTR(type, name) uint32_t name##pad; type *name
+#else
+#define DTRACE_PTR(type, name) type *name; uint32_t name##pad
+#endif
+#else
+#define DTRACE_PTR(type, name) type *name
+#endif
+
+/*
+ * DTrace Object Format (DOF)
+ *
+ * DTrace programs can be persistently encoded in the DOF format so that they
+ * may be embedded in other programs (for example, in an ELF file) or in the
+ * dtrace driver configuration file for use in anonymous tracing. The DOF
+ * format is versioned and extensible so that it can be revised and so that
+ * internal data structures can be modified or extended compatibly. All DOF
+ * structures use fixed-size types, so the 32-bit and 64-bit representations
+ * are identical and consumers can use either data model transparently.
+ *
+ * The file layout is structured as follows:
+ *
+ * +---------------+-------------------+----- ... ----+---- ... ------+
+ * | dof_hdr_t | dof_sec_t[ ... ] | loadable | non-loadable |
+ * | (file header) | (section headers) | section data | section data |
+ * +---------------+-------------------+----- ... ----+---- ... ------+
+ * |<------------ dof_hdr.dofh_loadsz --------------->| |
+ * |<------------ dof_hdr.dofh_filesz ------------------------------->|
+ *
+ * The file header stores meta-data including a magic number, data model for
+ * the instrumentation, data encoding, and properties of the DIF code within.
+ * The header describes its own size and the size of the section headers. By
+ * convention, an array of section headers follows the file header, and then
+ * the data for all loadable sections and unloadable sections. This permits
+ * consumer code to easily download the headers and all loadable data into the
+ * DTrace driver in one contiguous chunk, omitting other extraneous sections.
+ *
+ * The section headers describe the size, offset, alignment, and section type
+ * for each section. Sections are described using a set of #defines that tell
+ * the consumer what kind of data is expected. Sections can contain links to
+ * other sections by storing a dof_secidx_t, an index into the section header
+ * array, inside of the section data structures. The section header includes
+ * an entry size so that sections with data arrays can grow their structures.
+ *
+ * The DOF data itself can contain many snippets of DIF (i.e. >1 DIFOs), which
+ * are represented themselves as a collection of related DOF sections. This
+ * permits us to change the set of sections associated with a DIFO over time,
+ * and also permits us to encode DIFOs that contain different sets of sections.
+ * When a DOF section wants to refer to a DIFO, it stores the dof_secidx_t of a
+ * section of type DOF_SECT_DIFOHDR. This section's data is then an array of
+ * dof_secidx_t's which in turn denote the sections associated with this DIFO.
+ *
+ * This loose coupling of the file structure (header and sections) to the
+ * structure of the DTrace program itself (ECB descriptions, action
+ * descriptions, and DIFOs) permits activities such as relocation processing
+ * to occur in a single pass without having to understand D program structure.
+ *
+ * Finally, strings are always stored in ELF-style string tables along with a
+ * string table section index and string table offset. Therefore strings in
+ * DOF are always arbitrary-length and not bound to the current implementation.
+ */
+
+#define DOF_ID_SIZE 16 /* total size of dofh_ident[] in bytes */
+
+typedef struct dof_hdr {
+ uint8_t dofh_ident[DOF_ID_SIZE]; /* identification bytes (see below) */
+ uint32_t dofh_flags; /* file attribute flags (if any) */
+ uint32_t dofh_hdrsize; /* size of file header in bytes */
+ uint32_t dofh_secsize; /* size of section header in bytes */
+ uint32_t dofh_secnum; /* number of section headers */
+ uint64_t dofh_secoff; /* file offset of section headers */
+ uint64_t dofh_loadsz; /* file size of loadable portion */
+ uint64_t dofh_filesz; /* file size of entire DOF file */
+ uint64_t dofh_pad; /* reserved for future use */
+} dof_hdr_t;
+
+#define DOF_ID_MAG0 0 /* first byte of magic number */
+#define DOF_ID_MAG1 1 /* second byte of magic number */
+#define DOF_ID_MAG2 2 /* third byte of magic number */
+#define DOF_ID_MAG3 3 /* fourth byte of magic number */
+#define DOF_ID_MODEL 4 /* DOF data model (see below) */
+#define DOF_ID_ENCODING 5 /* DOF data encoding (see below) */
+#define DOF_ID_VERSION 6 /* DOF file format major version (see below) */
+#define DOF_ID_DIFVERS 7 /* DIF instruction set version */
+#define DOF_ID_DIFIREG 8 /* DIF integer registers used by compiler */
+#define DOF_ID_DIFTREG 9 /* DIF tuple registers used by compiler */
+#define DOF_ID_PAD 10 /* start of padding bytes (all zeroes) */
+
+#define DOF_MAG_MAG0 0x7F /* DOF_ID_MAG[0-3] */
+#define DOF_MAG_MAG1 'D'
+#define DOF_MAG_MAG2 'O'
+#define DOF_MAG_MAG3 'F'
+
+#define DOF_MAG_STRING "\177DOF"
+#define DOF_MAG_STRLEN 4
+
+#define DOF_MODEL_NONE 0 /* DOF_ID_MODEL */
+#define DOF_MODEL_ILP32 1
+#define DOF_MODEL_LP64 2
+
+#ifdef _LP64
+#define DOF_MODEL_NATIVE DOF_MODEL_LP64
+#else
+#define DOF_MODEL_NATIVE DOF_MODEL_ILP32
+#endif
+
+#define DOF_ENCODE_NONE 0 /* DOF_ID_ENCODING */
+#define DOF_ENCODE_LSB 1
+#define DOF_ENCODE_MSB 2
+
+#if BYTE_ORDER == _BIG_ENDIAN
+#define DOF_ENCODE_NATIVE DOF_ENCODE_MSB
+#else
+#define DOF_ENCODE_NATIVE DOF_ENCODE_LSB
+#endif
+
+#define DOF_VERSION_1 1 /* DOF version 1: Solaris 10 FCS */
+#define DOF_VERSION_2 2 /* DOF version 2: Solaris Express 6/06 */
+#define DOF_VERSION DOF_VERSION_2 /* Latest DOF version */
+
+#define DOF_FL_VALID 0 /* mask of all valid dofh_flags bits */
+
+typedef uint32_t dof_secidx_t; /* section header table index type */
+typedef uint32_t dof_stridx_t; /* string table index type */
+
+#define DOF_SECIDX_NONE (-1U) /* null value for section indices */
+#define DOF_STRIDX_NONE (-1U) /* null value for string indices */
+
+typedef struct dof_sec {
+ uint32_t dofs_type; /* section type (see below) */
+ uint32_t dofs_align; /* section data memory alignment */
+ uint32_t dofs_flags; /* section flags (if any) */
+ uint32_t dofs_entsize; /* size of section entry (if table) */
+ uint64_t dofs_offset; /* offset of section data within file */
+ uint64_t dofs_size; /* size of section data in bytes */
+} dof_sec_t;
+
+#define DOF_SECT_NONE 0 /* null section */
+#define DOF_SECT_COMMENTS 1 /* compiler comments */
+#define DOF_SECT_SOURCE 2 /* D program source code */
+#define DOF_SECT_ECBDESC 3 /* dof_ecbdesc_t */
+#define DOF_SECT_PROBEDESC 4 /* dof_probedesc_t */
+#define DOF_SECT_ACTDESC 5 /* dof_actdesc_t array */
+#define DOF_SECT_DIFOHDR 6 /* dof_difohdr_t (variable length) */
+#define DOF_SECT_DIF 7 /* uint32_t array of byte code */
+#define DOF_SECT_STRTAB 8 /* string table */
+#define DOF_SECT_VARTAB 9 /* dtrace_difv_t array */
+#define DOF_SECT_RELTAB 10 /* dof_relodesc_t array */
+#define DOF_SECT_TYPTAB 11 /* dtrace_diftype_t array */
+#define DOF_SECT_URELHDR 12 /* dof_relohdr_t (user relocations) */
+#define DOF_SECT_KRELHDR 13 /* dof_relohdr_t (kernel relocations) */
+#define DOF_SECT_OPTDESC 14 /* dof_optdesc_t array */
+#define DOF_SECT_PROVIDER 15 /* dof_provider_t */
+#define DOF_SECT_PROBES 16 /* dof_probe_t array */
+#define DOF_SECT_PRARGS 17 /* uint8_t array (probe arg mappings) */
+#define DOF_SECT_PROFFS 18 /* uint32_t array (probe arg offsets) */
+#define DOF_SECT_INTTAB 19 /* uint64_t array */
+#define DOF_SECT_UTSNAME 20 /* struct utsname */
+#define DOF_SECT_XLTAB 21 /* dof_xlref_t array */
+#define DOF_SECT_XLMEMBERS 22 /* dof_xlmember_t array */
+#define DOF_SECT_XLIMPORT 23 /* dof_xlator_t */
+#define DOF_SECT_XLEXPORT 24 /* dof_xlator_t */
+#define DOF_SECT_PREXPORT 25 /* dof_secidx_t array (exported objs) */
+#define DOF_SECT_PRENOFFS 26 /* uint32_t array (enabled offsets) */
+
+#define DOF_SECF_LOAD 1 /* section should be loaded */
+
+typedef struct dof_ecbdesc {
+ dof_secidx_t dofe_probes; /* link to DOF_SECT_PROBEDESC */
+ dof_secidx_t dofe_pred; /* link to DOF_SECT_DIFOHDR */
+ dof_secidx_t dofe_actions; /* link to DOF_SECT_ACTDESC */
+ uint32_t dofe_pad; /* reserved for future use */
+ uint64_t dofe_uarg; /* user-supplied library argument */
+} dof_ecbdesc_t;
+
+typedef struct dof_probedesc {
+ dof_secidx_t dofp_strtab; /* link to DOF_SECT_STRTAB section */
+ dof_stridx_t dofp_provider; /* provider string */
+ dof_stridx_t dofp_mod; /* module string */
+ dof_stridx_t dofp_func; /* function string */
+ dof_stridx_t dofp_name; /* name string */
+ uint32_t dofp_id; /* probe identifier (or zero) */
+} dof_probedesc_t;
+
+typedef struct dof_actdesc {
+ dof_secidx_t dofa_difo; /* link to DOF_SECT_DIFOHDR */
+ dof_secidx_t dofa_strtab; /* link to DOF_SECT_STRTAB section */
+ uint32_t dofa_kind; /* action kind (DTRACEACT_* constant) */
+ uint32_t dofa_ntuple; /* number of subsequent tuple actions */
+ uint64_t dofa_arg; /* kind-specific argument */
+ uint64_t dofa_uarg; /* user-supplied argument */
+} dof_actdesc_t;
+
+typedef struct dof_difohdr {
+ dtrace_diftype_t dofd_rtype; /* return type for this fragment */
+ dof_secidx_t dofd_links[1]; /* variable length array of indices */
+} dof_difohdr_t;
+
+typedef struct dof_relohdr {
+ dof_secidx_t dofr_strtab; /* link to DOF_SECT_STRTAB for names */
+ dof_secidx_t dofr_relsec; /* link to DOF_SECT_RELTAB for relos */
+ dof_secidx_t dofr_tgtsec; /* link to section we are relocating */
+} dof_relohdr_t;
+
+typedef struct dof_relodesc {
+ dof_stridx_t dofr_name; /* string name of relocation symbol */
+ uint32_t dofr_type; /* relo type (DOF_RELO_* constant) */
+ uint64_t dofr_offset; /* byte offset for relocation */
+ uint64_t dofr_data; /* additional type-specific data */
+} dof_relodesc_t;
+
+#define DOF_RELO_NONE 0 /* empty relocation entry */
+#define DOF_RELO_SETX 1 /* relocate setx value */
+
+typedef struct dof_optdesc {
+ uint32_t dofo_option; /* option identifier */
+ dof_secidx_t dofo_strtab; /* string table, if string option */
+ uint64_t dofo_value; /* option value or string index */
+} dof_optdesc_t;
+
+typedef uint32_t dof_attr_t; /* encoded stability attributes */
+
+#define DOF_ATTR(n, d, c) (((n) << 24) | ((d) << 16) | ((c) << 8))
+#define DOF_ATTR_NAME(a) (((a) >> 24) & 0xff)
+#define DOF_ATTR_DATA(a) (((a) >> 16) & 0xff)
+#define DOF_ATTR_CLASS(a) (((a) >> 8) & 0xff)
+
+typedef struct dof_provider {
+ dof_secidx_t dofpv_strtab; /* link to DOF_SECT_STRTAB section */
+ dof_secidx_t dofpv_probes; /* link to DOF_SECT_PROBES section */
+ dof_secidx_t dofpv_prargs; /* link to DOF_SECT_PRARGS section */
+ dof_secidx_t dofpv_proffs; /* link to DOF_SECT_PROFFS section */
+ dof_stridx_t dofpv_name; /* provider name string */
+ dof_attr_t dofpv_provattr; /* provider attributes */
+ dof_attr_t dofpv_modattr; /* module attributes */
+ dof_attr_t dofpv_funcattr; /* function attributes */
+ dof_attr_t dofpv_nameattr; /* name attributes */
+ dof_attr_t dofpv_argsattr; /* args attributes */
+ dof_secidx_t dofpv_prenoffs; /* link to DOF_SECT_PRENOFFS section */
+} dof_provider_t;
+
+typedef struct dof_probe {
+ uint64_t dofpr_addr; /* probe base address or offset */
+ dof_stridx_t dofpr_func; /* probe function string */
+ dof_stridx_t dofpr_name; /* probe name string */
+ dof_stridx_t dofpr_nargv; /* native argument type strings */
+ dof_stridx_t dofpr_xargv; /* translated argument type strings */
+ uint32_t dofpr_argidx; /* index of first argument mapping */
+ uint32_t dofpr_offidx; /* index of first offset entry */
+ uint8_t dofpr_nargc; /* native argument count */
+ uint8_t dofpr_xargc; /* translated argument count */
+ uint16_t dofpr_noffs; /* number of offset entries for probe */
+ uint32_t dofpr_enoffidx; /* index of first is-enabled offset */
+ uint16_t dofpr_nenoffs; /* number of is-enabled offsets */
+ uint16_t dofpr_pad1; /* reserved for future use */
+ uint32_t dofpr_pad2; /* reserved for future use */
+} dof_probe_t;
+
+typedef struct dof_xlator {
+ dof_secidx_t dofxl_members; /* link to DOF_SECT_XLMEMBERS section */
+ dof_secidx_t dofxl_strtab; /* link to DOF_SECT_STRTAB section */
+ dof_stridx_t dofxl_argv; /* input parameter type strings */
+ uint32_t dofxl_argc; /* input parameter list length */
+ dof_stridx_t dofxl_type; /* output type string name */
+ dof_attr_t dofxl_attr; /* output stability attributes */
+} dof_xlator_t;
+
+typedef struct dof_xlmember {
+ dof_secidx_t dofxm_difo; /* member link to DOF_SECT_DIFOHDR */
+ dof_stridx_t dofxm_name; /* member name */
+ dtrace_diftype_t dofxm_type; /* member type */
+} dof_xlmember_t;
+
+typedef struct dof_xlref {
+ dof_secidx_t dofxr_xlator; /* link to DOF_SECT_XLATORS section */
+ uint32_t dofxr_member; /* index of referenced dof_xlmember */
+ uint32_t dofxr_argn; /* index of argument for DIF_OP_XLARG */
+} dof_xlref_t;
+
+/*
+ * DTrace Intermediate Format Object (DIFO)
+ *
+ * A DIFO is used to store the compiled DIF for a D expression, its return
+ * type, and its string and variable tables. The string table is a single
+ * buffer of character data into which sets instructions and variable
+ * references can reference strings using a byte offset. The variable table
+ * is an array of dtrace_difv_t structures that describe the name and type of
+ * each variable and the id used in the DIF code. This structure is described
+ * above in the DIF section of this header file. The DIFO is used at both
+ * user-level (in the library) and in the kernel, but the structure is never
+ * passed between the two: the DOF structures form the only interface. As a
+ * result, the definition can change depending on the presence of _KERNEL.
+ */
+typedef struct dtrace_difo {
+ dif_instr_t *dtdo_buf; /* instruction buffer */
+ uint64_t *dtdo_inttab; /* integer table (optional) */
+ char *dtdo_strtab; /* string table (optional) */
+ dtrace_difv_t *dtdo_vartab; /* variable table (optional) */
+ uint_t dtdo_len; /* length of instruction buffer */
+ uint_t dtdo_intlen; /* length of integer table */
+ uint_t dtdo_strlen; /* length of string table */
+ uint_t dtdo_varlen; /* length of variable table */
+ dtrace_diftype_t dtdo_rtype; /* return type */
+ uint_t dtdo_refcnt; /* owner reference count */
+ uint_t dtdo_destructive; /* invokes destructive subroutines */
+#ifndef _KERNEL
+ dof_relodesc_t *dtdo_kreltab; /* kernel relocations */
+ dof_relodesc_t *dtdo_ureltab; /* user relocations */
+ struct dt_node **dtdo_xlmtab; /* translator references */
+ uint_t dtdo_krelen; /* length of krelo table */
+ uint_t dtdo_urelen; /* length of urelo table */
+ uint_t dtdo_xlmlen; /* length of translator table */
+#endif
+} dtrace_difo_t;
+
+/*
+ * DTrace Enabling Description Structures
+ *
+ * When DTrace is tracking the description of a DTrace enabling entity (probe,
+ * predicate, action, ECB, record, etc.), it does so in a description
+ * structure. These structures all end in "desc", and are used at both
+ * user-level and in the kernel -- but (with the exception of
+ * dtrace_probedesc_t) they are never passed between them. Typically,
+ * user-level will use the description structures when assembling an enabling.
+ * It will then distill those description structures into a DOF object (see
+ * above), and send it into the kernel. The kernel will again use the
+ * description structures to create a description of the enabling as it reads
+ * the DOF. When the description is complete, the enabling will be actually
+ * created -- turning it into the structures that represent the enabling
+ * instead of merely describing it. Not surprisingly, the description
+ * structures bear a strong resemblance to the DOF structures that act as their
+ * conduit.
+ */
+struct dtrace_predicate;
+
+typedef struct dtrace_probedesc {
+ dtrace_id_t dtpd_id; /* probe identifier */
+ char dtpd_provider[DTRACE_PROVNAMELEN]; /* probe provider name */
+ char dtpd_mod[DTRACE_MODNAMELEN]; /* probe module name */
+ char dtpd_func[DTRACE_FUNCNAMELEN]; /* probe function name */
+ char dtpd_name[DTRACE_NAMELEN]; /* probe name */
+} dtrace_probedesc_t;
+
+typedef struct dtrace_repldesc {
+ dtrace_probedesc_t dtrpd_match; /* probe descr. to match */
+ dtrace_probedesc_t dtrpd_create; /* probe descr. to create */
+} dtrace_repldesc_t;
+
+typedef struct dtrace_preddesc {
+ dtrace_difo_t *dtpdd_difo; /* pointer to DIF object */
+ struct dtrace_predicate *dtpdd_predicate; /* pointer to predicate */
+} dtrace_preddesc_t;
+
+typedef struct dtrace_actdesc {
+ dtrace_difo_t *dtad_difo; /* pointer to DIF object */
+ struct dtrace_actdesc *dtad_next; /* next action */
+ dtrace_actkind_t dtad_kind; /* kind of action */
+ uint32_t dtad_ntuple; /* number in tuple */
+ uint64_t dtad_arg; /* action argument */
+ uint64_t dtad_uarg; /* user argument */
+ int dtad_refcnt; /* reference count */
+} dtrace_actdesc_t;
+
+typedef struct dtrace_ecbdesc {
+ dtrace_actdesc_t *dted_action; /* action description(s) */
+ dtrace_preddesc_t dted_pred; /* predicate description */
+ dtrace_probedesc_t dted_probe; /* probe description */
+ uint64_t dted_uarg; /* library argument */
+ int dted_refcnt; /* reference count */
+} dtrace_ecbdesc_t;
+
+/*
+ * DTrace Metadata Description Structures
+ *
+ * DTrace separates the trace data stream from the metadata stream. The only
+ * metadata tokens placed in the data stream are enabled probe identifiers
+ * (EPIDs) or (in the case of aggregations) aggregation identifiers. In order
+ * to determine the structure of the data, DTrace consumers pass the token to
+ * the kernel, and receive in return a corresponding description of the enabled
+ * probe (via the dtrace_eprobedesc structure) or the aggregation (via the
+ * dtrace_aggdesc structure). Both of these structures are expressed in terms
+ * of record descriptions (via the dtrace_recdesc structure) that describe the
+ * exact structure of the data. Some record descriptions may also contain a
+ * format identifier; this additional bit of metadata can be retrieved from the
+ * kernel, for which a format description is returned via the dtrace_fmtdesc
+ * structure. Note that all four of these structures must be bitness-neutral
+ * to allow for a 32-bit DTrace consumer on a 64-bit kernel.
+ */
+typedef struct dtrace_recdesc {
+ dtrace_actkind_t dtrd_action; /* kind of action */
+ uint32_t dtrd_size; /* size of record */
+ uint32_t dtrd_offset; /* offset in ECB's data */
+ uint16_t dtrd_alignment; /* required alignment */
+ uint16_t dtrd_format; /* format, if any */
+ uint64_t dtrd_arg; /* action argument */
+ uint64_t dtrd_uarg; /* user argument */
+} dtrace_recdesc_t;
+
+typedef struct dtrace_eprobedesc {
+ dtrace_epid_t dtepd_epid; /* enabled probe ID */
+ dtrace_id_t dtepd_probeid; /* probe ID */
+ uint64_t dtepd_uarg; /* library argument */
+ uint32_t dtepd_size; /* total size */
+ int dtepd_nrecs; /* number of records */
+ dtrace_recdesc_t dtepd_rec[1]; /* records themselves */
+} dtrace_eprobedesc_t;
+
+typedef struct dtrace_aggdesc {
+ DTRACE_PTR(char, dtagd_name); /* not filled in by kernel */
+ dtrace_aggvarid_t dtagd_varid; /* not filled in by kernel */
+ int dtagd_flags; /* not filled in by kernel */
+ dtrace_aggid_t dtagd_id; /* aggregation ID */
+ dtrace_epid_t dtagd_epid; /* enabled probe ID */
+ uint32_t dtagd_size; /* size in bytes */
+ int dtagd_nrecs; /* number of records */
+ uint32_t dtagd_pad; /* explicit padding */
+ dtrace_recdesc_t dtagd_rec[1]; /* record descriptions */
+} dtrace_aggdesc_t;
+
+typedef struct dtrace_fmtdesc {
+ DTRACE_PTR(char, dtfd_string); /* format string */
+ int dtfd_length; /* length of format string */
+ uint16_t dtfd_format; /* format identifier */
+} dtrace_fmtdesc_t;
+
+#define DTRACE_SIZEOF_EPROBEDESC(desc) \
+ (sizeof (dtrace_eprobedesc_t) + ((desc)->dtepd_nrecs ? \
+ (((desc)->dtepd_nrecs - 1) * sizeof (dtrace_recdesc_t)) : 0))
+
+#define DTRACE_SIZEOF_AGGDESC(desc) \
+ (sizeof (dtrace_aggdesc_t) + ((desc)->dtagd_nrecs ? \
+ (((desc)->dtagd_nrecs - 1) * sizeof (dtrace_recdesc_t)) : 0))
+
+/*
+ * DTrace Option Interface
+ *
+ * Run-time DTrace options are set and retrieved via DOF_SECT_OPTDESC sections
+ * in a DOF image. The dof_optdesc structure contains an option identifier and
+ * an option value. The valid option identifiers are found below; the mapping
+ * between option identifiers and option identifying strings is maintained at
+ * user-level. Note that the value of DTRACEOPT_UNSET is such that all of the
+ * following are potentially valid option values: all positive integers, zero
+ * and negative one. Some options (notably "bufpolicy" and "bufresize") take
+ * predefined tokens as their values; these are defined with
+ * DTRACEOPT_{option}_{token}.
+ */
+#define DTRACEOPT_BUFSIZE 0 /* buffer size */
+#define DTRACEOPT_BUFPOLICY 1 /* buffer policy */
+#define DTRACEOPT_DYNVARSIZE 2 /* dynamic variable size */
+#define DTRACEOPT_AGGSIZE 3 /* aggregation size */
+#define DTRACEOPT_SPECSIZE 4 /* speculation size */
+#define DTRACEOPT_NSPEC 5 /* number of speculations */
+#define DTRACEOPT_STRSIZE 6 /* string size */
+#define DTRACEOPT_CLEANRATE 7 /* dynvar cleaning rate */
+#define DTRACEOPT_CPU 8 /* CPU to trace */
+#define DTRACEOPT_BUFRESIZE 9 /* buffer resizing policy */
+#define DTRACEOPT_GRABANON 10 /* grab anonymous state, if any */
+#define DTRACEOPT_FLOWINDENT 11 /* indent function entry/return */
+#define DTRACEOPT_QUIET 12 /* only output explicitly traced data */
+#define DTRACEOPT_STACKFRAMES 13 /* number of stack frames */
+#define DTRACEOPT_USTACKFRAMES 14 /* number of user stack frames */
+#define DTRACEOPT_AGGRATE 15 /* aggregation snapshot rate */
+#define DTRACEOPT_SWITCHRATE 16 /* buffer switching rate */
+#define DTRACEOPT_STATUSRATE 17 /* status rate */
+#define DTRACEOPT_DESTRUCTIVE 18 /* destructive actions allowed */
+#define DTRACEOPT_STACKINDENT 19 /* output indent for stack traces */
+#define DTRACEOPT_RAWBYTES 20 /* always print bytes in raw form */
+#define DTRACEOPT_JSTACKFRAMES 21 /* number of jstack() frames */
+#define DTRACEOPT_JSTACKSTRSIZE 22 /* size of jstack() string table */
+#define DTRACEOPT_AGGSORTKEY 23 /* sort aggregations by key */
+#define DTRACEOPT_AGGSORTREV 24 /* reverse-sort aggregations */
+#define DTRACEOPT_AGGSORTPOS 25 /* agg. position to sort on */
+#define DTRACEOPT_AGGSORTKEYPOS 26 /* agg. key position to sort on */
+#define DTRACEOPT_MAX 27 /* number of options */
+
+#define DTRACEOPT_UNSET (dtrace_optval_t)-2 /* unset option */
+
+#define DTRACEOPT_BUFPOLICY_RING 0 /* ring buffer */
+#define DTRACEOPT_BUFPOLICY_FILL 1 /* fill buffer, then stop */
+#define DTRACEOPT_BUFPOLICY_SWITCH 2 /* switch buffers */
+
+#define DTRACEOPT_BUFRESIZE_AUTO 0 /* automatic resizing */
+#define DTRACEOPT_BUFRESIZE_MANUAL 1 /* manual resizing */
+
+/*
+ * DTrace Buffer Interface
+ *
+ * In order to get a snapshot of the principal or aggregation buffer,
+ * user-level passes a buffer description to the kernel with the dtrace_bufdesc
+ * structure. This describes which CPU user-level is interested in, and
+ * where user-level wishes the kernel to snapshot the buffer to (the
+ * dtbd_data field). The kernel uses the same structure to pass back some
+ * information regarding the buffer: the size of data actually copied out, the
+ * number of drops, the number of errors, and the offset of the oldest record.
+ * If the buffer policy is a "switch" policy, taking a snapshot of the
+ * principal buffer has the additional effect of switching the active and
+ * inactive buffers. Taking a snapshot of the aggregation buffer _always_ has
+ * the additional effect of switching the active and inactive buffers.
+ */
+typedef struct dtrace_bufdesc {
+ uint64_t dtbd_size; /* size of buffer */
+ uint32_t dtbd_cpu; /* CPU or DTRACE_CPUALL */
+ uint32_t dtbd_errors; /* number of errors */
+ uint64_t dtbd_drops; /* number of drops */
+ DTRACE_PTR(char, dtbd_data); /* data */
+ uint64_t dtbd_oldest; /* offset of oldest record */
+} dtrace_bufdesc_t;
+
+/*
+ * DTrace Status
+ *
+ * The status of DTrace is relayed via the dtrace_status structure. This
+ * structure contains members to count drops other than the capacity drops
+ * available via the buffer interface (see above). This consists of dynamic
+ * drops (including capacity dynamic drops, rinsing drops and dirty drops), and
+ * speculative drops (including capacity speculative drops, drops due to busy
+ * speculative buffers and drops due to unavailable speculative buffers).
+ * Additionally, the status structure contains a field to indicate the number
+ * of "fill"-policy buffers have been filled and a boolean field to indicate
+ * that exit() has been called. If the dtst_exiting field is non-zero, no
+ * further data will be generated until tracing is stopped (at which time any
+ * enablings of the END action will be processed); if user-level sees that
+ * this field is non-zero, tracing should be stopped as soon as possible.
+ */
+typedef struct dtrace_status {
+ uint64_t dtst_dyndrops; /* dynamic drops */
+ uint64_t dtst_dyndrops_rinsing; /* dyn drops due to rinsing */
+ uint64_t dtst_dyndrops_dirty; /* dyn drops due to dirty */
+ uint64_t dtst_specdrops; /* speculative drops */
+ uint64_t dtst_specdrops_busy; /* spec drops due to busy */
+ uint64_t dtst_specdrops_unavail; /* spec drops due to unavail */
+ uint64_t dtst_errors; /* total errors */
+ uint64_t dtst_filled; /* number of filled bufs */
+ uint64_t dtst_stkstroverflows; /* stack string tab overflows */
+ uint64_t dtst_dblerrors; /* errors in ERROR probes */
+ char dtst_killed; /* non-zero if killed */
+ char dtst_exiting; /* non-zero if exit() called */
+ char dtst_pad[6]; /* pad out to 64-bit align */
+} dtrace_status_t;
+
+/*
+ * DTrace Configuration
+ *
+ * User-level may need to understand some elements of the kernel DTrace
+ * configuration in order to generate correct DIF. This information is
+ * conveyed via the dtrace_conf structure.
+ */
+typedef struct dtrace_conf {
+ uint_t dtc_difversion; /* supported DIF version */
+ uint_t dtc_difintregs; /* # of DIF integer registers */
+ uint_t dtc_diftupregs; /* # of DIF tuple registers */
+ uint_t dtc_ctfmodel; /* CTF data model */
+ uint_t dtc_pad[8]; /* reserved for future use */
+} dtrace_conf_t;
+
+/*
+ * DTrace Faults
+ *
+ * The constants below DTRACEFLT_LIBRARY indicate probe processing faults;
+ * constants at or above DTRACEFLT_LIBRARY indicate faults in probe
+ * postprocessing at user-level. Probe processing faults induce an ERROR
+ * probe and are replicated in unistd.d to allow users' ERROR probes to decode
+ * the error condition using thse symbolic labels.
+ */
+#define DTRACEFLT_UNKNOWN 0 /* Unknown fault */
+#define DTRACEFLT_BADADDR 1 /* Bad address */
+#define DTRACEFLT_BADALIGN 2 /* Bad alignment */
+#define DTRACEFLT_ILLOP 3 /* Illegal operation */
+#define DTRACEFLT_DIVZERO 4 /* Divide-by-zero */
+#define DTRACEFLT_NOSCRATCH 5 /* Out of scratch space */
+#define DTRACEFLT_KPRIV 6 /* Illegal kernel access */
+#define DTRACEFLT_UPRIV 7 /* Illegal user access */
+#define DTRACEFLT_TUPOFLOW 8 /* Tuple stack overflow */
+#define DTRACEFLT_BADSTACK 9 /* Bad stack */
+
+#define DTRACEFLT_LIBRARY 1000 /* Library-level fault */
+
+/*
+ * DTrace Argument Types
+ *
+ * Because it would waste both space and time, argument types do not reside
+ * with the probe. In order to determine argument types for args[X]
+ * variables, the D compiler queries for argument types on a probe-by-probe
+ * basis. (This optimizes for the common case that arguments are either not
+ * used or used in an untyped fashion.) Typed arguments are specified with a
+ * string of the type name in the dtragd_native member of the argument
+ * description structure. Typed arguments may be further translated to types
+ * of greater stability; the provider indicates such a translated argument by
+ * filling in the dtargd_xlate member with the string of the translated type.
+ * Finally, the provider may indicate which argument value a given argument
+ * maps to by setting the dtargd_mapping member -- allowing a single argument
+ * to map to multiple args[X] variables.
+ */
+typedef struct dtrace_argdesc {
+ dtrace_id_t dtargd_id; /* probe identifier */
+ int dtargd_ndx; /* arg number (-1 iff none) */
+ int dtargd_mapping; /* value mapping */
+ char dtargd_native[DTRACE_ARGTYPELEN]; /* native type name */
+ char dtargd_xlate[DTRACE_ARGTYPELEN]; /* translated type name */
+} dtrace_argdesc_t;
+
+/*
+ * DTrace Stability Attributes
+ *
+ * Each DTrace provider advertises the name and data stability of each of its
+ * probe description components, as well as its architectural dependencies.
+ * The D compiler can query the provider attributes (dtrace_pattr_t below) in
+ * order to compute the properties of an input program and report them.
+ */
+typedef uint8_t dtrace_stability_t; /* stability code (see attributes(5)) */
+typedef uint8_t dtrace_class_t; /* architectural dependency class */
+
+#define DTRACE_STABILITY_INTERNAL 0 /* private to DTrace itself */
+#define DTRACE_STABILITY_PRIVATE 1 /* private to Sun (see docs) */
+#define DTRACE_STABILITY_OBSOLETE 2 /* scheduled for removal */
+#define DTRACE_STABILITY_EXTERNAL 3 /* not controlled by Sun */
+#define DTRACE_STABILITY_UNSTABLE 4 /* new or rapidly changing */
+#define DTRACE_STABILITY_EVOLVING 5 /* less rapidly changing */
+#define DTRACE_STABILITY_STABLE 6 /* mature interface from Sun */
+#define DTRACE_STABILITY_STANDARD 7 /* industry standard */
+#define DTRACE_STABILITY_MAX 7 /* maximum valid stability */
+
+#define DTRACE_CLASS_UNKNOWN 0 /* unknown architectural dependency */
+#define DTRACE_CLASS_CPU 1 /* CPU-module-specific */
+#define DTRACE_CLASS_PLATFORM 2 /* platform-specific (uname -i) */
+#define DTRACE_CLASS_GROUP 3 /* hardware-group-specific (uname -m) */
+#define DTRACE_CLASS_ISA 4 /* ISA-specific (uname -p) */
+#define DTRACE_CLASS_COMMON 5 /* common to all systems */
+#define DTRACE_CLASS_MAX 5 /* maximum valid class */
+
+#define DTRACE_PRIV_NONE 0x0000
+#define DTRACE_PRIV_KERNEL 0x0001
+#define DTRACE_PRIV_USER 0x0002
+#define DTRACE_PRIV_PROC 0x0004
+#define DTRACE_PRIV_OWNER 0x0008
+#define DTRACE_PRIV_ZONEOWNER 0x0010
+
+#define DTRACE_PRIV_ALL \
+ (DTRACE_PRIV_KERNEL | DTRACE_PRIV_USER | \
+ DTRACE_PRIV_PROC | DTRACE_PRIV_OWNER | DTRACE_PRIV_ZONEOWNER)
+
+typedef struct dtrace_ppriv {
+ uint32_t dtpp_flags; /* privilege flags */
+ uid_t dtpp_uid; /* user ID */
+ zoneid_t dtpp_zoneid; /* zone ID */
+} dtrace_ppriv_t;
+
+typedef struct dtrace_attribute {
+ dtrace_stability_t dtat_name; /* entity name stability */
+ dtrace_stability_t dtat_data; /* entity data stability */
+ dtrace_class_t dtat_class; /* entity data dependency */
+} dtrace_attribute_t;
+
+typedef struct dtrace_pattr {
+ dtrace_attribute_t dtpa_provider; /* provider attributes */
+ dtrace_attribute_t dtpa_mod; /* module attributes */
+ dtrace_attribute_t dtpa_func; /* function attributes */
+ dtrace_attribute_t dtpa_name; /* name attributes */
+ dtrace_attribute_t dtpa_args; /* args[] attributes */
+} dtrace_pattr_t;
+
+typedef struct dtrace_providerdesc {
+ char dtvd_name[DTRACE_PROVNAMELEN]; /* provider name */
+ dtrace_pattr_t dtvd_attr; /* stability attributes */
+ dtrace_ppriv_t dtvd_priv; /* privileges required */
+} dtrace_providerdesc_t;
+
+/*
+ * DTrace Pseudodevice Interface
+ *
+ * DTrace is controlled through ioctl(2)'s to the in-kernel dtrace:dtrace
+ * pseudodevice driver. These ioctls comprise the user-kernel interface to
+ * DTrace.
+ */
+#if defined(sun)
+#define DTRACEIOC (('d' << 24) | ('t' << 16) | ('r' << 8))
+#define DTRACEIOC_PROVIDER (DTRACEIOC | 1) /* provider query */
+#define DTRACEIOC_PROBES (DTRACEIOC | 2) /* probe query */
+#define DTRACEIOC_BUFSNAP (DTRACEIOC | 4) /* snapshot buffer */
+#define DTRACEIOC_PROBEMATCH (DTRACEIOC | 5) /* match probes */
+#define DTRACEIOC_ENABLE (DTRACEIOC | 6) /* enable probes */
+#define DTRACEIOC_AGGSNAP (DTRACEIOC | 7) /* snapshot agg. */
+#define DTRACEIOC_EPROBE (DTRACEIOC | 8) /* get eprobe desc. */
+#define DTRACEIOC_PROBEARG (DTRACEIOC | 9) /* get probe arg */
+#define DTRACEIOC_CONF (DTRACEIOC | 10) /* get config. */
+#define DTRACEIOC_STATUS (DTRACEIOC | 11) /* get status */
+#define DTRACEIOC_GO (DTRACEIOC | 12) /* start tracing */
+#define DTRACEIOC_STOP (DTRACEIOC | 13) /* stop tracing */
+#define DTRACEIOC_AGGDESC (DTRACEIOC | 15) /* get agg. desc. */
+#define DTRACEIOC_FORMAT (DTRACEIOC | 16) /* get format str */
+#define DTRACEIOC_DOFGET (DTRACEIOC | 17) /* get DOF */
+#define DTRACEIOC_REPLICATE (DTRACEIOC | 18) /* replicate enab */
+#else
+#define DTRACEIOC_PROVIDER _IOWR('x',1,dtrace_providerdesc_t)
+ /* provider query */
+#define DTRACEIOC_PROBES _IOWR('x',2,dtrace_probedesc_t)
+ /* probe query */
+#define DTRACEIOC_BUFSNAP _IOW('x',4,dtrace_bufdesc_t *)
+ /* snapshot buffer */
+#define DTRACEIOC_PROBEMATCH _IOWR('x',5,dtrace_probedesc_t)
+ /* match probes */
+typedef struct {
+ void *dof; /* DOF userland address written to driver. */
+ int n_matched; /* # matches returned by driver. */
+} dtrace_enable_io_t;
+#define DTRACEIOC_ENABLE _IOWR('x',6,dtrace_enable_io_t)
+ /* enable probes */
+#define DTRACEIOC_AGGSNAP _IOW('x',7,dtrace_bufdesc_t *)
+ /* snapshot agg. */
+#define DTRACEIOC_EPROBE _IOW('x',8,dtrace_eprobedesc_t)
+ /* get eprobe desc. */
+#define DTRACEIOC_PROBEARG _IOWR('x',9,dtrace_argdesc_t)
+ /* get probe arg */
+#define DTRACEIOC_CONF _IOR('x',10,dtrace_conf_t)
+ /* get config. */
+#define DTRACEIOC_STATUS _IOR('x',11,dtrace_status_t)
+ /* get status */
+#define DTRACEIOC_GO _IOR('x',12,processorid_t)
+ /* start tracing */
+#define DTRACEIOC_STOP _IOWR('x',13,processorid_t)
+ /* stop tracing */
+#define DTRACEIOC_AGGDESC _IOW('x',15,dtrace_aggdesc_t *)
+ /* get agg. desc. */
+#define DTRACEIOC_FORMAT _IOWR('x',16,dtrace_fmtdesc_t)
+ /* get format str */
+#define DTRACEIOC_DOFGET _IOW('x',17,dof_hdr_t *)
+ /* get DOF */
+#define DTRACEIOC_REPLICATE _IOW('x',18,dtrace_repldesc_t)
+ /* replicate enab */
+#endif
+
+/*
+ * DTrace Helpers
+ *
+ * In general, DTrace establishes probes in processes and takes actions on
+ * processes without knowing their specific user-level structures. Instead of
+ * existing in the framework, process-specific knowledge is contained by the
+ * enabling D program -- which can apply process-specific knowledge by making
+ * appropriate use of DTrace primitives like copyin() and copyinstr() to
+ * operate on user-level data. However, there may exist some specific probes
+ * of particular semantic relevance that the application developer may wish to
+ * explicitly export. For example, an application may wish to export a probe
+ * at the point that it begins and ends certain well-defined transactions. In
+ * addition to providing probes, programs may wish to offer assistance for
+ * certain actions. For example, in highly dynamic environments (e.g., Java),
+ * it may be difficult to obtain a stack trace in terms of meaningful symbol
+ * names (the translation from instruction addresses to corresponding symbol
+ * names may only be possible in situ); these environments may wish to define
+ * a series of actions to be applied in situ to obtain a meaningful stack
+ * trace.
+ *
+ * These two mechanisms -- user-level statically defined tracing and assisting
+ * DTrace actions -- are provided via DTrace _helpers_. Helpers are specified
+ * via DOF, but unlike enabling DOF, helper DOF may contain definitions of
+ * providers, probes and their arguments. If a helper wishes to provide
+ * action assistance, probe descriptions and corresponding DIF actions may be
+ * specified in the helper DOF. For such helper actions, however, the probe
+ * description describes the specific helper: all DTrace helpers have the
+ * provider name "dtrace" and the module name "helper", and the name of the
+ * helper is contained in the function name (for example, the ustack() helper
+ * is named "ustack"). Any helper-specific name may be contained in the name
+ * (for example, if a helper were to have a constructor, it might be named
+ * "dtrace:helper:<helper>:init"). Helper actions are only called when the
+ * action that they are helping is taken. Helper actions may only return DIF
+ * expressions, and may only call the following subroutines:
+ *
+ * alloca() <= Allocates memory out of the consumer's scratch space
+ * bcopy() <= Copies memory to scratch space
+ * copyin() <= Copies memory from user-level into consumer's scratch
+ * copyinto() <= Copies memory into a specific location in scratch
+ * copyinstr() <= Copies a string into a specific location in scratch
+ *
+ * Helper actions may only access the following built-in variables:
+ *
+ * curthread <= Current kthread_t pointer
+ * tid <= Current thread identifier
+ * pid <= Current process identifier
+ * ppid <= Parent process identifier
+ * uid <= Current user ID
+ * gid <= Current group ID
+ * execname <= Current executable name
+ * zonename <= Current zone name
+ *
+ * Helper actions may not manipulate or allocate dynamic variables, but they
+ * may have clause-local and statically-allocated global variables. The
+ * helper action variable state is specific to the helper action -- variables
+ * used by the helper action may not be accessed outside of the helper
+ * action, and the helper action may not access variables that like outside
+ * of it. Helper actions may not load from kernel memory at-large; they are
+ * restricting to loading current user state (via copyin() and variants) and
+ * scratch space. As with probe enablings, helper actions are executed in
+ * program order. The result of the helper action is the result of the last
+ * executing helper expression.
+ *
+ * Helpers -- composed of either providers/probes or probes/actions (or both)
+ * -- are added by opening the "helper" minor node, and issuing an ioctl(2)
+ * (DTRACEHIOC_ADDDOF) that specifies the dof_helper_t structure. This
+ * encapsulates the name and base address of the user-level library or
+ * executable publishing the helpers and probes as well as the DOF that
+ * contains the definitions of those helpers and probes.
+ *
+ * The DTRACEHIOC_ADD and DTRACEHIOC_REMOVE are left in place for legacy
+ * helpers and should no longer be used. No other ioctls are valid on the
+ * helper minor node.
+ */
+#define DTRACEHIOC (('d' << 24) | ('t' << 16) | ('h' << 8))
+#define DTRACEHIOC_ADD (DTRACEHIOC | 1) /* add helper */
+#define DTRACEHIOC_REMOVE (DTRACEHIOC | 2) /* remove helper */
+#define DTRACEHIOC_ADDDOF (DTRACEHIOC | 3) /* add helper DOF */
+
+typedef struct dof_helper {
+ char dofhp_mod[DTRACE_MODNAMELEN]; /* executable or library name */
+ uint64_t dofhp_addr; /* base address of object */
+ uint64_t dofhp_dof; /* address of helper DOF */
+} dof_helper_t;
+
+#define DTRACEMNR_DTRACE "dtrace" /* node for DTrace ops */
+#define DTRACEMNR_HELPER "helper" /* node for helpers */
+#define DTRACEMNRN_DTRACE 0 /* minor for DTrace ops */
+#define DTRACEMNRN_HELPER 1 /* minor for helpers */
+#define DTRACEMNRN_CLONE 2 /* first clone minor */
+
+#ifdef _KERNEL
+
+/*
+ * DTrace Provider API
+ *
+ * The following functions are implemented by the DTrace framework and are
+ * used to implement separate in-kernel DTrace providers. Common functions
+ * are provided in uts/common/os/dtrace.c. ISA-dependent subroutines are
+ * defined in uts/<isa>/dtrace/dtrace_asm.s or uts/<isa>/dtrace/dtrace_isa.c.
+ *
+ * The provider API has two halves: the API that the providers consume from
+ * DTrace, and the API that providers make available to DTrace.
+ *
+ * 1 Framework-to-Provider API
+ *
+ * 1.1 Overview
+ *
+ * The Framework-to-Provider API is represented by the dtrace_pops structure
+ * that the provider passes to the framework when registering itself. This
+ * structure consists of the following members:
+ *
+ * dtps_provide() <-- Provide all probes, all modules
+ * dtps_provide_module() <-- Provide all probes in specified module
+ * dtps_enable() <-- Enable specified probe
+ * dtps_disable() <-- Disable specified probe
+ * dtps_suspend() <-- Suspend specified probe
+ * dtps_resume() <-- Resume specified probe
+ * dtps_getargdesc() <-- Get the argument description for args[X]
+ * dtps_getargval() <-- Get the value for an argX or args[X] variable
+ * dtps_usermode() <-- Find out if the probe was fired in user mode
+ * dtps_destroy() <-- Destroy all state associated with this probe
+ *
+ * 1.2 void dtps_provide(void *arg, const dtrace_probedesc_t *spec)
+ *
+ * 1.2.1 Overview
+ *
+ * Called to indicate that the provider should provide all probes. If the
+ * specified description is non-NULL, dtps_provide() is being called because
+ * no probe matched a specified probe -- if the provider has the ability to
+ * create custom probes, it may wish to create a probe that matches the
+ * specified description.
+ *
+ * 1.2.2 Arguments and notes
+ *
+ * The first argument is the cookie as passed to dtrace_register(). The
+ * second argument is a pointer to a probe description that the provider may
+ * wish to consider when creating custom probes. The provider is expected to
+ * call back into the DTrace framework via dtrace_probe_create() to create
+ * any necessary probes. dtps_provide() may be called even if the provider
+ * has made available all probes; the provider should check the return value
+ * of dtrace_probe_create() to handle this case. Note that the provider need
+ * not implement both dtps_provide() and dtps_provide_module(); see
+ * "Arguments and Notes" for dtrace_register(), below.
+ *
+ * 1.2.3 Return value
+ *
+ * None.
+ *
+ * 1.2.4 Caller's context
+ *
+ * dtps_provide() is typically called from open() or ioctl() context, but may
+ * be called from other contexts as well. The DTrace framework is locked in
+ * such a way that providers may not register or unregister. This means that
+ * the provider may not call any DTrace API that affects its registration with
+ * the framework, including dtrace_register(), dtrace_unregister(),
+ * dtrace_invalidate(), and dtrace_condense(). However, the context is such
+ * that the provider may (and indeed, is expected to) call probe-related
+ * DTrace routines, including dtrace_probe_create(), dtrace_probe_lookup(),
+ * and dtrace_probe_arg().
+ *
+ * 1.3 void dtps_provide_module(void *arg, modctl_t *mp)
+ *
+ * 1.3.1 Overview
+ *
+ * Called to indicate that the provider should provide all probes in the
+ * specified module.
+ *
+ * 1.3.2 Arguments and notes
+ *
+ * The first argument is the cookie as passed to dtrace_register(). The
+ * second argument is a pointer to a modctl structure that indicates the
+ * module for which probes should be created.
+ *
+ * 1.3.3 Return value
+ *
+ * None.
+ *
+ * 1.3.4 Caller's context
+ *
+ * dtps_provide_module() may be called from open() or ioctl() context, but
+ * may also be called from a module loading context. mod_lock is held, and
+ * the DTrace framework is locked in such a way that providers may not
+ * register or unregister. This means that the provider may not call any
+ * DTrace API that affects its registration with the framework, including
+ * dtrace_register(), dtrace_unregister(), dtrace_invalidate(), and
+ * dtrace_condense(). However, the context is such that the provider may (and
+ * indeed, is expected to) call probe-related DTrace routines, including
+ * dtrace_probe_create(), dtrace_probe_lookup(), and dtrace_probe_arg(). Note
+ * that the provider need not implement both dtps_provide() and
+ * dtps_provide_module(); see "Arguments and Notes" for dtrace_register(),
+ * below.
+ *
+ * 1.4 void dtps_enable(void *arg, dtrace_id_t id, void *parg)
+ *
+ * 1.4.1 Overview
+ *
+ * Called to enable the specified probe.
+ *
+ * 1.4.2 Arguments and notes
+ *
+ * The first argument is the cookie as passed to dtrace_register(). The
+ * second argument is the identifier of the probe to be enabled. The third
+ * argument is the probe argument as passed to dtrace_probe_create().
+ * dtps_enable() will be called when a probe transitions from not being
+ * enabled at all to having one or more ECB. The number of ECBs associated
+ * with the probe may change without subsequent calls into the provider.
+ * When the number of ECBs drops to zero, the provider will be explicitly
+ * told to disable the probe via dtps_disable(). dtrace_probe() should never
+ * be called for a probe identifier that hasn't been explicitly enabled via
+ * dtps_enable().
+ *
+ * 1.4.3 Return value
+ *
+ * None.
+ *
+ * 1.4.4 Caller's context
+ *
+ * The DTrace framework is locked in such a way that it may not be called
+ * back into at all. cpu_lock is held. mod_lock is not held and may not
+ * be acquired.
+ *
+ * 1.5 void dtps_disable(void *arg, dtrace_id_t id, void *parg)
+ *
+ * 1.5.1 Overview
+ *
+ * Called to disable the specified probe.
+ *
+ * 1.5.2 Arguments and notes
+ *
+ * The first argument is the cookie as passed to dtrace_register(). The
+ * second argument is the identifier of the probe to be disabled. The third
+ * argument is the probe argument as passed to dtrace_probe_create().
+ * dtps_disable() will be called when a probe transitions from being enabled
+ * to having zero ECBs. dtrace_probe() should never be called for a probe
+ * identifier that has been explicitly enabled via dtps_disable().
+ *
+ * 1.5.3 Return value
+ *
+ * None.
+ *
+ * 1.5.4 Caller's context
+ *
+ * The DTrace framework is locked in such a way that it may not be called
+ * back into at all. cpu_lock is held. mod_lock is not held and may not
+ * be acquired.
+ *
+ * 1.6 void dtps_suspend(void *arg, dtrace_id_t id, void *parg)
+ *
+ * 1.6.1 Overview
+ *
+ * Called to suspend the specified enabled probe. This entry point is for
+ * providers that may need to suspend some or all of their probes when CPUs
+ * are being powered on or when the boot monitor is being entered for a
+ * prolonged period of time.
+ *
+ * 1.6.2 Arguments and notes
+ *
+ * The first argument is the cookie as passed to dtrace_register(). The
+ * second argument is the identifier of the probe to be suspended. The
+ * third argument is the probe argument as passed to dtrace_probe_create().
+ * dtps_suspend will only be called on an enabled probe. Providers that
+ * provide a dtps_suspend entry point will want to take roughly the action
+ * that it takes for dtps_disable.
+ *
+ * 1.6.3 Return value
+ *
+ * None.
+ *
+ * 1.6.4 Caller's context
+ *
+ * Interrupts are disabled. The DTrace framework is in a state such that the
+ * specified probe cannot be disabled or destroyed for the duration of
+ * dtps_suspend(). As interrupts are disabled, the provider is afforded
+ * little latitude; the provider is expected to do no more than a store to
+ * memory.
+ *
+ * 1.7 void dtps_resume(void *arg, dtrace_id_t id, void *parg)
+ *
+ * 1.7.1 Overview
+ *
+ * Called to resume the specified enabled probe. This entry point is for
+ * providers that may need to resume some or all of their probes after the
+ * completion of an event that induced a call to dtps_suspend().
+ *
+ * 1.7.2 Arguments and notes
+ *
+ * The first argument is the cookie as passed to dtrace_register(). The
+ * second argument is the identifier of the probe to be resumed. The
+ * third argument is the probe argument as passed to dtrace_probe_create().
+ * dtps_resume will only be called on an enabled probe. Providers that
+ * provide a dtps_resume entry point will want to take roughly the action
+ * that it takes for dtps_enable.
+ *
+ * 1.7.3 Return value
+ *
+ * None.
+ *
+ * 1.7.4 Caller's context
+ *
+ * Interrupts are disabled. The DTrace framework is in a state such that the
+ * specified probe cannot be disabled or destroyed for the duration of
+ * dtps_resume(). As interrupts are disabled, the provider is afforded
+ * little latitude; the provider is expected to do no more than a store to
+ * memory.
+ *
+ * 1.8 void dtps_getargdesc(void *arg, dtrace_id_t id, void *parg,
+ * dtrace_argdesc_t *desc)
+ *
+ * 1.8.1 Overview
+ *
+ * Called to retrieve the argument description for an args[X] variable.
+ *
+ * 1.8.2 Arguments and notes
+ *
+ * The first argument is the cookie as passed to dtrace_register(). The
+ * second argument is the identifier of the current probe. The third
+ * argument is the probe argument as passed to dtrace_probe_create(). The
+ * fourth argument is a pointer to the argument description. This
+ * description is both an input and output parameter: it contains the
+ * index of the desired argument in the dtargd_ndx field, and expects
+ * the other fields to be filled in upon return. If there is no argument
+ * corresponding to the specified index, the dtargd_ndx field should be set
+ * to DTRACE_ARGNONE.
+ *
+ * 1.8.3 Return value
+ *
+ * None. The dtargd_ndx, dtargd_native, dtargd_xlate and dtargd_mapping
+ * members of the dtrace_argdesc_t structure are all output values.
+ *
+ * 1.8.4 Caller's context
+ *
+ * dtps_getargdesc() is called from ioctl() context. mod_lock is held, and
+ * the DTrace framework is locked in such a way that providers may not
+ * register or unregister. This means that the provider may not call any
+ * DTrace API that affects its registration with the framework, including
+ * dtrace_register(), dtrace_unregister(), dtrace_invalidate(), and
+ * dtrace_condense().
+ *
+ * 1.9 uint64_t dtps_getargval(void *arg, dtrace_id_t id, void *parg,
+ * int argno, int aframes)
+ *
+ * 1.9.1 Overview
+ *
+ * Called to retrieve a value for an argX or args[X] variable.
+ *
+ * 1.9.2 Arguments and notes
+ *
+ * The first argument is the cookie as passed to dtrace_register(). The
+ * second argument is the identifier of the current probe. The third
+ * argument is the probe argument as passed to dtrace_probe_create(). The
+ * fourth argument is the number of the argument (the X in the example in
+ * 1.9.1). The fifth argument is the number of stack frames that were used
+ * to get from the actual place in the code that fired the probe to
+ * dtrace_probe() itself, the so-called artificial frames. This argument may
+ * be used to descend an appropriate number of frames to find the correct
+ * values. If this entry point is left NULL, the dtrace_getarg() built-in
+ * function is used.
+ *
+ * 1.9.3 Return value
+ *
+ * The value of the argument.
+ *
+ * 1.9.4 Caller's context
+ *
+ * This is called from within dtrace_probe() meaning that interrupts
+ * are disabled. No locks should be taken within this entry point.
+ *
+ * 1.10 int dtps_usermode(void *arg, dtrace_id_t id, void *parg)
+ *
+ * 1.10.1 Overview
+ *
+ * Called to determine if the probe was fired in a user context.
+ *
+ * 1.10.2 Arguments and notes
+ *
+ * The first argument is the cookie as passed to dtrace_register(). The
+ * second argument is the identifier of the current probe. The third
+ * argument is the probe argument as passed to dtrace_probe_create(). This
+ * entry point must not be left NULL for providers whose probes allow for
+ * mixed mode tracing, that is to say those probes that can fire during
+ * kernel- _or_ user-mode execution
+ *
+ * 1.10.3 Return value
+ *
+ * A boolean value.
+ *
+ * 1.10.4 Caller's context
+ *
+ * This is called from within dtrace_probe() meaning that interrupts
+ * are disabled. No locks should be taken within this entry point.
+ *
+ * 1.11 void dtps_destroy(void *arg, dtrace_id_t id, void *parg)
+ *
+ * 1.11.1 Overview
+ *
+ * Called to destroy the specified probe.
+ *
+ * 1.11.2 Arguments and notes
+ *
+ * The first argument is the cookie as passed to dtrace_register(). The
+ * second argument is the identifier of the probe to be destroyed. The third
+ * argument is the probe argument as passed to dtrace_probe_create(). The
+ * provider should free all state associated with the probe. The framework
+ * guarantees that dtps_destroy() is only called for probes that have either
+ * been disabled via dtps_disable() or were never enabled via dtps_enable().
+ * Once dtps_disable() has been called for a probe, no further call will be
+ * made specifying the probe.
+ *
+ * 1.11.3 Return value
+ *
+ * None.
+ *
+ * 1.11.4 Caller's context
+ *
+ * The DTrace framework is locked in such a way that it may not be called
+ * back into at all. mod_lock is held. cpu_lock is not held, and may not be
+ * acquired.
+ *
+ *
+ * 2 Provider-to-Framework API
+ *
+ * 2.1 Overview
+ *
+ * The Provider-to-Framework API provides the mechanism for the provider to
+ * register itself with the DTrace framework, to create probes, to lookup
+ * probes and (most importantly) to fire probes. The Provider-to-Framework
+ * consists of:
+ *
+ * dtrace_register() <-- Register a provider with the DTrace framework
+ * dtrace_unregister() <-- Remove a provider's DTrace registration
+ * dtrace_invalidate() <-- Invalidate the specified provider
+ * dtrace_condense() <-- Remove a provider's unenabled probes
+ * dtrace_attached() <-- Indicates whether or not DTrace has attached
+ * dtrace_probe_create() <-- Create a DTrace probe
+ * dtrace_probe_lookup() <-- Lookup a DTrace probe based on its name
+ * dtrace_probe_arg() <-- Return the probe argument for a specific probe
+ * dtrace_probe() <-- Fire the specified probe
+ *
+ * 2.2 int dtrace_register(const char *name, const dtrace_pattr_t *pap,
+ * uint32_t priv, cred_t *cr, const dtrace_pops_t *pops, void *arg,
+ * dtrace_provider_id_t *idp)
+ *
+ * 2.2.1 Overview
+ *
+ * dtrace_register() registers the calling provider with the DTrace
+ * framework. It should generally be called by DTrace providers in their
+ * attach(9E) entry point.
+ *
+ * 2.2.2 Arguments and Notes
+ *
+ * The first argument is the name of the provider. The second argument is a
+ * pointer to the stability attributes for the provider. The third argument
+ * is the privilege flags for the provider, and must be some combination of:
+ *
+ * DTRACE_PRIV_NONE <= All users may enable probes from this provider
+ *
+ * DTRACE_PRIV_PROC <= Any user with privilege of PRIV_DTRACE_PROC may
+ * enable probes from this provider
+ *
+ * DTRACE_PRIV_USER <= Any user with privilege of PRIV_DTRACE_USER may
+ * enable probes from this provider
+ *
+ * DTRACE_PRIV_KERNEL <= Any user with privilege of PRIV_DTRACE_KERNEL
+ * may enable probes from this provider
+ *
+ * DTRACE_PRIV_OWNER <= This flag places an additional constraint on
+ * the privilege requirements above. These probes
+ * require either (a) a user ID matching the user
+ * ID of the cred passed in the fourth argument
+ * or (b) the PRIV_PROC_OWNER privilege.
+ *
+ * DTRACE_PRIV_ZONEOWNER<= This flag places an additional constraint on
+ * the privilege requirements above. These probes
+ * require either (a) a zone ID matching the zone
+ * ID of the cred passed in the fourth argument
+ * or (b) the PRIV_PROC_ZONE privilege.
+ *
+ * Note that these flags designate the _visibility_ of the probes, not
+ * the conditions under which they may or may not fire.
+ *
+ * The fourth argument is the credential that is associated with the
+ * provider. This argument should be NULL if the privilege flags don't
+ * include DTRACE_PRIV_OWNER or DTRACE_PRIV_ZONEOWNER. If non-NULL, the
+ * framework stashes the uid and zoneid represented by this credential
+ * for use at probe-time, in implicit predicates. These limit visibility
+ * of the probes to users and/or zones which have sufficient privilege to
+ * access them.
+ *
+ * The fifth argument is a DTrace provider operations vector, which provides
+ * the implementation for the Framework-to-Provider API. (See Section 1,
+ * above.) This must be non-NULL, and each member must be non-NULL. The
+ * exceptions to this are (1) the dtps_provide() and dtps_provide_module()
+ * members (if the provider so desires, _one_ of these members may be left
+ * NULL -- denoting that the provider only implements the other) and (2)
+ * the dtps_suspend() and dtps_resume() members, which must either both be
+ * NULL or both be non-NULL.
+ *
+ * The sixth argument is a cookie to be specified as the first argument for
+ * each function in the Framework-to-Provider API. This argument may have
+ * any value.
+ *
+ * The final argument is a pointer to dtrace_provider_id_t. If
+ * dtrace_register() successfully completes, the provider identifier will be
+ * stored in the memory pointed to be this argument. This argument must be
+ * non-NULL.
+ *
+ * 2.2.3 Return value
+ *
+ * On success, dtrace_register() returns 0 and stores the new provider's
+ * identifier into the memory pointed to by the idp argument. On failure,
+ * dtrace_register() returns an errno:
+ *
+ * EINVAL The arguments passed to dtrace_register() were somehow invalid.
+ * This may because a parameter that must be non-NULL was NULL,
+ * because the name was invalid (either empty or an illegal
+ * provider name) or because the attributes were invalid.
+ *
+ * No other failure code is returned.
+ *
+ * 2.2.4 Caller's context
+ *
+ * dtrace_register() may induce calls to dtrace_provide(); the provider must
+ * hold no locks across dtrace_register() that may also be acquired by
+ * dtrace_provide(). cpu_lock and mod_lock must not be held.
+ *
+ * 2.3 int dtrace_unregister(dtrace_provider_t id)
+ *
+ * 2.3.1 Overview
+ *
+ * Unregisters the specified provider from the DTrace framework. It should
+ * generally be called by DTrace providers in their detach(9E) entry point.
+ *
+ * 2.3.2 Arguments and Notes
+ *
+ * The only argument is the provider identifier, as returned from a
+ * successful call to dtrace_register(). As a result of calling
+ * dtrace_unregister(), the DTrace framework will call back into the provider
+ * via the dtps_destroy() entry point. Once dtrace_unregister() successfully
+ * completes, however, the DTrace framework will no longer make calls through
+ * the Framework-to-Provider API.
+ *
+ * 2.3.3 Return value
+ *
+ * On success, dtrace_unregister returns 0. On failure, dtrace_unregister()
+ * returns an errno:
+ *
+ * EBUSY There are currently processes that have the DTrace pseudodevice
+ * open, or there exists an anonymous enabling that hasn't yet
+ * been claimed.
+ *
+ * No other failure code is returned.
+ *
+ * 2.3.4 Caller's context
+ *
+ * Because a call to dtrace_unregister() may induce calls through the
+ * Framework-to-Provider API, the caller may not hold any lock across
+ * dtrace_register() that is also acquired in any of the Framework-to-
+ * Provider API functions. Additionally, mod_lock may not be held.
+ *
+ * 2.4 void dtrace_invalidate(dtrace_provider_id_t id)
+ *
+ * 2.4.1 Overview
+ *
+ * Invalidates the specified provider. All subsequent probe lookups for the
+ * specified provider will fail, but its probes will not be removed.
+ *
+ * 2.4.2 Arguments and note
+ *
+ * The only argument is the provider identifier, as returned from a
+ * successful call to dtrace_register(). In general, a provider's probes
+ * always remain valid; dtrace_invalidate() is a mechanism for invalidating
+ * an entire provider, regardless of whether or not probes are enabled or
+ * not. Note that dtrace_invalidate() will _not_ prevent already enabled
+ * probes from firing -- it will merely prevent any new enablings of the
+ * provider's probes.
+ *
+ * 2.5 int dtrace_condense(dtrace_provider_id_t id)
+ *
+ * 2.5.1 Overview
+ *
+ * Removes all the unenabled probes for the given provider. This function is
+ * not unlike dtrace_unregister(), except that it doesn't remove the
+ * provider just as many of its associated probes as it can.
+ *
+ * 2.5.2 Arguments and Notes
+ *
+ * As with dtrace_unregister(), the sole argument is the provider identifier
+ * as returned from a successful call to dtrace_register(). As a result of
+ * calling dtrace_condense(), the DTrace framework will call back into the
+ * given provider's dtps_destroy() entry point for each of the provider's
+ * unenabled probes.
+ *
+ * 2.5.3 Return value
+ *
+ * Currently, dtrace_condense() always returns 0. However, consumers of this
+ * function should check the return value as appropriate; its behavior may
+ * change in the future.
+ *
+ * 2.5.4 Caller's context
+ *
+ * As with dtrace_unregister(), the caller may not hold any lock across
+ * dtrace_condense() that is also acquired in the provider's entry points.
+ * Also, mod_lock may not be held.
+ *
+ * 2.6 int dtrace_attached()
+ *
+ * 2.6.1 Overview
+ *
+ * Indicates whether or not DTrace has attached.
+ *
+ * 2.6.2 Arguments and Notes
+ *
+ * For most providers, DTrace makes initial contact beyond registration.
+ * That is, once a provider has registered with DTrace, it waits to hear
+ * from DTrace to create probes. However, some providers may wish to
+ * proactively create probes without first being told by DTrace to do so.
+ * If providers wish to do this, they must first call dtrace_attached() to
+ * determine if DTrace itself has attached. If dtrace_attached() returns 0,
+ * the provider must not make any other Provider-to-Framework API call.
+ *
+ * 2.6.3 Return value
+ *
+ * dtrace_attached() returns 1 if DTrace has attached, 0 otherwise.
+ *
+ * 2.7 int dtrace_probe_create(dtrace_provider_t id, const char *mod,
+ * const char *func, const char *name, int aframes, void *arg)
+ *
+ * 2.7.1 Overview
+ *
+ * Creates a probe with specified module name, function name, and name.
+ *
+ * 2.7.2 Arguments and Notes
+ *
+ * The first argument is the provider identifier, as returned from a
+ * successful call to dtrace_register(). The second, third, and fourth
+ * arguments are the module name, function name, and probe name,
+ * respectively. Of these, module name and function name may both be NULL
+ * (in which case the probe is considered to be unanchored), or they may both
+ * be non-NULL. The name must be non-NULL, and must point to a non-empty
+ * string.
+ *
+ * The fifth argument is the number of artificial stack frames that will be
+ * found on the stack when dtrace_probe() is called for the new probe. These
+ * artificial frames will be automatically be pruned should the stack() or
+ * stackdepth() functions be called as part of one of the probe's ECBs. If
+ * the parameter doesn't add an artificial frame, this parameter should be
+ * zero.
+ *
+ * The final argument is a probe argument that will be passed back to the
+ * provider when a probe-specific operation is called. (e.g., via
+ * dtps_enable(), dtps_disable(), etc.)
+ *
+ * Note that it is up to the provider to be sure that the probe that it
+ * creates does not already exist -- if the provider is unsure of the probe's
+ * existence, it should assure its absence with dtrace_probe_lookup() before
+ * calling dtrace_probe_create().
+ *
+ * 2.7.3 Return value
+ *
+ * dtrace_probe_create() always succeeds, and always returns the identifier
+ * of the newly-created probe.
+ *
+ * 2.7.4 Caller's context
+ *
+ * While dtrace_probe_create() is generally expected to be called from
+ * dtps_provide() and/or dtps_provide_module(), it may be called from other
+ * non-DTrace contexts. Neither cpu_lock nor mod_lock may be held.
+ *
+ * 2.8 dtrace_id_t dtrace_probe_lookup(dtrace_provider_t id, const char *mod,
+ * const char *func, const char *name)
+ *
+ * 2.8.1 Overview
+ *
+ * Looks up a probe based on provdider and one or more of module name,
+ * function name and probe name.
+ *
+ * 2.8.2 Arguments and Notes
+ *
+ * The first argument is the provider identifier, as returned from a
+ * successful call to dtrace_register(). The second, third, and fourth
+ * arguments are the module name, function name, and probe name,
+ * respectively. Any of these may be NULL; dtrace_probe_lookup() will return
+ * the identifier of the first probe that is provided by the specified
+ * provider and matches all of the non-NULL matching criteria.
+ * dtrace_probe_lookup() is generally used by a provider to be check the
+ * existence of a probe before creating it with dtrace_probe_create().
+ *
+ * 2.8.3 Return value
+ *
+ * If the probe exists, returns its identifier. If the probe does not exist,
+ * return DTRACE_IDNONE.
+ *
+ * 2.8.4 Caller's context
+ *
+ * While dtrace_probe_lookup() is generally expected to be called from
+ * dtps_provide() and/or dtps_provide_module(), it may also be called from
+ * other non-DTrace contexts. Neither cpu_lock nor mod_lock may be held.
+ *
+ * 2.9 void *dtrace_probe_arg(dtrace_provider_t id, dtrace_id_t probe)
+ *
+ * 2.9.1 Overview
+ *
+ * Returns the probe argument associated with the specified probe.
+ *
+ * 2.9.2 Arguments and Notes
+ *
+ * The first argument is the provider identifier, as returned from a
+ * successful call to dtrace_register(). The second argument is a probe
+ * identifier, as returned from dtrace_probe_lookup() or
+ * dtrace_probe_create(). This is useful if a probe has multiple
+ * provider-specific components to it: the provider can create the probe
+ * once with provider-specific state, and then add to the state by looking
+ * up the probe based on probe identifier.
+ *
+ * 2.9.3 Return value
+ *
+ * Returns the argument associated with the specified probe. If the
+ * specified probe does not exist, or if the specified probe is not provided
+ * by the specified provider, NULL is returned.
+ *
+ * 2.9.4 Caller's context
+ *
+ * While dtrace_probe_arg() is generally expected to be called from
+ * dtps_provide() and/or dtps_provide_module(), it may also be called from
+ * other non-DTrace contexts. Neither cpu_lock nor mod_lock may be held.
+ *
+ * 2.10 void dtrace_probe(dtrace_id_t probe, uintptr_t arg0, uintptr_t arg1,
+ * uintptr_t arg2, uintptr_t arg3, uintptr_t arg4)
+ *
+ * 2.10.1 Overview
+ *
+ * The epicenter of DTrace: fires the specified probes with the specified
+ * arguments.
+ *
+ * 2.10.2 Arguments and Notes
+ *
+ * The first argument is a probe identifier as returned by
+ * dtrace_probe_create() or dtrace_probe_lookup(). The second through sixth
+ * arguments are the values to which the D variables "arg0" through "arg4"
+ * will be mapped.
+ *
+ * dtrace_probe() should be called whenever the specified probe has fired --
+ * however the provider defines it.
+ *
+ * 2.10.3 Return value
+ *
+ * None.
+ *
+ * 2.10.4 Caller's context
+ *
+ * dtrace_probe() may be called in virtually any context: kernel, user,
+ * interrupt, high-level interrupt, with arbitrary adaptive locks held, with
+ * dispatcher locks held, with interrupts disabled, etc. The only latitude
+ * that must be afforded to DTrace is the ability to make calls within
+ * itself (and to its in-kernel subroutines) and the ability to access
+ * arbitrary (but mapped) memory. On some platforms, this constrains
+ * context. For example, on UltraSPARC, dtrace_probe() cannot be called
+ * from any context in which TL is greater than zero. dtrace_probe() may
+ * also not be called from any routine which may be called by dtrace_probe()
+ * -- which includes functions in the DTrace framework and some in-kernel
+ * DTrace subroutines. All such functions "dtrace_"; providers that
+ * instrument the kernel arbitrarily should be sure to not instrument these
+ * routines.
+ */
+typedef struct dtrace_pops {
+ void (*dtps_provide)(void *arg, dtrace_probedesc_t *spec);
+ void (*dtps_provide_module)(void *arg, modctl_t *mp);
+ void (*dtps_enable)(void *arg, dtrace_id_t id, void *parg);
+ void (*dtps_disable)(void *arg, dtrace_id_t id, void *parg);
+ void (*dtps_suspend)(void *arg, dtrace_id_t id, void *parg);
+ void (*dtps_resume)(void *arg, dtrace_id_t id, void *parg);
+ void (*dtps_getargdesc)(void *arg, dtrace_id_t id, void *parg,
+ dtrace_argdesc_t *desc);
+ uint64_t (*dtps_getargval)(void *arg, dtrace_id_t id, void *parg,
+ int argno, int aframes);
+ int (*dtps_usermode)(void *arg, dtrace_id_t id, void *parg);
+ void (*dtps_destroy)(void *arg, dtrace_id_t id, void *parg);
+} dtrace_pops_t;
+
+typedef uintptr_t dtrace_provider_id_t;
+
+extern int dtrace_register(const char *, const dtrace_pattr_t *, uint32_t,
+ cred_t *, const dtrace_pops_t *, void *, dtrace_provider_id_t *);
+extern int dtrace_unregister(dtrace_provider_id_t);
+extern int dtrace_condense(dtrace_provider_id_t);
+extern void dtrace_invalidate(dtrace_provider_id_t);
+extern dtrace_id_t dtrace_probe_lookup(dtrace_provider_id_t, char *,
+ char *, char *);
+extern dtrace_id_t dtrace_probe_create(dtrace_provider_id_t, const char *,
+ const char *, const char *, int, void *);
+extern void *dtrace_probe_arg(dtrace_provider_id_t, dtrace_id_t);
+extern void dtrace_probe(dtrace_id_t, uintptr_t arg0, uintptr_t arg1,
+ uintptr_t arg2, uintptr_t arg3, uintptr_t arg4);
+
+/*
+ * DTrace Meta Provider API
+ *
+ * The following functions are implemented by the DTrace framework and are
+ * used to implement meta providers. Meta providers plug into the DTrace
+ * framework and are used to instantiate new providers on the fly. At
+ * present, there is only one type of meta provider and only one meta
+ * provider may be registered with the DTrace framework at a time. The
+ * sole meta provider type provides user-land static tracing facilities
+ * by taking meta probe descriptions and adding a corresponding provider
+ * into the DTrace framework.
+ *
+ * 1 Framework-to-Provider
+ *
+ * 1.1 Overview
+ *
+ * The Framework-to-Provider API is represented by the dtrace_mops structure
+ * that the meta provider passes to the framework when registering itself as
+ * a meta provider. This structure consists of the following members:
+ *
+ * dtms_create_probe() <-- Add a new probe to a created provider
+ * dtms_provide_pid() <-- Create a new provider for a given process
+ * dtms_remove_pid() <-- Remove a previously created provider
+ *
+ * 1.2 void dtms_create_probe(void *arg, void *parg,
+ * dtrace_helper_probedesc_t *probedesc);
+ *
+ * 1.2.1 Overview
+ *
+ * Called by the DTrace framework to create a new probe in a provider
+ * created by this meta provider.
+ *
+ * 1.2.2 Arguments and notes
+ *
+ * The first argument is the cookie as passed to dtrace_meta_register().
+ * The second argument is the provider cookie for the associated provider;
+ * this is obtained from the return value of dtms_provide_pid(). The third
+ * argument is the helper probe description.
+ *
+ * 1.2.3 Return value
+ *
+ * None
+ *
+ * 1.2.4 Caller's context
+ *
+ * dtms_create_probe() is called from either ioctl() or module load context.
+ * The DTrace framework is locked in such a way that meta providers may not
+ * register or unregister. This means that the meta provider cannot call
+ * dtrace_meta_register() or dtrace_meta_unregister(). However, the context is
+ * such that the provider may (and is expected to) call provider-related
+ * DTrace provider APIs including dtrace_probe_create().
+ *
+ * 1.3 void *dtms_provide_pid(void *arg, dtrace_meta_provider_t *mprov,
+ * pid_t pid)
+ *
+ * 1.3.1 Overview
+ *
+ * Called by the DTrace framework to instantiate a new provider given the
+ * description of the provider and probes in the mprov argument. The
+ * meta provider should call dtrace_register() to insert the new provider
+ * into the DTrace framework.
+ *
+ * 1.3.2 Arguments and notes
+ *
+ * The first argument is the cookie as passed to dtrace_meta_register().
+ * The second argument is a pointer to a structure describing the new
+ * helper provider. The third argument is the process identifier for
+ * process associated with this new provider. Note that the name of the
+ * provider as passed to dtrace_register() should be the contatenation of
+ * the dtmpb_provname member of the mprov argument and the processs
+ * identifier as a string.
+ *
+ * 1.3.3 Return value
+ *
+ * The cookie for the provider that the meta provider creates. This is
+ * the same value that it passed to dtrace_register().
+ *
+ * 1.3.4 Caller's context
+ *
+ * dtms_provide_pid() is called from either ioctl() or module load context.
+ * The DTrace framework is locked in such a way that meta providers may not
+ * register or unregister. This means that the meta provider cannot call
+ * dtrace_meta_register() or dtrace_meta_unregister(). However, the context
+ * is such that the provider may -- and is expected to -- call
+ * provider-related DTrace provider APIs including dtrace_register().
+ *
+ * 1.4 void dtms_remove_pid(void *arg, dtrace_meta_provider_t *mprov,
+ * pid_t pid)
+ *
+ * 1.4.1 Overview
+ *
+ * Called by the DTrace framework to remove a provider that had previously
+ * been instantiated via the dtms_provide_pid() entry point. The meta
+ * provider need not remove the provider immediately, but this entry
+ * point indicates that the provider should be removed as soon as possible
+ * using the dtrace_unregister() API.
+ *
+ * 1.4.2 Arguments and notes
+ *
+ * The first argument is the cookie as passed to dtrace_meta_register().
+ * The second argument is a pointer to a structure describing the helper
+ * provider. The third argument is the process identifier for process
+ * associated with this new provider.
+ *
+ * 1.4.3 Return value
+ *
+ * None
+ *
+ * 1.4.4 Caller's context
+ *
+ * dtms_remove_pid() is called from either ioctl() or exit() context.
+ * The DTrace framework is locked in such a way that meta providers may not
+ * register or unregister. This means that the meta provider cannot call
+ * dtrace_meta_register() or dtrace_meta_unregister(). However, the context
+ * is such that the provider may -- and is expected to -- call
+ * provider-related DTrace provider APIs including dtrace_unregister().
+ */
+typedef struct dtrace_helper_probedesc {
+ char *dthpb_mod; /* probe module */
+ char *dthpb_func; /* probe function */
+ char *dthpb_name; /* probe name */
+ uint64_t dthpb_base; /* base address */
+ uint32_t *dthpb_offs; /* offsets array */
+ uint32_t *dthpb_enoffs; /* is-enabled offsets array */
+ uint32_t dthpb_noffs; /* offsets count */
+ uint32_t dthpb_nenoffs; /* is-enabled offsets count */
+ uint8_t *dthpb_args; /* argument mapping array */
+ uint8_t dthpb_xargc; /* translated argument count */
+ uint8_t dthpb_nargc; /* native argument count */
+ char *dthpb_xtypes; /* translated types strings */
+ char *dthpb_ntypes; /* native types strings */
+} dtrace_helper_probedesc_t;
+
+typedef struct dtrace_helper_provdesc {
+ char *dthpv_provname; /* provider name */
+ dtrace_pattr_t dthpv_pattr; /* stability attributes */
+} dtrace_helper_provdesc_t;
+
+typedef struct dtrace_mops {
+ void (*dtms_create_probe)(void *, void *, dtrace_helper_probedesc_t *);
+ void *(*dtms_provide_pid)(void *, dtrace_helper_provdesc_t *, pid_t);
+ void (*dtms_remove_pid)(void *, dtrace_helper_provdesc_t *, pid_t);
+} dtrace_mops_t;
+
+typedef uintptr_t dtrace_meta_provider_id_t;
+
+extern int dtrace_meta_register(const char *, const dtrace_mops_t *, void *,
+ dtrace_meta_provider_id_t *);
+extern int dtrace_meta_unregister(dtrace_meta_provider_id_t);
+
+/*
+ * DTrace Kernel Hooks
+ *
+ * The following functions are implemented by the base kernel and form a set of
+ * hooks used by the DTrace framework. DTrace hooks are implemented in either
+ * uts/common/os/dtrace_subr.c, an ISA-specific assembly file, or in a
+ * uts/<platform>/os/dtrace_subr.c corresponding to each hardware platform.
+ */
+
+typedef enum dtrace_vtime_state {
+ DTRACE_VTIME_INACTIVE = 0, /* No DTrace, no TNF */
+ DTRACE_VTIME_ACTIVE, /* DTrace virtual time, no TNF */
+ DTRACE_VTIME_INACTIVE_TNF, /* No DTrace, TNF active */
+ DTRACE_VTIME_ACTIVE_TNF /* DTrace virtual time _and_ TNF */
+} dtrace_vtime_state_t;
+
+#if defined(sun)
+extern dtrace_vtime_state_t dtrace_vtime_active;
+#endif
+extern void dtrace_vtime_switch(kthread_t *next);
+extern void dtrace_vtime_enable_tnf(void);
+extern void dtrace_vtime_disable_tnf(void);
+extern void dtrace_vtime_enable(void);
+extern void dtrace_vtime_disable(void);
+
+struct regs;
+
+#if defined(sun)
+extern int (*dtrace_pid_probe_ptr)(struct regs *);
+extern int (*dtrace_return_probe_ptr)(struct regs *);
+extern void (*dtrace_fasttrap_fork_ptr)(proc_t *, proc_t *);
+extern void (*dtrace_fasttrap_exec_ptr)(proc_t *);
+extern void (*dtrace_fasttrap_exit_ptr)(proc_t *);
+extern void dtrace_fasttrap_fork(proc_t *, proc_t *);
+#endif
+
+typedef uintptr_t dtrace_icookie_t;
+typedef void (*dtrace_xcall_t)(void *);
+
+extern dtrace_icookie_t dtrace_interrupt_disable(void);
+extern void dtrace_interrupt_enable(dtrace_icookie_t);
+
+extern void dtrace_membar_producer(void);
+extern void dtrace_membar_consumer(void);
+
+extern void (*dtrace_cpu_init)(processorid_t);
+extern void (*dtrace_modload)(modctl_t *);
+extern void (*dtrace_modunload)(modctl_t *);
+extern void (*dtrace_helpers_cleanup)(void);
+extern void (*dtrace_helpers_fork)(proc_t *parent, proc_t *child);
+extern void (*dtrace_cpustart_init)(void);
+extern void (*dtrace_cpustart_fini)(void);
+
+extern void (*dtrace_debugger_init)(void);
+extern void (*dtrace_debugger_fini)(void);
+extern dtrace_cacheid_t dtrace_predcache_id;
+
+#if defined(sun)
+extern hrtime_t dtrace_gethrtime(void);
+#else
+void dtrace_debug_printf(const char *, ...) __printflike(1, 2);
+#endif
+extern void dtrace_sync(void);
+extern void dtrace_toxic_ranges(void (*)(uintptr_t, uintptr_t));
+extern void dtrace_xcall(processorid_t, dtrace_xcall_t, void *);
+extern void dtrace_vpanic(const char *, __va_list);
+extern void dtrace_panic(const char *, ...);
+
+extern int dtrace_safe_defer_signal(void);
+extern void dtrace_safe_synchronous_signal(void);
+
+extern int dtrace_mach_aframes(void);
+
+#if defined(__i386) || defined(__amd64)
+extern int dtrace_instr_size(uchar_t *instr);
+extern int dtrace_instr_size_isa(uchar_t *, model_t, int *);
+extern void dtrace_invop_add(int (*)(uintptr_t, uintptr_t *, uintptr_t));
+extern void dtrace_invop_remove(int (*)(uintptr_t, uintptr_t *, uintptr_t));
+extern void dtrace_invop_callsite(void);
+#endif
+
+#ifdef __sparc
+extern int dtrace_blksuword32(uintptr_t, uint32_t *, int);
+extern void dtrace_getfsr(uint64_t *);
+#endif
+
+#define DTRACE_CPUFLAG_ISSET(flag) \
+ (cpu_core[curcpu].cpuc_dtrace_flags & (flag))
+
+#define DTRACE_CPUFLAG_SET(flag) \
+ (cpu_core[curcpu].cpuc_dtrace_flags |= (flag))
+
+#define DTRACE_CPUFLAG_CLEAR(flag) \
+ (cpu_core[curcpu].cpuc_dtrace_flags &= ~(flag))
+
+#endif /* _KERNEL */
+
+#endif /* _ASM */
+
+#if defined(__i386) || defined(__amd64)
+
+#define DTRACE_INVOP_PUSHL_EBP 1
+#define DTRACE_INVOP_POPL_EBP 2
+#define DTRACE_INVOP_LEAVE 3
+#define DTRACE_INVOP_NOP 4
+#define DTRACE_INVOP_RET 5
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_DTRACE_H */
diff --git a/sys/cddl/contrib/opensolaris/uts/common/sys/dtrace_impl.h b/sys/cddl/contrib/opensolaris/uts/common/sys/dtrace_impl.h
new file mode 100644
index 000000000000..0b2a2f061968
--- /dev/null
+++ b/sys/cddl/contrib/opensolaris/uts/common/sys/dtrace_impl.h
@@ -0,0 +1,1323 @@
+/*
+ * 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: src/sys/cddl/contrib/opensolaris/uts/common/sys/dtrace_impl.h,v 1.3 2008/06/01 01:46:37 jb Exp $
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _SYS_DTRACE_IMPL_H
+#define _SYS_DTRACE_IMPL_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * DTrace Dynamic Tracing Software: Kernel Implementation Interfaces
+ *
+ * Note: The contents of this file are private to the implementation of the
+ * Solaris system and DTrace subsystem and are subject to change at any time
+ * without notice. Applications and drivers using these interfaces will fail
+ * to run on future releases. These interfaces should not be used for any
+ * purpose except those expressly outlined in dtrace(7D) and libdtrace(3LIB).
+ * Please refer to the "Solaris Dynamic Tracing Guide" for more information.
+ */
+
+#include <sys/dtrace.h>
+#if !defined(sun)
+#ifdef __sparcv9
+typedef uint32_t pc_t;
+#else
+typedef uintptr_t pc_t;
+#endif
+typedef u_long greg_t;
+#endif
+
+/*
+ * DTrace Implementation Constants and Typedefs
+ */
+#define DTRACE_MAXPROPLEN 128
+#define DTRACE_DYNVAR_CHUNKSIZE 256
+
+struct dtrace_probe;
+struct dtrace_ecb;
+struct dtrace_predicate;
+struct dtrace_action;
+struct dtrace_provider;
+struct dtrace_state;
+
+typedef struct dtrace_probe dtrace_probe_t;
+typedef struct dtrace_ecb dtrace_ecb_t;
+typedef struct dtrace_predicate dtrace_predicate_t;
+typedef struct dtrace_action dtrace_action_t;
+typedef struct dtrace_provider dtrace_provider_t;
+typedef struct dtrace_meta dtrace_meta_t;
+typedef struct dtrace_state dtrace_state_t;
+typedef uint32_t dtrace_optid_t;
+typedef uint32_t dtrace_specid_t;
+typedef uint64_t dtrace_genid_t;
+
+/*
+ * DTrace Probes
+ *
+ * The probe is the fundamental unit of the DTrace architecture. Probes are
+ * created by DTrace providers, and managed by the DTrace framework. A probe
+ * is identified by a unique <provider, module, function, name> tuple, and has
+ * a unique probe identifier assigned to it. (Some probes are not associated
+ * with a specific point in text; these are called _unanchored probes_ and have
+ * no module or function associated with them.) Probes are represented as a
+ * dtrace_probe structure. To allow quick lookups based on each element of the
+ * probe tuple, probes are hashed by each of provider, module, function and
+ * name. (If a lookup is performed based on a regular expression, a
+ * dtrace_probekey is prepared, and a linear search is performed.) Each probe
+ * is additionally pointed to by a linear array indexed by its identifier. The
+ * identifier is the provider's mechanism for indicating to the DTrace
+ * framework that a probe has fired: the identifier is passed as the first
+ * argument to dtrace_probe(), where it is then mapped into the corresponding
+ * dtrace_probe structure. From the dtrace_probe structure, dtrace_probe() can
+ * iterate over the probe's list of enabling control blocks; see "DTrace
+ * Enabling Control Blocks", below.)
+ */
+struct dtrace_probe {
+ dtrace_id_t dtpr_id; /* probe identifier */
+ dtrace_ecb_t *dtpr_ecb; /* ECB list; see below */
+ dtrace_ecb_t *dtpr_ecb_last; /* last ECB in list */
+ void *dtpr_arg; /* provider argument */
+ dtrace_cacheid_t dtpr_predcache; /* predicate cache ID */
+ int dtpr_aframes; /* artificial frames */
+ dtrace_provider_t *dtpr_provider; /* pointer to provider */
+ char *dtpr_mod; /* probe's module name */
+ char *dtpr_func; /* probe's function name */
+ char *dtpr_name; /* probe's name */
+ dtrace_probe_t *dtpr_nextmod; /* next in module hash */
+ dtrace_probe_t *dtpr_prevmod; /* previous in module hash */
+ dtrace_probe_t *dtpr_nextfunc; /* next in function hash */
+ dtrace_probe_t *dtpr_prevfunc; /* previous in function hash */
+ dtrace_probe_t *dtpr_nextname; /* next in name hash */
+ dtrace_probe_t *dtpr_prevname; /* previous in name hash */
+ dtrace_genid_t dtpr_gen; /* probe generation ID */
+};
+
+typedef int dtrace_probekey_f(const char *, const char *, int);
+
+typedef struct dtrace_probekey {
+ char *dtpk_prov; /* provider name to match */
+ dtrace_probekey_f *dtpk_pmatch; /* provider matching function */
+ char *dtpk_mod; /* module name to match */
+ dtrace_probekey_f *dtpk_mmatch; /* module matching function */
+ char *dtpk_func; /* func name to match */
+ dtrace_probekey_f *dtpk_fmatch; /* func matching function */
+ char *dtpk_name; /* name to match */
+ dtrace_probekey_f *dtpk_nmatch; /* name matching function */
+ dtrace_id_t dtpk_id; /* identifier to match */
+} dtrace_probekey_t;
+
+typedef struct dtrace_hashbucket {
+ struct dtrace_hashbucket *dthb_next; /* next on hash chain */
+ dtrace_probe_t *dthb_chain; /* chain of probes */
+ int dthb_len; /* number of probes here */
+} dtrace_hashbucket_t;
+
+typedef struct dtrace_hash {
+ dtrace_hashbucket_t **dth_tab; /* hash table */
+ int dth_size; /* size of hash table */
+ int dth_mask; /* mask to index into table */
+ int dth_nbuckets; /* total number of buckets */
+ uintptr_t dth_nextoffs; /* offset of next in probe */
+ uintptr_t dth_prevoffs; /* offset of prev in probe */
+ uintptr_t dth_stroffs; /* offset of str in probe */
+} dtrace_hash_t;
+
+/*
+ * DTrace Enabling Control Blocks
+ *
+ * When a provider wishes to fire a probe, it calls into dtrace_probe(),
+ * passing the probe identifier as the first argument. As described above,
+ * dtrace_probe() maps the identifier into a pointer to a dtrace_probe_t
+ * structure. This structure contains information about the probe, and a
+ * pointer to the list of Enabling Control Blocks (ECBs). Each ECB points to
+ * DTrace consumer state, and contains an optional predicate, and a list of
+ * actions. (Shown schematically below.) The ECB abstraction allows a single
+ * probe to be multiplexed across disjoint consumers, or across disjoint
+ * enablings of a single probe within one consumer.
+ *
+ * Enabling Control Block
+ * dtrace_ecb_t
+ * +------------------------+
+ * | dtrace_epid_t ---------+--------------> Enabled Probe ID (EPID)
+ * | dtrace_state_t * ------+--------------> State associated with this ECB
+ * | dtrace_predicate_t * --+---------+
+ * | dtrace_action_t * -----+----+ |
+ * | dtrace_ecb_t * ---+ | | | Predicate (if any)
+ * +-------------------+----+ | | dtrace_predicate_t
+ * | | +---> +--------------------+
+ * | | | dtrace_difo_t * ---+----> DIFO
+ * | | +--------------------+
+ * | |
+ * Next ECB | | Action
+ * (if any) | | dtrace_action_t
+ * : +--> +-------------------+
+ * : | dtrace_actkind_t -+------> kind
+ * v | dtrace_difo_t * --+------> DIFO (if any)
+ * | dtrace_recdesc_t -+------> record descr.
+ * | dtrace_action_t * +------+
+ * +-------------------+ |
+ * | Next action
+ * +-------------------------------+ (if any)
+ * |
+ * | Action
+ * | dtrace_action_t
+ * +--> +-------------------+
+ * | dtrace_actkind_t -+------> kind
+ * | dtrace_difo_t * --+------> DIFO (if any)
+ * | dtrace_action_t * +------+
+ * +-------------------+ |
+ * | Next action
+ * +-------------------------------+ (if any)
+ * |
+ * :
+ * v
+ *
+ *
+ * dtrace_probe() iterates over the ECB list. If the ECB needs less space
+ * than is available in the principal buffer, the ECB is processed: if the
+ * predicate is non-NULL, the DIF object is executed. If the result is
+ * non-zero, the action list is processed, with each action being executed
+ * accordingly. When the action list has been completely executed, processing
+ * advances to the next ECB. processing advances to the next ECB. If the
+ * result is non-zero; For each ECB, it first determines the The ECB
+ * abstraction allows disjoint consumers to multiplex on single probes.
+ */
+struct dtrace_ecb {
+ dtrace_epid_t dte_epid; /* enabled probe ID */
+ uint32_t dte_alignment; /* required alignment */
+ size_t dte_needed; /* bytes needed */
+ size_t dte_size; /* total size of payload */
+ dtrace_predicate_t *dte_predicate; /* predicate, if any */
+ dtrace_action_t *dte_action; /* actions, if any */
+ dtrace_ecb_t *dte_next; /* next ECB on probe */
+ dtrace_state_t *dte_state; /* pointer to state */
+ uint32_t dte_cond; /* security condition */
+ dtrace_probe_t *dte_probe; /* pointer to probe */
+ dtrace_action_t *dte_action_last; /* last action on ECB */
+ uint64_t dte_uarg; /* library argument */
+};
+
+struct dtrace_predicate {
+ dtrace_difo_t *dtp_difo; /* DIF object */
+ dtrace_cacheid_t dtp_cacheid; /* cache identifier */
+ int dtp_refcnt; /* reference count */
+};
+
+struct dtrace_action {
+ dtrace_actkind_t dta_kind; /* kind of action */
+ uint16_t dta_intuple; /* boolean: in aggregation */
+ uint32_t dta_refcnt; /* reference count */
+ dtrace_difo_t *dta_difo; /* pointer to DIFO */
+ dtrace_recdesc_t dta_rec; /* record description */
+ dtrace_action_t *dta_prev; /* previous action */
+ dtrace_action_t *dta_next; /* next action */
+};
+
+typedef struct dtrace_aggregation {
+ dtrace_action_t dtag_action; /* action; must be first */
+ dtrace_aggid_t dtag_id; /* identifier */
+ dtrace_ecb_t *dtag_ecb; /* corresponding ECB */
+ dtrace_action_t *dtag_first; /* first action in tuple */
+ uint32_t dtag_base; /* base of aggregation */
+ uint8_t dtag_hasarg; /* boolean: has argument */
+ uint64_t dtag_initial; /* initial value */
+ void (*dtag_aggregate)(uint64_t *, uint64_t, uint64_t);
+} dtrace_aggregation_t;
+
+/*
+ * DTrace Buffers
+ *
+ * Principal buffers, aggregation buffers, and speculative buffers are all
+ * managed with the dtrace_buffer structure. By default, this structure
+ * includes twin data buffers -- dtb_tomax and dtb_xamot -- that serve as the
+ * active and passive buffers, respectively. For speculative buffers,
+ * dtb_xamot will be NULL; for "ring" and "fill" buffers, dtb_xamot will point
+ * to a scratch buffer. For all buffer types, the dtrace_buffer structure is
+ * always allocated on a per-CPU basis; a single dtrace_buffer structure is
+ * never shared among CPUs. (That is, there is never true sharing of the
+ * dtrace_buffer structure; to prevent false sharing of the structure, it must
+ * always be aligned to the coherence granularity -- generally 64 bytes.)
+ *
+ * One of the critical design decisions of DTrace is that a given ECB always
+ * stores the same quantity and type of data. This is done to assure that the
+ * only metadata required for an ECB's traced data is the EPID. That is, from
+ * the EPID, the consumer can determine the data layout. (The data buffer
+ * layout is shown schematically below.) By assuring that one can determine
+ * data layout from the EPID, the metadata stream can be separated from the
+ * data stream -- simplifying the data stream enormously.
+ *
+ * base of data buffer ---> +------+--------------------+------+
+ * | EPID | data | EPID |
+ * +------+--------+------+----+------+
+ * | data | EPID | data |
+ * +---------------+------+-----------+
+ * | data, cont. |
+ * +------+--------------------+------+
+ * | EPID | data | |
+ * +------+--------------------+ |
+ * | || |
+ * | || |
+ * | \/ |
+ * : :
+ * . .
+ * . .
+ * . .
+ * : :
+ * | |
+ * limit of data buffer ---> +----------------------------------+
+ *
+ * When evaluating an ECB, dtrace_probe() determines if the ECB's needs of the
+ * principal buffer (both scratch and payload) exceed the available space. If
+ * the ECB's needs exceed available space (and if the principal buffer policy
+ * is the default "switch" policy), the ECB is dropped, the buffer's drop count
+ * is incremented, and processing advances to the next ECB. If the ECB's needs
+ * can be met with the available space, the ECB is processed, but the offset in
+ * the principal buffer is only advanced if the ECB completes processing
+ * without error.
+ *
+ * When a buffer is to be switched (either because the buffer is the principal
+ * buffer with a "switch" policy or because it is an aggregation buffer), a
+ * cross call is issued to the CPU associated with the buffer. In the cross
+ * call context, interrupts are disabled, and the active and the inactive
+ * buffers are atomically switched. This involves switching the data pointers,
+ * copying the various state fields (offset, drops, errors, etc.) into their
+ * inactive equivalents, and clearing the state fields. Because interrupts are
+ * disabled during this procedure, the switch is guaranteed to appear atomic to
+ * dtrace_probe().
+ *
+ * DTrace Ring Buffering
+ *
+ * To process a ring buffer correctly, one must know the oldest valid record.
+ * Processing starts at the oldest record in the buffer and continues until
+ * the end of the buffer is reached. Processing then resumes starting with
+ * the record stored at offset 0 in the buffer, and continues until the
+ * youngest record is processed. If trace records are of a fixed-length,
+ * determining the oldest record is trivial:
+ *
+ * - If the ring buffer has not wrapped, the oldest record is the record
+ * stored at offset 0.
+ *
+ * - If the ring buffer has wrapped, the oldest record is the record stored
+ * at the current offset.
+ *
+ * With variable length records, however, just knowing the current offset
+ * doesn't suffice for determining the oldest valid record: assuming that one
+ * allows for arbitrary data, one has no way of searching forward from the
+ * current offset to find the oldest valid record. (That is, one has no way
+ * of separating data from metadata.) It would be possible to simply refuse to
+ * process any data in the ring buffer between the current offset and the
+ * limit, but this leaves (potentially) an enormous amount of otherwise valid
+ * data unprocessed.
+ *
+ * To effect ring buffering, we track two offsets in the buffer: the current
+ * offset and the _wrapped_ offset. If a request is made to reserve some
+ * amount of data, and the buffer has wrapped, the wrapped offset is
+ * incremented until the wrapped offset minus the current offset is greater
+ * than or equal to the reserve request. This is done by repeatedly looking
+ * up the ECB corresponding to the EPID at the current wrapped offset, and
+ * incrementing the wrapped offset by the size of the data payload
+ * corresponding to that ECB. If this offset is greater than or equal to the
+ * limit of the data buffer, the wrapped offset is set to 0. Thus, the
+ * current offset effectively "chases" the wrapped offset around the buffer.
+ * Schematically:
+ *
+ * base of data buffer ---> +------+--------------------+------+
+ * | EPID | data | EPID |
+ * +------+--------+------+----+------+
+ * | data | EPID | data |
+ * +---------------+------+-----------+
+ * | data, cont. |
+ * +------+---------------------------+
+ * | EPID | data |
+ * current offset ---> +------+---------------------------+
+ * | invalid data |
+ * wrapped offset ---> +------+--------------------+------+
+ * | EPID | data | EPID |
+ * +------+--------+------+----+------+
+ * | data | EPID | data |
+ * +---------------+------+-----------+
+ * : :
+ * . .
+ * . ... valid data ... .
+ * . .
+ * : :
+ * +------+-------------+------+------+
+ * | EPID | data | EPID | data |
+ * +------+------------++------+------+
+ * | data, cont. | leftover |
+ * limit of data buffer ---> +-------------------+--------------+
+ *
+ * If the amount of requested buffer space exceeds the amount of space
+ * available between the current offset and the end of the buffer:
+ *
+ * (1) all words in the data buffer between the current offset and the limit
+ * of the data buffer (marked "leftover", above) are set to
+ * DTRACE_EPIDNONE
+ *
+ * (2) the wrapped offset is set to zero
+ *
+ * (3) the iteration process described above occurs until the wrapped offset
+ * is greater than the amount of desired space.
+ *
+ * The wrapped offset is implemented by (re-)using the inactive offset.
+ * In a "switch" buffer policy, the inactive offset stores the offset in
+ * the inactive buffer; in a "ring" buffer policy, it stores the wrapped
+ * offset.
+ *
+ * DTrace Scratch Buffering
+ *
+ * Some ECBs may wish to allocate dynamically-sized temporary scratch memory.
+ * To accommodate such requests easily, scratch memory may be allocated in
+ * the buffer beyond the current offset plus the needed memory of the current
+ * ECB. If there isn't sufficient room in the buffer for the requested amount
+ * of scratch space, the allocation fails and an error is generated. Scratch
+ * memory is tracked in the dtrace_mstate_t and is automatically freed when
+ * the ECB ceases processing. Note that ring buffers cannot allocate their
+ * scratch from the principal buffer -- lest they needlessly overwrite older,
+ * valid data. Ring buffers therefore have their own dedicated scratch buffer
+ * from which scratch is allocated.
+ */
+#define DTRACEBUF_RING 0x0001 /* bufpolicy set to "ring" */
+#define DTRACEBUF_FILL 0x0002 /* bufpolicy set to "fill" */
+#define DTRACEBUF_NOSWITCH 0x0004 /* do not switch buffer */
+#define DTRACEBUF_WRAPPED 0x0008 /* ring buffer has wrapped */
+#define DTRACEBUF_DROPPED 0x0010 /* drops occurred */
+#define DTRACEBUF_ERROR 0x0020 /* errors occurred */
+#define DTRACEBUF_FULL 0x0040 /* "fill" buffer is full */
+#define DTRACEBUF_CONSUMED 0x0080 /* buffer has been consumed */
+#define DTRACEBUF_INACTIVE 0x0100 /* buffer is not yet active */
+
+typedef struct dtrace_buffer {
+ uint64_t dtb_offset; /* current offset in buffer */
+ uint64_t dtb_size; /* size of buffer */
+ uint32_t dtb_flags; /* flags */
+ uint32_t dtb_drops; /* number of drops */
+ caddr_t dtb_tomax; /* active buffer */
+ caddr_t dtb_xamot; /* inactive buffer */
+ uint32_t dtb_xamot_flags; /* inactive flags */
+ uint32_t dtb_xamot_drops; /* drops in inactive buffer */
+ uint64_t dtb_xamot_offset; /* offset in inactive buffer */
+ uint32_t dtb_errors; /* number of errors */
+ uint32_t dtb_xamot_errors; /* errors in inactive buffer */
+#ifndef _LP64
+ uint64_t dtb_pad1;
+#endif
+} dtrace_buffer_t;
+
+/*
+ * DTrace Aggregation Buffers
+ *
+ * Aggregation buffers use much of the same mechanism as described above
+ * ("DTrace Buffers"). However, because an aggregation is fundamentally a
+ * hash, there exists dynamic metadata associated with an aggregation buffer
+ * that is not associated with other kinds of buffers. This aggregation
+ * metadata is _only_ relevant for the in-kernel implementation of
+ * aggregations; it is not actually relevant to user-level consumers. To do
+ * this, we allocate dynamic aggregation data (hash keys and hash buckets)
+ * starting below the _limit_ of the buffer, and we allocate data from the
+ * _base_ of the buffer. When the aggregation buffer is copied out, _only_ the
+ * data is copied out; the metadata is simply discarded. Schematically,
+ * aggregation buffers look like:
+ *
+ * base of data buffer ---> +-------+------+-----------+-------+
+ * | aggid | key | value | aggid |
+ * +-------+------+-----------+-------+
+ * | key |
+ * +-------+-------+-----+------------+
+ * | value | aggid | key | value |
+ * +-------+------++-----+------+-----+
+ * | aggid | key | value | |
+ * +-------+------+-------------+ |
+ * | || |
+ * | || |
+ * | \/ |
+ * : :
+ * . .
+ * . .
+ * . .
+ * : :
+ * | /\ |
+ * | || +------------+
+ * | || | |
+ * +---------------------+ |
+ * | hash keys |
+ * | (dtrace_aggkey structures) |
+ * | |
+ * +----------------------------------+
+ * | hash buckets |
+ * | (dtrace_aggbuffer structure) |
+ * | |
+ * limit of data buffer ---> +----------------------------------+
+ *
+ *
+ * As implied above, just as we assure that ECBs always store a constant
+ * amount of data, we assure that a given aggregation -- identified by its
+ * aggregation ID -- always stores data of a constant quantity and type.
+ * As with EPIDs, this allows the aggregation ID to serve as the metadata for a
+ * given record.
+ *
+ * Note that the size of the dtrace_aggkey structure must be sizeof (uintptr_t)
+ * aligned. (If this the structure changes such that this becomes false, an
+ * assertion will fail in dtrace_aggregate().)
+ */
+typedef struct dtrace_aggkey {
+ uint32_t dtak_hashval; /* hash value */
+ uint32_t dtak_action:4; /* action -- 4 bits */
+ uint32_t dtak_size:28; /* size -- 28 bits */
+ caddr_t dtak_data; /* data pointer */
+ struct dtrace_aggkey *dtak_next; /* next in hash chain */
+} dtrace_aggkey_t;
+
+typedef struct dtrace_aggbuffer {
+ uintptr_t dtagb_hashsize; /* number of buckets */
+ uintptr_t dtagb_free; /* free list of keys */
+ dtrace_aggkey_t **dtagb_hash; /* hash table */
+} dtrace_aggbuffer_t;
+
+/*
+ * DTrace Speculations
+ *
+ * Speculations have a per-CPU buffer and a global state. Once a speculation
+ * buffer has been comitted or discarded, it cannot be reused until all CPUs
+ * have taken the same action (commit or discard) on their respective
+ * speculative buffer. However, because DTrace probes may execute in arbitrary
+ * context, other CPUs cannot simply be cross-called at probe firing time to
+ * perform the necessary commit or discard. The speculation states thus
+ * optimize for the case that a speculative buffer is only active on one CPU at
+ * the time of a commit() or discard() -- for if this is the case, other CPUs
+ * need not take action, and the speculation is immediately available for
+ * reuse. If the speculation is active on multiple CPUs, it must be
+ * asynchronously cleaned -- potentially leading to a higher rate of dirty
+ * speculative drops. The speculation states are as follows:
+ *
+ * DTRACESPEC_INACTIVE <= Initial state; inactive speculation
+ * DTRACESPEC_ACTIVE <= Allocated, but not yet speculatively traced to
+ * DTRACESPEC_ACTIVEONE <= Speculatively traced to on one CPU
+ * DTRACESPEC_ACTIVEMANY <= Speculatively traced to on more than one CPU
+ * DTRACESPEC_COMMITTING <= Currently being commited on one CPU
+ * DTRACESPEC_COMMITTINGMANY <= Currently being commited on many CPUs
+ * DTRACESPEC_DISCARDING <= Currently being discarded on many CPUs
+ *
+ * The state transition diagram is as follows:
+ *
+ * +----------------------------------------------------------+
+ * | |
+ * | +------------+ |
+ * | +-------------------| COMMITTING |<-----------------+ |
+ * | | +------------+ | |
+ * | | copied spec. ^ commit() on | | discard() on
+ * | | into principal | active CPU | | active CPU
+ * | | | commit() | |
+ * V V | | |
+ * +----------+ +--------+ +-----------+
+ * | INACTIVE |---------------->| ACTIVE |--------------->| ACTIVEONE |
+ * +----------+ speculation() +--------+ speculate() +-----------+
+ * ^ ^ | | |
+ * | | | discard() | |
+ * | | asynchronously | discard() on | | speculate()
+ * | | cleaned V inactive CPU | | on inactive
+ * | | +------------+ | | CPU
+ * | +-------------------| DISCARDING |<-----------------+ |
+ * | +------------+ |
+ * | asynchronously ^ |
+ * | copied spec. | discard() |
+ * | into principal +------------------------+ |
+ * | | V
+ * +----------------+ commit() +------------+
+ * | COMMITTINGMANY |<----------------------------------| ACTIVEMANY |
+ * +----------------+ +------------+
+ */
+typedef enum dtrace_speculation_state {
+ DTRACESPEC_INACTIVE = 0,
+ DTRACESPEC_ACTIVE,
+ DTRACESPEC_ACTIVEONE,
+ DTRACESPEC_ACTIVEMANY,
+ DTRACESPEC_COMMITTING,
+ DTRACESPEC_COMMITTINGMANY,
+ DTRACESPEC_DISCARDING
+} dtrace_speculation_state_t;
+
+typedef struct dtrace_speculation {
+ dtrace_speculation_state_t dtsp_state; /* current speculation state */
+ int dtsp_cleaning; /* non-zero if being cleaned */
+ dtrace_buffer_t *dtsp_buffer; /* speculative buffer */
+} dtrace_speculation_t;
+
+/*
+ * DTrace Dynamic Variables
+ *
+ * The dynamic variable problem is obviously decomposed into two subproblems:
+ * allocating new dynamic storage, and freeing old dynamic storage. The
+ * presence of the second problem makes the first much more complicated -- or
+ * rather, the absence of the second renders the first trivial. This is the
+ * case with aggregations, for which there is effectively no deallocation of
+ * dynamic storage. (Or more accurately, all dynamic storage is deallocated
+ * when a snapshot is taken of the aggregation.) As DTrace dynamic variables
+ * allow for both dynamic allocation and dynamic deallocation, the
+ * implementation of dynamic variables is quite a bit more complicated than
+ * that of their aggregation kin.
+ *
+ * We observe that allocating new dynamic storage is tricky only because the
+ * size can vary -- the allocation problem is much easier if allocation sizes
+ * are uniform. We further observe that in D, the size of dynamic variables is
+ * actually _not_ dynamic -- dynamic variable sizes may be determined by static
+ * analysis of DIF text. (This is true even of putatively dynamically-sized
+ * objects like strings and stacks, the sizes of which are dictated by the
+ * "stringsize" and "stackframes" variables, respectively.) We exploit this by
+ * performing this analysis on all DIF before enabling any probes. For each
+ * dynamic load or store, we calculate the dynamically-allocated size plus the
+ * size of the dtrace_dynvar structure plus the storage required to key the
+ * data. For all DIF, we take the largest value and dub it the _chunksize_.
+ * We then divide dynamic memory into two parts: a hash table that is wide
+ * enough to have every chunk in its own bucket, and a larger region of equal
+ * chunksize units. Whenever we wish to dynamically allocate a variable, we
+ * always allocate a single chunk of memory. Depending on the uniformity of
+ * allocation, this will waste some amount of memory -- but it eliminates the
+ * non-determinism inherent in traditional heap fragmentation.
+ *
+ * Dynamic objects are allocated by storing a non-zero value to them; they are
+ * deallocated by storing a zero value to them. Dynamic variables are
+ * complicated enormously by being shared between CPUs. In particular,
+ * consider the following scenario:
+ *
+ * CPU A CPU B
+ * +---------------------------------+ +---------------------------------+
+ * | | | |
+ * | allocates dynamic object a[123] | | |
+ * | by storing the value 345 to it | | |
+ * | ---------> |
+ * | | | wishing to load from object |
+ * | | | a[123], performs lookup in |
+ * | | | dynamic variable space |
+ * | <--------- |
+ * | deallocates object a[123] by | | |
+ * | storing 0 to it | | |
+ * | | | |
+ * | allocates dynamic object b[567] | | performs load from a[123] |
+ * | by storing the value 789 to it | | |
+ * : : : :
+ * . . . .
+ *
+ * This is obviously a race in the D program, but there are nonetheless only
+ * two valid values for CPU B's load from a[123]: 345 or 0. Most importantly,
+ * CPU B may _not_ see the value 789 for a[123].
+ *
+ * There are essentially two ways to deal with this:
+ *
+ * (1) Explicitly spin-lock variables. That is, if CPU B wishes to load
+ * from a[123], it needs to lock a[123] and hold the lock for the
+ * duration that it wishes to manipulate it.
+ *
+ * (2) Avoid reusing freed chunks until it is known that no CPU is referring
+ * to them.
+ *
+ * The implementation of (1) is rife with complexity, because it requires the
+ * user of a dynamic variable to explicitly decree when they are done using it.
+ * Were all variables by value, this perhaps wouldn't be debilitating -- but
+ * dynamic variables of non-scalar types are tracked by reference. That is, if
+ * a dynamic variable is, say, a string, and that variable is to be traced to,
+ * say, the principal buffer, the DIF emulation code returns to the main
+ * dtrace_probe() loop a pointer to the underlying storage, not the contents of
+ * the storage. Further, code calling on DIF emulation would have to be aware
+ * that the DIF emulation has returned a reference to a dynamic variable that
+ * has been potentially locked. The variable would have to be unlocked after
+ * the main dtrace_probe() loop is finished with the variable, and the main
+ * dtrace_probe() loop would have to be careful to not call any further DIF
+ * emulation while the variable is locked to avoid deadlock. More generally,
+ * if one were to implement (1), DIF emulation code dealing with dynamic
+ * variables could only deal with one dynamic variable at a time (lest deadlock
+ * result). To sum, (1) exports too much subtlety to the users of dynamic
+ * variables -- increasing maintenance burden and imposing serious constraints
+ * on future DTrace development.
+ *
+ * The implementation of (2) is also complex, but the complexity is more
+ * manageable. We need to be sure that when a variable is deallocated, it is
+ * not placed on a traditional free list, but rather on a _dirty_ list. Once a
+ * variable is on a dirty list, it cannot be found by CPUs performing a
+ * subsequent lookup of the variable -- but it may still be in use by other
+ * CPUs. To assure that all CPUs that may be seeing the old variable have
+ * cleared out of probe context, a dtrace_sync() can be issued. Once the
+ * dtrace_sync() has completed, it can be known that all CPUs are done
+ * manipulating the dynamic variable -- the dirty list can be atomically
+ * appended to the free list. Unfortunately, there's a slight hiccup in this
+ * mechanism: dtrace_sync() may not be issued from probe context. The
+ * dtrace_sync() must be therefore issued asynchronously from non-probe
+ * context. For this we rely on the DTrace cleaner, a cyclic that runs at the
+ * "cleanrate" frequency. To ease this implementation, we define several chunk
+ * lists:
+ *
+ * - Dirty. Deallocated chunks, not yet cleaned. Not available.
+ *
+ * - Rinsing. Formerly dirty chunks that are currently being asynchronously
+ * cleaned. Not available, but will be shortly. Dynamic variable
+ * allocation may not spin or block for availability, however.
+ *
+ * - Clean. Clean chunks, ready for allocation -- but not on the free list.
+ *
+ * - Free. Available for allocation.
+ *
+ * Moreover, to avoid absurd contention, _each_ of these lists is implemented
+ * on a per-CPU basis. This is only for performance, not correctness; chunks
+ * may be allocated from another CPU's free list. The algorithm for allocation
+ * then is this:
+ *
+ * (1) Attempt to atomically allocate from current CPU's free list. If list
+ * is non-empty and allocation is successful, allocation is complete.
+ *
+ * (2) If the clean list is non-empty, atomically move it to the free list,
+ * and reattempt (1).
+ *
+ * (3) If the dynamic variable space is in the CLEAN state, look for free
+ * and clean lists on other CPUs by setting the current CPU to the next
+ * CPU, and reattempting (1). If the next CPU is the current CPU (that
+ * is, if all CPUs have been checked), atomically switch the state of
+ * the dynamic variable space based on the following:
+ *
+ * - If no free chunks were found and no dirty chunks were found,
+ * atomically set the state to EMPTY.
+ *
+ * - If dirty chunks were found, atomically set the state to DIRTY.
+ *
+ * - If rinsing chunks were found, atomically set the state to RINSING.
+ *
+ * (4) Based on state of dynamic variable space state, increment appropriate
+ * counter to indicate dynamic drops (if in EMPTY state) vs. dynamic
+ * dirty drops (if in DIRTY state) vs. dynamic rinsing drops (if in
+ * RINSING state). Fail the allocation.
+ *
+ * The cleaning cyclic operates with the following algorithm: for all CPUs
+ * with a non-empty dirty list, atomically move the dirty list to the rinsing
+ * list. Perform a dtrace_sync(). For all CPUs with a non-empty rinsing list,
+ * atomically move the rinsing list to the clean list. Perform another
+ * dtrace_sync(). By this point, all CPUs have seen the new clean list; the
+ * state of the dynamic variable space can be restored to CLEAN.
+ *
+ * There exist two final races that merit explanation. The first is a simple
+ * allocation race:
+ *
+ * CPU A CPU B
+ * +---------------------------------+ +---------------------------------+
+ * | | | |
+ * | allocates dynamic object a[123] | | allocates dynamic object a[123] |
+ * | by storing the value 345 to it | | by storing the value 567 to it |
+ * | | | |
+ * : : : :
+ * . . . .
+ *
+ * Again, this is a race in the D program. It can be resolved by having a[123]
+ * hold the value 345 or a[123] hold the value 567 -- but it must be true that
+ * a[123] have only _one_ of these values. (That is, the racing CPUs may not
+ * put the same element twice on the same hash chain.) This is resolved
+ * simply: before the allocation is undertaken, the start of the new chunk's
+ * hash chain is noted. Later, after the allocation is complete, the hash
+ * chain is atomically switched to point to the new element. If this fails
+ * (because of either concurrent allocations or an allocation concurrent with a
+ * deletion), the newly allocated chunk is deallocated to the dirty list, and
+ * the whole process of looking up (and potentially allocating) the dynamic
+ * variable is reattempted.
+ *
+ * The final race is a simple deallocation race:
+ *
+ * CPU A CPU B
+ * +---------------------------------+ +---------------------------------+
+ * | | | |
+ * | deallocates dynamic object | | deallocates dynamic object |
+ * | a[123] by storing the value 0 | | a[123] by storing the value 0 |
+ * | to it | | to it |
+ * | | | |
+ * : : : :
+ * . . . .
+ *
+ * Once again, this is a race in the D program, but it is one that we must
+ * handle without corrupting the underlying data structures. Because
+ * deallocations require the deletion of a chunk from the middle of a hash
+ * chain, we cannot use a single-word atomic operation to remove it. For this,
+ * we add a spin lock to the hash buckets that is _only_ used for deallocations
+ * (allocation races are handled as above). Further, this spin lock is _only_
+ * held for the duration of the delete; before control is returned to the DIF
+ * emulation code, the hash bucket is unlocked.
+ */
+typedef struct dtrace_key {
+ uint64_t dttk_value; /* data value or data pointer */
+ uint64_t dttk_size; /* 0 if by-val, >0 if by-ref */
+} dtrace_key_t;
+
+typedef struct dtrace_tuple {
+ uint32_t dtt_nkeys; /* number of keys in tuple */
+ uint32_t dtt_pad; /* padding */
+ dtrace_key_t dtt_key[1]; /* array of tuple keys */
+} dtrace_tuple_t;
+
+typedef struct dtrace_dynvar {
+ uint64_t dtdv_hashval; /* hash value -- 0 if free */
+ struct dtrace_dynvar *dtdv_next; /* next on list or hash chain */
+ void *dtdv_data; /* pointer to data */
+ dtrace_tuple_t dtdv_tuple; /* tuple key */
+} dtrace_dynvar_t;
+
+typedef enum dtrace_dynvar_op {
+ DTRACE_DYNVAR_ALLOC,
+ DTRACE_DYNVAR_NOALLOC,
+ DTRACE_DYNVAR_DEALLOC
+} dtrace_dynvar_op_t;
+
+typedef struct dtrace_dynhash {
+ dtrace_dynvar_t *dtdh_chain; /* hash chain for this bucket */
+ uintptr_t dtdh_lock; /* deallocation lock */
+#ifdef _LP64
+ uintptr_t dtdh_pad[6]; /* pad to avoid false sharing */
+#else
+ uintptr_t dtdh_pad[14]; /* pad to avoid false sharing */
+#endif
+} dtrace_dynhash_t;
+
+typedef struct dtrace_dstate_percpu {
+ dtrace_dynvar_t *dtdsc_free; /* free list for this CPU */
+ dtrace_dynvar_t *dtdsc_dirty; /* dirty list for this CPU */
+ dtrace_dynvar_t *dtdsc_rinsing; /* rinsing list for this CPU */
+ dtrace_dynvar_t *dtdsc_clean; /* clean list for this CPU */
+ uint64_t dtdsc_drops; /* number of capacity drops */
+ uint64_t dtdsc_dirty_drops; /* number of dirty drops */
+ uint64_t dtdsc_rinsing_drops; /* number of rinsing drops */
+#ifdef _LP64
+ uint64_t dtdsc_pad; /* pad to avoid false sharing */
+#else
+ uint64_t dtdsc_pad[2]; /* pad to avoid false sharing */
+#endif
+} dtrace_dstate_percpu_t;
+
+typedef enum dtrace_dstate_state {
+ DTRACE_DSTATE_CLEAN = 0,
+ DTRACE_DSTATE_EMPTY,
+ DTRACE_DSTATE_DIRTY,
+ DTRACE_DSTATE_RINSING
+} dtrace_dstate_state_t;
+
+typedef struct dtrace_dstate {
+ void *dtds_base; /* base of dynamic var. space */
+ size_t dtds_size; /* size of dynamic var. space */
+ size_t dtds_hashsize; /* number of buckets in hash */
+ size_t dtds_chunksize; /* size of each chunk */
+ dtrace_dynhash_t *dtds_hash; /* pointer to hash table */
+ dtrace_dstate_state_t dtds_state; /* current dynamic var. state */
+ dtrace_dstate_percpu_t *dtds_percpu; /* per-CPU dyn. var. state */
+} dtrace_dstate_t;
+
+/*
+ * DTrace Variable State
+ *
+ * The DTrace variable state tracks user-defined variables in its dtrace_vstate
+ * structure. Each DTrace consumer has exactly one dtrace_vstate structure,
+ * but some dtrace_vstate structures may exist without a corresponding DTrace
+ * consumer (see "DTrace Helpers", below). As described in <sys/dtrace.h>,
+ * user-defined variables can have one of three scopes:
+ *
+ * DIFV_SCOPE_GLOBAL => global scope
+ * DIFV_SCOPE_THREAD => thread-local scope (i.e. "self->" variables)
+ * DIFV_SCOPE_LOCAL => clause-local scope (i.e. "this->" variables)
+ *
+ * The variable state tracks variables by both their scope and their allocation
+ * type:
+ *
+ * - The dtvs_globals and dtvs_locals members each point to an array of
+ * dtrace_statvar structures. These structures contain both the variable
+ * metadata (dtrace_difv structures) and the underlying storage for all
+ * statically allocated variables, including statically allocated
+ * DIFV_SCOPE_GLOBAL variables and all DIFV_SCOPE_LOCAL variables.
+ *
+ * - The dtvs_tlocals member points to an array of dtrace_difv structures for
+ * DIFV_SCOPE_THREAD variables. As such, this array tracks _only_ the
+ * variable metadata for DIFV_SCOPE_THREAD variables; the underlying storage
+ * is allocated out of the dynamic variable space.
+ *
+ * - The dtvs_dynvars member is the dynamic variable state associated with the
+ * variable state. The dynamic variable state (described in "DTrace Dynamic
+ * Variables", above) tracks all DIFV_SCOPE_THREAD variables and all
+ * dynamically-allocated DIFV_SCOPE_GLOBAL variables.
+ */
+typedef struct dtrace_statvar {
+ uint64_t dtsv_data; /* data or pointer to it */
+ size_t dtsv_size; /* size of pointed-to data */
+ int dtsv_refcnt; /* reference count */
+ dtrace_difv_t dtsv_var; /* variable metadata */
+} dtrace_statvar_t;
+
+typedef struct dtrace_vstate {
+ dtrace_state_t *dtvs_state; /* back pointer to state */
+ dtrace_statvar_t **dtvs_globals; /* statically-allocated glbls */
+ int dtvs_nglobals; /* number of globals */
+ dtrace_difv_t *dtvs_tlocals; /* thread-local metadata */
+ int dtvs_ntlocals; /* number of thread-locals */
+ dtrace_statvar_t **dtvs_locals; /* clause-local data */
+ int dtvs_nlocals; /* number of clause-locals */
+ dtrace_dstate_t dtvs_dynvars; /* dynamic variable state */
+} dtrace_vstate_t;
+
+/*
+ * DTrace Machine State
+ *
+ * In the process of processing a fired probe, DTrace needs to track and/or
+ * cache some per-CPU state associated with that particular firing. This is
+ * state that is always discarded after the probe firing has completed, and
+ * much of it is not specific to any DTrace consumer, remaining valid across
+ * all ECBs. This state is tracked in the dtrace_mstate structure.
+ */
+#define DTRACE_MSTATE_ARGS 0x00000001
+#define DTRACE_MSTATE_PROBE 0x00000002
+#define DTRACE_MSTATE_EPID 0x00000004
+#define DTRACE_MSTATE_TIMESTAMP 0x00000008
+#define DTRACE_MSTATE_STACKDEPTH 0x00000010
+#define DTRACE_MSTATE_CALLER 0x00000020
+#define DTRACE_MSTATE_IPL 0x00000040
+#define DTRACE_MSTATE_FLTOFFS 0x00000080
+#define DTRACE_MSTATE_WALLTIMESTAMP 0x00000100
+#define DTRACE_MSTATE_USTACKDEPTH 0x00000200
+#define DTRACE_MSTATE_UCALLER 0x00000400
+
+typedef struct dtrace_mstate {
+ uintptr_t dtms_scratch_base; /* base of scratch space */
+ uintptr_t dtms_scratch_ptr; /* current scratch pointer */
+ size_t dtms_scratch_size; /* scratch size */
+ uint32_t dtms_present; /* variables that are present */
+ uint64_t dtms_arg[5]; /* cached arguments */
+ dtrace_epid_t dtms_epid; /* current EPID */
+ uint64_t dtms_timestamp; /* cached timestamp */
+ hrtime_t dtms_walltimestamp; /* cached wall timestamp */
+ int dtms_stackdepth; /* cached stackdepth */
+ int dtms_ustackdepth; /* cached ustackdepth */
+ struct dtrace_probe *dtms_probe; /* current probe */
+ uintptr_t dtms_caller; /* cached caller */
+ uint64_t dtms_ucaller; /* cached user-level caller */
+ int dtms_ipl; /* cached interrupt pri lev */
+ int dtms_fltoffs; /* faulting DIFO offset */
+ uintptr_t dtms_strtok; /* saved strtok() pointer */
+ uint32_t dtms_access; /* memory access rights */
+ dtrace_difo_t *dtms_difo; /* current dif object */
+} dtrace_mstate_t;
+
+#define DTRACE_COND_OWNER 0x1
+#define DTRACE_COND_USERMODE 0x2
+#define DTRACE_COND_ZONEOWNER 0x4
+
+#define DTRACE_PROBEKEY_MAXDEPTH 8 /* max glob recursion depth */
+
+/*
+ * Access flag used by dtrace_mstate.dtms_access.
+ */
+#define DTRACE_ACCESS_KERNEL 0x1 /* the priv to read kmem */
+
+
+/*
+ * DTrace Activity
+ *
+ * Each DTrace consumer is in one of several states, which (for purposes of
+ * avoiding yet-another overloading of the noun "state") we call the current
+ * _activity_. The activity transitions on dtrace_go() (from DTRACIOCGO), on
+ * dtrace_stop() (from DTRACIOCSTOP) and on the exit() action. Activities may
+ * only transition in one direction; the activity transition diagram is a
+ * directed acyclic graph. The activity transition diagram is as follows:
+ *
+ *
+ * +----------+ +--------+ +--------+
+ * | INACTIVE |------------------>| WARMUP |------------------>| ACTIVE |
+ * +----------+ dtrace_go(), +--------+ dtrace_go(), +--------+
+ * before BEGIN | after BEGIN | | |
+ * | | | |
+ * exit() action | | | |
+ * from BEGIN ECB | | | |
+ * | | | |
+ * v | | |
+ * +----------+ exit() action | | |
+ * +-----------------------------| DRAINING |<-------------------+ | |
+ * | +----------+ | |
+ * | | | |
+ * | dtrace_stop(), | | |
+ * | before END | | |
+ * | | | |
+ * | v | |
+ * | +---------+ +----------+ | |
+ * | | STOPPED |<----------------| COOLDOWN |<----------------------+ |
+ * | +---------+ dtrace_stop(), +----------+ dtrace_stop(), |
+ * | after END before END |
+ * | |
+ * | +--------+ |
+ * +----------------------------->| KILLED |<--------------------------+
+ * deadman timeout or +--------+ deadman timeout or
+ * killed consumer killed consumer
+ *
+ * Note that once a DTrace consumer has stopped tracing, there is no way to
+ * restart it; if a DTrace consumer wishes to restart tracing, it must reopen
+ * the DTrace pseudodevice.
+ */
+typedef enum dtrace_activity {
+ DTRACE_ACTIVITY_INACTIVE = 0, /* not yet running */
+ DTRACE_ACTIVITY_WARMUP, /* while starting */
+ DTRACE_ACTIVITY_ACTIVE, /* running */
+ DTRACE_ACTIVITY_DRAINING, /* before stopping */
+ DTRACE_ACTIVITY_COOLDOWN, /* while stopping */
+ DTRACE_ACTIVITY_STOPPED, /* after stopping */
+ DTRACE_ACTIVITY_KILLED /* killed */
+} dtrace_activity_t;
+
+/*
+ * DTrace Helper Implementation
+ *
+ * A description of the helper architecture may be found in <sys/dtrace.h>.
+ * Each process contains a pointer to its helpers in its p_dtrace_helpers
+ * member. This is a pointer to a dtrace_helpers structure, which contains an
+ * array of pointers to dtrace_helper structures, helper variable state (shared
+ * among a process's helpers) and a generation count. (The generation count is
+ * used to provide an identifier when a helper is added so that it may be
+ * subsequently removed.) The dtrace_helper structure is self-explanatory,
+ * containing pointers to the objects needed to execute the helper. Note that
+ * helpers are _duplicated_ across fork(2), and destroyed on exec(2). No more
+ * than dtrace_helpers_max are allowed per-process.
+ */
+#define DTRACE_HELPER_ACTION_USTACK 0
+#define DTRACE_NHELPER_ACTIONS 1
+
+typedef struct dtrace_helper_action {
+ int dtha_generation; /* helper action generation */
+ int dtha_nactions; /* number of actions */
+ dtrace_difo_t *dtha_predicate; /* helper action predicate */
+ dtrace_difo_t **dtha_actions; /* array of actions */
+ struct dtrace_helper_action *dtha_next; /* next helper action */
+} dtrace_helper_action_t;
+
+typedef struct dtrace_helper_provider {
+ int dthp_generation; /* helper provider generation */
+ uint32_t dthp_ref; /* reference count */
+ dof_helper_t dthp_prov; /* DOF w/ provider and probes */
+} dtrace_helper_provider_t;
+
+typedef struct dtrace_helpers {
+ dtrace_helper_action_t **dthps_actions; /* array of helper actions */
+ dtrace_vstate_t dthps_vstate; /* helper action var. state */
+ dtrace_helper_provider_t **dthps_provs; /* array of providers */
+ uint_t dthps_nprovs; /* count of providers */
+ uint_t dthps_maxprovs; /* provider array size */
+ int dthps_generation; /* current generation */
+ pid_t dthps_pid; /* pid of associated proc */
+ int dthps_deferred; /* helper in deferred list */
+ struct dtrace_helpers *dthps_next; /* next pointer */
+ struct dtrace_helpers *dthps_prev; /* prev pointer */
+} dtrace_helpers_t;
+
+/*
+ * DTrace Helper Action Tracing
+ *
+ * Debugging helper actions can be arduous. To ease the development and
+ * debugging of helpers, DTrace contains a tracing-framework-within-a-tracing-
+ * framework: helper tracing. If dtrace_helptrace_enabled is non-zero (which
+ * it is by default on DEBUG kernels), all helper activity will be traced to a
+ * global, in-kernel ring buffer. Each entry includes a pointer to the specific
+ * helper, the location within the helper, and a trace of all local variables.
+ * The ring buffer may be displayed in a human-readable format with the
+ * ::dtrace_helptrace mdb(1) dcmd.
+ */
+#define DTRACE_HELPTRACE_NEXT (-1)
+#define DTRACE_HELPTRACE_DONE (-2)
+#define DTRACE_HELPTRACE_ERR (-3)
+
+typedef struct dtrace_helptrace {
+ dtrace_helper_action_t *dtht_helper; /* helper action */
+ int dtht_where; /* where in helper action */
+ int dtht_nlocals; /* number of locals */
+ int dtht_fault; /* type of fault (if any) */
+ int dtht_fltoffs; /* DIF offset */
+ uint64_t dtht_illval; /* faulting value */
+ uint64_t dtht_locals[1]; /* local variables */
+} dtrace_helptrace_t;
+
+/*
+ * DTrace Credentials
+ *
+ * In probe context, we have limited flexibility to examine the credentials
+ * of the DTrace consumer that created a particular enabling. We use
+ * the Least Privilege interfaces to cache the consumer's cred pointer and
+ * some facts about that credential in a dtrace_cred_t structure. These
+ * can limit the consumer's breadth of visibility and what actions the
+ * consumer may take.
+ */
+#define DTRACE_CRV_ALLPROC 0x01
+#define DTRACE_CRV_KERNEL 0x02
+#define DTRACE_CRV_ALLZONE 0x04
+
+#define DTRACE_CRV_ALL (DTRACE_CRV_ALLPROC | DTRACE_CRV_KERNEL | \
+ DTRACE_CRV_ALLZONE)
+
+#define DTRACE_CRA_PROC 0x0001
+#define DTRACE_CRA_PROC_CONTROL 0x0002
+#define DTRACE_CRA_PROC_DESTRUCTIVE_ALLUSER 0x0004
+#define DTRACE_CRA_PROC_DESTRUCTIVE_ALLZONE 0x0008
+#define DTRACE_CRA_PROC_DESTRUCTIVE_CREDCHG 0x0010
+#define DTRACE_CRA_KERNEL 0x0020
+#define DTRACE_CRA_KERNEL_DESTRUCTIVE 0x0040
+
+#define DTRACE_CRA_ALL (DTRACE_CRA_PROC | \
+ DTRACE_CRA_PROC_CONTROL | \
+ DTRACE_CRA_PROC_DESTRUCTIVE_ALLUSER | \
+ DTRACE_CRA_PROC_DESTRUCTIVE_ALLZONE | \
+ DTRACE_CRA_PROC_DESTRUCTIVE_CREDCHG | \
+ DTRACE_CRA_KERNEL | \
+ DTRACE_CRA_KERNEL_DESTRUCTIVE)
+
+typedef struct dtrace_cred {
+ cred_t *dcr_cred;
+ uint8_t dcr_destructive;
+ uint8_t dcr_visible;
+ uint16_t dcr_action;
+} dtrace_cred_t;
+
+/*
+ * DTrace Consumer State
+ *
+ * Each DTrace consumer has an associated dtrace_state structure that contains
+ * its in-kernel DTrace state -- including options, credentials, statistics and
+ * pointers to ECBs, buffers, speculations and formats. A dtrace_state
+ * structure is also allocated for anonymous enablings. When anonymous state
+ * is grabbed, the grabbing consumers dts_anon pointer is set to the grabbed
+ * dtrace_state structure.
+ */
+struct dtrace_state {
+#if defined(sun)
+ dev_t dts_dev; /* device */
+#else
+ struct cdev *dts_dev; /* device */
+#endif
+ int dts_necbs; /* total number of ECBs */
+ dtrace_ecb_t **dts_ecbs; /* array of ECBs */
+ dtrace_epid_t dts_epid; /* next EPID to allocate */
+ size_t dts_needed; /* greatest needed space */
+ struct dtrace_state *dts_anon; /* anon. state, if grabbed */
+ dtrace_activity_t dts_activity; /* current activity */
+ dtrace_vstate_t dts_vstate; /* variable state */
+ dtrace_buffer_t *dts_buffer; /* principal buffer */
+ dtrace_buffer_t *dts_aggbuffer; /* aggregation buffer */
+ dtrace_speculation_t *dts_speculations; /* speculation array */
+ int dts_nspeculations; /* number of speculations */
+ int dts_naggregations; /* number of aggregations */
+ dtrace_aggregation_t **dts_aggregations; /* aggregation array */
+#if defined(sun)
+ vmem_t *dts_aggid_arena; /* arena for aggregation IDs */
+#else
+ struct unrhdr *dts_aggid_arena; /* arena for aggregation IDs */
+#endif
+ uint64_t dts_errors; /* total number of errors */
+ uint32_t dts_speculations_busy; /* number of spec. busy */
+ uint32_t dts_speculations_unavail; /* number of spec unavail */
+ uint32_t dts_stkstroverflows; /* stack string tab overflows */
+ uint32_t dts_dblerrors; /* errors in ERROR probes */
+ uint32_t dts_reserve; /* space reserved for END */
+ hrtime_t dts_laststatus; /* time of last status */
+#if defined(sun)
+ cyclic_id_t dts_cleaner; /* cleaning cyclic */
+ cyclic_id_t dts_deadman; /* deadman cyclic */
+#else
+ struct callout dts_cleaner; /* Cleaning callout. */
+ struct callout dts_deadman; /* Deadman callout. */
+#endif
+ hrtime_t dts_alive; /* time last alive */
+ char dts_speculates; /* boolean: has speculations */
+ char dts_destructive; /* boolean: has dest. actions */
+ int dts_nformats; /* number of formats */
+ char **dts_formats; /* format string array */
+ dtrace_optval_t dts_options[DTRACEOPT_MAX]; /* options */
+ dtrace_cred_t dts_cred; /* credentials */
+ size_t dts_nretained; /* number of retained enabs */
+};
+
+struct dtrace_provider {
+ dtrace_pattr_t dtpv_attr; /* provider attributes */
+ dtrace_ppriv_t dtpv_priv; /* provider privileges */
+ dtrace_pops_t dtpv_pops; /* provider operations */
+ char *dtpv_name; /* provider name */
+ void *dtpv_arg; /* provider argument */
+ uint_t dtpv_defunct; /* boolean: defunct provider */
+ struct dtrace_provider *dtpv_next; /* next provider */
+};
+
+struct dtrace_meta {
+ dtrace_mops_t dtm_mops; /* meta provider operations */
+ char *dtm_name; /* meta provider name */
+ void *dtm_arg; /* meta provider user arg */
+ uint64_t dtm_count; /* no. of associated provs. */
+};
+
+/*
+ * DTrace Enablings
+ *
+ * A dtrace_enabling structure is used to track a collection of ECB
+ * descriptions -- before they have been turned into actual ECBs. This is
+ * created as a result of DOF processing, and is generally used to generate
+ * ECBs immediately thereafter. However, enablings are also generally
+ * retained should the probes they describe be created at a later time; as
+ * each new module or provider registers with the framework, the retained
+ * enablings are reevaluated, with any new match resulting in new ECBs. To
+ * prevent probes from being matched more than once, the enabling tracks the
+ * last probe generation matched, and only matches probes from subsequent
+ * generations.
+ */
+typedef struct dtrace_enabling {
+ dtrace_ecbdesc_t **dten_desc; /* all ECB descriptions */
+ int dten_ndesc; /* number of ECB descriptions */
+ int dten_maxdesc; /* size of ECB array */
+ dtrace_vstate_t *dten_vstate; /* associated variable state */
+ dtrace_genid_t dten_probegen; /* matched probe generation */
+ dtrace_ecbdesc_t *dten_current; /* current ECB description */
+ int dten_error; /* current error value */
+ int dten_primed; /* boolean: set if primed */
+ struct dtrace_enabling *dten_prev; /* previous enabling */
+ struct dtrace_enabling *dten_next; /* next enabling */
+} dtrace_enabling_t;
+
+/*
+ * DTrace Anonymous Enablings
+ *
+ * Anonymous enablings are DTrace enablings that are not associated with a
+ * controlling process, but rather derive their enabling from DOF stored as
+ * properties in the dtrace.conf file. If there is an anonymous enabling, a
+ * DTrace consumer state and enabling are created on attach. The state may be
+ * subsequently grabbed by the first consumer specifying the "grabanon"
+ * option. As long as an anonymous DTrace enabling exists, dtrace(7D) will
+ * refuse to unload.
+ */
+typedef struct dtrace_anon {
+ dtrace_state_t *dta_state; /* DTrace consumer state */
+ dtrace_enabling_t *dta_enabling; /* pointer to enabling */
+ processorid_t dta_beganon; /* which CPU BEGIN ran on */
+} dtrace_anon_t;
+
+/*
+ * DTrace Error Debugging
+ */
+#ifdef DEBUG
+#define DTRACE_ERRDEBUG
+#endif
+
+#ifdef DTRACE_ERRDEBUG
+
+typedef struct dtrace_errhash {
+ const char *dter_msg; /* error message */
+ int dter_count; /* number of times seen */
+} dtrace_errhash_t;
+
+#define DTRACE_ERRHASHSZ 256 /* must be > number of err msgs */
+
+#endif /* DTRACE_ERRDEBUG */
+
+/*
+ * DTrace Toxic Ranges
+ *
+ * DTrace supports safe loads from probe context; if the address turns out to
+ * be invalid, a bit will be set by the kernel indicating that DTrace
+ * encountered a memory error, and DTrace will propagate the error to the user
+ * accordingly. However, there may exist some regions of memory in which an
+ * arbitrary load can change system state, and from which it is impossible to
+ * recover from such a load after it has been attempted. Examples of this may
+ * include memory in which programmable I/O registers are mapped (for which a
+ * read may have some implications for the device) or (in the specific case of
+ * UltraSPARC-I and -II) the virtual address hole. The platform is required
+ * to make DTrace aware of these toxic ranges; DTrace will then check that
+ * target addresses are not in a toxic range before attempting to issue a
+ * safe load.
+ */
+typedef struct dtrace_toxrange {
+ uintptr_t dtt_base; /* base of toxic range */
+ uintptr_t dtt_limit; /* limit of toxic range */
+} dtrace_toxrange_t;
+
+extern uint64_t dtrace_getarg(int, int);
+extern greg_t dtrace_getfp(void);
+extern int dtrace_getipl(void);
+extern uintptr_t dtrace_caller(int);
+extern uint32_t dtrace_cas32(uint32_t *, uint32_t, uint32_t);
+extern void *dtrace_casptr(volatile void *, volatile void *, volatile void *);
+extern void dtrace_copyin(uintptr_t, uintptr_t, size_t, volatile uint16_t *);
+extern void dtrace_copyinstr(uintptr_t, uintptr_t, size_t, volatile uint16_t *);
+extern void dtrace_copyout(uintptr_t, uintptr_t, size_t, volatile uint16_t *);
+extern void dtrace_copyoutstr(uintptr_t, uintptr_t, size_t,
+ volatile uint16_t *);
+extern void dtrace_getpcstack(pc_t *, int, int, uint32_t *);
+extern ulong_t dtrace_getreg(struct regs *, uint_t);
+extern int dtrace_getstackdepth(int);
+extern void dtrace_getupcstack(uint64_t *, int);
+extern void dtrace_getufpstack(uint64_t *, uint64_t *, int);
+extern int dtrace_getustackdepth(void);
+extern uintptr_t dtrace_fulword(void *);
+extern uint8_t dtrace_fuword8(void *);
+extern uint16_t dtrace_fuword16(void *);
+extern uint32_t dtrace_fuword32(void *);
+extern uint64_t dtrace_fuword64(void *);
+extern void dtrace_probe_error(dtrace_state_t *, dtrace_epid_t, int, int,
+ int, uintptr_t);
+extern int dtrace_assfail(const char *, const char *, int);
+extern int dtrace_attached(void);
+#if defined(sun)
+extern hrtime_t dtrace_gethrestime(void);
+#endif
+
+#ifdef __sparc
+extern void dtrace_flush_windows(void);
+extern void dtrace_flush_user_windows(void);
+extern uint_t dtrace_getotherwin(void);
+extern uint_t dtrace_getfprs(void);
+#else
+extern void dtrace_copy(uintptr_t, uintptr_t, size_t);
+extern void dtrace_copystr(uintptr_t, uintptr_t, size_t, volatile uint16_t *);
+#endif
+
+/*
+ * DTrace Assertions
+ *
+ * DTrace calls ASSERT from probe context. To assure that a failed ASSERT
+ * does not induce a markedly more catastrophic failure (e.g., one from which
+ * a dump cannot be gleaned), DTrace must define its own ASSERT to be one that
+ * may safely be called from probe context. This header file must thus be
+ * included by any DTrace component that calls ASSERT from probe context, and
+ * _only_ by those components. (The only exception to this is kernel
+ * debugging infrastructure at user-level that doesn't depend on calling
+ * ASSERT.)
+ */
+#undef ASSERT
+#ifdef DEBUG
+#define ASSERT(EX) ((void)((EX) || \
+ dtrace_assfail(#EX, __FILE__, __LINE__)))
+#else
+#define ASSERT(X) ((void)0)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_DTRACE_IMPL_H */
diff --git a/sys/cddl/contrib/opensolaris/uts/common/sys/fasttrap.h b/sys/cddl/contrib/opensolaris/uts/common/sys/fasttrap.h
new file mode 100644
index 000000000000..7f803144bcf8
--- /dev/null
+++ b/sys/cddl/contrib/opensolaris/uts/common/sys/fasttrap.h
@@ -0,0 +1,93 @@
+/*
+ * 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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _SYS_FASTTRAP_H
+#define _SYS_FASTTRAP_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/fasttrap_isa.h>
+#include <sys/dtrace.h>
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define FASTTRAPIOC (('m' << 24) | ('r' << 16) | ('f' << 8))
+#define FASTTRAPIOC_MAKEPROBE (FASTTRAPIOC | 1)
+#define FASTTRAPIOC_GETINSTR (FASTTRAPIOC | 2)
+
+typedef enum fasttrap_probe_type {
+ DTFTP_NONE = 0,
+ DTFTP_ENTRY,
+ DTFTP_RETURN,
+ DTFTP_OFFSETS,
+ DTFTP_POST_OFFSETS,
+ DTFTP_IS_ENABLED
+} fasttrap_probe_type_t;
+
+typedef struct fasttrap_probe_spec {
+ pid_t ftps_pid;
+ fasttrap_probe_type_t ftps_type;
+
+ char ftps_func[DTRACE_FUNCNAMELEN];
+ char ftps_mod[DTRACE_MODNAMELEN];
+
+ uint64_t ftps_pc;
+ uint64_t ftps_size;
+ uint64_t ftps_noffs;
+ uint64_t ftps_offs[1];
+} fasttrap_probe_spec_t;
+
+typedef struct fasttrap_instr_query {
+ uint64_t ftiq_pc;
+ pid_t ftiq_pid;
+ fasttrap_instr_t ftiq_instr;
+} fasttrap_instr_query_t;
+
+/*
+ * To support the fasttrap provider from very early in a process's life,
+ * the run-time linker, ld.so.1, has a program header of type PT_SUNWDTRACE
+ * which points to a data object which must be PT_SUNWDTRACE_SIZE bytes.
+ * This structure mimics the fasttrap provider section of the ulwp_t structure.
+ * When the fasttrap provider is changed to require new or different
+ * instructions, the data object in ld.so.1 and the thread initializers in libc
+ * (libc_init() and _thrp_create()) need to be updated to include the new
+ * instructions, and PT_SUNWDTRACE needs to be changed to a new unique number
+ * (while the old value gets assigned something like PT_SUNWDTRACE_1). Since the
+ * linker must be backward compatible with old Solaris releases, it must have
+ * program headers for each of the PT_SUNWDTRACE versions. The kernel's
+ * elfexec() function only has to look for the latest version of the
+ * PT_SUNWDTRACE program header.
+ */
+#define PT_SUNWDTRACE_SIZE FASTTRAP_SUNWDTRACE_SIZE
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_FASTTRAP_H */
diff --git a/sys/cddl/contrib/opensolaris/uts/common/sys/fasttrap_impl.h b/sys/cddl/contrib/opensolaris/uts/common/sys/fasttrap_impl.h
new file mode 100644
index 000000000000..46b92caba695
--- /dev/null
+++ b/sys/cddl/contrib/opensolaris/uts/common/sys/fasttrap_impl.h
@@ -0,0 +1,205 @@
+/*
+ * 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.
+ */
+
+#ifndef _FASTTRAP_IMPL_H
+#define _FASTTRAP_IMPL_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <sys/dtrace.h>
+#include <sys/proc.h>
+#include <sys/fasttrap.h>
+#include <sys/fasttrap_isa.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Fasttrap Providers, Probes and Tracepoints
+ *
+ * Each Solaris process can have multiple providers -- the pid provider as
+ * well as any number of user-level statically defined tracing (USDT)
+ * providers. Those providers are each represented by a fasttrap_provider_t.
+ * All providers for a given process have a pointer to a shared
+ * fasttrap_proc_t. The fasttrap_proc_t has two states: active or defunct.
+ * When the count of live providers goes to zero it becomes defunct; providers
+ * drop their count when they are removed individually or en masse when a
+ * process exits or performs an exec.
+ *
+ * Each probe is represented by a fasttrap_probe_t which has a pointer to
+ * its associated provider as well as a list of fasttrap_id_tp_t structures
+ * which are tuples combining a fasttrap_id_t and a fasttrap_tracepoint_t.
+ * A fasttrap_tracepoint_t represents the actual point of instrumentation
+ * and it contains two lists of fasttrap_id_t structures (to be fired pre-
+ * and post-instruction emulation) that identify the probes attached to the
+ * tracepoint. Tracepoints also have a pointer to the fasttrap_proc_t for the
+ * process they trace which is used when looking up a tracepoint both at
+ * probe fire time and when enabling and disabling probes.
+ *
+ * It's important to note that probes are preallocated with the necessary
+ * number of tracepoints, but that tracepoints can be shared by probes and
+ * swapped between probes. If a probe's preallocated tracepoint is enabled
+ * (and, therefore, the associated probe is enabled), and that probe is
+ * then disabled, ownership of that tracepoint may be exchanged for an
+ * unused tracepoint belonging to another probe that was attached to the
+ * enabled tracepoint.
+ */
+
+typedef struct fasttrap_proc {
+ pid_t ftpc_pid; /* process ID for this proc */
+ uint64_t ftpc_acount; /* count of active providers */
+ uint64_t ftpc_rcount; /* provider reference count */
+#if defined(sun)
+ kmutex_t ftpc_mtx; /* proc lock */
+#else
+ struct mtx ftpc_mtx; /* proc lock */
+#endif
+ struct fasttrap_proc *ftpc_next; /* next proc in hash chain */
+} fasttrap_proc_t;
+
+typedef struct fasttrap_provider {
+ pid_t ftp_pid; /* process ID for this prov */
+ char ftp_name[DTRACE_PROVNAMELEN]; /* prov name (w/o the pid) */
+ dtrace_provider_id_t ftp_provid; /* DTrace provider handle */
+ uint_t ftp_marked; /* mark for possible removal */
+ uint_t ftp_retired; /* mark when retired */
+#if defined(sun)
+ kmutex_t ftp_mtx; /* provider lock */
+ kmutex_t ftp_cmtx; /* lock on creating probes */
+#else
+ struct mtx ftp_mtx; /* provider lock */
+ struct mtx ftp_cmtx; /* lock on creating probes */
+#endif
+ uint64_t ftp_rcount; /* enabled probes ref count */
+ uint64_t ftp_ccount; /* consumers creating probes */
+ uint64_t ftp_mcount; /* meta provider count */
+ fasttrap_proc_t *ftp_proc; /* shared proc for all provs */
+ struct fasttrap_provider *ftp_next; /* next prov in hash chain */
+} fasttrap_provider_t;
+
+typedef struct fasttrap_id fasttrap_id_t;
+typedef struct fasttrap_probe fasttrap_probe_t;
+typedef struct fasttrap_tracepoint fasttrap_tracepoint_t;
+
+struct fasttrap_id {
+ fasttrap_probe_t *fti_probe; /* referrring probe */
+ fasttrap_id_t *fti_next; /* enabled probe list on tp */
+ fasttrap_probe_type_t fti_ptype; /* probe type */
+};
+
+typedef struct fasttrap_id_tp {
+ fasttrap_id_t fit_id;
+ fasttrap_tracepoint_t *fit_tp;
+} fasttrap_id_tp_t;
+
+struct fasttrap_probe {
+ dtrace_id_t ftp_id; /* DTrace probe identifier */
+ pid_t ftp_pid; /* pid for this probe */
+ fasttrap_provider_t *ftp_prov; /* this probe's provider */
+ uintptr_t ftp_faddr; /* associated function's addr */
+ size_t ftp_fsize; /* associated function's size */
+ uint64_t ftp_gen; /* modification generation */
+ uint64_t ftp_ntps; /* number of tracepoints */
+ uint8_t *ftp_argmap; /* native to translated args */
+ uint8_t ftp_nargs; /* translated argument count */
+ uint8_t ftp_enabled; /* is this probe enabled */
+ char *ftp_xtypes; /* translated types index */
+ char *ftp_ntypes; /* native types index */
+ fasttrap_id_tp_t ftp_tps[1]; /* flexible array */
+};
+
+#define FASTTRAP_ID_INDEX(id) \
+((fasttrap_id_tp_t *)(((char *)(id) - offsetof(fasttrap_id_tp_t, fit_id))) - \
+&(id)->fti_probe->ftp_tps[0])
+
+struct fasttrap_tracepoint {
+ fasttrap_proc_t *ftt_proc; /* associated process struct */
+ uintptr_t ftt_pc; /* address of tracepoint */
+ pid_t ftt_pid; /* pid of tracepoint */
+ fasttrap_machtp_t ftt_mtp; /* ISA-specific portion */
+ fasttrap_id_t *ftt_ids; /* NULL-terminated list */
+ fasttrap_id_t *ftt_retids; /* NULL-terminated list */
+ fasttrap_tracepoint_t *ftt_next; /* link in global hash */
+};
+
+typedef struct fasttrap_bucket {
+#if defined(sun)
+ kmutex_t ftb_mtx; /* bucket lock */
+#else
+ struct mtx ftb_mtx; /* bucket lock */
+#endif
+ void *ftb_data; /* data payload */
+
+ uint8_t ftb_pad[64 - sizeof (struct mtx) - sizeof (void *)];
+} fasttrap_bucket_t;
+
+typedef struct fasttrap_hash {
+ ulong_t fth_nent; /* power-of-2 num. of entries */
+ ulong_t fth_mask; /* fth_nent - 1 */
+ fasttrap_bucket_t *fth_table; /* array of buckets */
+} fasttrap_hash_t;
+
+/*
+ * If at some future point these assembly functions become observable by
+ * DTrace, then these defines should become separate functions so that the
+ * fasttrap provider doesn't trigger probes during internal operations.
+ */
+#define fasttrap_copyout copyout
+#define fasttrap_fuword32 fuword32
+#define fasttrap_suword32 suword32
+
+#define fasttrap_fulword fulword
+#define fasttrap_sulword sulword
+
+extern void fasttrap_sigtrap(proc_t *, kthread_t *, uintptr_t);
+
+extern dtrace_id_t fasttrap_probe_id;
+extern fasttrap_hash_t fasttrap_tpoints;
+
+#define FASTTRAP_TPOINTS_INDEX(pid, pc) \
+ (((pc) / sizeof (fasttrap_instr_t) + (pid)) & fasttrap_tpoints.fth_mask)
+
+/*
+ * Must be implemented by fasttrap_isa.c
+ */
+extern int fasttrap_tracepoint_init(proc_t *, fasttrap_tracepoint_t *,
+ uintptr_t, fasttrap_probe_type_t);
+extern int fasttrap_tracepoint_install(proc_t *, fasttrap_tracepoint_t *);
+extern int fasttrap_tracepoint_remove(proc_t *, fasttrap_tracepoint_t *);
+
+extern int fasttrap_pid_probe(struct regs *);
+extern int fasttrap_return_probe(struct regs *);
+
+extern uint64_t fasttrap_pid_getarg(void *, dtrace_id_t, void *, int, int);
+extern uint64_t fasttrap_usdt_getarg(void *, dtrace_id_t, void *, int, int);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _FASTTRAP_IMPL_H */
diff --git a/sys/cddl/contrib/opensolaris/uts/common/sys/isa_defs.h b/sys/cddl/contrib/opensolaris/uts/common/sys/isa_defs.h
index 08fcbd02e5c0..b1b99adb8bc4 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/sys/isa_defs.h
+++ b/sys/cddl/contrib/opensolaris/uts/common/sys/isa_defs.h
@@ -206,6 +206,9 @@
* This indicates that the implementation uses a dynamically
* linked unix + krtld to form the core kernel image at boot
* time, or (in the absence of this symbol) a prelinked kernel image.
+ *
+ * _OBP
+ * This indicates the firmware interface is OBP.
*/
#ifdef __cplusplus
@@ -216,7 +219,7 @@ extern "C" {
* The following set of definitions characterize Solaris on AMD's
* 64-bit systems.
*/
-#if defined(__x86_64) || defined(__amd64)
+#if defined(__x86_64) || defined(__amd64) || defined(__ia64__)
#if !defined(__amd64)
#define __amd64 /* preferred guard */
@@ -229,7 +232,9 @@ extern "C" {
/*
* Define the appropriate "processor characteristics"
*/
+#if defined(sun)
#define _LITTLE_ENDIAN
+#endif
#define _STACK_GROWS_DOWNWARD
#define _LONG_LONG_LTOH
#define _BIT_FIELDS_LTOH
@@ -293,7 +298,9 @@ extern "C" {
/*
* Define the appropriate "processor characteristics"
*/
+#if defined(sun)
#define _LITTLE_ENDIAN
+#endif
#define _STACK_GROWS_DOWNWARD
#define _LONG_LONG_LTOH
#define _BIT_FIELDS_LTOH
@@ -332,6 +339,51 @@ extern "C" {
#define _DONT_USE_1275_GENERIC_NAMES
#define _HAVE_CPUID_INSN
+#elif defined(__arm__)
+
+/*
+ * Define the appropriate "processor characteristics"
+ */
+#define _STACK_GROWS_DOWNWARD
+#define _LONG_LONG_LTOH
+#define _BIT_FIELDS_LTOH
+#define _IEEE_754
+#define _CHAR_IS_SIGNED
+#define _BOOL_ALIGNMENT 1
+#define _CHAR_ALIGNMENT 1
+#define _SHORT_ALIGNMENT 2
+#define _INT_ALIGNMENT 4
+#define _FLOAT_ALIGNMENT 4
+#define _FLOAT_COMPLEX_ALIGNMENT 4
+#define _LONG_ALIGNMENT 4
+#define _LONG_LONG_ALIGNMENT 4
+#define _DOUBLE_ALIGNMENT 4
+#define _DOUBLE_COMPLEX_ALIGNMENT 4
+#define _LONG_DOUBLE_ALIGNMENT 4
+#define _LONG_DOUBLE_COMPLEX_ALIGNMENT 4
+#define _POINTER_ALIGNMENT 4
+#define _MAX_ALIGNMENT 4
+#define _ALIGNMENT_REQUIRED 0
+
+#define _LONG_LONG_ALIGNMENT_32 _LONG_LONG_ALIGNMENT
+
+/*
+ * Define the appropriate "implementation choices".
+ */
+#define _ILP32
+#if !defined(_I32LPx) && defined(_KERNEL)
+#define _I32LPx
+#endif
+#define _SUNOS_VTOC_16
+#define _DMA_USES_PHYSADDR
+#define _FIRMWARE_NEEDS_FDISK
+#define _PSM_MODULES
+#define _RTC_CONFIG
+#define _DONT_USE_1275_GENERIC_NAMES
+#define _HAVE_CPUID_INSN
+
+#elif defined(__powerpc__)
+
/*
* The following set of definitions characterize the Solaris on SPARC systems.
*
@@ -378,7 +430,9 @@ extern "C" {
* Define the appropriate "processor characteristics" shared between
* all Solaris on SPARC systems.
*/
+#if defined(sun)
#define _BIG_ENDIAN
+#endif
#define _STACK_GROWS_DOWNWARD
#define _LONG_LONG_HTOL
#define _BIT_FIELDS_HTOL
@@ -402,7 +456,7 @@ extern "C" {
#define _DMA_USES_VIRTADDR
#define _NO_FDISK_PRESENT
#define _HAVE_TEM_FIRMWARE
-#define _UNIX_KRTLD
+#define _OBP
/*
* The following set of definitions characterize the implementation of
diff --git a/sys/cddl/contrib/opensolaris/uts/common/sys/procset.h b/sys/cddl/contrib/opensolaris/uts/common/sys/procset.h
index c3b58675746e..c367c93966a1 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/sys/procset.h
+++ b/sys/cddl/contrib/opensolaris/uts/common/sys/procset.h
@@ -139,6 +139,7 @@ typedef struct procset {
#endif /* !defined(_XPG4_2) || defined(__EXTENSIONS__) */
+#if defined(sun)
#ifdef _KERNEL
struct proc;
@@ -152,6 +153,7 @@ extern boolean_t cur_inset_only(procset_t *);
extern id_t getmyid(idtype_t);
#endif /* _KERNEL */
+#endif
#ifdef __cplusplus
}
diff --git a/sys/cddl/contrib/opensolaris/uts/common/sys/sdt.h b/sys/cddl/contrib/opensolaris/uts/common/sys/sdt.h
deleted file mode 100644
index da695c9203c7..000000000000
--- a/sys/cddl/contrib/opensolaris/uts/common/sys/sdt.h
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef _SYS_SDT_H
-#define _SYS_SDT_H
-
-#pragma ident "%Z%%M% %I% %E% SMI"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#ifndef _KERNEL
-
-#define DTRACE_PROBE(provider, name) { \
- extern void __dtrace_##provider##___##name(void); \
- __dtrace_##provider##___##name(); \
-}
-
-#define DTRACE_PROBE1(provider, name, arg1) { \
- extern void __dtrace_##provider##___##name(unsigned long); \
- __dtrace_##provider##___##name((unsigned long)arg1); \
-}
-
-#define DTRACE_PROBE2(provider, name, arg1, arg2) { \
- extern void __dtrace_##provider##___##name(unsigned long, \
- unsigned long); \
- __dtrace_##provider##___##name((unsigned long)arg1, \
- (unsigned long)arg2); \
-}
-
-#define DTRACE_PROBE3(provider, name, arg1, arg2, arg3) { \
- extern void __dtrace_##provider##___##name(unsigned long, \
- unsigned long, unsigned long); \
- __dtrace_##provider##___##name((unsigned long)arg1, \
- (unsigned long)arg2, (unsigned long)arg3); \
-}
-
-#define DTRACE_PROBE4(provider, name, arg1, arg2, arg3, arg4) { \
- extern void __dtrace_##provider##___##name(unsigned long, \
- unsigned long, unsigned long, unsigned long); \
- __dtrace_##provider##___##name((unsigned long)arg1, \
- (unsigned long)arg2, (unsigned long)arg3, \
- (unsigned long)arg4); \
-}
-
-#define DTRACE_PROBE5(provider, name, arg1, arg2, arg3, arg4, arg5) { \
- extern void __dtrace_##provider##___##name(unsigned long, \
- unsigned long, unsigned long, unsigned long, unsigned long);\
- __dtrace_##provider##___##name((unsigned long)arg1, \
- (unsigned long)arg2, (unsigned long)arg3, \
- (unsigned long)arg4, (unsigned long)arg5); \
-}
-
-#else /* _KERNEL */
-
-#define DTRACE_PROBE(name) { \
- extern void __dtrace_probe_##name(void); \
- __dtrace_probe_##name(); \
-}
-
-#define DTRACE_PROBE1(name, type1, arg1) { \
- extern void __dtrace_probe_##name(uintptr_t); \
- __dtrace_probe_##name((uintptr_t)(arg1)); \
-}
-
-#define DTRACE_PROBE2(name, type1, arg1, type2, arg2) { \
- extern void __dtrace_probe_##name(uintptr_t, uintptr_t); \
- __dtrace_probe_##name((uintptr_t)(arg1), (uintptr_t)(arg2)); \
-}
-
-#define DTRACE_PROBE3(name, type1, arg1, type2, arg2, type3, arg3) { \
- extern void __dtrace_probe_##name(uintptr_t, uintptr_t, uintptr_t); \
- __dtrace_probe_##name((uintptr_t)(arg1), (uintptr_t)(arg2), \
- (uintptr_t)(arg3)); \
-}
-
-#define DTRACE_PROBE4(name, type1, arg1, type2, arg2, \
- type3, arg3, type4, arg4) { \
- extern void __dtrace_probe_##name(uintptr_t, uintptr_t, \
- uintptr_t, uintptr_t); \
- __dtrace_probe_##name((uintptr_t)(arg1), (uintptr_t)(arg2), \
- (uintptr_t)(arg3), (uintptr_t)(arg4)); \
-}
-
-#define DTRACE_SCHED(name) \
- DTRACE_PROBE(__sched_##name);
-
-#define DTRACE_SCHED1(name, type1, arg1) \
- DTRACE_PROBE1(__sched_##name, type1, arg1);
-
-#define DTRACE_SCHED2(name, type1, arg1, type2, arg2) \
- DTRACE_PROBE2(__sched_##name, type1, arg1, type2, arg2);
-
-#define DTRACE_SCHED3(name, type1, arg1, type2, arg2, type3, arg3) \
- DTRACE_PROBE3(__sched_##name, type1, arg1, type2, arg2, type3, arg3);
-
-#define DTRACE_SCHED4(name, type1, arg1, type2, arg2, \
- type3, arg3, type4, arg4) \
- DTRACE_PROBE4(__sched_##name, type1, arg1, type2, arg2, \
- type3, arg3, type4, arg4);
-
-#define DTRACE_PROC(name) \
- DTRACE_PROBE(__proc_##name);
-
-#define DTRACE_PROC1(name, type1, arg1) \
- DTRACE_PROBE1(__proc_##name, type1, arg1);
-
-#define DTRACE_PROC2(name, type1, arg1, type2, arg2) \
- DTRACE_PROBE2(__proc_##name, type1, arg1, type2, arg2);
-
-#define DTRACE_PROC3(name, type1, arg1, type2, arg2, type3, arg3) \
- DTRACE_PROBE3(__proc_##name, type1, arg1, type2, arg2, type3, arg3);
-
-#define DTRACE_PROC4(name, type1, arg1, type2, arg2, \
- type3, arg3, type4, arg4) \
- DTRACE_PROBE4(__proc_##name, type1, arg1, type2, arg2, \
- type3, arg3, type4, arg4);
-
-#define DTRACE_IO(name) \
- DTRACE_PROBE(__io_##name);
-
-#define DTRACE_IO1(name, type1, arg1) \
- DTRACE_PROBE1(__io_##name, type1, arg1);
-
-#define DTRACE_IO2(name, type1, arg1, type2, arg2) \
- DTRACE_PROBE2(__io_##name, type1, arg1, type2, arg2);
-
-#define DTRACE_IO3(name, type1, arg1, type2, arg2, type3, arg3) \
- DTRACE_PROBE3(__io_##name, type1, arg1, type2, arg2, type3, arg3);
-
-#define DTRACE_IO4(name, type1, arg1, type2, arg2, \
- type3, arg3, type4, arg4) \
- DTRACE_PROBE4(__io_##name, type1, arg1, type2, arg2, \
- type3, arg3, type4, arg4);
-
-#define DTRACE_SYSEVENT2(name, type1, arg1, type2, arg2) \
- DTRACE_PROBE2(__sysevent_##name, type1, arg1, type2, arg2);
-
-#endif /* _KERNEL */
-
-extern const char *sdt_prefix;
-
-typedef struct sdt_probedesc {
- char *sdpd_name; /* name of this probe */
- unsigned long sdpd_offset; /* offset of call in text */
- struct sdt_probedesc *sdpd_next; /* next static probe */
-} sdt_probedesc_t;
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _SYS_SDT_H */
diff --git a/sys/cddl/contrib/opensolaris/uts/common/sys/vmem.h b/sys/cddl/contrib/opensolaris/uts/common/sys/vmem.h
deleted file mode 100644
index 1cd2f30e9ba3..000000000000
--- a/sys/cddl/contrib/opensolaris/uts/common/sys/vmem.h
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * 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 _SYS_VMEM_H
-#define _SYS_VMEM_H
-
-#pragma ident "%Z%%M% %I% %E% SMI"
-
-#include <sys/types.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-
-/*
- * Per-allocation flags
- */
-#define VM_SLEEP 0x00000000 /* same as KM_SLEEP */
-#define VM_NOSLEEP 0x00000001 /* same as KM_NOSLEEP */
-#define VM_PANIC 0x00000002 /* same as KM_PANIC */
-#define VM_PUSHPAGE 0x00000004 /* same as KM_PUSHPAGE */
-#define VM_KMFLAGS 0x000000ff /* flags that must match KM_* flags */
-
-#define VM_BESTFIT 0x00000100
-#define VM_FIRSTFIT 0x00000200
-#define VM_NEXTFIT 0x00000400
-
-/*
- * The following flags are restricted for use only within the kernel.
- * VM_MEMLOAD is for use by the HAT to avoid infinite recursion.
- * VM_NORELOC is used by the kernel when static VA->PA mappings are required.
- */
-#define VM_MEMLOAD 0x00000800
-#define VM_NORELOC 0x00001000
-/*
- * VM_ABORT requests that vmem_alloc() *ignore* the VM_SLEEP/VM_NOSLEEP flags
- * and forgo reaping if the allocation or attempted import, fails. This
- * flag is a segkmem-specific flag, and should not be used by anyone else.
- */
-#define VM_ABORT 0x00002000
-
-#define VM_FLAGS 0x0000FFFF
-
-/*
- * Arena creation flags
- */
-#define VMC_POPULATOR 0x00010000
-#define VMC_NO_QCACHE 0x00020000 /* cannot use quantum caches */
-#define VMC_IDENTIFIER 0x00040000 /* not backed by memory */
-/*
- * internal use only; the import function uses the vmem_ximport_t interface
- * and may increase the request size if it so desires
- */
-#define VMC_XALLOC 0x00080000
-#define VMC_FLAGS 0xFFFF0000
-
-/*
- * Public segment types
- */
-#define VMEM_ALLOC 0x01
-#define VMEM_FREE 0x02
-
-/*
- * Implementation-private segment types
- */
-#define VMEM_SPAN 0x10
-#define VMEM_ROTOR 0x20
-#define VMEM_WALKER 0x40
-
-/*
- * VMEM_REENTRANT indicates to vmem_walk() that the callback routine may
- * call back into the arena being walked, so vmem_walk() must drop the
- * arena lock before each callback. The caveat is that since the arena
- * isn't locked, its state can change. Therefore it is up to the callback
- * routine to handle cases where the segment isn't of the expected type.
- * For example, we use this to walk heap_arena when generating a crash dump;
- * see segkmem_dump() for sample usage.
- */
-#define VMEM_REENTRANT 0x80000000
-
-typedef struct vmem vmem_t;
-typedef void *(vmem_alloc_t)(vmem_t *, size_t, int);
-typedef void (vmem_free_t)(vmem_t *, void *, size_t);
-
-/*
- * Alternate import style; the requested size is passed in a pointer,
- * which can be increased by the import function if desired.
- */
-typedef void *(vmem_ximport_t)(vmem_t *, size_t *, int);
-
-#ifdef _KERNEL
-extern vmem_t *vmem_init(const char *, void *, size_t, size_t,
- vmem_alloc_t *, vmem_free_t *);
-extern void vmem_update(void *);
-extern int vmem_is_populator();
-extern size_t vmem_seg_size;
-#endif
-
-extern vmem_t *vmem_create(const char *, void *, size_t, size_t,
- vmem_alloc_t *, vmem_free_t *, vmem_t *, size_t, int);
-extern vmem_t *vmem_xcreate(const char *, void *, size_t, size_t,
- vmem_ximport_t *, vmem_free_t *, vmem_t *, size_t, int);
-extern void vmem_destroy(vmem_t *);
-extern void *vmem_alloc(vmem_t *, size_t, int);
-extern void *vmem_xalloc(vmem_t *, size_t, size_t, size_t, size_t,
- void *, void *, int);
-extern void vmem_free(vmem_t *, void *, size_t);
-extern void vmem_xfree(vmem_t *, void *, size_t);
-extern void *vmem_add(vmem_t *, void *, size_t, int);
-extern int vmem_contains(vmem_t *, void *, size_t);
-extern void vmem_walk(vmem_t *, int, void (*)(void *, void *, size_t), void *);
-extern size_t vmem_size(vmem_t *, int);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _SYS_VMEM_H */
diff --git a/sys/cddl/contrib/opensolaris/uts/intel/sys/fasttrap_isa.h b/sys/cddl/contrib/opensolaris/uts/intel/sys/fasttrap_isa.h
new file mode 100644
index 000000000000..9fee8cdb6be0
--- /dev/null
+++ b/sys/cddl/contrib/opensolaris/uts/intel/sys/fasttrap_isa.h
@@ -0,0 +1,114 @@
+/*
+ * 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 2006 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
+
+#define FASTTRAP_MAX_INSTR_SIZE 15
+
+#define FASTTRAP_INSTR 0xcc
+
+#define FASTTRAP_SUNWDTRACE_SIZE 64
+
+typedef uint8_t fasttrap_instr_t;
+
+typedef struct fasttrap_machtp {
+ uint8_t ftmt_instr[FASTTRAP_MAX_INSTR_SIZE]; /* orig. instr. */
+ uint8_t ftmt_size; /* instruction size */
+#ifdef __amd64
+ uint8_t ftmt_ripmode; /* %rip-relative handling mode */
+ uint8_t ftmt_modrm; /* saved modrm byte */
+#endif
+ uint8_t ftmt_type; /* emulation type */
+ uint8_t ftmt_code; /* branch condition */
+ uint8_t ftmt_base; /* branch base */
+ uint8_t ftmt_index; /* branch index */
+ uint8_t ftmt_scale; /* branch scale */
+ uint8_t ftmt_segment; /* segment for memory accesses */
+ uintptr_t ftmt_dest; /* destination of control flow */
+} fasttrap_machtp_t;
+
+#define ftt_instr ftt_mtp.ftmt_instr
+#ifdef __amd64
+#define ftt_ripmode ftt_mtp.ftmt_ripmode
+#define ftt_modrm ftt_mtp.ftmt_modrm
+#endif
+#define ftt_size ftt_mtp.ftmt_size
+#define ftt_type ftt_mtp.ftmt_type
+#define ftt_code ftt_mtp.ftmt_code
+#define ftt_base ftt_mtp.ftmt_base
+#define ftt_index ftt_mtp.ftmt_index
+#define ftt_scale ftt_mtp.ftmt_scale
+#define ftt_segment ftt_mtp.ftmt_segment
+#define ftt_dest ftt_mtp.ftmt_dest
+
+#define FASTTRAP_T_COMMON 0x00 /* common case -- no emulation */
+#define FASTTRAP_T_JCC 0x01 /* near and far conditional jumps */
+#define FASTTRAP_T_LOOP 0x02 /* loop instructions */
+#define FASTTRAP_T_JCXZ 0x03 /* jump if %ecx/%rcx is zero */
+#define FASTTRAP_T_JMP 0x04 /* relative jump */
+#define FASTTRAP_T_CALL 0x05 /* near call (and link) */
+#define FASTTRAP_T_RET 0x06 /* ret */
+#define FASTTRAP_T_RET16 0x07 /* ret <imm16> */
+
+/*
+ * For performance rather than correctness.
+ */
+#define FASTTRAP_T_PUSHL_EBP 0x10 /* pushl %ebp (for function entry) */
+#define FASTTRAP_T_NOP 0x11 /* nop */
+
+#define FASTTRAP_RIP_1 0x1
+#define FASTTRAP_RIP_2 0x2
+#define FASTTRAP_RIP_X 0x4
+
+/*
+ * Segment values.
+ */
+#define FASTTRAP_SEG_NONE 0
+#define FASTTRAP_SEG_CS 1
+#define FASTTRAP_SEG_DS 2
+#define FASTTRAP_SEG_ES 3
+#define FASTTRAP_SEG_FS 4
+#define FASTTRAP_SEG_GS 5
+#define FASTTRAP_SEG_SS 6
+
+#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/sparc/sys/fasttrap_isa.h b/sys/cddl/contrib/opensolaris/uts/sparc/sys/fasttrap_isa.h
new file mode 100644
index 000000000000..10361cbed8de
--- /dev/null
+++ b/sys/cddl/contrib/opensolaris/uts/sparc/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/dev/cyclic/amd64/cyclic_machdep.c b/sys/cddl/dev/cyclic/amd64/cyclic_machdep.c
new file mode 100644
index 000000000000..9a15b2cdad03
--- /dev/null
+++ b/sys/cddl/dev/cyclic/amd64/cyclic_machdep.c
@@ -0,0 +1,133 @@
+/*-
+ * Copyright 2007 John Birrell <jb@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ *
+ */
+
+static void enable(cyb_arg_t);
+static void disable(cyb_arg_t);
+static void reprogram(cyb_arg_t, hrtime_t);
+static void xcall(cyb_arg_t, cpu_t *, cyc_func_t, void *);
+
+static cyc_backend_t be = {
+ NULL, /* cyb_configure */
+ NULL, /* cyb_unconfigure */
+ enable,
+ disable,
+ reprogram,
+ xcall,
+ NULL /* cyb_arg_t cyb_arg */
+};
+
+static void
+cyclic_ap_start(void *dummy)
+{
+ /* Initialise the rest of the CPUs. */
+ cyclic_mp_init();
+}
+
+SYSINIT(cyclic_ap_start, SI_SUB_SMP, SI_ORDER_ANY, cyclic_ap_start, NULL);
+
+/*
+ * Machine dependent cyclic subsystem initialisation.
+ */
+static void
+cyclic_machdep_init(void)
+{
+ /* Register the cyclic backend. */
+ cyclic_init(&be);
+}
+
+static void
+cyclic_machdep_uninit(void)
+{
+ int i;
+
+ for (i = 0; i <= mp_maxid; i++)
+ /* Reset the cyclic clock callback hook. */
+ lapic_cyclic_clock_func[i] = NULL;
+
+ /* De-register the cyclic backend. */
+ cyclic_uninit();
+}
+
+static hrtime_t exp_due[MAXCPU];
+
+/*
+ * This function is the one registered by the machine dependent
+ * initialiser as the callback for high speed timer events.
+ */
+static void
+cyclic_clock(struct trapframe *frame)
+{
+ cpu_t *c = &solaris_cpu[curcpu];
+
+ if (c->cpu_cyclic != NULL && gethrtime() >= exp_due[curcpu]) {
+ if (TRAPF_USERMODE(frame)) {
+ c->cpu_profile_pc = 0;
+ c->cpu_profile_upc = TRAPF_PC(frame);
+ } else {
+ c->cpu_profile_pc = TRAPF_PC(frame);
+ c->cpu_profile_upc = 0;
+ }
+
+ c->cpu_intr_actv = 1;
+
+ /* Fire any timers that are due. */
+ cyclic_fire(c);
+
+ c->cpu_intr_actv = 0;
+ }
+}
+
+static void enable(cyb_arg_t arg)
+{
+ /* Register the cyclic clock callback function. */
+ lapic_cyclic_clock_func[curcpu] = cyclic_clock;
+}
+
+static void disable(cyb_arg_t arg)
+{
+ /* Reset the cyclic clock callback function. */
+ lapic_cyclic_clock_func[curcpu] = NULL;
+}
+
+static void reprogram(cyb_arg_t arg, hrtime_t exp)
+{
+ exp_due[curcpu] = exp;
+}
+
+static void xcall(cyb_arg_t arg, cpu_t *c, cyc_func_t func, void *param)
+{
+ /*
+ * If the target CPU is the current one, just call the
+ * function. This covers the non-SMP case.
+ */
+ if (c == &solaris_cpu[curcpu])
+ (*func)(param);
+ else
+ smp_rendezvous_cpus((cpumask_t) (1 << c->cpuid), NULL,
+ func, smp_no_rendevous_barrier, param);
+}
diff --git a/sys/cddl/dev/cyclic/cyclic.c b/sys/cddl/dev/cyclic/cyclic.c
new file mode 100644
index 000000000000..9d4d2d076d97
--- /dev/null
+++ b/sys/cddl/dev/cyclic/cyclic.c
@@ -0,0 +1,1421 @@
+/*
+ * 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 2008 John Birrell <jb@freebsd.org>
+ *
+ * $FreeBSD$
+ *
+ * This is a simplified version of the cyclic timer subsystem from
+ * OpenSolaris. In the FreeBSD version, we don't use interrupt levels.
+ */
+
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * The Cyclic Subsystem
+ * --------------------
+ *
+ * Prehistory
+ *
+ * Historically, most computer architectures have specified interval-based
+ * timer parts (e.g. SPARCstation's counter/timer; Intel's i8254). While
+ * these parts deal in relative (i.e. not absolute) time values, they are
+ * typically used by the operating system to implement the abstraction of
+ * absolute time. As a result, these parts cannot typically be reprogrammed
+ * without introducing error in the system's notion of time.
+ *
+ * Starting in about 1994, chip architectures began specifying high resolution
+ * timestamp registers. As of this writing (1999), all major chip families
+ * (UltraSPARC, PentiumPro, MIPS, PowerPC, Alpha) have high resolution
+ * timestamp registers, and two (UltraSPARC and MIPS) have added the capacity
+ * to interrupt based on timestamp values. These timestamp-compare registers
+ * present a time-based interrupt source which can be reprogrammed arbitrarily
+ * often without introducing error. Given the low cost of implementing such a
+ * timestamp-compare register (and the tangible benefit of eliminating
+ * discrete timer parts), it is reasonable to expect that future chip
+ * architectures will adopt this feature.
+ *
+ * The cyclic subsystem has been designed to take advantage of chip
+ * architectures with the capacity to interrupt based on absolute, high
+ * resolution values of time.
+ *
+ * Subsystem Overview
+ *
+ * The cyclic subsystem is a low-level kernel subsystem designed to provide
+ * arbitrarily high resolution, per-CPU interval timers (to avoid colliding
+ * with existing terms, we dub such an interval timer a "cyclic").
+ * Alternatively, a cyclic may be specified to be "omnipresent", denoting
+ * firing on all online CPUs.
+ *
+ * Cyclic Subsystem Interface Overview
+ * -----------------------------------
+ *
+ * The cyclic subsystem has interfaces with the kernel at-large, with other
+ * kernel subsystems (e.g. the processor management subsystem, the checkpoint
+ * resume subsystem) and with the platform (the cyclic backend). Each
+ * of these interfaces is given a brief synopsis here, and is described
+ * in full above the interface's implementation.
+ *
+ * The following diagram displays the cyclic subsystem's interfaces to
+ * other kernel components. The arrows denote a "calls" relationship, with
+ * the large arrow indicating the cyclic subsystem's consumer interface.
+ * Each arrow is labeled with the section in which the corresponding
+ * interface is described.
+ *
+ * Kernel at-large consumers
+ * -----------++------------
+ * ||
+ * ||
+ * _||_
+ * \ /
+ * \/
+ * +---------------------+
+ * | |
+ * | Cyclic subsystem |<----------- Other kernel subsystems
+ * | |
+ * +---------------------+
+ * ^ |
+ * | |
+ * | |
+ * | v
+ * +---------------------+
+ * | |
+ * | Cyclic backend |
+ * | (platform specific) |
+ * | |
+ * +---------------------+
+ *
+ *
+ * Kernel At-Large Interfaces
+ *
+ * cyclic_add() <-- Creates a cyclic
+ * cyclic_add_omni() <-- Creates an omnipresent cyclic
+ * cyclic_remove() <-- Removes a cyclic
+ *
+ * Backend Interfaces
+ *
+ * cyclic_init() <-- Initializes the cyclic subsystem
+ * cyclic_fire() <-- Interrupt entry point
+ *
+ * The backend-supplied interfaces (through the cyc_backend structure) are
+ * documented in detail in <sys/cyclic_impl.h>
+ *
+ *
+ * Cyclic Subsystem Implementation Overview
+ * ----------------------------------------
+ *
+ * The cyclic subsystem is designed to minimize interference between cyclics
+ * on different CPUs. Thus, all of the cyclic subsystem's data structures
+ * hang off of a per-CPU structure, cyc_cpu.
+ *
+ * Each cyc_cpu has a power-of-two sized array of cyclic structures (the
+ * cyp_cyclics member of the cyc_cpu structure). If cyclic_add() is called
+ * and there does not exist a free slot in the cyp_cyclics array, the size of
+ * the array will be doubled. The array will never shrink. Cyclics are
+ * referred to by their index in the cyp_cyclics array, which is of type
+ * cyc_index_t.
+ *
+ * The cyclics are kept sorted by expiration time in the cyc_cpu's heap. The
+ * heap is keyed by cyclic expiration time, with parents expiring earlier
+ * than their children.
+ *
+ * Heap Management
+ *
+ * The heap is managed primarily by cyclic_fire(). Upon entry, cyclic_fire()
+ * compares the root cyclic's expiration time to the current time. If the
+ * expiration time is in the past, cyclic_expire() is called on the root
+ * cyclic. Upon return from cyclic_expire(), the cyclic's new expiration time
+ * is derived by adding its interval to its old expiration time, and a
+ * downheap operation is performed. After the downheap, cyclic_fire()
+ * examines the (potentially changed) root cyclic, repeating the
+ * cyclic_expire()/add interval/cyclic_downheap() sequence until the root
+ * cyclic has an expiration time in the future. This expiration time
+ * (guaranteed to be the earliest in the heap) is then communicated to the
+ * backend via cyb_reprogram. Optimal backends will next call cyclic_fire()
+ * shortly after the root cyclic's expiration time.
+ *
+ * To allow efficient, deterministic downheap operations, we implement the
+ * heap as an array (the cyp_heap member of the cyc_cpu structure), with each
+ * element containing an index into the CPU's cyp_cyclics array.
+ *
+ * The heap is laid out in the array according to the following:
+ *
+ * 1. The root of the heap is always in the 0th element of the heap array
+ * 2. The left and right children of the nth element are element
+ * (((n + 1) << 1) - 1) and element ((n + 1) << 1), respectively.
+ *
+ * This layout is standard (see, e.g., Cormen's "Algorithms"); the proof
+ * that these constraints correctly lay out a heap (or indeed, any binary
+ * tree) is trivial and left to the reader.
+ *
+ * To see the heap by example, assume our cyclics array has the following
+ * members (at time t):
+ *
+ * cy_handler cy_expire
+ * ---------------------------------------------
+ * [ 0] clock() t+10000000
+ * [ 1] deadman() t+1000000000
+ * [ 2] clock_highres_fire() t+100
+ * [ 3] clock_highres_fire() t+1000
+ * [ 4] clock_highres_fire() t+500
+ * [ 5] (free) --
+ * [ 6] (free) --
+ * [ 7] (free) --
+ *
+ * The heap array could be:
+ *
+ * [0] [1] [2] [3] [4] [5] [6] [7]
+ * +-----+-----+-----+-----+-----+-----+-----+-----+
+ * | | | | | | | | |
+ * | 2 | 3 | 4 | 0 | 1 | x | x | x |
+ * | | | | | | | | |
+ * +-----+-----+-----+-----+-----+-----+-----+-----+
+ *
+ * Graphically, this array corresponds to the following (excuse the ASCII art):
+ *
+ * 2
+ * |
+ * +------------------+------------------+
+ * 3 4
+ * |
+ * +---------+--------+
+ * 0 1
+ *
+ * Note that the heap is laid out by layer: all nodes at a given depth are
+ * stored in consecutive elements of the array. Moreover, layers of
+ * consecutive depths are in adjacent element ranges. This property
+ * guarantees high locality of reference during downheap operations.
+ * Specifically, we are guaranteed that we can downheap to a depth of
+ *
+ * lg (cache_line_size / sizeof (cyc_index_t))
+ *
+ * nodes with at most one cache miss. On UltraSPARC (64 byte e-cache line
+ * size), this corresponds to a depth of four nodes. Thus, if there are
+ * fewer than sixteen cyclics in the heap, downheaps on UltraSPARC miss at
+ * most once in the e-cache.
+ *
+ * Downheaps are required to compare siblings as they proceed down the
+ * heap. For downheaps proceeding beyond the one-cache-miss depth, every
+ * access to a left child could potentially miss in the cache. However,
+ * if we assume
+ *
+ * (cache_line_size / sizeof (cyc_index_t)) > 2,
+ *
+ * then all siblings are guaranteed to be on the same cache line. Thus, the
+ * miss on the left child will guarantee a hit on the right child; downheaps
+ * will incur at most one cache miss per layer beyond the one-cache-miss
+ * depth. The total number of cache misses for heap management during a
+ * downheap operation is thus bounded by
+ *
+ * lg (n) - lg (cache_line_size / sizeof (cyc_index_t))
+ *
+ * Traditional pointer-based heaps are implemented without regard to
+ * locality. Downheaps can thus incur two cache misses per layer (one for
+ * each child), but at most one cache miss at the root. This yields a bound
+ * of
+ *
+ * 2 * lg (n) - 1
+ *
+ * on the total cache misses.
+ *
+ * This difference may seem theoretically trivial (the difference is, after
+ * all, constant), but can become substantial in practice -- especially for
+ * caches with very large cache lines and high miss penalties (e.g. TLBs).
+ *
+ * Heaps must always be full, balanced trees. Heap management must therefore
+ * track the next point-of-insertion into the heap. In pointer-based heaps,
+ * recomputing this point takes O(lg (n)). Given the layout of the
+ * array-based implementation, however, the next point-of-insertion is
+ * always:
+ *
+ * heap[number_of_elements]
+ *
+ * We exploit this property by implementing the free-list in the usused
+ * heap elements. Heap insertion, therefore, consists only of filling in
+ * the cyclic at cyp_cyclics[cyp_heap[number_of_elements]], incrementing
+ * the number of elements, and performing an upheap. Heap deletion consists
+ * of decrementing the number of elements, swapping the to-be-deleted element
+ * with the element at cyp_heap[number_of_elements], and downheaping.
+ *
+ * Filling in more details in our earlier example:
+ *
+ * +--- free list head
+ * |
+ * V
+ *
+ * [0] [1] [2] [3] [4] [5] [6] [7]
+ * +-----+-----+-----+-----+-----+-----+-----+-----+
+ * | | | | | | | | |
+ * | 2 | 3 | 4 | 0 | 1 | 5 | 6 | 7 |
+ * | | | | | | | | |
+ * +-----+-----+-----+-----+-----+-----+-----+-----+
+ *
+ * To insert into this heap, we would just need to fill in the cyclic at
+ * cyp_cyclics[5], bump the number of elements (from 5 to 6) and perform
+ * an upheap.
+ *
+ * If we wanted to remove, say, cyp_cyclics[3], we would first scan for it
+ * in the cyp_heap, and discover it at cyp_heap[1]. We would then decrement
+ * the number of elements (from 5 to 4), swap cyp_heap[1] with cyp_heap[4],
+ * and perform a downheap from cyp_heap[1]. The linear scan is required
+ * because the cyclic does not keep a backpointer into the heap. This makes
+ * heap manipulation (e.g. downheaps) faster at the expense of removal
+ * operations.
+ *
+ * Expiry processing
+ *
+ * As alluded to above, cyclic_expire() is called by cyclic_fire() to expire
+ * a cyclic. Cyclic subsystem consumers are guaranteed that for an arbitrary
+ * time t in the future, their cyclic handler will have been called
+ * (t - cyt_when) / cyt_interval times. cyclic_expire() simply needs to call
+ * the handler.
+ *
+ * Resizing
+ *
+ * All of the discussion thus far has assumed a static number of cyclics.
+ * Obviously, static limitations are not practical; we need the capacity
+ * to resize our data structures dynamically.
+ *
+ * We resize our data structures lazily, and only on a per-CPU basis.
+ * The size of the data structures always doubles and never shrinks. We
+ * serialize adds (and thus resizes) on cpu_lock; we never need to deal
+ * with concurrent resizes. Resizes should be rare; they may induce jitter
+ * on the CPU being resized, but should not affect cyclic operation on other
+ * CPUs.
+ *
+ * Three key cyc_cpu data structures need to be resized: the cyclics array,
+ * nad the heap array. Resizing is relatively straightforward:
+ *
+ * 1. The new, larger arrays are allocated in cyclic_expand() (called
+ * from cyclic_add()).
+ * 2. The contents of the old arrays are copied into the new arrays.
+ * 3. The old cyclics array is bzero()'d
+ * 4. The pointers are updated.
+ *
+ * Removals
+ *
+ * Cyclic removals should be rare. To simplify the implementation (and to
+ * allow optimization for the cyclic_fire()/cyclic_expire()
+ * path), we force removals and adds to serialize on cpu_lock.
+ *
+ */
+#include <sys/cdefs.h>
+#include <sys/param.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/sx.h>
+#include <sys/cyclic_impl.h>
+#include <sys/module.h>
+#include <sys/systm.h>
+#include <sys/atomic.h>
+#include <sys/kmem.h>
+#include <sys/cmn_err.h>
+#include <sys/dtrace_bsd.h>
+#include <machine/cpu.h>
+
+static kmem_cache_t *cyclic_id_cache;
+static cyc_id_t *cyclic_id_head;
+static cyc_backend_t cyclic_backend;
+
+MALLOC_DEFINE(M_CYCLIC, "cyclic", "Cyclic timer subsystem");
+
+/*
+ * Returns 1 if the upheap propagated to the root, 0 if it did not. This
+ * allows the caller to reprogram the backend only when the root has been
+ * modified.
+ */
+static int
+cyclic_upheap(cyc_cpu_t *cpu, cyc_index_t ndx)
+{
+ cyclic_t *cyclics;
+ cyc_index_t *heap;
+ cyc_index_t heap_parent, heap_current = ndx;
+ cyc_index_t parent, current;
+
+ if (heap_current == 0)
+ return (1);
+
+ heap = cpu->cyp_heap;
+ cyclics = cpu->cyp_cyclics;
+ heap_parent = CYC_HEAP_PARENT(heap_current);
+
+ for (;;) {
+ current = heap[heap_current];
+ parent = heap[heap_parent];
+
+ /*
+ * We have an expiration time later than our parent; we're
+ * done.
+ */
+ if (cyclics[current].cy_expire >= cyclics[parent].cy_expire)
+ return (0);
+
+ /*
+ * We need to swap with our parent, and continue up the heap.
+ */
+ heap[heap_parent] = current;
+ heap[heap_current] = parent;
+
+ /*
+ * If we just reached the root, we're done.
+ */
+ if (heap_parent == 0)
+ return (1);
+
+ heap_current = heap_parent;
+ heap_parent = CYC_HEAP_PARENT(heap_current);
+ }
+}
+
+static void
+cyclic_downheap(cyc_cpu_t *cpu, cyc_index_t ndx)
+{
+ cyclic_t *cyclics = cpu->cyp_cyclics;
+ cyc_index_t *heap = cpu->cyp_heap;
+
+ cyc_index_t heap_left, heap_right, heap_me = ndx;
+ cyc_index_t left, right, me;
+ cyc_index_t nelems = cpu->cyp_nelems;
+
+ for (;;) {
+ /*
+ * If we don't have a left child (i.e., we're a leaf), we're
+ * done.
+ */
+ if ((heap_left = CYC_HEAP_LEFT(heap_me)) >= nelems)
+ return;
+
+ left = heap[heap_left];
+ me = heap[heap_me];
+
+ heap_right = CYC_HEAP_RIGHT(heap_me);
+
+ /*
+ * Even if we don't have a right child, we still need to compare
+ * our expiration time against that of our left child.
+ */
+ if (heap_right >= nelems)
+ goto comp_left;
+
+ right = heap[heap_right];
+
+ /*
+ * We have both a left and a right child. We need to compare
+ * the expiration times of the children to determine which
+ * expires earlier.
+ */
+ if (cyclics[right].cy_expire < cyclics[left].cy_expire) {
+ /*
+ * Our right child is the earlier of our children.
+ * We'll now compare our expiration time to its; if
+ * ours is the earlier, we're done.
+ */
+ if (cyclics[me].cy_expire <= cyclics[right].cy_expire)
+ return;
+
+ /*
+ * Our right child expires earlier than we do; swap
+ * with our right child, and descend right.
+ */
+ heap[heap_right] = me;
+ heap[heap_me] = right;
+ heap_me = heap_right;
+ continue;
+ }
+
+comp_left:
+ /*
+ * Our left child is the earlier of our children (or we have
+ * no right child). We'll now compare our expiration time
+ * to its; if ours is the earlier, we're done.
+ */
+ if (cyclics[me].cy_expire <= cyclics[left].cy_expire)
+ return;
+
+ /*
+ * Our left child expires earlier than we do; swap with our
+ * left child, and descend left.
+ */
+ heap[heap_left] = me;
+ heap[heap_me] = left;
+ heap_me = heap_left;
+ }
+}
+
+static void
+cyclic_expire(cyc_cpu_t *cpu, cyc_index_t ndx, cyclic_t *cyclic)
+{
+ cyc_func_t handler = cyclic->cy_handler;
+ void *arg = cyclic->cy_arg;
+
+ (*handler)(arg);
+}
+
+static void
+cyclic_enable_xcall(void *v)
+{
+ cyc_xcallarg_t *argp = v;
+ cyc_cpu_t *cpu = argp->cyx_cpu;
+ cyc_backend_t *be = cpu->cyp_backend;
+
+ be->cyb_enable(be->cyb_arg);
+}
+
+static void
+cyclic_enable(cyc_cpu_t *cpu)
+{
+ cyc_backend_t *be = cpu->cyp_backend;
+ cyc_xcallarg_t arg;
+
+ arg.cyx_cpu = cpu;
+
+ /* Cross call to the target CPU */
+ be->cyb_xcall(be->cyb_arg, cpu->cyp_cpu, cyclic_enable_xcall, &arg);
+}
+
+static void
+cyclic_disable_xcall(void *v)
+{
+ cyc_xcallarg_t *argp = v;
+ cyc_cpu_t *cpu = argp->cyx_cpu;
+ cyc_backend_t *be = cpu->cyp_backend;
+
+ be->cyb_disable(be->cyb_arg);
+}
+
+static void
+cyclic_disable(cyc_cpu_t *cpu)
+{
+ cyc_backend_t *be = cpu->cyp_backend;
+ cyc_xcallarg_t arg;
+
+ arg.cyx_cpu = cpu;
+
+ /* Cross call to the target CPU */
+ be->cyb_xcall(be->cyb_arg, cpu->cyp_cpu, cyclic_disable_xcall, &arg);
+}
+
+static void
+cyclic_reprogram_xcall(void *v)
+{
+ cyc_xcallarg_t *argp = v;
+ cyc_cpu_t *cpu = argp->cyx_cpu;
+ cyc_backend_t *be = cpu->cyp_backend;
+
+ be->cyb_reprogram(be->cyb_arg, argp->cyx_exp);
+}
+
+static void
+cyclic_reprogram(cyc_cpu_t *cpu, hrtime_t exp)
+{
+ cyc_backend_t *be = cpu->cyp_backend;
+ cyc_xcallarg_t arg;
+
+ arg.cyx_cpu = cpu;
+ arg.cyx_exp = exp;
+
+ /* Cross call to the target CPU */
+ be->cyb_xcall(be->cyb_arg, cpu->cyp_cpu, cyclic_reprogram_xcall, &arg);
+}
+
+/*
+ * cyclic_fire(cpu_t *)
+ *
+ * Overview
+ *
+ * cyclic_fire() is the cyclic subsystem's interrupt handler.
+ * Called by the cyclic backend.
+ *
+ * Arguments and notes
+ *
+ * The only argument is the CPU on which the interrupt is executing;
+ * backends must call into cyclic_fire() on the specified CPU.
+ *
+ * cyclic_fire() may be called spuriously without ill effect. Optimal
+ * backends will call into cyclic_fire() at or shortly after the time
+ * requested via cyb_reprogram(). However, calling cyclic_fire()
+ * arbitrarily late will only manifest latency bubbles; the correctness
+ * of the cyclic subsystem does not rely on the timeliness of the backend.
+ *
+ * cyclic_fire() is wait-free; it will not block or spin.
+ *
+ * Return values
+ *
+ * None.
+ *
+ */
+static void
+cyclic_fire(cpu_t *c)
+{
+ cyc_cpu_t *cpu = c->cpu_cyclic;
+
+ mtx_lock_spin(&cpu->cyp_mtx);
+
+ cyc_index_t *heap = cpu->cyp_heap;
+ cyclic_t *cyclic, *cyclics = cpu->cyp_cyclics;
+ hrtime_t now = gethrtime();
+ hrtime_t exp;
+
+ if (cpu->cyp_nelems == 0) {
+ /* This is a spurious fire. */
+ mtx_unlock_spin(&cpu->cyp_mtx);
+ return;
+ }
+
+ for (;;) {
+ cyc_index_t ndx = heap[0];
+
+ cyclic = &cyclics[ndx];
+
+ ASSERT(!(cyclic->cy_flags & CYF_FREE));
+
+ if ((exp = cyclic->cy_expire) > now)
+ break;
+
+ cyclic_expire(cpu, ndx, cyclic);
+
+ /*
+ * If this cyclic will be set to next expire in the distant
+ * past, we have one of two situations:
+ *
+ * a) This is the first firing of a cyclic which had
+ * cy_expire set to 0.
+ *
+ * b) We are tragically late for a cyclic -- most likely
+ * due to being in the debugger.
+ *
+ * In either case, we set the new expiration time to be the
+ * the next interval boundary. This assures that the
+ * expiration time modulo the interval is invariant.
+ *
+ * We arbitrarily define "distant" to be one second (one second
+ * is chosen because it's shorter than any foray to the
+ * debugger while still being longer than any legitimate
+ * stretch).
+ */
+ exp += cyclic->cy_interval;
+
+ if (now - exp > NANOSEC) {
+ hrtime_t interval = cyclic->cy_interval;
+
+ exp += ((now - exp) / interval + 1) * interval;
+ }
+
+ cyclic->cy_expire = exp;
+ cyclic_downheap(cpu, 0);
+ }
+
+ /*
+ * Now we have a cyclic in the root slot which isn't in the past;
+ * reprogram the interrupt source.
+ */
+ cyclic_reprogram(cpu, exp);
+
+ mtx_unlock_spin(&cpu->cyp_mtx);
+}
+
+/*
+ * cyclic_expand() will cross call onto the CPU to perform the actual
+ * expand operation.
+ */
+static void
+cyclic_expand(cyc_cpu_t *cpu)
+{
+ cyc_index_t new_size, old_size, i;
+ cyc_index_t *new_heap, *old_heap;
+ cyclic_t *new_cyclics, *old_cyclics;
+
+ ASSERT(MUTEX_HELD(&cpu_lock));
+
+ if ((new_size = ((old_size = cpu->cyp_size) << 1)) == 0)
+ new_size = CY_DEFAULT_PERCPU;
+
+ /*
+ * Check that the new_size is a power of 2.
+ */
+ ASSERT(((new_size - 1) & new_size) == 0);
+
+ /* Unlock the mutex while allocating memory so we can wait... */
+ mtx_unlock_spin(&cpu->cyp_mtx);
+
+ new_heap = malloc(sizeof(cyc_index_t) * new_size, M_CYCLIC, M_WAITOK);
+ new_cyclics = malloc(sizeof(cyclic_t) * new_size, M_CYCLIC, M_ZERO | M_WAITOK);
+
+ /* Grab the lock again now we've got the memory... */
+ mtx_lock_spin(&cpu->cyp_mtx);
+
+ /* Check if another thread beat us while the mutex was unlocked. */
+ if (old_size != cpu->cyp_size) {
+ /* Oh well, he won. */
+ mtx_unlock_spin(&cpu->cyp_mtx);
+
+ free(new_heap, M_CYCLIC);
+ free(new_cyclics, M_CYCLIC);
+
+ mtx_lock_spin(&cpu->cyp_mtx);
+ return;
+ }
+
+ old_heap = cpu->cyp_heap;
+ old_cyclics = cpu->cyp_cyclics;
+
+ bcopy(cpu->cyp_heap, new_heap, sizeof (cyc_index_t) * old_size);
+ bcopy(old_cyclics, new_cyclics, sizeof (cyclic_t) * old_size);
+
+ /*
+ * Set up the free list, and set all of the new cyclics to be CYF_FREE.
+ */
+ for (i = old_size; i < new_size; i++) {
+ new_heap[i] = i;
+ new_cyclics[i].cy_flags = CYF_FREE;
+ }
+
+ /*
+ * We can go ahead and plow the value of cyp_heap and cyp_cyclics;
+ * cyclic_expand() has kept a copy.
+ */
+ cpu->cyp_heap = new_heap;
+ cpu->cyp_cyclics = new_cyclics;
+ cpu->cyp_size = new_size;
+
+ if (old_cyclics != NULL) {
+ ASSERT(old_heap != NULL);
+ ASSERT(old_size != 0);
+ mtx_unlock_spin(&cpu->cyp_mtx);
+
+ free(old_cyclics, M_CYCLIC);
+ free(old_heap, M_CYCLIC);
+
+ mtx_lock_spin(&cpu->cyp_mtx);
+ }
+}
+
+static cyc_index_t
+cyclic_add_here(cyc_cpu_t *cpu, cyc_handler_t *hdlr,
+ cyc_time_t *when, uint16_t flags)
+{
+ cyc_index_t ndx, nelems;
+ cyclic_t *cyclic;
+
+ ASSERT(MUTEX_HELD(&cpu_lock));
+
+ mtx_lock_spin(&cpu->cyp_mtx);
+
+ ASSERT(!(cpu->cyp_cpu->cpu_flags & CPU_OFFLINE));
+ ASSERT(when->cyt_when >= 0 && when->cyt_interval > 0);
+
+ while (cpu->cyp_nelems == cpu->cyp_size)
+ cyclic_expand(cpu);
+
+ ASSERT(cpu->cyp_nelems < cpu->cyp_size);
+
+ nelems = cpu->cyp_nelems++;
+
+ if (nelems == 0)
+ /*
+ * If this is the first element, we need to enable the
+ * backend on this CPU.
+ */
+ cyclic_enable(cpu);
+
+ ndx = cpu->cyp_heap[nelems];
+ cyclic = &cpu->cyp_cyclics[ndx];
+
+ ASSERT(cyclic->cy_flags == CYF_FREE);
+ cyclic->cy_interval = when->cyt_interval;
+
+ if (when->cyt_when == 0)
+ cyclic->cy_expire = gethrtime() + cyclic->cy_interval;
+ else
+ cyclic->cy_expire = when->cyt_when;
+
+ cyclic->cy_handler = hdlr->cyh_func;
+ cyclic->cy_arg = hdlr->cyh_arg;
+ cyclic->cy_flags = flags;
+
+ if (cyclic_upheap(cpu, nelems)) {
+ hrtime_t exp = cyclic->cy_expire;
+
+ /*
+ * If our upheap propagated to the root, we need to
+ * reprogram the interrupt source.
+ */
+ cyclic_reprogram(cpu, exp);
+ }
+
+ mtx_unlock_spin(&cpu->cyp_mtx);
+
+ return (ndx);
+}
+
+
+static int
+cyclic_remove_here(cyc_cpu_t *cpu, cyc_index_t ndx, cyc_time_t *when, int wait)
+{
+ cyc_index_t nelems, i;
+ cyclic_t *cyclic;
+ cyc_index_t *heap, last;
+
+ ASSERT(MUTEX_HELD(&cpu_lock));
+ ASSERT(wait == CY_WAIT || wait == CY_NOWAIT);
+
+ mtx_lock_spin(&cpu->cyp_mtx);
+
+ heap = cpu->cyp_heap;
+
+ nelems = cpu->cyp_nelems;
+
+ cyclic = &cpu->cyp_cyclics[ndx];
+
+ /*
+ * Grab the current expiration time. If this cyclic is being
+ * removed as part of a juggling operation, the expiration time
+ * will be used when the cyclic is added to the new CPU.
+ */
+ if (when != NULL) {
+ when->cyt_when = cyclic->cy_expire;
+ when->cyt_interval = cyclic->cy_interval;
+ }
+
+ cyclic->cy_flags = CYF_FREE;
+
+ for (i = 0; i < nelems; i++) {
+ if (heap[i] == ndx)
+ break;
+ }
+
+ if (i == nelems)
+ panic("attempt to remove non-existent cyclic");
+
+ cpu->cyp_nelems = --nelems;
+
+ if (nelems == 0)
+ /*
+ * If we just removed the last element, then we need to
+ * disable the backend on this CPU.
+ */
+ cyclic_disable(cpu);
+
+ if (i == nelems)
+ /*
+ * If we just removed the last element of the heap, then
+ * we don't have to downheap.
+ */
+ goto done;
+
+ /*
+ * Swap the last element of the heap with the one we want to
+ * remove, and downheap (this has the implicit effect of putting
+ * the newly freed element on the free list).
+ */
+ heap[i] = (last = heap[nelems]);
+ heap[nelems] = ndx;
+
+ if (i == 0)
+ cyclic_downheap(cpu, 0);
+ else {
+ if (cyclic_upheap(cpu, i) == 0) {
+ /*
+ * The upheap didn't propagate to the root; if it
+ * didn't propagate at all, we need to downheap.
+ */
+ if (heap[i] == last)
+ cyclic_downheap(cpu, i);
+ goto done;
+ }
+ }
+
+ /*
+ * We're here because we changed the root; we need to reprogram
+ * the clock source.
+ */
+ cyclic = &cpu->cyp_cyclics[heap[0]];
+
+ ASSERT(nelems != 0);
+ cyclic_reprogram(cpu, cyclic->cy_expire);
+
+done:
+ mtx_unlock_spin(&cpu->cyp_mtx);
+
+ return (1);
+}
+
+static void
+cyclic_configure(cpu_t *c)
+{
+ cyc_cpu_t *cpu = malloc(sizeof(cyc_cpu_t), M_CYCLIC, M_ZERO | M_WAITOK);
+ cyc_backend_t *nbe = malloc(sizeof(cyc_backend_t), M_CYCLIC, M_ZERO | M_WAITOK);
+
+ ASSERT(MUTEX_HELD(&cpu_lock));
+
+ if (cyclic_id_cache == NULL)
+ cyclic_id_cache = kmem_cache_create("cyclic_id_cache",
+ sizeof (cyc_id_t), 0, NULL, NULL, NULL, NULL, NULL, 0);
+
+ cpu->cyp_cpu = c;
+
+ cpu->cyp_size = 1;
+ cpu->cyp_heap = malloc(sizeof(cyc_index_t), M_CYCLIC, M_ZERO | M_WAITOK);
+ cpu->cyp_cyclics = malloc(sizeof(cyclic_t), M_CYCLIC, M_ZERO | M_WAITOK);
+ cpu->cyp_cyclics->cy_flags = CYF_FREE;
+
+ mtx_init(&cpu->cyp_mtx, "cyclic cpu", NULL, MTX_SPIN);
+
+ /*
+ * Setup the backend for this CPU.
+ */
+ bcopy(&cyclic_backend, nbe, sizeof (cyc_backend_t));
+ if (nbe->cyb_configure != NULL)
+ nbe->cyb_arg = nbe->cyb_configure(c);
+ cpu->cyp_backend = nbe;
+
+ /*
+ * On platforms where stray interrupts may be taken during startup,
+ * the CPU's cpu_cyclic pointer serves as an indicator that the
+ * cyclic subsystem for this CPU is prepared to field interrupts.
+ */
+ membar_producer();
+
+ c->cpu_cyclic = cpu;
+}
+
+static void
+cyclic_unconfigure(cpu_t *c)
+{
+ cyc_cpu_t *cpu = c->cpu_cyclic;
+ cyc_backend_t *be = cpu->cyp_backend;
+ cyb_arg_t bar = be->cyb_arg;
+
+ ASSERT(MUTEX_HELD(&cpu_lock));
+
+ c->cpu_cyclic = NULL;
+
+ /*
+ * Let the backend know that the CPU is being yanked, and free up
+ * the backend structure.
+ */
+ if (be->cyb_unconfigure != NULL)
+ be->cyb_unconfigure(bar);
+ free(be, M_CYCLIC);
+ cpu->cyp_backend = NULL;
+
+ mtx_destroy(&cpu->cyp_mtx);
+
+ /* Finally, clean up our remaining dynamic structures. */
+ free(cpu->cyp_cyclics, M_CYCLIC);
+ free(cpu->cyp_heap, M_CYCLIC);
+ free(cpu, M_CYCLIC);
+}
+
+static void
+cyclic_omni_start(cyc_id_t *idp, cyc_cpu_t *cpu)
+{
+ cyc_omni_handler_t *omni = &idp->cyi_omni_hdlr;
+ cyc_omni_cpu_t *ocpu = malloc(sizeof(cyc_omni_cpu_t), M_CYCLIC , M_WAITOK);
+ cyc_handler_t hdlr;
+ cyc_time_t when;
+
+ ASSERT(MUTEX_HELD(&cpu_lock));
+ ASSERT(idp->cyi_cpu == NULL);
+
+ hdlr.cyh_func = NULL;
+ hdlr.cyh_arg = NULL;
+
+ when.cyt_when = 0;
+ when.cyt_interval = 0;
+
+ omni->cyo_online(omni->cyo_arg, cpu->cyp_cpu, &hdlr, &when);
+
+ ASSERT(hdlr.cyh_func != NULL);
+ ASSERT(when.cyt_when >= 0 && when.cyt_interval > 0);
+
+ ocpu->cyo_cpu = cpu;
+ ocpu->cyo_arg = hdlr.cyh_arg;
+ ocpu->cyo_ndx = cyclic_add_here(cpu, &hdlr, &when, 0);
+ ocpu->cyo_next = idp->cyi_omni_list;
+ idp->cyi_omni_list = ocpu;
+}
+
+static void
+cyclic_omni_stop(cyc_id_t *idp, cyc_cpu_t *cpu)
+{
+ cyc_omni_handler_t *omni = &idp->cyi_omni_hdlr;
+ cyc_omni_cpu_t *ocpu = idp->cyi_omni_list, *prev = NULL;
+
+ ASSERT(MUTEX_HELD(&cpu_lock));
+ ASSERT(idp->cyi_cpu == NULL);
+ ASSERT(ocpu != NULL);
+
+ while (ocpu != NULL && ocpu->cyo_cpu != cpu) {
+ prev = ocpu;
+ ocpu = ocpu->cyo_next;
+ }
+
+ /*
+ * We _must_ have found an cyc_omni_cpu which corresponds to this
+ * CPU -- the definition of an omnipresent cyclic is that it runs
+ * on all online CPUs.
+ */
+ ASSERT(ocpu != NULL);
+
+ if (prev == NULL) {
+ idp->cyi_omni_list = ocpu->cyo_next;
+ } else {
+ prev->cyo_next = ocpu->cyo_next;
+ }
+
+ (void) cyclic_remove_here(ocpu->cyo_cpu, ocpu->cyo_ndx, NULL, CY_WAIT);
+
+ /*
+ * The cyclic has been removed from this CPU; time to call the
+ * omnipresent offline handler.
+ */
+ if (omni->cyo_offline != NULL)
+ omni->cyo_offline(omni->cyo_arg, cpu->cyp_cpu, ocpu->cyo_arg);
+
+ free(ocpu, M_CYCLIC);
+}
+
+static cyc_id_t *
+cyclic_new_id(void)
+{
+ cyc_id_t *idp;
+
+ ASSERT(MUTEX_HELD(&cpu_lock));
+
+ idp = kmem_cache_alloc(cyclic_id_cache, KM_SLEEP);
+
+ /*
+ * The cyi_cpu field of the cyc_id_t structure tracks the CPU
+ * associated with the cyclic. If and only if this field is NULL, the
+ * cyc_id_t is an omnipresent cyclic. Note that cyi_omni_list may be
+ * NULL for an omnipresent cyclic while the cyclic is being created
+ * or destroyed.
+ */
+ idp->cyi_cpu = NULL;
+ idp->cyi_ndx = 0;
+
+ idp->cyi_next = cyclic_id_head;
+ idp->cyi_prev = NULL;
+ idp->cyi_omni_list = NULL;
+
+ if (cyclic_id_head != NULL) {
+ ASSERT(cyclic_id_head->cyi_prev == NULL);
+ cyclic_id_head->cyi_prev = idp;
+ }
+
+ cyclic_id_head = idp;
+
+ return (idp);
+}
+
+/*
+ * cyclic_id_t cyclic_add(cyc_handler_t *, cyc_time_t *)
+ *
+ * Overview
+ *
+ * cyclic_add() will create an unbound cyclic with the specified handler and
+ * interval. The cyclic will run on a CPU which both has interrupts enabled
+ * and is in the system CPU partition.
+ *
+ * Arguments and notes
+ *
+ * As its first argument, cyclic_add() takes a cyc_handler, which has the
+ * following members:
+ *
+ * cyc_func_t cyh_func <-- Cyclic handler
+ * void *cyh_arg <-- Argument to cyclic handler
+ *
+ * In addition to a cyc_handler, cyclic_add() takes a cyc_time, which
+ * has the following members:
+ *
+ * hrtime_t cyt_when <-- Absolute time, in nanoseconds since boot, at
+ * which to start firing
+ * hrtime_t cyt_interval <-- Length of interval, in nanoseconds
+ *
+ * gethrtime() is the time source for nanoseconds since boot. If cyt_when
+ * is set to 0, the cyclic will start to fire when cyt_interval next
+ * divides the number of nanoseconds since boot.
+ *
+ * The cyt_interval field _must_ be filled in by the caller; one-shots are
+ * _not_ explicitly supported by the cyclic subsystem (cyclic_add() will
+ * assert that cyt_interval is non-zero). The maximum value for either
+ * field is INT64_MAX; the caller is responsible for assuring that
+ * cyt_when + cyt_interval <= INT64_MAX. Neither field may be negative.
+ *
+ * For an arbitrary time t in the future, the cyclic handler is guaranteed
+ * to have been called (t - cyt_when) / cyt_interval times. This will
+ * be true even if interrupts have been disabled for periods greater than
+ * cyt_interval nanoseconds. In order to compensate for such periods,
+ * the cyclic handler may be called a finite number of times with an
+ * arbitrarily small interval.
+ *
+ * The cyclic subsystem will not enforce any lower bound on the interval;
+ * if the interval is less than the time required to process an interrupt,
+ * the CPU will wedge. It's the responsibility of the caller to assure that
+ * either the value of the interval is sane, or that its caller has
+ * sufficient privilege to deny service (i.e. its caller is root).
+ *
+ * Return value
+ *
+ * cyclic_add() returns a cyclic_id_t, which is guaranteed to be a value
+ * other than CYCLIC_NONE. cyclic_add() cannot fail.
+ *
+ * Caller's context
+ *
+ * cpu_lock must be held by the caller, and the caller must not be in
+ * interrupt context. cyclic_add() will perform a KM_SLEEP kernel
+ * memory allocation, so the usual rules (e.g. p_lock cannot be held)
+ * apply. A cyclic may be added even in the presence of CPUs that have
+ * not been configured with respect to the cyclic subsystem, but only
+ * configured CPUs will be eligible to run the new cyclic.
+ *
+ * Cyclic handler's context
+ *
+ * Cyclic handlers will be executed in the interrupt context corresponding
+ * to the specified level (i.e. either high, lock or low level). The
+ * usual context rules apply.
+ *
+ * A cyclic handler may not grab ANY locks held by the caller of any of
+ * cyclic_add() or cyclic_remove(); the implementation of these functions
+ * may require blocking on cyclic handler completion.
+ * Moreover, cyclic handlers may not make any call back into the cyclic
+ * subsystem.
+ */
+cyclic_id_t
+cyclic_add(cyc_handler_t *hdlr, cyc_time_t *when)
+{
+ cyc_id_t *idp = cyclic_new_id();
+ solaris_cpu_t *c = &solaris_cpu[curcpu];
+
+ ASSERT(MUTEX_HELD(&cpu_lock));
+ ASSERT(when->cyt_when >= 0 && when->cyt_interval > 0);
+
+ idp->cyi_cpu = c->cpu_cyclic;
+ idp->cyi_ndx = cyclic_add_here(idp->cyi_cpu, hdlr, when, 0);
+
+ return ((uintptr_t)idp);
+}
+
+/*
+ * cyclic_id_t cyclic_add_omni(cyc_omni_handler_t *)
+ *
+ * Overview
+ *
+ * cyclic_add_omni() will create an omnipresent cyclic with the specified
+ * online and offline handlers. Omnipresent cyclics run on all online
+ * CPUs, including CPUs which have unbound interrupts disabled.
+ *
+ * Arguments
+ *
+ * As its only argument, cyclic_add_omni() takes a cyc_omni_handler, which
+ * has the following members:
+ *
+ * void (*cyo_online)() <-- Online handler
+ * void (*cyo_offline)() <-- Offline handler
+ * void *cyo_arg <-- Argument to be passed to on/offline handlers
+ *
+ * Online handler
+ *
+ * The cyo_online member is a pointer to a function which has the following
+ * four arguments:
+ *
+ * void * <-- Argument (cyo_arg)
+ * cpu_t * <-- Pointer to CPU about to be onlined
+ * cyc_handler_t * <-- Pointer to cyc_handler_t; must be filled in
+ * by omni online handler
+ * cyc_time_t * <-- Pointer to cyc_time_t; must be filled in by
+ * omni online handler
+ *
+ * The omni cyclic online handler is always called _before_ the omni
+ * cyclic begins to fire on the specified CPU. As the above argument
+ * description implies, the online handler must fill in the two structures
+ * passed to it: the cyc_handler_t and the cyc_time_t. These are the
+ * same two structures passed to cyclic_add(), outlined above. This
+ * allows the omni cyclic to have maximum flexibility; different CPUs may
+ * optionally
+ *
+ * (a) have different intervals
+ * (b) be explicitly in or out of phase with one another
+ * (c) have different handlers
+ * (d) have different handler arguments
+ * (e) fire at different levels
+ *
+ * Of these, (e) seems somewhat dubious, but is nonetheless allowed.
+ *
+ * The omni online handler is called in the same context as cyclic_add(),
+ * and has the same liberties: omni online handlers may perform KM_SLEEP
+ * kernel memory allocations, and may grab locks which are also acquired
+ * by cyclic handlers. However, omni cyclic online handlers may _not_
+ * call back into the cyclic subsystem, and should be generally careful
+ * about calling into arbitrary kernel subsystems.
+ *
+ * Offline handler
+ *
+ * The cyo_offline member is a pointer to a function which has the following
+ * three arguments:
+ *
+ * void * <-- Argument (cyo_arg)
+ * cpu_t * <-- Pointer to CPU about to be offlined
+ * void * <-- CPU's cyclic argument (that is, value
+ * to which cyh_arg member of the cyc_handler_t
+ * was set in the omni online handler)
+ *
+ * The omni cyclic offline handler is always called _after_ the omni
+ * cyclic has ceased firing on the specified CPU. Its purpose is to
+ * allow cleanup of any resources dynamically allocated in the omni cyclic
+ * online handler. The context of the offline handler is identical to
+ * that of the online handler; the same constraints and liberties apply.
+ *
+ * The offline handler is optional; it may be NULL.
+ *
+ * Return value
+ *
+ * cyclic_add_omni() returns a cyclic_id_t, which is guaranteed to be a
+ * value other than CYCLIC_NONE. cyclic_add_omni() cannot fail.
+ *
+ * Caller's context
+ *
+ * The caller's context is identical to that of cyclic_add(), specified
+ * above.
+ */
+cyclic_id_t
+cyclic_add_omni(cyc_omni_handler_t *omni)
+{
+ cyc_id_t *idp = cyclic_new_id();
+ cyc_cpu_t *cpu;
+ cpu_t *c;
+ int i;
+
+ ASSERT(MUTEX_HELD(&cpu_lock));
+ ASSERT(omni != NULL && omni->cyo_online != NULL);
+
+ idp->cyi_omni_hdlr = *omni;
+
+ for (i = 0; i < MAXCPU; i++) {
+ if (pcpu_find(i) == NULL)
+ continue;
+
+ c = &solaris_cpu[i];
+
+ if ((cpu = c->cpu_cyclic) == NULL)
+ continue;
+
+ cyclic_omni_start(idp, cpu);
+ }
+
+ /*
+ * We must have found at least one online CPU on which to run
+ * this cyclic.
+ */
+ ASSERT(idp->cyi_omni_list != NULL);
+ ASSERT(idp->cyi_cpu == NULL);
+
+ return ((uintptr_t)idp);
+}
+
+/*
+ * void cyclic_remove(cyclic_id_t)
+ *
+ * Overview
+ *
+ * cyclic_remove() will remove the specified cyclic from the system.
+ *
+ * Arguments and notes
+ *
+ * The only argument is a cyclic_id returned from either cyclic_add() or
+ * cyclic_add_omni().
+ *
+ * By the time cyclic_remove() returns, the caller is guaranteed that the
+ * removed cyclic handler has completed execution (this is the same
+ * semantic that untimeout() provides). As a result, cyclic_remove() may
+ * need to block, waiting for the removed cyclic to complete execution.
+ * This leads to an important constraint on the caller: no lock may be
+ * held across cyclic_remove() that also may be acquired by a cyclic
+ * handler.
+ *
+ * Return value
+ *
+ * None; cyclic_remove() always succeeds.
+ *
+ * Caller's context
+ *
+ * cpu_lock must be held by the caller, and the caller must not be in
+ * interrupt context. The caller may not hold any locks which are also
+ * grabbed by any cyclic handler. See "Arguments and notes", above.
+ */
+void
+cyclic_remove(cyclic_id_t id)
+{
+ cyc_id_t *idp = (cyc_id_t *)id;
+ cyc_id_t *prev = idp->cyi_prev, *next = idp->cyi_next;
+ cyc_cpu_t *cpu = idp->cyi_cpu;
+
+ ASSERT(MUTEX_HELD(&cpu_lock));
+
+ if (cpu != NULL) {
+ (void) cyclic_remove_here(cpu, idp->cyi_ndx, NULL, CY_WAIT);
+ } else {
+ ASSERT(idp->cyi_omni_list != NULL);
+ while (idp->cyi_omni_list != NULL)
+ cyclic_omni_stop(idp, idp->cyi_omni_list->cyo_cpu);
+ }
+
+ if (prev != NULL) {
+ ASSERT(cyclic_id_head != idp);
+ prev->cyi_next = next;
+ } else {
+ ASSERT(cyclic_id_head == idp);
+ cyclic_id_head = next;
+ }
+
+ if (next != NULL)
+ next->cyi_prev = prev;
+
+ kmem_cache_free(cyclic_id_cache, idp);
+}
+
+static void
+cyclic_init(cyc_backend_t *be)
+{
+ ASSERT(MUTEX_HELD(&cpu_lock));
+
+ /*
+ * Copy the passed cyc_backend into the backend template. This must
+ * be done before the CPU can be configured.
+ */
+ bcopy(be, &cyclic_backend, sizeof (cyc_backend_t));
+
+ cyclic_configure(&solaris_cpu[curcpu]);
+}
+
+/*
+ * It is assumed that cyclic_mp_init() is called some time after cyclic
+ * init (and therefore, after cpu0 has been initialized). We grab cpu_lock,
+ * find the already initialized CPU, and initialize every other CPU with the
+ * same backend.
+ */
+static void
+cyclic_mp_init(void)
+{
+ cpu_t *c;
+ int i;
+
+ mutex_enter(&cpu_lock);
+
+ for (i = 0; i <= mp_maxid; i++) {
+ if (pcpu_find(i) == NULL)
+ continue;
+
+ c = &solaris_cpu[i];
+
+ if (c->cpu_cyclic == NULL)
+ cyclic_configure(c);
+ }
+
+ mutex_exit(&cpu_lock);
+}
+
+static void
+cyclic_uninit(void)
+{
+ struct pcpu *pc;
+ cpu_t *c;
+ int id;
+
+ for (id = 0; id <= mp_maxid; id++) {
+ if ((pc = pcpu_find(id)) == NULL)
+ continue;
+
+ c = &solaris_cpu[id];
+
+ if (c->cpu_cyclic == NULL)
+ continue;
+
+ cyclic_unconfigure(c);
+ }
+
+ if (cyclic_id_cache != NULL)
+ kmem_cache_destroy(cyclic_id_cache);
+}
+
+#include "cyclic_machdep.c"
+
+/*
+ * Cyclic subsystem initialisation.
+ */
+static void
+cyclic_load(void *dummy)
+{
+ mutex_enter(&cpu_lock);
+
+ /* Initialise the machine-dependent backend. */
+ cyclic_machdep_init();
+
+ mutex_exit(&cpu_lock);
+}
+
+SYSINIT(cyclic_register, SI_SUB_CYCLIC, SI_ORDER_SECOND, cyclic_load, NULL);
+
+static void
+cyclic_unload(void)
+{
+ mutex_enter(&cpu_lock);
+
+ /* Uninitialise the machine-dependent backend. */
+ cyclic_machdep_uninit();
+
+ mutex_exit(&cpu_lock);
+}
+
+SYSUNINIT(cyclic_unregister, SI_SUB_CYCLIC, SI_ORDER_SECOND, cyclic_unload, NULL);
+
+/* ARGSUSED */
+static int
+cyclic_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);
+}
+
+DEV_MODULE(cyclic, cyclic_modevent, NULL);
+MODULE_VERSION(cyclic, 1);
+MODULE_DEPEND(cyclic, opensolaris, 1, 1, 1);
diff --git a/sys/cddl/dev/cyclic/cyclic_test.c b/sys/cddl/dev/cyclic/cyclic_test.c
new file mode 100644
index 000000000000..063dbc77e7a3
--- /dev/null
+++ b/sys/cddl/dev/cyclic/cyclic_test.c
@@ -0,0 +1,301 @@
+/*-
+ * Copyright 2007 John Birrell <jb@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ *
+ */
+
+#include <sys/cdefs.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/conf.h>
+#include <sys/kthread.h>
+#include <sys/module.h>
+#include <sys/sysctl.h>
+#include <sys/cyclic.h>
+#include <sys/time.h>
+
+static struct timespec test_001_start;
+
+static void
+cyclic_test_001_func(void *arg)
+{
+ struct timespec ts;
+
+ nanotime(&ts);
+ timespecsub(&ts,&test_001_start);
+ printf("%s: called after %lu.%09lu on curcpu %d\n",__func__,(u_long) ts.tv_sec,(u_long) ts.tv_nsec, curcpu);
+}
+
+static void
+cyclic_test_001(void)
+{
+ int error = 0;
+ cyc_handler_t hdlr;
+ cyc_time_t when;
+ cyclic_id_t id;
+
+ printf("%s: starting\n",__func__);
+
+ hdlr.cyh_func = (cyc_func_t) cyclic_test_001_func;
+ hdlr.cyh_arg = 0;
+
+ when.cyt_when = 0;
+ when.cyt_interval = 1000000000;
+
+ nanotime(&test_001_start);
+
+ mutex_enter(&cpu_lock);
+
+ id = cyclic_add(&hdlr, &when);
+
+ mutex_exit(&cpu_lock);
+
+ DELAY(1200000);
+
+ mutex_enter(&cpu_lock);
+
+ cyclic_remove(id);
+
+ mutex_exit(&cpu_lock);
+
+ printf("%s: %s\n",__func__, error == 0 ? "passed":"failed");
+}
+
+static struct timespec test_002_start;
+
+static void
+cyclic_test_002_func(void *arg)
+{
+ struct timespec ts;
+
+ nanotime(&ts);
+ timespecsub(&ts,&test_002_start);
+ printf("%s: called after %lu.%09lu on curcpu %d\n",__func__,(u_long) ts.tv_sec,(u_long) ts.tv_nsec, curcpu);
+}
+
+static void
+cyclic_test_002_online(void *arg, cpu_t *c, cyc_handler_t *hdlr, cyc_time_t *t)
+{
+ printf("%s: online on curcpu %d\n",__func__, curcpu);
+ hdlr->cyh_func = cyclic_test_002_func;
+ hdlr->cyh_arg = NULL;
+ t->cyt_when = 0;
+ t->cyt_interval = 1000000000;
+}
+
+static void
+cyclic_test_002_offline(void *arg, cpu_t *c, void *arg1)
+{
+ printf("%s: offline on curcpu %d\n",__func__, curcpu);
+}
+
+static void
+cyclic_test_002(void)
+{
+ int error = 0;
+ cyc_omni_handler_t hdlr;
+ cyclic_id_t id;
+
+ printf("%s: starting\n",__func__);
+
+ hdlr.cyo_online = cyclic_test_002_online;
+ hdlr.cyo_offline = cyclic_test_002_offline;
+ hdlr.cyo_arg = NULL;
+
+ nanotime(&test_002_start);
+
+ mutex_enter(&cpu_lock);
+
+ id = cyclic_add_omni(&hdlr);
+
+ mutex_exit(&cpu_lock);
+
+ DELAY(1200000);
+
+ mutex_enter(&cpu_lock);
+
+ cyclic_remove(id);
+
+ mutex_exit(&cpu_lock);
+
+ printf("%s: %s\n",__func__, error == 0 ? "passed":"failed");
+}
+
+static struct timespec test_003_start;
+
+static void
+cyclic_test_003_func(void *arg)
+{
+ struct timespec ts;
+
+ nanotime(&ts);
+ timespecsub(&ts,&test_003_start);
+ printf("%s: called after %lu.%09lu on curcpu %d id %ju\n",__func__,(u_long) ts.tv_sec,(u_long) ts.tv_nsec, curcpu, (uintmax_t)(uintptr_t) arg);
+}
+
+static void
+cyclic_test_003(void)
+{
+ int error = 0;
+ cyc_handler_t hdlr;
+ cyc_time_t when;
+ cyclic_id_t id;
+ cyclic_id_t id1;
+ cyclic_id_t id2;
+ cyclic_id_t id3;
+
+ printf("%s: starting\n",__func__);
+
+ hdlr.cyh_func = (cyc_func_t) cyclic_test_003_func;
+
+ when.cyt_when = 0;
+
+ nanotime(&test_003_start);
+
+ mutex_enter(&cpu_lock);
+
+ when.cyt_interval = 200000000;
+ hdlr.cyh_arg = (void *) 0UL;
+ id = cyclic_add(&hdlr, &when);
+
+ when.cyt_interval = 400000000;
+ hdlr.cyh_arg = (void *) 1UL;
+ id1 = cyclic_add(&hdlr, &when);
+
+ hdlr.cyh_arg = (void *) 2UL;
+ when.cyt_interval = 1000000000;
+ id2 = cyclic_add(&hdlr, &when);
+
+ hdlr.cyh_arg = (void *) 3UL;
+ when.cyt_interval = 1300000000;
+ id3 = cyclic_add(&hdlr, &when);
+
+ mutex_exit(&cpu_lock);
+
+ DELAY(1200000);
+
+ mutex_enter(&cpu_lock);
+
+ cyclic_remove(id);
+ cyclic_remove(id1);
+ cyclic_remove(id2);
+ cyclic_remove(id3);
+
+ mutex_exit(&cpu_lock);
+
+ printf("%s: %s\n",__func__, error == 0 ? "passed":"failed");
+}
+
+/* Kernel thread command routine. */
+static void
+cyclic_run_tests(void *arg)
+{
+ intptr_t cmd = (intptr_t) arg;
+
+ switch (cmd) {
+ case 1:
+ cyclic_test_001();
+ break;
+ case 2:
+ cyclic_test_002();
+ break;
+ case 3:
+ cyclic_test_003();
+ break;
+ default:
+ cyclic_test_001();
+ cyclic_test_002();
+ cyclic_test_003();
+ break;
+ }
+
+ printf("%s: finished\n",__func__);
+
+ kthread_exit();
+}
+
+static int
+cyclic_test(SYSCTL_HANDLER_ARGS)
+{
+ int error, cmd = 0;
+
+ error = sysctl_wire_old_buffer(req, sizeof(int));
+ if (error == 0)
+ error = sysctl_handle_int(oidp, &cmd, 0, req);
+ if (error != 0 || req->newptr == NULL)
+ return (error);
+
+ /* Check for command validity. */
+ switch (cmd) {
+ case 1:
+ case 2:
+ case -1:
+ /*
+ * Execute the tests in a kernel thread to avoid blocking
+ * the sysctl. Look for the results in the syslog.
+ */
+ error = kthread_add(cyclic_run_tests, (void *)(uintptr_t) cmd,
+ NULL, NULL, 0, 0, "cyctest%d", cmd);
+ break;
+ default:
+ printf("Usage: debug.cyclic.test=(1..9) or -1 for all tests\n");
+ error = EINVAL;
+ break;
+ }
+
+ return (error);
+}
+
+SYSCTL_NODE(_debug, OID_AUTO, cyclic, CTLFLAG_RW, NULL, "Cyclic nodes");
+SYSCTL_PROC(_debug_cyclic, OID_AUTO, test, CTLTYPE_INT | CTLFLAG_RW, 0, 0,
+ cyclic_test, "I", "Enables a cyclic test. Use -1 for all tests.");
+
+static int
+cyclic_test_modevent(module_t mod, int type, void *data)
+{
+ int error = 0;
+
+ switch (type) {
+ case MOD_LOAD:
+ break;
+
+ case MOD_UNLOAD:
+ break;
+
+ case MOD_SHUTDOWN:
+ break;
+
+ default:
+ error = EOPNOTSUPP;
+ break;
+
+ }
+ return (error);
+}
+
+DEV_MODULE(cyclic_test, cyclic_test_modevent, NULL);
+MODULE_VERSION(cyclic_test, 1);
+MODULE_DEPEND(cyclic_test, cyclic, 1, 1, 1);
+MODULE_DEPEND(cyclic_test, opensolaris, 1, 1, 1);
diff --git a/sys/cddl/dev/cyclic/i386/cyclic_machdep.c b/sys/cddl/dev/cyclic/i386/cyclic_machdep.c
new file mode 100644
index 000000000000..fa40db9329c1
--- /dev/null
+++ b/sys/cddl/dev/cyclic/i386/cyclic_machdep.c
@@ -0,0 +1,133 @@
+/*-
+ * Copyright 2006-2008 John Birrell <jb@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ *
+ */
+
+static void enable(cyb_arg_t);
+static void disable(cyb_arg_t);
+static void reprogram(cyb_arg_t, hrtime_t);
+static void xcall(cyb_arg_t, cpu_t *, cyc_func_t, void *);
+
+static cyc_backend_t be = {
+ NULL, /* cyb_configure */
+ NULL, /* cyb_unconfigure */
+ enable,
+ disable,
+ reprogram,
+ xcall,
+ NULL /* cyb_arg_t cyb_arg */
+};
+
+static void
+cyclic_ap_start(void *dummy)
+{
+ /* Initialise the rest of the CPUs. */
+ cyclic_mp_init();
+}
+
+SYSINIT(cyclic_ap_start, SI_SUB_SMP, SI_ORDER_ANY, cyclic_ap_start, NULL);
+
+/*
+ * Machine dependent cyclic subsystem initialisation.
+ */
+static void
+cyclic_machdep_init(void)
+{
+ /* Register the cyclic backend. */
+ cyclic_init(&be);
+}
+
+static void
+cyclic_machdep_uninit(void)
+{
+ int i;
+
+ for (i = 0; i <= mp_maxid; i++)
+ /* Reset the cyclic clock callback hook. */
+ lapic_cyclic_clock_func[i] = NULL;
+
+ /* De-register the cyclic backend. */
+ cyclic_uninit();
+}
+
+static hrtime_t exp_due[MAXCPU];
+
+/*
+ * This function is the one registered by the machine dependent
+ * initialiser as the callback for high speed timer events.
+ */
+static void
+cyclic_clock(struct trapframe *frame)
+{
+ cpu_t *c = &solaris_cpu[curcpu];
+
+ if (c->cpu_cyclic != NULL && gethrtime() >= exp_due[curcpu]) {
+ if (TRAPF_USERMODE(frame)) {
+ c->cpu_profile_pc = 0;
+ c->cpu_profile_upc = TRAPF_PC(frame);
+ } else {
+ c->cpu_profile_pc = TRAPF_PC(frame);
+ c->cpu_profile_upc = 0;
+ }
+
+ c->cpu_intr_actv = 1;
+
+ /* Fire any timers that are due. */
+ cyclic_fire(c);
+
+ c->cpu_intr_actv = 0;
+ }
+}
+
+static void enable(cyb_arg_t arg)
+{
+ /* Register the cyclic clock callback function. */
+ lapic_cyclic_clock_func[curcpu] = cyclic_clock;
+}
+
+static void disable(cyb_arg_t arg)
+{
+ /* Reset the cyclic clock callback function. */
+ lapic_cyclic_clock_func[curcpu] = NULL;
+}
+
+static void reprogram(cyb_arg_t arg, hrtime_t exp)
+{
+ exp_due[curcpu] = exp;
+}
+
+static void xcall(cyb_arg_t arg, cpu_t *c, cyc_func_t func, void *param)
+{
+ /*
+ * If the target CPU is the current one, just call the
+ * function. This covers the non-SMP case.
+ */
+ if (c == &solaris_cpu[curcpu])
+ (*func)(param);
+ else
+ smp_rendezvous_cpus((cpumask_t) (1 << c->cpuid), NULL,
+ func, smp_no_rendevous_barrier, param);
+}
diff --git a/sys/cddl/dev/dtmalloc/dtmalloc.c b/sys/cddl/dev/dtmalloc/dtmalloc.c
new file mode 100644
index 000000000000..ca822f988668
--- /dev/null
+++ b/sys/cddl/dev/dtmalloc/dtmalloc.c
@@ -0,0 +1,220 @@
+/*
+ * 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
+ *
+ * $FreeBSD$
+ *
+ */
+
+#include <sys/cdefs.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+
+#include <sys/dtrace.h>
+#include <sys/dtrace_bsd.h>
+
+static d_open_t dtmalloc_open;
+static int dtmalloc_unload(void);
+static void dtmalloc_getargdesc(void *, dtrace_id_t, void *, dtrace_argdesc_t *);
+static void dtmalloc_provide(void *, dtrace_probedesc_t *);
+static void dtmalloc_destroy(void *, dtrace_id_t, void *);
+static void dtmalloc_enable(void *, dtrace_id_t, void *);
+static void dtmalloc_disable(void *, dtrace_id_t, void *);
+static void dtmalloc_load(void *);
+
+static struct cdevsw dtmalloc_cdevsw = {
+ .d_version = D_VERSION,
+ .d_open = dtmalloc_open,
+ .d_name = "dtmalloc",
+};
+
+static dtrace_pattr_t dtmalloc_attr = {
+{ DTRACE_STABILITY_STABLE, DTRACE_STABILITY_STABLE, DTRACE_CLASS_COMMON },
+{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
+{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
+{ DTRACE_STABILITY_STABLE, DTRACE_STABILITY_STABLE, DTRACE_CLASS_COMMON },
+{ DTRACE_STABILITY_STABLE, DTRACE_STABILITY_STABLE, DTRACE_CLASS_COMMON },
+};
+
+static dtrace_pops_t dtmalloc_pops = {
+ dtmalloc_provide,
+ NULL,
+ dtmalloc_enable,
+ dtmalloc_disable,
+ NULL,
+ NULL,
+ dtmalloc_getargdesc,
+ NULL,
+ NULL,
+ dtmalloc_destroy
+};
+
+static struct cdev *dtmalloc_cdev;
+static dtrace_provider_id_t dtmalloc_id;
+
+static void
+dtmalloc_getargdesc(void *arg, dtrace_id_t id, void *parg, dtrace_argdesc_t *desc)
+{
+ const char *p = NULL;
+
+ switch (desc->dtargd_ndx) {
+ case 0:
+ p = "struct malloc_type *";
+ break;
+ case 1:
+ p = "struct malloc_type_internal *";
+ break;
+ case 2:
+ p = "struct malloc_type_stats *";
+ break;
+ case 3:
+ p = "unsigned long";
+ break;
+ case 4:
+ p = "int";
+ break;
+ default:
+ desc->dtargd_ndx = DTRACE_ARGNONE;
+ break;
+ }
+
+ if (p != NULL)
+ strlcpy(desc->dtargd_native, p, sizeof(desc->dtargd_native));
+
+ return;
+}
+
+static void
+dtmalloc_type_cb(struct malloc_type *mtp, void *arg __unused)
+{
+ char name[DTRACE_FUNCNAMELEN];
+ struct malloc_type_internal *mtip = mtp->ks_handle;
+
+ strlcpy(name, mtp->ks_shortdesc, sizeof(name));
+
+ if (dtrace_probe_lookup(dtmalloc_id, NULL, name, "malloc") != 0)
+ return;
+
+ (void) dtrace_probe_create(dtmalloc_id, NULL, name, "malloc", 0,
+ &mtip->mti_probes[DTMALLOC_PROBE_MALLOC]);
+ (void) dtrace_probe_create(dtmalloc_id, NULL, name, "free", 0,
+ &mtip->mti_probes[DTMALLOC_PROBE_FREE]);
+}
+
+static void
+dtmalloc_provide(void *arg, dtrace_probedesc_t *desc)
+{
+ if (desc != NULL)
+ return;
+
+ malloc_type_list(dtmalloc_type_cb, desc);
+}
+
+static void
+dtmalloc_destroy(void *arg, dtrace_id_t id, void *parg)
+{
+}
+
+static void
+dtmalloc_enable(void *arg, dtrace_id_t id, void *parg)
+{
+ uint32_t *p = parg;
+ *p = id;
+}
+
+static void
+dtmalloc_disable(void *arg, dtrace_id_t id, void *parg)
+{
+ uint32_t *p = parg;
+ *p = 0;
+}
+
+static void
+dtmalloc_load(void *dummy)
+{
+ /* Create the /dev/dtrace/dtmalloc entry. */
+ dtmalloc_cdev = make_dev(&dtmalloc_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600,
+ "dtrace/dtmalloc");
+
+ if (dtrace_register("dtmalloc", &dtmalloc_attr, DTRACE_PRIV_USER,
+ NULL, &dtmalloc_pops, NULL, &dtmalloc_id) != 0)
+ return;
+
+ dtrace_malloc_probe = dtrace_probe;
+}
+
+
+static int
+dtmalloc_unload()
+{
+ int error = 0;
+
+ dtrace_malloc_probe = NULL;
+
+ if ((error = dtrace_unregister(dtmalloc_id)) != 0)
+ return (error);
+
+ destroy_dev(dtmalloc_cdev);
+
+ return (error);
+}
+
+static int
+dtmalloc_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
+dtmalloc_open(struct cdev *dev __unused, int oflags __unused, int devtype __unused, struct thread *td __unused)
+{
+ return (0);
+}
+
+SYSINIT(dtmalloc_load, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, dtmalloc_load, NULL);
+SYSUNINIT(dtmalloc_unload, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, dtmalloc_unload, NULL);
+
+DEV_MODULE(dtmalloc, dtmalloc_modevent, NULL);
+MODULE_VERSION(dtmalloc, 1);
+MODULE_DEPEND(dtmalloc, dtrace, 1, 1, 1);
+MODULE_DEPEND(dtmalloc, opensolaris, 1, 1, 1);
diff --git a/sys/cddl/dev/dtrace/amd64/dis_tables.c b/sys/cddl/dev/dtrace/amd64/dis_tables.c
new file mode 100644
index 000000000000..5a5bc25c02c3
--- /dev/null
+++ b/sys/cddl/dev/dtrace/amd64/dis_tables.c
@@ -0,0 +1,3193 @@
+/*
+ * 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$
+ */
+/*
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1988 AT&T */
+/* All Rights Reserved */
+
+
+#if defined(sun)
+#pragma ident "@(#)dis_tables.c 1.11 06/03/02 SMI"
+#endif
+
+#include "dis_tables.h"
+
+/* BEGIN CSTYLED */
+
+/*
+ * Disassembly begins in dis_distable, which is equivalent to the One-byte
+ * Opcode Map in the Intel IA32 ISA Reference (page A-6 in my copy). The
+ * decoding loops then traverse out through the other tables as necessary to
+ * decode a given instruction.
+ *
+ * The behavior of this file can be controlled by one of the following flags:
+ *
+ * DIS_TEXT Include text for disassembly
+ * DIS_MEM Include memory-size calculations
+ *
+ * Either or both of these can be defined.
+ *
+ * This file is not, and will never be, cstyled. If anything, the tables should
+ * be taken out another tab stop or two so nothing overlaps.
+ */
+
+/*
+ * These functions must be provided for the consumer to do disassembly.
+ */
+#ifdef DIS_TEXT
+extern char *strncpy(char *, const char *, size_t);
+extern size_t strlen(const char *);
+extern int strcmp(const char *, const char *);
+extern int strncmp(const char *, const char *, size_t);
+extern size_t strlcat(char *, const char *, size_t);
+#endif
+
+
+#define TERM NULL /* used to indicate that the 'indirect' */
+ /* field terminates - no pointer. */
+
+/* Used to decode instructions. */
+typedef struct instable {
+ const struct instable *it_indirect; /* for decode op codes */
+ uchar_t it_adrmode;
+#ifdef DIS_TEXT
+ char it_name[NCPS];
+ uint_t it_suffix:1; /* mneu + "w", "l", or "d" */
+#endif
+#ifdef DIS_MEM
+ uint_t it_size:16;
+#endif
+ uint_t it_invalid64:1; /* opcode invalid in amd64 */
+ uint_t it_always64:1; /* 64 bit when in 64 bit mode */
+ uint_t it_invalid32:1; /* invalid in IA32 */
+ uint_t it_stackop:1; /* push/pop stack operation */
+} instable_t;
+
+/*
+ * Instruction formats.
+ */
+enum {
+ UNKNOWN,
+ MRw,
+ IMlw,
+ IMw,
+ IR,
+ OA,
+ AO,
+ MS,
+ SM,
+ Mv,
+ Mw,
+ M, /* register or memory */
+ Mb, /* register or memory, always byte sized */
+ MO, /* memory only (no registers) */
+ PREF,
+ SWAPGS,
+ R,
+ RA,
+ SEG,
+ MR,
+ RM,
+ IA,
+ MA,
+ SD,
+ AD,
+ SA,
+ D,
+ INM,
+ SO,
+ BD,
+ I,
+ P,
+ V,
+ DSHIFT, /* for double shift that has an 8-bit immediate */
+ U,
+ OVERRIDE,
+ NORM, /* instructions w/o ModR/M byte, no memory access */
+ IMPLMEM, /* instructions w/o ModR/M byte, implicit mem access */
+ O, /* for call */
+ JTAB, /* jump table */
+ IMUL, /* for 186 iimul instr */
+ CBW, /* so data16 can be evaluated for cbw and variants */
+ MvI, /* for 186 logicals */
+ ENTER, /* for 186 enter instr */
+ RMw, /* for 286 arpl instr */
+ Ib, /* for push immediate byte */
+ F, /* for 287 instructions */
+ FF, /* for 287 instructions */
+ FFC, /* for 287 instructions */
+ DM, /* 16-bit data */
+ AM, /* 16-bit addr */
+ LSEG, /* for 3-bit seg reg encoding */
+ MIb, /* for 386 logicals */
+ SREG, /* for 386 special registers */
+ PREFIX, /* a REP instruction prefix */
+ LOCK, /* a LOCK instruction prefix */
+ INT3, /* The int 3 instruction, which has a fake operand */
+ INTx, /* The normal int instruction, with explicit int num */
+ DSHIFTcl, /* for double shift that implicitly uses %cl */
+ CWD, /* so data16 can be evaluated for cwd and variants */
+ RET, /* single immediate 16-bit operand */
+ MOVZ, /* for movs and movz, with different size operands */
+ XADDB, /* for xaddb */
+ MOVSXZ, /* AMD64 mov sign extend 32 to 64 bit instruction */
+
+/*
+ * MMX/SIMD addressing modes.
+ */
+
+ MMO, /* Prefixable MMX/SIMD-Int mm/mem -> mm */
+ MMOIMPL, /* Prefixable MMX/SIMD-Int mm -> mm (mem) */
+ MMO3P, /* Prefixable MMX/SIMD-Int mm -> r32,imm8 */
+ MMOM3, /* Prefixable MMX/SIMD-Int mm -> r32 */
+ MMOS, /* Prefixable MMX/SIMD-Int mm -> mm/mem */
+ MMOMS, /* Prefixable MMX/SIMD-Int mm -> mem */
+ MMOPM, /* MMX/SIMD-Int mm/mem -> mm,imm8 */
+ MMOPRM, /* Prefixable MMX/SIMD-Int r32/mem -> mm,imm8 */
+ MMOSH, /* Prefixable MMX mm,imm8 */
+ MM, /* MMX/SIMD-Int mm/mem -> mm */
+ MMS, /* MMX/SIMD-Int mm -> mm/mem */
+ MMSH, /* MMX mm,imm8 */
+ XMMO, /* Prefixable SIMD xmm/mem -> xmm */
+ XMMOS, /* Prefixable SIMD xmm -> xmm/mem */
+ XMMOPM, /* Prefixable SIMD xmm/mem w/to xmm,imm8 */
+ XMMOMX, /* Prefixable SIMD mm/mem -> xmm */
+ XMMOX3, /* Prefixable SIMD xmm -> r32 */
+ XMMOXMM, /* Prefixable SIMD xmm/mem -> mm */
+ XMMOM, /* Prefixable SIMD xmm -> mem */
+ XMMOMS, /* Prefixable SIMD mem -> xmm */
+ XMM, /* SIMD xmm/mem -> xmm */
+ XMMXIMPL, /* SIMD xmm -> xmm (mem) */
+ XMM3P, /* SIMD xmm -> r32,imm8 */
+ XMMP, /* SIMD xmm/mem w/to xmm,imm8 */
+ XMMPRM, /* SIMD r32/mem -> xmm,imm8 */
+ XMMS, /* SIMD xmm -> xmm/mem */
+ XMMM, /* SIMD mem -> xmm */
+ XMMMS, /* SIMD xmm -> mem */
+ XMM3MX, /* SIMD r32/mem -> xmm */
+ XMM3MXS, /* SIMD xmm -> r32/mem */
+ XMMSH, /* SIMD xmm,imm8 */
+ XMMXM3, /* SIMD xmm/mem -> r32 */
+ XMMX3, /* SIMD xmm -> r32 */
+ XMMXMM, /* SIMD xmm/mem -> mm */
+ XMMMX, /* SIMD mm -> xmm */
+ XMMXM, /* SIMD xmm -> mm */
+ XMMFENCE, /* SIMD lfence or mfence */
+ XMMSFNC /* SIMD sfence (none or mem) */
+};
+
+#define FILL 0x90 /* Fill byte used for alignment (nop) */
+
+/*
+** Register numbers for the i386
+*/
+#define EAX_REGNO 0
+#define ECX_REGNO 1
+#define EDX_REGNO 2
+#define EBX_REGNO 3
+#define ESP_REGNO 4
+#define EBP_REGNO 5
+#define ESI_REGNO 6
+#define EDI_REGNO 7
+
+/*
+ * modes for immediate values
+ */
+#define MODE_NONE 0
+#define MODE_IPREL 1 /* signed IP relative value */
+#define MODE_SIGNED 2 /* sign extended immediate */
+#define MODE_IMPLIED 3 /* constant value implied from opcode */
+#define MODE_OFFSET 4 /* offset part of an address */
+
+/*
+ * The letters used in these macros are:
+ * IND - indirect to another to another table
+ * "T" - means to Terminate indirections (this is the final opcode)
+ * "S" - means "operand length suffix required"
+ * "NS" - means "no suffix" which is the operand length suffix of the opcode
+ * "Z" - means instruction size arg required
+ * "u" - means the opcode is invalid in IA32 but valid in amd64
+ * "x" - means the opcode is invalid in amd64, but not IA32
+ * "y" - means the operand size is always 64 bits in 64 bit mode
+ * "p" - means push/pop stack operation
+ */
+
+#if defined(DIS_TEXT) && defined(DIS_MEM)
+#define IND(table) {table, 0, "", 0, 0, 0, 0, 0, 0}
+#define INDx(table) {table, 0, "", 0, 0, 1, 0, 0, 0}
+#define TNS(name, amode) {TERM, amode, name, 0, 0, 0, 0, 0, 0}
+#define TNSu(name, amode) {TERM, amode, name, 0, 0, 0, 0, 1, 0}
+#define TNSx(name, amode) {TERM, amode, name, 0, 0, 1, 0, 0, 0}
+#define TNSy(name, amode) {TERM, amode, name, 0, 0, 0, 1, 0, 0}
+#define TNSyp(name, amode) {TERM, amode, name, 0, 0, 0, 1, 0, 1}
+#define TNSZ(name, amode, sz) {TERM, amode, name, 0, sz, 0, 0, 0, 0}
+#define TNSZy(name, amode, sz) {TERM, amode, name, 0, sz, 0, 1, 0, 0}
+#define TS(name, amode) {TERM, amode, name, 1, 0, 0, 0, 0, 0}
+#define TSx(name, amode) {TERM, amode, name, 1, 0, 1, 0, 0, 0}
+#define TSy(name, amode) {TERM, amode, name, 1, 0, 0, 1, 0, 0}
+#define TSp(name, amode) {TERM, amode, name, 1, 0, 0, 0, 0, 1}
+#define TSZ(name, amode, sz) {TERM, amode, name, 1, sz, 0, 0, 0, 0}
+#define TSZx(name, amode, sz) {TERM, amode, name, 1, sz, 1, 0, 0, 0}
+#define TSZy(name, amode, sz) {TERM, amode, name, 1, sz, 0, 1, 0, 0}
+#define INVALID {TERM, UNKNOWN, "", 0, 0, 0, 0, 0}
+#elif defined(DIS_TEXT)
+#define IND(table) {table, 0, "", 0, 0, 0, 0, 0}
+#define INDx(table) {table, 0, "", 0, 1, 0, 0, 0}
+#define TNS(name, amode) {TERM, amode, name, 0, 0, 0, 0, 0}
+#define TNSu(name, amode) {TERM, amode, name, 0, 0, 0, 1, 0}
+#define TNSx(name, amode) {TERM, amode, name, 0, 1, 0, 0, 0}
+#define TNSy(name, amode) {TERM, amode, name, 0, 0, 1, 0, 0}
+#define TNSyp(name, amode) {TERM, amode, name, 0, 0, 1, 0, 1}
+#define TNSZ(name, amode, sz) {TERM, amode, name, 0, 0, 0, 0, 0}
+#define TNSZy(name, amode, sz) {TERM, amode, name, 0, 0, 1, 0, 0}
+#define TS(name, amode) {TERM, amode, name, 1, 0, 0, 0, 0}
+#define TSx(name, amode) {TERM, amode, name, 1, 1, 0, 0, 0}
+#define TSy(name, amode) {TERM, amode, name, 1, 0, 1, 0, 0}
+#define TSp(name, amode) {TERM, amode, name, 1, 0, 0, 0, 1}
+#define TSZ(name, amode, sz) {TERM, amode, name, 1, 0, 0, 0, 0}
+#define TSZx(name, amode, sz) {TERM, amode, name, 1, 1, 0, 0, 0}
+#define TSZy(name, amode, sz) {TERM, amode, name, 1, 0, 1, 0, 0}
+#define INVALID {TERM, UNKNOWN, "", 0, 0, 0, 0, 0}
+#elif defined(DIS_MEM)
+#define IND(table) {table, 0, 0, 0, 0, 0, 0}
+#define INDx(table) {table, 0, 0, 1, 0, 0, 0}
+#define TNS(name, amode) {TERM, amode, 0, 0, 0, 0, 0}
+#define TNSu(name, amode) {TERM, amode, 0, 0, 0, 1, 0}
+#define TNSy(name, amode) {TERM, amode, 0, 0, 1, 0, 0}
+#define TNSyp(name, amode) {TERM, amode, 0, 0, 1, 0, 1}
+#define TNSx(name, amode) {TERM, amode, 0, 1, 0, 0, 0}
+#define TNSZ(name, amode, sz) {TERM, amode, sz, 0, 0, 0, 0}
+#define TNSZy(name, amode, sz) {TERM, amode, sz, 0, 1, 0, 0}
+#define TS(name, amode) {TERM, amode, 0, 0, 0, 0, 0}
+#define TSx(name, amode) {TERM, amode, 0, 1, 0, 0, 0}
+#define TSy(name, amode) {TERM, amode, 0, 0, 1, 0, 0}
+#define TSp(name, amode) {TERM, amode, 0, 0, 0, 0, 1}
+#define TSZ(name, amode, sz) {TERM, amode, sz, 0, 0, 0, 0}
+#define TSZx(name, amode, sz) {TERM, amode, sz, 1, 0, 0, 0}
+#define TSZy(name, amode, sz) {TERM, amode, sz, 0, 1, 0, 0}
+#define INVALID {TERM, UNKNOWN, 0, 0, 0, 0, 0}
+#else
+#define IND(table) {table[0], 0, 0, 0, 0, 0}
+#define INDx(table) {table[0], 0, 1, 0, 0, 0}
+#define TNS(name, amode) {TERM, amode, 0, 0, 0, 0}
+#define TNSu(name, amode) {TERM, amode, 0, 0, 1, 0}
+#define TNSy(name, amode) {TERM, amode, 0, 1, 0, 0}
+#define TNSyp(name, amode) {TERM, amode, 0, 1, 0, 1}
+#define TNSx(name, amode) {TERM, amode, 1, 0, 0, 0}
+#define TNSZ(name, amode, sz) {TERM, amode, 0, 0, 0, 0}
+#define TNSZy(name, amode, sz) {TERM, amode, 0, 1, 0, 0}
+#define TS(name, amode) {TERM, amode, 0, 0, 0, 0}
+#define TSx(name, amode) {TERM, amode, 1, 0, 0, 0}
+#define TSy(name, amode) {TERM, amode, 0, 1, 0, 0}
+#define TSp(name, amode) {TERM, amode, 0, 0, 0, 1}
+#define TSZ(name, amode, sz) {TERM, amode, 0, 0, 0, 0}
+#define TSZx(name, amode, sz) {TERM, amode, 1, 0, 0, 0}
+#define TSZy(name, amode, sz) {TERM, amode, 0, 1, 0, 0}
+#define INVALID {TERM, UNKNOWN, 0, 0, 0, 0}
+#endif
+
+#ifdef DIS_TEXT
+/*
+ * this decodes the r_m field for mode's 0, 1, 2 in 16 bit mode
+ */
+const char *const dis_addr16[3][8] = {
+"(%bx,%si)", "(%bx,%di)", "(%bp,%si)", "(%bp,%di)", "(%si)", "(%di)", "",
+ "(%bx)",
+"(%bx,%si)", "(%bx,%di)", "(%bp,%si)", "(%bp,%di)", "(%si)", "(%di", "(%bp)",
+ "(%bx)",
+"(%bx,%si)", "(%bx,%di)", "(%bp,%si)", "(%bp,%di)", "(%si)", "(%di)", "(%bp)",
+ "(%bx)",
+};
+
+
+/*
+ * This decodes 32 bit addressing mode r_m field for modes 0, 1, 2
+ */
+const char *const dis_addr32_mode0[16] = {
+ "(%eax)", "(%ecx)", "(%edx)", "(%ebx)", "", "", "(%esi)", "(%edi)",
+ "(%r8d)", "(%r9d)", "(%r10d)", "(%r11d)", "", "", "(%r14d)", "(%r15d)"
+};
+
+const char *const dis_addr32_mode12[16] = {
+ "(%eax)", "(%ecx)", "(%edx)", "(%ebx)", "", "(%ebp)", "(%esi)", "(%edi)",
+ "(%r8d)", "(%r9d)", "(%r10d)", "(%r11d)", "", "(%r13d)", "(%r14d)", "(%r15d)"
+};
+
+/*
+ * This decodes 64 bit addressing mode r_m field for modes 0, 1, 2
+ */
+const char *const dis_addr64_mode0[16] = {
+ "(%rax)", "(%rcx)", "(%rdx)", "(%rbx)", "", "(%rip)", "(%rsi)", "(%rdi)",
+ "(%r8)", "(%r9)", "(%r10)", "(%r11)", "(%r12)", "(%rip)", "(%r14)", "(%r15)"
+};
+const char *const dis_addr64_mode12[16] = {
+ "(%rax)", "(%rcx)", "(%rdx)", "(%rbx)", "", "(%rbp)", "(%rsi)", "(%rdi)",
+ "(%r8)", "(%r9)", "(%r10)", "(%r11)", "(%r12)", "(%r13)", "(%r14)", "(%r15)"
+};
+
+/*
+ * decode for scale from SIB byte
+ */
+const char *const dis_scale_factor[4] = { ")", ",2)", ",4)", ",8)" };
+
+/*
+ * register decoding for normal references to registers (ie. not addressing)
+ */
+const char *const dis_REG8[16] = {
+ "%al", "%cl", "%dl", "%bl", "%ah", "%ch", "%dh", "%bh",
+ "%r8b", "%r9b", "%r10b", "%r11b", "%r12b", "%r13b", "%r14b", "%r15b"
+};
+
+const char *const dis_REG8_REX[16] = {
+ "%al", "%cl", "%dl", "%bl", "%spl", "%bpl", "%sil", "%dil",
+ "%r8b", "%r9b", "%r10b", "%r11b", "%r12b", "%r13b", "%r14b", "%r15b"
+};
+
+const char *const dis_REG16[16] = {
+ "%ax", "%cx", "%dx", "%bx", "%sp", "%bp", "%si", "%di",
+ "%r8w", "%r9w", "%r10w", "%r11w", "%r12w", "%r13w", "%r14w", "%r15w"
+};
+
+const char *const dis_REG32[16] = {
+ "%eax", "%ecx", "%edx", "%ebx", "%esp", "%ebp", "%esi", "%edi",
+ "%r8d", "%r9d", "%r10d", "%r11d", "%r12d", "%r13d", "%r14d", "%r15d"
+};
+
+const char *const dis_REG64[16] = {
+ "%rax", "%rcx", "%rdx", "%rbx", "%rsp", "%rbp", "%rsi", "%rdi",
+ "%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15"
+};
+
+const char *const dis_DEBUGREG[16] = {
+ "%db0", "%db1", "%db2", "%db3", "%db4", "%db5", "%db6", "%db7",
+ "%db8", "%db9", "%db10", "%db11", "%db12", "%db13", "%db14", "%db15"
+};
+
+const char *const dis_CONTROLREG[16] = {
+ "%cr0", "%cr1", "%cr2", "%cr3", "%cr4", "%cr5?", "%cr6?", "%cr7?",
+ "%cr8", "%cr9?", "%cr10?", "%cr11?", "%cr12?", "%cr13?", "%cr14?", "%cr15?"
+};
+
+const char *const dis_TESTREG[16] = {
+ "%tr0?", "%tr1?", "%tr2?", "%tr3", "%tr4", "%tr5", "%tr6", "%tr7",
+ "%tr0?", "%tr1?", "%tr2?", "%tr3", "%tr4", "%tr5", "%tr6", "%tr7"
+};
+
+const char *const dis_MMREG[16] = {
+ "%mm0", "%mm1", "%mm2", "%mm3", "%mm4", "%mm5", "%mm6", "%mm7",
+ "%mm0", "%mm1", "%mm2", "%mm3", "%mm4", "%mm5", "%mm6", "%mm7"
+};
+
+const char *const dis_XMMREG[16] = {
+ "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7",
+ "%xmm8", "%xmm9", "%xmm10", "%xmm11", "%xmm12", "%xmm13", "%xmm14", "%xmm15"
+};
+
+const char *const dis_SEGREG[16] = {
+ "%es", "%cs", "%ss", "%ds", "%fs", "%gs", "<reserved>", "<reserved>",
+ "%es", "%cs", "%ss", "%ds", "%fs", "%gs", "<reserved>", "<reserved>"
+};
+
+/*
+ * SIMD predicate suffixes
+ */
+const char *const dis_PREDSUFFIX[8] = {
+ "eq", "lt", "le", "unord", "neq", "nlt", "nle", "ord"
+};
+
+
+
+#endif /* DIS_TEXT */
+
+
+
+
+/*
+ * "decode table" for 64 bit mode MOVSXD instruction (opcode 0x63)
+ */
+const instable_t dis_opMOVSLD = TNS("movslq",MOVSXZ);
+
+/*
+ * "decode table" for pause and clflush instructions
+ */
+const instable_t dis_opPause = TNS("pause", NORM);
+
+/*
+ * Decode table for 0x0F00 opcodes
+ */
+const instable_t dis_op0F00[8] = {
+
+/* [0] */ TNS("sldt",M), TNS("str",M), TNSy("lldt",M), TNSy("ltr",M),
+/* [4] */ TNSZ("verr",M,2), TNSZ("verw",M,2), INVALID, INVALID,
+};
+
+
+/*
+ * Decode table for 0x0F01 opcodes
+ */
+const instable_t dis_op0F01[8] = {
+
+/* [0] */ TNSZ("sgdt",MO,6), TNSZ("sidt",MO,6), TNSZ("lgdt",MO,6), TNSZ("lidt",MO,6),
+/* [4] */ TNSZ("smsw",M,2), INVALID, TNSZ("lmsw",M,2), TNS("invlpg",SWAPGS),
+};
+
+/*
+ * Decode table for 0x0F18 opcodes -- SIMD prefetch
+ */
+const instable_t dis_op0F18[8] = {
+
+/* [0] */ TNS("prefetchnta",PREF),TNS("prefetcht0",PREF), TNS("prefetcht1",PREF), TNS("prefetcht2",PREF),
+/* [4] */ INVALID, INVALID, INVALID, INVALID,
+};
+
+/*
+ * Decode table for 0x0FAE opcodes -- SIMD state save/restore
+ */
+const instable_t dis_op0FAE[8] = {
+/* [0] */ TNSZ("fxsave",M,512), TNSZ("fxrstor",M,512), TNS("ldmxcsr",M), TNS("stmxcsr",M),
+/* [4] */ INVALID, TNS("lfence",XMMFENCE), TNS("mfence",XMMFENCE), TNS("sfence",XMMSFNC),
+};
+
+/*
+ * Decode table for 0x0FBA opcodes
+ */
+
+const instable_t dis_op0FBA[8] = {
+
+/* [0] */ INVALID, INVALID, INVALID, INVALID,
+/* [4] */ TS("bt",MIb), TS("bts",MIb), TS("btr",MIb), TS("btc",MIb),
+};
+
+/*
+ * Decode table for 0x0FC7 opcode
+ */
+
+const instable_t dis_op0FC7[8] = {
+
+/* [0] */ INVALID, TNS("cmpxchg8b",M), INVALID, INVALID,
+/* [4] */ INVALID, INVALID, INVALID, INVALID,
+};
+
+
+/*
+ * Decode table for 0x0FC8 opcode -- 486 bswap instruction
+ *
+ *bit pattern: 0000 1111 1100 1reg
+ */
+const instable_t dis_op0FC8[4] = {
+/* [0] */ TNS("bswap",R), INVALID, INVALID, INVALID,
+};
+
+/*
+ * Decode table for 0x0F71, 0x0F72, and 0x0F73 opcodes -- MMX instructions
+ */
+const instable_t dis_op0F7123[4][8] = {
+{
+/* [70].0 */ INVALID, INVALID, INVALID, INVALID,
+/* .4 */ INVALID, INVALID, INVALID, INVALID,
+}, {
+/* [71].0 */ INVALID, INVALID, TNS("psrlw",MMOSH), INVALID,
+/* .4 */ TNS("psraw",MMOSH), INVALID, TNS("psllw",MMOSH), INVALID,
+}, {
+/* [72].0 */ INVALID, INVALID, TNS("psrld",MMOSH), INVALID,
+/* .4 */ TNS("psrad",MMOSH), INVALID, TNS("pslld",MMOSH), INVALID,
+}, {
+/* [73].0 */ INVALID, INVALID, TNS("psrlq",MMOSH), TNS("INVALID",MMOSH),
+/* .4 */ INVALID, INVALID, TNS("psllq",MMOSH), TNS("INVALID",MMOSH),
+} };
+
+/*
+ * Decode table for SIMD extensions to above 0x0F71-0x0F73 opcodes.
+ */
+const instable_t dis_opSIMD7123[32] = {
+/* [70].0 */ INVALID, INVALID, INVALID, INVALID,
+/* .4 */ INVALID, INVALID, INVALID, INVALID,
+
+/* [71].0 */ INVALID, INVALID, TNS("psrlw",XMMSH), INVALID,
+/* .4 */ TNS("psraw",XMMSH), INVALID, TNS("psllw",XMMSH), INVALID,
+
+/* [72].0 */ INVALID, INVALID, TNS("psrld",XMMSH), INVALID,
+/* .4 */ TNS("psrad",XMMSH), INVALID, TNS("pslld",XMMSH), INVALID,
+
+/* [73].0 */ INVALID, INVALID, TNS("psrlq",XMMSH), TNS("psrldq",XMMSH),
+/* .4 */ INVALID, INVALID, TNS("psllq",XMMSH), TNS("pslldq",XMMSH),
+};
+
+/*
+ * SIMD instructions have been wedged into the existing IA32 instruction
+ * set through the use of prefixes. That is, while 0xf0 0x58 may be
+ * addps, 0xf3 0xf0 0x58 (literally, repz addps) is a completely different
+ * instruction - addss. At present, three prefixes have been coopted in
+ * this manner - address size (0x66), repnz (0xf2) and repz (0xf3). The
+ * following tables are used to provide the prefixed instruction names.
+ * The arrays are sparse, but they're fast.
+ */
+
+/*
+ * Decode table for SIMD instructions with the address size (0x66) prefix.
+ */
+const instable_t dis_opSIMDdata16[256] = {
+/* [00] */ INVALID, INVALID, INVALID, INVALID,
+/* [04] */ INVALID, INVALID, INVALID, INVALID,
+/* [08] */ INVALID, INVALID, INVALID, INVALID,
+/* [0C] */ INVALID, INVALID, INVALID, INVALID,
+
+/* [10] */ TNSZ("movupd",XMM,16), TNSZ("movupd",XMMS,16), TNSZ("movlpd",XMMM,8), TNSZ("movlpd",XMMMS,8),
+/* [14] */ TNSZ("unpcklpd",XMM,16),TNSZ("unpckhpd",XMM,16),TNSZ("movhpd",XMMM,8), TNSZ("movhpd",XMMMS,8),
+/* [18] */ INVALID, INVALID, INVALID, INVALID,
+/* [1C] */ INVALID, INVALID, INVALID, INVALID,
+
+/* [20] */ INVALID, INVALID, INVALID, INVALID,
+/* [24] */ INVALID, INVALID, INVALID, INVALID,
+/* [28] */ TNSZ("movapd",XMM,16), TNSZ("movapd",XMMS,16), TNSZ("cvtpi2pd",XMMOMX,8),TNSZ("movntpd",XMMOMS,16),
+/* [2C] */ TNSZ("cvttpd2pi",XMMXMM,16),TNSZ("cvtpd2pi",XMMXMM,16),TNSZ("ucomisd",XMM,8),TNSZ("comisd",XMM,8),
+
+/* [30] */ INVALID, INVALID, INVALID, INVALID,
+/* [34] */ INVALID, INVALID, INVALID, INVALID,
+/* [38] */ INVALID, INVALID, INVALID, INVALID,
+/* [3C] */ INVALID, INVALID, INVALID, INVALID,
+
+/* [40] */ INVALID, INVALID, INVALID, INVALID,
+/* [44] */ INVALID, INVALID, INVALID, INVALID,
+/* [48] */ INVALID, INVALID, INVALID, INVALID,
+/* [4C] */ INVALID, INVALID, INVALID, INVALID,
+
+/* [50] */ TNS("movmskpd",XMMOX3), TNSZ("sqrtpd",XMM,16), INVALID, INVALID,
+/* [54] */ TNSZ("andpd",XMM,16), TNSZ("andnpd",XMM,16), TNSZ("orpd",XMM,16), TNSZ("xorpd",XMM,16),
+/* [58] */ TNSZ("addpd",XMM,16), TNSZ("mulpd",XMM,16), TNSZ("cvtpd2ps",XMM,16),TNSZ("cvtps2dq",XMM,16),
+/* [5C] */ TNSZ("subpd",XMM,16), TNSZ("minpd",XMM,16), TNSZ("divpd",XMM,16), TNSZ("maxpd",XMM,16),
+
+/* [60] */ TNSZ("punpcklbw",XMM,16),TNSZ("punpcklwd",XMM,16),TNSZ("punpckldq",XMM,16),TNSZ("packsswb",XMM,16),
+/* [64] */ TNSZ("pcmpgtb",XMM,16), TNSZ("pcmpgtw",XMM,16), TNSZ("pcmpgtd",XMM,16), TNSZ("packuswb",XMM,16),
+/* [68] */ TNSZ("punpckhbw",XMM,16),TNSZ("punpckhwd",XMM,16),TNSZ("punpckhdq",XMM,16),TNSZ("packssdw",XMM,16),
+/* [6C] */ TNSZ("punpcklqdq",XMM,16),TNSZ("punpckhqdq",XMM,16),TNSZ("movd",XMM3MX,4),TNSZ("movdqa",XMM,16),
+
+/* [70] */ TNSZ("pshufd",XMMP,16), INVALID, INVALID, INVALID,
+/* [74] */ TNSZ("pcmpeqb",XMM,16), TNSZ("pcmpeqw",XMM,16), TNSZ("pcmpeqd",XMM,16), INVALID,
+/* [78] */ INVALID, INVALID, INVALID, INVALID,
+/* [7C] */ INVALID, INVALID, TNSZ("movd",XMM3MXS,4), TNSZ("movdqa",XMMS,16),
+
+/* [80] */ INVALID, INVALID, INVALID, INVALID,
+/* [84] */ INVALID, INVALID, INVALID, INVALID,
+/* [88] */ INVALID, INVALID, INVALID, INVALID,
+/* [8C] */ INVALID, INVALID, INVALID, INVALID,
+
+/* [90] */ INVALID, INVALID, INVALID, INVALID,
+/* [94] */ INVALID, INVALID, INVALID, INVALID,
+/* [98] */ INVALID, INVALID, INVALID, INVALID,
+/* [9C] */ INVALID, INVALID, INVALID, INVALID,
+
+/* [A0] */ INVALID, INVALID, INVALID, INVALID,
+/* [A4] */ INVALID, INVALID, INVALID, INVALID,
+/* [A8] */ INVALID, INVALID, INVALID, INVALID,
+/* [AC] */ INVALID, INVALID, INVALID, INVALID,
+
+/* [B0] */ INVALID, INVALID, INVALID, INVALID,
+/* [B4] */ INVALID, INVALID, INVALID, INVALID,
+/* [B8] */ INVALID, INVALID, INVALID, INVALID,
+/* [BC] */ INVALID, INVALID, INVALID, INVALID,
+
+/* [C0] */ INVALID, INVALID, TNSZ("cmppd",XMMP,16), INVALID,
+/* [C4] */ TNSZ("pinsrw",XMMPRM,2),TNS("pextrw",XMM3P), TNSZ("shufpd",XMMP,16), INVALID,
+/* [C8] */ INVALID, INVALID, INVALID, INVALID,
+/* [CC] */ INVALID, INVALID, INVALID, INVALID,
+
+/* [D0] */ INVALID, TNSZ("psrlw",XMM,16), TNSZ("psrld",XMM,16), TNSZ("psrlq",XMM,16),
+/* [D4] */ TNSZ("paddq",XMM,16), TNSZ("pmullw",XMM,16), TNSZ("movq",XMMS,8), TNS("pmovmskb",XMMX3),
+/* [D8] */ TNSZ("psubusb",XMM,16), TNSZ("psubusw",XMM,16), TNSZ("pminub",XMM,16), TNSZ("pand",XMM,16),
+/* [DC] */ TNSZ("paddusb",XMM,16), TNSZ("paddusw",XMM,16), TNSZ("pmaxub",XMM,16), TNSZ("pandn",XMM,16),
+
+/* [E0] */ TNSZ("pavgb",XMM,16), TNSZ("psraw",XMM,16), TNSZ("psrad",XMM,16), TNSZ("pavgw",XMM,16),
+/* [E4] */ TNSZ("pmulhuw",XMM,16), TNSZ("pmulhw",XMM,16), TNSZ("cvttpd2dq",XMM,16),TNSZ("movntdq",XMMS,16),
+/* [E8] */ TNSZ("psubsb",XMM,16), TNSZ("psubsw",XMM,16), TNSZ("pminsw",XMM,16), TNSZ("por",XMM,16),
+/* [EC] */ TNSZ("paddsb",XMM,16), TNSZ("paddsw",XMM,16), TNSZ("pmaxsw",XMM,16), TNSZ("pxor",XMM,16),
+
+/* [F0] */ INVALID, TNSZ("psllw",XMM,16), TNSZ("pslld",XMM,16), TNSZ("psllq",XMM,16),
+/* [F4] */ TNSZ("pmuludq",XMM,16), TNSZ("pmaddwd",XMM,16), TNSZ("psadbw",XMM,16), TNSZ("maskmovdqu", XMMXIMPL,16),
+/* [F8] */ TNSZ("psubb",XMM,16), TNSZ("psubw",XMM,16), TNSZ("psubd",XMM,16), TNSZ("psubq",XMM,16),
+/* [FC] */ TNSZ("paddb",XMM,16), TNSZ("paddw",XMM,16), TNSZ("paddd",XMM,16), INVALID,
+};
+
+/*
+ * Decode table for SIMD instructions with the repnz (0xf2) prefix.
+ */
+const instable_t dis_opSIMDrepnz[256] = {
+/* [00] */ INVALID, INVALID, INVALID, INVALID,
+/* [04] */ INVALID, INVALID, INVALID, INVALID,
+/* [08] */ INVALID, INVALID, INVALID, INVALID,
+/* [0C] */ INVALID, INVALID, INVALID, INVALID,
+
+/* [10] */ TNSZ("movsd",XMM,8), TNSZ("movsd",XMMS,8), INVALID, INVALID,
+/* [14] */ INVALID, INVALID, INVALID, INVALID,
+/* [18] */ INVALID, INVALID, INVALID, INVALID,
+/* [1C] */ INVALID, INVALID, INVALID, INVALID,
+
+/* [20] */ INVALID, INVALID, INVALID, INVALID,
+/* [24] */ INVALID, INVALID, INVALID, INVALID,
+/* [28] */ INVALID, INVALID, TNSZ("cvtsi2sd",XMM3MX,4),INVALID,
+/* [2C] */ TNSZ("cvttsd2si",XMMXM3,8),TNSZ("cvtsd2si",XMMXM3,8),INVALID, INVALID,
+
+/* [30] */ INVALID, INVALID, INVALID, INVALID,
+/* [34] */ INVALID, INVALID, INVALID, INVALID,
+/* [38] */ INVALID, INVALID, INVALID, INVALID,
+/* [3C] */ INVALID, INVALID, INVALID, INVALID,
+
+/* [40] */ INVALID, INVALID, INVALID, INVALID,
+/* [44] */ INVALID, INVALID, INVALID, INVALID,
+/* [48] */ INVALID, INVALID, INVALID, INVALID,
+/* [4C] */ INVALID, INVALID, INVALID, INVALID,
+
+/* [50] */ INVALID, TNSZ("sqrtsd",XMM,8), INVALID, INVALID,
+/* [54] */ INVALID, INVALID, INVALID, INVALID,
+/* [58] */ TNSZ("addsd",XMM,8), TNSZ("mulsd",XMM,8), TNSZ("cvtsd2ss",XMM,8), INVALID,
+/* [5C] */ TNSZ("subsd",XMM,8), TNSZ("minsd",XMM,8), TNSZ("divsd",XMM,8), TNSZ("maxsd",XMM,8),
+
+/* [60] */ INVALID, INVALID, INVALID, INVALID,
+/* [64] */ INVALID, INVALID, INVALID, INVALID,
+/* [68] */ INVALID, INVALID, INVALID, INVALID,
+/* [6C] */ INVALID, INVALID, INVALID, INVALID,
+
+/* [70] */ TNSZ("pshuflw",XMMP,16),INVALID, INVALID, INVALID,
+/* [74] */ INVALID, INVALID, INVALID, INVALID,
+/* [78] */ INVALID, INVALID, INVALID, INVALID,
+/* [7C] */ INVALID, INVALID, INVALID, INVALID,
+
+/* [80] */ INVALID, INVALID, INVALID, INVALID,
+/* [84] */ INVALID, INVALID, INVALID, INVALID,
+/* [88] */ INVALID, INVALID, INVALID, INVALID,
+/* [0C] */ INVALID, INVALID, INVALID, INVALID,
+
+/* [90] */ INVALID, INVALID, INVALID, INVALID,
+/* [94] */ INVALID, INVALID, INVALID, INVALID,
+/* [98] */ INVALID, INVALID, INVALID, INVALID,
+/* [9C] */ INVALID, INVALID, INVALID, INVALID,
+
+/* [A0] */ INVALID, INVALID, INVALID, INVALID,
+/* [A4] */ INVALID, INVALID, INVALID, INVALID,
+/* [A8] */ INVALID, INVALID, INVALID, INVALID,
+/* [AC] */ INVALID, INVALID, INVALID, INVALID,
+
+/* [B0] */ INVALID, INVALID, INVALID, INVALID,
+/* [B4] */ INVALID, INVALID, INVALID, INVALID,
+/* [B8] */ INVALID, INVALID, INVALID, INVALID,
+/* [BC] */ INVALID, INVALID, INVALID, INVALID,
+
+/* [C0] */ INVALID, INVALID, TNSZ("cmpsd",XMMP,8), INVALID,
+/* [C4] */ INVALID, INVALID, INVALID, INVALID,
+/* [C8] */ INVALID, INVALID, INVALID, INVALID,
+/* [CC] */ INVALID, INVALID, INVALID, INVALID,
+
+/* [D0] */ INVALID, INVALID, INVALID, INVALID,
+/* [D4] */ INVALID, INVALID, TNS("movdq2q",XMMXM), INVALID,
+/* [D8] */ INVALID, INVALID, INVALID, INVALID,
+/* [DC] */ INVALID, INVALID, INVALID, INVALID,
+
+/* [E0] */ INVALID, INVALID, INVALID, INVALID,
+/* [E4] */ INVALID, INVALID, TNSZ("cvtpd2dq",XMM,16),INVALID,
+/* [E8] */ INVALID, INVALID, INVALID, INVALID,
+/* [EC] */ INVALID, INVALID, INVALID, INVALID,
+
+/* [F0] */ INVALID, INVALID, INVALID, INVALID,
+/* [F4] */ INVALID, INVALID, INVALID, INVALID,
+/* [F8] */ INVALID, INVALID, INVALID, INVALID,
+/* [FC] */ INVALID, INVALID, INVALID, INVALID,
+};
+
+/*
+ * Decode table for SIMD instructions with the repz (0xf3) prefix.
+ */
+const instable_t dis_opSIMDrepz[256] = {
+/* [00] */ INVALID, INVALID, INVALID, INVALID,
+/* [04] */ INVALID, INVALID, INVALID, INVALID,
+/* [08] */ INVALID, INVALID, INVALID, INVALID,
+/* [0C] */ INVALID, INVALID, INVALID, INVALID,
+
+/* [10] */ TNSZ("movss",XMM,4), TNSZ("movss",XMMS,4), INVALID, INVALID,
+/* [14] */ INVALID, INVALID, INVALID, INVALID,
+/* [18] */ INVALID, INVALID, INVALID, INVALID,
+/* [1C] */ INVALID, INVALID, INVALID, INVALID,
+
+/* [20] */ INVALID, INVALID, INVALID, INVALID,
+/* [24] */ INVALID, INVALID, INVALID, INVALID,
+/* [28] */ INVALID, INVALID, TNSZ("cvtsi2ss",XMM3MX,4),INVALID,
+/* [2C] */ TNSZ("cvttss2si",XMMXM3,4),TNSZ("cvtss2si",XMMXM3,4),INVALID, INVALID,
+
+/* [30] */ INVALID, INVALID, INVALID, INVALID,
+/* [34] */ INVALID, INVALID, INVALID, INVALID,
+/* [38] */ INVALID, INVALID, INVALID, INVALID,
+/* [3C] */ INVALID, INVALID, INVALID, INVALID,
+
+/* [40] */ INVALID, INVALID, INVALID, INVALID,
+/* [44] */ INVALID, INVALID, INVALID, INVALID,
+/* [48] */ INVALID, INVALID, INVALID, INVALID,
+/* [4C] */ INVALID, INVALID, INVALID, INVALID,
+
+/* [50] */ INVALID, TNSZ("sqrtss",XMM,4), TNSZ("rsqrtss",XMM,4), TNSZ("rcpss",XMM,4),
+/* [54] */ INVALID, INVALID, INVALID, INVALID,
+/* [58] */ TNSZ("addss",XMM,4), TNSZ("mulss",XMM,4), TNSZ("cvtss2sd",XMM,4), TNSZ("cvttps2dq",XMM,16),
+/* [5C] */ TNSZ("subss",XMM,4), TNSZ("minss",XMM,4), TNSZ("divss",XMM,4), TNSZ("maxss",XMM,4),
+
+/* [60] */ INVALID, INVALID, INVALID, INVALID,
+/* [64] */ INVALID, INVALID, INVALID, INVALID,
+/* [68] */ INVALID, INVALID, INVALID, INVALID,
+/* [6C] */ INVALID, INVALID, INVALID, TNSZ("movdqu",XMM,16),
+
+/* [70] */ TNSZ("pshufhw",XMMP,16),INVALID, INVALID, INVALID,
+/* [74] */ INVALID, INVALID, INVALID, INVALID,
+/* [78] */ INVALID, INVALID, INVALID, INVALID,
+/* [7C] */ INVALID, INVALID, TNSZ("movq",XMM,8), TNSZ("movdqu",XMMS,16),
+
+/* [80] */ INVALID, INVALID, INVALID, INVALID,
+/* [84] */ INVALID, INVALID, INVALID, INVALID,
+/* [88] */ INVALID, INVALID, INVALID, INVALID,
+/* [0C] */ INVALID, INVALID, INVALID, INVALID,
+
+/* [90] */ INVALID, INVALID, INVALID, INVALID,
+/* [94] */ INVALID, INVALID, INVALID, INVALID,
+/* [98] */ INVALID, INVALID, INVALID, INVALID,
+/* [9C] */ INVALID, INVALID, INVALID, INVALID,
+
+/* [A0] */ INVALID, INVALID, INVALID, INVALID,
+/* [A4] */ INVALID, INVALID, INVALID, INVALID,
+/* [A8] */ INVALID, INVALID, INVALID, INVALID,
+/* [AC] */ INVALID, INVALID, INVALID, INVALID,
+
+/* [B0] */ INVALID, INVALID, INVALID, INVALID,
+/* [B4] */ INVALID, INVALID, INVALID, INVALID,
+/* [B8] */ INVALID, INVALID, INVALID, INVALID,
+/* [BC] */ INVALID, INVALID, INVALID, INVALID,
+
+/* [C0] */ INVALID, INVALID, TNSZ("cmpss",XMMP,4), INVALID,
+/* [C4] */ INVALID, INVALID, INVALID, INVALID,
+/* [C8] */ INVALID, INVALID, INVALID, INVALID,
+/* [CC] */ INVALID, INVALID, INVALID, INVALID,
+
+/* [D0] */ INVALID, INVALID, INVALID, INVALID,
+/* [D4] */ INVALID, INVALID, TNS("movq2dq",XMMMX), INVALID,
+/* [D8] */ INVALID, INVALID, INVALID, INVALID,
+/* [DC] */ INVALID, INVALID, INVALID, INVALID,
+
+/* [E0] */ INVALID, INVALID, INVALID, INVALID,
+/* [E4] */ INVALID, INVALID, TNSZ("cvtdq2pd",XMM,8), INVALID,
+/* [E8] */ INVALID, INVALID, INVALID, INVALID,
+/* [EC] */ INVALID, INVALID, INVALID, INVALID,
+
+/* [F0] */ INVALID, INVALID, INVALID, INVALID,
+/* [F4] */ INVALID, INVALID, INVALID, INVALID,
+/* [F8] */ INVALID, INVALID, INVALID, INVALID,
+/* [FC] */ INVALID, INVALID, INVALID, INVALID,
+};
+
+/*
+ * Decode table for 0x0F opcodes
+ */
+
+const instable_t dis_op0F[16][16] = {
+{
+/* [00] */ IND(dis_op0F00), IND(dis_op0F01), TNS("lar",MR), TNS("lsl",MR),
+/* [04] */ INVALID, TNS("syscall",NORM), TNS("clts",NORM), TNS("sysret",NORM),
+/* [08] */ TNS("invd",NORM), TNS("wbinvd",NORM), INVALID, TNS("ud2",NORM),
+/* [0C] */ INVALID, INVALID, INVALID, INVALID,
+}, {
+/* [10] */ TNSZ("movups",XMMO,16), TNSZ("movups",XMMOS,16),TNSZ("movlps",XMMO,8), TNSZ("movlps",XMMOS,8),
+/* [14] */ TNSZ("unpcklps",XMMO,16),TNSZ("unpckhps",XMMO,16),TNSZ("movhps",XMMOM,8),TNSZ("movhps",XMMOMS,8),
+/* [18] */ IND(dis_op0F18), INVALID, INVALID, INVALID,
+/* [1C] */ INVALID, INVALID, INVALID, INVALID,
+}, {
+/* [20] */ TSy("mov",SREG), TSy("mov",SREG), TSy("mov",SREG), TSy("mov",SREG),
+/* [24] */ TSx("mov",SREG), INVALID, TSx("mov",SREG), INVALID,
+/* [28] */ TNSZ("movaps",XMMO,16), TNSZ("movaps",XMMOS,16),TNSZ("cvtpi2ps",XMMOMX,8),TNSZ("movntps",XMMOS,16),
+/* [2C] */ TNSZ("cvttps2pi",XMMOXMM,8),TNSZ("cvtps2pi",XMMOXMM,8),TNSZ("ucomiss",XMMO,4),TNSZ("comiss",XMMO,4),
+}, {
+/* [30] */ TNS("wrmsr",NORM), TNS("rdtsc",NORM), TNS("rdmsr",NORM), TNS("rdpmc",NORM),
+/* [34] */ TNSx("sysenter",NORM), TNSx("sysexit",NORM), INVALID, INVALID,
+/* [38] */ INVALID, INVALID, INVALID, INVALID,
+/* [3C] */ INVALID, INVALID, INVALID, INVALID,
+}, {
+/* [40] */ TS("cmovx.o",MR), TS("cmovx.no",MR), TS("cmovx.b",MR), TS("cmovx.ae",MR),
+/* [44] */ TS("cmovx.e",MR), TS("cmovx.ne",MR), TS("cmovx.be",MR), TS("cmovx.a",MR),
+/* [48] */ TS("cmovx.s",MR), TS("cmovx.ns",MR), TS("cmovx.pe",MR), TS("cmovx.po",MR),
+/* [4C] */ TS("cmovx.l",MR), TS("cmovx.ge",MR), TS("cmovx.le",MR), TS("cmovx.g",MR),
+}, {
+/* [50] */ TNS("movmskps",XMMOX3), TNSZ("sqrtps",XMMO,16), TNSZ("rsqrtps",XMMO,16),TNSZ("rcpps",XMMO,16),
+/* [54] */ TNSZ("andps",XMMO,16), TNSZ("andnps",XMMO,16), TNSZ("orps",XMMO,16), TNSZ("xorps",XMMO,16),
+/* [58] */ TNSZ("addps",XMMO,16), TNSZ("mulps",XMMO,16), TNSZ("cvtps2pd",XMMO,8),TNSZ("cvtdq2ps",XMMO,16),
+/* [5C] */ TNSZ("subps",XMMO,16), TNSZ("minps",XMMO,16), TNSZ("divps",XMMO,16), TNSZ("maxps",XMMO,16),
+}, {
+/* [60] */ TNSZ("punpcklbw",MMO,4),TNSZ("punpcklwd",MMO,4),TNSZ("punpckldq",MMO,4),TNSZ("packsswb",MMO,8),
+/* [64] */ TNSZ("pcmpgtb",MMO,8), TNSZ("pcmpgtw",MMO,8), TNSZ("pcmpgtd",MMO,8), TNSZ("packuswb",MMO,8),
+/* [68] */ TNSZ("punpckhbw",MMO,8),TNSZ("punpckhwd",MMO,8),TNSZ("punpckhdq",MMO,8),TNSZ("packssdw",MMO,8),
+/* [6C] */ TNSZ("INVALID",MMO,0), TNSZ("INVALID",MMO,0), TNSZ("movd",MMO,4), TNSZ("movq",MMO,8),
+}, {
+/* [70] */ TNSZ("pshufw",MMOPM,8), TNS("psrXXX",MR), TNS("psrXXX",MR), TNS("psrXXX",MR),
+/* [74] */ TNSZ("pcmpeqb",MMO,8), TNSZ("pcmpeqw",MMO,8), TNSZ("pcmpeqd",MMO,8), TNS("emms",NORM),
+/* [78] */ INVALID, INVALID, INVALID, INVALID,
+/* [7C] */ INVALID, INVALID, TNSZ("movd",MMOS,4), TNSZ("movq",MMOS,8),
+}, {
+/* [80] */ TNS("jo",D), TNS("jno",D), TNS("jb",D), TNS("jae",D),
+/* [84] */ TNS("je",D), TNS("jne",D), TNS("jbe",D), TNS("ja",D),
+/* [88] */ TNS("js",D), TNS("jns",D), TNS("jp",D), TNS("jnp",D),
+/* [8C] */ TNS("jl",D), TNS("jge",D), TNS("jle",D), TNS("jg",D),
+}, {
+/* [90] */ TNS("seto",Mb), TNS("setno",Mb), TNS("setb",Mb), TNS("setae",Mb),
+/* [94] */ TNS("sete",Mb), TNS("setne",Mb), TNS("setbe",Mb), TNS("seta",Mb),
+/* [98] */ TNS("sets",Mb), TNS("setns",Mb), TNS("setp",Mb), TNS("setnp",Mb),
+/* [9C] */ TNS("setl",Mb), TNS("setge",Mb), TNS("setle",Mb), TNS("setg",Mb),
+}, {
+/* [A0] */ TSp("push",LSEG), TSp("pop",LSEG), TNS("cpuid",NORM), TS("bt",RMw),
+/* [A4] */ TS("shld",DSHIFT), TS("shld",DSHIFTcl), INVALID, INVALID,
+/* [A8] */ TSp("push",LSEG), TSp("pop",LSEG), TNS("rsm",NORM), TS("bts",RMw),
+/* [AC] */ TS("shrd",DSHIFT), TS("shrd",DSHIFTcl), IND(dis_op0FAE), TS("imul",MRw),
+}, {
+/* [B0] */ TNS("cmpxchgb",RMw), TS("cmpxchg",RMw), TS("lss",MR), TS("btr",RMw),
+/* [B4] */ TS("lfs",MR), TS("lgs",MR), TS("movzb",MOVZ), TNS("movzwl",MOVZ),
+/* [B8] */ INVALID, INVALID, IND(dis_op0FBA), TS("btc",RMw),
+/* [BC] */ TS("bsf",MRw), TS("bsr",MRw), TS("movsb",MOVZ), TNS("movswl",MOVZ),
+}, {
+/* [C0] */ TNS("xaddb",XADDB), TS("xadd",RMw), TNSZ("cmpps",XMMOPM,16),TNS("movnti",RM),
+/* [C4] */ TNSZ("pinsrw",MMOPRM,2),TNS("pextrw",MMO3P), TNSZ("shufps",XMMOPM,16),IND(dis_op0FC7),
+/* [C8] */ INVALID, INVALID, INVALID, INVALID,
+/* [CC] */ INVALID, INVALID, INVALID, INVALID,
+}, {
+/* [D0] */ INVALID, TNSZ("psrlw",MMO,8), TNSZ("psrld",MMO,8), TNSZ("psrlq",MMO,8),
+/* [D4] */ TNSZ("paddq",MMO,8), TNSZ("pmullw",MMO,8), TNSZ("INVALID",MMO,0), TNS("pmovmskb",MMOM3),
+/* [D8] */ TNSZ("psubusb",MMO,8), TNSZ("psubusw",MMO,8), TNSZ("pminub",MMO,8), TNSZ("pand",MMO,8),
+/* [DC] */ TNSZ("paddusb",MMO,8), TNSZ("paddusw",MMO,8), TNSZ("pmaxub",MMO,8), TNSZ("pandn",MMO,8),
+}, {
+/* [E0] */ TNSZ("pavgb",MMO,8), TNSZ("psraw",MMO,8), TNSZ("psrad",MMO,8), TNSZ("pavgw",MMO,8),
+/* [E4] */ TNSZ("pmulhuw",MMO,8), TNSZ("pmulhw",MMO,8), TNS("INVALID",XMMO), TNSZ("movntq",MMOMS,8),
+/* [E8] */ TNSZ("psubsb",MMO,8), TNSZ("psubsw",MMO,8), TNSZ("pminsw",MMO,8), TNSZ("por",MMO,8),
+/* [EC] */ TNSZ("paddsb",MMO,8), TNSZ("paddsw",MMO,8), TNSZ("pmaxsw",MMO,8), TNSZ("pxor",MMO,8),
+}, {
+/* [F0] */ INVALID, TNSZ("psllw",MMO,8), TNSZ("pslld",MMO,8), TNSZ("psllq",MMO,8),
+/* [F4] */ TNSZ("pmuludq",MMO,8), TNSZ("pmaddwd",MMO,8), TNSZ("psadbw",MMO,8), TNSZ("maskmovq",MMOIMPL,8),
+/* [F8] */ TNSZ("psubb",MMO,8), TNSZ("psubw",MMO,8), TNSZ("psubd",MMO,8), TNSZ("psubq",MMO,8),
+/* [FC] */ TNSZ("paddb",MMO,8), TNSZ("paddw",MMO,8), TNSZ("paddd",MMO,8), INVALID,
+} };
+
+
+/*
+ * Decode table for 0x80 opcodes
+ */
+
+const instable_t dis_op80[8] = {
+
+/* [0] */ TNS("addb",IMlw), TNS("orb",IMw), TNS("adcb",IMlw), TNS("sbbb",IMlw),
+/* [4] */ TNS("andb",IMw), TNS("subb",IMlw), TNS("xorb",IMw), TNS("cmpb",IMlw),
+};
+
+
+/*
+ * Decode table for 0x81 opcodes.
+ */
+
+const instable_t dis_op81[8] = {
+
+/* [0] */ TS("add",IMlw), TS("or",IMw), TS("adc",IMlw), TS("sbb",IMlw),
+/* [4] */ TS("and",IMw), TS("sub",IMlw), TS("xor",IMw), TS("cmp",IMlw),
+};
+
+
+/*
+ * Decode table for 0x82 opcodes.
+ */
+
+const instable_t dis_op82[8] = {
+
+/* [0] */ TNSx("addb",IMlw), TNSx("orb",IMlw), TNSx("adcb",IMlw), TNSx("sbbb",IMlw),
+/* [4] */ TNSx("andb",IMlw), TNSx("subb",IMlw), TNSx("xorb",IMlw), TNSx("cmpb",IMlw),
+};
+/*
+ * Decode table for 0x83 opcodes.
+ */
+
+const instable_t dis_op83[8] = {
+
+/* [0] */ TS("add",IMlw), TS("or",IMlw), TS("adc",IMlw), TS("sbb",IMlw),
+/* [4] */ TS("and",IMlw), TS("sub",IMlw), TS("xor",IMlw), TS("cmp",IMlw),
+};
+
+/*
+ * Decode table for 0xC0 opcodes.
+ */
+
+const instable_t dis_opC0[8] = {
+
+/* [0] */ TNS("rolb",MvI), TNS("rorb",MvI), TNS("rclb",MvI), TNS("rcrb",MvI),
+/* [4] */ TNS("shlb",MvI), TNS("shrb",MvI), INVALID, TNS("sarb",MvI),
+};
+
+/*
+ * Decode table for 0xD0 opcodes.
+ */
+
+const instable_t dis_opD0[8] = {
+
+/* [0] */ TNS("rolb",Mv), TNS("rorb",Mv), TNS("rclb",Mv), TNS("rcrb",Mv),
+/* [4] */ TNS("shlb",Mv), TNS("shrb",Mv), TNS("salb",Mv), TNS("sarb",Mv),
+};
+
+/*
+ * Decode table for 0xC1 opcodes.
+ * 186 instruction set
+ */
+
+const instable_t dis_opC1[8] = {
+
+/* [0] */ TS("rol",MvI), TS("ror",MvI), TS("rcl",MvI), TS("rcr",MvI),
+/* [4] */ TS("shl",MvI), TS("shr",MvI), TS("sal",MvI), TS("sar",MvI),
+};
+
+/*
+ * Decode table for 0xD1 opcodes.
+ */
+
+const instable_t dis_opD1[8] = {
+
+/* [0] */ TS("rol",Mv), TS("ror",Mv), TS("rcl",Mv), TS("rcr",Mv),
+/* [4] */ TS("shl",Mv), TS("shr",Mv), TS("sal",Mv), TS("sar",Mv),
+};
+
+
+/*
+ * Decode table for 0xD2 opcodes.
+ */
+
+const instable_t dis_opD2[8] = {
+
+/* [0] */ TNS("rolb",Mv), TNS("rorb",Mv), TNS("rclb",Mv), TNS("rcrb",Mv),
+/* [4] */ TNS("shlb",Mv), TNS("shrb",Mv), TNS("salb",Mv), TNS("sarb",Mv),
+};
+/*
+ * Decode table for 0xD3 opcodes.
+ */
+
+const instable_t dis_opD3[8] = {
+
+/* [0] */ TS("rol",Mv), TS("ror",Mv), TS("rcl",Mv), TS("rcr",Mv),
+/* [4] */ TS("shl",Mv), TS("shr",Mv), TS("salb",Mv), TS("sar",Mv),
+};
+
+
+/*
+ * Decode table for 0xF6 opcodes.
+ */
+
+const instable_t dis_opF6[8] = {
+
+/* [0] */ TNS("testb",IMw), TNS("testb",IMw), TNS("notb",Mw), TNS("negb",Mw),
+/* [4] */ TNS("mulb",MA), TNS("imulb",MA), TNS("divb",MA), TNS("idivb",MA),
+};
+
+
+/*
+ * Decode table for 0xF7 opcodes.
+ */
+
+const instable_t dis_opF7[8] = {
+
+/* [0] */ TS("test",IMw), TS("test",IMw), TS("not",Mw), TS("neg",Mw),
+/* [4] */ TS("mul",MA), TS("imul",MA), TS("div",MA), TS("idiv",MA),
+};
+
+
+/*
+ * Decode table for 0xFE opcodes.
+ */
+
+const instable_t dis_opFE[8] = {
+
+/* [0] */ TNS("incb",Mw), TNS("decb",Mw), INVALID, INVALID,
+/* [4] */ INVALID, INVALID, INVALID, INVALID,
+};
+/*
+ * Decode table for 0xFF opcodes.
+ */
+
+const instable_t dis_opFF[8] = {
+
+/* [0] */ TS("inc",Mw), TS("dec",Mw), TNSyp("call",INM), TNS("lcall",INM),
+/* [4] */ TNSy("jmp",INM), TNS("ljmp",INM), TSp("push",M), INVALID,
+};
+
+/* for 287 instructions, which are a mess to decode */
+
+const instable_t dis_opFP1n2[8][8] = {
+{
+/* bit pattern: 1101 1xxx MODxx xR/M */
+/* [0,0] */ TNS("fadds",M), TNS("fmuls",M), TNS("fcoms",M), TNS("fcomps",M),
+/* [0,4] */ TNS("fsubs",M), TNS("fsubrs",M), TNS("fdivs",M), TNS("fdivrs",M),
+}, {
+/* [1,0] */ TNS("flds",M), INVALID, TNS("fsts",M), TNS("fstps",M),
+/* [1,4] */ TNSZ("fldenv",M,28), TNSZ("fldcw",M,2), TNSZ("fnstenv",M,28), TNSZ("fnstcw",M,2),
+}, {
+/* [2,0] */ TNS("fiaddl",M), TNS("fimull",M), TNS("ficoml",M), TNS("ficompl",M),
+/* [2,4] */ TNS("fisubl",M), TNS("fisubrl",M), TNS("fidivl",M), TNS("fidivrl",M),
+}, {
+/* [3,0] */ TNS("fildl",M), INVALID, TNS("fistl",M), TNS("fistpl",M),
+/* [3,4] */ INVALID, TNSZ("fldt",M,10), INVALID, TNSZ("fstpt",M,10),
+}, {
+/* [4,0] */ TNSZ("faddl",M,8), TNSZ("fmull",M,8), TNSZ("fcoml",M,8), TNSZ("fcompl",M,8),
+/* [4,1] */ TNSZ("fsubl",M,8), TNSZ("fsubrl",M,8), TNSZ("fdivl",M,8), TNSZ("fdivrl",M,8),
+}, {
+/* [5,0] */ TNSZ("fldl",M,8), INVALID, TNSZ("fstl",M,8), TNSZ("fstpl",M,8),
+/* [5,4] */ TNSZ("frstor",M,108), INVALID, TNSZ("fnsave",M,108), TNSZ("fnstsw",M,2),
+}, {
+/* [6,0] */ TNSZ("fiadd",M,2), TNSZ("fimul",M,2), TNSZ("ficom",M,2), TNSZ("ficomp",M,2),
+/* [6,4] */ TNSZ("fisub",M,2), TNSZ("fisubr",M,2), TNSZ("fidiv",M,2), TNSZ("fidivr",M,2),
+}, {
+/* [7,0] */ TNSZ("fild",M,2), INVALID, TNSZ("fist",M,2), TNSZ("fistp",M,2),
+/* [7,4] */ TNSZ("fbld",M,10), TNSZ("fildll",M,8), TNSZ("fbstp",M,10), TNSZ("fistpll",M,8),
+} };
+
+const instable_t dis_opFP3[8][8] = {
+{
+/* bit pattern: 1101 1xxx 11xx xREG */
+/* [0,0] */ TNS("fadd",FF), TNS("fmul",FF), TNS("fcom",F), TNS("fcomp",F),
+/* [0,4] */ TNS("fsub",FF), TNS("fsubr",FF), TNS("fdiv",FF), TNS("fdivr",FF),
+}, {
+/* [1,0] */ TNS("fld",F), TNS("fxch",F), TNS("fnop",NORM), TNS("fstp",F),
+/* [1,4] */ INVALID, INVALID, INVALID, INVALID,
+}, {
+/* [2,0] */ INVALID, INVALID, INVALID, INVALID,
+/* [2,4] */ INVALID, TNS("fucompp",NORM), INVALID, INVALID,
+}, {
+/* [3,0] */ INVALID, INVALID, INVALID, INVALID,
+/* [3,4] */ INVALID, INVALID, INVALID, INVALID,
+}, {
+/* [4,0] */ TNS("fadd",FF), TNS("fmul",FF), TNS("fcom",F), TNS("fcomp",F),
+/* [4,4] */ TNS("fsub",FF), TNS("fsubr",FF), TNS("fdiv",FF), TNS("fdivr",FF),
+}, {
+/* [5,0] */ TNS("ffree",F), TNS("fxch",F), TNS("fst",F), TNS("fstp",F),
+/* [5,4] */ TNS("fucom",F), TNS("fucomp",F), INVALID, INVALID,
+}, {
+/* [6,0] */ TNS("faddp",FF), TNS("fmulp",FF), TNS("fcomp",F), TNS("fcompp",NORM),
+/* [6,4] */ TNS("fsubp",FF), TNS("fsubrp",FF), TNS("fdivp",FF), TNS("fdivrp",FF),
+}, {
+/* [7,0] */ TNS("ffree",F), TNS("fxch",F), TNS("fstp",F), TNS("fstp",F),
+/* [7,4] */ TNS("fnstsw",M), TNS("fucomip",FFC), TNS("fcomip",FFC), INVALID,
+} };
+
+const instable_t dis_opFP4[4][8] = {
+{
+/* bit pattern: 1101 1001 111x xxxx */
+/* [0,0] */ TNS("fchs",NORM), TNS("fabs",NORM), INVALID, INVALID,
+/* [0,4] */ TNS("ftst",NORM), TNS("fxam",NORM), TNS("ftstp",NORM), INVALID,
+}, {
+/* [1,0] */ TNS("fld1",NORM), TNS("fldl2t",NORM), TNS("fldl2e",NORM), TNS("fldpi",NORM),
+/* [1,4] */ TNS("fldlg2",NORM), TNS("fldln2",NORM), TNS("fldz",NORM), INVALID,
+}, {
+/* [2,0] */ TNS("f2xm1",NORM), TNS("fyl2x",NORM), TNS("fptan",NORM), TNS("fpatan",NORM),
+/* [2,4] */ TNS("fxtract",NORM), TNS("fprem1",NORM), TNS("fdecstp",NORM), TNS("fincstp",NORM),
+}, {
+/* [3,0] */ TNS("fprem",NORM), TNS("fyl2xp1",NORM), TNS("fsqrt",NORM), TNS("fsincos",NORM),
+/* [3,4] */ TNS("frndint",NORM), TNS("fscale",NORM), TNS("fsin",NORM), TNS("fcos",NORM),
+} };
+
+const instable_t dis_opFP5[8] = {
+/* bit pattern: 1101 1011 111x xxxx */
+/* [0] */ TNS("feni",NORM), TNS("fdisi",NORM), TNS("fnclex",NORM), TNS("fninit",NORM),
+/* [4] */ TNS("fsetpm",NORM), TNS("frstpm",NORM), INVALID, INVALID,
+};
+
+const instable_t dis_opFP6[8] = {
+/* bit pattern: 1101 1011 11yy yxxx */
+/* [00] */ TNS("fcmov.nb",FF), TNS("fcmov.ne",FF), TNS("fcmov.nbe",FF), TNS("fcmov.nu",FF),
+/* [04] */ INVALID, TNS("fucomi",F), TNS("fcomi",F), INVALID,
+};
+
+const instable_t dis_opFP7[8] = {
+/* bit pattern: 1101 1010 11yy yxxx */
+/* [00] */ TNS("fcmov.b",FF), TNS("fcmov.e",FF), TNS("fcmov.be",FF), TNS("fcmov.u",FF),
+/* [04] */ INVALID, INVALID, INVALID, INVALID,
+};
+
+/*
+ * Main decode table for the op codes. The first two nibbles
+ * will be used as an index into the table. If there is a
+ * a need to further decode an instruction, the array to be
+ * referenced is indicated with the other two entries being
+ * empty.
+ */
+
+const instable_t dis_distable[16][16] = {
+{
+/* [0,0] */ TNS("addb",RMw), TS("add",RMw), TNS("addb",MRw), TS("add",MRw),
+/* [0,4] */ TNS("addb",IA), TS("add",IA), TSx("push",SEG), TSx("pop",SEG),
+/* [0,8] */ TNS("orb",RMw), TS("or",RMw), TNS("orb",MRw), TS("or",MRw),
+/* [0,C] */ TNS("orb",IA), TS("or",IA), TSx("push",SEG), IND(&dis_op0F[0][0]),
+}, {
+/* [1,0] */ TNS("adcb",RMw), TS("adc",RMw), TNS("adcb",MRw), TS("adc",MRw),
+/* [1,4] */ TNS("adcb",IA), TS("adc",IA), TSx("push",SEG), TSx("pop",SEG),
+/* [1,8] */ TNS("sbbb",RMw), TS("sbb",RMw), TNS("sbbb",MRw), TS("sbb",MRw),
+/* [1,C] */ TNS("sbbb",IA), TS("sbb",IA), TSx("push",SEG), TSx("pop",SEG),
+}, {
+/* [2,0] */ TNS("andb",RMw), TS("and",RMw), TNS("andb",MRw), TS("and",MRw),
+/* [2,4] */ TNS("andb",IA), TS("and",IA), TNSx("%es:",OVERRIDE), TNSx("daa",NORM),
+/* [2,8] */ TNS("subb",RMw), TS("sub",RMw), TNS("subb",MRw), TS("sub",MRw),
+/* [2,C] */ TNS("subb",IA), TS("sub",IA), TNSx("%cs:",OVERRIDE), TNSx("das",NORM),
+}, {
+/* [3,0] */ TNS("xorb",RMw), TS("xor",RMw), TNS("xorb",MRw), TS("xor",MRw),
+/* [3,4] */ TNS("xorb",IA), TS("xor",IA), TNSx("%ss:",OVERRIDE), TNSx("aaa",NORM),
+/* [3,8] */ TNS("cmpb",RMw), TS("cmp",RMw), TNS("cmpb",MRw), TS("cmp",MRw),
+/* [3,C] */ TNS("cmpb",IA), TS("cmp",IA), TNSx("%ds:",OVERRIDE), TNSx("aas",NORM),
+}, {
+/* [4,0] */ TSx("inc",R), TSx("inc",R), TSx("inc",R), TSx("inc",R),
+/* [4,4] */ TSx("inc",R), TSx("inc",R), TSx("inc",R), TSx("inc",R),
+/* [4,8] */ TSx("dec",R), TSx("dec",R), TSx("dec",R), TSx("dec",R),
+/* [4,C] */ TSx("dec",R), TSx("dec",R), TSx("dec",R), TSx("dec",R),
+}, {
+/* [5,0] */ TSp("push",R), TSp("push",R), TSp("push",R), TSp("push",R),
+/* [5,4] */ TSp("push",R), TSp("push",R), TSp("push",R), TSp("push",R),
+/* [5,8] */ TSp("pop",R), TSp("pop",R), TSp("pop",R), TSp("pop",R),
+/* [5,C] */ TSp("pop",R), TSp("pop",R), TSp("pop",R), TSp("pop",R),
+}, {
+/* [6,0] */ TSZx("pusha",IMPLMEM,28),TSZx("popa",IMPLMEM,28), TSx("bound",MR), TNS("arpl",RMw),
+/* [6,4] */ TNS("%fs:",OVERRIDE), TNS("%gs:",OVERRIDE), TNS("data16",DM), TNS("addr16",AM),
+/* [6,8] */ TSp("push",I), TS("imul",IMUL), TSp("push",Ib), TS("imul",IMUL),
+/* [6,C] */ TNSZ("insb",IMPLMEM,1), TSZ("ins",IMPLMEM,4), TNSZ("outsb",IMPLMEM,1),TSZ("outs",IMPLMEM,4),
+}, {
+/* [7,0] */ TNSy("jo",BD), TNSy("jno",BD), TNSy("jb",BD), TNSy("jae",BD),
+/* [7,4] */ TNSy("je",BD), TNSy("jne",BD), TNSy("jbe",BD), TNSy("ja",BD),
+/* [7,8] */ TNSy("js",BD), TNSy("jns",BD), TNSy("jp",BD), TNSy("jnp",BD),
+/* [7,C] */ TNSy("jl",BD), TNSy("jge",BD), TNSy("jle",BD), TNSy("jg",BD),
+}, {
+/* [8,0] */ IND(dis_op80), IND(dis_op81), INDx(dis_op82), IND(dis_op83),
+/* [8,4] */ TNS("testb",RMw), TS("test",RMw), TNS("xchgb",RMw), TS("xchg",RMw),
+/* [8,8] */ TNS("movb",RMw), TS("mov",RMw), TNS("movb",MRw), TS("mov",MRw),
+/* [8,C] */ TNS("movw",SM), TS("lea",MR), TNS("movw",MS), TSp("pop",M),
+}, {
+/* [9,0] */ TNS("nop",NORM), TS("xchg",RA), TS("xchg",RA), TS("xchg",RA),
+/* [9,4] */ TS("xchg",RA), TS("xchg",RA), TS("xchg",RA), TS("xchg",RA),
+/* [9,8] */ TNS("cXtX",CBW), TNS("cXtX",CWD), TNSx("lcall",SO), TNS("fwait",NORM),
+/* [9,C] */ TSZy("pushf",IMPLMEM,4),TSZy("popf",IMPLMEM,4), TNSx("sahf",NORM), TNSx("lahf",NORM),
+}, {
+/* [A,0] */ TNS("movb",OA), TS("mov",OA), TNS("movb",AO), TS("mov",AO),
+/* [A,4] */ TNSZ("movsb",SD,1), TS("movs",SD), TNSZ("cmpsb",SD,1), TS("cmps",SD),
+/* [A,8] */ TNS("testb",IA), TS("test",IA), TNS("stosb",AD), TS("stos",AD),
+/* [A,C] */ TNS("lodsb",SA), TS("lods",SA), TNS("scasb",AD), TS("scas",AD),
+}, {
+/* [B,0] */ TNS("movb",IR), TNS("movb",IR), TNS("movb",IR), TNS("movb",IR),
+/* [B,4] */ TNS("movb",IR), TNS("movb",IR), TNS("movb",IR), TNS("movb",IR),
+/* [B,8] */ TS("mov",IR), TS("mov",IR), TS("mov",IR), TS("mov",IR),
+/* [B,C] */ TS("mov",IR), TS("mov",IR), TS("mov",IR), TS("mov",IR),
+}, {
+/* [C,0] */ IND(dis_opC0), IND(dis_opC1), TNSyp("ret",RET), TNSyp("ret",NORM),
+/* [C,4] */ TNSx("les",MR), TNSx("lds",MR), TNS("movb",IMw), TS("mov",IMw),
+/* [C,8] */ TNSyp("enter",ENTER), TNSyp("leave",NORM), TNS("lret",RET), TNS("lret",NORM),
+/* [C,C] */ TNS("int",INT3), TNS("int",INTx), TNSx("into",NORM), TNS("iret",NORM),
+}, {
+/* [D,0] */ IND(dis_opD0), IND(dis_opD1), IND(dis_opD2), IND(dis_opD3),
+/* [D,4] */ TNSx("aam",U), TNSx("aad",U), TNSx("falc",NORM), TNSZ("xlat",IMPLMEM,1),
+
+/* 287 instructions. Note that although the indirect field */
+/* indicates opFP1n2 for further decoding, this is not necessarily */
+/* the case since the opFP arrays are not partitioned according to key1 */
+/* and key2. opFP1n2 is given only to indicate that we haven't */
+/* finished decoding the instruction. */
+/* [D,8] */ IND(&dis_opFP1n2[0][0]), IND(&dis_opFP1n2[0][0]), IND(&dis_opFP1n2[0][0]), IND(&dis_opFP1n2[0][0]),
+/* [D,C] */ IND(&dis_opFP1n2[0][0]), IND(&dis_opFP1n2[0][0]), IND(&dis_opFP1n2[0][0]), IND(&dis_opFP1n2[0][0]),
+}, {
+/* [E,0] */ TNSy("loopnz",BD), TNSy("loopz",BD), TNSy("loop",BD), TNSy("jcxz",BD),
+/* [E,4] */ TNS("inb",P), TS("in",P), TNS("outb",P), TS("out",P),
+/* [E,8] */ TNSyp("call",D), TNSy("jmp",D), TNSx("ljmp",SO), TNSy("jmp",BD),
+/* [E,C] */ TNS("inb",V), TS("in",V), TNS("outb",V), TS("out",V),
+}, {
+/* [F,0] */ TNS("lock",LOCK), TNS("icebp", NORM), TNS("repnz",PREFIX), TNS("repz",PREFIX),
+/* [F,4] */ TNS("hlt",NORM), TNS("cmc",NORM), IND(dis_opF6), IND(dis_opF7),
+/* [F,8] */ TNS("clc",NORM), TNS("stc",NORM), TNS("cli",NORM), TNS("sti",NORM),
+/* [F,C] */ TNS("cld",NORM), TNS("std",NORM), IND(dis_opFE), IND(dis_opFF),
+} };
+
+/* END CSTYLED */
+
+/*
+ * common functions to decode and disassemble an x86 or amd64 instruction
+ */
+
+/*
+ * These are the individual fields of a REX prefix. Note that a REX
+ * prefix with none of these set is still needed to:
+ * - use the MOVSXD (sign extend 32 to 64 bits) instruction
+ * - access the %sil, %dil, %bpl, %spl registers
+ */
+#define REX_W 0x08 /* 64 bit operand size when set */
+#define REX_R 0x04 /* high order bit extension of ModRM reg field */
+#define REX_X 0x02 /* high order bit extension of SIB index field */
+#define REX_B 0x01 /* extends ModRM r_m, SIB base, or opcode reg */
+
+static uint_t opnd_size; /* SIZE16, SIZE32 or SIZE64 */
+static uint_t addr_size; /* SIZE16, SIZE32 or SIZE64 */
+
+/*
+ * Even in 64 bit mode, usually only 4 byte immediate operands are supported.
+ */
+static int isize[] = {1, 2, 4, 4};
+static int isize64[] = {1, 2, 4, 8};
+
+/*
+ * Just a bunch of useful macros.
+ */
+#define WBIT(x) (x & 0x1) /* to get w bit */
+#define REGNO(x) (x & 0x7) /* to get 3 bit register */
+#define VBIT(x) ((x)>>1 & 0x1) /* to get 'v' bit */
+#define OPSIZE(osize, wbit) ((wbit) ? isize[osize] : 1)
+#define OPSIZE64(osize, wbit) ((wbit) ? isize64[osize] : 1)
+
+#define REG_ONLY 3 /* mode to indicate a register operand (not memory) */
+
+#define BYTE_OPND 0 /* w-bit value indicating byte register */
+#define LONG_OPND 1 /* w-bit value indicating opnd_size register */
+#define MM_OPND 2 /* "value" used to indicate a mmx reg */
+#define XMM_OPND 3 /* "value" used to indicate a xmm reg */
+#define SEG_OPND 4 /* "value" used to indicate a segment reg */
+#define CONTROL_OPND 5 /* "value" used to indicate a control reg */
+#define DEBUG_OPND 6 /* "value" used to indicate a debug reg */
+#define TEST_OPND 7 /* "value" used to indicate a test reg */
+#define WORD_OPND 8 /* w-bit value indicating word size reg */
+
+/*
+ * Get the next byte and separate the op code into the high and low nibbles.
+ */
+static int
+dtrace_get_opcode(dis86_t *x, uint_t *high, uint_t *low)
+{
+ int byte;
+
+ /*
+ * x86 instructions have a maximum length of 15 bytes. Bail out if
+ * we try to read more.
+ */
+ if (x->d86_len >= 15)
+ return (x->d86_error = 1);
+
+ if (x->d86_error)
+ return (1);
+ byte = x->d86_get_byte(x->d86_data);
+ if (byte < 0)
+ return (x->d86_error = 1);
+ x->d86_bytes[x->d86_len++] = byte;
+ *low = byte & 0xf; /* ----xxxx low 4 bits */
+ *high = byte >> 4 & 0xf; /* xxxx---- bits 7 to 4 */
+ return (0);
+}
+
+/*
+ * Get and decode an SIB (scaled index base) byte
+ */
+static void
+dtrace_get_SIB(dis86_t *x, uint_t *ss, uint_t *index, uint_t *base)
+{
+ int byte;
+
+ if (x->d86_error)
+ return;
+
+ byte = x->d86_get_byte(x->d86_data);
+ if (byte < 0) {
+ x->d86_error = 1;
+ return;
+ }
+ x->d86_bytes[x->d86_len++] = byte;
+
+ *base = byte & 0x7;
+ *index = (byte >> 3) & 0x7;
+ *ss = (byte >> 6) & 0x3;
+}
+
+/*
+ * Get the byte following the op code and separate it into the
+ * mode, register, and r/m fields.
+ */
+static void
+dtrace_get_modrm(dis86_t *x, uint_t *mode, uint_t *reg, uint_t *r_m)
+{
+ if (x->d86_got_modrm == 0) {
+ if (x->d86_rmindex == -1)
+ x->d86_rmindex = x->d86_len;
+ dtrace_get_SIB(x, mode, reg, r_m);
+ x->d86_got_modrm = 1;
+ }
+}
+
+/*
+ * Adjust register selection based on any REX prefix bits present.
+ */
+/*ARGSUSED*/
+static void
+dtrace_rex_adjust(uint_t rex_prefix, uint_t mode, uint_t *reg, uint_t *r_m)
+{
+ if (reg != NULL && r_m == NULL) {
+ if (rex_prefix & REX_B)
+ *reg += 8;
+ } else {
+ if (reg != NULL && (REX_R & rex_prefix) != 0)
+ *reg += 8;
+ if (r_m != NULL && (REX_B & rex_prefix) != 0)
+ *r_m += 8;
+ }
+}
+
+/*
+ * Get an immediate operand of the given size, with sign extension.
+ */
+static void
+dtrace_imm_opnd(dis86_t *x, int wbit, int size, int opindex)
+{
+ int i;
+ int byte;
+ int valsize = 0;
+
+ if (x->d86_numopnds < opindex + 1)
+ x->d86_numopnds = opindex + 1;
+
+ switch (wbit) {
+ case BYTE_OPND:
+ valsize = 1;
+ break;
+ case LONG_OPND:
+ if (x->d86_opnd_size == SIZE16)
+ valsize = 2;
+ else if (x->d86_opnd_size == SIZE32)
+ valsize = 4;
+ else
+ valsize = 8;
+ break;
+ case MM_OPND:
+ case XMM_OPND:
+ case SEG_OPND:
+ case CONTROL_OPND:
+ case DEBUG_OPND:
+ case TEST_OPND:
+ valsize = size;
+ break;
+ case WORD_OPND:
+ valsize = 2;
+ break;
+ }
+ if (valsize < size)
+ valsize = size;
+
+ if (x->d86_error)
+ return;
+ x->d86_opnd[opindex].d86_value = 0;
+ for (i = 0; i < size; ++i) {
+ byte = x->d86_get_byte(x->d86_data);
+ if (byte < 0) {
+ x->d86_error = 1;
+ return;
+ }
+ x->d86_bytes[x->d86_len++] = byte;
+ x->d86_opnd[opindex].d86_value |= (uint64_t)byte << (i * 8);
+ }
+ /* Do sign extension */
+ if (x->d86_bytes[x->d86_len - 1] & 0x80) {
+ for (; i < valsize; i++)
+ x->d86_opnd[opindex].d86_value |=
+ (uint64_t)0xff << (i* 8);
+ }
+#ifdef DIS_TEXT
+ x->d86_opnd[opindex].d86_mode = MODE_SIGNED;
+ x->d86_opnd[opindex].d86_value_size = valsize;
+ x->d86_imm_bytes += size;
+#endif
+}
+
+/*
+ * Get an ip relative operand of the given size, with sign extension.
+ */
+static void
+dtrace_disp_opnd(dis86_t *x, int wbit, int size, int opindex)
+{
+ dtrace_imm_opnd(x, wbit, size, opindex);
+#ifdef DIS_TEXT
+ x->d86_opnd[opindex].d86_mode = MODE_IPREL;
+#endif
+}
+
+/*
+ * Check to see if there is a segment override prefix pending.
+ * If so, print it in the current 'operand' location and set
+ * the override flag back to false.
+ */
+/*ARGSUSED*/
+static void
+dtrace_check_override(dis86_t *x, int opindex)
+{
+#ifdef DIS_TEXT
+ if (x->d86_seg_prefix) {
+ (void) strlcat(x->d86_opnd[opindex].d86_prefix,
+ x->d86_seg_prefix, PFIXLEN);
+ }
+#endif
+ x->d86_seg_prefix = NULL;
+}
+
+
+/*
+ * Process a single instruction Register or Memory operand.
+ *
+ * mode = addressing mode from ModRM byte
+ * r_m = r_m (or reg if mode == 3) field from ModRM byte
+ * wbit = indicates which register (8bit, 16bit, ... MMX, etc.) set to use.
+ * o = index of operand that we are processing (0, 1 or 2)
+ *
+ * the value of reg or r_m must have already been adjusted for any REX prefix.
+ */
+/*ARGSUSED*/
+static void
+dtrace_get_operand(dis86_t *x, uint_t mode, uint_t r_m, int wbit, int opindex)
+{
+ int have_SIB = 0; /* flag presence of scale-index-byte */
+ uint_t ss; /* scale-factor from opcode */
+ uint_t index; /* index register number */
+ uint_t base; /* base register number */
+ int dispsize; /* size of displacement in bytes */
+#ifdef DIS_TEXT
+ char *opnd = x->d86_opnd[opindex].d86_opnd;
+#endif
+
+ if (x->d86_numopnds < opindex + 1)
+ x->d86_numopnds = opindex + 1;
+
+ if (x->d86_error)
+ return;
+
+ /*
+ * first handle a simple register
+ */
+ if (mode == REG_ONLY) {
+#ifdef DIS_TEXT
+ switch (wbit) {
+ case MM_OPND:
+ (void) strlcat(opnd, dis_MMREG[r_m], OPLEN);
+ break;
+ case XMM_OPND:
+ (void) strlcat(opnd, dis_XMMREG[r_m], OPLEN);
+ break;
+ case SEG_OPND:
+ (void) strlcat(opnd, dis_SEGREG[r_m], OPLEN);
+ break;
+ case CONTROL_OPND:
+ (void) strlcat(opnd, dis_CONTROLREG[r_m], OPLEN);
+ break;
+ case DEBUG_OPND:
+ (void) strlcat(opnd, dis_DEBUGREG[r_m], OPLEN);
+ break;
+ case TEST_OPND:
+ (void) strlcat(opnd, dis_TESTREG[r_m], OPLEN);
+ break;
+ case BYTE_OPND:
+ if (x->d86_rex_prefix == 0)
+ (void) strlcat(opnd, dis_REG8[r_m], OPLEN);
+ else
+ (void) strlcat(opnd, dis_REG8_REX[r_m], OPLEN);
+ break;
+ case WORD_OPND:
+ (void) strlcat(opnd, dis_REG16[r_m], OPLEN);
+ break;
+ case LONG_OPND:
+ if (x->d86_opnd_size == SIZE16)
+ (void) strlcat(opnd, dis_REG16[r_m], OPLEN);
+ else if (x->d86_opnd_size == SIZE32)
+ (void) strlcat(opnd, dis_REG32[r_m], OPLEN);
+ else
+ (void) strlcat(opnd, dis_REG64[r_m], OPLEN);
+ break;
+ }
+#endif /* DIS_TEXT */
+ return;
+ }
+
+ /*
+ * if symbolic representation, skip override prefix, if any
+ */
+ dtrace_check_override(x, opindex);
+
+ /*
+ * Handle 16 bit memory references first, since they decode
+ * the mode values more simply.
+ * mode 1 is r_m + 8 bit displacement
+ * mode 2 is r_m + 16 bit displacement
+ * mode 0 is just r_m, unless r_m is 6 which is 16 bit disp
+ */
+ if (x->d86_addr_size == SIZE16) {
+ if ((mode == 0 && r_m == 6) || mode == 2)
+ dtrace_imm_opnd(x, WORD_OPND, 2, opindex);
+ else if (mode == 1)
+ dtrace_imm_opnd(x, BYTE_OPND, 1, opindex);
+#ifdef DIS_TEXT
+ if (mode == 0 && r_m == 6)
+ x->d86_opnd[opindex].d86_mode = MODE_SIGNED;
+ else if (mode == 0)
+ x->d86_opnd[opindex].d86_mode = MODE_NONE;
+ else
+ x->d86_opnd[opindex].d86_mode = MODE_OFFSET;
+ (void) strlcat(opnd, dis_addr16[mode][r_m], OPLEN);
+#endif
+ return;
+ }
+
+ /*
+ * 32 and 64 bit addressing modes are more complex since they
+ * can involve an SIB (scaled index and base) byte to decode.
+ */
+ if (r_m == ESP_REGNO || r_m == ESP_REGNO + 8) {
+ have_SIB = 1;
+ dtrace_get_SIB(x, &ss, &index, &base);
+ if (x->d86_error)
+ return;
+ if (base != 5 || mode != 0)
+ if (x->d86_rex_prefix & REX_B)
+ base += 8;
+ if (x->d86_rex_prefix & REX_X)
+ index += 8;
+ } else {
+ base = r_m;
+ }
+
+ /*
+ * Compute the displacement size and get its bytes
+ */
+ dispsize = 0;
+
+ if (mode == 1)
+ dispsize = 1;
+ else if (mode == 2)
+ dispsize = 4;
+ else if ((r_m & 7) == EBP_REGNO ||
+ (have_SIB && (base & 7) == EBP_REGNO))
+ dispsize = 4;
+
+ if (dispsize > 0) {
+ dtrace_imm_opnd(x, dispsize == 4 ? LONG_OPND : BYTE_OPND,
+ dispsize, opindex);
+ if (x->d86_error)
+ return;
+ }
+
+#ifdef DIS_TEXT
+ if (dispsize > 0)
+ x->d86_opnd[opindex].d86_mode = MODE_OFFSET;
+
+ if (have_SIB == 0) {
+ if (x->d86_mode == SIZE32) {
+ if (mode == 0)
+ (void) strlcat(opnd, dis_addr32_mode0[r_m],
+ OPLEN);
+ else
+ (void) strlcat(opnd, dis_addr32_mode12[r_m],
+ OPLEN);
+ } else {
+ if (mode == 0)
+ (void) strlcat(opnd, dis_addr64_mode0[r_m],
+ OPLEN);
+ else
+ (void) strlcat(opnd, dis_addr64_mode12[r_m],
+ OPLEN);
+ }
+ } else {
+ uint_t need_paren = 0;
+ char **regs;
+ if (x->d86_mode == SIZE32) /* NOTE this is not addr_size! */
+ regs = (char **)dis_REG32;
+ else
+ regs = (char **)dis_REG64;
+
+ /*
+ * print the base (if any)
+ */
+ if (base == EBP_REGNO && mode == 0) {
+ if (index != ESP_REGNO) {
+ (void) strlcat(opnd, "(", OPLEN);
+ need_paren = 1;
+ }
+ } else {
+ (void) strlcat(opnd, "(", OPLEN);
+ (void) strlcat(opnd, regs[base], OPLEN);
+ need_paren = 1;
+ }
+
+ /*
+ * print the index (if any)
+ */
+ if (index != ESP_REGNO) {
+ (void) strlcat(opnd, ",", OPLEN);
+ (void) strlcat(opnd, regs[index], OPLEN);
+ (void) strlcat(opnd, dis_scale_factor[ss], OPLEN);
+ } else
+ if (need_paren)
+ (void) strlcat(opnd, ")", OPLEN);
+ }
+#endif
+}
+
+/*
+ * Operand sequence for standard instruction involving one register
+ * and one register/memory operand.
+ * wbit indicates a byte(0) or opnd_size(1) operation
+ * vbit indicates direction (0 for "opcode r,r_m") or (1 for "opcode r_m, r")
+ */
+#define STANDARD_MODRM(x, mode, reg, r_m, rex_prefix, wbit, vbit) { \
+ dtrace_get_modrm(x, &mode, &reg, &r_m); \
+ dtrace_rex_adjust(rex_prefix, mode, &reg, &r_m); \
+ dtrace_get_operand(x, mode, r_m, wbit, vbit); \
+ dtrace_get_operand(x, REG_ONLY, reg, wbit, 1 - vbit); \
+}
+
+/*
+ * Similar to above, but allows for the two operands to be of different
+ * classes (ie. wbit).
+ * wbit is for the r_m operand
+ * w2 is for the reg operand
+ */
+#define MIXED_MM(x, mode, reg, r_m, rex_prefix, wbit, w2, vbit) { \
+ dtrace_get_modrm(x, &mode, &reg, &r_m); \
+ dtrace_rex_adjust(rex_prefix, mode, &reg, &r_m); \
+ dtrace_get_operand(x, mode, r_m, wbit, vbit); \
+ dtrace_get_operand(x, REG_ONLY, reg, w2, 1 - vbit); \
+}
+
+/*
+ * Similar, but for 2 operands plus an immediate.
+ */
+#define THREEOPERAND(x, mode, reg, r_m, rex_prefix, wbit, w2, immsize) { \
+ dtrace_get_modrm(x, &mode, &reg, &r_m); \
+ dtrace_rex_adjust(rex_prefix, mode, &reg, &r_m); \
+ dtrace_get_operand(x, mode, r_m, wbit, 1); \
+ dtrace_get_operand(x, REG_ONLY, reg, w2, 2); \
+ dtrace_imm_opnd(x, wbit, immsize, 0); \
+}
+
+/*
+ * Dissassemble a single x86 or amd64 instruction.
+ *
+ * Mode determines the default operating mode (SIZE16, SIZE32 or SIZE64)
+ * for interpreting instructions.
+ *
+ * returns non-zero for bad opcode
+ */
+int
+dtrace_disx86(dis86_t *x, uint_t cpu_mode)
+{
+ const instable_t *dp = NULL; /* decode table being used */
+#ifdef DIS_TEXT
+ uint_t i;
+#endif
+#ifdef DIS_MEM
+ uint_t nomem = 0;
+#define NOMEM (nomem = 1)
+#else
+#define NOMEM /* nothing */
+#endif
+ uint_t wbit = 0; /* opcode wbit, 0 is 8 bit, !0 for opnd_size */
+ uint_t w2; /* wbit value for second operand */
+ uint_t vbit;
+ uint_t mode = 0; /* mode value from ModRM byte */
+ uint_t reg; /* reg value from ModRM byte */
+ uint_t r_m; /* r_m value from ModRM byte */
+
+ uint_t opcode1; /* high nibble of 1st byte */
+ uint_t opcode2; /* low nibble of 1st byte */
+ uint_t opcode3; /* extra opcode bits usually from ModRM byte */
+ uint_t opcode4; /* high nibble of 2nd byte */
+ uint_t opcode5; /* low nibble of 2ne byte */
+ uint_t opcode6; /* high nibble of 3rd byte */
+ uint_t opcode7; /* low nibble of 3rd byte */
+ uint_t opcode_bytes = 1;
+
+ /*
+ * legacy prefixes come in 5 flavors, you should have only one of each
+ */
+ uint_t opnd_size_prefix = 0;
+ uint_t addr_size_prefix = 0;
+ uint_t segment_prefix = 0;
+ uint_t lock_prefix = 0;
+ uint_t rep_prefix = 0;
+ uint_t rex_prefix = 0; /* amd64 register extension prefix */
+ size_t off;
+
+ x->d86_len = 0;
+ x->d86_rmindex = -1;
+ x->d86_error = 0;
+#ifdef DIS_TEXT
+ x->d86_numopnds = 0;
+ x->d86_seg_prefix = NULL;
+ x->d86_mneu[0] = 0;
+ for (i = 0; i < 3; ++i) {
+ x->d86_opnd[i].d86_opnd[0] = 0;
+ x->d86_opnd[i].d86_prefix[0] = 0;
+ x->d86_opnd[i].d86_value_size = 0;
+ x->d86_opnd[i].d86_value = 0;
+ x->d86_opnd[i].d86_mode = MODE_NONE;
+ }
+#endif
+ x->d86_error = 0;
+ x->d86_memsize = 0;
+
+ if (cpu_mode == SIZE16) {
+ opnd_size = SIZE16;
+ addr_size = SIZE16;
+ } else if (cpu_mode == SIZE32) {
+ opnd_size = SIZE32;
+ addr_size = SIZE32;
+ } else {
+ opnd_size = SIZE32;
+ addr_size = SIZE64;
+ }
+
+ /*
+ * Get one opcode byte and check for zero padding that follows
+ * jump tables.
+ */
+ if (dtrace_get_opcode(x, &opcode1, &opcode2) != 0)
+ goto error;
+
+ if (opcode1 == 0 && opcode2 == 0 &&
+ x->d86_check_func != NULL && x->d86_check_func(x->d86_data)) {
+#ifdef DIS_TEXT
+ (void) strncpy(x->d86_mneu, ".byte\t0", OPLEN);
+#endif
+ goto done;
+ }
+
+ /*
+ * Gather up legacy x86 prefix bytes.
+ */
+ for (;;) {
+ uint_t *which_prefix = NULL;
+
+ dp = &dis_distable[opcode1][opcode2];
+
+ switch (dp->it_adrmode) {
+ case PREFIX:
+ which_prefix = &rep_prefix;
+ break;
+ case LOCK:
+ which_prefix = &lock_prefix;
+ break;
+ case OVERRIDE:
+ which_prefix = &segment_prefix;
+#ifdef DIS_TEXT
+ x->d86_seg_prefix = (char *)dp->it_name;
+#endif
+ if (dp->it_invalid64 && cpu_mode == SIZE64)
+ goto error;
+ break;
+ case AM:
+ which_prefix = &addr_size_prefix;
+ break;
+ case DM:
+ which_prefix = &opnd_size_prefix;
+ break;
+ }
+ if (which_prefix == NULL)
+ break;
+ *which_prefix = (opcode1 << 4) | opcode2;
+ if (dtrace_get_opcode(x, &opcode1, &opcode2) != 0)
+ goto error;
+ }
+
+ /*
+ * Handle amd64 mode PREFIX values.
+ * Some of the segment prefixes are no-ops. (only FS/GS actually work)
+ * We might have a REX prefix (opcodes 0x40-0x4f)
+ */
+ if (cpu_mode == SIZE64) {
+ if (segment_prefix != 0x64 && segment_prefix != 0x65)
+ segment_prefix = 0;
+
+ if (opcode1 == 0x4) {
+ rex_prefix = (opcode1 << 4) | opcode2;
+ if (dtrace_get_opcode(x, &opcode1, &opcode2) != 0)
+ goto error;
+ dp = &dis_distable[opcode1][opcode2];
+ }
+ }
+
+ /*
+ * Deal with selection of operand and address size now.
+ * Note that the REX.W bit being set causes opnd_size_prefix to be
+ * ignored.
+ */
+ if (cpu_mode == SIZE64) {
+ if (rex_prefix & 0x08)
+ opnd_size = SIZE64;
+ else if (opnd_size_prefix)
+ opnd_size = SIZE16;
+
+ if (addr_size_prefix)
+ addr_size = SIZE32;
+ } else if (cpu_mode == SIZE32) {
+ if (opnd_size_prefix)
+ opnd_size = SIZE16;
+ if (addr_size_prefix)
+ addr_size = SIZE16;
+ } else {
+ if (opnd_size_prefix)
+ opnd_size = SIZE32;
+ if (addr_size_prefix)
+ addr_size = SIZE32;
+ }
+
+ /*
+ * The pause instruction - a repz'd nop. This doesn't fit
+ * with any of the other prefix goop added for SSE, so we'll
+ * special-case it here.
+ */
+ if (rep_prefix == 0xf3 && opcode1 == 0x9 && opcode2 == 0x0) {
+ rep_prefix = 0;
+ dp = &dis_opPause;
+ }
+
+ /*
+ * Some 386 instructions have 2 bytes of opcode before the mod_r/m
+ * byte so we may need to perform a table indirection.
+ */
+ if (dp->it_indirect == dis_op0F[0]) {
+ if (dtrace_get_opcode(x, &opcode4, &opcode5) != 0)
+ goto error;
+ opcode_bytes = 2;
+ if (opcode4 == 0x7 && opcode5 >= 0x1 && opcode5 <= 0x3) {
+ uint_t subcode;
+
+ if (dtrace_get_opcode(x, &opcode6, &opcode7) != 0)
+ goto error;
+ opcode_bytes = 3;
+ subcode = ((opcode6 & 0x3) << 1) |
+ ((opcode7 & 0x8) >> 3);
+ dp = &dis_op0F7123[opcode5][subcode];
+ } else if ((opcode4 == 0xc) && (opcode5 >= 0x8)) {
+ dp = &dis_op0FC8[0];
+ } else {
+ dp = &dis_op0F[opcode4][opcode5];
+ }
+ }
+
+ /*
+ * If still not at a TERM decode entry, then a ModRM byte
+ * exists and its fields further decode the instruction.
+ */
+ x->d86_got_modrm = 0;
+ if (dp->it_indirect != TERM) {
+ dtrace_get_modrm(x, &mode, &opcode3, &r_m);
+ if (x->d86_error)
+ goto error;
+ reg = opcode3;
+
+ /*
+ * decode 287 instructions (D8-DF) from opcodeN
+ */
+ if (opcode1 == 0xD && opcode2 >= 0x8) {
+ if (opcode2 == 0xB && mode == 0x3 && opcode3 == 4)
+ dp = &dis_opFP5[r_m];
+ else if (opcode2 == 0xA && mode == 0x3 && opcode3 < 4)
+ dp = &dis_opFP7[opcode3];
+ else if (opcode2 == 0xB && mode == 0x3)
+ dp = &dis_opFP6[opcode3];
+ else if (opcode2 == 0x9 && mode == 0x3 && opcode3 >= 4)
+ dp = &dis_opFP4[opcode3 - 4][r_m];
+ else if (mode == 0x3)
+ dp = &dis_opFP3[opcode2 - 8][opcode3];
+ else
+ dp = &dis_opFP1n2[opcode2 - 8][opcode3];
+ } else {
+ dp = dp->it_indirect + opcode3;
+ }
+ }
+
+ /*
+ * In amd64 bit mode, ARPL opcode is changed to MOVSXD
+ * (sign extend 32bit to 64 bit)
+ */
+ if (cpu_mode == SIZE64 && opcode1 == 0x6 && opcode2 == 0x3)
+ dp = &dis_opMOVSLD;
+
+ /*
+ * at this point we should have a correct (or invalid) opcode
+ */
+ if ((cpu_mode == SIZE64 && dp->it_invalid64) ||
+ (cpu_mode != SIZE64 && dp->it_invalid32))
+ goto error;
+ if (dp->it_indirect != TERM)
+ goto error;
+
+ /*
+ * deal with MMX/SSE opcodes which are changed by prefixes
+ */
+ switch (dp->it_adrmode) {
+ case MMO:
+ case MMOIMPL:
+ case MMO3P:
+ case MMOM3:
+ case MMOMS:
+ case MMOPM:
+ case MMOPRM:
+ case MMOS:
+ case XMMO:
+ case XMMOM:
+ case XMMOMS:
+ case XMMOPM:
+ case XMMOS:
+ case XMMOMX:
+ case XMMOX3:
+ case XMMOXMM:
+ /*
+ * This is horrible. Some SIMD instructions take the
+ * form 0x0F 0x?? ..., which is easily decoded using the
+ * existing tables. Other SIMD instructions use various
+ * prefix bytes to overload existing instructions. For
+ * Example, addps is F0, 58, whereas addss is F3 (repz),
+ * F0, 58. Presumably someone got a raise for this.
+ *
+ * If we see one of the instructions which can be
+ * modified in this way (if we've got one of the SIMDO*
+ * address modes), we'll check to see if the last prefix
+ * was a repz. If it was, we strip the prefix from the
+ * mnemonic, and we indirect using the dis_opSIMDrepz
+ * table.
+ */
+
+ /*
+ * Calculate our offset in dis_op0F
+ */
+ if ((uintptr_t)dp - (uintptr_t)dis_op0F > sizeof (dis_op0F))
+ goto error;
+
+ off = ((uintptr_t)dp - (uintptr_t)dis_op0F) /
+ sizeof (instable_t);
+
+ /*
+ * Rewrite if this instruction used one of the magic prefixes.
+ */
+ if (rep_prefix) {
+ if (rep_prefix == 0xf2)
+ dp = &dis_opSIMDrepnz[off];
+ else
+ dp = &dis_opSIMDrepz[off];
+ rep_prefix = 0;
+ } else if (opnd_size_prefix) {
+ dp = &dis_opSIMDdata16[off];
+ opnd_size_prefix = 0;
+ if (opnd_size == SIZE16)
+ opnd_size = SIZE32;
+ }
+ break;
+
+ case MMOSH:
+ /*
+ * As with the "normal" SIMD instructions, the MMX
+ * shuffle instructions are overloaded. These
+ * instructions, however, are special in that they use
+ * an extra byte, and thus an extra table. As of this
+ * writing, they only use the opnd_size prefix.
+ */
+
+ /*
+ * Calculate our offset in dis_op0F7123
+ */
+ if ((uintptr_t)dp - (uintptr_t)dis_op0F7123 >
+ sizeof (dis_op0F7123))
+ goto error;
+
+ if (opnd_size_prefix) {
+ off = ((uintptr_t)dp - (uintptr_t)dis_op0F7123) /
+ sizeof (instable_t);
+ dp = &dis_opSIMD7123[off];
+ opnd_size_prefix = 0;
+ if (opnd_size == SIZE16)
+ opnd_size = SIZE32;
+ }
+ break;
+ }
+
+ /*
+ * In 64 bit mode, some opcodes automatically use opnd_size == SIZE64.
+ */
+ if (cpu_mode == SIZE64)
+ if (dp->it_always64 || (opnd_size == SIZE32 && dp->it_stackop))
+ opnd_size = SIZE64;
+
+#ifdef DIS_TEXT
+ /*
+ * At this point most instructions can format the opcode mnemonic
+ * including the prefixes.
+ */
+ if (lock_prefix)
+ (void) strlcat(x->d86_mneu, "lock ", OPLEN);
+
+ if (rep_prefix == 0xf2)
+ (void) strlcat(x->d86_mneu, "repnz ", OPLEN);
+ else if (rep_prefix == 0xf3)
+ (void) strlcat(x->d86_mneu, "repz ", OPLEN);
+
+ if (cpu_mode == SIZE64 && addr_size_prefix)
+ (void) strlcat(x->d86_mneu, "addr32 ", OPLEN);
+
+ if (dp->it_adrmode != CBW &&
+ dp->it_adrmode != CWD &&
+ dp->it_adrmode != XMMSFNC) {
+ if (strcmp(dp->it_name, "INVALID") == 0)
+ goto error;
+ (void) strlcat(x->d86_mneu, dp->it_name, OPLEN);
+ if (dp->it_suffix) {
+ char *types[] = {"", "w", "l", "q"};
+ if (opcode_bytes == 2 && opcode4 == 4) {
+ /* It's a cmovx.yy. Replace the suffix x */
+ for (i = 5; i < OPLEN; i++) {
+ if (x->d86_mneu[i] == '.')
+ break;
+ }
+ x->d86_mneu[i - 1] = *types[opnd_size];
+ } else {
+ (void) strlcat(x->d86_mneu, types[opnd_size],
+ OPLEN);
+ }
+ }
+ }
+#endif
+
+ /*
+ * Process operands based on the addressing modes.
+ */
+ x->d86_mode = cpu_mode;
+ x->d86_rex_prefix = rex_prefix;
+ x->d86_opnd_size = opnd_size;
+ x->d86_addr_size = addr_size;
+ vbit = 0; /* initialize for mem/reg -> reg */
+ switch (dp->it_adrmode) {
+ /*
+ * amd64 instruction to sign extend 32 bit reg/mem operands
+ * into 64 bit register values
+ */
+ case MOVSXZ:
+#ifdef DIS_TEXT
+ if (rex_prefix == 0)
+ (void) strncpy(x->d86_mneu, "movzld", OPLEN);
+#endif
+ dtrace_get_modrm(x, &mode, &reg, &r_m);
+ dtrace_rex_adjust(rex_prefix, mode, &reg, &r_m);
+ x->d86_opnd_size = SIZE64;
+ dtrace_get_operand(x, REG_ONLY, reg, LONG_OPND, 1);
+ x->d86_opnd_size = opnd_size = SIZE32;
+ wbit = LONG_OPND;
+ dtrace_get_operand(x, mode, r_m, wbit, 0);
+ break;
+
+ /*
+ * movsbl movsbw movsbq (0x0FBE) or movswl movswq (0x0FBF)
+ * movzbl movzbw movzbq (0x0FB6) or mobzwl movzwq (0x0FB7)
+ * wbit lives in 2nd byte, note that operands
+ * are different sized
+ */
+ case MOVZ:
+ if (rex_prefix & REX_W) {
+ /* target register size = 64 bit */
+ x->d86_mneu[5] = 'q';
+ }
+ dtrace_get_modrm(x, &mode, &reg, &r_m);
+ dtrace_rex_adjust(rex_prefix, mode, &reg, &r_m);
+ dtrace_get_operand(x, REG_ONLY, reg, LONG_OPND, 1);
+ x->d86_opnd_size = opnd_size = SIZE16;
+ wbit = WBIT(opcode5);
+ dtrace_get_operand(x, mode, r_m, wbit, 0);
+ break;
+
+ /*
+ * imul instruction, with either 8-bit or longer immediate
+ * opcode 0x6B for byte, sign-extended displacement, 0x69 for word(s)
+ */
+ case IMUL:
+ wbit = LONG_OPND;
+ THREEOPERAND(x, mode, reg, r_m, rex_prefix, wbit, LONG_OPND,
+ OPSIZE(opnd_size, opcode2 == 0x9));
+ break;
+
+ /* memory or register operand to register, with 'w' bit */
+ case MRw:
+ wbit = WBIT(opcode2);
+ STANDARD_MODRM(x, mode, reg, r_m, rex_prefix, wbit, 0);
+ break;
+
+ /* register to memory or register operand, with 'w' bit */
+ /* arpl happens to fit here also because it is odd */
+ case RMw:
+ if (opcode_bytes == 2)
+ wbit = WBIT(opcode5);
+ else
+ wbit = WBIT(opcode2);
+ STANDARD_MODRM(x, mode, reg, r_m, rex_prefix, wbit, 1);
+ break;
+
+ /* xaddb instruction */
+ case XADDB:
+ wbit = 0;
+ STANDARD_MODRM(x, mode, reg, r_m, rex_prefix, wbit, 1);
+ break;
+
+ /* MMX register to memory or register operand */
+ case MMS:
+ case MMOS:
+#ifdef DIS_TEXT
+ wbit = strcmp(dp->it_name, "movd") ? MM_OPND : LONG_OPND;
+#else
+ wbit = LONG_OPND;
+#endif
+ MIXED_MM(x, mode, reg, r_m, rex_prefix, wbit, MM_OPND, 1);
+ break;
+
+ /* MMX register to memory */
+ case MMOMS:
+ dtrace_get_modrm(x, &mode, &reg, &r_m);
+ if (mode == REG_ONLY)
+ goto error;
+ wbit = MM_OPND;
+ MIXED_MM(x, mode, reg, r_m, rex_prefix, wbit, MM_OPND, 1);
+ break;
+
+ /* Double shift. Has immediate operand specifying the shift. */
+ case DSHIFT:
+ wbit = LONG_OPND;
+ dtrace_get_modrm(x, &mode, &reg, &r_m);
+ dtrace_rex_adjust(rex_prefix, mode, &reg, &r_m);
+ dtrace_get_operand(x, mode, r_m, wbit, 2);
+ dtrace_get_operand(x, REG_ONLY, reg, LONG_OPND, 1);
+ dtrace_imm_opnd(x, wbit, 1, 0);
+ break;
+
+ /*
+ * Double shift. With no immediate operand, specifies using %cl.
+ */
+ case DSHIFTcl:
+ wbit = LONG_OPND;
+ STANDARD_MODRM(x, mode, reg, r_m, rex_prefix, wbit, 1);
+ break;
+
+ /* immediate to memory or register operand */
+ case IMlw:
+ wbit = WBIT(opcode2);
+ dtrace_rex_adjust(rex_prefix, mode, NULL, &r_m);
+ dtrace_get_operand(x, mode, r_m, wbit, 1);
+ /*
+ * Have long immediate for opcode 0x81, but not 0x80 nor 0x83
+ */
+ dtrace_imm_opnd(x, wbit, OPSIZE(opnd_size, opcode2 == 1), 0);
+ break;
+
+ /* immediate to memory or register operand with the */
+ /* 'w' bit present */
+ case IMw:
+ wbit = WBIT(opcode2);
+ dtrace_get_modrm(x, &mode, &reg, &r_m);
+ dtrace_rex_adjust(rex_prefix, mode, NULL, &r_m);
+ dtrace_get_operand(x, mode, r_m, wbit, 1);
+ dtrace_imm_opnd(x, wbit, OPSIZE(opnd_size, wbit), 0);
+ break;
+
+ /* immediate to register with register in low 3 bits */
+ /* of op code */
+ case IR:
+ /* w-bit here (with regs) is bit 3 */
+ wbit = opcode2 >>3 & 0x1;
+ reg = REGNO(opcode2);
+ dtrace_rex_adjust(rex_prefix, mode, &reg, NULL);
+ mode = REG_ONLY;
+ r_m = reg;
+ dtrace_get_operand(x, mode, r_m, wbit, 1);
+ dtrace_imm_opnd(x, wbit, OPSIZE64(opnd_size, wbit), 0);
+ break;
+
+ /* MMX immediate shift of register */
+ case MMSH:
+ case MMOSH:
+ wbit = MM_OPND;
+ goto mm_shift; /* in next case */
+
+ /* SIMD immediate shift of register */
+ case XMMSH:
+ wbit = XMM_OPND;
+mm_shift:
+ reg = REGNO(opcode7);
+ dtrace_rex_adjust(rex_prefix, mode, &reg, NULL);
+ dtrace_get_operand(x, REG_ONLY, reg, wbit, 1);
+ dtrace_imm_opnd(x, wbit, 1, 0);
+ NOMEM;
+ break;
+
+ /* accumulator to memory operand */
+ case AO:
+ vbit = 1;
+ /*FALLTHROUGH*/
+
+ /* memory operand to accumulator */
+ case OA:
+ wbit = WBIT(opcode2);
+ dtrace_get_operand(x, REG_ONLY, EAX_REGNO, wbit, 1 - vbit);
+ dtrace_imm_opnd(x, wbit, OPSIZE64(addr_size, LONG_OPND), vbit);
+#ifdef DIS_TEXT
+ x->d86_opnd[vbit].d86_mode = MODE_OFFSET;
+#endif
+ break;
+
+
+ /* segment register to memory or register operand */
+ case SM:
+ vbit = 1;
+ /*FALLTHROUGH*/
+
+ /* memory or register operand to segment register */
+ case MS:
+ dtrace_get_modrm(x, &mode, &reg, &r_m);
+ dtrace_rex_adjust(rex_prefix, mode, NULL, &r_m);
+ dtrace_get_operand(x, mode, r_m, LONG_OPND, vbit);
+ dtrace_get_operand(x, REG_ONLY, reg, SEG_OPND, 1 - vbit);
+ break;
+
+ /*
+ * rotate or shift instructions, which may shift by 1 or
+ * consult the cl register, depending on the 'v' bit
+ */
+ case Mv:
+ vbit = VBIT(opcode2);
+ wbit = WBIT(opcode2);
+ dtrace_rex_adjust(rex_prefix, mode, NULL, &r_m);
+ dtrace_get_operand(x, mode, r_m, wbit, 1);
+#ifdef DIS_TEXT
+ if (vbit) {
+ (void) strlcat(x->d86_opnd[0].d86_opnd, "%cl", OPLEN);
+ } else {
+ x->d86_opnd[0].d86_mode = MODE_SIGNED;
+ x->d86_opnd[0].d86_value_size = 1;
+ x->d86_opnd[0].d86_value = 1;
+ }
+#endif
+ break;
+ /*
+ * immediate rotate or shift instructions
+ */
+ case MvI:
+ wbit = WBIT(opcode2);
+normal_imm_mem:
+ dtrace_rex_adjust(rex_prefix, mode, NULL, &r_m);
+ dtrace_get_operand(x, mode, r_m, wbit, 1);
+ dtrace_imm_opnd(x, wbit, 1, 0);
+ break;
+
+ /* bit test instructions */
+ case MIb:
+ wbit = LONG_OPND;
+ goto normal_imm_mem;
+
+ /* single memory or register operand with 'w' bit present */
+ case Mw:
+ wbit = WBIT(opcode2);
+just_mem:
+ dtrace_get_modrm(x, &mode, &reg, &r_m);
+ dtrace_rex_adjust(rex_prefix, mode, NULL, &r_m);
+ dtrace_get_operand(x, mode, r_m, wbit, 0);
+ break;
+
+ case SWAPGS:
+ if (cpu_mode == SIZE64 && mode == 3 && r_m == 0) {
+#ifdef DIS_TEXT
+ (void) strncpy(x->d86_mneu, "swapgs", OPLEN);
+#endif
+ NOMEM;
+ break;
+ }
+ /*FALLTHROUGH*/
+
+ /* prefetch instruction - memory operand, but no memory acess */
+ case PREF:
+ NOMEM;
+ /*FALLTHROUGH*/
+
+ /* single memory or register operand */
+ case M:
+ wbit = LONG_OPND;
+ goto just_mem;
+
+ /* single memory or register byte operand */
+ case Mb:
+ wbit = BYTE_OPND;
+ goto just_mem;
+
+ case MO:
+ /* Similar to M, but only memory (no direct registers) */
+ wbit = LONG_OPND;
+ dtrace_get_modrm(x, &mode, &reg, &r_m);
+ if (mode == 3)
+ goto error;
+ dtrace_rex_adjust(rex_prefix, mode, NULL, &r_m);
+ dtrace_get_operand(x, mode, r_m, wbit, 0);
+ break;
+
+ /* move special register to register or reverse if vbit */
+ case SREG:
+ switch (opcode5) {
+
+ case 2:
+ vbit = 1;
+ /*FALLTHROUGH*/
+ case 0:
+ wbit = CONTROL_OPND;
+ break;
+
+ case 3:
+ vbit = 1;
+ /*FALLTHROUGH*/
+ case 1:
+ wbit = DEBUG_OPND;
+ break;
+
+ case 6:
+ vbit = 1;
+ /*FALLTHROUGH*/
+ case 4:
+ wbit = TEST_OPND;
+ break;
+
+ }
+ dtrace_get_modrm(x, &mode, &reg, &r_m);
+ dtrace_rex_adjust(rex_prefix, mode, &reg, &r_m);
+ dtrace_get_operand(x, REG_ONLY, reg, wbit, vbit);
+ dtrace_get_operand(x, REG_ONLY, r_m, LONG_OPND, 1 - vbit);
+ NOMEM;
+ break;
+
+ /*
+ * single register operand with register in the low 3
+ * bits of op code
+ */
+ case R:
+ if (opcode_bytes == 2)
+ reg = REGNO(opcode5);
+ else
+ reg = REGNO(opcode2);
+ dtrace_rex_adjust(rex_prefix, mode, &reg, NULL);
+ dtrace_get_operand(x, REG_ONLY, reg, LONG_OPND, 0);
+ NOMEM;
+ break;
+
+ /*
+ * register to accumulator with register in the low 3
+ * bits of op code, xchg instructions
+ */
+ case RA:
+ NOMEM;
+ reg = REGNO(opcode2);
+ dtrace_rex_adjust(rex_prefix, mode, &reg, NULL);
+ dtrace_get_operand(x, REG_ONLY, reg, LONG_OPND, 0);
+ dtrace_get_operand(x, REG_ONLY, EAX_REGNO, LONG_OPND, 1);
+ break;
+
+ /*
+ * single segment register operand, with register in
+ * bits 3-4 of op code byte
+ */
+ case SEG:
+ NOMEM;
+ reg = (x->d86_bytes[x->d86_len - 1] >> 3) & 0x3;
+ dtrace_get_operand(x, REG_ONLY, reg, SEG_OPND, 0);
+ break;
+
+ /*
+ * single segment register operand, with register in
+ * bits 3-5 of op code
+ */
+ case LSEG:
+ NOMEM;
+ /* long seg reg from opcode */
+ reg = (x->d86_bytes[x->d86_len - 1] >> 3) & 0x7;
+ dtrace_get_operand(x, REG_ONLY, reg, SEG_OPND, 0);
+ break;
+
+ /* memory or register operand to register */
+ case MR:
+ wbit = LONG_OPND;
+ STANDARD_MODRM(x, mode, reg, r_m, rex_prefix, wbit, 0);
+ break;
+
+ case RM:
+ wbit = LONG_OPND;
+ STANDARD_MODRM(x, mode, reg, r_m, rex_prefix, wbit, 1);
+ break;
+
+ /* MMX/SIMD-Int memory or mm reg to mm reg */
+ case MM:
+ case MMO:
+#ifdef DIS_TEXT
+ wbit = strcmp(dp->it_name, "movd") ? MM_OPND : LONG_OPND;
+#else
+ wbit = LONG_OPND;
+#endif
+ MIXED_MM(x, mode, reg, r_m, rex_prefix, wbit, MM_OPND, 0);
+ break;
+
+ case MMOIMPL:
+#ifdef DIS_TEXT
+ wbit = strcmp(dp->it_name, "movd") ? MM_OPND : LONG_OPND;
+#else
+ wbit = LONG_OPND;
+#endif
+ dtrace_get_modrm(x, &mode, &reg, &r_m);
+ if (mode != REG_ONLY)
+ goto error;
+
+ dtrace_rex_adjust(rex_prefix, mode, &reg, &r_m);
+ dtrace_get_operand(x, mode, r_m, wbit, 0);
+ dtrace_get_operand(x, REG_ONLY, reg, MM_OPND, 1);
+ mode = 0; /* change for memory access size... */
+ break;
+
+ /* MMX/SIMD-Int and SIMD-FP predicated mm reg to r32 */
+ case MMO3P:
+ wbit = MM_OPND;
+ goto xmm3p;
+ case XMM3P:
+ wbit = XMM_OPND;
+xmm3p:
+ dtrace_get_modrm(x, &mode, &reg, &r_m);
+ if (mode != REG_ONLY)
+ goto error;
+
+ THREEOPERAND(x, mode, reg, r_m, rex_prefix, wbit, LONG_OPND, 1);
+ NOMEM;
+ break;
+
+ /* MMX/SIMD-Int predicated r32/mem to mm reg */
+ case MMOPRM:
+ wbit = LONG_OPND;
+ w2 = MM_OPND;
+ goto xmmprm;
+ case XMMPRM:
+ wbit = LONG_OPND;
+ w2 = XMM_OPND;
+xmmprm:
+ THREEOPERAND(x, mode, reg, r_m, rex_prefix, wbit, w2, 1);
+ break;
+
+ /* MMX/SIMD-Int predicated mm/mem to mm reg */
+ case MMOPM:
+ wbit = w2 = MM_OPND;
+ goto xmmprm;
+
+ /* MMX/SIMD-Int mm reg to r32 */
+ case MMOM3:
+ NOMEM;
+ dtrace_get_modrm(x, &mode, &reg, &r_m);
+ if (mode != REG_ONLY)
+ goto error;
+ wbit = MM_OPND;
+ MIXED_MM(x, mode, reg, r_m, rex_prefix, wbit, LONG_OPND, 0);
+ break;
+
+ /* SIMD memory or xmm reg operand to xmm reg */
+ case XMM:
+ case XMMO:
+ case XMMXIMPL:
+ wbit = XMM_OPND;
+ STANDARD_MODRM(x, mode, reg, r_m, rex_prefix, wbit, 0);
+
+ if (dp->it_adrmode == XMMXIMPL && mode != REG_ONLY)
+ goto error;
+
+#ifdef DIS_TEXT
+ /*
+ * movlps and movhlps share opcodes. They differ in the
+ * addressing modes allowed for their operands.
+ * movhps and movlhps behave similarly.
+ */
+ if (mode == REG_ONLY) {
+ if (strcmp(dp->it_name, "movlps") == 0)
+ (void) strncpy(x->d86_mneu, "movhlps", OPLEN);
+ else if (strcmp(dp->it_name, "movhps") == 0)
+ (void) strncpy(x->d86_mneu, "movlhps", OPLEN);
+ }
+#endif
+ if (dp->it_adrmode == XMMXIMPL)
+ mode = 0; /* change for memory access size... */
+ break;
+
+ /* SIMD xmm reg to memory or xmm reg */
+ case XMMS:
+ case XMMOS:
+ case XMMMS:
+ case XMMOMS:
+ dtrace_get_modrm(x, &mode, &reg, &r_m);
+#ifdef DIS_TEXT
+ if ((strcmp(dp->it_name, "movlps") == 0 ||
+ strcmp(dp->it_name, "movhps") == 0 ||
+ strcmp(dp->it_name, "movntps") == 0) &&
+ mode == REG_ONLY)
+ goto error;
+#endif
+ wbit = XMM_OPND;
+ MIXED_MM(x, mode, reg, r_m, rex_prefix, wbit, XMM_OPND, 1);
+ break;
+
+ /* SIMD memory to xmm reg */
+ case XMMM:
+ case XMMOM:
+ wbit = XMM_OPND;
+ dtrace_get_modrm(x, &mode, &reg, &r_m);
+#ifdef DIS_TEXT
+ if (mode == REG_ONLY) {
+ if (strcmp(dp->it_name, "movhps") == 0)
+ (void) strncpy(x->d86_mneu, "movlhps", OPLEN);
+ else
+ goto error;
+ }
+#endif
+ MIXED_MM(x, mode, reg, r_m, rex_prefix, wbit, XMM_OPND, 0);
+ break;
+
+ /* SIMD memory or r32 to xmm reg */
+ case XMM3MX:
+ wbit = LONG_OPND;
+ MIXED_MM(x, mode, reg, r_m, rex_prefix, wbit, XMM_OPND, 0);
+ break;
+
+ case XMM3MXS:
+ wbit = LONG_OPND;
+ MIXED_MM(x, mode, reg, r_m, rex_prefix, wbit, XMM_OPND, 1);
+ break;
+
+ /* SIMD memory or mm reg to xmm reg */
+ case XMMOMX:
+ /* SIMD mm to xmm */
+ case XMMMX:
+ wbit = MM_OPND;
+ MIXED_MM(x, mode, reg, r_m, rex_prefix, wbit, XMM_OPND, 0);
+ break;
+
+ /* SIMD memory or xmm reg to mm reg */
+ case XMMXMM:
+ case XMMOXMM:
+ case XMMXM:
+ wbit = XMM_OPND;
+ MIXED_MM(x, mode, reg, r_m, rex_prefix, wbit, MM_OPND, 0);
+ break;
+
+
+ /* SIMD memory or xmm reg to r32 */
+ case XMMXM3:
+ wbit = XMM_OPND;
+ MIXED_MM(x, mode, reg, r_m, rex_prefix, wbit, LONG_OPND, 0);
+ break;
+
+ /* SIMD xmm to r32 */
+ case XMMX3:
+ case XMMOX3:
+ dtrace_get_modrm(x, &mode, &reg, &r_m);
+ if (mode != REG_ONLY)
+ goto error;
+ dtrace_rex_adjust(rex_prefix, mode, &reg, &r_m);
+ dtrace_get_operand(x, mode, r_m, XMM_OPND, 0);
+ dtrace_get_operand(x, REG_ONLY, reg, LONG_OPND, 1);
+ NOMEM;
+ break;
+
+ /* SIMD predicated memory or xmm reg with/to xmm reg */
+ case XMMP:
+ case XMMOPM:
+ wbit = XMM_OPND;
+ THREEOPERAND(x, mode, reg, r_m, rex_prefix, wbit, XMM_OPND, 1);
+
+#ifdef DIS_TEXT
+ /*
+ * cmpps and cmpss vary their instruction name based
+ * on the value of imm8. Other XMMP instructions,
+ * such as shufps, require explicit specification of
+ * the predicate.
+ */
+ if (dp->it_name[0] == 'c' &&
+ dp->it_name[1] == 'm' &&
+ dp->it_name[2] == 'p' &&
+ strlen(dp->it_name) == 5) {
+ uchar_t pred = x->d86_opnd[0].d86_value & 0xff;
+
+ if (pred >= (sizeof (dis_PREDSUFFIX) / sizeof (char *)))
+ goto error;
+
+ (void) strncpy(x->d86_mneu, "cmp", OPLEN);
+ (void) strlcat(x->d86_mneu, dis_PREDSUFFIX[pred],
+ OPLEN);
+ (void) strlcat(x->d86_mneu,
+ dp->it_name + strlen(dp->it_name) - 2,
+ OPLEN);
+ x->d86_opnd[0] = x->d86_opnd[1];
+ x->d86_opnd[1] = x->d86_opnd[2];
+ x->d86_numopnds = 2;
+ }
+#endif
+ break;
+
+ /* immediate operand to accumulator */
+ case IA:
+ wbit = WBIT(opcode2);
+ dtrace_get_operand(x, REG_ONLY, EAX_REGNO, wbit, 1);
+ dtrace_imm_opnd(x, wbit, OPSIZE(opnd_size, wbit), 0);
+ NOMEM;
+ break;
+
+ /* memory or register operand to accumulator */
+ case MA:
+ wbit = WBIT(opcode2);
+ dtrace_rex_adjust(rex_prefix, mode, NULL, &r_m);
+ dtrace_get_operand(x, mode, r_m, wbit, 0);
+ break;
+
+ /* si register to di register used to reference memory */
+ case SD:
+#ifdef DIS_TEXT
+ dtrace_check_override(x, 0);
+ x->d86_numopnds = 2;
+ if (addr_size == SIZE64) {
+ (void) strlcat(x->d86_opnd[0].d86_opnd, "(%rsi)",
+ OPLEN);
+ (void) strlcat(x->d86_opnd[1].d86_opnd, "(%rdi)",
+ OPLEN);
+ } else if (addr_size == SIZE32) {
+ (void) strlcat(x->d86_opnd[0].d86_opnd, "(%esi)",
+ OPLEN);
+ (void) strlcat(x->d86_opnd[1].d86_opnd, "(%edi)",
+ OPLEN);
+ } else {
+ (void) strlcat(x->d86_opnd[0].d86_opnd, "(%si)",
+ OPLEN);
+ (void) strlcat(x->d86_opnd[1].d86_opnd, "(%di)",
+ OPLEN);
+ }
+#endif
+ wbit = LONG_OPND;
+ break;
+
+ /* accumulator to di register */
+ case AD:
+ wbit = WBIT(opcode2);
+#ifdef DIS_TEXT
+ dtrace_check_override(x, 1);
+ x->d86_numopnds = 2;
+ dtrace_get_operand(x, REG_ONLY, EAX_REGNO, wbit, 0);
+ if (addr_size == SIZE64)
+ (void) strlcat(x->d86_opnd[1].d86_opnd, "(%rdi)",
+ OPLEN);
+ else if (addr_size == SIZE32)
+ (void) strlcat(x->d86_opnd[1].d86_opnd, "(%edi)",
+ OPLEN);
+ else
+ (void) strlcat(x->d86_opnd[1].d86_opnd, "(%di)",
+ OPLEN);
+#endif
+ break;
+
+ /* si register to accumulator */
+ case SA:
+ wbit = WBIT(opcode2);
+#ifdef DIS_TEXT
+ dtrace_check_override(x, 0);
+ x->d86_numopnds = 2;
+ if (addr_size == SIZE64)
+ (void) strlcat(x->d86_opnd[0].d86_opnd, "(%rsi)",
+ OPLEN);
+ else if (addr_size == SIZE32)
+ (void) strlcat(x->d86_opnd[0].d86_opnd, "(%esi)",
+ OPLEN);
+ else
+ (void) strlcat(x->d86_opnd[0].d86_opnd, "(%si)",
+ OPLEN);
+ dtrace_get_operand(x, REG_ONLY, EAX_REGNO, wbit, 1);
+#endif
+ break;
+
+ /*
+ * single operand, a 16/32 bit displacement
+ */
+ case D:
+ wbit = LONG_OPND;
+ dtrace_disp_opnd(x, wbit, OPSIZE(opnd_size, LONG_OPND), 0);
+ NOMEM;
+ break;
+
+ /* jmp/call indirect to memory or register operand */
+ case INM:
+#ifdef DIS_TEXT
+ (void) strlcat(x->d86_opnd[0].d86_prefix, "*", OPLEN);
+#endif
+ dtrace_rex_adjust(rex_prefix, mode, NULL, &r_m);
+ dtrace_get_operand(x, mode, r_m, LONG_OPND, 0);
+ wbit = LONG_OPND;
+ break;
+
+ /*
+ * for long jumps and long calls -- a new code segment
+ * register and an offset in IP -- stored in object
+ * code in reverse order. Note - not valid in amd64
+ */
+ case SO:
+ dtrace_check_override(x, 1);
+ wbit = LONG_OPND;
+ dtrace_imm_opnd(x, wbit, OPSIZE(opnd_size, LONG_OPND), 1);
+#ifdef DIS_TEXT
+ x->d86_opnd[1].d86_mode = MODE_SIGNED;
+#endif
+ /* will now get segment operand */
+ dtrace_imm_opnd(x, wbit, 2, 0);
+ break;
+
+ /*
+ * jmp/call. single operand, 8 bit displacement.
+ * added to current EIP in 'compofff'
+ */
+ case BD:
+ dtrace_disp_opnd(x, BYTE_OPND, 1, 0);
+ NOMEM;
+ break;
+
+ /* single 32/16 bit immediate operand */
+ case I:
+ wbit = LONG_OPND;
+ dtrace_imm_opnd(x, wbit, OPSIZE(opnd_size, LONG_OPND), 0);
+ break;
+
+ /* single 8 bit immediate operand */
+ case Ib:
+ wbit = LONG_OPND;
+ dtrace_imm_opnd(x, wbit, 1, 0);
+ break;
+
+ case ENTER:
+ wbit = LONG_OPND;
+ dtrace_imm_opnd(x, wbit, 2, 0);
+ dtrace_imm_opnd(x, wbit, 1, 1);
+ switch (opnd_size) {
+ case SIZE64:
+ x->d86_memsize = (x->d86_opnd[1].d86_value + 1) * 8;
+ break;
+ case SIZE32:
+ x->d86_memsize = (x->d86_opnd[1].d86_value + 1) * 4;
+ break;
+ case SIZE16:
+ x->d86_memsize = (x->d86_opnd[1].d86_value + 1) * 2;
+ break;
+ }
+
+ break;
+
+ /* 16-bit immediate operand */
+ case RET:
+ wbit = LONG_OPND;
+ dtrace_imm_opnd(x, wbit, 2, 0);
+ break;
+
+ /* single 8 bit port operand */
+ case P:
+ dtrace_check_override(x, 0);
+ dtrace_imm_opnd(x, BYTE_OPND, 1, 0);
+ NOMEM;
+ break;
+
+ /* single operand, dx register (variable port instruction) */
+ case V:
+ x->d86_numopnds = 1;
+ dtrace_check_override(x, 0);
+#ifdef DIS_TEXT
+ (void) strlcat(x->d86_opnd[0].d86_opnd, "(%dx)", OPLEN);
+#endif
+ NOMEM;
+ break;
+
+ /*
+ * The int instruction, which has two forms:
+ * int 3 (breakpoint) or
+ * int n, where n is indicated in the subsequent
+ * byte (format Ib). The int 3 instruction (opcode 0xCC),
+ * where, although the 3 looks like an operand,
+ * it is implied by the opcode. It must be converted
+ * to the correct base and output.
+ */
+ case INT3:
+#ifdef DIS_TEXT
+ x->d86_numopnds = 1;
+ x->d86_opnd[0].d86_mode = MODE_SIGNED;
+ x->d86_opnd[0].d86_value_size = 1;
+ x->d86_opnd[0].d86_value = 3;
+#endif
+ NOMEM;
+ break;
+
+ /* single 8 bit immediate operand */
+ case INTx:
+ dtrace_imm_opnd(x, BYTE_OPND, 1, 0);
+ NOMEM;
+ break;
+
+ /* an unused byte must be discarded */
+ case U:
+ if (x->d86_get_byte(x->d86_data) < 0)
+ goto error;
+ x->d86_len++;
+ NOMEM;
+ break;
+
+ case CBW:
+#ifdef DIS_TEXT
+ if (opnd_size == SIZE16)
+ (void) strlcat(x->d86_mneu, "cbtw", OPLEN);
+ else if (opnd_size == SIZE32)
+ (void) strlcat(x->d86_mneu, "cwtl", OPLEN);
+ else
+ (void) strlcat(x->d86_mneu, "cltq", OPLEN);
+#endif
+ wbit = LONG_OPND;
+ NOMEM;
+ break;
+
+ case CWD:
+#ifdef DIS_TEXT
+ if (opnd_size == SIZE16)
+ (void) strlcat(x->d86_mneu, "cwtd", OPLEN);
+ else if (opnd_size == SIZE32)
+ (void) strlcat(x->d86_mneu, "cltd", OPLEN);
+ else
+ (void) strlcat(x->d86_mneu, "cqtd", OPLEN);
+#endif
+ wbit = LONG_OPND;
+ NOMEM;
+ break;
+
+ case XMMSFNC:
+ /*
+ * sfence is sfence if mode is REG_ONLY. If mode isn't
+ * REG_ONLY, mnemonic should be 'clflush'.
+ */
+ dtrace_get_modrm(x, &mode, &reg, &r_m);
+
+ /* sfence doesn't take operands */
+#ifdef DIS_TEXT
+ if (mode == REG_ONLY) {
+ (void) strlcat(x->d86_mneu, "sfence", OPLEN);
+ } else {
+ (void) strlcat(x->d86_mneu, "clflush", OPLEN);
+ dtrace_rex_adjust(rex_prefix, mode, &reg, &r_m);
+ dtrace_get_operand(x, mode, r_m, BYTE_OPND, 0);
+ NOMEM;
+ }
+#else
+ if (mode != REG_ONLY) {
+ dtrace_rex_adjust(rex_prefix, mode, &reg, &r_m);
+ dtrace_get_operand(x, mode, r_m, BYTE_OPND, 0);
+ NOMEM;
+ }
+#endif
+ break;
+
+ /*
+ * no disassembly, the mnemonic was all there was so go on
+ */
+ case NORM:
+ if (dp->it_invalid32 && cpu_mode != SIZE64)
+ goto error;
+ NOMEM;
+ /*FALLTHROUGH*/
+ case IMPLMEM:
+ break;
+
+ case XMMFENCE:
+ /*
+ * Only the following exact byte sequences are allowed:
+ *
+ * 0f ae e8 lfence
+ * 0f ae f0 mfence
+ */
+ if ((uint8_t)x->d86_bytes[x->d86_len - 1] != 0xe8 &&
+ (uint8_t)x->d86_bytes[x->d86_len - 1] != 0xf0)
+ goto error;
+
+ break;
+
+
+ /* float reg */
+ case F:
+#ifdef DIS_TEXT
+ x->d86_numopnds = 1;
+ (void) strlcat(x->d86_opnd[0].d86_opnd, "%st(X)", OPLEN);
+ x->d86_opnd[0].d86_opnd[4] = r_m + '0';
+#endif
+ NOMEM;
+ break;
+
+ /* float reg to float reg, with ret bit present */
+ case FF:
+ vbit = opcode2 >> 2 & 0x1; /* vbit = 1: st -> st(i) */
+ /*FALLTHROUGH*/
+ case FFC: /* case for vbit always = 0 */
+#ifdef DIS_TEXT
+ x->d86_numopnds = 2;
+ (void) strlcat(x->d86_opnd[1 - vbit].d86_opnd, "%st", OPLEN);
+ (void) strlcat(x->d86_opnd[vbit].d86_opnd, "%st(X)", OPLEN);
+ x->d86_opnd[vbit].d86_opnd[4] = r_m + '0';
+#endif
+ NOMEM;
+ break;
+
+ /* an invalid op code */
+ case AM:
+ case DM:
+ case OVERRIDE:
+ case PREFIX:
+ case UNKNOWN:
+ NOMEM;
+ default:
+ goto error;
+ } /* end switch */
+ if (x->d86_error)
+ goto error;
+
+done:
+#ifdef DIS_MEM
+ /*
+ * compute the size of any memory accessed by the instruction
+ */
+ if (x->d86_memsize != 0) {
+ return (0);
+ } else if (dp->it_stackop) {
+ switch (opnd_size) {
+ case SIZE16:
+ x->d86_memsize = 2;
+ break;
+ case SIZE32:
+ x->d86_memsize = 4;
+ break;
+ case SIZE64:
+ x->d86_memsize = 8;
+ break;
+ }
+ } else if (nomem || mode == REG_ONLY) {
+ x->d86_memsize = 0;
+
+ } else if (dp->it_size != 0) {
+ /*
+ * In 64 bit mode descriptor table entries
+ * go up to 10 bytes and popf/pushf are always 8 bytes
+ */
+ if (x->d86_mode == SIZE64 && dp->it_size == 6)
+ x->d86_memsize = 10;
+ else if (x->d86_mode == SIZE64 && opcode1 == 0x9 &&
+ (opcode2 == 0xc || opcode2 == 0xd))
+ x->d86_memsize = 8;
+ else
+ x->d86_memsize = dp->it_size;
+
+ } else if (wbit == 0) {
+ x->d86_memsize = 1;
+
+ } else if (wbit == LONG_OPND) {
+ if (opnd_size == SIZE64)
+ x->d86_memsize = 8;
+ else if (opnd_size == SIZE32)
+ x->d86_memsize = 4;
+ else
+ x->d86_memsize = 2;
+
+ } else if (wbit == SEG_OPND) {
+ x->d86_memsize = 4;
+
+ } else {
+ x->d86_memsize = 8;
+ }
+#endif
+ return (0);
+
+error:
+#ifdef DIS_TEXT
+ (void) strlcat(x->d86_mneu, "undef", OPLEN);
+#endif
+ return (1);
+}
+
+#ifdef DIS_TEXT
+
+/*
+ * Some instructions should have immediate operands printed
+ * as unsigned integers. We compare against this table.
+ */
+static char *unsigned_ops[] = {
+ "or", "and", "xor", "test", "in", "out", "lcall", "ljmp",
+ "rcr", "rcl", "ror", "rol", "shl", "shr", "sal", "psr", "psl",
+ 0
+};
+
+static int
+isunsigned_op(char *opcode)
+{
+ char *where;
+ int i;
+ int is_unsigned = 0;
+
+ /*
+ * Work back to start of last mnemonic, since we may have
+ * prefixes on some opcodes.
+ */
+ where = opcode + strlen(opcode) - 1;
+ while (where > opcode && *where != ' ')
+ --where;
+ if (*where == ' ')
+ ++where;
+
+ for (i = 0; unsigned_ops[i]; ++i) {
+ if (strncmp(where, unsigned_ops[i],
+ strlen(unsigned_ops[i])))
+ continue;
+ is_unsigned = 1;
+ break;
+ }
+ return (is_unsigned);
+}
+
+/* ARGSUSED */
+void
+dtrace_disx86_str(dis86_t *dis, uint_t mode, uintptr_t pc, char *buf,
+ size_t buflen)
+{
+ int i;
+
+ dis->d86_sprintf_func(buf, buflen, "%-6s ", dis->d86_mneu);
+
+ /*
+ * For PC-relative jumps, the pc is really the next pc after executing
+ * this instruction, so increment it appropriately.
+ */
+ pc += dis->d86_len;
+
+ for (i = 0; i < dis->d86_numopnds; i++) {
+ d86opnd_t *op = &dis->d86_opnd[i];
+ int64_t sv;
+ uint64_t mask;
+
+ if (i != 0)
+ (void) strlcat(buf, ",", buflen);
+
+ (void) strlcat(buf, op->d86_prefix, buflen);
+
+ sv = op->d86_value;
+
+ switch (op->d86_mode) {
+
+ case MODE_NONE:
+
+ (void) strlcat(buf, op->d86_opnd, buflen);
+ break;
+
+ case MODE_SIGNED:
+ case MODE_IMPLIED:
+ case MODE_OFFSET:
+
+ if (dis->d86_seg_prefix)
+ (void) strlcat(buf, dis->d86_seg_prefix,
+ buflen);
+
+ switch (op->d86_value_size) {
+ case 1:
+ sv = (int8_t)sv;
+ mask = 0xff;
+ break;
+ case 2:
+ sv = (int16_t)sv;
+ mask = 0xffff;
+ break;
+ case 4:
+ sv = (int32_t)sv;
+ mask = 0xffffffff;
+ break;
+ case 8:
+ mask = 0xffffffffffffffffULL;
+ break;
+ }
+
+ if (op->d86_mode == MODE_SIGNED ||
+ op->d86_mode == MODE_IMPLIED)
+ (void) strlcat(buf, "$", buflen);
+
+ if (sv < 0 && sv > -0xffff &&
+ !isunsigned_op(dis->d86_mneu)) {
+ dis->d86_sprintf_func(buf + strlen(buf),
+ buflen - strlen(buf),
+ (dis->d86_flags & DIS_OP_OCTAL) ?
+ "-0%llo" : "-0x%llx", -sv & mask);
+ } else {
+ dis->d86_sprintf_func(buf + strlen(buf),
+ buflen - strlen(buf),
+ (dis->d86_flags & DIS_OP_OCTAL) ?
+ "0%llo" : "0x%llx", sv & mask);
+ }
+ (void) strlcat(buf, op->d86_opnd, buflen);
+ break;
+
+ case MODE_IPREL:
+
+ switch (op->d86_value_size) {
+ case 1:
+ sv = (int8_t)sv;
+ break;
+ case 2:
+ sv = (int16_t)sv;
+ break;
+ case 4:
+ sv = (int32_t)sv;
+ break;
+ }
+
+ if (sv < 0)
+ dis->d86_sprintf_func(buf + strlen(buf),
+ buflen - strlen(buf),
+ (dis->d86_flags & DIS_OP_OCTAL) ?
+ "-0%llo" : "-0x%llx", -sv - dis->d86_len);
+ else
+ dis->d86_sprintf_func(buf + strlen(buf),
+ buflen - strlen(buf),
+ (dis->d86_flags & DIS_OP_OCTAL) ?
+ "+0%llo" : "+0x%llx", sv + dis->d86_len);
+
+ (void) strlcat(buf, "\t<", buflen);
+
+ if (dis->d86_sym_lookup == NULL ||
+ dis->d86_sym_lookup(dis->d86_data, pc + sv,
+ buf + strlen(buf), buflen - strlen(buf)) != 0)
+ dis->d86_sprintf_func(buf + strlen(buf),
+ buflen - strlen(buf),
+ (dis->d86_flags & DIS_OP_OCTAL) ?
+ "0%llo" : "0x%llx", pc + sv);
+
+ (void) strlcat(buf, ">", buflen);
+
+ break;
+ }
+ }
+}
+
+#endif /* DIS_TEXT */
diff --git a/sys/cddl/dev/dtrace/amd64/dis_tables.h b/sys/cddl/dev/dtrace/amd64/dis_tables.h
new file mode 100644
index 000000000000..b45a8c50e738
--- /dev/null
+++ b/sys/cddl/dev/dtrace/amd64/dis_tables.h
@@ -0,0 +1,112 @@
+/*
+ * 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$
+ */
+/*
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1988 AT&T */
+/* All Rights Reserved */
+
+
+#ifndef _DIS_TABLES_H
+#define _DIS_TABLES_H
+
+#if defined(sun)
+#pragma ident "@(#)dis_tables.h 1.7 06/03/02 SMI"
+#endif
+
+/*
+ * Constants and prototypes for the IA32 disassembler backend. See dis_tables.c
+ * for usage information and documentation.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/types.h>
+#include <sys/param.h>
+
+/*
+ * values for cpu mode
+ */
+#define SIZE16 1
+#define SIZE32 2
+#define SIZE64 3
+
+#define OPLEN 256
+#define PFIXLEN 8
+#define NCPS 12 /* number of chars per symbol */
+
+/*
+ * data structures that must be provided to dtrace_dis86()
+ */
+typedef struct d86opnd {
+ char d86_opnd[OPLEN]; /* symbolic rep of operand */
+ char d86_prefix[PFIXLEN]; /* any prefix string or "" */
+ uint_t d86_mode; /* mode for immediate */
+ uint_t d86_value_size; /* size in bytes of d86_value */
+ uint64_t d86_value; /* immediate value of opnd */
+} d86opnd_t;
+
+typedef struct dis86 {
+ uint_t d86_mode;
+ uint_t d86_error;
+ uint_t d86_len; /* instruction length */
+ int d86_rmindex; /* index of modrm byte or -1 */
+ uint_t d86_memsize; /* size of memory referenced */
+ char d86_bytes[16]; /* bytes of instruction */
+ char d86_mneu[OPLEN];
+ uint_t d86_numopnds;
+ uint_t d86_rex_prefix; /* value of REX prefix if !0 */
+ char *d86_seg_prefix; /* segment prefix, if any */
+ uint_t d86_opnd_size;
+ uint_t d86_addr_size;
+ uint_t d86_got_modrm;
+ struct d86opnd d86_opnd[3]; /* up to 3 operands */
+ int (*d86_check_func)(void *);
+ int (*d86_get_byte)(void *);
+#ifdef DIS_TEXT
+ int (*d86_sym_lookup)(void *, uint64_t, char *, size_t);
+ int (*d86_sprintf_func)(char *, size_t, const char *, ...);
+ int d86_flags;
+ uint_t d86_imm_bytes;
+#endif
+ void *d86_data;
+} dis86_t;
+
+extern int dtrace_disx86(dis86_t *x, uint_t cpu_mode);
+
+#define DIS_OP_OCTAL 0x1 /* Print all numbers in octal */
+
+#ifdef DIS_TEXT
+extern void dtrace_disx86_str(dis86_t *x, uint_t cpu_mode, uintptr_t pc,
+ char *buf, size_t len);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _DIS_TABLES_H */
diff --git a/sys/cddl/dev/dtrace/amd64/dtrace_asm.S b/sys/cddl/dev/dtrace/amd64/dtrace_asm.S
new file mode 100644
index 000000000000..cf8314c6dceb
--- /dev/null
+++ b/sys/cddl/dev/dtrace/amd64/dtrace_asm.S
@@ -0,0 +1,573 @@
+/*
+ * 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 2008 John Birrell <jb@freebsd.org>
+ *
+ * $FreeBSD$
+ *
+ */
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#define _ASM
+
+#include <machine/asmacros.h>
+#include <sys/cpuvar_defs.h>
+#include <sys/dtrace.h>
+
+#include "assym.s"
+
+#define INTR_POP \
+ MEXITCOUNT; \
+ movq TF_RDI(%rsp),%rdi; \
+ movq TF_RSI(%rsp),%rsi; \
+ movq TF_RDX(%rsp),%rdx; \
+ movq TF_RCX(%rsp),%rcx; \
+ movq TF_R8(%rsp),%r8; \
+ movq TF_R9(%rsp),%r9; \
+ movq TF_RAX(%rsp),%rax; \
+ movq TF_RBX(%rsp),%rbx; \
+ movq TF_RBP(%rsp),%rbp; \
+ movq TF_R10(%rsp),%r10; \
+ movq TF_R11(%rsp),%r11; \
+ movq TF_R12(%rsp),%r12; \
+ movq TF_R13(%rsp),%r13; \
+ movq TF_R14(%rsp),%r14; \
+ movq TF_R15(%rsp),%r15; \
+ testb $SEL_RPL_MASK,TF_CS(%rsp); \
+ jz 1f; \
+ cli; \
+ swapgs; \
+1: addq $TF_RIP,%rsp;
+
+
+ .globl calltrap
+ .type calltrap,@function
+ ENTRY(dtrace_invop_start)
+
+ /*
+ * #BP traps with %rip set to the next address. We need to decrement
+ * the value to indicate the address of the int3 (0xcc) instruction
+ * that we substituted.
+ */
+ movq TF_RIP(%rsp), %rdi
+ decq %rdi
+ movq TF_RSP(%rsp), %rsi
+ movq TF_RAX(%rsp), %rdx
+ pushq (%rsi)
+ movq %rsp, %rsi
+ call dtrace_invop
+ ALTENTRY(dtrace_invop_callsite)
+ addq $8, %rsp
+ cmpl $DTRACE_INVOP_PUSHL_EBP, %eax
+ je bp_push
+ cmpl $DTRACE_INVOP_LEAVE, %eax
+ je bp_leave
+ cmpl $DTRACE_INVOP_NOP, %eax
+ je bp_nop
+ cmpl $DTRACE_INVOP_RET, %eax
+ je bp_ret
+
+ /* When all else fails handle the trap in the usual way. */
+ jmpq *dtrace_invop_calltrap_addr
+
+bp_push:
+ /*
+ * We must emulate a "pushq %rbp". To do this, we pull the stack
+ * down 8 bytes, and then store the base pointer.
+ */
+ INTR_POP
+ subq $16, %rsp /* make room for %rbp */
+ pushq %rax /* push temp */
+ movq 24(%rsp), %rax /* load calling RIP */
+ movq %rax, 8(%rsp) /* store calling RIP */
+ movq 32(%rsp), %rax /* load calling CS */
+ movq %rax, 16(%rsp) /* store calling CS */
+ movq 40(%rsp), %rax /* load calling RFLAGS */
+ movq %rax, 24(%rsp) /* store calling RFLAGS */
+ movq 48(%rsp), %rax /* load calling RSP */
+ subq $8, %rax /* make room for %rbp */
+ movq %rax, 32(%rsp) /* store calling RSP */
+ movq 56(%rsp), %rax /* load calling SS */
+ movq %rax, 40(%rsp) /* store calling SS */
+ movq 32(%rsp), %rax /* reload calling RSP */
+ movq %rbp, (%rax) /* store %rbp there */
+ popq %rax /* pop off temp */
+ iretq /* return from interrupt */
+ /*NOTREACHED*/
+
+bp_leave:
+ /*
+ * We must emulate a "leave", which is the same as a "movq %rbp, %rsp"
+ * followed by a "popq %rbp". This is quite a bit simpler on amd64
+ * than it is on i386 -- we can exploit the fact that the %rsp is
+ * explicitly saved to effect the pop without having to reshuffle
+ * the other data pushed for the trap.
+ */
+ INTR_POP
+ pushq %rax /* push temp */
+ movq 8(%rsp), %rax /* load calling RIP */
+ movq %rax, 8(%rsp) /* store calling RIP */
+ movq (%rbp), %rax /* get new %rbp */
+ addq $8, %rbp /* adjust new %rsp */
+ movq %rbp, 32(%rsp) /* store new %rsp */
+ movq %rax, %rbp /* set new %rbp */
+ popq %rax /* pop off temp */
+ iretq /* return from interrupt */
+ /*NOTREACHED*/
+
+bp_nop:
+ /* We must emulate a "nop". */
+ INTR_POP
+ iretq
+ /*NOTREACHED*/
+
+bp_ret:
+ INTR_POP
+ pushq %rax /* push temp */
+ movq 32(%rsp), %rax /* load %rsp */
+ movq (%rax), %rax /* load calling RIP */
+ movq %rax, 8(%rsp) /* store calling RIP */
+ addq $8, 32(%rsp) /* adjust new %rsp */
+ popq %rax /* pop off temp */
+ iretq /* return from interrupt */
+ /*NOTREACHED*/
+
+ END(dtrace_invop_start)
+
+/*
+void dtrace_invop_init(void)
+*/
+ ENTRY(dtrace_invop_init)
+ movq $dtrace_invop_start, dtrace_invop_jump_addr(%rip)
+ ret
+ END(dtrace_invop_init)
+
+/*
+void dtrace_invop_uninit(void)
+*/
+ ENTRY(dtrace_invop_uninit)
+ movq $0, dtrace_invop_jump_addr(%rip)
+ ret
+ END(dtrace_invop_uninit)
+
+/*
+greg_t dtrace_getfp(void)
+*/
+ ENTRY(dtrace_getfp)
+ movq %rbp, %rax
+ ret
+ END(dtrace_getfp)
+
+/*
+uint32_t
+dtrace_cas32(uint32_t *target, uint32_t cmp, uint32_t new)
+*/
+ ENTRY(dtrace_cas32)
+ movl %esi, %eax
+ lock
+ cmpxchgl %edx, (%rdi)
+ ret
+ END(dtrace_cas32)
+
+/*
+void *
+dtrace_casptr(void *target, void *cmp, void *new)
+*/
+ ENTRY(dtrace_casptr)
+ movq %rsi, %rax
+ lock
+ cmpxchgq %rdx, (%rdi)
+ ret
+ END(dtrace_casptr)
+
+/*
+uintptr_t
+dtrace_caller(int aframes)
+*/
+ ENTRY(dtrace_caller)
+ movq $-1, %rax
+ ret
+ END(dtrace_caller)
+
+/*
+void
+dtrace_copy(uintptr_t src, uintptr_t dest, size_t size)
+*/
+ ENTRY(dtrace_copy)
+ pushq %rbp
+ movq %rsp, %rbp
+
+ xchgq %rdi, %rsi /* make %rsi source, %rdi dest */
+ movq %rdx, %rcx /* load count */
+ repz /* repeat for count ... */
+ smovb /* move from %ds:rsi to %ed:rdi */
+ leave
+ ret
+ END(dtrace_copy)
+
+/*
+void
+dtrace_copystr(uintptr_t uaddr, uintptr_t kaddr, size_t size,
+ volatile uint16_t *flags)
+*/
+ ENTRY(dtrace_copystr)
+ pushq %rbp
+ movq %rsp, %rbp
+
+0:
+ movb (%rdi), %al /* load from source */
+ movb %al, (%rsi) /* store to destination */
+ addq $1, %rdi /* increment source pointer */
+ addq $1, %rsi /* increment destination pointer */
+ subq $1, %rdx /* decrement remaining count */
+ cmpb $0, %al
+ je 2f
+ testq $0xfff, %rdx /* test if count is 4k-aligned */
+ jnz 1f /* if not, continue with copying */
+ testq $CPU_DTRACE_BADADDR, (%rcx) /* load and test dtrace flags */
+ jnz 2f
+1:
+ cmpq $0, %rdx
+ jne 0b
+2:
+ leave
+ ret
+
+ END(dtrace_copystr)
+
+/*
+uintptr_t
+dtrace_fulword(void *addr)
+*/
+ ENTRY(dtrace_fulword)
+ movq (%rdi), %rax
+ ret
+ END(dtrace_fulword)
+
+/*
+uint8_t
+dtrace_fuword8_nocheck(void *addr)
+*/
+ ENTRY(dtrace_fuword8_nocheck)
+ xorq %rax, %rax
+ movb (%rdi), %al
+ ret
+ END(dtrace_fuword8_nocheck)
+
+/*
+uint16_t
+dtrace_fuword16_nocheck(void *addr)
+*/
+ ENTRY(dtrace_fuword16_nocheck)
+ xorq %rax, %rax
+ movw (%rdi), %ax
+ ret
+ END(dtrace_fuword16_nocheck)
+
+/*
+uint32_t
+dtrace_fuword32_nocheck(void *addr)
+*/
+ ENTRY(dtrace_fuword32_nocheck)
+ xorq %rax, %rax
+ movl (%rdi), %eax
+ ret
+ END(dtrace_fuword32_nocheck)
+
+/*
+uint64_t
+dtrace_fuword64_nocheck(void *addr)
+*/
+ ENTRY(dtrace_fuword64_nocheck)
+ movq (%rdi), %rax
+ ret
+ END(dtrace_fuword64_nocheck)
+
+/*
+void
+dtrace_probe_error(dtrace_state_t *state, dtrace_epid_t epid, int which,
+ int fault, int fltoffs, uintptr_t illval)
+*/
+ ENTRY(dtrace_probe_error)
+ pushq %rbp
+ movq %rsp, %rbp
+ subq $0x8, %rsp
+ movq %r9, (%rsp)
+ movq %r8, %r9
+ movq %rcx, %r8
+ movq %rdx, %rcx
+ movq %rsi, %rdx
+ movq %rdi, %rsi
+ movl dtrace_probeid_error(%rip), %edi
+ call dtrace_probe
+ addq $0x8, %rsp
+ leave
+ ret
+ END(dtrace_probe_error)
+
+/*
+void
+dtrace_membar_producer(void)
+*/
+ ENTRY(dtrace_membar_producer)
+ rep; ret /* use 2 byte return instruction when branch target */
+ /* AMD Software Optimization Guide - Section 6.2 */
+ END(dtrace_membar_producer)
+
+/*
+void
+dtrace_membar_consumer(void)
+*/
+ ENTRY(dtrace_membar_consumer)
+ rep; ret /* use 2 byte return instruction when branch target */
+ /* AMD Software Optimization Guide - Section 6.2 */
+ END(dtrace_membar_consumer)
+
+/*
+dtrace_icookie_t
+dtrace_interrupt_disable(void)
+*/
+ ENTRY(dtrace_interrupt_disable)
+ pushfq
+ popq %rax
+ cli
+ ret
+ END(dtrace_interrupt_disable)
+
+/*
+void
+dtrace_interrupt_enable(dtrace_icookie_t cookie)
+*/
+ ENTRY(dtrace_interrupt_enable)
+ pushq %rdi
+ popfq
+ ret
+ END(dtrace_interrupt_enable)
+
+/*
+ * The panic() and cmn_err() functions invoke vpanic() as a common entry point
+ * into the panic code implemented in panicsys(). vpanic() is responsible
+ * for passing through the format string and arguments, and constructing a
+ * regs structure on the stack into which it saves the current register
+ * values. If we are not dying due to a fatal trap, these registers will
+ * then be preserved in panicbuf as the current processor state. Before
+ * invoking panicsys(), vpanic() activates the first panic trigger (see
+ * common/os/panic.c) and switches to the panic_stack if successful. Note that
+ * DTrace takes a slightly different panic path if it must panic from probe
+ * context. Instead of calling panic, it calls into dtrace_vpanic(), which
+ * sets up the initial stack as vpanic does, calls dtrace_panic_trigger(), and
+ * branches back into vpanic().
+ */
+
+/*
+void
+vpanic(const char *format, va_list alist)
+*/
+ ENTRY(vpanic) /* Initial stack layout: */
+
+ pushq %rbp /* | %rip | 0x60 */
+ movq %rsp, %rbp /* | %rbp | 0x58 */
+ pushfq /* | rfl | 0x50 */
+ pushq %r11 /* | %r11 | 0x48 */
+ pushq %r10 /* | %r10 | 0x40 */
+ pushq %rbx /* | %rbx | 0x38 */
+ pushq %rax /* | %rax | 0x30 */
+ pushq %r9 /* | %r9 | 0x28 */
+ pushq %r8 /* | %r8 | 0x20 */
+ pushq %rcx /* | %rcx | 0x18 */
+ pushq %rdx /* | %rdx | 0x10 */
+ pushq %rsi /* | %rsi | 0x8 alist */
+ pushq %rdi /* | %rdi | 0x0 format */
+
+ movq %rsp, %rbx /* %rbx = current %rsp */
+
+ leaq panic_quiesce(%rip), %rdi /* %rdi = &panic_quiesce */
+ call panic_trigger /* %eax = panic_trigger() */
+
+vpanic_common:
+ /*
+ * The panic_trigger result is in %eax from the call above, and
+ * dtrace_panic places it in %eax before branching here.
+ * The rdmsr instructions that follow below will clobber %eax so
+ * we stash the panic_trigger result in %r11d.
+ */
+ movl %eax, %r11d
+ cmpl $0, %r11d
+ je 0f
+
+ /*
+ * If panic_trigger() was successful, we are the first to initiate a
+ * panic: we now switch to the reserved panic_stack before continuing.
+ */
+ leaq panic_stack(%rip), %rsp
+ addq $PANICSTKSIZE, %rsp
+0: subq $REGSIZE, %rsp
+ /*
+ * Now that we've got everything set up, store the register values as
+ * they were when we entered vpanic() to the designated location in
+ * the regs structure we allocated on the stack.
+ */
+#ifdef notyet
+ movq 0x0(%rbx), %rcx
+ movq %rcx, REGOFF_RDI(%rsp)
+ movq 0x8(%rbx), %rcx
+ movq %rcx, REGOFF_RSI(%rsp)
+ movq 0x10(%rbx), %rcx
+ movq %rcx, REGOFF_RDX(%rsp)
+ movq 0x18(%rbx), %rcx
+ movq %rcx, REGOFF_RCX(%rsp)
+ movq 0x20(%rbx), %rcx
+
+ movq %rcx, REGOFF_R8(%rsp)
+ movq 0x28(%rbx), %rcx
+ movq %rcx, REGOFF_R9(%rsp)
+ movq 0x30(%rbx), %rcx
+ movq %rcx, REGOFF_RAX(%rsp)
+ movq 0x38(%rbx), %rcx
+ movq %rcx, REGOFF_RBX(%rsp)
+ movq 0x58(%rbx), %rcx
+
+ movq %rcx, REGOFF_RBP(%rsp)
+ movq 0x40(%rbx), %rcx
+ movq %rcx, REGOFF_R10(%rsp)
+ movq 0x48(%rbx), %rcx
+ movq %rcx, REGOFF_R11(%rsp)
+ movq %r12, REGOFF_R12(%rsp)
+
+ movq %r13, REGOFF_R13(%rsp)
+ movq %r14, REGOFF_R14(%rsp)
+ movq %r15, REGOFF_R15(%rsp)
+
+ xorl %ecx, %ecx
+ movw %ds, %cx
+ movq %rcx, REGOFF_DS(%rsp)
+ movw %es, %cx
+ movq %rcx, REGOFF_ES(%rsp)
+ movw %fs, %cx
+ movq %rcx, REGOFF_FS(%rsp)
+ movw %gs, %cx
+ movq %rcx, REGOFF_GS(%rsp)
+
+ movq $0, REGOFF_TRAPNO(%rsp)
+
+ movq $0, REGOFF_ERR(%rsp)
+ leaq vpanic(%rip), %rcx
+ movq %rcx, REGOFF_RIP(%rsp)
+ movw %cs, %cx
+ movzwq %cx, %rcx
+ movq %rcx, REGOFF_CS(%rsp)
+ movq 0x50(%rbx), %rcx
+ movq %rcx, REGOFF_RFL(%rsp)
+ movq %rbx, %rcx
+ addq $0x60, %rcx
+ movq %rcx, REGOFF_RSP(%rsp)
+ movw %ss, %cx
+ movzwq %cx, %rcx
+ movq %rcx, REGOFF_SS(%rsp)
+
+ /*
+ * panicsys(format, alist, rp, on_panic_stack)
+ */
+ movq REGOFF_RDI(%rsp), %rdi /* format */
+ movq REGOFF_RSI(%rsp), %rsi /* alist */
+ movq %rsp, %rdx /* struct regs */
+ movl %r11d, %ecx /* on_panic_stack */
+ call panicsys
+ addq $REGSIZE, %rsp
+#endif
+ popq %rdi
+ popq %rsi
+ popq %rdx
+ popq %rcx
+ popq %r8
+ popq %r9
+ popq %rax
+ popq %rbx
+ popq %r10
+ popq %r11
+ popfq
+ leave
+ ret
+ END(vpanic)
+
+/*
+void
+dtrace_vpanic(const char *format, va_list alist)
+*/
+ ENTRY(dtrace_vpanic) /* Initial stack layout: */
+
+ pushq %rbp /* | %rip | 0x60 */
+ movq %rsp, %rbp /* | %rbp | 0x58 */
+ pushfq /* | rfl | 0x50 */
+ pushq %r11 /* | %r11 | 0x48 */
+ pushq %r10 /* | %r10 | 0x40 */
+ pushq %rbx /* | %rbx | 0x38 */
+ pushq %rax /* | %rax | 0x30 */
+ pushq %r9 /* | %r9 | 0x28 */
+ pushq %r8 /* | %r8 | 0x20 */
+ pushq %rcx /* | %rcx | 0x18 */
+ pushq %rdx /* | %rdx | 0x10 */
+ pushq %rsi /* | %rsi | 0x8 alist */
+ pushq %rdi /* | %rdi | 0x0 format */
+
+ movq %rsp, %rbx /* %rbx = current %rsp */
+
+ leaq panic_quiesce(%rip), %rdi /* %rdi = &panic_quiesce */
+ call dtrace_panic_trigger /* %eax = dtrace_panic_trigger() */
+ jmp vpanic_common
+
+ END(dtrace_vpanic)
+
+/*
+int
+panic_trigger(int *tp)
+*/
+ ENTRY(panic_trigger)
+ xorl %eax, %eax
+ movl $0xdefacedd, %edx
+ lock
+ xchgl %edx, (%rdi)
+ cmpl $0, %edx
+ je 0f
+ movl $0, %eax
+ ret
+0: movl $1, %eax
+ ret
+ END(panic_trigger)
+
+/*
+int
+dtrace_panic_trigger(int *tp)
+*/
+ ENTRY(dtrace_panic_trigger)
+ xorl %eax, %eax
+ movl $0xdefacedd, %edx
+ lock
+ xchgl %edx, (%rdi)
+ cmpl $0, %edx
+ je 0f
+ movl $0, %eax
+ ret
+0: movl $1, %eax
+ ret
+ END(dtrace_panic_trigger)
diff --git a/sys/cddl/dev/dtrace/amd64/dtrace_isa.c b/sys/cddl/dev/dtrace/amd64/dtrace_isa.c
new file mode 100644
index 000000000000..8cd2a0ff649d
--- /dev/null
+++ b/sys/cddl/dev/dtrace/amd64/dtrace_isa.c
@@ -0,0 +1,612 @@
+/*
+ * 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 <machine/stack.h>
+
+#include <vm/vm.h>
+#include <vm/vm_param.h>
+#include <vm/pmap.h>
+
+extern uintptr_t kernbase;
+uintptr_t kernelbase = (uintptr_t) &kernbase;
+
+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)
+{
+ int depth = 0;
+ register_t rbp;
+ struct amd64_frame *frame;
+ vm_offset_t callpc;
+ pc_t caller = (pc_t) solaris_cpu[curcpu].cpu_dtrace_caller;
+
+ if (intrpc != 0)
+ pcstack[depth++] = (pc_t) intrpc;
+
+ aframes++;
+
+ __asm __volatile("movq %%rbp,%0" : "=r" (rbp));
+
+ frame = (struct amd64_frame *)rbp;
+ while (depth < pcstack_limit) {
+ if (!INKERNEL((long) frame))
+ break;
+
+ callpc = frame->f_retaddr;
+
+ if (!INKERNEL(callpc))
+ break;
+
+ if (aframes > 0) {
+ aframes--;
+ if ((aframes == 0) && (caller != 0)) {
+ pcstack[depth++] = caller;
+ }
+ }
+ else {
+ pcstack[depth++] = callpc;
+ }
+
+ if (frame->f_frame <= frame ||
+ (vm_offset_t)frame->f_frame >=
+ (vm_offset_t)rbp + KSTACK_PAGES * PAGE_SIZE)
+ break;
+ frame = frame->f_frame;
+ }
+
+ 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)
+{
+ volatile uint16_t *flags =
+ (volatile uint16_t *)&cpu_core[curcpu].cpuc_dtrace_flags;
+ struct amd64_frame *frame;
+ int ret = 0;
+
+ ASSERT(pcstack == NULL || pcstack_limit > 0);
+
+ while (pc != 0 && sp != 0) {
+ ret++;
+ if (pcstack != NULL) {
+ *pcstack++ = (uint64_t)pc;
+ pcstack_limit--;
+ if (pcstack_limit <= 0)
+ break;
+ }
+
+ frame = (struct amd64_frame *) sp;
+
+ pc = dtrace_fulword(&frame->f_retaddr);
+ sp = dtrace_fulword(&frame->f_frame);
+
+ /*
+ * 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;
+ }
+ }
+
+ 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->tf_rip;
+ sp = tf->tf_rsp;
+
+ if (DTRACE_CPUFLAG_ISSET(CPU_DTRACE_ENTRY)) {
+ *pcstack++ = (uint64_t)pc;
+ pcstack_limit--;
+ if (pcstack_limit <= 0)
+ return;
+
+ pc = dtrace_fulword((void *) sp);
+ }
+
+ 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->tf_rip;
+ sp = tf->tf_rsp;
+
+ if (DTRACE_CPUFLAG_ISSET(CPU_DTRACE_ENTRY)) {
+ n++;
+
+ pc = dtrace_fulword((void *) sp);
+ }
+
+ n += dtrace_getustack_common(NULL, 0, pc, sp);
+
+ return (n);
+}
+
+#ifdef notyet
+void
+dtrace_getufpstack(uint64_t *pcstack, uint64_t *fpstack, int pcstack_limit)
+{
+ klwp_t *lwp = ttolwp(curthread);
+ proc_t *p = curproc;
+ struct regs *rp;
+ uintptr_t pc, sp, oldcontext;
+ volatile uint16_t *flags =
+ (volatile uint16_t *)&cpu_core[curcpu].cpuc_dtrace_flags;
+ size_t s1, s2;
+
+ 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 (lwp == NULL || p == NULL || (rp = lwp->lwp_regs) == NULL)
+ goto zero;
+
+ *pcstack++ = (uint64_t)p->p_pid;
+ pcstack_limit--;
+
+ if (pcstack_limit <= 0)
+ return;
+
+ pc = rp->r_pc;
+ sp = rp->r_fp;
+ oldcontext = lwp->lwp_oldcontext;
+
+ s1 = sizeof (struct xframe) + 2 * sizeof (long);
+ s2 = s1 + sizeof (siginfo_t);
+
+ if (DTRACE_CPUFLAG_ISSET(CPU_DTRACE_ENTRY)) {
+ *pcstack++ = (uint64_t)pc;
+ *fpstack++ = 0;
+ pcstack_limit--;
+ if (pcstack_limit <= 0)
+ return;
+
+ if (p->p_model == DATAMODEL_NATIVE)
+ pc = dtrace_fulword((void *)rp->r_sp);
+ else
+ pc = dtrace_fuword32((void *)rp->r_sp);
+ }
+
+ while (pc != 0 && sp != 0) {
+ *pcstack++ = (uint64_t)pc;
+ *fpstack++ = sp;
+ pcstack_limit--;
+ if (pcstack_limit <= 0)
+ break;
+
+ 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 {
+ struct xframe *fr = (struct xframe *)sp;
+
+ pc = dtrace_fulword(&fr->fr_savpc);
+ sp = dtrace_fulword(&fr->fr_savfp);
+ }
+
+ /*
+ * 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++ = NULL;
+}
+#endif
+
+/*ARGSUSED*/
+uint64_t
+dtrace_getarg(int arg, int aframes)
+{
+ uintptr_t val;
+ struct amd64_frame *fp = (struct amd64_frame *)dtrace_getfp();
+ uintptr_t *stack;
+ int i;
+
+ /*
+ * A total of 6 arguments are passed via registers; any argument with
+ * index of 5 or lower is therefore in a register.
+ */
+ int inreg = 5;
+
+ for (i = 1; i <= aframes; i++) {
+ fp = fp->f_frame;
+
+ if (fp->f_retaddr == (long)dtrace_invop_callsite) {
+ /*
+ * In the case of amd64, 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, and then again beyond
+ * the calling RIP stored in dtrace_invop(). 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.
+ */
+ struct reg *rp = (struct reg *)((uintptr_t)&fp[1] +
+ sizeof (uintptr_t));
+
+ if (arg <= inreg) {
+ stack = (uintptr_t *)&rp->r_rdi;
+ } else {
+ stack = (uintptr_t *)(rp->r_rsp);
+ 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 = (uintptr_t *)&fp[1];
+
+load:
+ DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT);
+ val = stack[arg];
+ DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT);
+
+ return (val);
+ return (0);
+}
+
+int
+dtrace_getstackdepth(int aframes)
+{
+ int depth = 0;
+ struct amd64_frame *frame;
+ vm_offset_t rbp;
+
+ aframes++;
+ rbp = dtrace_getfp();
+ frame = (struct amd64_frame *)rbp;
+ depth++;
+ for(;;) {
+ if (!INKERNEL((long) frame))
+ break;
+ if (!INKERNEL((long) frame->f_frame))
+ break;
+ depth++;
+ if (frame->f_frame <= frame ||
+ (vm_offset_t)frame->f_frame >=
+ (vm_offset_t)rbp + KSTACK_PAGES * PAGE_SIZE)
+ break;
+ frame = frame->f_frame;
+ }
+ if (depth < aframes)
+ return 0;
+ else
+ return depth - aframes;
+}
+
+#ifdef notyet
+ulong_t
+dtrace_getreg(struct regs *rp, uint_t reg)
+{
+#if defined(__amd64)
+ int regmap[] = {
+ REG_GS, /* GS */
+ REG_FS, /* FS */
+ REG_ES, /* ES */
+ REG_DS, /* DS */
+ REG_RDI, /* EDI */
+ REG_RSI, /* ESI */
+ REG_RBP, /* EBP */
+ REG_RSP, /* ESP */
+ REG_RBX, /* EBX */
+ REG_RDX, /* EDX */
+ REG_RCX, /* ECX */
+ REG_RAX, /* EAX */
+ REG_TRAPNO, /* TRAPNO */
+ REG_ERR, /* ERR */
+ REG_RIP, /* EIP */
+ REG_CS, /* CS */
+ REG_RFL, /* EFL */
+ REG_RSP, /* UESP */
+ REG_SS /* SS */
+ };
+
+ if (reg <= SS) {
+ if (reg >= sizeof (regmap) / sizeof (int)) {
+ DTRACE_CPUFLAG_SET(CPU_DTRACE_ILLOP);
+ return (0);
+ }
+
+ reg = regmap[reg];
+ } else {
+ reg -= SS + 1;
+ }
+
+ switch (reg) {
+ case REG_RDI:
+ return (rp->r_rdi);
+ case REG_RSI:
+ return (rp->r_rsi);
+ case REG_RDX:
+ return (rp->r_rdx);
+ case REG_RCX:
+ return (rp->r_rcx);
+ case REG_R8:
+ return (rp->r_r8);
+ case REG_R9:
+ return (rp->r_r9);
+ case REG_RAX:
+ return (rp->r_rax);
+ case REG_RBX:
+ return (rp->r_rbx);
+ case REG_RBP:
+ return (rp->r_rbp);
+ case REG_R10:
+ return (rp->r_r10);
+ case REG_R11:
+ return (rp->r_r11);
+ case REG_R12:
+ return (rp->r_r12);
+ case REG_R13:
+ return (rp->r_r13);
+ case REG_R14:
+ return (rp->r_r14);
+ case REG_R15:
+ return (rp->r_r15);
+ case REG_DS:
+ return (rp->r_ds);
+ case REG_ES:
+ return (rp->r_es);
+ case REG_FS:
+ return (rp->r_fs);
+ case REG_GS:
+ return (rp->r_gs);
+ case REG_TRAPNO:
+ return (rp->r_trapno);
+ case REG_ERR:
+ return (rp->r_err);
+ case REG_RIP:
+ return (rp->r_rip);
+ case REG_CS:
+ return (rp->r_cs);
+ case REG_SS:
+ return (rp->r_ss);
+ case REG_RFL:
+ return (rp->r_rfl);
+ case REG_RSP:
+ return (rp->r_rsp);
+ default:
+ DTRACE_CPUFLAG_SET(CPU_DTRACE_ILLOP);
+ return (0);
+ }
+
+#else
+ if (reg > SS) {
+ DTRACE_CPUFLAG_SET(CPU_DTRACE_ILLOP);
+ return (0);
+ }
+
+ return ((&rp->r_gs)[reg]);
+#endif
+}
+#endif
+
+static int
+dtrace_copycheck(uintptr_t uaddr, uintptr_t kaddr, size_t size)
+{
+ ASSERT(kaddr >= kernelbase && kaddr + size >= kaddr);
+
+ if (uaddr + size >= kernelbase || 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 >= kernelbase) {
+ 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 >= kernelbase) {
+ 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 >= kernelbase) {
+ 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 >= kernelbase) {
+ DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
+ cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
+ return (0);
+ }
+ return (dtrace_fuword64_nocheck(uaddr));
+}
diff --git a/sys/cddl/dev/dtrace/amd64/dtrace_subr.c b/sys/cddl/dev/dtrace/amd64/dtrace_subr.c
new file mode 100644
index 000000000000..4bf0f039ec9b
--- /dev/null
+++ b/sys/cddl/dev/dtrace/amd64/dtrace_subr.c
@@ -0,0 +1,507 @@
+/*
+ * 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/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 <vm/pmap.h>
+
+extern uintptr_t kernelbase;
+extern uintptr_t dtrace_in_probe_addr;
+extern int dtrace_in_probe;
+
+int dtrace_invop(uintptr_t, uintptr_t *, uintptr_t);
+
+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))
+{
+ (*func)(0, (uintptr_t) addr_PTmap);
+}
+
+void
+dtrace_xcall(processorid_t cpu, dtrace_xcall_t func, void *arg)
+{
+ cpumask_t cpus;
+
+ critical_enter();
+
+ if (cpu == DTRACE_CPUALL)
+ cpus = all_cpus;
+ else
+ cpus = (cpumask_t) (1 << cpu);
+
+ /* If the current CPU is in the set, call the function directly: */
+ if ((cpus & (1 << curcpu)) != 0) {
+ (*func)(arg);
+
+ /* Mask the current CPU from the set */
+ cpus &= ~(1 << curcpu);
+ }
+
+ /* If there are any CPUs in the set, cross-call to those CPUs */
+ if (cpus != 0)
+ smp_rendezvous_cpus(cpus, NULL, func, smp_no_rendevous_barrier, arg);
+
+ critical_exit();
+}
+
+static void
+dtrace_sync_func(void)
+{
+}
+
+void
+dtrace_sync(void)
+{
+ dtrace_xcall(DTRACE_CPUALL, (dtrace_xcall_t)dtrace_sync_func, NULL);
+}
+
+#ifdef notyet
+int (*dtrace_fasttrap_probe_ptr)(struct regs *);
+int (*dtrace_pid_probe_ptr)(struct regs *);
+int (*dtrace_return_probe_ptr)(struct regs *);
+
+void
+dtrace_user_probe(struct regs *rp, caddr_t addr, processorid_t cpuid)
+{
+ krwlock_t *rwp;
+ proc_t *p = curproc;
+ extern void trap(struct regs *, caddr_t, processorid_t);
+
+ if (USERMODE(rp->r_cs) || (rp->r_ps & PS_VM)) {
+ if (curthread->t_cred != p->p_cred) {
+ cred_t *oldcred = curthread->t_cred;
+ /*
+ * DTrace accesses t_cred in probe context. t_cred
+ * must always be either NULL, or point to a valid,
+ * allocated cred structure.
+ */
+ curthread->t_cred = crgetcred();
+ crfree(oldcred);
+ }
+ }
+
+ if (rp->r_trapno == T_DTRACE_RET) {
+ uint8_t step = curthread->t_dtrace_step;
+ uint8_t ret = curthread->t_dtrace_ret;
+ uintptr_t npc = curthread->t_dtrace_npc;
+
+ if (curthread->t_dtrace_ast) {
+ aston(curthread);
+ curthread->t_sig_check = 1;
+ }
+
+ /*
+ * Clear all user tracing flags.
+ */
+ curthread->t_dtrace_ft = 0;
+
+ /*
+ * If we weren't expecting to take a return probe trap, kill
+ * the process as though it had just executed an unassigned
+ * trap instruction.
+ */
+ if (step == 0) {
+ tsignal(curthread, SIGILL);
+ return;
+ }
+
+ /*
+ * If we hit this trap unrelated to a return probe, we're
+ * just here to reset the AST flag since we deferred a signal
+ * until after we logically single-stepped the instruction we
+ * copied out.
+ */
+ if (ret == 0) {
+ rp->r_pc = npc;
+ return;
+ }
+
+ /*
+ * We need to wait until after we've called the
+ * dtrace_return_probe_ptr function pointer to set %pc.
+ */
+ rwp = &CPU->cpu_ft_lock;
+ rw_enter(rwp, RW_READER);
+ if (dtrace_return_probe_ptr != NULL)
+ (void) (*dtrace_return_probe_ptr)(rp);
+ rw_exit(rwp);
+ rp->r_pc = npc;
+
+ } else if (rp->r_trapno == T_DTRACE_PROBE) {
+ rwp = &CPU->cpu_ft_lock;
+ rw_enter(rwp, RW_READER);
+ if (dtrace_fasttrap_probe_ptr != NULL)
+ (void) (*dtrace_fasttrap_probe_ptr)(rp);
+ rw_exit(rwp);
+
+ } else if (rp->r_trapno == T_BPTFLT) {
+ uint8_t instr;
+ rwp = &CPU->cpu_ft_lock;
+
+ /*
+ * The DTrace fasttrap provider uses the breakpoint trap
+ * (int 3). We let DTrace take the first crack at handling
+ * this trap; if it's not a probe that DTrace knowns about,
+ * we call into the trap() routine to handle it like a
+ * breakpoint placed by a conventional debugger.
+ */
+ rw_enter(rwp, RW_READER);
+ if (dtrace_pid_probe_ptr != NULL &&
+ (*dtrace_pid_probe_ptr)(rp) == 0) {
+ rw_exit(rwp);
+ return;
+ }
+ rw_exit(rwp);
+
+ /*
+ * If the instruction that caused the breakpoint trap doesn't
+ * look like an int 3 anymore, it may be that this tracepoint
+ * was removed just after the user thread executed it. In
+ * that case, return to user land to retry the instuction.
+ */
+ if (fuword8((void *)(rp->r_pc - 1), &instr) == 0 &&
+ instr != FASTTRAP_INSTR) {
+ rp->r_pc--;
+ return;
+ }
+
+ trap(rp, addr, cpuid);
+
+ } else {
+ trap(rp, addr, cpuid);
+ }
+}
+
+void
+dtrace_safe_synchronous_signal(void)
+{
+ kthread_t *t = curthread;
+ struct regs *rp = lwptoregs(ttolwp(t));
+ size_t isz = t->t_dtrace_npc - t->t_dtrace_pc;
+
+ ASSERT(t->t_dtrace_on);
+
+ /*
+ * If we're not in the range of scratch addresses, we're not actually
+ * tracing user instructions so turn off the flags. If the instruction
+ * we copied out caused a synchonous trap, reset the pc back to its
+ * original value and turn off the flags.
+ */
+ if (rp->r_pc < t->t_dtrace_scrpc ||
+ rp->r_pc > t->t_dtrace_astpc + isz) {
+ t->t_dtrace_ft = 0;
+ } else if (rp->r_pc == t->t_dtrace_scrpc ||
+ rp->r_pc == t->t_dtrace_astpc) {
+ rp->r_pc = t->t_dtrace_pc;
+ t->t_dtrace_ft = 0;
+ }
+}
+
+int
+dtrace_safe_defer_signal(void)
+{
+ kthread_t *t = curthread;
+ struct regs *rp = lwptoregs(ttolwp(t));
+ size_t isz = t->t_dtrace_npc - t->t_dtrace_pc;
+
+ ASSERT(t->t_dtrace_on);
+
+ /*
+ * If we're not in the range of scratch addresses, we're not actually
+ * tracing user instructions so turn off the flags.
+ */
+ if (rp->r_pc < t->t_dtrace_scrpc ||
+ rp->r_pc > t->t_dtrace_astpc + isz) {
+ t->t_dtrace_ft = 0;
+ return (0);
+ }
+
+ /*
+ * If we've executed the original instruction, but haven't performed
+ * the jmp back to t->t_dtrace_npc or the clean up of any registers
+ * used to emulate %rip-relative instructions in 64-bit mode, do that
+ * here and take the signal right away. We detect this condition by
+ * seeing if the program counter is the range [scrpc + isz, astpc).
+ */
+ if (t->t_dtrace_astpc - rp->r_pc <
+ t->t_dtrace_astpc - t->t_dtrace_scrpc - isz) {
+#ifdef __amd64
+ /*
+ * If there is a scratch register and we're on the
+ * instruction immediately after the modified instruction,
+ * restore the value of that scratch register.
+ */
+ if (t->t_dtrace_reg != 0 &&
+ rp->r_pc == t->t_dtrace_scrpc + isz) {
+ switch (t->t_dtrace_reg) {
+ case REG_RAX:
+ rp->r_rax = t->t_dtrace_regv;
+ break;
+ case REG_RCX:
+ rp->r_rcx = t->t_dtrace_regv;
+ break;
+ case REG_R8:
+ rp->r_r8 = t->t_dtrace_regv;
+ break;
+ case REG_R9:
+ rp->r_r9 = t->t_dtrace_regv;
+ break;
+ }
+ }
+#endif
+ rp->r_pc = t->t_dtrace_npc;
+ t->t_dtrace_ft = 0;
+ return (0);
+ }
+
+ /*
+ * Otherwise, make sure we'll return to the kernel after executing
+ * the copied out instruction and defer the signal.
+ */
+ if (!t->t_dtrace_step) {
+ ASSERT(rp->r_pc < t->t_dtrace_astpc);
+ rp->r_pc += t->t_dtrace_astpc - t->t_dtrace_scrpc;
+ t->t_dtrace_step = 1;
+ }
+
+ t->t_dtrace_ast = 1;
+
+ return (1);
+}
+#endif
+
+static int64_t tgt_cpu_tsc;
+static int64_t hst_cpu_tsc;
+static int64_t tsc_skew[MAXCPU];
+
+static void
+dtrace_gethrtime_init_sync(void *arg)
+{
+#ifdef CHECK_SYNC
+ /*
+ * Delay this function from returning on one
+ * of the CPUs to check that the synchronisation
+ * works.
+ */
+ uintptr_t cpu = (uintptr_t) arg;
+
+ if (cpu == curcpu) {
+ int i;
+ for (i = 0; i < 1000000000; i++)
+ tgt_cpu_tsc = rdtsc();
+ tgt_cpu_tsc = 0;
+ }
+#endif
+}
+
+static void
+dtrace_gethrtime_init_cpu(void *arg)
+{
+ uintptr_t cpu = (uintptr_t) arg;
+
+ if (cpu == curcpu)
+ tgt_cpu_tsc = rdtsc();
+ else
+ hst_cpu_tsc = rdtsc();
+}
+
+static void
+dtrace_gethrtime_init(void *arg)
+{
+ cpumask_t map;
+ int i;
+ struct pcpu *cp;
+
+ /* The current CPU is the reference one. */
+ tsc_skew[curcpu] = 0;
+
+ for (i = 0; i <= mp_maxid; i++) {
+ if (i == curcpu)
+ continue;
+
+ if ((cp = pcpu_find(i)) == NULL)
+ continue;
+
+ map = 0;
+ map |= (1 << curcpu);
+ map |= (1 << i);
+
+ smp_rendezvous_cpus(map, dtrace_gethrtime_init_sync,
+ dtrace_gethrtime_init_cpu,
+ smp_no_rendevous_barrier, (void *)(uintptr_t) i);
+
+ tsc_skew[i] = tgt_cpu_tsc - hst_cpu_tsc;
+ }
+}
+
+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()
+{
+ return ((rdtsc() + tsc_skew[curcpu]) * (int64_t) 1000000000 / tsc_freq);
+}
+
+uint64_t
+dtrace_gethrestime(void)
+{
+ printf("%s(%d): XXX\n",__func__,__LINE__);
+ return (0);
+}
+
+/* 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 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) {
+ /* Privilieged instruction fault. */
+ case T_PRIVINFLT:
+ break;
+ /* General protection fault. */
+ case T_PROTFLT:
+ /* Flag an illegal operation. */
+ cpu_core[curcpu].cpuc_dtrace_flags |= CPU_DTRACE_ILLOP;
+
+ /*
+ * Offset the instruction pointer to the instruction
+ * following the one causing the fault.
+ */
+ frame->tf_rip += dtrace_instr_size((u_char *) frame->tf_rip);
+ return (1);
+ /* Page fault. */
+ case T_PAGEFLT:
+ /* Flag a bad address. */
+ cpu_core[curcpu].cpuc_dtrace_flags |= CPU_DTRACE_BADADDR;
+ cpu_core[curcpu].cpuc_dtrace_illval = frame->tf_addr;
+
+ /*
+ * Offset the instruction pointer to the instruction
+ * following the one causing the fault.
+ */
+ frame->tf_rip += dtrace_instr_size((u_char *) frame->tf_rip);
+ return (1);
+ default:
+ /* Handle all other traps in the usual way. */
+ break;
+ }
+ }
+
+ /* Handle the trap in the usual way. */
+ return (0);
+}
diff --git a/sys/cddl/dev/dtrace/amd64/instr_size.c b/sys/cddl/dev/dtrace/amd64/instr_size.c
new file mode 100644
index 000000000000..418d9f1fb53b
--- /dev/null
+++ b/sys/cddl/dev/dtrace/amd64/instr_size.c
@@ -0,0 +1,132 @@
+/*
+ * 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.
+ */
+
+/* Copyright (c) 1988 AT&T */
+/* All Rights Reserved */
+
+
+#if defined(sun)
+#pragma ident "@(#)instr_size.c 1.14 05/07/08 SMI"
+#endif
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/proc.h>
+#if defined(sun)
+#include <sys/cmn_err.h>
+#include <sys/archsystm.h>
+#include <sys/copyops.h>
+#include <vm/seg_enum.h>
+#include <sys/privregs.h>
+#else
+typedef u_int model_t;
+#define DATAMODEL_NATIVE 0
+int dtrace_instr_size(uchar_t *);
+#endif
+
+#include <dis_tables.h>
+
+/*
+ * This subsystem (with the minor exception of the instr_size() function) is
+ * is called from DTrace probe context. This imposes several requirements on
+ * the implementation:
+ *
+ * 1. External subsystems and functions may not be referenced. The one current
+ * exception is for cmn_err, but only to signal the detection of table
+ * errors. Assuming the tables are correct, no combination of input is to
+ * trigger a cmn_err call.
+ *
+ * 2. These functions can't be allowed to be traced. To prevent this,
+ * all functions in the probe path (everything except instr_size()) must
+ * have names that begin with "dtrace_".
+ */
+
+typedef enum dis_isize {
+ DIS_ISIZE_INSTR,
+ DIS_ISIZE_OPERAND
+} dis_isize_t;
+
+
+/*
+ * get a byte from instruction stream
+ */
+static int
+dtrace_dis_get_byte(void *p)
+{
+ int ret;
+ uchar_t **instr = p;
+
+ ret = **instr;
+ *instr += 1;
+
+ return (ret);
+}
+
+/*
+ * Returns either the size of a given instruction, in bytes, or the size of that
+ * instruction's memory access (if any), depending on the value of `which'.
+ * If a programming error in the tables is detected, the system will panic to
+ * ease diagnosis. Invalid instructions will not be flagged. They will appear
+ * to have an instruction size between 1 and the actual size, and will be
+ * reported as having no memory impact.
+ */
+/* ARGSUSED2 */
+static int
+dtrace_dis_isize(uchar_t *instr, dis_isize_t which, model_t model, int *rmindex)
+{
+ int sz;
+ dis86_t x;
+ uint_t mode = SIZE64;
+
+#if defined(sun)
+ mode = (model == DATAMODEL_LP64) ? SIZE64 : SIZE32;
+#endif
+
+ x.d86_data = (void **)&instr;
+ x.d86_get_byte = dtrace_dis_get_byte;
+ x.d86_check_func = NULL;
+
+ if (dtrace_disx86(&x, mode) != 0)
+ return (-1);
+
+ if (which == DIS_ISIZE_INSTR)
+ sz = x.d86_len; /* length of the instruction */
+ else
+ sz = x.d86_memsize; /* length of memory operand */
+
+ if (rmindex != NULL)
+ *rmindex = x.d86_rmindex;
+ return (sz);
+}
+
+int
+dtrace_instr_size(uchar_t *instr)
+{
+ return (dtrace_dis_isize(instr, DIS_ISIZE_INSTR, DATAMODEL_NATIVE,
+ NULL));
+}
diff --git a/sys/cddl/dev/dtrace/dtrace_anon.c b/sys/cddl/dev/dtrace/dtrace_anon.c
new file mode 100644
index 000000000000..b81ec5be3f11
--- /dev/null
+++ b/sys/cddl/dev/dtrace/dtrace_anon.c
@@ -0,0 +1,84 @@
+/*
+ * 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$
+ */
+
+/*
+ * DTrace Anonymous Enabling Functions
+ */
+static void
+dtrace_anon_init(void *dummy)
+{
+ dtrace_state_t *state = NULL;
+ dtrace_enabling_t *enab;
+
+ mutex_enter(&cpu_lock);
+ mutex_enter(&dtrace_provider_lock);
+ mutex_enter(&dtrace_lock);
+
+ dtrace_anon_property();
+
+ mutex_exit(&cpu_lock);
+
+ /*
+ * If there are already providers, we must ask them to provide their
+ * probes, and then match any anonymous enabling against them. Note
+ * that there should be no other retained enablings at this time:
+ * the only retained enablings at this time should be the anonymous
+ * enabling.
+ */
+ if (dtrace_anon.dta_enabling != NULL) {
+ ASSERT(dtrace_retained == dtrace_anon.dta_enabling);
+
+ dtrace_enabling_provide(NULL);
+ state = dtrace_anon.dta_state;
+
+ /*
+ * We couldn't hold cpu_lock across the above call to
+ * dtrace_enabling_provide(), but we must hold it to actually
+ * enable the probes. We have to drop all of our locks, pick
+ * up cpu_lock, and regain our locks before matching the
+ * retained anonymous enabling.
+ */
+ mutex_exit(&dtrace_lock);
+ mutex_exit(&dtrace_provider_lock);
+
+ mutex_enter(&cpu_lock);
+ mutex_enter(&dtrace_provider_lock);
+ mutex_enter(&dtrace_lock);
+
+ if ((enab = dtrace_anon.dta_enabling) != NULL)
+ (void) dtrace_enabling_match(enab, NULL);
+
+ mutex_exit(&cpu_lock);
+ }
+
+ mutex_exit(&dtrace_provider_lock);
+ mutex_exit(&dtrace_lock);
+
+ if (state != NULL) {
+ /*
+ * If we created any anonymous state, set it going now.
+ */
+ (void) dtrace_state_go(state, &dtrace_anon.dta_beganon);
+ }
+}
diff --git a/sys/cddl/dev/dtrace/dtrace_cddl.h b/sys/cddl/dev/dtrace/dtrace_cddl.h
new file mode 100644
index 000000000000..75fe8648cc12
--- /dev/null
+++ b/sys/cddl/dev/dtrace/dtrace_cddl.h
@@ -0,0 +1,134 @@
+/*
+ * 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 _DTRACE_CDDL_H_
+#define _DTRACE_CDDL_H_
+
+#include <sys/proc.h>
+
+#define LOCK_LEVEL 10
+
+/*
+ * Kernel DTrace extension to 'struct proc' for FreeBSD.
+ */
+typedef struct kdtrace_proc {
+ int p_dtrace_probes; /* Are there probes for this proc? */
+ u_int64_t p_dtrace_count; /* Number of DTrace tracepoints */
+ void *p_dtrace_helpers; /* DTrace helpers, if any */
+
+} kdtrace_proc_t;
+
+/*
+ * Kernel DTrace extension to 'struct thread' for FreeBSD.
+ */
+typedef struct kdtrace_thread {
+ u_int8_t td_dtrace_stop; /* Indicates a DTrace-desired stop */
+ u_int8_t td_dtrace_sig; /* Signal sent via DTrace's raise() */
+ u_int td_predcache; /* DTrace predicate cache */
+ u_int64_t td_dtrace_vtime; /* DTrace virtual time */
+ u_int64_t td_dtrace_start; /* DTrace slice start time */
+
+ union __tdu {
+ struct __tds {
+ u_int8_t _td_dtrace_on;
+ /* Hit a fasttrap tracepoint. */
+ u_int8_t _td_dtrace_step;
+ /* About to return to kernel. */
+ u_int8_t _td_dtrace_ret;
+ /* Handling a return probe. */
+ u_int8_t _td_dtrace_ast;
+ /* Saved ast flag. */
+ } _tds;
+ u_long _td_dtrace_ft; /* Bitwise or of these flags. */
+ } _tdu;
+#define td_dtrace_ft _tdu._td_dtrace_ft
+#define td_dtrace_on _tdu._tds._td_dtrace_on
+#define td_dtrace_step _tdu._tds._td_dtrace_step
+#define td_dtrace_ret _tdu._tds._td_dtrace_ret
+#define td_dtrace_ast _tdu._tds._td_dtrace_ast
+
+ uintptr_t td_dtrace_pc; /* DTrace saved pc from fasttrap. */
+ uintptr_t td_dtrace_npc; /* DTrace next pc from fasttrap. */
+ uintptr_t td_dtrace_scrpc;
+ /* DTrace per-thread scratch location. */
+ uintptr_t td_dtrace_astpc;
+ /* DTrace return sequence location. */
+ u_int64_t td_hrtime; /* Last time on cpu. */
+ int td_errno; /* Syscall return value. */
+} kdtrace_thread_t;
+
+/*
+ * Definitions to reference fields in the FreeBSD DTrace structures defined
+ * above using the names of fields in similar structures in Solaris. Note
+ * that the separation on FreeBSD is a licensing constraint designed to
+ * keep the GENERIC kernel BSD licensed.
+ */
+#define t_dtrace_vtime td_dtrace->td_dtrace_vtime
+#define t_dtrace_start td_dtrace->td_dtrace_start
+#define t_dtrace_stop td_dtrace->td_dtrace_stop
+#define t_dtrace_sig td_dtrace->td_dtrace_sig
+#define t_predcache td_dtrace->td_predcache
+#define p_dtrace_helpers p_dtrace->p_dtrace_helpers
+
+/*
+ * Definitions for fields in struct proc which are named differntly in FreeBSD.
+ */
+#define p_cred p_ucred
+#define p_parent p_pptr
+
+/*
+ * Definitions for fields in struct thread which are named differntly in FreeBSD.
+ */
+#define t_procp td_proc
+#define t_tid td_tid
+#define t_did td_tid
+
+
+int priv_policy(const cred_t *, int, boolean_t, int, const char *);
+boolean_t priv_policy_only(const cred_t *, int, boolean_t);
+boolean_t priv_policy_choice(const cred_t *, int, boolean_t);
+
+/*
+ * Test privilege. Audit success or failure, allow privilege debugging.
+ * Returns 0 for success, err for failure.
+ */
+#define PRIV_POLICY(cred, priv, all, err, reason) \
+ priv_policy((cred), (priv), (all), (err), (reason))
+
+/*
+ * Test privilege. Audit success only, no privilege debugging.
+ * Returns 1 for success, and 0 for failure.
+ */
+#define PRIV_POLICY_CHOICE(cred, priv, all) \
+ priv_policy_choice((cred), (priv), (all))
+
+/*
+ * Test privilege. No priv_debugging, no auditing.
+ * Returns 1 for success, and 0 for failure.
+ */
+
+#define PRIV_POLICY_ONLY(cred, priv, all) \
+ priv_policy_only((cred), (priv), (all))
+
+#endif /* !_DTRACE_CDDL_H_ */
diff --git a/sys/cddl/dev/dtrace/dtrace_clone.c b/sys/cddl/dev/dtrace/dtrace_clone.c
new file mode 100644
index 000000000000..52e48ff1bd79
--- /dev/null
+++ b/sys/cddl/dev/dtrace/dtrace_clone.c
@@ -0,0 +1,61 @@
+/*-
+ * Copyright (C) 2006 John Birrell <jb@freebsd.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice(s), this list of conditions and the following disclaimer as
+ * the first lines of this file unmodified other than the possible
+ * addition of one or more copyright notices.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice(s), this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * $FreeBSD$
+ *
+ */
+
+static void
+dtrace_clone(void *arg, struct ucred *cred, char *name, int namelen, struct cdev **dev)
+{
+ int u = -1;
+ size_t len;
+
+ if (*dev != NULL)
+ return;
+
+ len = strlen(name);
+
+ if (len != 6 && len != 13)
+ return;
+
+ if (bcmp(name,"dtrace",6) != 0)
+ return;
+
+ if (len == 13 && bcmp(name,"dtrace/dtrace",13) != 0)
+ return;
+
+ /* Clone the device to the new minor number. */
+ if (clone_create(&dtrace_clones, &dtrace_cdevsw, &u, dev, 0) != 0)
+ /* Create the /dev/dtrace/dtraceNN entry. */
+ *dev = make_dev_cred(&dtrace_cdevsw, unit2minor(u), cred,
+ UID_ROOT, GID_WHEEL, 0600, "dtrace/dtrace%d", u);
+ if (*dev != NULL) {
+ dev_ref(*dev);
+ (*dev)->si_flags |= SI_CHEAPCLONE;
+ }
+}
diff --git a/sys/cddl/dev/dtrace/dtrace_debug.c b/sys/cddl/dev/dtrace/dtrace_debug.c
new file mode 100644
index 000000000000..24a7a09a0dfc
--- /dev/null
+++ b/sys/cddl/dev/dtrace/dtrace_debug.c
@@ -0,0 +1,596 @@
+/*-
+ * Copyright (C) 2008 John Birrell <jb@freebsd.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice(s), this list of conditions and the following disclaimer as
+ * the first lines of this file unmodified other than the possible
+ * addition of one or more copyright notices.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice(s), this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * $FreeBSD$
+ *
+ */
+
+#ifdef DEBUG
+
+#if defined(__amd64__)
+static __inline int
+dtrace_cmpset_long(volatile u_long *dst, u_long exp, u_long src)
+{
+ u_char res;
+
+ __asm __volatile(
+ " lock ; "
+ " cmpxchgq %2,%1 ; "
+ " sete %0 ; "
+ "1: "
+ "# dtrace_cmpset_long"
+ : "=a" (res), /* 0 */
+ "=m" (*dst) /* 1 */
+ : "r" (src), /* 2 */
+ "a" (exp), /* 3 */
+ "m" (*dst) /* 4 */
+ : "memory");
+
+ return (res);
+}
+#elif defined(__i386__)
+static __inline int
+dtrace_cmpset_long(volatile u_long *dst, u_long exp, u_long src)
+{
+ u_char res;
+
+ __asm __volatile(
+ " lock ; "
+ " cmpxchgl %2,%1 ; "
+ " sete %0 ; "
+ "1: "
+ "# dtrace_cmpset_long"
+ : "=a" (res), /* 0 */
+ "=m" (*dst) /* 1 */
+ : "r" (src), /* 2 */
+ "a" (exp), /* 3 */
+ "m" (*dst) /* 4 */
+ : "memory");
+
+ return (res);
+}
+#endif
+
+#define DTRACE_DEBUG_BUFR_SIZE (32 * 1024)
+
+struct dtrace_debug_data {
+ char bufr[DTRACE_DEBUG_BUFR_SIZE];
+ char *first;
+ char *last;
+ char *next;
+} dtrace_debug_data[MAXCPU];
+
+static char dtrace_debug_bufr[DTRACE_DEBUG_BUFR_SIZE];
+
+static volatile u_long dtrace_debug_flag[MAXCPU];
+
+static void
+dtrace_debug_lock(int cpu)
+{
+ while (dtrace_cmpset_long(&dtrace_debug_flag[cpu], 0, 1) == 0)
+ /* Loop until the lock is obtained. */
+ ;
+}
+
+static void
+dtrace_debug_unlock(int cpu)
+{
+ dtrace_debug_flag[cpu] = 0;
+}
+
+static void
+dtrace_debug_init(void *dummy)
+{
+ int i;
+ struct dtrace_debug_data *d;
+
+ for (i = 0; i <= mp_maxid; i++) {
+ if (pcpu_find(i) == NULL)
+ continue;
+
+ d = &dtrace_debug_data[i];
+
+ if (d->first == NULL) {
+ d->first = d->bufr;
+ d->next = d->bufr;
+ d->last = d->bufr + DTRACE_DEBUG_BUFR_SIZE - 1;
+ *(d->last) = '\0';
+ }
+ }
+}
+
+SYSINIT(dtrace_debug_init, SI_SUB_KDTRACE, SI_ORDER_ANY, dtrace_debug_init, NULL);
+SYSINIT(dtrace_debug_smpinit, SI_SUB_SMP, SI_ORDER_ANY, dtrace_debug_init, NULL);
+
+static void
+dtrace_debug_output(void)
+{
+ char *p;
+ int i;
+ struct dtrace_debug_data *d;
+ uintptr_t count;
+
+ for (i = 0; i <= mp_maxid; i++) {
+ if (pcpu_find(i) == NULL)
+ continue;
+
+ dtrace_debug_lock(i);
+
+ d = &dtrace_debug_data[i];
+
+ count = 0;
+
+ if (d->first < d->next) {
+ char *p1 = dtrace_debug_bufr;
+
+ count = (uintptr_t) d->next - (uintptr_t) d->first;
+
+ for (p = d->first; p < d->next; p++)
+ *p1++ = *p;
+ } else if (d->next > d->first) {
+ char *p1 = dtrace_debug_bufr;
+
+ count = (uintptr_t) d->last - (uintptr_t) d->first;
+
+ for (p = d->first; p < d->last; p++)
+ *p1++ = *p;
+
+ count += (uintptr_t) d->next - (uintptr_t) d->bufr;
+
+ for (p = d->bufr; p < d->next; p++)
+ *p1++ = *p;
+ }
+
+ d->first = d->bufr;
+ d->next = d->bufr;
+
+ dtrace_debug_unlock(i);
+
+ if (count > 0) {
+ char *last = dtrace_debug_bufr + count;
+
+ p = dtrace_debug_bufr;
+
+ while (p < last) {
+ if (*p == '\0') {
+ p++;
+ continue;
+ }
+
+ printf("%s", p);
+
+ p += strlen(p);
+ }
+ }
+ }
+}
+
+/*
+ * Functions below here are called from the probe context, so they can't call
+ * _any_ functions outside the dtrace module without running foul of the function
+ * boundary trace provider (fbt). The purpose of these functions is limited to
+ * buffering debug strings for output when the probe completes on the current CPU.
+ */
+
+static __inline void
+dtrace_debug__putc(char c)
+{
+ struct dtrace_debug_data *d = &dtrace_debug_data[curcpu];
+
+ *d->next++ = c;
+
+ if (d->next == d->last)
+ d->next = d->bufr;
+
+ *(d->next) = '\0';
+
+ if (d->next == d->first)
+ d->first++;
+
+ if (d->first == d->last)
+ d->first = d->bufr;
+}
+
+static void __used
+dtrace_debug_putc(char c)
+{
+ dtrace_debug_lock(curcpu);
+
+ dtrace_debug__putc(c);
+
+ dtrace_debug_unlock(curcpu);
+}
+
+static void __used
+dtrace_debug_puts(const char *s)
+{
+ dtrace_debug_lock(curcpu);
+
+ while (*s != '\0')
+ dtrace_debug__putc(*s++);
+
+ dtrace_debug__putc('\0');
+
+ dtrace_debug_unlock(curcpu);
+}
+
+/*
+ * Snaffled from sys/kern/subr_prf.c
+ *
+ * Put a NUL-terminated ASCII number (base <= 36) in a buffer in reverse
+ * order; return an optional length and a pointer to the last character
+ * written in the buffer (i.e., the first character of the string).
+ * The buffer pointed to by `nbuf' must have length >= MAXNBUF.
+ */
+static char *
+dtrace_debug_ksprintn(char *nbuf, uintmax_t num, int base, int *lenp, int upper)
+{
+ char *p, c;
+
+ p = nbuf;
+ *p = '\0';
+ do {
+ c = hex2ascii(num % base);
+ *++p = upper ? toupper(c) : c;
+ } while (num /= base);
+ if (lenp)
+ *lenp = p - nbuf;
+ return (p);
+}
+
+#define MAXNBUF (sizeof(intmax_t) * NBBY + 1)
+
+static void
+dtrace_debug_vprintf(const char *fmt, va_list ap)
+{
+ char nbuf[MAXNBUF];
+ const char *p, *percent, *q;
+ u_char *up;
+ int ch, n;
+ uintmax_t num;
+ int base, lflag, qflag, tmp, width, ladjust, sharpflag, neg, sign, dot;
+ int cflag, hflag, jflag, tflag, zflag;
+ int dwidth, upper;
+ int radix = 10;
+ char padc;
+ int stop = 0, retval = 0;
+
+ num = 0;
+
+ if (fmt == NULL)
+ fmt = "(fmt null)\n";
+
+ for (;;) {
+ padc = ' ';
+ width = 0;
+ while ((ch = (u_char)*fmt++) != '%' || stop) {
+ if (ch == '\0') {
+ dtrace_debug__putc('\0');
+ return;
+ }
+ dtrace_debug__putc(ch);
+ }
+ percent = fmt - 1;
+ qflag = 0; lflag = 0; ladjust = 0; sharpflag = 0; neg = 0;
+ sign = 0; dot = 0; dwidth = 0; upper = 0;
+ cflag = 0; hflag = 0; jflag = 0; tflag = 0; zflag = 0;
+reswitch: switch (ch = (u_char)*fmt++) {
+ case '.':
+ dot = 1;
+ goto reswitch;
+ case '#':
+ sharpflag = 1;
+ goto reswitch;
+ case '+':
+ sign = 1;
+ goto reswitch;
+ case '-':
+ ladjust = 1;
+ goto reswitch;
+ case '%':
+ dtrace_debug__putc(ch);
+ break;
+ case '*':
+ if (!dot) {
+ width = va_arg(ap, int);
+ if (width < 0) {
+ ladjust = !ladjust;
+ width = -width;
+ }
+ } else {
+ dwidth = va_arg(ap, int);
+ }
+ goto reswitch;
+ case '0':
+ if (!dot) {
+ padc = '0';
+ goto reswitch;
+ }
+ case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ for (n = 0;; ++fmt) {
+ n = n * 10 + ch - '0';
+ ch = *fmt;
+ if (ch < '0' || ch > '9')
+ break;
+ }
+ if (dot)
+ dwidth = n;
+ else
+ width = n;
+ goto reswitch;
+ case 'b':
+ num = (u_int)va_arg(ap, int);
+ p = va_arg(ap, char *);
+ for (q = dtrace_debug_ksprintn(nbuf, num, *p++, NULL, 0); *q;)
+ dtrace_debug__putc(*q--);
+
+ if (num == 0)
+ break;
+
+ for (tmp = 0; *p;) {
+ n = *p++;
+ if (num & (1 << (n - 1))) {
+ dtrace_debug__putc(tmp ? ',' : '<');
+ for (; (n = *p) > ' '; ++p)
+ dtrace_debug__putc(n);
+ tmp = 1;
+ } else
+ for (; *p > ' '; ++p)
+ continue;
+ }
+ if (tmp)
+ dtrace_debug__putc('>');
+ break;
+ case 'c':
+ dtrace_debug__putc(va_arg(ap, int));
+ break;
+ case 'D':
+ up = va_arg(ap, u_char *);
+ p = va_arg(ap, char *);
+ if (!width)
+ width = 16;
+ while(width--) {
+ dtrace_debug__putc(hex2ascii(*up >> 4));
+ dtrace_debug__putc(hex2ascii(*up & 0x0f));
+ up++;
+ if (width)
+ for (q=p;*q;q++)
+ dtrace_debug__putc(*q);
+ }
+ break;
+ case 'd':
+ case 'i':
+ base = 10;
+ sign = 1;
+ goto handle_sign;
+ case 'h':
+ if (hflag) {
+ hflag = 0;
+ cflag = 1;
+ } else
+ hflag = 1;
+ goto reswitch;
+ case 'j':
+ jflag = 1;
+ goto reswitch;
+ case 'l':
+ if (lflag) {
+ lflag = 0;
+ qflag = 1;
+ } else
+ lflag = 1;
+ goto reswitch;
+ case 'n':
+ if (jflag)
+ *(va_arg(ap, intmax_t *)) = retval;
+ else if (qflag)
+ *(va_arg(ap, quad_t *)) = retval;
+ else if (lflag)
+ *(va_arg(ap, long *)) = retval;
+ else if (zflag)
+ *(va_arg(ap, size_t *)) = retval;
+ else if (hflag)
+ *(va_arg(ap, short *)) = retval;
+ else if (cflag)
+ *(va_arg(ap, char *)) = retval;
+ else
+ *(va_arg(ap, int *)) = retval;
+ break;
+ case 'o':
+ base = 8;
+ goto handle_nosign;
+ case 'p':
+ base = 16;
+ sharpflag = (width == 0);
+ sign = 0;
+ num = (uintptr_t)va_arg(ap, void *);
+ goto number;
+ case 'q':
+ qflag = 1;
+ goto reswitch;
+ case 'r':
+ base = radix;
+ if (sign)
+ goto handle_sign;
+ goto handle_nosign;
+ case 's':
+ p = va_arg(ap, char *);
+ if (p == NULL)
+ p = "(null)";
+ if (!dot)
+ n = strlen (p);
+ else
+ for (n = 0; n < dwidth && p[n]; n++)
+ continue;
+
+ width -= n;
+
+ if (!ladjust && width > 0)
+ while (width--)
+ dtrace_debug__putc(padc);
+ while (n--)
+ dtrace_debug__putc(*p++);
+ if (ladjust && width > 0)
+ while (width--)
+ dtrace_debug__putc(padc);
+ break;
+ case 't':
+ tflag = 1;
+ goto reswitch;
+ case 'u':
+ base = 10;
+ goto handle_nosign;
+ case 'X':
+ upper = 1;
+ case 'x':
+ base = 16;
+ goto handle_nosign;
+ case 'y':
+ base = 16;
+ sign = 1;
+ goto handle_sign;
+ case 'z':
+ zflag = 1;
+ goto reswitch;
+handle_nosign:
+ sign = 0;
+ if (jflag)
+ num = va_arg(ap, uintmax_t);
+ else if (qflag)
+ num = va_arg(ap, u_quad_t);
+ else if (tflag)
+ num = va_arg(ap, ptrdiff_t);
+ else if (lflag)
+ num = va_arg(ap, u_long);
+ else if (zflag)
+ num = va_arg(ap, size_t);
+ else if (hflag)
+ num = (u_short)va_arg(ap, int);
+ else if (cflag)
+ num = (u_char)va_arg(ap, int);
+ else
+ num = va_arg(ap, u_int);
+ goto number;
+handle_sign:
+ if (jflag)
+ num = va_arg(ap, intmax_t);
+ else if (qflag)
+ num = va_arg(ap, quad_t);
+ else if (tflag)
+ num = va_arg(ap, ptrdiff_t);
+ else if (lflag)
+ num = va_arg(ap, long);
+ else if (zflag)
+ num = va_arg(ap, size_t);
+ else if (hflag)
+ num = (short)va_arg(ap, int);
+ else if (cflag)
+ num = (char)va_arg(ap, int);
+ else
+ num = va_arg(ap, int);
+number:
+ if (sign && (intmax_t)num < 0) {
+ neg = 1;
+ num = -(intmax_t)num;
+ }
+ p = dtrace_debug_ksprintn(nbuf, num, base, &tmp, upper);
+ if (sharpflag && num != 0) {
+ if (base == 8)
+ tmp++;
+ else if (base == 16)
+ tmp += 2;
+ }
+ if (neg)
+ tmp++;
+
+ if (!ladjust && padc != '0' && width
+ && (width -= tmp) > 0)
+ while (width--)
+ dtrace_debug__putc(padc);
+ if (neg)
+ dtrace_debug__putc('-');
+ if (sharpflag && num != 0) {
+ if (base == 8) {
+ dtrace_debug__putc('0');
+ } else if (base == 16) {
+ dtrace_debug__putc('0');
+ dtrace_debug__putc('x');
+ }
+ }
+ if (!ladjust && width && (width -= tmp) > 0)
+ while (width--)
+ dtrace_debug__putc(padc);
+
+ while (*p)
+ dtrace_debug__putc(*p--);
+
+ if (ladjust && width && (width -= tmp) > 0)
+ while (width--)
+ dtrace_debug__putc(padc);
+
+ break;
+ default:
+ while (percent < fmt)
+ dtrace_debug__putc(*percent++);
+ /*
+ * Since we ignore an formatting argument it is no
+ * longer safe to obey the remaining formatting
+ * arguments as the arguments will no longer match
+ * the format specs.
+ */
+ stop = 1;
+ break;
+ }
+ }
+
+ dtrace_debug__putc('\0');
+}
+
+void
+dtrace_debug_printf(const char *fmt, ...)
+{
+ va_list ap;
+
+ dtrace_debug_lock(curcpu);
+
+ va_start(ap, fmt);
+
+ dtrace_debug_vprintf(fmt, ap);
+
+ va_end(ap);
+
+ dtrace_debug_unlock(curcpu);
+}
+
+#else
+
+#define dtrace_debug_output()
+#define dtrace_debug_puts(_s)
+#define dtrace_debug_printf(fmt, ...)
+
+#endif
diff --git a/sys/cddl/dev/dtrace/dtrace_hacks.c b/sys/cddl/dev/dtrace/dtrace_hacks.c
new file mode 100644
index 000000000000..21da9f82e703
--- /dev/null
+++ b/sys/cddl/dev/dtrace/dtrace_hacks.c
@@ -0,0 +1,13 @@
+/* $FreeBSD$ */
+/* XXX Hacks.... */
+
+dtrace_cacheid_t dtrace_predcache_id;
+
+int panic_quiesce;
+char panic_stack[PANICSTKSIZE];
+
+boolean_t
+priv_policy_only(const cred_t *a, int b, boolean_t c)
+{
+ return 0;
+}
diff --git a/sys/cddl/dev/dtrace/dtrace_ioctl.c b/sys/cddl/dev/dtrace/dtrace_ioctl.c
new file mode 100644
index 000000000000..bba8c080c829
--- /dev/null
+++ b/sys/cddl/dev/dtrace/dtrace_ioctl.c
@@ -0,0 +1,778 @@
+/*
+ * 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$
+ *
+ */
+
+static int dtrace_verbose_ioctl;
+SYSCTL_INT(_debug_dtrace, OID_AUTO, verbose_ioctl, CTLFLAG_RW, &dtrace_verbose_ioctl, 0, "");
+
+#define DTRACE_IOCTL_PRINTF(fmt, ...) if (dtrace_verbose_ioctl) printf(fmt, ## __VA_ARGS__ )
+
+/* ARGSUSED */
+static int
+dtrace_ioctl(struct cdev *dev, u_long cmd, caddr_t addr,
+ int flags __unused, struct thread *td)
+{
+ dtrace_state_t *state;
+ devfs_get_cdevpriv((void **) &state);
+ int error = 0;
+ if (state == NULL)
+ return (EINVAL);
+
+ if (state->dts_anon) {
+ ASSERT(dtrace_anon.dta_state == NULL);
+ state = state->dts_anon;
+ }
+
+ switch (cmd) {
+ case DTRACEIOC_AGGDESC: {
+ dtrace_aggdesc_t **paggdesc = (dtrace_aggdesc_t **) addr;
+ dtrace_aggdesc_t aggdesc;
+ dtrace_action_t *act;
+ dtrace_aggregation_t *agg;
+ int nrecs;
+ uint32_t offs;
+ dtrace_recdesc_t *lrec;
+ void *buf;
+ size_t size;
+ uintptr_t dest;
+
+ DTRACE_IOCTL_PRINTF("%s(%d): DTRACEIOC_AGGDESC\n",__func__,__LINE__);
+
+ if (copyin((void *) *paggdesc, &aggdesc, sizeof (aggdesc)) != 0)
+ return (EFAULT);
+
+ mutex_enter(&dtrace_lock);
+
+ if ((agg = dtrace_aggid2agg(state, aggdesc.dtagd_id)) == NULL) {
+ mutex_exit(&dtrace_lock);
+ return (EINVAL);
+ }
+
+ aggdesc.dtagd_epid = agg->dtag_ecb->dte_epid;
+
+ nrecs = aggdesc.dtagd_nrecs;
+ aggdesc.dtagd_nrecs = 0;
+
+ offs = agg->dtag_base;
+ lrec = &agg->dtag_action.dta_rec;
+ aggdesc.dtagd_size = lrec->dtrd_offset + lrec->dtrd_size - offs;
+
+ for (act = agg->dtag_first; ; act = act->dta_next) {
+ ASSERT(act->dta_intuple ||
+ DTRACEACT_ISAGG(act->dta_kind));
+
+ /*
+ * If this action has a record size of zero, it
+ * denotes an argument to the aggregating action.
+ * Because the presence of this record doesn't (or
+ * shouldn't) affect the way the data is interpreted,
+ * we don't copy it out to save user-level the
+ * confusion of dealing with a zero-length record.
+ */
+ if (act->dta_rec.dtrd_size == 0) {
+ ASSERT(agg->dtag_hasarg);
+ continue;
+ }
+
+ aggdesc.dtagd_nrecs++;
+
+ if (act == &agg->dtag_action)
+ break;
+ }
+
+ /*
+ * Now that we have the size, we need to allocate a temporary
+ * buffer in which to store the complete description. We need
+ * the temporary buffer to be able to drop dtrace_lock()
+ * across the copyout(), below.
+ */
+ size = sizeof (dtrace_aggdesc_t) +
+ (aggdesc.dtagd_nrecs * sizeof (dtrace_recdesc_t));
+
+ buf = kmem_alloc(size, KM_SLEEP);
+ dest = (uintptr_t)buf;
+
+ bcopy(&aggdesc, (void *)dest, sizeof (aggdesc));
+ dest += offsetof(dtrace_aggdesc_t, dtagd_rec[0]);
+
+ for (act = agg->dtag_first; ; act = act->dta_next) {
+ dtrace_recdesc_t rec = act->dta_rec;
+
+ /*
+ * See the comment in the above loop for why we pass
+ * over zero-length records.
+ */
+ if (rec.dtrd_size == 0) {
+ ASSERT(agg->dtag_hasarg);
+ continue;
+ }
+
+ if (nrecs-- == 0)
+ break;
+
+ rec.dtrd_offset -= offs;
+ bcopy(&rec, (void *)dest, sizeof (rec));
+ dest += sizeof (dtrace_recdesc_t);
+
+ if (act == &agg->dtag_action)
+ break;
+ }
+
+ mutex_exit(&dtrace_lock);
+
+ if (copyout(buf, (void *) *paggdesc, dest - (uintptr_t)buf) != 0) {
+ kmem_free(buf, size);
+ return (EFAULT);
+ }
+
+ kmem_free(buf, size);
+ return (0);
+ }
+ case DTRACEIOC_AGGSNAP:
+ case DTRACEIOC_BUFSNAP: {
+ dtrace_bufdesc_t **pdesc = (dtrace_bufdesc_t **) addr;
+ dtrace_bufdesc_t desc;
+ caddr_t cached;
+ dtrace_buffer_t *buf;
+
+ dtrace_debug_output();
+
+ if (copyin((void *) *pdesc, &desc, sizeof (desc)) != 0)
+ return (EFAULT);
+
+ DTRACE_IOCTL_PRINTF("%s(%d): %s curcpu %d cpu %d\n",
+ __func__,__LINE__,
+ cmd == DTRACEIOC_AGGSNAP ?
+ "DTRACEIOC_AGGSNAP":"DTRACEIOC_BUFSNAP",
+ curcpu, desc.dtbd_cpu);
+
+ if (desc.dtbd_cpu < 0 || desc.dtbd_cpu >= NCPU)
+ return (ENOENT);
+ if (pcpu_find(desc.dtbd_cpu) == NULL)
+ return (ENOENT);
+
+ mutex_enter(&dtrace_lock);
+
+ if (cmd == DTRACEIOC_BUFSNAP) {
+ buf = &state->dts_buffer[desc.dtbd_cpu];
+ } else {
+ buf = &state->dts_aggbuffer[desc.dtbd_cpu];
+ }
+
+ if (buf->dtb_flags & (DTRACEBUF_RING | DTRACEBUF_FILL)) {
+ size_t sz = buf->dtb_offset;
+
+ if (state->dts_activity != DTRACE_ACTIVITY_STOPPED) {
+ mutex_exit(&dtrace_lock);
+ return (EBUSY);
+ }
+
+ /*
+ * If this buffer has already been consumed, we're
+ * going to indicate that there's nothing left here
+ * to consume.
+ */
+ if (buf->dtb_flags & DTRACEBUF_CONSUMED) {
+ mutex_exit(&dtrace_lock);
+
+ desc.dtbd_size = 0;
+ desc.dtbd_drops = 0;
+ desc.dtbd_errors = 0;
+ desc.dtbd_oldest = 0;
+ sz = sizeof (desc);
+
+ if (copyout(&desc, (void *) *pdesc, sz) != 0)
+ return (EFAULT);
+
+ return (0);
+ }
+
+ /*
+ * If this is a ring buffer that has wrapped, we want
+ * to copy the whole thing out.
+ */
+ if (buf->dtb_flags & DTRACEBUF_WRAPPED) {
+ dtrace_buffer_polish(buf);
+ sz = buf->dtb_size;
+ }
+
+ if (copyout(buf->dtb_tomax, desc.dtbd_data, sz) != 0) {
+ mutex_exit(&dtrace_lock);
+ return (EFAULT);
+ }
+
+ desc.dtbd_size = sz;
+ desc.dtbd_drops = buf->dtb_drops;
+ desc.dtbd_errors = buf->dtb_errors;
+ desc.dtbd_oldest = buf->dtb_xamot_offset;
+
+ mutex_exit(&dtrace_lock);
+
+ if (copyout(&desc, (void *) *pdesc, sizeof (desc)) != 0)
+ return (EFAULT);
+
+ buf->dtb_flags |= DTRACEBUF_CONSUMED;
+
+ return (0);
+ }
+
+ if (buf->dtb_tomax == NULL) {
+ ASSERT(buf->dtb_xamot == NULL);
+ mutex_exit(&dtrace_lock);
+ return (ENOENT);
+ }
+
+ cached = buf->dtb_tomax;
+ ASSERT(!(buf->dtb_flags & DTRACEBUF_NOSWITCH));
+
+ dtrace_xcall(desc.dtbd_cpu,
+ (dtrace_xcall_t)dtrace_buffer_switch, buf);
+
+ state->dts_errors += buf->dtb_xamot_errors;
+
+ /*
+ * If the buffers did not actually switch, then the cross call
+ * did not take place -- presumably because the given CPU is
+ * not in the ready set. If this is the case, we'll return
+ * ENOENT.
+ */
+ if (buf->dtb_tomax == cached) {
+ ASSERT(buf->dtb_xamot != cached);
+ mutex_exit(&dtrace_lock);
+ return (ENOENT);
+ }
+
+ ASSERT(cached == buf->dtb_xamot);
+
+ DTRACE_IOCTL_PRINTF("%s(%d): copyout the buffer snapshot\n",__func__,__LINE__);
+
+ /*
+ * We have our snapshot; now copy it out.
+ */
+ if (copyout(buf->dtb_xamot, desc.dtbd_data,
+ buf->dtb_xamot_offset) != 0) {
+ mutex_exit(&dtrace_lock);
+ return (EFAULT);
+ }
+
+ desc.dtbd_size = buf->dtb_xamot_offset;
+ desc.dtbd_drops = buf->dtb_xamot_drops;
+ desc.dtbd_errors = buf->dtb_xamot_errors;
+ desc.dtbd_oldest = 0;
+
+ mutex_exit(&dtrace_lock);
+
+ DTRACE_IOCTL_PRINTF("%s(%d): copyout buffer desc: size %zd drops %lu errors %lu\n",__func__,__LINE__,(size_t) desc.dtbd_size,(u_long) desc.dtbd_drops,(u_long) desc.dtbd_errors);
+
+ /*
+ * Finally, copy out the buffer description.
+ */
+ if (copyout(&desc, (void *) *pdesc, sizeof (desc)) != 0)
+ return (EFAULT);
+
+ return (0);
+ }
+ case DTRACEIOC_CONF: {
+ dtrace_conf_t conf;
+
+ DTRACE_IOCTL_PRINTF("%s(%d): DTRACEIOC_CONF\n",__func__,__LINE__);
+
+ bzero(&conf, sizeof (conf));
+ conf.dtc_difversion = DIF_VERSION;
+ conf.dtc_difintregs = DIF_DIR_NREGS;
+ conf.dtc_diftupregs = DIF_DTR_NREGS;
+ conf.dtc_ctfmodel = CTF_MODEL_NATIVE;
+
+ *((dtrace_conf_t *) addr) = conf;
+
+ return (0);
+ }
+ case DTRACEIOC_DOFGET: {
+ dof_hdr_t **pdof = (dof_hdr_t **) addr;
+ dof_hdr_t hdr, *dof = *pdof;
+ int rval;
+ uint64_t len;
+
+ DTRACE_IOCTL_PRINTF("%s(%d): DTRACEIOC_DOFGET\n",__func__,__LINE__);
+
+ if (copyin((void *)dof, &hdr, sizeof (hdr)) != 0)
+ return (EFAULT);
+
+ mutex_enter(&dtrace_lock);
+ dof = dtrace_dof_create(state);
+ mutex_exit(&dtrace_lock);
+
+ len = MIN(hdr.dofh_loadsz, dof->dofh_loadsz);
+ rval = copyout(dof, (void *) *pdof, len);
+ dtrace_dof_destroy(dof);
+
+ return (rval == 0 ? 0 : EFAULT);
+ }
+ case DTRACEIOC_ENABLE: {
+ dof_hdr_t *dof = NULL;
+ dtrace_enabling_t *enab = NULL;
+ dtrace_vstate_t *vstate;
+ int err = 0;
+ int rval;
+ dtrace_enable_io_t *p = (dtrace_enable_io_t *) addr;
+
+ DTRACE_IOCTL_PRINTF("%s(%d): DTRACEIOC_ENABLE\n",__func__,__LINE__);
+
+ /*
+ * If a NULL argument has been passed, we take this as our
+ * cue to reevaluate our enablings.
+ */
+ if (p->dof == NULL) {
+ dtrace_enabling_matchall();
+
+ return (0);
+ }
+
+ if ((dof = dtrace_dof_copyin((uintptr_t) p->dof, &rval)) == NULL)
+ return (EINVAL);
+
+ mutex_enter(&cpu_lock);
+ mutex_enter(&dtrace_lock);
+ vstate = &state->dts_vstate;
+
+ if (state->dts_activity != DTRACE_ACTIVITY_INACTIVE) {
+ mutex_exit(&dtrace_lock);
+ mutex_exit(&cpu_lock);
+ dtrace_dof_destroy(dof);
+ return (EBUSY);
+ }
+
+ if (dtrace_dof_slurp(dof, vstate, td->td_ucred, &enab, 0, B_TRUE) != 0) {
+ mutex_exit(&dtrace_lock);
+ mutex_exit(&cpu_lock);
+ dtrace_dof_destroy(dof);
+ return (EINVAL);
+ }
+
+ if ((rval = dtrace_dof_options(dof, state)) != 0) {
+ dtrace_enabling_destroy(enab);
+ mutex_exit(&dtrace_lock);
+ mutex_exit(&cpu_lock);
+ dtrace_dof_destroy(dof);
+ return (rval);
+ }
+
+ if ((err = dtrace_enabling_match(enab, &p->n_matched)) == 0) {
+ err = dtrace_enabling_retain(enab);
+ } else {
+ dtrace_enabling_destroy(enab);
+ }
+
+ mutex_exit(&cpu_lock);
+ mutex_exit(&dtrace_lock);
+ dtrace_dof_destroy(dof);
+
+ return (err);
+ }
+ case DTRACEIOC_EPROBE: {
+ dtrace_eprobedesc_t **pepdesc = (dtrace_eprobedesc_t **) addr;
+ dtrace_eprobedesc_t epdesc;
+ dtrace_ecb_t *ecb;
+ dtrace_action_t *act;
+ void *buf;
+ size_t size;
+ uintptr_t dest;
+ int nrecs;
+
+ DTRACE_IOCTL_PRINTF("%s(%d): DTRACEIOC_EPROBE\n",__func__,__LINE__);
+
+ if (copyin((void *)*pepdesc, &epdesc, sizeof (epdesc)) != 0)
+ return (EFAULT);
+
+ mutex_enter(&dtrace_lock);
+
+ if ((ecb = dtrace_epid2ecb(state, epdesc.dtepd_epid)) == NULL) {
+ mutex_exit(&dtrace_lock);
+ return (EINVAL);
+ }
+
+ if (ecb->dte_probe == NULL) {
+ mutex_exit(&dtrace_lock);
+ return (EINVAL);
+ }
+
+ epdesc.dtepd_probeid = ecb->dte_probe->dtpr_id;
+ epdesc.dtepd_uarg = ecb->dte_uarg;
+ epdesc.dtepd_size = ecb->dte_size;
+
+ nrecs = epdesc.dtepd_nrecs;
+ epdesc.dtepd_nrecs = 0;
+ for (act = ecb->dte_action; act != NULL; act = act->dta_next) {
+ if (DTRACEACT_ISAGG(act->dta_kind) || act->dta_intuple)
+ continue;
+
+ epdesc.dtepd_nrecs++;
+ }
+
+ /*
+ * Now that we have the size, we need to allocate a temporary
+ * buffer in which to store the complete description. We need
+ * the temporary buffer to be able to drop dtrace_lock()
+ * across the copyout(), below.
+ */
+ size = sizeof (dtrace_eprobedesc_t) +
+ (epdesc.dtepd_nrecs * sizeof (dtrace_recdesc_t));
+
+ buf = kmem_alloc(size, KM_SLEEP);
+ dest = (uintptr_t)buf;
+
+ bcopy(&epdesc, (void *)dest, sizeof (epdesc));
+ dest += offsetof(dtrace_eprobedesc_t, dtepd_rec[0]);
+
+ for (act = ecb->dte_action; act != NULL; act = act->dta_next) {
+ if (DTRACEACT_ISAGG(act->dta_kind) || act->dta_intuple)
+ continue;
+
+ if (nrecs-- == 0)
+ break;
+
+ bcopy(&act->dta_rec, (void *)dest,
+ sizeof (dtrace_recdesc_t));
+ dest += sizeof (dtrace_recdesc_t);
+ }
+
+ mutex_exit(&dtrace_lock);
+
+ if (copyout(buf, (void *) *pepdesc, dest - (uintptr_t)buf) != 0) {
+ kmem_free(buf, size);
+ return (EFAULT);
+ }
+
+ kmem_free(buf, size);
+ return (0);
+ }
+ case DTRACEIOC_FORMAT: {
+ dtrace_fmtdesc_t *fmt = (dtrace_fmtdesc_t *) addr;
+ char *str;
+ int len;
+
+ DTRACE_IOCTL_PRINTF("%s(%d): DTRACEIOC_FORMAT\n",__func__,__LINE__);
+
+ mutex_enter(&dtrace_lock);
+
+ if (fmt->dtfd_format == 0 ||
+ fmt->dtfd_format > state->dts_nformats) {
+ mutex_exit(&dtrace_lock);
+ return (EINVAL);
+ }
+
+ /*
+ * Format strings are allocated contiguously and they are
+ * never freed; if a format index is less than the number
+ * of formats, we can assert that the format map is non-NULL
+ * and that the format for the specified index is non-NULL.
+ */
+ ASSERT(state->dts_formats != NULL);
+ str = state->dts_formats[fmt->dtfd_format - 1];
+ ASSERT(str != NULL);
+
+ len = strlen(str) + 1;
+
+ if (len > fmt->dtfd_length) {
+ fmt->dtfd_length = len;
+ } else {
+ if (copyout(str, fmt->dtfd_string, len) != 0) {
+ mutex_exit(&dtrace_lock);
+ return (EINVAL);
+ }
+ }
+
+ mutex_exit(&dtrace_lock);
+ return (0);
+ }
+ case DTRACEIOC_GO: {
+ int rval;
+ processorid_t *cpuid = (processorid_t *) addr;
+
+ DTRACE_IOCTL_PRINTF("%s(%d): DTRACEIOC_GO\n",__func__,__LINE__);
+
+ rval = dtrace_state_go(state, cpuid);
+
+ return (rval);
+ }
+ case DTRACEIOC_PROBEARG: {
+ dtrace_argdesc_t *desc = (dtrace_argdesc_t *) addr;
+ dtrace_probe_t *probe;
+ dtrace_provider_t *prov;
+
+ DTRACE_IOCTL_PRINTF("%s(%d): DTRACEIOC_PROBEARG\n",__func__,__LINE__);
+
+ if (desc->dtargd_id == DTRACE_IDNONE)
+ return (EINVAL);
+
+ if (desc->dtargd_ndx == DTRACE_ARGNONE)
+ return (EINVAL);
+
+ mutex_enter(&dtrace_provider_lock);
+ mutex_enter(&mod_lock);
+ mutex_enter(&dtrace_lock);
+
+ if (desc->dtargd_id > dtrace_nprobes) {
+ mutex_exit(&dtrace_lock);
+ mutex_exit(&mod_lock);
+ mutex_exit(&dtrace_provider_lock);
+ return (EINVAL);
+ }
+
+ if ((probe = dtrace_probes[desc->dtargd_id - 1]) == NULL) {
+ mutex_exit(&dtrace_lock);
+ mutex_exit(&mod_lock);
+ mutex_exit(&dtrace_provider_lock);
+ return (EINVAL);
+ }
+
+ mutex_exit(&dtrace_lock);
+
+ prov = probe->dtpr_provider;
+
+ if (prov->dtpv_pops.dtps_getargdesc == NULL) {
+ /*
+ * There isn't any typed information for this probe.
+ * Set the argument number to DTRACE_ARGNONE.
+ */
+ desc->dtargd_ndx = DTRACE_ARGNONE;
+ } else {
+ desc->dtargd_native[0] = '\0';
+ desc->dtargd_xlate[0] = '\0';
+ desc->dtargd_mapping = desc->dtargd_ndx;
+
+ prov->dtpv_pops.dtps_getargdesc(prov->dtpv_arg,
+ probe->dtpr_id, probe->dtpr_arg, desc);
+ }
+
+ mutex_exit(&mod_lock);
+ mutex_exit(&dtrace_provider_lock);
+
+ return (0);
+ }
+ case DTRACEIOC_PROBEMATCH:
+ case DTRACEIOC_PROBES: {
+ dtrace_probedesc_t *p_desc = (dtrace_probedesc_t *) addr;
+ dtrace_probe_t *probe = NULL;
+ dtrace_probekey_t pkey;
+ dtrace_id_t i;
+ int m = 0;
+ uint32_t priv = 0;
+ uid_t uid = 0;
+ zoneid_t zoneid = 0;
+
+ DTRACE_IOCTL_PRINTF("%s(%d): %s\n",__func__,__LINE__,
+ cmd == DTRACEIOC_PROBEMATCH ?
+ "DTRACEIOC_PROBEMATCH":"DTRACEIOC_PROBES");
+
+ p_desc->dtpd_provider[DTRACE_PROVNAMELEN - 1] = '\0';
+ p_desc->dtpd_mod[DTRACE_MODNAMELEN - 1] = '\0';
+ p_desc->dtpd_func[DTRACE_FUNCNAMELEN - 1] = '\0';
+ p_desc->dtpd_name[DTRACE_NAMELEN - 1] = '\0';
+
+ /*
+ * Before we attempt to match this probe, we want to give
+ * all providers the opportunity to provide it.
+ */
+ if (p_desc->dtpd_id == DTRACE_IDNONE) {
+ mutex_enter(&dtrace_provider_lock);
+ dtrace_probe_provide(p_desc, NULL);
+ mutex_exit(&dtrace_provider_lock);
+ p_desc->dtpd_id++;
+ }
+
+ if (cmd == DTRACEIOC_PROBEMATCH) {
+ dtrace_probekey(p_desc, &pkey);
+ pkey.dtpk_id = DTRACE_IDNONE;
+ }
+
+ dtrace_cred2priv(td->td_ucred, &priv, &uid, &zoneid);
+
+ mutex_enter(&dtrace_lock);
+
+ if (cmd == DTRACEIOC_PROBEMATCH) {
+ for (i = p_desc->dtpd_id; i <= dtrace_nprobes; i++) {
+ if ((probe = dtrace_probes[i - 1]) != NULL &&
+ (m = dtrace_match_probe(probe, &pkey,
+ priv, uid, zoneid)) != 0)
+ break;
+ }
+
+ if (m < 0) {
+ mutex_exit(&dtrace_lock);
+ return (EINVAL);
+ }
+
+ } else {
+ for (i = p_desc->dtpd_id; i <= dtrace_nprobes; i++) {
+ if ((probe = dtrace_probes[i - 1]) != NULL &&
+ dtrace_match_priv(probe, priv, uid, zoneid))
+ break;
+ }
+ }
+
+ if (probe == NULL) {
+ mutex_exit(&dtrace_lock);
+ return (ESRCH);
+ }
+
+ dtrace_probe_description(probe, p_desc);
+ mutex_exit(&dtrace_lock);
+
+ return (0);
+ }
+ case DTRACEIOC_PROVIDER: {
+ dtrace_providerdesc_t *pvd = (dtrace_providerdesc_t *) addr;
+ dtrace_provider_t *pvp;
+
+ DTRACE_IOCTL_PRINTF("%s(%d): DTRACEIOC_PROVIDER\n",__func__,__LINE__);
+
+ pvd->dtvd_name[DTRACE_PROVNAMELEN - 1] = '\0';
+ mutex_enter(&dtrace_provider_lock);
+
+ for (pvp = dtrace_provider; pvp != NULL; pvp = pvp->dtpv_next) {
+ if (strcmp(pvp->dtpv_name, pvd->dtvd_name) == 0)
+ break;
+ }
+
+ mutex_exit(&dtrace_provider_lock);
+
+ if (pvp == NULL)
+ return (ESRCH);
+
+ bcopy(&pvp->dtpv_priv, &pvd->dtvd_priv, sizeof (dtrace_ppriv_t));
+ bcopy(&pvp->dtpv_attr, &pvd->dtvd_attr, sizeof (dtrace_pattr_t));
+
+ return (0);
+ }
+ case DTRACEIOC_REPLICATE: {
+ dtrace_repldesc_t *desc = (dtrace_repldesc_t *) addr;
+ dtrace_probedesc_t *match = &desc->dtrpd_match;
+ dtrace_probedesc_t *create = &desc->dtrpd_create;
+ int err;
+
+ DTRACE_IOCTL_PRINTF("%s(%d): DTRACEIOC_REPLICATE\n",__func__,__LINE__);
+
+ match->dtpd_provider[DTRACE_PROVNAMELEN - 1] = '\0';
+ match->dtpd_mod[DTRACE_MODNAMELEN - 1] = '\0';
+ match->dtpd_func[DTRACE_FUNCNAMELEN - 1] = '\0';
+ match->dtpd_name[DTRACE_NAMELEN - 1] = '\0';
+
+ create->dtpd_provider[DTRACE_PROVNAMELEN - 1] = '\0';
+ create->dtpd_mod[DTRACE_MODNAMELEN - 1] = '\0';
+ create->dtpd_func[DTRACE_FUNCNAMELEN - 1] = '\0';
+ create->dtpd_name[DTRACE_NAMELEN - 1] = '\0';
+
+ mutex_enter(&dtrace_lock);
+ err = dtrace_enabling_replicate(state, match, create);
+ mutex_exit(&dtrace_lock);
+
+ return (err);
+ }
+ case DTRACEIOC_STATUS: {
+ dtrace_status_t *stat = (dtrace_status_t *) addr;
+ dtrace_dstate_t *dstate;
+ int i, j;
+ uint64_t nerrs;
+
+ DTRACE_IOCTL_PRINTF("%s(%d): DTRACEIOC_STATUS\n",__func__,__LINE__);
+
+ /*
+ * See the comment in dtrace_state_deadman() for the reason
+ * for setting dts_laststatus to INT64_MAX before setting
+ * it to the correct value.
+ */
+ state->dts_laststatus = INT64_MAX;
+ dtrace_membar_producer();
+ state->dts_laststatus = dtrace_gethrtime();
+
+ bzero(stat, sizeof (*stat));
+
+ mutex_enter(&dtrace_lock);
+
+ if (state->dts_activity == DTRACE_ACTIVITY_INACTIVE) {
+ mutex_exit(&dtrace_lock);
+ return (ENOENT);
+ }
+
+ if (state->dts_activity == DTRACE_ACTIVITY_DRAINING)
+ stat->dtst_exiting = 1;
+
+ nerrs = state->dts_errors;
+ dstate = &state->dts_vstate.dtvs_dynvars;
+
+ for (i = 0; i < NCPU; i++) {
+#if !defined(sun)
+ if (pcpu_find(i) == NULL)
+ continue;
+#endif
+ dtrace_dstate_percpu_t *dcpu = &dstate->dtds_percpu[i];
+
+ stat->dtst_dyndrops += dcpu->dtdsc_drops;
+ stat->dtst_dyndrops_dirty += dcpu->dtdsc_dirty_drops;
+ stat->dtst_dyndrops_rinsing += dcpu->dtdsc_rinsing_drops;
+
+ if (state->dts_buffer[i].dtb_flags & DTRACEBUF_FULL)
+ stat->dtst_filled++;
+
+ nerrs += state->dts_buffer[i].dtb_errors;
+
+ for (j = 0; j < state->dts_nspeculations; j++) {
+ dtrace_speculation_t *spec;
+ dtrace_buffer_t *buf;
+
+ spec = &state->dts_speculations[j];
+ buf = &spec->dtsp_buffer[i];
+ stat->dtst_specdrops += buf->dtb_xamot_drops;
+ }
+ }
+
+ stat->dtst_specdrops_busy = state->dts_speculations_busy;
+ stat->dtst_specdrops_unavail = state->dts_speculations_unavail;
+ stat->dtst_stkstroverflows = state->dts_stkstroverflows;
+ stat->dtst_dblerrors = state->dts_dblerrors;
+ stat->dtst_killed =
+ (state->dts_activity == DTRACE_ACTIVITY_KILLED);
+ stat->dtst_errors = nerrs;
+
+ mutex_exit(&dtrace_lock);
+
+ return (0);
+ }
+ case DTRACEIOC_STOP: {
+ int rval;
+ processorid_t *cpuid = (processorid_t *) addr;
+
+ DTRACE_IOCTL_PRINTF("%s(%d): DTRACEIOC_STOP\n",__func__,__LINE__);
+
+ mutex_enter(&dtrace_lock);
+ rval = dtrace_state_stop(state, cpuid);
+ mutex_exit(&dtrace_lock);
+
+ return (rval);
+ }
+ default:
+ error = ENOTTY;
+ }
+ return (error);
+}
diff --git a/sys/cddl/dev/dtrace/dtrace_load.c b/sys/cddl/dev/dtrace/dtrace_load.c
new file mode 100644
index 000000000000..08506efcf195
--- /dev/null
+++ b/sys/cddl/dev/dtrace/dtrace_load.c
@@ -0,0 +1,160 @@
+/*
+ * 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$
+ *
+ */
+
+static void
+dtrace_ap_start(void *dummy)
+{
+ int i;
+
+ mutex_enter(&cpu_lock);
+
+ /* Setup the rest of the CPUs. */
+ for (i = 1; i <= mp_maxid; i++) {
+ if (pcpu_find(i) == NULL)
+ continue;
+
+ (void) dtrace_cpu_setup(CPU_CONFIG, i);
+ }
+
+ mutex_exit(&cpu_lock);
+}
+
+SYSINIT(dtrace_ap_start, SI_SUB_SMP, SI_ORDER_ANY, dtrace_ap_start, NULL);
+
+static void
+dtrace_load(void *dummy)
+{
+ dtrace_provider_id_t id;
+
+ /* Hook into the trap handler. */
+ dtrace_trap_func = dtrace_trap;
+
+ /* Hang our hook for thread switches. */
+ dtrace_vtime_switch_func = dtrace_vtime_switch;
+
+ /* Hang our hook for exceptions. */
+ dtrace_invop_init();
+
+ /*
+ * XXX This is a short term hack to avoid having to comment
+ * out lots and lots of lock/unlock calls.
+ */
+ mutex_init(&mod_lock,"XXX mod_lock hack", MUTEX_DEFAULT, NULL);
+
+ /*
+ * Initialise the mutexes without 'witness' because the dtrace
+ * code is mostly written to wait for memory. To have the
+ * witness code change a malloc() from M_WAITOK to M_NOWAIT
+ * because a lock is held would surely create a panic in a
+ * low memory situation. And that low memory situation might be
+ * the very problem we are trying to trace.
+ */
+ mutex_init(&dtrace_lock,"dtrace probe state", MUTEX_DEFAULT, NULL);
+ mutex_init(&dtrace_provider_lock,"dtrace provider state", MUTEX_DEFAULT, NULL);
+ mutex_init(&dtrace_meta_lock,"dtrace meta-provider state", MUTEX_DEFAULT, NULL);
+ mutex_init(&dtrace_errlock,"dtrace error lock", MUTEX_DEFAULT, NULL);
+
+ mutex_enter(&dtrace_provider_lock);
+ mutex_enter(&dtrace_lock);
+ mutex_enter(&cpu_lock);
+
+ ASSERT(MUTEX_HELD(&cpu_lock));
+
+ dtrace_arena = new_unrhdr(1, INT_MAX, &dtrace_unr_mtx);
+
+ dtrace_state_cache = kmem_cache_create("dtrace_state_cache",
+ sizeof (dtrace_dstate_percpu_t) * NCPU, DTRACE_STATE_ALIGN,
+ NULL, NULL, NULL, NULL, NULL, 0);
+
+ ASSERT(MUTEX_HELD(&cpu_lock));
+ dtrace_bymod = dtrace_hash_create(offsetof(dtrace_probe_t, dtpr_mod),
+ offsetof(dtrace_probe_t, dtpr_nextmod),
+ offsetof(dtrace_probe_t, dtpr_prevmod));
+
+ dtrace_byfunc = dtrace_hash_create(offsetof(dtrace_probe_t, dtpr_func),
+ offsetof(dtrace_probe_t, dtpr_nextfunc),
+ offsetof(dtrace_probe_t, dtpr_prevfunc));
+
+ dtrace_byname = dtrace_hash_create(offsetof(dtrace_probe_t, dtpr_name),
+ offsetof(dtrace_probe_t, dtpr_nextname),
+ offsetof(dtrace_probe_t, dtpr_prevname));
+
+ if (dtrace_retain_max < 1) {
+ cmn_err(CE_WARN, "illegal value (%lu) for dtrace_retain_max; "
+ "setting to 1", dtrace_retain_max);
+ dtrace_retain_max = 1;
+ }
+
+ /*
+ * Now discover our toxic ranges.
+ */
+ dtrace_toxic_ranges(dtrace_toxrange_add);
+
+ /*
+ * Before we register ourselves as a provider to our own framework,
+ * we would like to assert that dtrace_provider is NULL -- but that's
+ * not true if we were loaded as a dependency of a DTrace provider.
+ * Once we've registered, we can assert that dtrace_provider is our
+ * pseudo provider.
+ */
+ (void) dtrace_register("dtrace", &dtrace_provider_attr,
+ DTRACE_PRIV_NONE, 0, &dtrace_provider_ops, NULL, &id);
+
+ ASSERT(dtrace_provider != NULL);
+ ASSERT((dtrace_provider_id_t)dtrace_provider == id);
+
+ dtrace_probeid_begin = dtrace_probe_create((dtrace_provider_id_t)
+ dtrace_provider, NULL, NULL, "BEGIN", 0, NULL);
+ dtrace_probeid_end = dtrace_probe_create((dtrace_provider_id_t)
+ dtrace_provider, NULL, NULL, "END", 0, NULL);
+ dtrace_probeid_error = dtrace_probe_create((dtrace_provider_id_t)
+ dtrace_provider, NULL, NULL, "ERROR", 1, NULL);
+
+ mutex_exit(&cpu_lock);
+
+ /*
+ * If DTrace helper tracing is enabled, we need to allocate the
+ * trace buffer and initialize the values.
+ */
+ if (dtrace_helptrace_enabled) {
+ ASSERT(dtrace_helptrace_buffer == NULL);
+ dtrace_helptrace_buffer =
+ kmem_zalloc(dtrace_helptrace_bufsize, KM_SLEEP);
+ dtrace_helptrace_next = 0;
+ }
+
+ mutex_exit(&dtrace_lock);
+ mutex_exit(&dtrace_provider_lock);
+
+ mutex_enter(&cpu_lock);
+
+ /* Setup the boot CPU */
+ (void) dtrace_cpu_setup(CPU_CONFIG, 0);
+
+ mutex_exit(&cpu_lock);
+
+ dtrace_dev = make_dev(&dtrace_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "dtrace/dtrace");
+
+ return;
+}
diff --git a/sys/cddl/dev/dtrace/dtrace_modevent.c b/sys/cddl/dev/dtrace/dtrace_modevent.c
new file mode 100644
index 000000000000..8d318532dee9
--- /dev/null
+++ b/sys/cddl/dev/dtrace/dtrace_modevent.c
@@ -0,0 +1,47 @@
+/*
+ * 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$
+ *
+ */
+
+/* ARGSUSED */
+static int
+dtrace_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);
+}
diff --git a/sys/cddl/dev/dtrace/dtrace_sysctl.c b/sys/cddl/dev/dtrace/dtrace_sysctl.c
new file mode 100644
index 000000000000..00ed70916010
--- /dev/null
+++ b/sys/cddl/dev/dtrace/dtrace_sysctl.c
@@ -0,0 +1,82 @@
+/*
+ * 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$
+ *
+ */
+
+int dtrace_debug = 0;
+TUNABLE_INT("debug.dtrace.debug", &dtrace_debug);
+SYSCTL_INT(_debug_dtrace, OID_AUTO, debug, CTLFLAG_RW, &dtrace_debug, 0, "");
+
+/* Report registered DTrace providers. */
+static int
+sysctl_dtrace_providers(SYSCTL_HANDLER_ARGS)
+{
+ char *p_name = NULL;
+ dtrace_provider_t
+ *prov = dtrace_provider;
+ int error = 0;
+ size_t len = 0;
+
+ mutex_enter(&dtrace_provider_lock);
+ mutex_enter(&dtrace_lock);
+
+ /* Compute the length of the space-separated provider name string. */
+ while (prov != NULL) {
+ len += strlen(prov->dtpv_name) + 1;
+ prov = prov->dtpv_next;
+ }
+
+ if ((p_name = kmem_alloc(len, KM_SLEEP)) == NULL)
+ error = ENOMEM;
+ else {
+ /* Start with an empty string. */
+ *p_name = '\0';
+
+ /* Point to the first provider again. */
+ prov = dtrace_provider;
+
+ /* Loop through the providers, appending the names. */
+ while (prov != NULL) {
+ if (prov != dtrace_provider)
+ (void) strlcat(p_name, " ", len);
+
+ (void) strlcat(p_name, prov->dtpv_name, len);
+
+ prov = prov->dtpv_next;
+ }
+ }
+
+ mutex_exit(&dtrace_lock);
+ mutex_exit(&dtrace_provider_lock);
+
+ if (p_name != NULL) {
+ error = sysctl_handle_string(oidp, p_name, len, req);
+
+ kmem_free(p_name, 0);
+ }
+
+ return (error);
+}
+
+SYSCTL_PROC(_debug_dtrace, OID_AUTO, providers, CTLTYPE_STRING | CTLFLAG_RD,
+ 0, 0, sysctl_dtrace_providers, "A", "");
+
diff --git a/sys/cddl/dev/dtrace/dtrace_test.c b/sys/cddl/dev/dtrace/dtrace_test.c
new file mode 100644
index 000000000000..d484fb2c0802
--- /dev/null
+++ b/sys/cddl/dev/dtrace/dtrace_test.c
@@ -0,0 +1,73 @@
+/*-
+ * Copyright 2008 John Birrell <jb@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ *
+ */
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/vnode.h>
+
+/*
+ * These are variables that the DTrace test suite references in the
+ * Solaris kernel. We define them here so that the tests function
+ * unaltered.
+ */
+int kmem_flags;
+
+typedef struct vnode vnode_t;
+vnode_t dummy;
+vnode_t *rootvp = &dummy;
+
+static int
+dtrace_test_modevent(module_t mod, int type, void *data)
+{
+ int error = 0;
+
+ switch (type) {
+ case MOD_LOAD:
+ break;
+
+ case MOD_UNLOAD:
+ break;
+
+ case MOD_SHUTDOWN:
+ break;
+
+ default:
+ error = EOPNOTSUPP;
+ break;
+
+ }
+ return (error);
+}
+
+DEV_MODULE(dtrace_test, dtrace_test_modevent, NULL);
+MODULE_VERSION(dtrace_test, 1);
+MODULE_DEPEND(dtrace_test, dtraceall, 1, 1, 1);
diff --git a/sys/cddl/dev/dtrace/dtrace_unload.c b/sys/cddl/dev/dtrace/dtrace_unload.c
new file mode 100644
index 000000000000..15d41afd29df
--- /dev/null
+++ b/sys/cddl/dev/dtrace/dtrace_unload.c
@@ -0,0 +1,127 @@
+/*
+ * 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$
+ *
+ */
+
+static int
+dtrace_unload()
+{
+ dtrace_state_t *state;
+ int error = 0;
+
+ destroy_dev(dtrace_dev);
+
+ mutex_enter(&dtrace_provider_lock);
+ mutex_enter(&dtrace_lock);
+ mutex_enter(&cpu_lock);
+
+ ASSERT(dtrace_opens == 0);
+
+ if (dtrace_helpers > 0) {
+ mutex_exit(&cpu_lock);
+ mutex_exit(&dtrace_lock);
+ mutex_exit(&dtrace_provider_lock);
+ return (EBUSY);
+ }
+
+ if (dtrace_unregister((dtrace_provider_id_t)dtrace_provider) != 0) {
+ mutex_exit(&cpu_lock);
+ mutex_exit(&dtrace_lock);
+ mutex_exit(&dtrace_provider_lock);
+ return (EBUSY);
+ }
+
+ dtrace_provider = NULL;
+
+ if ((state = dtrace_anon_grab()) != NULL) {
+ /*
+ * If there were ECBs on this state, the provider should
+ * have not been allowed to detach; assert that there is
+ * none.
+ */
+ ASSERT(state->dts_necbs == 0);
+ dtrace_state_destroy(state);
+ }
+
+ bzero(&dtrace_anon, sizeof (dtrace_anon_t));
+
+ mutex_exit(&cpu_lock);
+
+ if (dtrace_helptrace_enabled) {
+ kmem_free(dtrace_helptrace_buffer, 0);
+ dtrace_helptrace_buffer = NULL;
+ }
+
+ if (dtrace_probes != NULL) {
+ kmem_free(dtrace_probes, 0);
+ dtrace_probes = NULL;
+ dtrace_nprobes = 0;
+ }
+
+ dtrace_hash_destroy(dtrace_bymod);
+ dtrace_hash_destroy(dtrace_byfunc);
+ dtrace_hash_destroy(dtrace_byname);
+ dtrace_bymod = NULL;
+ dtrace_byfunc = NULL;
+ dtrace_byname = NULL;
+
+ kmem_cache_destroy(dtrace_state_cache);
+
+ delete_unrhdr(dtrace_arena);
+
+ if (dtrace_toxrange != NULL) {
+ kmem_free(dtrace_toxrange, 0);
+ dtrace_toxrange = NULL;
+ dtrace_toxranges = 0;
+ dtrace_toxranges_max = 0;
+ }
+
+ ASSERT(dtrace_vtime_references == 0);
+ ASSERT(dtrace_opens == 0);
+ ASSERT(dtrace_retained == NULL);
+
+ mutex_exit(&dtrace_lock);
+ mutex_exit(&dtrace_provider_lock);
+
+ mutex_destroy(&dtrace_meta_lock);
+ mutex_destroy(&dtrace_provider_lock);
+ mutex_destroy(&dtrace_lock);
+ mutex_destroy(&dtrace_errlock);
+
+ /* XXX Hack */
+ mutex_destroy(&mod_lock);
+
+ /* Reset our hook for exceptions. */
+ dtrace_invop_uninit();
+
+ /*
+ * Reset our hook for thread switches, but ensure that vtime isn't
+ * active first.
+ */
+ dtrace_vtime_active = 0;
+ dtrace_vtime_switch_func = NULL;
+
+ /* Unhook from the trap handler. */
+ dtrace_trap_func = NULL;
+
+ return (error);
+}
diff --git a/sys/cddl/dev/dtrace/dtrace_vtime.c b/sys/cddl/dev/dtrace/dtrace_vtime.c
new file mode 100644
index 000000000000..a3fa7f77287a
--- /dev/null
+++ b/sys/cddl/dev/dtrace/dtrace_vtime.c
@@ -0,0 +1,101 @@
+/*
+ * 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$
+ */
+
+/*
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+void
+dtrace_vtime_enable(void)
+{
+ dtrace_vtime_state_t state, nstate = 0;
+
+ do {
+ state = dtrace_vtime_active;
+
+ switch (state) {
+ case DTRACE_VTIME_INACTIVE:
+ nstate = DTRACE_VTIME_ACTIVE;
+ break;
+
+ case DTRACE_VTIME_INACTIVE_TNF:
+ nstate = DTRACE_VTIME_ACTIVE_TNF;
+ break;
+
+ case DTRACE_VTIME_ACTIVE:
+ case DTRACE_VTIME_ACTIVE_TNF:
+ panic("DTrace virtual time already enabled");
+ /*NOTREACHED*/
+ }
+
+ } while (dtrace_cas32((uint32_t *)&dtrace_vtime_active,
+ state, nstate) != state);
+}
+
+void
+dtrace_vtime_disable(void)
+{
+ dtrace_vtime_state_t state, nstate = 0;
+
+ do {
+ state = dtrace_vtime_active;
+
+ switch (state) {
+ case DTRACE_VTIME_ACTIVE:
+ nstate = DTRACE_VTIME_INACTIVE;
+ break;
+
+ case DTRACE_VTIME_ACTIVE_TNF:
+ nstate = DTRACE_VTIME_INACTIVE_TNF;
+ break;
+
+ case DTRACE_VTIME_INACTIVE:
+ case DTRACE_VTIME_INACTIVE_TNF:
+ panic("DTrace virtual time already disabled");
+ /*NOTREACHED*/
+ }
+
+ } while (dtrace_cas32((uint32_t *)&dtrace_vtime_active,
+ state, nstate) != state);
+}
+
+void
+dtrace_vtime_switch(kthread_t *next)
+{
+ dtrace_icookie_t cookie;
+ hrtime_t ts;
+
+ cookie = dtrace_interrupt_disable();
+ ts = dtrace_gethrtime();
+
+ if (curthread->t_dtrace_start != 0) {
+ curthread->t_dtrace_vtime += ts - curthread->t_dtrace_start;
+ curthread->t_dtrace_start = 0;
+ }
+
+ if (next != NULL)
+ next->t_dtrace_start = ts;
+
+ dtrace_interrupt_enable(cookie);
+}
diff --git a/sys/cddl/dev/dtrace/i386/dis_tables.c b/sys/cddl/dev/dtrace/i386/dis_tables.c
new file mode 100644
index 000000000000..5a5bc25c02c3
--- /dev/null
+++ b/sys/cddl/dev/dtrace/i386/dis_tables.c
@@ -0,0 +1,3193 @@
+/*
+ * 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$
+ */
+/*
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1988 AT&T */
+/* All Rights Reserved */
+
+
+#if defined(sun)
+#pragma ident "@(#)dis_tables.c 1.11 06/03/02 SMI"
+#endif
+
+#include "dis_tables.h"
+
+/* BEGIN CSTYLED */
+
+/*
+ * Disassembly begins in dis_distable, which is equivalent to the One-byte
+ * Opcode Map in the Intel IA32 ISA Reference (page A-6 in my copy). The
+ * decoding loops then traverse out through the other tables as necessary to
+ * decode a given instruction.
+ *
+ * The behavior of this file can be controlled by one of the following flags:
+ *
+ * DIS_TEXT Include text for disassembly
+ * DIS_MEM Include memory-size calculations
+ *
+ * Either or both of these can be defined.
+ *
+ * This file is not, and will never be, cstyled. If anything, the tables should
+ * be taken out another tab stop or two so nothing overlaps.
+ */
+
+/*
+ * These functions must be provided for the consumer to do disassembly.
+ */
+#ifdef DIS_TEXT
+extern char *strncpy(char *, const char *, size_t);
+extern size_t strlen(const char *);
+extern int strcmp(const char *, const char *);
+extern int strncmp(const char *, const char *, size_t);
+extern size_t strlcat(char *, const char *, size_t);
+#endif
+
+
+#define TERM NULL /* used to indicate that the 'indirect' */
+ /* field terminates - no pointer. */
+
+/* Used to decode instructions. */
+typedef struct instable {
+ const struct instable *it_indirect; /* for decode op codes */
+ uchar_t it_adrmode;
+#ifdef DIS_TEXT
+ char it_name[NCPS];
+ uint_t it_suffix:1; /* mneu + "w", "l", or "d" */
+#endif
+#ifdef DIS_MEM
+ uint_t it_size:16;
+#endif
+ uint_t it_invalid64:1; /* opcode invalid in amd64 */
+ uint_t it_always64:1; /* 64 bit when in 64 bit mode */
+ uint_t it_invalid32:1; /* invalid in IA32 */
+ uint_t it_stackop:1; /* push/pop stack operation */
+} instable_t;
+
+/*
+ * Instruction formats.
+ */
+enum {
+ UNKNOWN,
+ MRw,
+ IMlw,
+ IMw,
+ IR,
+ OA,
+ AO,
+ MS,
+ SM,
+ Mv,
+ Mw,
+ M, /* register or memory */
+ Mb, /* register or memory, always byte sized */
+ MO, /* memory only (no registers) */
+ PREF,
+ SWAPGS,
+ R,
+ RA,
+ SEG,
+ MR,
+ RM,
+ IA,
+ MA,
+ SD,
+ AD,
+ SA,
+ D,
+ INM,
+ SO,
+ BD,
+ I,
+ P,
+ V,
+ DSHIFT, /* for double shift that has an 8-bit immediate */
+ U,
+ OVERRIDE,
+ NORM, /* instructions w/o ModR/M byte, no memory access */
+ IMPLMEM, /* instructions w/o ModR/M byte, implicit mem access */
+ O, /* for call */
+ JTAB, /* jump table */
+ IMUL, /* for 186 iimul instr */
+ CBW, /* so data16 can be evaluated for cbw and variants */
+ MvI, /* for 186 logicals */
+ ENTER, /* for 186 enter instr */
+ RMw, /* for 286 arpl instr */
+ Ib, /* for push immediate byte */
+ F, /* for 287 instructions */
+ FF, /* for 287 instructions */
+ FFC, /* for 287 instructions */
+ DM, /* 16-bit data */
+ AM, /* 16-bit addr */
+ LSEG, /* for 3-bit seg reg encoding */
+ MIb, /* for 386 logicals */
+ SREG, /* for 386 special registers */
+ PREFIX, /* a REP instruction prefix */
+ LOCK, /* a LOCK instruction prefix */
+ INT3, /* The int 3 instruction, which has a fake operand */
+ INTx, /* The normal int instruction, with explicit int num */
+ DSHIFTcl, /* for double shift that implicitly uses %cl */
+ CWD, /* so data16 can be evaluated for cwd and variants */
+ RET, /* single immediate 16-bit operand */
+ MOVZ, /* for movs and movz, with different size operands */
+ XADDB, /* for xaddb */
+ MOVSXZ, /* AMD64 mov sign extend 32 to 64 bit instruction */
+
+/*
+ * MMX/SIMD addressing modes.
+ */
+
+ MMO, /* Prefixable MMX/SIMD-Int mm/mem -> mm */
+ MMOIMPL, /* Prefixable MMX/SIMD-Int mm -> mm (mem) */
+ MMO3P, /* Prefixable MMX/SIMD-Int mm -> r32,imm8 */
+ MMOM3, /* Prefixable MMX/SIMD-Int mm -> r32 */
+ MMOS, /* Prefixable MMX/SIMD-Int mm -> mm/mem */
+ MMOMS, /* Prefixable MMX/SIMD-Int mm -> mem */
+ MMOPM, /* MMX/SIMD-Int mm/mem -> mm,imm8 */
+ MMOPRM, /* Prefixable MMX/SIMD-Int r32/mem -> mm,imm8 */
+ MMOSH, /* Prefixable MMX mm,imm8 */
+ MM, /* MMX/SIMD-Int mm/mem -> mm */
+ MMS, /* MMX/SIMD-Int mm -> mm/mem */
+ MMSH, /* MMX mm,imm8 */
+ XMMO, /* Prefixable SIMD xmm/mem -> xmm */
+ XMMOS, /* Prefixable SIMD xmm -> xmm/mem */
+ XMMOPM, /* Prefixable SIMD xmm/mem w/to xmm,imm8 */
+ XMMOMX, /* Prefixable SIMD mm/mem -> xmm */
+ XMMOX3, /* Prefixable SIMD xmm -> r32 */
+ XMMOXMM, /* Prefixable SIMD xmm/mem -> mm */
+ XMMOM, /* Prefixable SIMD xmm -> mem */
+ XMMOMS, /* Prefixable SIMD mem -> xmm */
+ XMM, /* SIMD xmm/mem -> xmm */
+ XMMXIMPL, /* SIMD xmm -> xmm (mem) */
+ XMM3P, /* SIMD xmm -> r32,imm8 */
+ XMMP, /* SIMD xmm/mem w/to xmm,imm8 */
+ XMMPRM, /* SIMD r32/mem -> xmm,imm8 */
+ XMMS, /* SIMD xmm -> xmm/mem */
+ XMMM, /* SIMD mem -> xmm */
+ XMMMS, /* SIMD xmm -> mem */
+ XMM3MX, /* SIMD r32/mem -> xmm */
+ XMM3MXS, /* SIMD xmm -> r32/mem */
+ XMMSH, /* SIMD xmm,imm8 */
+ XMMXM3, /* SIMD xmm/mem -> r32 */
+ XMMX3, /* SIMD xmm -> r32 */
+ XMMXMM, /* SIMD xmm/mem -> mm */
+ XMMMX, /* SIMD mm -> xmm */
+ XMMXM, /* SIMD xmm -> mm */
+ XMMFENCE, /* SIMD lfence or mfence */
+ XMMSFNC /* SIMD sfence (none or mem) */
+};
+
+#define FILL 0x90 /* Fill byte used for alignment (nop) */
+
+/*
+** Register numbers for the i386
+*/
+#define EAX_REGNO 0
+#define ECX_REGNO 1
+#define EDX_REGNO 2
+#define EBX_REGNO 3
+#define ESP_REGNO 4
+#define EBP_REGNO 5
+#define ESI_REGNO 6
+#define EDI_REGNO 7
+
+/*
+ * modes for immediate values
+ */
+#define MODE_NONE 0
+#define MODE_IPREL 1 /* signed IP relative value */
+#define MODE_SIGNED 2 /* sign extended immediate */
+#define MODE_IMPLIED 3 /* constant value implied from opcode */
+#define MODE_OFFSET 4 /* offset part of an address */
+
+/*
+ * The letters used in these macros are:
+ * IND - indirect to another to another table
+ * "T" - means to Terminate indirections (this is the final opcode)
+ * "S" - means "operand length suffix required"
+ * "NS" - means "no suffix" which is the operand length suffix of the opcode
+ * "Z" - means instruction size arg required
+ * "u" - means the opcode is invalid in IA32 but valid in amd64
+ * "x" - means the opcode is invalid in amd64, but not IA32
+ * "y" - means the operand size is always 64 bits in 64 bit mode
+ * "p" - means push/pop stack operation
+ */
+
+#if defined(DIS_TEXT) && defined(DIS_MEM)
+#define IND(table) {table, 0, "", 0, 0, 0, 0, 0, 0}
+#define INDx(table) {table, 0, "", 0, 0, 1, 0, 0, 0}
+#define TNS(name, amode) {TERM, amode, name, 0, 0, 0, 0, 0, 0}
+#define TNSu(name, amode) {TERM, amode, name, 0, 0, 0, 0, 1, 0}
+#define TNSx(name, amode) {TERM, amode, name, 0, 0, 1, 0, 0, 0}
+#define TNSy(name, amode) {TERM, amode, name, 0, 0, 0, 1, 0, 0}
+#define TNSyp(name, amode) {TERM, amode, name, 0, 0, 0, 1, 0, 1}
+#define TNSZ(name, amode, sz) {TERM, amode, name, 0, sz, 0, 0, 0, 0}
+#define TNSZy(name, amode, sz) {TERM, amode, name, 0, sz, 0, 1, 0, 0}
+#define TS(name, amode) {TERM, amode, name, 1, 0, 0, 0, 0, 0}
+#define TSx(name, amode) {TERM, amode, name, 1, 0, 1, 0, 0, 0}
+#define TSy(name, amode) {TERM, amode, name, 1, 0, 0, 1, 0, 0}
+#define TSp(name, amode) {TERM, amode, name, 1, 0, 0, 0, 0, 1}
+#define TSZ(name, amode, sz) {TERM, amode, name, 1, sz, 0, 0, 0, 0}
+#define TSZx(name, amode, sz) {TERM, amode, name, 1, sz, 1, 0, 0, 0}
+#define TSZy(name, amode, sz) {TERM, amode, name, 1, sz, 0, 1, 0, 0}
+#define INVALID {TERM, UNKNOWN, "", 0, 0, 0, 0, 0}
+#elif defined(DIS_TEXT)
+#define IND(table) {table, 0, "", 0, 0, 0, 0, 0}
+#define INDx(table) {table, 0, "", 0, 1, 0, 0, 0}
+#define TNS(name, amode) {TERM, amode, name, 0, 0, 0, 0, 0}
+#define TNSu(name, amode) {TERM, amode, name, 0, 0, 0, 1, 0}
+#define TNSx(name, amode) {TERM, amode, name, 0, 1, 0, 0, 0}
+#define TNSy(name, amode) {TERM, amode, name, 0, 0, 1, 0, 0}
+#define TNSyp(name, amode) {TERM, amode, name, 0, 0, 1, 0, 1}
+#define TNSZ(name, amode, sz) {TERM, amode, name, 0, 0, 0, 0, 0}
+#define TNSZy(name, amode, sz) {TERM, amode, name, 0, 0, 1, 0, 0}
+#define TS(name, amode) {TERM, amode, name, 1, 0, 0, 0, 0}
+#define TSx(name, amode) {TERM, amode, name, 1, 1, 0, 0, 0}
+#define TSy(name, amode) {TERM, amode, name, 1, 0, 1, 0, 0}
+#define TSp(name, amode) {TERM, amode, name, 1, 0, 0, 0, 1}
+#define TSZ(name, amode, sz) {TERM, amode, name, 1, 0, 0, 0, 0}
+#define TSZx(name, amode, sz) {TERM, amode, name, 1, 1, 0, 0, 0}
+#define TSZy(name, amode, sz) {TERM, amode, name, 1, 0, 1, 0, 0}
+#define INVALID {TERM, UNKNOWN, "", 0, 0, 0, 0, 0}
+#elif defined(DIS_MEM)
+#define IND(table) {table, 0, 0, 0, 0, 0, 0}
+#define INDx(table) {table, 0, 0, 1, 0, 0, 0}
+#define TNS(name, amode) {TERM, amode, 0, 0, 0, 0, 0}
+#define TNSu(name, amode) {TERM, amode, 0, 0, 0, 1, 0}
+#define TNSy(name, amode) {TERM, amode, 0, 0, 1, 0, 0}
+#define TNSyp(name, amode) {TERM, amode, 0, 0, 1, 0, 1}
+#define TNSx(name, amode) {TERM, amode, 0, 1, 0, 0, 0}
+#define TNSZ(name, amode, sz) {TERM, amode, sz, 0, 0, 0, 0}
+#define TNSZy(name, amode, sz) {TERM, amode, sz, 0, 1, 0, 0}
+#define TS(name, amode) {TERM, amode, 0, 0, 0, 0, 0}
+#define TSx(name, amode) {TERM, amode, 0, 1, 0, 0, 0}
+#define TSy(name, amode) {TERM, amode, 0, 0, 1, 0, 0}
+#define TSp(name, amode) {TERM, amode, 0, 0, 0, 0, 1}
+#define TSZ(name, amode, sz) {TERM, amode, sz, 0, 0, 0, 0}
+#define TSZx(name, amode, sz) {TERM, amode, sz, 1, 0, 0, 0}
+#define TSZy(name, amode, sz) {TERM, amode, sz, 0, 1, 0, 0}
+#define INVALID {TERM, UNKNOWN, 0, 0, 0, 0, 0}
+#else
+#define IND(table) {table[0], 0, 0, 0, 0, 0}
+#define INDx(table) {table[0], 0, 1, 0, 0, 0}
+#define TNS(name, amode) {TERM, amode, 0, 0, 0, 0}
+#define TNSu(name, amode) {TERM, amode, 0, 0, 1, 0}
+#define TNSy(name, amode) {TERM, amode, 0, 1, 0, 0}
+#define TNSyp(name, amode) {TERM, amode, 0, 1, 0, 1}
+#define TNSx(name, amode) {TERM, amode, 1, 0, 0, 0}
+#define TNSZ(name, amode, sz) {TERM, amode, 0, 0, 0, 0}
+#define TNSZy(name, amode, sz) {TERM, amode, 0, 1, 0, 0}
+#define TS(name, amode) {TERM, amode, 0, 0, 0, 0}
+#define TSx(name, amode) {TERM, amode, 1, 0, 0, 0}
+#define TSy(name, amode) {TERM, amode, 0, 1, 0, 0}
+#define TSp(name, amode) {TERM, amode, 0, 0, 0, 1}
+#define TSZ(name, amode, sz) {TERM, amode, 0, 0, 0, 0}
+#define TSZx(name, amode, sz) {TERM, amode, 1, 0, 0, 0}
+#define TSZy(name, amode, sz) {TERM, amode, 0, 1, 0, 0}
+#define INVALID {TERM, UNKNOWN, 0, 0, 0, 0}
+#endif
+
+#ifdef DIS_TEXT
+/*
+ * this decodes the r_m field for mode's 0, 1, 2 in 16 bit mode
+ */
+const char *const dis_addr16[3][8] = {
+"(%bx,%si)", "(%bx,%di)", "(%bp,%si)", "(%bp,%di)", "(%si)", "(%di)", "",
+ "(%bx)",
+"(%bx,%si)", "(%bx,%di)", "(%bp,%si)", "(%bp,%di)", "(%si)", "(%di", "(%bp)",
+ "(%bx)",
+"(%bx,%si)", "(%bx,%di)", "(%bp,%si)", "(%bp,%di)", "(%si)", "(%di)", "(%bp)",
+ "(%bx)",
+};
+
+
+/*
+ * This decodes 32 bit addressing mode r_m field for modes 0, 1, 2
+ */
+const char *const dis_addr32_mode0[16] = {
+ "(%eax)", "(%ecx)", "(%edx)", "(%ebx)", "", "", "(%esi)", "(%edi)",
+ "(%r8d)", "(%r9d)", "(%r10d)", "(%r11d)", "", "", "(%r14d)", "(%r15d)"
+};
+
+const char *const dis_addr32_mode12[16] = {
+ "(%eax)", "(%ecx)", "(%edx)", "(%ebx)", "", "(%ebp)", "(%esi)", "(%edi)",
+ "(%r8d)", "(%r9d)", "(%r10d)", "(%r11d)", "", "(%r13d)", "(%r14d)", "(%r15d)"
+};
+
+/*
+ * This decodes 64 bit addressing mode r_m field for modes 0, 1, 2
+ */
+const char *const dis_addr64_mode0[16] = {
+ "(%rax)", "(%rcx)", "(%rdx)", "(%rbx)", "", "(%rip)", "(%rsi)", "(%rdi)",
+ "(%r8)", "(%r9)", "(%r10)", "(%r11)", "(%r12)", "(%rip)", "(%r14)", "(%r15)"
+};
+const char *const dis_addr64_mode12[16] = {
+ "(%rax)", "(%rcx)", "(%rdx)", "(%rbx)", "", "(%rbp)", "(%rsi)", "(%rdi)",
+ "(%r8)", "(%r9)", "(%r10)", "(%r11)", "(%r12)", "(%r13)", "(%r14)", "(%r15)"
+};
+
+/*
+ * decode for scale from SIB byte
+ */
+const char *const dis_scale_factor[4] = { ")", ",2)", ",4)", ",8)" };
+
+/*
+ * register decoding for normal references to registers (ie. not addressing)
+ */
+const char *const dis_REG8[16] = {
+ "%al", "%cl", "%dl", "%bl", "%ah", "%ch", "%dh", "%bh",
+ "%r8b", "%r9b", "%r10b", "%r11b", "%r12b", "%r13b", "%r14b", "%r15b"
+};
+
+const char *const dis_REG8_REX[16] = {
+ "%al", "%cl", "%dl", "%bl", "%spl", "%bpl", "%sil", "%dil",
+ "%r8b", "%r9b", "%r10b", "%r11b", "%r12b", "%r13b", "%r14b", "%r15b"
+};
+
+const char *const dis_REG16[16] = {
+ "%ax", "%cx", "%dx", "%bx", "%sp", "%bp", "%si", "%di",
+ "%r8w", "%r9w", "%r10w", "%r11w", "%r12w", "%r13w", "%r14w", "%r15w"
+};
+
+const char *const dis_REG32[16] = {
+ "%eax", "%ecx", "%edx", "%ebx", "%esp", "%ebp", "%esi", "%edi",
+ "%r8d", "%r9d", "%r10d", "%r11d", "%r12d", "%r13d", "%r14d", "%r15d"
+};
+
+const char *const dis_REG64[16] = {
+ "%rax", "%rcx", "%rdx", "%rbx", "%rsp", "%rbp", "%rsi", "%rdi",
+ "%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15"
+};
+
+const char *const dis_DEBUGREG[16] = {
+ "%db0", "%db1", "%db2", "%db3", "%db4", "%db5", "%db6", "%db7",
+ "%db8", "%db9", "%db10", "%db11", "%db12", "%db13", "%db14", "%db15"
+};
+
+const char *const dis_CONTROLREG[16] = {
+ "%cr0", "%cr1", "%cr2", "%cr3", "%cr4", "%cr5?", "%cr6?", "%cr7?",
+ "%cr8", "%cr9?", "%cr10?", "%cr11?", "%cr12?", "%cr13?", "%cr14?", "%cr15?"
+};
+
+const char *const dis_TESTREG[16] = {
+ "%tr0?", "%tr1?", "%tr2?", "%tr3", "%tr4", "%tr5", "%tr6", "%tr7",
+ "%tr0?", "%tr1?", "%tr2?", "%tr3", "%tr4", "%tr5", "%tr6", "%tr7"
+};
+
+const char *const dis_MMREG[16] = {
+ "%mm0", "%mm1", "%mm2", "%mm3", "%mm4", "%mm5", "%mm6", "%mm7",
+ "%mm0", "%mm1", "%mm2", "%mm3", "%mm4", "%mm5", "%mm6", "%mm7"
+};
+
+const char *const dis_XMMREG[16] = {
+ "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7",
+ "%xmm8", "%xmm9", "%xmm10", "%xmm11", "%xmm12", "%xmm13", "%xmm14", "%xmm15"
+};
+
+const char *const dis_SEGREG[16] = {
+ "%es", "%cs", "%ss", "%ds", "%fs", "%gs", "<reserved>", "<reserved>",
+ "%es", "%cs", "%ss", "%ds", "%fs", "%gs", "<reserved>", "<reserved>"
+};
+
+/*
+ * SIMD predicate suffixes
+ */
+const char *const dis_PREDSUFFIX[8] = {
+ "eq", "lt", "le", "unord", "neq", "nlt", "nle", "ord"
+};
+
+
+
+#endif /* DIS_TEXT */
+
+
+
+
+/*
+ * "decode table" for 64 bit mode MOVSXD instruction (opcode 0x63)
+ */
+const instable_t dis_opMOVSLD = TNS("movslq",MOVSXZ);
+
+/*
+ * "decode table" for pause and clflush instructions
+ */
+const instable_t dis_opPause = TNS("pause", NORM);
+
+/*
+ * Decode table for 0x0F00 opcodes
+ */
+const instable_t dis_op0F00[8] = {
+
+/* [0] */ TNS("sldt",M), TNS("str",M), TNSy("lldt",M), TNSy("ltr",M),
+/* [4] */ TNSZ("verr",M,2), TNSZ("verw",M,2), INVALID, INVALID,
+};
+
+
+/*
+ * Decode table for 0x0F01 opcodes
+ */
+const instable_t dis_op0F01[8] = {
+
+/* [0] */ TNSZ("sgdt",MO,6), TNSZ("sidt",MO,6), TNSZ("lgdt",MO,6), TNSZ("lidt",MO,6),
+/* [4] */ TNSZ("smsw",M,2), INVALID, TNSZ("lmsw",M,2), TNS("invlpg",SWAPGS),
+};
+
+/*
+ * Decode table for 0x0F18 opcodes -- SIMD prefetch
+ */
+const instable_t dis_op0F18[8] = {
+
+/* [0] */ TNS("prefetchnta",PREF),TNS("prefetcht0",PREF), TNS("prefetcht1",PREF), TNS("prefetcht2",PREF),
+/* [4] */ INVALID, INVALID, INVALID, INVALID,
+};
+
+/*
+ * Decode table for 0x0FAE opcodes -- SIMD state save/restore
+ */
+const instable_t dis_op0FAE[8] = {
+/* [0] */ TNSZ("fxsave",M,512), TNSZ("fxrstor",M,512), TNS("ldmxcsr",M), TNS("stmxcsr",M),
+/* [4] */ INVALID, TNS("lfence",XMMFENCE), TNS("mfence",XMMFENCE), TNS("sfence",XMMSFNC),
+};
+
+/*
+ * Decode table for 0x0FBA opcodes
+ */
+
+const instable_t dis_op0FBA[8] = {
+
+/* [0] */ INVALID, INVALID, INVALID, INVALID,
+/* [4] */ TS("bt",MIb), TS("bts",MIb), TS("btr",MIb), TS("btc",MIb),
+};
+
+/*
+ * Decode table for 0x0FC7 opcode
+ */
+
+const instable_t dis_op0FC7[8] = {
+
+/* [0] */ INVALID, TNS("cmpxchg8b",M), INVALID, INVALID,
+/* [4] */ INVALID, INVALID, INVALID, INVALID,
+};
+
+
+/*
+ * Decode table for 0x0FC8 opcode -- 486 bswap instruction
+ *
+ *bit pattern: 0000 1111 1100 1reg
+ */
+const instable_t dis_op0FC8[4] = {
+/* [0] */ TNS("bswap",R), INVALID, INVALID, INVALID,
+};
+
+/*
+ * Decode table for 0x0F71, 0x0F72, and 0x0F73 opcodes -- MMX instructions
+ */
+const instable_t dis_op0F7123[4][8] = {
+{
+/* [70].0 */ INVALID, INVALID, INVALID, INVALID,
+/* .4 */ INVALID, INVALID, INVALID, INVALID,
+}, {
+/* [71].0 */ INVALID, INVALID, TNS("psrlw",MMOSH), INVALID,
+/* .4 */ TNS("psraw",MMOSH), INVALID, TNS("psllw",MMOSH), INVALID,
+}, {
+/* [72].0 */ INVALID, INVALID, TNS("psrld",MMOSH), INVALID,
+/* .4 */ TNS("psrad",MMOSH), INVALID, TNS("pslld",MMOSH), INVALID,
+}, {
+/* [73].0 */ INVALID, INVALID, TNS("psrlq",MMOSH), TNS("INVALID",MMOSH),
+/* .4 */ INVALID, INVALID, TNS("psllq",MMOSH), TNS("INVALID",MMOSH),
+} };
+
+/*
+ * Decode table for SIMD extensions to above 0x0F71-0x0F73 opcodes.
+ */
+const instable_t dis_opSIMD7123[32] = {
+/* [70].0 */ INVALID, INVALID, INVALID, INVALID,
+/* .4 */ INVALID, INVALID, INVALID, INVALID,
+
+/* [71].0 */ INVALID, INVALID, TNS("psrlw",XMMSH), INVALID,
+/* .4 */ TNS("psraw",XMMSH), INVALID, TNS("psllw",XMMSH), INVALID,
+
+/* [72].0 */ INVALID, INVALID, TNS("psrld",XMMSH), INVALID,
+/* .4 */ TNS("psrad",XMMSH), INVALID, TNS("pslld",XMMSH), INVALID,
+
+/* [73].0 */ INVALID, INVALID, TNS("psrlq",XMMSH), TNS("psrldq",XMMSH),
+/* .4 */ INVALID, INVALID, TNS("psllq",XMMSH), TNS("pslldq",XMMSH),
+};
+
+/*
+ * SIMD instructions have been wedged into the existing IA32 instruction
+ * set through the use of prefixes. That is, while 0xf0 0x58 may be
+ * addps, 0xf3 0xf0 0x58 (literally, repz addps) is a completely different
+ * instruction - addss. At present, three prefixes have been coopted in
+ * this manner - address size (0x66), repnz (0xf2) and repz (0xf3). The
+ * following tables are used to provide the prefixed instruction names.
+ * The arrays are sparse, but they're fast.
+ */
+
+/*
+ * Decode table for SIMD instructions with the address size (0x66) prefix.
+ */
+const instable_t dis_opSIMDdata16[256] = {
+/* [00] */ INVALID, INVALID, INVALID, INVALID,
+/* [04] */ INVALID, INVALID, INVALID, INVALID,
+/* [08] */ INVALID, INVALID, INVALID, INVALID,
+/* [0C] */ INVALID, INVALID, INVALID, INVALID,
+
+/* [10] */ TNSZ("movupd",XMM,16), TNSZ("movupd",XMMS,16), TNSZ("movlpd",XMMM,8), TNSZ("movlpd",XMMMS,8),
+/* [14] */ TNSZ("unpcklpd",XMM,16),TNSZ("unpckhpd",XMM,16),TNSZ("movhpd",XMMM,8), TNSZ("movhpd",XMMMS,8),
+/* [18] */ INVALID, INVALID, INVALID, INVALID,
+/* [1C] */ INVALID, INVALID, INVALID, INVALID,
+
+/* [20] */ INVALID, INVALID, INVALID, INVALID,
+/* [24] */ INVALID, INVALID, INVALID, INVALID,
+/* [28] */ TNSZ("movapd",XMM,16), TNSZ("movapd",XMMS,16), TNSZ("cvtpi2pd",XMMOMX,8),TNSZ("movntpd",XMMOMS,16),
+/* [2C] */ TNSZ("cvttpd2pi",XMMXMM,16),TNSZ("cvtpd2pi",XMMXMM,16),TNSZ("ucomisd",XMM,8),TNSZ("comisd",XMM,8),
+
+/* [30] */ INVALID, INVALID, INVALID, INVALID,
+/* [34] */ INVALID, INVALID, INVALID, INVALID,
+/* [38] */ INVALID, INVALID, INVALID, INVALID,
+/* [3C] */ INVALID, INVALID, INVALID, INVALID,
+
+/* [40] */ INVALID, INVALID, INVALID, INVALID,
+/* [44] */ INVALID, INVALID, INVALID, INVALID,
+/* [48] */ INVALID, INVALID, INVALID, INVALID,
+/* [4C] */ INVALID, INVALID, INVALID, INVALID,
+
+/* [50] */ TNS("movmskpd",XMMOX3), TNSZ("sqrtpd",XMM,16), INVALID, INVALID,
+/* [54] */ TNSZ("andpd",XMM,16), TNSZ("andnpd",XMM,16), TNSZ("orpd",XMM,16), TNSZ("xorpd",XMM,16),
+/* [58] */ TNSZ("addpd",XMM,16), TNSZ("mulpd",XMM,16), TNSZ("cvtpd2ps",XMM,16),TNSZ("cvtps2dq",XMM,16),
+/* [5C] */ TNSZ("subpd",XMM,16), TNSZ("minpd",XMM,16), TNSZ("divpd",XMM,16), TNSZ("maxpd",XMM,16),
+
+/* [60] */ TNSZ("punpcklbw",XMM,16),TNSZ("punpcklwd",XMM,16),TNSZ("punpckldq",XMM,16),TNSZ("packsswb",XMM,16),
+/* [64] */ TNSZ("pcmpgtb",XMM,16), TNSZ("pcmpgtw",XMM,16), TNSZ("pcmpgtd",XMM,16), TNSZ("packuswb",XMM,16),
+/* [68] */ TNSZ("punpckhbw",XMM,16),TNSZ("punpckhwd",XMM,16),TNSZ("punpckhdq",XMM,16),TNSZ("packssdw",XMM,16),
+/* [6C] */ TNSZ("punpcklqdq",XMM,16),TNSZ("punpckhqdq",XMM,16),TNSZ("movd",XMM3MX,4),TNSZ("movdqa",XMM,16),
+
+/* [70] */ TNSZ("pshufd",XMMP,16), INVALID, INVALID, INVALID,
+/* [74] */ TNSZ("pcmpeqb",XMM,16), TNSZ("pcmpeqw",XMM,16), TNSZ("pcmpeqd",XMM,16), INVALID,
+/* [78] */ INVALID, INVALID, INVALID, INVALID,
+/* [7C] */ INVALID, INVALID, TNSZ("movd",XMM3MXS,4), TNSZ("movdqa",XMMS,16),
+
+/* [80] */ INVALID, INVALID, INVALID, INVALID,
+/* [84] */ INVALID, INVALID, INVALID, INVALID,
+/* [88] */ INVALID, INVALID, INVALID, INVALID,
+/* [8C] */ INVALID, INVALID, INVALID, INVALID,
+
+/* [90] */ INVALID, INVALID, INVALID, INVALID,
+/* [94] */ INVALID, INVALID, INVALID, INVALID,
+/* [98] */ INVALID, INVALID, INVALID, INVALID,
+/* [9C] */ INVALID, INVALID, INVALID, INVALID,
+
+/* [A0] */ INVALID, INVALID, INVALID, INVALID,
+/* [A4] */ INVALID, INVALID, INVALID, INVALID,
+/* [A8] */ INVALID, INVALID, INVALID, INVALID,
+/* [AC] */ INVALID, INVALID, INVALID, INVALID,
+
+/* [B0] */ INVALID, INVALID, INVALID, INVALID,
+/* [B4] */ INVALID, INVALID, INVALID, INVALID,
+/* [B8] */ INVALID, INVALID, INVALID, INVALID,
+/* [BC] */ INVALID, INVALID, INVALID, INVALID,
+
+/* [C0] */ INVALID, INVALID, TNSZ("cmppd",XMMP,16), INVALID,
+/* [C4] */ TNSZ("pinsrw",XMMPRM,2),TNS("pextrw",XMM3P), TNSZ("shufpd",XMMP,16), INVALID,
+/* [C8] */ INVALID, INVALID, INVALID, INVALID,
+/* [CC] */ INVALID, INVALID, INVALID, INVALID,
+
+/* [D0] */ INVALID, TNSZ("psrlw",XMM,16), TNSZ("psrld",XMM,16), TNSZ("psrlq",XMM,16),
+/* [D4] */ TNSZ("paddq",XMM,16), TNSZ("pmullw",XMM,16), TNSZ("movq",XMMS,8), TNS("pmovmskb",XMMX3),
+/* [D8] */ TNSZ("psubusb",XMM,16), TNSZ("psubusw",XMM,16), TNSZ("pminub",XMM,16), TNSZ("pand",XMM,16),
+/* [DC] */ TNSZ("paddusb",XMM,16), TNSZ("paddusw",XMM,16), TNSZ("pmaxub",XMM,16), TNSZ("pandn",XMM,16),
+
+/* [E0] */ TNSZ("pavgb",XMM,16), TNSZ("psraw",XMM,16), TNSZ("psrad",XMM,16), TNSZ("pavgw",XMM,16),
+/* [E4] */ TNSZ("pmulhuw",XMM,16), TNSZ("pmulhw",XMM,16), TNSZ("cvttpd2dq",XMM,16),TNSZ("movntdq",XMMS,16),
+/* [E8] */ TNSZ("psubsb",XMM,16), TNSZ("psubsw",XMM,16), TNSZ("pminsw",XMM,16), TNSZ("por",XMM,16),
+/* [EC] */ TNSZ("paddsb",XMM,16), TNSZ("paddsw",XMM,16), TNSZ("pmaxsw",XMM,16), TNSZ("pxor",XMM,16),
+
+/* [F0] */ INVALID, TNSZ("psllw",XMM,16), TNSZ("pslld",XMM,16), TNSZ("psllq",XMM,16),
+/* [F4] */ TNSZ("pmuludq",XMM,16), TNSZ("pmaddwd",XMM,16), TNSZ("psadbw",XMM,16), TNSZ("maskmovdqu", XMMXIMPL,16),
+/* [F8] */ TNSZ("psubb",XMM,16), TNSZ("psubw",XMM,16), TNSZ("psubd",XMM,16), TNSZ("psubq",XMM,16),
+/* [FC] */ TNSZ("paddb",XMM,16), TNSZ("paddw",XMM,16), TNSZ("paddd",XMM,16), INVALID,
+};
+
+/*
+ * Decode table for SIMD instructions with the repnz (0xf2) prefix.
+ */
+const instable_t dis_opSIMDrepnz[256] = {
+/* [00] */ INVALID, INVALID, INVALID, INVALID,
+/* [04] */ INVALID, INVALID, INVALID, INVALID,
+/* [08] */ INVALID, INVALID, INVALID, INVALID,
+/* [0C] */ INVALID, INVALID, INVALID, INVALID,
+
+/* [10] */ TNSZ("movsd",XMM,8), TNSZ("movsd",XMMS,8), INVALID, INVALID,
+/* [14] */ INVALID, INVALID, INVALID, INVALID,
+/* [18] */ INVALID, INVALID, INVALID, INVALID,
+/* [1C] */ INVALID, INVALID, INVALID, INVALID,
+
+/* [20] */ INVALID, INVALID, INVALID, INVALID,
+/* [24] */ INVALID, INVALID, INVALID, INVALID,
+/* [28] */ INVALID, INVALID, TNSZ("cvtsi2sd",XMM3MX,4),INVALID,
+/* [2C] */ TNSZ("cvttsd2si",XMMXM3,8),TNSZ("cvtsd2si",XMMXM3,8),INVALID, INVALID,
+
+/* [30] */ INVALID, INVALID, INVALID, INVALID,
+/* [34] */ INVALID, INVALID, INVALID, INVALID,
+/* [38] */ INVALID, INVALID, INVALID, INVALID,
+/* [3C] */ INVALID, INVALID, INVALID, INVALID,
+
+/* [40] */ INVALID, INVALID, INVALID, INVALID,
+/* [44] */ INVALID, INVALID, INVALID, INVALID,
+/* [48] */ INVALID, INVALID, INVALID, INVALID,
+/* [4C] */ INVALID, INVALID, INVALID, INVALID,
+
+/* [50] */ INVALID, TNSZ("sqrtsd",XMM,8), INVALID, INVALID,
+/* [54] */ INVALID, INVALID, INVALID, INVALID,
+/* [58] */ TNSZ("addsd",XMM,8), TNSZ("mulsd",XMM,8), TNSZ("cvtsd2ss",XMM,8), INVALID,
+/* [5C] */ TNSZ("subsd",XMM,8), TNSZ("minsd",XMM,8), TNSZ("divsd",XMM,8), TNSZ("maxsd",XMM,8),
+
+/* [60] */ INVALID, INVALID, INVALID, INVALID,
+/* [64] */ INVALID, INVALID, INVALID, INVALID,
+/* [68] */ INVALID, INVALID, INVALID, INVALID,
+/* [6C] */ INVALID, INVALID, INVALID, INVALID,
+
+/* [70] */ TNSZ("pshuflw",XMMP,16),INVALID, INVALID, INVALID,
+/* [74] */ INVALID, INVALID, INVALID, INVALID,
+/* [78] */ INVALID, INVALID, INVALID, INVALID,
+/* [7C] */ INVALID, INVALID, INVALID, INVALID,
+
+/* [80] */ INVALID, INVALID, INVALID, INVALID,
+/* [84] */ INVALID, INVALID, INVALID, INVALID,
+/* [88] */ INVALID, INVALID, INVALID, INVALID,
+/* [0C] */ INVALID, INVALID, INVALID, INVALID,
+
+/* [90] */ INVALID, INVALID, INVALID, INVALID,
+/* [94] */ INVALID, INVALID, INVALID, INVALID,
+/* [98] */ INVALID, INVALID, INVALID, INVALID,
+/* [9C] */ INVALID, INVALID, INVALID, INVALID,
+
+/* [A0] */ INVALID, INVALID, INVALID, INVALID,
+/* [A4] */ INVALID, INVALID, INVALID, INVALID,
+/* [A8] */ INVALID, INVALID, INVALID, INVALID,
+/* [AC] */ INVALID, INVALID, INVALID, INVALID,
+
+/* [B0] */ INVALID, INVALID, INVALID, INVALID,
+/* [B4] */ INVALID, INVALID, INVALID, INVALID,
+/* [B8] */ INVALID, INVALID, INVALID, INVALID,
+/* [BC] */ INVALID, INVALID, INVALID, INVALID,
+
+/* [C0] */ INVALID, INVALID, TNSZ("cmpsd",XMMP,8), INVALID,
+/* [C4] */ INVALID, INVALID, INVALID, INVALID,
+/* [C8] */ INVALID, INVALID, INVALID, INVALID,
+/* [CC] */ INVALID, INVALID, INVALID, INVALID,
+
+/* [D0] */ INVALID, INVALID, INVALID, INVALID,
+/* [D4] */ INVALID, INVALID, TNS("movdq2q",XMMXM), INVALID,
+/* [D8] */ INVALID, INVALID, INVALID, INVALID,
+/* [DC] */ INVALID, INVALID, INVALID, INVALID,
+
+/* [E0] */ INVALID, INVALID, INVALID, INVALID,
+/* [E4] */ INVALID, INVALID, TNSZ("cvtpd2dq",XMM,16),INVALID,
+/* [E8] */ INVALID, INVALID, INVALID, INVALID,
+/* [EC] */ INVALID, INVALID, INVALID, INVALID,
+
+/* [F0] */ INVALID, INVALID, INVALID, INVALID,
+/* [F4] */ INVALID, INVALID, INVALID, INVALID,
+/* [F8] */ INVALID, INVALID, INVALID, INVALID,
+/* [FC] */ INVALID, INVALID, INVALID, INVALID,
+};
+
+/*
+ * Decode table for SIMD instructions with the repz (0xf3) prefix.
+ */
+const instable_t dis_opSIMDrepz[256] = {
+/* [00] */ INVALID, INVALID, INVALID, INVALID,
+/* [04] */ INVALID, INVALID, INVALID, INVALID,
+/* [08] */ INVALID, INVALID, INVALID, INVALID,
+/* [0C] */ INVALID, INVALID, INVALID, INVALID,
+
+/* [10] */ TNSZ("movss",XMM,4), TNSZ("movss",XMMS,4), INVALID, INVALID,
+/* [14] */ INVALID, INVALID, INVALID, INVALID,
+/* [18] */ INVALID, INVALID, INVALID, INVALID,
+/* [1C] */ INVALID, INVALID, INVALID, INVALID,
+
+/* [20] */ INVALID, INVALID, INVALID, INVALID,
+/* [24] */ INVALID, INVALID, INVALID, INVALID,
+/* [28] */ INVALID, INVALID, TNSZ("cvtsi2ss",XMM3MX,4),INVALID,
+/* [2C] */ TNSZ("cvttss2si",XMMXM3,4),TNSZ("cvtss2si",XMMXM3,4),INVALID, INVALID,
+
+/* [30] */ INVALID, INVALID, INVALID, INVALID,
+/* [34] */ INVALID, INVALID, INVALID, INVALID,
+/* [38] */ INVALID, INVALID, INVALID, INVALID,
+/* [3C] */ INVALID, INVALID, INVALID, INVALID,
+
+/* [40] */ INVALID, INVALID, INVALID, INVALID,
+/* [44] */ INVALID, INVALID, INVALID, INVALID,
+/* [48] */ INVALID, INVALID, INVALID, INVALID,
+/* [4C] */ INVALID, INVALID, INVALID, INVALID,
+
+/* [50] */ INVALID, TNSZ("sqrtss",XMM,4), TNSZ("rsqrtss",XMM,4), TNSZ("rcpss",XMM,4),
+/* [54] */ INVALID, INVALID, INVALID, INVALID,
+/* [58] */ TNSZ("addss",XMM,4), TNSZ("mulss",XMM,4), TNSZ("cvtss2sd",XMM,4), TNSZ("cvttps2dq",XMM,16),
+/* [5C] */ TNSZ("subss",XMM,4), TNSZ("minss",XMM,4), TNSZ("divss",XMM,4), TNSZ("maxss",XMM,4),
+
+/* [60] */ INVALID, INVALID, INVALID, INVALID,
+/* [64] */ INVALID, INVALID, INVALID, INVALID,
+/* [68] */ INVALID, INVALID, INVALID, INVALID,
+/* [6C] */ INVALID, INVALID, INVALID, TNSZ("movdqu",XMM,16),
+
+/* [70] */ TNSZ("pshufhw",XMMP,16),INVALID, INVALID, INVALID,
+/* [74] */ INVALID, INVALID, INVALID, INVALID,
+/* [78] */ INVALID, INVALID, INVALID, INVALID,
+/* [7C] */ INVALID, INVALID, TNSZ("movq",XMM,8), TNSZ("movdqu",XMMS,16),
+
+/* [80] */ INVALID, INVALID, INVALID, INVALID,
+/* [84] */ INVALID, INVALID, INVALID, INVALID,
+/* [88] */ INVALID, INVALID, INVALID, INVALID,
+/* [0C] */ INVALID, INVALID, INVALID, INVALID,
+
+/* [90] */ INVALID, INVALID, INVALID, INVALID,
+/* [94] */ INVALID, INVALID, INVALID, INVALID,
+/* [98] */ INVALID, INVALID, INVALID, INVALID,
+/* [9C] */ INVALID, INVALID, INVALID, INVALID,
+
+/* [A0] */ INVALID, INVALID, INVALID, INVALID,
+/* [A4] */ INVALID, INVALID, INVALID, INVALID,
+/* [A8] */ INVALID, INVALID, INVALID, INVALID,
+/* [AC] */ INVALID, INVALID, INVALID, INVALID,
+
+/* [B0] */ INVALID, INVALID, INVALID, INVALID,
+/* [B4] */ INVALID, INVALID, INVALID, INVALID,
+/* [B8] */ INVALID, INVALID, INVALID, INVALID,
+/* [BC] */ INVALID, INVALID, INVALID, INVALID,
+
+/* [C0] */ INVALID, INVALID, TNSZ("cmpss",XMMP,4), INVALID,
+/* [C4] */ INVALID, INVALID, INVALID, INVALID,
+/* [C8] */ INVALID, INVALID, INVALID, INVALID,
+/* [CC] */ INVALID, INVALID, INVALID, INVALID,
+
+/* [D0] */ INVALID, INVALID, INVALID, INVALID,
+/* [D4] */ INVALID, INVALID, TNS("movq2dq",XMMMX), INVALID,
+/* [D8] */ INVALID, INVALID, INVALID, INVALID,
+/* [DC] */ INVALID, INVALID, INVALID, INVALID,
+
+/* [E0] */ INVALID, INVALID, INVALID, INVALID,
+/* [E4] */ INVALID, INVALID, TNSZ("cvtdq2pd",XMM,8), INVALID,
+/* [E8] */ INVALID, INVALID, INVALID, INVALID,
+/* [EC] */ INVALID, INVALID, INVALID, INVALID,
+
+/* [F0] */ INVALID, INVALID, INVALID, INVALID,
+/* [F4] */ INVALID, INVALID, INVALID, INVALID,
+/* [F8] */ INVALID, INVALID, INVALID, INVALID,
+/* [FC] */ INVALID, INVALID, INVALID, INVALID,
+};
+
+/*
+ * Decode table for 0x0F opcodes
+ */
+
+const instable_t dis_op0F[16][16] = {
+{
+/* [00] */ IND(dis_op0F00), IND(dis_op0F01), TNS("lar",MR), TNS("lsl",MR),
+/* [04] */ INVALID, TNS("syscall",NORM), TNS("clts",NORM), TNS("sysret",NORM),
+/* [08] */ TNS("invd",NORM), TNS("wbinvd",NORM), INVALID, TNS("ud2",NORM),
+/* [0C] */ INVALID, INVALID, INVALID, INVALID,
+}, {
+/* [10] */ TNSZ("movups",XMMO,16), TNSZ("movups",XMMOS,16),TNSZ("movlps",XMMO,8), TNSZ("movlps",XMMOS,8),
+/* [14] */ TNSZ("unpcklps",XMMO,16),TNSZ("unpckhps",XMMO,16),TNSZ("movhps",XMMOM,8),TNSZ("movhps",XMMOMS,8),
+/* [18] */ IND(dis_op0F18), INVALID, INVALID, INVALID,
+/* [1C] */ INVALID, INVALID, INVALID, INVALID,
+}, {
+/* [20] */ TSy("mov",SREG), TSy("mov",SREG), TSy("mov",SREG), TSy("mov",SREG),
+/* [24] */ TSx("mov",SREG), INVALID, TSx("mov",SREG), INVALID,
+/* [28] */ TNSZ("movaps",XMMO,16), TNSZ("movaps",XMMOS,16),TNSZ("cvtpi2ps",XMMOMX,8),TNSZ("movntps",XMMOS,16),
+/* [2C] */ TNSZ("cvttps2pi",XMMOXMM,8),TNSZ("cvtps2pi",XMMOXMM,8),TNSZ("ucomiss",XMMO,4),TNSZ("comiss",XMMO,4),
+}, {
+/* [30] */ TNS("wrmsr",NORM), TNS("rdtsc",NORM), TNS("rdmsr",NORM), TNS("rdpmc",NORM),
+/* [34] */ TNSx("sysenter",NORM), TNSx("sysexit",NORM), INVALID, INVALID,
+/* [38] */ INVALID, INVALID, INVALID, INVALID,
+/* [3C] */ INVALID, INVALID, INVALID, INVALID,
+}, {
+/* [40] */ TS("cmovx.o",MR), TS("cmovx.no",MR), TS("cmovx.b",MR), TS("cmovx.ae",MR),
+/* [44] */ TS("cmovx.e",MR), TS("cmovx.ne",MR), TS("cmovx.be",MR), TS("cmovx.a",MR),
+/* [48] */ TS("cmovx.s",MR), TS("cmovx.ns",MR), TS("cmovx.pe",MR), TS("cmovx.po",MR),
+/* [4C] */ TS("cmovx.l",MR), TS("cmovx.ge",MR), TS("cmovx.le",MR), TS("cmovx.g",MR),
+}, {
+/* [50] */ TNS("movmskps",XMMOX3), TNSZ("sqrtps",XMMO,16), TNSZ("rsqrtps",XMMO,16),TNSZ("rcpps",XMMO,16),
+/* [54] */ TNSZ("andps",XMMO,16), TNSZ("andnps",XMMO,16), TNSZ("orps",XMMO,16), TNSZ("xorps",XMMO,16),
+/* [58] */ TNSZ("addps",XMMO,16), TNSZ("mulps",XMMO,16), TNSZ("cvtps2pd",XMMO,8),TNSZ("cvtdq2ps",XMMO,16),
+/* [5C] */ TNSZ("subps",XMMO,16), TNSZ("minps",XMMO,16), TNSZ("divps",XMMO,16), TNSZ("maxps",XMMO,16),
+}, {
+/* [60] */ TNSZ("punpcklbw",MMO,4),TNSZ("punpcklwd",MMO,4),TNSZ("punpckldq",MMO,4),TNSZ("packsswb",MMO,8),
+/* [64] */ TNSZ("pcmpgtb",MMO,8), TNSZ("pcmpgtw",MMO,8), TNSZ("pcmpgtd",MMO,8), TNSZ("packuswb",MMO,8),
+/* [68] */ TNSZ("punpckhbw",MMO,8),TNSZ("punpckhwd",MMO,8),TNSZ("punpckhdq",MMO,8),TNSZ("packssdw",MMO,8),
+/* [6C] */ TNSZ("INVALID",MMO,0), TNSZ("INVALID",MMO,0), TNSZ("movd",MMO,4), TNSZ("movq",MMO,8),
+}, {
+/* [70] */ TNSZ("pshufw",MMOPM,8), TNS("psrXXX",MR), TNS("psrXXX",MR), TNS("psrXXX",MR),
+/* [74] */ TNSZ("pcmpeqb",MMO,8), TNSZ("pcmpeqw",MMO,8), TNSZ("pcmpeqd",MMO,8), TNS("emms",NORM),
+/* [78] */ INVALID, INVALID, INVALID, INVALID,
+/* [7C] */ INVALID, INVALID, TNSZ("movd",MMOS,4), TNSZ("movq",MMOS,8),
+}, {
+/* [80] */ TNS("jo",D), TNS("jno",D), TNS("jb",D), TNS("jae",D),
+/* [84] */ TNS("je",D), TNS("jne",D), TNS("jbe",D), TNS("ja",D),
+/* [88] */ TNS("js",D), TNS("jns",D), TNS("jp",D), TNS("jnp",D),
+/* [8C] */ TNS("jl",D), TNS("jge",D), TNS("jle",D), TNS("jg",D),
+}, {
+/* [90] */ TNS("seto",Mb), TNS("setno",Mb), TNS("setb",Mb), TNS("setae",Mb),
+/* [94] */ TNS("sete",Mb), TNS("setne",Mb), TNS("setbe",Mb), TNS("seta",Mb),
+/* [98] */ TNS("sets",Mb), TNS("setns",Mb), TNS("setp",Mb), TNS("setnp",Mb),
+/* [9C] */ TNS("setl",Mb), TNS("setge",Mb), TNS("setle",Mb), TNS("setg",Mb),
+}, {
+/* [A0] */ TSp("push",LSEG), TSp("pop",LSEG), TNS("cpuid",NORM), TS("bt",RMw),
+/* [A4] */ TS("shld",DSHIFT), TS("shld",DSHIFTcl), INVALID, INVALID,
+/* [A8] */ TSp("push",LSEG), TSp("pop",LSEG), TNS("rsm",NORM), TS("bts",RMw),
+/* [AC] */ TS("shrd",DSHIFT), TS("shrd",DSHIFTcl), IND(dis_op0FAE), TS("imul",MRw),
+}, {
+/* [B0] */ TNS("cmpxchgb",RMw), TS("cmpxchg",RMw), TS("lss",MR), TS("btr",RMw),
+/* [B4] */ TS("lfs",MR), TS("lgs",MR), TS("movzb",MOVZ), TNS("movzwl",MOVZ),
+/* [B8] */ INVALID, INVALID, IND(dis_op0FBA), TS("btc",RMw),
+/* [BC] */ TS("bsf",MRw), TS("bsr",MRw), TS("movsb",MOVZ), TNS("movswl",MOVZ),
+}, {
+/* [C0] */ TNS("xaddb",XADDB), TS("xadd",RMw), TNSZ("cmpps",XMMOPM,16),TNS("movnti",RM),
+/* [C4] */ TNSZ("pinsrw",MMOPRM,2),TNS("pextrw",MMO3P), TNSZ("shufps",XMMOPM,16),IND(dis_op0FC7),
+/* [C8] */ INVALID, INVALID, INVALID, INVALID,
+/* [CC] */ INVALID, INVALID, INVALID, INVALID,
+}, {
+/* [D0] */ INVALID, TNSZ("psrlw",MMO,8), TNSZ("psrld",MMO,8), TNSZ("psrlq",MMO,8),
+/* [D4] */ TNSZ("paddq",MMO,8), TNSZ("pmullw",MMO,8), TNSZ("INVALID",MMO,0), TNS("pmovmskb",MMOM3),
+/* [D8] */ TNSZ("psubusb",MMO,8), TNSZ("psubusw",MMO,8), TNSZ("pminub",MMO,8), TNSZ("pand",MMO,8),
+/* [DC] */ TNSZ("paddusb",MMO,8), TNSZ("paddusw",MMO,8), TNSZ("pmaxub",MMO,8), TNSZ("pandn",MMO,8),
+}, {
+/* [E0] */ TNSZ("pavgb",MMO,8), TNSZ("psraw",MMO,8), TNSZ("psrad",MMO,8), TNSZ("pavgw",MMO,8),
+/* [E4] */ TNSZ("pmulhuw",MMO,8), TNSZ("pmulhw",MMO,8), TNS("INVALID",XMMO), TNSZ("movntq",MMOMS,8),
+/* [E8] */ TNSZ("psubsb",MMO,8), TNSZ("psubsw",MMO,8), TNSZ("pminsw",MMO,8), TNSZ("por",MMO,8),
+/* [EC] */ TNSZ("paddsb",MMO,8), TNSZ("paddsw",MMO,8), TNSZ("pmaxsw",MMO,8), TNSZ("pxor",MMO,8),
+}, {
+/* [F0] */ INVALID, TNSZ("psllw",MMO,8), TNSZ("pslld",MMO,8), TNSZ("psllq",MMO,8),
+/* [F4] */ TNSZ("pmuludq",MMO,8), TNSZ("pmaddwd",MMO,8), TNSZ("psadbw",MMO,8), TNSZ("maskmovq",MMOIMPL,8),
+/* [F8] */ TNSZ("psubb",MMO,8), TNSZ("psubw",MMO,8), TNSZ("psubd",MMO,8), TNSZ("psubq",MMO,8),
+/* [FC] */ TNSZ("paddb",MMO,8), TNSZ("paddw",MMO,8), TNSZ("paddd",MMO,8), INVALID,
+} };
+
+
+/*
+ * Decode table for 0x80 opcodes
+ */
+
+const instable_t dis_op80[8] = {
+
+/* [0] */ TNS("addb",IMlw), TNS("orb",IMw), TNS("adcb",IMlw), TNS("sbbb",IMlw),
+/* [4] */ TNS("andb",IMw), TNS("subb",IMlw), TNS("xorb",IMw), TNS("cmpb",IMlw),
+};
+
+
+/*
+ * Decode table for 0x81 opcodes.
+ */
+
+const instable_t dis_op81[8] = {
+
+/* [0] */ TS("add",IMlw), TS("or",IMw), TS("adc",IMlw), TS("sbb",IMlw),
+/* [4] */ TS("and",IMw), TS("sub",IMlw), TS("xor",IMw), TS("cmp",IMlw),
+};
+
+
+/*
+ * Decode table for 0x82 opcodes.
+ */
+
+const instable_t dis_op82[8] = {
+
+/* [0] */ TNSx("addb",IMlw), TNSx("orb",IMlw), TNSx("adcb",IMlw), TNSx("sbbb",IMlw),
+/* [4] */ TNSx("andb",IMlw), TNSx("subb",IMlw), TNSx("xorb",IMlw), TNSx("cmpb",IMlw),
+};
+/*
+ * Decode table for 0x83 opcodes.
+ */
+
+const instable_t dis_op83[8] = {
+
+/* [0] */ TS("add",IMlw), TS("or",IMlw), TS("adc",IMlw), TS("sbb",IMlw),
+/* [4] */ TS("and",IMlw), TS("sub",IMlw), TS("xor",IMlw), TS("cmp",IMlw),
+};
+
+/*
+ * Decode table for 0xC0 opcodes.
+ */
+
+const instable_t dis_opC0[8] = {
+
+/* [0] */ TNS("rolb",MvI), TNS("rorb",MvI), TNS("rclb",MvI), TNS("rcrb",MvI),
+/* [4] */ TNS("shlb",MvI), TNS("shrb",MvI), INVALID, TNS("sarb",MvI),
+};
+
+/*
+ * Decode table for 0xD0 opcodes.
+ */
+
+const instable_t dis_opD0[8] = {
+
+/* [0] */ TNS("rolb",Mv), TNS("rorb",Mv), TNS("rclb",Mv), TNS("rcrb",Mv),
+/* [4] */ TNS("shlb",Mv), TNS("shrb",Mv), TNS("salb",Mv), TNS("sarb",Mv),
+};
+
+/*
+ * Decode table for 0xC1 opcodes.
+ * 186 instruction set
+ */
+
+const instable_t dis_opC1[8] = {
+
+/* [0] */ TS("rol",MvI), TS("ror",MvI), TS("rcl",MvI), TS("rcr",MvI),
+/* [4] */ TS("shl",MvI), TS("shr",MvI), TS("sal",MvI), TS("sar",MvI),
+};
+
+/*
+ * Decode table for 0xD1 opcodes.
+ */
+
+const instable_t dis_opD1[8] = {
+
+/* [0] */ TS("rol",Mv), TS("ror",Mv), TS("rcl",Mv), TS("rcr",Mv),
+/* [4] */ TS("shl",Mv), TS("shr",Mv), TS("sal",Mv), TS("sar",Mv),
+};
+
+
+/*
+ * Decode table for 0xD2 opcodes.
+ */
+
+const instable_t dis_opD2[8] = {
+
+/* [0] */ TNS("rolb",Mv), TNS("rorb",Mv), TNS("rclb",Mv), TNS("rcrb",Mv),
+/* [4] */ TNS("shlb",Mv), TNS("shrb",Mv), TNS("salb",Mv), TNS("sarb",Mv),
+};
+/*
+ * Decode table for 0xD3 opcodes.
+ */
+
+const instable_t dis_opD3[8] = {
+
+/* [0] */ TS("rol",Mv), TS("ror",Mv), TS("rcl",Mv), TS("rcr",Mv),
+/* [4] */ TS("shl",Mv), TS("shr",Mv), TS("salb",Mv), TS("sar",Mv),
+};
+
+
+/*
+ * Decode table for 0xF6 opcodes.
+ */
+
+const instable_t dis_opF6[8] = {
+
+/* [0] */ TNS("testb",IMw), TNS("testb",IMw), TNS("notb",Mw), TNS("negb",Mw),
+/* [4] */ TNS("mulb",MA), TNS("imulb",MA), TNS("divb",MA), TNS("idivb",MA),
+};
+
+
+/*
+ * Decode table for 0xF7 opcodes.
+ */
+
+const instable_t dis_opF7[8] = {
+
+/* [0] */ TS("test",IMw), TS("test",IMw), TS("not",Mw), TS("neg",Mw),
+/* [4] */ TS("mul",MA), TS("imul",MA), TS("div",MA), TS("idiv",MA),
+};
+
+
+/*
+ * Decode table for 0xFE opcodes.
+ */
+
+const instable_t dis_opFE[8] = {
+
+/* [0] */ TNS("incb",Mw), TNS("decb",Mw), INVALID, INVALID,
+/* [4] */ INVALID, INVALID, INVALID, INVALID,
+};
+/*
+ * Decode table for 0xFF opcodes.
+ */
+
+const instable_t dis_opFF[8] = {
+
+/* [0] */ TS("inc",Mw), TS("dec",Mw), TNSyp("call",INM), TNS("lcall",INM),
+/* [4] */ TNSy("jmp",INM), TNS("ljmp",INM), TSp("push",M), INVALID,
+};
+
+/* for 287 instructions, which are a mess to decode */
+
+const instable_t dis_opFP1n2[8][8] = {
+{
+/* bit pattern: 1101 1xxx MODxx xR/M */
+/* [0,0] */ TNS("fadds",M), TNS("fmuls",M), TNS("fcoms",M), TNS("fcomps",M),
+/* [0,4] */ TNS("fsubs",M), TNS("fsubrs",M), TNS("fdivs",M), TNS("fdivrs",M),
+}, {
+/* [1,0] */ TNS("flds",M), INVALID, TNS("fsts",M), TNS("fstps",M),
+/* [1,4] */ TNSZ("fldenv",M,28), TNSZ("fldcw",M,2), TNSZ("fnstenv",M,28), TNSZ("fnstcw",M,2),
+}, {
+/* [2,0] */ TNS("fiaddl",M), TNS("fimull",M), TNS("ficoml",M), TNS("ficompl",M),
+/* [2,4] */ TNS("fisubl",M), TNS("fisubrl",M), TNS("fidivl",M), TNS("fidivrl",M),
+}, {
+/* [3,0] */ TNS("fildl",M), INVALID, TNS("fistl",M), TNS("fistpl",M),
+/* [3,4] */ INVALID, TNSZ("fldt",M,10), INVALID, TNSZ("fstpt",M,10),
+}, {
+/* [4,0] */ TNSZ("faddl",M,8), TNSZ("fmull",M,8), TNSZ("fcoml",M,8), TNSZ("fcompl",M,8),
+/* [4,1] */ TNSZ("fsubl",M,8), TNSZ("fsubrl",M,8), TNSZ("fdivl",M,8), TNSZ("fdivrl",M,8),
+}, {
+/* [5,0] */ TNSZ("fldl",M,8), INVALID, TNSZ("fstl",M,8), TNSZ("fstpl",M,8),
+/* [5,4] */ TNSZ("frstor",M,108), INVALID, TNSZ("fnsave",M,108), TNSZ("fnstsw",M,2),
+}, {
+/* [6,0] */ TNSZ("fiadd",M,2), TNSZ("fimul",M,2), TNSZ("ficom",M,2), TNSZ("ficomp",M,2),
+/* [6,4] */ TNSZ("fisub",M,2), TNSZ("fisubr",M,2), TNSZ("fidiv",M,2), TNSZ("fidivr",M,2),
+}, {
+/* [7,0] */ TNSZ("fild",M,2), INVALID, TNSZ("fist",M,2), TNSZ("fistp",M,2),
+/* [7,4] */ TNSZ("fbld",M,10), TNSZ("fildll",M,8), TNSZ("fbstp",M,10), TNSZ("fistpll",M,8),
+} };
+
+const instable_t dis_opFP3[8][8] = {
+{
+/* bit pattern: 1101 1xxx 11xx xREG */
+/* [0,0] */ TNS("fadd",FF), TNS("fmul",FF), TNS("fcom",F), TNS("fcomp",F),
+/* [0,4] */ TNS("fsub",FF), TNS("fsubr",FF), TNS("fdiv",FF), TNS("fdivr",FF),
+}, {
+/* [1,0] */ TNS("fld",F), TNS("fxch",F), TNS("fnop",NORM), TNS("fstp",F),
+/* [1,4] */ INVALID, INVALID, INVALID, INVALID,
+}, {
+/* [2,0] */ INVALID, INVALID, INVALID, INVALID,
+/* [2,4] */ INVALID, TNS("fucompp",NORM), INVALID, INVALID,
+}, {
+/* [3,0] */ INVALID, INVALID, INVALID, INVALID,
+/* [3,4] */ INVALID, INVALID, INVALID, INVALID,
+}, {
+/* [4,0] */ TNS("fadd",FF), TNS("fmul",FF), TNS("fcom",F), TNS("fcomp",F),
+/* [4,4] */ TNS("fsub",FF), TNS("fsubr",FF), TNS("fdiv",FF), TNS("fdivr",FF),
+}, {
+/* [5,0] */ TNS("ffree",F), TNS("fxch",F), TNS("fst",F), TNS("fstp",F),
+/* [5,4] */ TNS("fucom",F), TNS("fucomp",F), INVALID, INVALID,
+}, {
+/* [6,0] */ TNS("faddp",FF), TNS("fmulp",FF), TNS("fcomp",F), TNS("fcompp",NORM),
+/* [6,4] */ TNS("fsubp",FF), TNS("fsubrp",FF), TNS("fdivp",FF), TNS("fdivrp",FF),
+}, {
+/* [7,0] */ TNS("ffree",F), TNS("fxch",F), TNS("fstp",F), TNS("fstp",F),
+/* [7,4] */ TNS("fnstsw",M), TNS("fucomip",FFC), TNS("fcomip",FFC), INVALID,
+} };
+
+const instable_t dis_opFP4[4][8] = {
+{
+/* bit pattern: 1101 1001 111x xxxx */
+/* [0,0] */ TNS("fchs",NORM), TNS("fabs",NORM), INVALID, INVALID,
+/* [0,4] */ TNS("ftst",NORM), TNS("fxam",NORM), TNS("ftstp",NORM), INVALID,
+}, {
+/* [1,0] */ TNS("fld1",NORM), TNS("fldl2t",NORM), TNS("fldl2e",NORM), TNS("fldpi",NORM),
+/* [1,4] */ TNS("fldlg2",NORM), TNS("fldln2",NORM), TNS("fldz",NORM), INVALID,
+}, {
+/* [2,0] */ TNS("f2xm1",NORM), TNS("fyl2x",NORM), TNS("fptan",NORM), TNS("fpatan",NORM),
+/* [2,4] */ TNS("fxtract",NORM), TNS("fprem1",NORM), TNS("fdecstp",NORM), TNS("fincstp",NORM),
+}, {
+/* [3,0] */ TNS("fprem",NORM), TNS("fyl2xp1",NORM), TNS("fsqrt",NORM), TNS("fsincos",NORM),
+/* [3,4] */ TNS("frndint",NORM), TNS("fscale",NORM), TNS("fsin",NORM), TNS("fcos",NORM),
+} };
+
+const instable_t dis_opFP5[8] = {
+/* bit pattern: 1101 1011 111x xxxx */
+/* [0] */ TNS("feni",NORM), TNS("fdisi",NORM), TNS("fnclex",NORM), TNS("fninit",NORM),
+/* [4] */ TNS("fsetpm",NORM), TNS("frstpm",NORM), INVALID, INVALID,
+};
+
+const instable_t dis_opFP6[8] = {
+/* bit pattern: 1101 1011 11yy yxxx */
+/* [00] */ TNS("fcmov.nb",FF), TNS("fcmov.ne",FF), TNS("fcmov.nbe",FF), TNS("fcmov.nu",FF),
+/* [04] */ INVALID, TNS("fucomi",F), TNS("fcomi",F), INVALID,
+};
+
+const instable_t dis_opFP7[8] = {
+/* bit pattern: 1101 1010 11yy yxxx */
+/* [00] */ TNS("fcmov.b",FF), TNS("fcmov.e",FF), TNS("fcmov.be",FF), TNS("fcmov.u",FF),
+/* [04] */ INVALID, INVALID, INVALID, INVALID,
+};
+
+/*
+ * Main decode table for the op codes. The first two nibbles
+ * will be used as an index into the table. If there is a
+ * a need to further decode an instruction, the array to be
+ * referenced is indicated with the other two entries being
+ * empty.
+ */
+
+const instable_t dis_distable[16][16] = {
+{
+/* [0,0] */ TNS("addb",RMw), TS("add",RMw), TNS("addb",MRw), TS("add",MRw),
+/* [0,4] */ TNS("addb",IA), TS("add",IA), TSx("push",SEG), TSx("pop",SEG),
+/* [0,8] */ TNS("orb",RMw), TS("or",RMw), TNS("orb",MRw), TS("or",MRw),
+/* [0,C] */ TNS("orb",IA), TS("or",IA), TSx("push",SEG), IND(&dis_op0F[0][0]),
+}, {
+/* [1,0] */ TNS("adcb",RMw), TS("adc",RMw), TNS("adcb",MRw), TS("adc",MRw),
+/* [1,4] */ TNS("adcb",IA), TS("adc",IA), TSx("push",SEG), TSx("pop",SEG),
+/* [1,8] */ TNS("sbbb",RMw), TS("sbb",RMw), TNS("sbbb",MRw), TS("sbb",MRw),
+/* [1,C] */ TNS("sbbb",IA), TS("sbb",IA), TSx("push",SEG), TSx("pop",SEG),
+}, {
+/* [2,0] */ TNS("andb",RMw), TS("and",RMw), TNS("andb",MRw), TS("and",MRw),
+/* [2,4] */ TNS("andb",IA), TS("and",IA), TNSx("%es:",OVERRIDE), TNSx("daa",NORM),
+/* [2,8] */ TNS("subb",RMw), TS("sub",RMw), TNS("subb",MRw), TS("sub",MRw),
+/* [2,C] */ TNS("subb",IA), TS("sub",IA), TNSx("%cs:",OVERRIDE), TNSx("das",NORM),
+}, {
+/* [3,0] */ TNS("xorb",RMw), TS("xor",RMw), TNS("xorb",MRw), TS("xor",MRw),
+/* [3,4] */ TNS("xorb",IA), TS("xor",IA), TNSx("%ss:",OVERRIDE), TNSx("aaa",NORM),
+/* [3,8] */ TNS("cmpb",RMw), TS("cmp",RMw), TNS("cmpb",MRw), TS("cmp",MRw),
+/* [3,C] */ TNS("cmpb",IA), TS("cmp",IA), TNSx("%ds:",OVERRIDE), TNSx("aas",NORM),
+}, {
+/* [4,0] */ TSx("inc",R), TSx("inc",R), TSx("inc",R), TSx("inc",R),
+/* [4,4] */ TSx("inc",R), TSx("inc",R), TSx("inc",R), TSx("inc",R),
+/* [4,8] */ TSx("dec",R), TSx("dec",R), TSx("dec",R), TSx("dec",R),
+/* [4,C] */ TSx("dec",R), TSx("dec",R), TSx("dec",R), TSx("dec",R),
+}, {
+/* [5,0] */ TSp("push",R), TSp("push",R), TSp("push",R), TSp("push",R),
+/* [5,4] */ TSp("push",R), TSp("push",R), TSp("push",R), TSp("push",R),
+/* [5,8] */ TSp("pop",R), TSp("pop",R), TSp("pop",R), TSp("pop",R),
+/* [5,C] */ TSp("pop",R), TSp("pop",R), TSp("pop",R), TSp("pop",R),
+}, {
+/* [6,0] */ TSZx("pusha",IMPLMEM,28),TSZx("popa",IMPLMEM,28), TSx("bound",MR), TNS("arpl",RMw),
+/* [6,4] */ TNS("%fs:",OVERRIDE), TNS("%gs:",OVERRIDE), TNS("data16",DM), TNS("addr16",AM),
+/* [6,8] */ TSp("push",I), TS("imul",IMUL), TSp("push",Ib), TS("imul",IMUL),
+/* [6,C] */ TNSZ("insb",IMPLMEM,1), TSZ("ins",IMPLMEM,4), TNSZ("outsb",IMPLMEM,1),TSZ("outs",IMPLMEM,4),
+}, {
+/* [7,0] */ TNSy("jo",BD), TNSy("jno",BD), TNSy("jb",BD), TNSy("jae",BD),
+/* [7,4] */ TNSy("je",BD), TNSy("jne",BD), TNSy("jbe",BD), TNSy("ja",BD),
+/* [7,8] */ TNSy("js",BD), TNSy("jns",BD), TNSy("jp",BD), TNSy("jnp",BD),
+/* [7,C] */ TNSy("jl",BD), TNSy("jge",BD), TNSy("jle",BD), TNSy("jg",BD),
+}, {
+/* [8,0] */ IND(dis_op80), IND(dis_op81), INDx(dis_op82), IND(dis_op83),
+/* [8,4] */ TNS("testb",RMw), TS("test",RMw), TNS("xchgb",RMw), TS("xchg",RMw),
+/* [8,8] */ TNS("movb",RMw), TS("mov",RMw), TNS("movb",MRw), TS("mov",MRw),
+/* [8,C] */ TNS("movw",SM), TS("lea",MR), TNS("movw",MS), TSp("pop",M),
+}, {
+/* [9,0] */ TNS("nop",NORM), TS("xchg",RA), TS("xchg",RA), TS("xchg",RA),
+/* [9,4] */ TS("xchg",RA), TS("xchg",RA), TS("xchg",RA), TS("xchg",RA),
+/* [9,8] */ TNS("cXtX",CBW), TNS("cXtX",CWD), TNSx("lcall",SO), TNS("fwait",NORM),
+/* [9,C] */ TSZy("pushf",IMPLMEM,4),TSZy("popf",IMPLMEM,4), TNSx("sahf",NORM), TNSx("lahf",NORM),
+}, {
+/* [A,0] */ TNS("movb",OA), TS("mov",OA), TNS("movb",AO), TS("mov",AO),
+/* [A,4] */ TNSZ("movsb",SD,1), TS("movs",SD), TNSZ("cmpsb",SD,1), TS("cmps",SD),
+/* [A,8] */ TNS("testb",IA), TS("test",IA), TNS("stosb",AD), TS("stos",AD),
+/* [A,C] */ TNS("lodsb",SA), TS("lods",SA), TNS("scasb",AD), TS("scas",AD),
+}, {
+/* [B,0] */ TNS("movb",IR), TNS("movb",IR), TNS("movb",IR), TNS("movb",IR),
+/* [B,4] */ TNS("movb",IR), TNS("movb",IR), TNS("movb",IR), TNS("movb",IR),
+/* [B,8] */ TS("mov",IR), TS("mov",IR), TS("mov",IR), TS("mov",IR),
+/* [B,C] */ TS("mov",IR), TS("mov",IR), TS("mov",IR), TS("mov",IR),
+}, {
+/* [C,0] */ IND(dis_opC0), IND(dis_opC1), TNSyp("ret",RET), TNSyp("ret",NORM),
+/* [C,4] */ TNSx("les",MR), TNSx("lds",MR), TNS("movb",IMw), TS("mov",IMw),
+/* [C,8] */ TNSyp("enter",ENTER), TNSyp("leave",NORM), TNS("lret",RET), TNS("lret",NORM),
+/* [C,C] */ TNS("int",INT3), TNS("int",INTx), TNSx("into",NORM), TNS("iret",NORM),
+}, {
+/* [D,0] */ IND(dis_opD0), IND(dis_opD1), IND(dis_opD2), IND(dis_opD3),
+/* [D,4] */ TNSx("aam",U), TNSx("aad",U), TNSx("falc",NORM), TNSZ("xlat",IMPLMEM,1),
+
+/* 287 instructions. Note that although the indirect field */
+/* indicates opFP1n2 for further decoding, this is not necessarily */
+/* the case since the opFP arrays are not partitioned according to key1 */
+/* and key2. opFP1n2 is given only to indicate that we haven't */
+/* finished decoding the instruction. */
+/* [D,8] */ IND(&dis_opFP1n2[0][0]), IND(&dis_opFP1n2[0][0]), IND(&dis_opFP1n2[0][0]), IND(&dis_opFP1n2[0][0]),
+/* [D,C] */ IND(&dis_opFP1n2[0][0]), IND(&dis_opFP1n2[0][0]), IND(&dis_opFP1n2[0][0]), IND(&dis_opFP1n2[0][0]),
+}, {
+/* [E,0] */ TNSy("loopnz",BD), TNSy("loopz",BD), TNSy("loop",BD), TNSy("jcxz",BD),
+/* [E,4] */ TNS("inb",P), TS("in",P), TNS("outb",P), TS("out",P),
+/* [E,8] */ TNSyp("call",D), TNSy("jmp",D), TNSx("ljmp",SO), TNSy("jmp",BD),
+/* [E,C] */ TNS("inb",V), TS("in",V), TNS("outb",V), TS("out",V),
+}, {
+/* [F,0] */ TNS("lock",LOCK), TNS("icebp", NORM), TNS("repnz",PREFIX), TNS("repz",PREFIX),
+/* [F,4] */ TNS("hlt",NORM), TNS("cmc",NORM), IND(dis_opF6), IND(dis_opF7),
+/* [F,8] */ TNS("clc",NORM), TNS("stc",NORM), TNS("cli",NORM), TNS("sti",NORM),
+/* [F,C] */ TNS("cld",NORM), TNS("std",NORM), IND(dis_opFE), IND(dis_opFF),
+} };
+
+/* END CSTYLED */
+
+/*
+ * common functions to decode and disassemble an x86 or amd64 instruction
+ */
+
+/*
+ * These are the individual fields of a REX prefix. Note that a REX
+ * prefix with none of these set is still needed to:
+ * - use the MOVSXD (sign extend 32 to 64 bits) instruction
+ * - access the %sil, %dil, %bpl, %spl registers
+ */
+#define REX_W 0x08 /* 64 bit operand size when set */
+#define REX_R 0x04 /* high order bit extension of ModRM reg field */
+#define REX_X 0x02 /* high order bit extension of SIB index field */
+#define REX_B 0x01 /* extends ModRM r_m, SIB base, or opcode reg */
+
+static uint_t opnd_size; /* SIZE16, SIZE32 or SIZE64 */
+static uint_t addr_size; /* SIZE16, SIZE32 or SIZE64 */
+
+/*
+ * Even in 64 bit mode, usually only 4 byte immediate operands are supported.
+ */
+static int isize[] = {1, 2, 4, 4};
+static int isize64[] = {1, 2, 4, 8};
+
+/*
+ * Just a bunch of useful macros.
+ */
+#define WBIT(x) (x & 0x1) /* to get w bit */
+#define REGNO(x) (x & 0x7) /* to get 3 bit register */
+#define VBIT(x) ((x)>>1 & 0x1) /* to get 'v' bit */
+#define OPSIZE(osize, wbit) ((wbit) ? isize[osize] : 1)
+#define OPSIZE64(osize, wbit) ((wbit) ? isize64[osize] : 1)
+
+#define REG_ONLY 3 /* mode to indicate a register operand (not memory) */
+
+#define BYTE_OPND 0 /* w-bit value indicating byte register */
+#define LONG_OPND 1 /* w-bit value indicating opnd_size register */
+#define MM_OPND 2 /* "value" used to indicate a mmx reg */
+#define XMM_OPND 3 /* "value" used to indicate a xmm reg */
+#define SEG_OPND 4 /* "value" used to indicate a segment reg */
+#define CONTROL_OPND 5 /* "value" used to indicate a control reg */
+#define DEBUG_OPND 6 /* "value" used to indicate a debug reg */
+#define TEST_OPND 7 /* "value" used to indicate a test reg */
+#define WORD_OPND 8 /* w-bit value indicating word size reg */
+
+/*
+ * Get the next byte and separate the op code into the high and low nibbles.
+ */
+static int
+dtrace_get_opcode(dis86_t *x, uint_t *high, uint_t *low)
+{
+ int byte;
+
+ /*
+ * x86 instructions have a maximum length of 15 bytes. Bail out if
+ * we try to read more.
+ */
+ if (x->d86_len >= 15)
+ return (x->d86_error = 1);
+
+ if (x->d86_error)
+ return (1);
+ byte = x->d86_get_byte(x->d86_data);
+ if (byte < 0)
+ return (x->d86_error = 1);
+ x->d86_bytes[x->d86_len++] = byte;
+ *low = byte & 0xf; /* ----xxxx low 4 bits */
+ *high = byte >> 4 & 0xf; /* xxxx---- bits 7 to 4 */
+ return (0);
+}
+
+/*
+ * Get and decode an SIB (scaled index base) byte
+ */
+static void
+dtrace_get_SIB(dis86_t *x, uint_t *ss, uint_t *index, uint_t *base)
+{
+ int byte;
+
+ if (x->d86_error)
+ return;
+
+ byte = x->d86_get_byte(x->d86_data);
+ if (byte < 0) {
+ x->d86_error = 1;
+ return;
+ }
+ x->d86_bytes[x->d86_len++] = byte;
+
+ *base = byte & 0x7;
+ *index = (byte >> 3) & 0x7;
+ *ss = (byte >> 6) & 0x3;
+}
+
+/*
+ * Get the byte following the op code and separate it into the
+ * mode, register, and r/m fields.
+ */
+static void
+dtrace_get_modrm(dis86_t *x, uint_t *mode, uint_t *reg, uint_t *r_m)
+{
+ if (x->d86_got_modrm == 0) {
+ if (x->d86_rmindex == -1)
+ x->d86_rmindex = x->d86_len;
+ dtrace_get_SIB(x, mode, reg, r_m);
+ x->d86_got_modrm = 1;
+ }
+}
+
+/*
+ * Adjust register selection based on any REX prefix bits present.
+ */
+/*ARGSUSED*/
+static void
+dtrace_rex_adjust(uint_t rex_prefix, uint_t mode, uint_t *reg, uint_t *r_m)
+{
+ if (reg != NULL && r_m == NULL) {
+ if (rex_prefix & REX_B)
+ *reg += 8;
+ } else {
+ if (reg != NULL && (REX_R & rex_prefix) != 0)
+ *reg += 8;
+ if (r_m != NULL && (REX_B & rex_prefix) != 0)
+ *r_m += 8;
+ }
+}
+
+/*
+ * Get an immediate operand of the given size, with sign extension.
+ */
+static void
+dtrace_imm_opnd(dis86_t *x, int wbit, int size, int opindex)
+{
+ int i;
+ int byte;
+ int valsize = 0;
+
+ if (x->d86_numopnds < opindex + 1)
+ x->d86_numopnds = opindex + 1;
+
+ switch (wbit) {
+ case BYTE_OPND:
+ valsize = 1;
+ break;
+ case LONG_OPND:
+ if (x->d86_opnd_size == SIZE16)
+ valsize = 2;
+ else if (x->d86_opnd_size == SIZE32)
+ valsize = 4;
+ else
+ valsize = 8;
+ break;
+ case MM_OPND:
+ case XMM_OPND:
+ case SEG_OPND:
+ case CONTROL_OPND:
+ case DEBUG_OPND:
+ case TEST_OPND:
+ valsize = size;
+ break;
+ case WORD_OPND:
+ valsize = 2;
+ break;
+ }
+ if (valsize < size)
+ valsize = size;
+
+ if (x->d86_error)
+ return;
+ x->d86_opnd[opindex].d86_value = 0;
+ for (i = 0; i < size; ++i) {
+ byte = x->d86_get_byte(x->d86_data);
+ if (byte < 0) {
+ x->d86_error = 1;
+ return;
+ }
+ x->d86_bytes[x->d86_len++] = byte;
+ x->d86_opnd[opindex].d86_value |= (uint64_t)byte << (i * 8);
+ }
+ /* Do sign extension */
+ if (x->d86_bytes[x->d86_len - 1] & 0x80) {
+ for (; i < valsize; i++)
+ x->d86_opnd[opindex].d86_value |=
+ (uint64_t)0xff << (i* 8);
+ }
+#ifdef DIS_TEXT
+ x->d86_opnd[opindex].d86_mode = MODE_SIGNED;
+ x->d86_opnd[opindex].d86_value_size = valsize;
+ x->d86_imm_bytes += size;
+#endif
+}
+
+/*
+ * Get an ip relative operand of the given size, with sign extension.
+ */
+static void
+dtrace_disp_opnd(dis86_t *x, int wbit, int size, int opindex)
+{
+ dtrace_imm_opnd(x, wbit, size, opindex);
+#ifdef DIS_TEXT
+ x->d86_opnd[opindex].d86_mode = MODE_IPREL;
+#endif
+}
+
+/*
+ * Check to see if there is a segment override prefix pending.
+ * If so, print it in the current 'operand' location and set
+ * the override flag back to false.
+ */
+/*ARGSUSED*/
+static void
+dtrace_check_override(dis86_t *x, int opindex)
+{
+#ifdef DIS_TEXT
+ if (x->d86_seg_prefix) {
+ (void) strlcat(x->d86_opnd[opindex].d86_prefix,
+ x->d86_seg_prefix, PFIXLEN);
+ }
+#endif
+ x->d86_seg_prefix = NULL;
+}
+
+
+/*
+ * Process a single instruction Register or Memory operand.
+ *
+ * mode = addressing mode from ModRM byte
+ * r_m = r_m (or reg if mode == 3) field from ModRM byte
+ * wbit = indicates which register (8bit, 16bit, ... MMX, etc.) set to use.
+ * o = index of operand that we are processing (0, 1 or 2)
+ *
+ * the value of reg or r_m must have already been adjusted for any REX prefix.
+ */
+/*ARGSUSED*/
+static void
+dtrace_get_operand(dis86_t *x, uint_t mode, uint_t r_m, int wbit, int opindex)
+{
+ int have_SIB = 0; /* flag presence of scale-index-byte */
+ uint_t ss; /* scale-factor from opcode */
+ uint_t index; /* index register number */
+ uint_t base; /* base register number */
+ int dispsize; /* size of displacement in bytes */
+#ifdef DIS_TEXT
+ char *opnd = x->d86_opnd[opindex].d86_opnd;
+#endif
+
+ if (x->d86_numopnds < opindex + 1)
+ x->d86_numopnds = opindex + 1;
+
+ if (x->d86_error)
+ return;
+
+ /*
+ * first handle a simple register
+ */
+ if (mode == REG_ONLY) {
+#ifdef DIS_TEXT
+ switch (wbit) {
+ case MM_OPND:
+ (void) strlcat(opnd, dis_MMREG[r_m], OPLEN);
+ break;
+ case XMM_OPND:
+ (void) strlcat(opnd, dis_XMMREG[r_m], OPLEN);
+ break;
+ case SEG_OPND:
+ (void) strlcat(opnd, dis_SEGREG[r_m], OPLEN);
+ break;
+ case CONTROL_OPND:
+ (void) strlcat(opnd, dis_CONTROLREG[r_m], OPLEN);
+ break;
+ case DEBUG_OPND:
+ (void) strlcat(opnd, dis_DEBUGREG[r_m], OPLEN);
+ break;
+ case TEST_OPND:
+ (void) strlcat(opnd, dis_TESTREG[r_m], OPLEN);
+ break;
+ case BYTE_OPND:
+ if (x->d86_rex_prefix == 0)
+ (void) strlcat(opnd, dis_REG8[r_m], OPLEN);
+ else
+ (void) strlcat(opnd, dis_REG8_REX[r_m], OPLEN);
+ break;
+ case WORD_OPND:
+ (void) strlcat(opnd, dis_REG16[r_m], OPLEN);
+ break;
+ case LONG_OPND:
+ if (x->d86_opnd_size == SIZE16)
+ (void) strlcat(opnd, dis_REG16[r_m], OPLEN);
+ else if (x->d86_opnd_size == SIZE32)
+ (void) strlcat(opnd, dis_REG32[r_m], OPLEN);
+ else
+ (void) strlcat(opnd, dis_REG64[r_m], OPLEN);
+ break;
+ }
+#endif /* DIS_TEXT */
+ return;
+ }
+
+ /*
+ * if symbolic representation, skip override prefix, if any
+ */
+ dtrace_check_override(x, opindex);
+
+ /*
+ * Handle 16 bit memory references first, since they decode
+ * the mode values more simply.
+ * mode 1 is r_m + 8 bit displacement
+ * mode 2 is r_m + 16 bit displacement
+ * mode 0 is just r_m, unless r_m is 6 which is 16 bit disp
+ */
+ if (x->d86_addr_size == SIZE16) {
+ if ((mode == 0 && r_m == 6) || mode == 2)
+ dtrace_imm_opnd(x, WORD_OPND, 2, opindex);
+ else if (mode == 1)
+ dtrace_imm_opnd(x, BYTE_OPND, 1, opindex);
+#ifdef DIS_TEXT
+ if (mode == 0 && r_m == 6)
+ x->d86_opnd[opindex].d86_mode = MODE_SIGNED;
+ else if (mode == 0)
+ x->d86_opnd[opindex].d86_mode = MODE_NONE;
+ else
+ x->d86_opnd[opindex].d86_mode = MODE_OFFSET;
+ (void) strlcat(opnd, dis_addr16[mode][r_m], OPLEN);
+#endif
+ return;
+ }
+
+ /*
+ * 32 and 64 bit addressing modes are more complex since they
+ * can involve an SIB (scaled index and base) byte to decode.
+ */
+ if (r_m == ESP_REGNO || r_m == ESP_REGNO + 8) {
+ have_SIB = 1;
+ dtrace_get_SIB(x, &ss, &index, &base);
+ if (x->d86_error)
+ return;
+ if (base != 5 || mode != 0)
+ if (x->d86_rex_prefix & REX_B)
+ base += 8;
+ if (x->d86_rex_prefix & REX_X)
+ index += 8;
+ } else {
+ base = r_m;
+ }
+
+ /*
+ * Compute the displacement size and get its bytes
+ */
+ dispsize = 0;
+
+ if (mode == 1)
+ dispsize = 1;
+ else if (mode == 2)
+ dispsize = 4;
+ else if ((r_m & 7) == EBP_REGNO ||
+ (have_SIB && (base & 7) == EBP_REGNO))
+ dispsize = 4;
+
+ if (dispsize > 0) {
+ dtrace_imm_opnd(x, dispsize == 4 ? LONG_OPND : BYTE_OPND,
+ dispsize, opindex);
+ if (x->d86_error)
+ return;
+ }
+
+#ifdef DIS_TEXT
+ if (dispsize > 0)
+ x->d86_opnd[opindex].d86_mode = MODE_OFFSET;
+
+ if (have_SIB == 0) {
+ if (x->d86_mode == SIZE32) {
+ if (mode == 0)
+ (void) strlcat(opnd, dis_addr32_mode0[r_m],
+ OPLEN);
+ else
+ (void) strlcat(opnd, dis_addr32_mode12[r_m],
+ OPLEN);
+ } else {
+ if (mode == 0)
+ (void) strlcat(opnd, dis_addr64_mode0[r_m],
+ OPLEN);
+ else
+ (void) strlcat(opnd, dis_addr64_mode12[r_m],
+ OPLEN);
+ }
+ } else {
+ uint_t need_paren = 0;
+ char **regs;
+ if (x->d86_mode == SIZE32) /* NOTE this is not addr_size! */
+ regs = (char **)dis_REG32;
+ else
+ regs = (char **)dis_REG64;
+
+ /*
+ * print the base (if any)
+ */
+ if (base == EBP_REGNO && mode == 0) {
+ if (index != ESP_REGNO) {
+ (void) strlcat(opnd, "(", OPLEN);
+ need_paren = 1;
+ }
+ } else {
+ (void) strlcat(opnd, "(", OPLEN);
+ (void) strlcat(opnd, regs[base], OPLEN);
+ need_paren = 1;
+ }
+
+ /*
+ * print the index (if any)
+ */
+ if (index != ESP_REGNO) {
+ (void) strlcat(opnd, ",", OPLEN);
+ (void) strlcat(opnd, regs[index], OPLEN);
+ (void) strlcat(opnd, dis_scale_factor[ss], OPLEN);
+ } else
+ if (need_paren)
+ (void) strlcat(opnd, ")", OPLEN);
+ }
+#endif
+}
+
+/*
+ * Operand sequence for standard instruction involving one register
+ * and one register/memory operand.
+ * wbit indicates a byte(0) or opnd_size(1) operation
+ * vbit indicates direction (0 for "opcode r,r_m") or (1 for "opcode r_m, r")
+ */
+#define STANDARD_MODRM(x, mode, reg, r_m, rex_prefix, wbit, vbit) { \
+ dtrace_get_modrm(x, &mode, &reg, &r_m); \
+ dtrace_rex_adjust(rex_prefix, mode, &reg, &r_m); \
+ dtrace_get_operand(x, mode, r_m, wbit, vbit); \
+ dtrace_get_operand(x, REG_ONLY, reg, wbit, 1 - vbit); \
+}
+
+/*
+ * Similar to above, but allows for the two operands to be of different
+ * classes (ie. wbit).
+ * wbit is for the r_m operand
+ * w2 is for the reg operand
+ */
+#define MIXED_MM(x, mode, reg, r_m, rex_prefix, wbit, w2, vbit) { \
+ dtrace_get_modrm(x, &mode, &reg, &r_m); \
+ dtrace_rex_adjust(rex_prefix, mode, &reg, &r_m); \
+ dtrace_get_operand(x, mode, r_m, wbit, vbit); \
+ dtrace_get_operand(x, REG_ONLY, reg, w2, 1 - vbit); \
+}
+
+/*
+ * Similar, but for 2 operands plus an immediate.
+ */
+#define THREEOPERAND(x, mode, reg, r_m, rex_prefix, wbit, w2, immsize) { \
+ dtrace_get_modrm(x, &mode, &reg, &r_m); \
+ dtrace_rex_adjust(rex_prefix, mode, &reg, &r_m); \
+ dtrace_get_operand(x, mode, r_m, wbit, 1); \
+ dtrace_get_operand(x, REG_ONLY, reg, w2, 2); \
+ dtrace_imm_opnd(x, wbit, immsize, 0); \
+}
+
+/*
+ * Dissassemble a single x86 or amd64 instruction.
+ *
+ * Mode determines the default operating mode (SIZE16, SIZE32 or SIZE64)
+ * for interpreting instructions.
+ *
+ * returns non-zero for bad opcode
+ */
+int
+dtrace_disx86(dis86_t *x, uint_t cpu_mode)
+{
+ const instable_t *dp = NULL; /* decode table being used */
+#ifdef DIS_TEXT
+ uint_t i;
+#endif
+#ifdef DIS_MEM
+ uint_t nomem = 0;
+#define NOMEM (nomem = 1)
+#else
+#define NOMEM /* nothing */
+#endif
+ uint_t wbit = 0; /* opcode wbit, 0 is 8 bit, !0 for opnd_size */
+ uint_t w2; /* wbit value for second operand */
+ uint_t vbit;
+ uint_t mode = 0; /* mode value from ModRM byte */
+ uint_t reg; /* reg value from ModRM byte */
+ uint_t r_m; /* r_m value from ModRM byte */
+
+ uint_t opcode1; /* high nibble of 1st byte */
+ uint_t opcode2; /* low nibble of 1st byte */
+ uint_t opcode3; /* extra opcode bits usually from ModRM byte */
+ uint_t opcode4; /* high nibble of 2nd byte */
+ uint_t opcode5; /* low nibble of 2ne byte */
+ uint_t opcode6; /* high nibble of 3rd byte */
+ uint_t opcode7; /* low nibble of 3rd byte */
+ uint_t opcode_bytes = 1;
+
+ /*
+ * legacy prefixes come in 5 flavors, you should have only one of each
+ */
+ uint_t opnd_size_prefix = 0;
+ uint_t addr_size_prefix = 0;
+ uint_t segment_prefix = 0;
+ uint_t lock_prefix = 0;
+ uint_t rep_prefix = 0;
+ uint_t rex_prefix = 0; /* amd64 register extension prefix */
+ size_t off;
+
+ x->d86_len = 0;
+ x->d86_rmindex = -1;
+ x->d86_error = 0;
+#ifdef DIS_TEXT
+ x->d86_numopnds = 0;
+ x->d86_seg_prefix = NULL;
+ x->d86_mneu[0] = 0;
+ for (i = 0; i < 3; ++i) {
+ x->d86_opnd[i].d86_opnd[0] = 0;
+ x->d86_opnd[i].d86_prefix[0] = 0;
+ x->d86_opnd[i].d86_value_size = 0;
+ x->d86_opnd[i].d86_value = 0;
+ x->d86_opnd[i].d86_mode = MODE_NONE;
+ }
+#endif
+ x->d86_error = 0;
+ x->d86_memsize = 0;
+
+ if (cpu_mode == SIZE16) {
+ opnd_size = SIZE16;
+ addr_size = SIZE16;
+ } else if (cpu_mode == SIZE32) {
+ opnd_size = SIZE32;
+ addr_size = SIZE32;
+ } else {
+ opnd_size = SIZE32;
+ addr_size = SIZE64;
+ }
+
+ /*
+ * Get one opcode byte and check for zero padding that follows
+ * jump tables.
+ */
+ if (dtrace_get_opcode(x, &opcode1, &opcode2) != 0)
+ goto error;
+
+ if (opcode1 == 0 && opcode2 == 0 &&
+ x->d86_check_func != NULL && x->d86_check_func(x->d86_data)) {
+#ifdef DIS_TEXT
+ (void) strncpy(x->d86_mneu, ".byte\t0", OPLEN);
+#endif
+ goto done;
+ }
+
+ /*
+ * Gather up legacy x86 prefix bytes.
+ */
+ for (;;) {
+ uint_t *which_prefix = NULL;
+
+ dp = &dis_distable[opcode1][opcode2];
+
+ switch (dp->it_adrmode) {
+ case PREFIX:
+ which_prefix = &rep_prefix;
+ break;
+ case LOCK:
+ which_prefix = &lock_prefix;
+ break;
+ case OVERRIDE:
+ which_prefix = &segment_prefix;
+#ifdef DIS_TEXT
+ x->d86_seg_prefix = (char *)dp->it_name;
+#endif
+ if (dp->it_invalid64 && cpu_mode == SIZE64)
+ goto error;
+ break;
+ case AM:
+ which_prefix = &addr_size_prefix;
+ break;
+ case DM:
+ which_prefix = &opnd_size_prefix;
+ break;
+ }
+ if (which_prefix == NULL)
+ break;
+ *which_prefix = (opcode1 << 4) | opcode2;
+ if (dtrace_get_opcode(x, &opcode1, &opcode2) != 0)
+ goto error;
+ }
+
+ /*
+ * Handle amd64 mode PREFIX values.
+ * Some of the segment prefixes are no-ops. (only FS/GS actually work)
+ * We might have a REX prefix (opcodes 0x40-0x4f)
+ */
+ if (cpu_mode == SIZE64) {
+ if (segment_prefix != 0x64 && segment_prefix != 0x65)
+ segment_prefix = 0;
+
+ if (opcode1 == 0x4) {
+ rex_prefix = (opcode1 << 4) | opcode2;
+ if (dtrace_get_opcode(x, &opcode1, &opcode2) != 0)
+ goto error;
+ dp = &dis_distable[opcode1][opcode2];
+ }
+ }
+
+ /*
+ * Deal with selection of operand and address size now.
+ * Note that the REX.W bit being set causes opnd_size_prefix to be
+ * ignored.
+ */
+ if (cpu_mode == SIZE64) {
+ if (rex_prefix & 0x08)
+ opnd_size = SIZE64;
+ else if (opnd_size_prefix)
+ opnd_size = SIZE16;
+
+ if (addr_size_prefix)
+ addr_size = SIZE32;
+ } else if (cpu_mode == SIZE32) {
+ if (opnd_size_prefix)
+ opnd_size = SIZE16;
+ if (addr_size_prefix)
+ addr_size = SIZE16;
+ } else {
+ if (opnd_size_prefix)
+ opnd_size = SIZE32;
+ if (addr_size_prefix)
+ addr_size = SIZE32;
+ }
+
+ /*
+ * The pause instruction - a repz'd nop. This doesn't fit
+ * with any of the other prefix goop added for SSE, so we'll
+ * special-case it here.
+ */
+ if (rep_prefix == 0xf3 && opcode1 == 0x9 && opcode2 == 0x0) {
+ rep_prefix = 0;
+ dp = &dis_opPause;
+ }
+
+ /*
+ * Some 386 instructions have 2 bytes of opcode before the mod_r/m
+ * byte so we may need to perform a table indirection.
+ */
+ if (dp->it_indirect == dis_op0F[0]) {
+ if (dtrace_get_opcode(x, &opcode4, &opcode5) != 0)
+ goto error;
+ opcode_bytes = 2;
+ if (opcode4 == 0x7 && opcode5 >= 0x1 && opcode5 <= 0x3) {
+ uint_t subcode;
+
+ if (dtrace_get_opcode(x, &opcode6, &opcode7) != 0)
+ goto error;
+ opcode_bytes = 3;
+ subcode = ((opcode6 & 0x3) << 1) |
+ ((opcode7 & 0x8) >> 3);
+ dp = &dis_op0F7123[opcode5][subcode];
+ } else if ((opcode4 == 0xc) && (opcode5 >= 0x8)) {
+ dp = &dis_op0FC8[0];
+ } else {
+ dp = &dis_op0F[opcode4][opcode5];
+ }
+ }
+
+ /*
+ * If still not at a TERM decode entry, then a ModRM byte
+ * exists and its fields further decode the instruction.
+ */
+ x->d86_got_modrm = 0;
+ if (dp->it_indirect != TERM) {
+ dtrace_get_modrm(x, &mode, &opcode3, &r_m);
+ if (x->d86_error)
+ goto error;
+ reg = opcode3;
+
+ /*
+ * decode 287 instructions (D8-DF) from opcodeN
+ */
+ if (opcode1 == 0xD && opcode2 >= 0x8) {
+ if (opcode2 == 0xB && mode == 0x3 && opcode3 == 4)
+ dp = &dis_opFP5[r_m];
+ else if (opcode2 == 0xA && mode == 0x3 && opcode3 < 4)
+ dp = &dis_opFP7[opcode3];
+ else if (opcode2 == 0xB && mode == 0x3)
+ dp = &dis_opFP6[opcode3];
+ else if (opcode2 == 0x9 && mode == 0x3 && opcode3 >= 4)
+ dp = &dis_opFP4[opcode3 - 4][r_m];
+ else if (mode == 0x3)
+ dp = &dis_opFP3[opcode2 - 8][opcode3];
+ else
+ dp = &dis_opFP1n2[opcode2 - 8][opcode3];
+ } else {
+ dp = dp->it_indirect + opcode3;
+ }
+ }
+
+ /*
+ * In amd64 bit mode, ARPL opcode is changed to MOVSXD
+ * (sign extend 32bit to 64 bit)
+ */
+ if (cpu_mode == SIZE64 && opcode1 == 0x6 && opcode2 == 0x3)
+ dp = &dis_opMOVSLD;
+
+ /*
+ * at this point we should have a correct (or invalid) opcode
+ */
+ if ((cpu_mode == SIZE64 && dp->it_invalid64) ||
+ (cpu_mode != SIZE64 && dp->it_invalid32))
+ goto error;
+ if (dp->it_indirect != TERM)
+ goto error;
+
+ /*
+ * deal with MMX/SSE opcodes which are changed by prefixes
+ */
+ switch (dp->it_adrmode) {
+ case MMO:
+ case MMOIMPL:
+ case MMO3P:
+ case MMOM3:
+ case MMOMS:
+ case MMOPM:
+ case MMOPRM:
+ case MMOS:
+ case XMMO:
+ case XMMOM:
+ case XMMOMS:
+ case XMMOPM:
+ case XMMOS:
+ case XMMOMX:
+ case XMMOX3:
+ case XMMOXMM:
+ /*
+ * This is horrible. Some SIMD instructions take the
+ * form 0x0F 0x?? ..., which is easily decoded using the
+ * existing tables. Other SIMD instructions use various
+ * prefix bytes to overload existing instructions. For
+ * Example, addps is F0, 58, whereas addss is F3 (repz),
+ * F0, 58. Presumably someone got a raise for this.
+ *
+ * If we see one of the instructions which can be
+ * modified in this way (if we've got one of the SIMDO*
+ * address modes), we'll check to see if the last prefix
+ * was a repz. If it was, we strip the prefix from the
+ * mnemonic, and we indirect using the dis_opSIMDrepz
+ * table.
+ */
+
+ /*
+ * Calculate our offset in dis_op0F
+ */
+ if ((uintptr_t)dp - (uintptr_t)dis_op0F > sizeof (dis_op0F))
+ goto error;
+
+ off = ((uintptr_t)dp - (uintptr_t)dis_op0F) /
+ sizeof (instable_t);
+
+ /*
+ * Rewrite if this instruction used one of the magic prefixes.
+ */
+ if (rep_prefix) {
+ if (rep_prefix == 0xf2)
+ dp = &dis_opSIMDrepnz[off];
+ else
+ dp = &dis_opSIMDrepz[off];
+ rep_prefix = 0;
+ } else if (opnd_size_prefix) {
+ dp = &dis_opSIMDdata16[off];
+ opnd_size_prefix = 0;
+ if (opnd_size == SIZE16)
+ opnd_size = SIZE32;
+ }
+ break;
+
+ case MMOSH:
+ /*
+ * As with the "normal" SIMD instructions, the MMX
+ * shuffle instructions are overloaded. These
+ * instructions, however, are special in that they use
+ * an extra byte, and thus an extra table. As of this
+ * writing, they only use the opnd_size prefix.
+ */
+
+ /*
+ * Calculate our offset in dis_op0F7123
+ */
+ if ((uintptr_t)dp - (uintptr_t)dis_op0F7123 >
+ sizeof (dis_op0F7123))
+ goto error;
+
+ if (opnd_size_prefix) {
+ off = ((uintptr_t)dp - (uintptr_t)dis_op0F7123) /
+ sizeof (instable_t);
+ dp = &dis_opSIMD7123[off];
+ opnd_size_prefix = 0;
+ if (opnd_size == SIZE16)
+ opnd_size = SIZE32;
+ }
+ break;
+ }
+
+ /*
+ * In 64 bit mode, some opcodes automatically use opnd_size == SIZE64.
+ */
+ if (cpu_mode == SIZE64)
+ if (dp->it_always64 || (opnd_size == SIZE32 && dp->it_stackop))
+ opnd_size = SIZE64;
+
+#ifdef DIS_TEXT
+ /*
+ * At this point most instructions can format the opcode mnemonic
+ * including the prefixes.
+ */
+ if (lock_prefix)
+ (void) strlcat(x->d86_mneu, "lock ", OPLEN);
+
+ if (rep_prefix == 0xf2)
+ (void) strlcat(x->d86_mneu, "repnz ", OPLEN);
+ else if (rep_prefix == 0xf3)
+ (void) strlcat(x->d86_mneu, "repz ", OPLEN);
+
+ if (cpu_mode == SIZE64 && addr_size_prefix)
+ (void) strlcat(x->d86_mneu, "addr32 ", OPLEN);
+
+ if (dp->it_adrmode != CBW &&
+ dp->it_adrmode != CWD &&
+ dp->it_adrmode != XMMSFNC) {
+ if (strcmp(dp->it_name, "INVALID") == 0)
+ goto error;
+ (void) strlcat(x->d86_mneu, dp->it_name, OPLEN);
+ if (dp->it_suffix) {
+ char *types[] = {"", "w", "l", "q"};
+ if (opcode_bytes == 2 && opcode4 == 4) {
+ /* It's a cmovx.yy. Replace the suffix x */
+ for (i = 5; i < OPLEN; i++) {
+ if (x->d86_mneu[i] == '.')
+ break;
+ }
+ x->d86_mneu[i - 1] = *types[opnd_size];
+ } else {
+ (void) strlcat(x->d86_mneu, types[opnd_size],
+ OPLEN);
+ }
+ }
+ }
+#endif
+
+ /*
+ * Process operands based on the addressing modes.
+ */
+ x->d86_mode = cpu_mode;
+ x->d86_rex_prefix = rex_prefix;
+ x->d86_opnd_size = opnd_size;
+ x->d86_addr_size = addr_size;
+ vbit = 0; /* initialize for mem/reg -> reg */
+ switch (dp->it_adrmode) {
+ /*
+ * amd64 instruction to sign extend 32 bit reg/mem operands
+ * into 64 bit register values
+ */
+ case MOVSXZ:
+#ifdef DIS_TEXT
+ if (rex_prefix == 0)
+ (void) strncpy(x->d86_mneu, "movzld", OPLEN);
+#endif
+ dtrace_get_modrm(x, &mode, &reg, &r_m);
+ dtrace_rex_adjust(rex_prefix, mode, &reg, &r_m);
+ x->d86_opnd_size = SIZE64;
+ dtrace_get_operand(x, REG_ONLY, reg, LONG_OPND, 1);
+ x->d86_opnd_size = opnd_size = SIZE32;
+ wbit = LONG_OPND;
+ dtrace_get_operand(x, mode, r_m, wbit, 0);
+ break;
+
+ /*
+ * movsbl movsbw movsbq (0x0FBE) or movswl movswq (0x0FBF)
+ * movzbl movzbw movzbq (0x0FB6) or mobzwl movzwq (0x0FB7)
+ * wbit lives in 2nd byte, note that operands
+ * are different sized
+ */
+ case MOVZ:
+ if (rex_prefix & REX_W) {
+ /* target register size = 64 bit */
+ x->d86_mneu[5] = 'q';
+ }
+ dtrace_get_modrm(x, &mode, &reg, &r_m);
+ dtrace_rex_adjust(rex_prefix, mode, &reg, &r_m);
+ dtrace_get_operand(x, REG_ONLY, reg, LONG_OPND, 1);
+ x->d86_opnd_size = opnd_size = SIZE16;
+ wbit = WBIT(opcode5);
+ dtrace_get_operand(x, mode, r_m, wbit, 0);
+ break;
+
+ /*
+ * imul instruction, with either 8-bit or longer immediate
+ * opcode 0x6B for byte, sign-extended displacement, 0x69 for word(s)
+ */
+ case IMUL:
+ wbit = LONG_OPND;
+ THREEOPERAND(x, mode, reg, r_m, rex_prefix, wbit, LONG_OPND,
+ OPSIZE(opnd_size, opcode2 == 0x9));
+ break;
+
+ /* memory or register operand to register, with 'w' bit */
+ case MRw:
+ wbit = WBIT(opcode2);
+ STANDARD_MODRM(x, mode, reg, r_m, rex_prefix, wbit, 0);
+ break;
+
+ /* register to memory or register operand, with 'w' bit */
+ /* arpl happens to fit here also because it is odd */
+ case RMw:
+ if (opcode_bytes == 2)
+ wbit = WBIT(opcode5);
+ else
+ wbit = WBIT(opcode2);
+ STANDARD_MODRM(x, mode, reg, r_m, rex_prefix, wbit, 1);
+ break;
+
+ /* xaddb instruction */
+ case XADDB:
+ wbit = 0;
+ STANDARD_MODRM(x, mode, reg, r_m, rex_prefix, wbit, 1);
+ break;
+
+ /* MMX register to memory or register operand */
+ case MMS:
+ case MMOS:
+#ifdef DIS_TEXT
+ wbit = strcmp(dp->it_name, "movd") ? MM_OPND : LONG_OPND;
+#else
+ wbit = LONG_OPND;
+#endif
+ MIXED_MM(x, mode, reg, r_m, rex_prefix, wbit, MM_OPND, 1);
+ break;
+
+ /* MMX register to memory */
+ case MMOMS:
+ dtrace_get_modrm(x, &mode, &reg, &r_m);
+ if (mode == REG_ONLY)
+ goto error;
+ wbit = MM_OPND;
+ MIXED_MM(x, mode, reg, r_m, rex_prefix, wbit, MM_OPND, 1);
+ break;
+
+ /* Double shift. Has immediate operand specifying the shift. */
+ case DSHIFT:
+ wbit = LONG_OPND;
+ dtrace_get_modrm(x, &mode, &reg, &r_m);
+ dtrace_rex_adjust(rex_prefix, mode, &reg, &r_m);
+ dtrace_get_operand(x, mode, r_m, wbit, 2);
+ dtrace_get_operand(x, REG_ONLY, reg, LONG_OPND, 1);
+ dtrace_imm_opnd(x, wbit, 1, 0);
+ break;
+
+ /*
+ * Double shift. With no immediate operand, specifies using %cl.
+ */
+ case DSHIFTcl:
+ wbit = LONG_OPND;
+ STANDARD_MODRM(x, mode, reg, r_m, rex_prefix, wbit, 1);
+ break;
+
+ /* immediate to memory or register operand */
+ case IMlw:
+ wbit = WBIT(opcode2);
+ dtrace_rex_adjust(rex_prefix, mode, NULL, &r_m);
+ dtrace_get_operand(x, mode, r_m, wbit, 1);
+ /*
+ * Have long immediate for opcode 0x81, but not 0x80 nor 0x83
+ */
+ dtrace_imm_opnd(x, wbit, OPSIZE(opnd_size, opcode2 == 1), 0);
+ break;
+
+ /* immediate to memory or register operand with the */
+ /* 'w' bit present */
+ case IMw:
+ wbit = WBIT(opcode2);
+ dtrace_get_modrm(x, &mode, &reg, &r_m);
+ dtrace_rex_adjust(rex_prefix, mode, NULL, &r_m);
+ dtrace_get_operand(x, mode, r_m, wbit, 1);
+ dtrace_imm_opnd(x, wbit, OPSIZE(opnd_size, wbit), 0);
+ break;
+
+ /* immediate to register with register in low 3 bits */
+ /* of op code */
+ case IR:
+ /* w-bit here (with regs) is bit 3 */
+ wbit = opcode2 >>3 & 0x1;
+ reg = REGNO(opcode2);
+ dtrace_rex_adjust(rex_prefix, mode, &reg, NULL);
+ mode = REG_ONLY;
+ r_m = reg;
+ dtrace_get_operand(x, mode, r_m, wbit, 1);
+ dtrace_imm_opnd(x, wbit, OPSIZE64(opnd_size, wbit), 0);
+ break;
+
+ /* MMX immediate shift of register */
+ case MMSH:
+ case MMOSH:
+ wbit = MM_OPND;
+ goto mm_shift; /* in next case */
+
+ /* SIMD immediate shift of register */
+ case XMMSH:
+ wbit = XMM_OPND;
+mm_shift:
+ reg = REGNO(opcode7);
+ dtrace_rex_adjust(rex_prefix, mode, &reg, NULL);
+ dtrace_get_operand(x, REG_ONLY, reg, wbit, 1);
+ dtrace_imm_opnd(x, wbit, 1, 0);
+ NOMEM;
+ break;
+
+ /* accumulator to memory operand */
+ case AO:
+ vbit = 1;
+ /*FALLTHROUGH*/
+
+ /* memory operand to accumulator */
+ case OA:
+ wbit = WBIT(opcode2);
+ dtrace_get_operand(x, REG_ONLY, EAX_REGNO, wbit, 1 - vbit);
+ dtrace_imm_opnd(x, wbit, OPSIZE64(addr_size, LONG_OPND), vbit);
+#ifdef DIS_TEXT
+ x->d86_opnd[vbit].d86_mode = MODE_OFFSET;
+#endif
+ break;
+
+
+ /* segment register to memory or register operand */
+ case SM:
+ vbit = 1;
+ /*FALLTHROUGH*/
+
+ /* memory or register operand to segment register */
+ case MS:
+ dtrace_get_modrm(x, &mode, &reg, &r_m);
+ dtrace_rex_adjust(rex_prefix, mode, NULL, &r_m);
+ dtrace_get_operand(x, mode, r_m, LONG_OPND, vbit);
+ dtrace_get_operand(x, REG_ONLY, reg, SEG_OPND, 1 - vbit);
+ break;
+
+ /*
+ * rotate or shift instructions, which may shift by 1 or
+ * consult the cl register, depending on the 'v' bit
+ */
+ case Mv:
+ vbit = VBIT(opcode2);
+ wbit = WBIT(opcode2);
+ dtrace_rex_adjust(rex_prefix, mode, NULL, &r_m);
+ dtrace_get_operand(x, mode, r_m, wbit, 1);
+#ifdef DIS_TEXT
+ if (vbit) {
+ (void) strlcat(x->d86_opnd[0].d86_opnd, "%cl", OPLEN);
+ } else {
+ x->d86_opnd[0].d86_mode = MODE_SIGNED;
+ x->d86_opnd[0].d86_value_size = 1;
+ x->d86_opnd[0].d86_value = 1;
+ }
+#endif
+ break;
+ /*
+ * immediate rotate or shift instructions
+ */
+ case MvI:
+ wbit = WBIT(opcode2);
+normal_imm_mem:
+ dtrace_rex_adjust(rex_prefix, mode, NULL, &r_m);
+ dtrace_get_operand(x, mode, r_m, wbit, 1);
+ dtrace_imm_opnd(x, wbit, 1, 0);
+ break;
+
+ /* bit test instructions */
+ case MIb:
+ wbit = LONG_OPND;
+ goto normal_imm_mem;
+
+ /* single memory or register operand with 'w' bit present */
+ case Mw:
+ wbit = WBIT(opcode2);
+just_mem:
+ dtrace_get_modrm(x, &mode, &reg, &r_m);
+ dtrace_rex_adjust(rex_prefix, mode, NULL, &r_m);
+ dtrace_get_operand(x, mode, r_m, wbit, 0);
+ break;
+
+ case SWAPGS:
+ if (cpu_mode == SIZE64 && mode == 3 && r_m == 0) {
+#ifdef DIS_TEXT
+ (void) strncpy(x->d86_mneu, "swapgs", OPLEN);
+#endif
+ NOMEM;
+ break;
+ }
+ /*FALLTHROUGH*/
+
+ /* prefetch instruction - memory operand, but no memory acess */
+ case PREF:
+ NOMEM;
+ /*FALLTHROUGH*/
+
+ /* single memory or register operand */
+ case M:
+ wbit = LONG_OPND;
+ goto just_mem;
+
+ /* single memory or register byte operand */
+ case Mb:
+ wbit = BYTE_OPND;
+ goto just_mem;
+
+ case MO:
+ /* Similar to M, but only memory (no direct registers) */
+ wbit = LONG_OPND;
+ dtrace_get_modrm(x, &mode, &reg, &r_m);
+ if (mode == 3)
+ goto error;
+ dtrace_rex_adjust(rex_prefix, mode, NULL, &r_m);
+ dtrace_get_operand(x, mode, r_m, wbit, 0);
+ break;
+
+ /* move special register to register or reverse if vbit */
+ case SREG:
+ switch (opcode5) {
+
+ case 2:
+ vbit = 1;
+ /*FALLTHROUGH*/
+ case 0:
+ wbit = CONTROL_OPND;
+ break;
+
+ case 3:
+ vbit = 1;
+ /*FALLTHROUGH*/
+ case 1:
+ wbit = DEBUG_OPND;
+ break;
+
+ case 6:
+ vbit = 1;
+ /*FALLTHROUGH*/
+ case 4:
+ wbit = TEST_OPND;
+ break;
+
+ }
+ dtrace_get_modrm(x, &mode, &reg, &r_m);
+ dtrace_rex_adjust(rex_prefix, mode, &reg, &r_m);
+ dtrace_get_operand(x, REG_ONLY, reg, wbit, vbit);
+ dtrace_get_operand(x, REG_ONLY, r_m, LONG_OPND, 1 - vbit);
+ NOMEM;
+ break;
+
+ /*
+ * single register operand with register in the low 3
+ * bits of op code
+ */
+ case R:
+ if (opcode_bytes == 2)
+ reg = REGNO(opcode5);
+ else
+ reg = REGNO(opcode2);
+ dtrace_rex_adjust(rex_prefix, mode, &reg, NULL);
+ dtrace_get_operand(x, REG_ONLY, reg, LONG_OPND, 0);
+ NOMEM;
+ break;
+
+ /*
+ * register to accumulator with register in the low 3
+ * bits of op code, xchg instructions
+ */
+ case RA:
+ NOMEM;
+ reg = REGNO(opcode2);
+ dtrace_rex_adjust(rex_prefix, mode, &reg, NULL);
+ dtrace_get_operand(x, REG_ONLY, reg, LONG_OPND, 0);
+ dtrace_get_operand(x, REG_ONLY, EAX_REGNO, LONG_OPND, 1);
+ break;
+
+ /*
+ * single segment register operand, with register in
+ * bits 3-4 of op code byte
+ */
+ case SEG:
+ NOMEM;
+ reg = (x->d86_bytes[x->d86_len - 1] >> 3) & 0x3;
+ dtrace_get_operand(x, REG_ONLY, reg, SEG_OPND, 0);
+ break;
+
+ /*
+ * single segment register operand, with register in
+ * bits 3-5 of op code
+ */
+ case LSEG:
+ NOMEM;
+ /* long seg reg from opcode */
+ reg = (x->d86_bytes[x->d86_len - 1] >> 3) & 0x7;
+ dtrace_get_operand(x, REG_ONLY, reg, SEG_OPND, 0);
+ break;
+
+ /* memory or register operand to register */
+ case MR:
+ wbit = LONG_OPND;
+ STANDARD_MODRM(x, mode, reg, r_m, rex_prefix, wbit, 0);
+ break;
+
+ case RM:
+ wbit = LONG_OPND;
+ STANDARD_MODRM(x, mode, reg, r_m, rex_prefix, wbit, 1);
+ break;
+
+ /* MMX/SIMD-Int memory or mm reg to mm reg */
+ case MM:
+ case MMO:
+#ifdef DIS_TEXT
+ wbit = strcmp(dp->it_name, "movd") ? MM_OPND : LONG_OPND;
+#else
+ wbit = LONG_OPND;
+#endif
+ MIXED_MM(x, mode, reg, r_m, rex_prefix, wbit, MM_OPND, 0);
+ break;
+
+ case MMOIMPL:
+#ifdef DIS_TEXT
+ wbit = strcmp(dp->it_name, "movd") ? MM_OPND : LONG_OPND;
+#else
+ wbit = LONG_OPND;
+#endif
+ dtrace_get_modrm(x, &mode, &reg, &r_m);
+ if (mode != REG_ONLY)
+ goto error;
+
+ dtrace_rex_adjust(rex_prefix, mode, &reg, &r_m);
+ dtrace_get_operand(x, mode, r_m, wbit, 0);
+ dtrace_get_operand(x, REG_ONLY, reg, MM_OPND, 1);
+ mode = 0; /* change for memory access size... */
+ break;
+
+ /* MMX/SIMD-Int and SIMD-FP predicated mm reg to r32 */
+ case MMO3P:
+ wbit = MM_OPND;
+ goto xmm3p;
+ case XMM3P:
+ wbit = XMM_OPND;
+xmm3p:
+ dtrace_get_modrm(x, &mode, &reg, &r_m);
+ if (mode != REG_ONLY)
+ goto error;
+
+ THREEOPERAND(x, mode, reg, r_m, rex_prefix, wbit, LONG_OPND, 1);
+ NOMEM;
+ break;
+
+ /* MMX/SIMD-Int predicated r32/mem to mm reg */
+ case MMOPRM:
+ wbit = LONG_OPND;
+ w2 = MM_OPND;
+ goto xmmprm;
+ case XMMPRM:
+ wbit = LONG_OPND;
+ w2 = XMM_OPND;
+xmmprm:
+ THREEOPERAND(x, mode, reg, r_m, rex_prefix, wbit, w2, 1);
+ break;
+
+ /* MMX/SIMD-Int predicated mm/mem to mm reg */
+ case MMOPM:
+ wbit = w2 = MM_OPND;
+ goto xmmprm;
+
+ /* MMX/SIMD-Int mm reg to r32 */
+ case MMOM3:
+ NOMEM;
+ dtrace_get_modrm(x, &mode, &reg, &r_m);
+ if (mode != REG_ONLY)
+ goto error;
+ wbit = MM_OPND;
+ MIXED_MM(x, mode, reg, r_m, rex_prefix, wbit, LONG_OPND, 0);
+ break;
+
+ /* SIMD memory or xmm reg operand to xmm reg */
+ case XMM:
+ case XMMO:
+ case XMMXIMPL:
+ wbit = XMM_OPND;
+ STANDARD_MODRM(x, mode, reg, r_m, rex_prefix, wbit, 0);
+
+ if (dp->it_adrmode == XMMXIMPL && mode != REG_ONLY)
+ goto error;
+
+#ifdef DIS_TEXT
+ /*
+ * movlps and movhlps share opcodes. They differ in the
+ * addressing modes allowed for their operands.
+ * movhps and movlhps behave similarly.
+ */
+ if (mode == REG_ONLY) {
+ if (strcmp(dp->it_name, "movlps") == 0)
+ (void) strncpy(x->d86_mneu, "movhlps", OPLEN);
+ else if (strcmp(dp->it_name, "movhps") == 0)
+ (void) strncpy(x->d86_mneu, "movlhps", OPLEN);
+ }
+#endif
+ if (dp->it_adrmode == XMMXIMPL)
+ mode = 0; /* change for memory access size... */
+ break;
+
+ /* SIMD xmm reg to memory or xmm reg */
+ case XMMS:
+ case XMMOS:
+ case XMMMS:
+ case XMMOMS:
+ dtrace_get_modrm(x, &mode, &reg, &r_m);
+#ifdef DIS_TEXT
+ if ((strcmp(dp->it_name, "movlps") == 0 ||
+ strcmp(dp->it_name, "movhps") == 0 ||
+ strcmp(dp->it_name, "movntps") == 0) &&
+ mode == REG_ONLY)
+ goto error;
+#endif
+ wbit = XMM_OPND;
+ MIXED_MM(x, mode, reg, r_m, rex_prefix, wbit, XMM_OPND, 1);
+ break;
+
+ /* SIMD memory to xmm reg */
+ case XMMM:
+ case XMMOM:
+ wbit = XMM_OPND;
+ dtrace_get_modrm(x, &mode, &reg, &r_m);
+#ifdef DIS_TEXT
+ if (mode == REG_ONLY) {
+ if (strcmp(dp->it_name, "movhps") == 0)
+ (void) strncpy(x->d86_mneu, "movlhps", OPLEN);
+ else
+ goto error;
+ }
+#endif
+ MIXED_MM(x, mode, reg, r_m, rex_prefix, wbit, XMM_OPND, 0);
+ break;
+
+ /* SIMD memory or r32 to xmm reg */
+ case XMM3MX:
+ wbit = LONG_OPND;
+ MIXED_MM(x, mode, reg, r_m, rex_prefix, wbit, XMM_OPND, 0);
+ break;
+
+ case XMM3MXS:
+ wbit = LONG_OPND;
+ MIXED_MM(x, mode, reg, r_m, rex_prefix, wbit, XMM_OPND, 1);
+ break;
+
+ /* SIMD memory or mm reg to xmm reg */
+ case XMMOMX:
+ /* SIMD mm to xmm */
+ case XMMMX:
+ wbit = MM_OPND;
+ MIXED_MM(x, mode, reg, r_m, rex_prefix, wbit, XMM_OPND, 0);
+ break;
+
+ /* SIMD memory or xmm reg to mm reg */
+ case XMMXMM:
+ case XMMOXMM:
+ case XMMXM:
+ wbit = XMM_OPND;
+ MIXED_MM(x, mode, reg, r_m, rex_prefix, wbit, MM_OPND, 0);
+ break;
+
+
+ /* SIMD memory or xmm reg to r32 */
+ case XMMXM3:
+ wbit = XMM_OPND;
+ MIXED_MM(x, mode, reg, r_m, rex_prefix, wbit, LONG_OPND, 0);
+ break;
+
+ /* SIMD xmm to r32 */
+ case XMMX3:
+ case XMMOX3:
+ dtrace_get_modrm(x, &mode, &reg, &r_m);
+ if (mode != REG_ONLY)
+ goto error;
+ dtrace_rex_adjust(rex_prefix, mode, &reg, &r_m);
+ dtrace_get_operand(x, mode, r_m, XMM_OPND, 0);
+ dtrace_get_operand(x, REG_ONLY, reg, LONG_OPND, 1);
+ NOMEM;
+ break;
+
+ /* SIMD predicated memory or xmm reg with/to xmm reg */
+ case XMMP:
+ case XMMOPM:
+ wbit = XMM_OPND;
+ THREEOPERAND(x, mode, reg, r_m, rex_prefix, wbit, XMM_OPND, 1);
+
+#ifdef DIS_TEXT
+ /*
+ * cmpps and cmpss vary their instruction name based
+ * on the value of imm8. Other XMMP instructions,
+ * such as shufps, require explicit specification of
+ * the predicate.
+ */
+ if (dp->it_name[0] == 'c' &&
+ dp->it_name[1] == 'm' &&
+ dp->it_name[2] == 'p' &&
+ strlen(dp->it_name) == 5) {
+ uchar_t pred = x->d86_opnd[0].d86_value & 0xff;
+
+ if (pred >= (sizeof (dis_PREDSUFFIX) / sizeof (char *)))
+ goto error;
+
+ (void) strncpy(x->d86_mneu, "cmp", OPLEN);
+ (void) strlcat(x->d86_mneu, dis_PREDSUFFIX[pred],
+ OPLEN);
+ (void) strlcat(x->d86_mneu,
+ dp->it_name + strlen(dp->it_name) - 2,
+ OPLEN);
+ x->d86_opnd[0] = x->d86_opnd[1];
+ x->d86_opnd[1] = x->d86_opnd[2];
+ x->d86_numopnds = 2;
+ }
+#endif
+ break;
+
+ /* immediate operand to accumulator */
+ case IA:
+ wbit = WBIT(opcode2);
+ dtrace_get_operand(x, REG_ONLY, EAX_REGNO, wbit, 1);
+ dtrace_imm_opnd(x, wbit, OPSIZE(opnd_size, wbit), 0);
+ NOMEM;
+ break;
+
+ /* memory or register operand to accumulator */
+ case MA:
+ wbit = WBIT(opcode2);
+ dtrace_rex_adjust(rex_prefix, mode, NULL, &r_m);
+ dtrace_get_operand(x, mode, r_m, wbit, 0);
+ break;
+
+ /* si register to di register used to reference memory */
+ case SD:
+#ifdef DIS_TEXT
+ dtrace_check_override(x, 0);
+ x->d86_numopnds = 2;
+ if (addr_size == SIZE64) {
+ (void) strlcat(x->d86_opnd[0].d86_opnd, "(%rsi)",
+ OPLEN);
+ (void) strlcat(x->d86_opnd[1].d86_opnd, "(%rdi)",
+ OPLEN);
+ } else if (addr_size == SIZE32) {
+ (void) strlcat(x->d86_opnd[0].d86_opnd, "(%esi)",
+ OPLEN);
+ (void) strlcat(x->d86_opnd[1].d86_opnd, "(%edi)",
+ OPLEN);
+ } else {
+ (void) strlcat(x->d86_opnd[0].d86_opnd, "(%si)",
+ OPLEN);
+ (void) strlcat(x->d86_opnd[1].d86_opnd, "(%di)",
+ OPLEN);
+ }
+#endif
+ wbit = LONG_OPND;
+ break;
+
+ /* accumulator to di register */
+ case AD:
+ wbit = WBIT(opcode2);
+#ifdef DIS_TEXT
+ dtrace_check_override(x, 1);
+ x->d86_numopnds = 2;
+ dtrace_get_operand(x, REG_ONLY, EAX_REGNO, wbit, 0);
+ if (addr_size == SIZE64)
+ (void) strlcat(x->d86_opnd[1].d86_opnd, "(%rdi)",
+ OPLEN);
+ else if (addr_size == SIZE32)
+ (void) strlcat(x->d86_opnd[1].d86_opnd, "(%edi)",
+ OPLEN);
+ else
+ (void) strlcat(x->d86_opnd[1].d86_opnd, "(%di)",
+ OPLEN);
+#endif
+ break;
+
+ /* si register to accumulator */
+ case SA:
+ wbit = WBIT(opcode2);
+#ifdef DIS_TEXT
+ dtrace_check_override(x, 0);
+ x->d86_numopnds = 2;
+ if (addr_size == SIZE64)
+ (void) strlcat(x->d86_opnd[0].d86_opnd, "(%rsi)",
+ OPLEN);
+ else if (addr_size == SIZE32)
+ (void) strlcat(x->d86_opnd[0].d86_opnd, "(%esi)",
+ OPLEN);
+ else
+ (void) strlcat(x->d86_opnd[0].d86_opnd, "(%si)",
+ OPLEN);
+ dtrace_get_operand(x, REG_ONLY, EAX_REGNO, wbit, 1);
+#endif
+ break;
+
+ /*
+ * single operand, a 16/32 bit displacement
+ */
+ case D:
+ wbit = LONG_OPND;
+ dtrace_disp_opnd(x, wbit, OPSIZE(opnd_size, LONG_OPND), 0);
+ NOMEM;
+ break;
+
+ /* jmp/call indirect to memory or register operand */
+ case INM:
+#ifdef DIS_TEXT
+ (void) strlcat(x->d86_opnd[0].d86_prefix, "*", OPLEN);
+#endif
+ dtrace_rex_adjust(rex_prefix, mode, NULL, &r_m);
+ dtrace_get_operand(x, mode, r_m, LONG_OPND, 0);
+ wbit = LONG_OPND;
+ break;
+
+ /*
+ * for long jumps and long calls -- a new code segment
+ * register and an offset in IP -- stored in object
+ * code in reverse order. Note - not valid in amd64
+ */
+ case SO:
+ dtrace_check_override(x, 1);
+ wbit = LONG_OPND;
+ dtrace_imm_opnd(x, wbit, OPSIZE(opnd_size, LONG_OPND), 1);
+#ifdef DIS_TEXT
+ x->d86_opnd[1].d86_mode = MODE_SIGNED;
+#endif
+ /* will now get segment operand */
+ dtrace_imm_opnd(x, wbit, 2, 0);
+ break;
+
+ /*
+ * jmp/call. single operand, 8 bit displacement.
+ * added to current EIP in 'compofff'
+ */
+ case BD:
+ dtrace_disp_opnd(x, BYTE_OPND, 1, 0);
+ NOMEM;
+ break;
+
+ /* single 32/16 bit immediate operand */
+ case I:
+ wbit = LONG_OPND;
+ dtrace_imm_opnd(x, wbit, OPSIZE(opnd_size, LONG_OPND), 0);
+ break;
+
+ /* single 8 bit immediate operand */
+ case Ib:
+ wbit = LONG_OPND;
+ dtrace_imm_opnd(x, wbit, 1, 0);
+ break;
+
+ case ENTER:
+ wbit = LONG_OPND;
+ dtrace_imm_opnd(x, wbit, 2, 0);
+ dtrace_imm_opnd(x, wbit, 1, 1);
+ switch (opnd_size) {
+ case SIZE64:
+ x->d86_memsize = (x->d86_opnd[1].d86_value + 1) * 8;
+ break;
+ case SIZE32:
+ x->d86_memsize = (x->d86_opnd[1].d86_value + 1) * 4;
+ break;
+ case SIZE16:
+ x->d86_memsize = (x->d86_opnd[1].d86_value + 1) * 2;
+ break;
+ }
+
+ break;
+
+ /* 16-bit immediate operand */
+ case RET:
+ wbit = LONG_OPND;
+ dtrace_imm_opnd(x, wbit, 2, 0);
+ break;
+
+ /* single 8 bit port operand */
+ case P:
+ dtrace_check_override(x, 0);
+ dtrace_imm_opnd(x, BYTE_OPND, 1, 0);
+ NOMEM;
+ break;
+
+ /* single operand, dx register (variable port instruction) */
+ case V:
+ x->d86_numopnds = 1;
+ dtrace_check_override(x, 0);
+#ifdef DIS_TEXT
+ (void) strlcat(x->d86_opnd[0].d86_opnd, "(%dx)", OPLEN);
+#endif
+ NOMEM;
+ break;
+
+ /*
+ * The int instruction, which has two forms:
+ * int 3 (breakpoint) or
+ * int n, where n is indicated in the subsequent
+ * byte (format Ib). The int 3 instruction (opcode 0xCC),
+ * where, although the 3 looks like an operand,
+ * it is implied by the opcode. It must be converted
+ * to the correct base and output.
+ */
+ case INT3:
+#ifdef DIS_TEXT
+ x->d86_numopnds = 1;
+ x->d86_opnd[0].d86_mode = MODE_SIGNED;
+ x->d86_opnd[0].d86_value_size = 1;
+ x->d86_opnd[0].d86_value = 3;
+#endif
+ NOMEM;
+ break;
+
+ /* single 8 bit immediate operand */
+ case INTx:
+ dtrace_imm_opnd(x, BYTE_OPND, 1, 0);
+ NOMEM;
+ break;
+
+ /* an unused byte must be discarded */
+ case U:
+ if (x->d86_get_byte(x->d86_data) < 0)
+ goto error;
+ x->d86_len++;
+ NOMEM;
+ break;
+
+ case CBW:
+#ifdef DIS_TEXT
+ if (opnd_size == SIZE16)
+ (void) strlcat(x->d86_mneu, "cbtw", OPLEN);
+ else if (opnd_size == SIZE32)
+ (void) strlcat(x->d86_mneu, "cwtl", OPLEN);
+ else
+ (void) strlcat(x->d86_mneu, "cltq", OPLEN);
+#endif
+ wbit = LONG_OPND;
+ NOMEM;
+ break;
+
+ case CWD:
+#ifdef DIS_TEXT
+ if (opnd_size == SIZE16)
+ (void) strlcat(x->d86_mneu, "cwtd", OPLEN);
+ else if (opnd_size == SIZE32)
+ (void) strlcat(x->d86_mneu, "cltd", OPLEN);
+ else
+ (void) strlcat(x->d86_mneu, "cqtd", OPLEN);
+#endif
+ wbit = LONG_OPND;
+ NOMEM;
+ break;
+
+ case XMMSFNC:
+ /*
+ * sfence is sfence if mode is REG_ONLY. If mode isn't
+ * REG_ONLY, mnemonic should be 'clflush'.
+ */
+ dtrace_get_modrm(x, &mode, &reg, &r_m);
+
+ /* sfence doesn't take operands */
+#ifdef DIS_TEXT
+ if (mode == REG_ONLY) {
+ (void) strlcat(x->d86_mneu, "sfence", OPLEN);
+ } else {
+ (void) strlcat(x->d86_mneu, "clflush", OPLEN);
+ dtrace_rex_adjust(rex_prefix, mode, &reg, &r_m);
+ dtrace_get_operand(x, mode, r_m, BYTE_OPND, 0);
+ NOMEM;
+ }
+#else
+ if (mode != REG_ONLY) {
+ dtrace_rex_adjust(rex_prefix, mode, &reg, &r_m);
+ dtrace_get_operand(x, mode, r_m, BYTE_OPND, 0);
+ NOMEM;
+ }
+#endif
+ break;
+
+ /*
+ * no disassembly, the mnemonic was all there was so go on
+ */
+ case NORM:
+ if (dp->it_invalid32 && cpu_mode != SIZE64)
+ goto error;
+ NOMEM;
+ /*FALLTHROUGH*/
+ case IMPLMEM:
+ break;
+
+ case XMMFENCE:
+ /*
+ * Only the following exact byte sequences are allowed:
+ *
+ * 0f ae e8 lfence
+ * 0f ae f0 mfence
+ */
+ if ((uint8_t)x->d86_bytes[x->d86_len - 1] != 0xe8 &&
+ (uint8_t)x->d86_bytes[x->d86_len - 1] != 0xf0)
+ goto error;
+
+ break;
+
+
+ /* float reg */
+ case F:
+#ifdef DIS_TEXT
+ x->d86_numopnds = 1;
+ (void) strlcat(x->d86_opnd[0].d86_opnd, "%st(X)", OPLEN);
+ x->d86_opnd[0].d86_opnd[4] = r_m + '0';
+#endif
+ NOMEM;
+ break;
+
+ /* float reg to float reg, with ret bit present */
+ case FF:
+ vbit = opcode2 >> 2 & 0x1; /* vbit = 1: st -> st(i) */
+ /*FALLTHROUGH*/
+ case FFC: /* case for vbit always = 0 */
+#ifdef DIS_TEXT
+ x->d86_numopnds = 2;
+ (void) strlcat(x->d86_opnd[1 - vbit].d86_opnd, "%st", OPLEN);
+ (void) strlcat(x->d86_opnd[vbit].d86_opnd, "%st(X)", OPLEN);
+ x->d86_opnd[vbit].d86_opnd[4] = r_m + '0';
+#endif
+ NOMEM;
+ break;
+
+ /* an invalid op code */
+ case AM:
+ case DM:
+ case OVERRIDE:
+ case PREFIX:
+ case UNKNOWN:
+ NOMEM;
+ default:
+ goto error;
+ } /* end switch */
+ if (x->d86_error)
+ goto error;
+
+done:
+#ifdef DIS_MEM
+ /*
+ * compute the size of any memory accessed by the instruction
+ */
+ if (x->d86_memsize != 0) {
+ return (0);
+ } else if (dp->it_stackop) {
+ switch (opnd_size) {
+ case SIZE16:
+ x->d86_memsize = 2;
+ break;
+ case SIZE32:
+ x->d86_memsize = 4;
+ break;
+ case SIZE64:
+ x->d86_memsize = 8;
+ break;
+ }
+ } else if (nomem || mode == REG_ONLY) {
+ x->d86_memsize = 0;
+
+ } else if (dp->it_size != 0) {
+ /*
+ * In 64 bit mode descriptor table entries
+ * go up to 10 bytes and popf/pushf are always 8 bytes
+ */
+ if (x->d86_mode == SIZE64 && dp->it_size == 6)
+ x->d86_memsize = 10;
+ else if (x->d86_mode == SIZE64 && opcode1 == 0x9 &&
+ (opcode2 == 0xc || opcode2 == 0xd))
+ x->d86_memsize = 8;
+ else
+ x->d86_memsize = dp->it_size;
+
+ } else if (wbit == 0) {
+ x->d86_memsize = 1;
+
+ } else if (wbit == LONG_OPND) {
+ if (opnd_size == SIZE64)
+ x->d86_memsize = 8;
+ else if (opnd_size == SIZE32)
+ x->d86_memsize = 4;
+ else
+ x->d86_memsize = 2;
+
+ } else if (wbit == SEG_OPND) {
+ x->d86_memsize = 4;
+
+ } else {
+ x->d86_memsize = 8;
+ }
+#endif
+ return (0);
+
+error:
+#ifdef DIS_TEXT
+ (void) strlcat(x->d86_mneu, "undef", OPLEN);
+#endif
+ return (1);
+}
+
+#ifdef DIS_TEXT
+
+/*
+ * Some instructions should have immediate operands printed
+ * as unsigned integers. We compare against this table.
+ */
+static char *unsigned_ops[] = {
+ "or", "and", "xor", "test", "in", "out", "lcall", "ljmp",
+ "rcr", "rcl", "ror", "rol", "shl", "shr", "sal", "psr", "psl",
+ 0
+};
+
+static int
+isunsigned_op(char *opcode)
+{
+ char *where;
+ int i;
+ int is_unsigned = 0;
+
+ /*
+ * Work back to start of last mnemonic, since we may have
+ * prefixes on some opcodes.
+ */
+ where = opcode + strlen(opcode) - 1;
+ while (where > opcode && *where != ' ')
+ --where;
+ if (*where == ' ')
+ ++where;
+
+ for (i = 0; unsigned_ops[i]; ++i) {
+ if (strncmp(where, unsigned_ops[i],
+ strlen(unsigned_ops[i])))
+ continue;
+ is_unsigned = 1;
+ break;
+ }
+ return (is_unsigned);
+}
+
+/* ARGSUSED */
+void
+dtrace_disx86_str(dis86_t *dis, uint_t mode, uintptr_t pc, char *buf,
+ size_t buflen)
+{
+ int i;
+
+ dis->d86_sprintf_func(buf, buflen, "%-6s ", dis->d86_mneu);
+
+ /*
+ * For PC-relative jumps, the pc is really the next pc after executing
+ * this instruction, so increment it appropriately.
+ */
+ pc += dis->d86_len;
+
+ for (i = 0; i < dis->d86_numopnds; i++) {
+ d86opnd_t *op = &dis->d86_opnd[i];
+ int64_t sv;
+ uint64_t mask;
+
+ if (i != 0)
+ (void) strlcat(buf, ",", buflen);
+
+ (void) strlcat(buf, op->d86_prefix, buflen);
+
+ sv = op->d86_value;
+
+ switch (op->d86_mode) {
+
+ case MODE_NONE:
+
+ (void) strlcat(buf, op->d86_opnd, buflen);
+ break;
+
+ case MODE_SIGNED:
+ case MODE_IMPLIED:
+ case MODE_OFFSET:
+
+ if (dis->d86_seg_prefix)
+ (void) strlcat(buf, dis->d86_seg_prefix,
+ buflen);
+
+ switch (op->d86_value_size) {
+ case 1:
+ sv = (int8_t)sv;
+ mask = 0xff;
+ break;
+ case 2:
+ sv = (int16_t)sv;
+ mask = 0xffff;
+ break;
+ case 4:
+ sv = (int32_t)sv;
+ mask = 0xffffffff;
+ break;
+ case 8:
+ mask = 0xffffffffffffffffULL;
+ break;
+ }
+
+ if (op->d86_mode == MODE_SIGNED ||
+ op->d86_mode == MODE_IMPLIED)
+ (void) strlcat(buf, "$", buflen);
+
+ if (sv < 0 && sv > -0xffff &&
+ !isunsigned_op(dis->d86_mneu)) {
+ dis->d86_sprintf_func(buf + strlen(buf),
+ buflen - strlen(buf),
+ (dis->d86_flags & DIS_OP_OCTAL) ?
+ "-0%llo" : "-0x%llx", -sv & mask);
+ } else {
+ dis->d86_sprintf_func(buf + strlen(buf),
+ buflen - strlen(buf),
+ (dis->d86_flags & DIS_OP_OCTAL) ?
+ "0%llo" : "0x%llx", sv & mask);
+ }
+ (void) strlcat(buf, op->d86_opnd, buflen);
+ break;
+
+ case MODE_IPREL:
+
+ switch (op->d86_value_size) {
+ case 1:
+ sv = (int8_t)sv;
+ break;
+ case 2:
+ sv = (int16_t)sv;
+ break;
+ case 4:
+ sv = (int32_t)sv;
+ break;
+ }
+
+ if (sv < 0)
+ dis->d86_sprintf_func(buf + strlen(buf),
+ buflen - strlen(buf),
+ (dis->d86_flags & DIS_OP_OCTAL) ?
+ "-0%llo" : "-0x%llx", -sv - dis->d86_len);
+ else
+ dis->d86_sprintf_func(buf + strlen(buf),
+ buflen - strlen(buf),
+ (dis->d86_flags & DIS_OP_OCTAL) ?
+ "+0%llo" : "+0x%llx", sv + dis->d86_len);
+
+ (void) strlcat(buf, "\t<", buflen);
+
+ if (dis->d86_sym_lookup == NULL ||
+ dis->d86_sym_lookup(dis->d86_data, pc + sv,
+ buf + strlen(buf), buflen - strlen(buf)) != 0)
+ dis->d86_sprintf_func(buf + strlen(buf),
+ buflen - strlen(buf),
+ (dis->d86_flags & DIS_OP_OCTAL) ?
+ "0%llo" : "0x%llx", pc + sv);
+
+ (void) strlcat(buf, ">", buflen);
+
+ break;
+ }
+ }
+}
+
+#endif /* DIS_TEXT */
diff --git a/sys/cddl/dev/dtrace/i386/dis_tables.h b/sys/cddl/dev/dtrace/i386/dis_tables.h
new file mode 100644
index 000000000000..b45a8c50e738
--- /dev/null
+++ b/sys/cddl/dev/dtrace/i386/dis_tables.h
@@ -0,0 +1,112 @@
+/*
+ * 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$
+ */
+/*
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1988 AT&T */
+/* All Rights Reserved */
+
+
+#ifndef _DIS_TABLES_H
+#define _DIS_TABLES_H
+
+#if defined(sun)
+#pragma ident "@(#)dis_tables.h 1.7 06/03/02 SMI"
+#endif
+
+/*
+ * Constants and prototypes for the IA32 disassembler backend. See dis_tables.c
+ * for usage information and documentation.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/types.h>
+#include <sys/param.h>
+
+/*
+ * values for cpu mode
+ */
+#define SIZE16 1
+#define SIZE32 2
+#define SIZE64 3
+
+#define OPLEN 256
+#define PFIXLEN 8
+#define NCPS 12 /* number of chars per symbol */
+
+/*
+ * data structures that must be provided to dtrace_dis86()
+ */
+typedef struct d86opnd {
+ char d86_opnd[OPLEN]; /* symbolic rep of operand */
+ char d86_prefix[PFIXLEN]; /* any prefix string or "" */
+ uint_t d86_mode; /* mode for immediate */
+ uint_t d86_value_size; /* size in bytes of d86_value */
+ uint64_t d86_value; /* immediate value of opnd */
+} d86opnd_t;
+
+typedef struct dis86 {
+ uint_t d86_mode;
+ uint_t d86_error;
+ uint_t d86_len; /* instruction length */
+ int d86_rmindex; /* index of modrm byte or -1 */
+ uint_t d86_memsize; /* size of memory referenced */
+ char d86_bytes[16]; /* bytes of instruction */
+ char d86_mneu[OPLEN];
+ uint_t d86_numopnds;
+ uint_t d86_rex_prefix; /* value of REX prefix if !0 */
+ char *d86_seg_prefix; /* segment prefix, if any */
+ uint_t d86_opnd_size;
+ uint_t d86_addr_size;
+ uint_t d86_got_modrm;
+ struct d86opnd d86_opnd[3]; /* up to 3 operands */
+ int (*d86_check_func)(void *);
+ int (*d86_get_byte)(void *);
+#ifdef DIS_TEXT
+ int (*d86_sym_lookup)(void *, uint64_t, char *, size_t);
+ int (*d86_sprintf_func)(char *, size_t, const char *, ...);
+ int d86_flags;
+ uint_t d86_imm_bytes;
+#endif
+ void *d86_data;
+} dis86_t;
+
+extern int dtrace_disx86(dis86_t *x, uint_t cpu_mode);
+
+#define DIS_OP_OCTAL 0x1 /* Print all numbers in octal */
+
+#ifdef DIS_TEXT
+extern void dtrace_disx86_str(dis86_t *x, uint_t cpu_mode, uintptr_t pc,
+ char *buf, size_t len);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _DIS_TABLES_H */
diff --git a/sys/cddl/dev/dtrace/i386/dtrace_asm.S b/sys/cddl/dev/dtrace/i386/dtrace_asm.S
new file mode 100644
index 000000000000..787a3c8b2ffe
--- /dev/null
+++ b/sys/cddl/dev/dtrace/i386/dtrace_asm.S
@@ -0,0 +1,527 @@
+/*
+ * 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
+
+#include <machine/asmacros.h>
+#include <sys/cpuvar_defs.h>
+#include <sys/dtrace.h>
+
+#include "assym.s"
+
+ .globl calltrap
+ .type calltrap,@function
+ ENTRY(dtrace_invop_start)
+
+ pushl %eax /* push %eax -- may be return value */
+ pushl %esp /* push stack pointer */
+ addl $48, (%esp) /* adjust to incoming args */
+ pushl 40(%esp) /* push calling EIP */
+
+ /*
+ * Call dtrace_invop to let it check if the exception was
+ * a fbt one. The return value in %eax will tell us what
+ * dtrace_invop wants us to do.
+ */
+ call dtrace_invop
+
+ /*
+ * We pushed 3 times for the arguments to dtrace_invop,
+ * so we need to increment the stack pointer to get rid of
+ * those values.
+ */
+ addl $12, %esp
+ ALTENTRY(dtrace_invop_callsite)
+ cmpl $DTRACE_INVOP_PUSHL_EBP, %eax
+ je invop_push
+ cmpl $DTRACE_INVOP_POPL_EBP, %eax
+ je invop_pop
+ cmpl $DTRACE_INVOP_LEAVE, %eax
+ je invop_leave
+ cmpl $DTRACE_INVOP_NOP, %eax
+ je invop_nop
+
+ /* When all else fails handle the trap in the usual way. */
+ jmpl *dtrace_invop_calltrap_addr
+
+invop_push:
+ /*
+ * We must emulate a "pushl %ebp". To do this, we pull the stack
+ * down 4 bytes, and then store the base pointer.
+ */
+ popal
+ subl $4, %esp /* make room for %ebp */
+ pushl %eax /* push temp */
+ movl 8(%esp), %eax /* load calling EIP */
+ incl %eax /* increment over LOCK prefix */
+ movl %eax, 4(%esp) /* store calling EIP */
+ movl 12(%esp), %eax /* load calling CS */
+ movl %eax, 8(%esp) /* store calling CS */
+ movl 16(%esp), %eax /* load calling EFLAGS */
+ movl %eax, 12(%esp) /* store calling EFLAGS */
+ movl %ebp, 16(%esp) /* push %ebp */
+ popl %eax /* pop off temp */
+ iret /* Return from interrupt. */
+invop_pop:
+ /*
+ * We must emulate a "popl %ebp". To do this, we do the opposite of
+ * the above: we remove the %ebp from the stack, and squeeze up the
+ * saved state from the trap.
+ */
+ popal
+ pushl %eax /* push temp */
+ movl 16(%esp), %ebp /* pop %ebp */
+ movl 12(%esp), %eax /* load calling EFLAGS */
+ movl %eax, 16(%esp) /* store calling EFLAGS */
+ movl 8(%esp), %eax /* load calling CS */
+ movl %eax, 12(%esp) /* store calling CS */
+ movl 4(%esp), %eax /* load calling EIP */
+ incl %eax /* increment over LOCK prefix */
+ movl %eax, 8(%esp) /* store calling EIP */
+ popl %eax /* pop off temp */
+ addl $4, %esp /* adjust stack pointer */
+ iret /* Return from interrupt. */
+invop_leave:
+ /*
+ * We must emulate a "leave", which is the same as a "movl %ebp, %esp"
+ * followed by a "popl %ebp". This looks similar to the above, but
+ * requires two temporaries: one for the new base pointer, and one
+ * for the staging register.
+ */
+ popa
+ pushl %eax /* push temp */
+ pushl %ebx /* push temp */
+ movl %ebp, %ebx /* set temp to old %ebp */
+ movl (%ebx), %ebp /* pop %ebp */
+ movl 16(%esp), %eax /* load calling EFLAGS */
+ movl %eax, (%ebx) /* store calling EFLAGS */
+ movl 12(%esp), %eax /* load calling CS */
+ movl %eax, -4(%ebx) /* store calling CS */
+ movl 8(%esp), %eax /* load calling EIP */
+ incl %eax /* increment over LOCK prefix */
+ movl %eax, -8(%ebx) /* store calling EIP */
+ movl %ebx, -4(%esp) /* temporarily store new %esp */
+ popl %ebx /* pop off temp */
+ popl %eax /* pop off temp */
+ movl -12(%esp), %esp /* set stack pointer */
+ subl $8, %esp /* adjust for three pushes, one pop */
+ iret /* return from interrupt */
+invop_nop:
+ /*
+ * We must emulate a "nop". This is obviously not hard: we need only
+ * advance the %eip by one.
+ */
+ popa
+ incl (%esp)
+ iret /* return from interrupt */
+
+ END(dtrace_invop_start)
+
+/*
+void dtrace_invop_init(void)
+*/
+ ENTRY(dtrace_invop_init)
+ movl $dtrace_invop_start, dtrace_invop_jump_addr
+ ret
+ END(dtrace_invop_init)
+
+/*
+void dtrace_invop_uninit(void)
+*/
+ ENTRY(dtrace_invop_uninit)
+ movl $0, dtrace_invop_jump_addr
+ ret
+ END(dtrace_invop_uninit)
+
+/*
+greg_t dtrace_getfp(void)
+*/
+
+ ENTRY(dtrace_getfp)
+ movl %ebp, %eax
+ ret
+ END(dtrace_getfp)
+
+/*
+uint32_t dtrace_cas32(uint32_t *target, uint32_t cmp, uint32_t new)
+*/
+
+ ENTRY(dtrace_cas32)
+ ALTENTRY(dtrace_casptr)
+ movl 4(%esp), %edx
+ movl 8(%esp), %eax
+ movl 12(%esp), %ecx
+ lock
+ cmpxchgl %ecx, (%edx)
+ ret
+ END(dtrace_casptr)
+ END(dtrace_cas32)
+
+/*
+uintptr_t dtrace_caller(int aframes)
+*/
+
+ ENTRY(dtrace_caller)
+ movl $-1, %eax
+ ret
+ END(dtrace_caller)
+
+/*
+void dtrace_copy(uintptr_t src, uintptr_t dest, size_t size)
+*/
+
+ ENTRY(dtrace_copy)
+ pushl %ebp
+ movl %esp, %ebp
+ pushl %esi
+ pushl %edi
+
+ movl 8(%ebp), %esi /* Load source address */
+ movl 12(%ebp), %edi /* Load destination address */
+ movl 16(%ebp), %ecx /* Load count */
+ repz /* Repeat for count... */
+ smovb /* move from %ds:si to %es:di */
+
+ popl %edi
+ popl %esi
+ movl %ebp, %esp
+ popl %ebp
+ ret
+ END(dtrace_copy)
+
+/*
+void dtrace_copystr(uintptr_t uaddr, uintptr_t kaddr, size_t size)
+*/
+
+ ENTRY(dtrace_copystr)
+
+ pushl %ebp /* Setup stack frame */
+ movl %esp, %ebp
+ pushl %ebx /* Save registers */
+
+ movl 8(%ebp), %ebx /* Load source address */
+ movl 12(%ebp), %edx /* Load destination address */
+ movl 16(%ebp), %ecx /* Load count */
+
+0:
+ movb (%ebx), %al /* Load from source */
+ movb %al, (%edx) /* Store to destination */
+ incl %ebx /* Increment source pointer */
+ incl %edx /* Increment destination pointer */
+ decl %ecx /* Decrement remaining count */
+ cmpb $0, %al
+ je 1f
+ cmpl $0, %ecx
+ jne 0b
+
+1:
+ popl %ebx
+ movl %ebp, %esp
+ popl %ebp
+ ret
+
+ END(dtrace_copystr)
+
+/*
+uintptr_t dtrace_fulword(void *addr)
+*/
+
+ ENTRY(dtrace_fulword)
+ movl 4(%esp), %ecx
+ xorl %eax, %eax
+ movl (%ecx), %eax
+ ret
+ END(dtrace_fulword)
+
+/*
+uint8_t dtrace_fuword8_nocheck(void *addr)
+*/
+
+ ENTRY(dtrace_fuword8_nocheck)
+ movl 4(%esp), %ecx
+ xorl %eax, %eax
+ movzbl (%ecx), %eax
+ ret
+ END(dtrace_fuword8_nocheck)
+
+/*
+uint16_t dtrace_fuword16_nocheck(void *addr)
+*/
+
+ ENTRY(dtrace_fuword16_nocheck)
+ movl 4(%esp), %ecx
+ xorl %eax, %eax
+ movzwl (%ecx), %eax
+ ret
+ END(dtrace_fuword16_nocheck)
+
+/*
+uint32_t dtrace_fuword32_nocheck(void *addr)
+*/
+
+ ENTRY(dtrace_fuword32_nocheck)
+ movl 4(%esp), %ecx
+ xorl %eax, %eax
+ movl (%ecx), %eax
+ ret
+ END(dtrace_fuword32_nocheck)
+
+/*
+uint64_t dtrace_fuword64_nocheck(void *addr)
+*/
+
+ ENTRY(dtrace_fuword64_nocheck)
+ movl 4(%esp), %ecx
+ xorl %eax, %eax
+ xorl %edx, %edx
+ movl (%ecx), %eax
+ movl 4(%ecx), %edx
+ ret
+ END(dtrace_fuword64_nocheck)
+
+/*
+void dtrace_probe_error(dtrace_state_t *state, dtrace_epid_t epid, int which, int fault, int fltoffs, uintptr_t illval)
+*/
+
+ ENTRY(dtrace_probe_error)
+ pushl %ebp
+ movl %esp, %ebp
+ pushl 0x1c(%ebp)
+ pushl 0x18(%ebp)
+ pushl 0x14(%ebp)
+ pushl 0x10(%ebp)
+ pushl 0xc(%ebp)
+ pushl 0x8(%ebp)
+ pushl dtrace_probeid_error
+ call dtrace_probe
+ movl %ebp, %esp
+ popl %ebp
+ ret
+ END(dtrace_probe_error)
+
+/*
+void dtrace_membar_producer(void)
+*/
+
+ ENTRY(dtrace_membar_producer)
+ rep; ret /* use 2 byte return instruction when branch target */
+ /* AMD Software Optimization Guide - Section 6.2 */
+ END(dtrace_membar_producer)
+
+/*
+void dtrace_membar_consumer(void)
+*/
+
+ ENTRY(dtrace_membar_consumer)
+ rep; ret /* use 2 byte return instruction when branch target */
+ /* AMD Software Optimization Guide - Section 6.2 */
+ END(dtrace_membar_consumer)
+
+/*
+dtrace_icookie_t dtrace_interrupt_disable(void)
+*/
+ ENTRY(dtrace_interrupt_disable)
+ pushfl
+ popl %eax
+ cli
+ ret
+ END(dtrace_interrupt_disable)
+
+/*
+void dtrace_interrupt_enable(dtrace_icookie_t cookie)
+*/
+ ENTRY(dtrace_interrupt_enable)
+ movl 4(%esp), %eax
+ pushl %eax
+ popfl
+ ret
+ END(dtrace_interrupt_enable)
+
+/*
+ * The panic() and cmn_err() functions invoke vpanic() as a common entry point
+ * into the panic code implemented in panicsys(). vpanic() is responsible
+ * for passing through the format string and arguments, and constructing a
+ * regs structure on the stack into which it saves the current register
+ * values. If we are not dying due to a fatal trap, these registers will
+ * then be preserved in panicbuf as the current processor state. Before
+ * invoking panicsys(), vpanic() activates the first panic trigger (see
+ * common/os/panic.c) and switches to the panic_stack if successful. Note that
+ * DTrace takes a slightly different panic path if it must panic from probe
+ * context. Instead of calling panic, it calls into dtrace_vpanic(), which
+ * sets up the initial stack as vpanic does, calls dtrace_panic_trigger(), and
+ * branches back into vpanic().
+ */
+/*
+void vpanic(const char *format, va_list alist)
+*/
+ ENTRY(vpanic) /* Initial stack layout: */
+
+ pushl %ebp /* | %eip | 20 */
+ movl %esp, %ebp /* | %ebp | 16 */
+ pushl %eax /* | %eax | 12 */
+ pushl %ebx /* | %ebx | 8 */
+ pushl %ecx /* | %ecx | 4 */
+ pushl %edx /* | %edx | 0 */
+
+ movl %esp, %ebx /* %ebx = current stack pointer */
+
+ lea panic_quiesce, %eax /* %eax = &panic_quiesce */
+ pushl %eax /* push &panic_quiesce */
+ call panic_trigger /* %eax = panic_trigger() */
+ addl $4, %esp /* reset stack pointer */
+
+vpanic_common:
+ cmpl $0, %eax /* if (%eax == 0) */
+ je 0f /* goto 0f; */
+
+ /*
+ * If panic_trigger() was successful, we are the first to initiate a
+ * panic: we now switch to the reserved panic_stack before continuing.
+ */
+ lea panic_stack, %esp /* %esp = panic_stack */
+ addl $PANICSTKSIZE, %esp /* %esp += PANICSTKSIZE */
+
+0: subl $REGSIZE, %esp /* allocate struct regs */
+
+ /*
+ * Now that we've got everything set up, store the register values as
+ * they were when we entered vpanic() to the designated location in
+ * the regs structure we allocated on the stack.
+ */
+#ifdef notyet
+ mov %gs, %edx
+ mov %edx, REGOFF_GS(%esp)
+ mov %fs, %edx
+ mov %edx, REGOFF_FS(%esp)
+ mov %es, %edx
+ mov %edx, REGOFF_ES(%esp)
+ mov %ds, %edx
+ mov %edx, REGOFF_DS(%esp)
+ movl %edi, REGOFF_EDI(%esp)
+ movl %esi, REGOFF_ESI(%esp)
+ movl 16(%ebx), %ecx
+ movl %ecx, REGOFF_EBP(%esp)
+ movl %ebx, %ecx
+ addl $20, %ecx
+ movl %ecx, REGOFF_ESP(%esp)
+ movl 8(%ebx), %ecx
+ movl %ecx, REGOFF_EBX(%esp)
+ movl 0(%ebx), %ecx
+ movl %ecx, REGOFF_EDX(%esp)
+ movl 4(%ebx), %ecx
+ movl %ecx, REGOFF_ECX(%esp)
+ movl 12(%ebx), %ecx
+ movl %ecx, REGOFF_EAX(%esp)
+ movl $0, REGOFF_TRAPNO(%esp)
+ movl $0, REGOFF_ERR(%esp)
+ lea vpanic, %ecx
+ movl %ecx, REGOFF_EIP(%esp)
+ mov %cs, %edx
+ movl %edx, REGOFF_CS(%esp)
+ pushfl
+ popl %ecx
+ movl %ecx, REGOFF_EFL(%esp)
+ movl $0, REGOFF_UESP(%esp)
+ mov %ss, %edx
+ movl %edx, REGOFF_SS(%esp)
+
+ movl %esp, %ecx /* %ecx = &regs */
+ pushl %eax /* push on_panic_stack */
+ pushl %ecx /* push &regs */
+ movl 12(%ebp), %ecx /* %ecx = alist */
+ pushl %ecx /* push alist */
+ movl 8(%ebp), %ecx /* %ecx = format */
+ pushl %ecx /* push format */
+ call panicsys /* panicsys(); */
+ addl $16, %esp /* pop arguments */
+
+ addl $REGSIZE, %esp
+#endif
+ popl %edx
+ popl %ecx
+ popl %ebx
+ popl %eax
+ leave
+ ret
+ END(vpanic)
+
+/*
+void dtrace_vpanic(const char *format, va_list alist)
+*/
+ ENTRY(dtrace_vpanic) /* Initial stack layout: */
+
+ pushl %ebp /* | %eip | 20 */
+ movl %esp, %ebp /* | %ebp | 16 */
+ pushl %eax /* | %eax | 12 */
+ pushl %ebx /* | %ebx | 8 */
+ pushl %ecx /* | %ecx | 4 */
+ pushl %edx /* | %edx | 0 */
+
+ movl %esp, %ebx /* %ebx = current stack pointer */
+
+ lea panic_quiesce, %eax /* %eax = &panic_quiesce */
+ pushl %eax /* push &panic_quiesce */
+ call dtrace_panic_trigger /* %eax = dtrace_panic_trigger() */
+ addl $4, %esp /* reset stack pointer */
+ jmp vpanic_common /* jump back to common code */
+
+ END(dtrace_vpanic)
+
+/*
+int
+panic_trigger(int *tp)
+*/
+ ENTRY(panic_trigger)
+ xorl %eax, %eax
+ movl $0xdefacedd, %edx
+ lock
+ xchgl %edx, (%edi)
+ cmpl $0, %edx
+ je 0f
+ movl $0, %eax
+ ret
+0: movl $1, %eax
+ ret
+ END(panic_trigger)
+
+/*
+int
+dtrace_panic_trigger(int *tp)
+*/
+ ENTRY(dtrace_panic_trigger)
+ xorl %eax, %eax
+ movl $0xdefacedd, %edx
+ lock
+ xchgl %edx, (%edi)
+ cmpl $0, %edx
+ je 0f
+ movl $0, %eax
+ ret
+0: movl $1, %eax
+ ret
+ END(dtrace_panic_trigger)
diff --git a/sys/cddl/dev/dtrace/i386/dtrace_isa.c b/sys/cddl/dev/dtrace/i386/dtrace_isa.c
new file mode 100644
index 000000000000..bf891aa39e17
--- /dev/null
+++ b/sys/cddl/dev/dtrace/i386/dtrace_isa.c
@@ -0,0 +1,622 @@
+/*
+ * 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/md_var.h>
+#include <machine/stack.h>
+
+#include <vm/vm.h>
+#include <vm/vm_param.h>
+#include <vm/pmap.h>
+
+extern uintptr_t kernbase;
+uintptr_t kernelbase = (uintptr_t) &kernbase;
+
+#define INKERNEL(va) (((vm_offset_t)(va)) >= USRSTACK && \
+ ((vm_offset_t)(va)) < VM_MAX_KERNEL_ADDRESS)
+
+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)
+{
+ int depth = 0;
+ register_t ebp;
+ struct i386_frame *frame;
+ vm_offset_t callpc;
+ pc_t caller = (pc_t) solaris_cpu[curcpu].cpu_dtrace_caller;
+
+ if (intrpc != 0)
+ pcstack[depth++] = (pc_t) intrpc;
+
+ aframes++;
+
+ __asm __volatile("movl %%ebp,%0" : "=r" (ebp));
+
+ frame = (struct i386_frame *)ebp;
+ while (depth < pcstack_limit) {
+ if (!INKERNEL(frame))
+ break;
+
+ callpc = frame->f_retaddr;
+
+ if (!INKERNEL(callpc))
+ break;
+
+ if (aframes > 0) {
+ aframes--;
+ if ((aframes == 0) && (caller != 0)) {
+ pcstack[depth++] = caller;
+ }
+ }
+ else {
+ pcstack[depth++] = callpc;
+ }
+
+ if (frame->f_frame <= frame ||
+ (vm_offset_t)frame->f_frame >=
+ (vm_offset_t)ebp + KSTACK_PAGES * PAGE_SIZE)
+ break;
+ frame = frame->f_frame;
+ }
+
+ for (; depth < pcstack_limit; depth++) {
+ pcstack[depth] = 0;
+ }
+}
+
+#ifdef notyet
+static int
+dtrace_getustack_common(uint64_t *pcstack, int pcstack_limit, uintptr_t pc,
+ uintptr_t sp)
+{
+ klwp_t *lwp = ttolwp(curthread);
+ proc_t *p = curproc;
+ uintptr_t oldcontext = lwp->lwp_oldcontext;
+ volatile uint16_t *flags =
+ (volatile uint16_t *)&cpu_core[curcpu].cpuc_dtrace_flags;
+ size_t s1, s2;
+ int ret = 0;
+
+ ASSERT(pcstack == NULL || pcstack_limit > 0);
+
+ if (p->p_model == DATAMODEL_NATIVE) {
+ s1 = sizeof (struct frame) + 2 * sizeof (long);
+ s2 = s1 + sizeof (siginfo_t);
+ } else {
+ s1 = sizeof (struct frame32) + 3 * sizeof (int);
+ s2 = s1 + sizeof (siginfo32_t);
+ }
+
+ while (pc != 0 && sp != 0) {
+ ret++;
+ if (pcstack != NULL) {
+ *pcstack++ = (uint64_t)pc;
+ pcstack_limit--;
+ if (pcstack_limit <= 0)
+ break;
+ }
+
+ if (oldcontext == sp + s1 || oldcontext == sp + s2) {
+ if (p->p_model == DATAMODEL_NATIVE) {
+ 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 {
+ ucontext32_t *ucp = (ucontext32_t *)oldcontext;
+ greg32_t *gregs = ucp->uc_mcontext.gregs;
+
+ sp = dtrace_fuword32(&gregs[EBP]);
+ pc = dtrace_fuword32(&gregs[EIP]);
+
+ oldcontext = dtrace_fuword32(&ucp->uc_link);
+ }
+ } else {
+ if (p->p_model == DATAMODEL_NATIVE) {
+ struct frame *fr = (struct frame *)sp;
+
+ pc = dtrace_fulword(&fr->fr_savpc);
+ sp = dtrace_fulword(&fr->fr_savfp);
+ } else {
+ struct frame32 *fr = (struct frame32 *)sp;
+
+ pc = dtrace_fuword32(&fr->fr_savpc);
+ sp = dtrace_fuword32(&fr->fr_savfp);
+ }
+ }
+
+ /*
+ * 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;
+ }
+ }
+
+ return (ret);
+}
+
+void
+dtrace_getupcstack(uint64_t *pcstack, int pcstack_limit)
+{
+ klwp_t *lwp = ttolwp(curthread);
+ proc_t *p = curproc;
+ struct regs *rp;
+ 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 (lwp == NULL || p == NULL || (rp = lwp->lwp_regs) == NULL)
+ goto zero;
+
+ *pcstack++ = (uint64_t)p->p_pid;
+ pcstack_limit--;
+
+ if (pcstack_limit <= 0)
+ return;
+
+ pc = rp->r_pc;
+ sp = rp->r_fp;
+
+ if (DTRACE_CPUFLAG_ISSET(CPU_DTRACE_ENTRY)) {
+ *pcstack++ = (uint64_t)pc;
+ pcstack_limit--;
+ if (pcstack_limit <= 0)
+ return;
+
+ if (p->p_model == DATAMODEL_NATIVE)
+ pc = dtrace_fulword((void *)rp->r_sp);
+ else
+ pc = dtrace_fuword32((void *)rp->r_sp);
+ }
+
+ 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++ = NULL;
+}
+
+int
+dtrace_getustackdepth(void)
+{
+}
+
+void
+dtrace_getufpstack(uint64_t *pcstack, uint64_t *fpstack, int pcstack_limit)
+{
+ klwp_t *lwp = ttolwp(curthread);
+ proc_t *p = curproc;
+ struct regs *rp;
+ uintptr_t pc, sp, oldcontext;
+ volatile uint16_t *flags =
+ (volatile uint16_t *)&cpu_core[curcpu].cpuc_dtrace_flags;
+ size_t s1, s2;
+
+ 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 (lwp == NULL || p == NULL || (rp = lwp->lwp_regs) == NULL)
+ goto zero;
+
+ *pcstack++ = (uint64_t)p->p_pid;
+ pcstack_limit--;
+
+ if (pcstack_limit <= 0)
+ return;
+
+ pc = rp->r_pc;
+ sp = rp->r_fp;
+ oldcontext = lwp->lwp_oldcontext;
+
+ if (p->p_model == DATAMODEL_NATIVE) {
+ s1 = sizeof (struct frame) + 2 * sizeof (long);
+ s2 = s1 + sizeof (siginfo_t);
+ } else {
+ s1 = sizeof (struct frame32) + 3 * sizeof (int);
+ s2 = s1 + sizeof (siginfo32_t);
+ }
+
+ if (DTRACE_CPUFLAG_ISSET(CPU_DTRACE_ENTRY)) {
+ *pcstack++ = (uint64_t)pc;
+ *fpstack++ = 0;
+ pcstack_limit--;
+ if (pcstack_limit <= 0)
+ return;
+
+ if (p->p_model == DATAMODEL_NATIVE)
+ pc = dtrace_fulword((void *)rp->r_sp);
+ else
+ pc = dtrace_fuword32((void *)rp->r_sp);
+ }
+
+ while (pc != 0 && sp != 0) {
+ *pcstack++ = (uint64_t)pc;
+ *fpstack++ = sp;
+ pcstack_limit--;
+ if (pcstack_limit <= 0)
+ break;
+
+ if (oldcontext == sp + s1 || oldcontext == sp + s2) {
+ if (p->p_model == DATAMODEL_NATIVE) {
+ 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 {
+ ucontext_t *ucp = (ucontext_t *)oldcontext;
+ greg_t *gregs = ucp->uc_mcontext.gregs;
+
+ sp = dtrace_fuword32(&gregs[EBP]);
+ pc = dtrace_fuword32(&gregs[EIP]);
+
+ oldcontext = dtrace_fuword32(&ucp->uc_link);
+ }
+ } else {
+ if (p->p_model == DATAMODEL_NATIVE) {
+ struct frame *fr = (struct frame *)sp;
+
+ pc = dtrace_fulword(&fr->fr_savpc);
+ sp = dtrace_fulword(&fr->fr_savfp);
+ } else {
+ struct frame32 *fr = (struct frame32 *)sp;
+
+ pc = dtrace_fuword32(&fr->fr_savpc);
+ sp = dtrace_fuword32(&fr->fr_savfp);
+ }
+ }
+
+ /*
+ * 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++ = NULL;
+}
+#endif
+
+uint64_t
+dtrace_getarg(int arg, int aframes)
+{
+ uintptr_t val;
+ struct i386_frame *fp = (struct i386_frame *)dtrace_getfp();
+ uintptr_t *stack;
+ int i;
+
+ for (i = 1; i <= aframes; i++) {
+ fp = fp->f_frame;
+
+ if (fp->f_retaddr == (long)dtrace_invop_callsite) {
+ /*
+ * If we pass through the invalid op handler, we will
+ * use the pointer that it passed to the stack as the
+ * second argument to dtrace_invop() as the pointer to
+ * the stack. When using this stack, we must step
+ * beyond the EIP/RIP that was pushed when the trap was
+ * taken -- hence the "+ 1" below.
+ */
+ stack = ((uintptr_t **)&fp[1])[1] + 1;
+ 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++;
+
+ stack = (uintptr_t *)&fp[1];
+
+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;
+ struct i386_frame *frame;
+ vm_offset_t ebp;
+
+ aframes++;
+ ebp = dtrace_getfp();
+ frame = (struct i386_frame *)ebp;
+ depth++;
+ for(;;) {
+ if (!INKERNEL((long) frame))
+ break;
+ if (!INKERNEL((long) frame->f_frame))
+ break;
+ depth++;
+ if (frame->f_frame <= frame ||
+ (vm_offset_t)frame->f_frame >=
+ (vm_offset_t)ebp + KSTACK_PAGES * PAGE_SIZE)
+ break;
+ frame = frame->f_frame;
+ }
+ if (depth < aframes)
+ return 0;
+ else
+ return depth - aframes;
+}
+
+#ifdef notyet
+ulong_t
+dtrace_getreg(struct regs *rp, uint_t reg)
+{
+#if defined(__amd64)
+ int regmap[] = {
+ REG_GS, /* GS */
+ REG_FS, /* FS */
+ REG_ES, /* ES */
+ REG_DS, /* DS */
+ REG_RDI, /* EDI */
+ REG_RSI, /* ESI */
+ REG_RBP, /* EBP */
+ REG_RSP, /* ESP */
+ REG_RBX, /* EBX */
+ REG_RDX, /* EDX */
+ REG_RCX, /* ECX */
+ REG_RAX, /* EAX */
+ REG_TRAPNO, /* TRAPNO */
+ REG_ERR, /* ERR */
+ REG_RIP, /* EIP */
+ REG_CS, /* CS */
+ REG_RFL, /* EFL */
+ REG_RSP, /* UESP */
+ REG_SS /* SS */
+ };
+
+ if (reg <= SS) {
+ if (reg >= sizeof (regmap) / sizeof (int)) {
+ DTRACE_CPUFLAG_SET(CPU_DTRACE_ILLOP);
+ return (0);
+ }
+
+ reg = regmap[reg];
+ } else {
+ reg -= SS + 1;
+ }
+
+ switch (reg) {
+ case REG_RDI:
+ return (rp->r_rdi);
+ case REG_RSI:
+ return (rp->r_rsi);
+ case REG_RDX:
+ return (rp->r_rdx);
+ case REG_RCX:
+ return (rp->r_rcx);
+ case REG_R8:
+ return (rp->r_r8);
+ case REG_R9:
+ return (rp->r_r9);
+ case REG_RAX:
+ return (rp->r_rax);
+ case REG_RBX:
+ return (rp->r_rbx);
+ case REG_RBP:
+ return (rp->r_rbp);
+ case REG_R10:
+ return (rp->r_r10);
+ case REG_R11:
+ return (rp->r_r11);
+ case REG_R12:
+ return (rp->r_r12);
+ case REG_R13:
+ return (rp->r_r13);
+ case REG_R14:
+ return (rp->r_r14);
+ case REG_R15:
+ return (rp->r_r15);
+ case REG_DS:
+ return (rp->r_ds);
+ case REG_ES:
+ return (rp->r_es);
+ case REG_FS:
+ return (rp->r_fs);
+ case REG_GS:
+ return (rp->r_gs);
+ case REG_TRAPNO:
+ return (rp->r_trapno);
+ case REG_ERR:
+ return (rp->r_err);
+ case REG_RIP:
+ return (rp->r_rip);
+ case REG_CS:
+ return (rp->r_cs);
+ case REG_SS:
+ return (rp->r_ss);
+ case REG_RFL:
+ return (rp->r_rfl);
+ case REG_RSP:
+ return (rp->r_rsp);
+ default:
+ DTRACE_CPUFLAG_SET(CPU_DTRACE_ILLOP);
+ return (0);
+ }
+
+#else
+ if (reg > SS) {
+ DTRACE_CPUFLAG_SET(CPU_DTRACE_ILLOP);
+ return (0);
+ }
+
+ return ((&rp->r_gs)[reg]);
+#endif
+}
+#endif
+
+static int
+dtrace_copycheck(uintptr_t uaddr, uintptr_t kaddr, size_t size)
+{
+ ASSERT(kaddr >= kernelbase && kaddr + size >= kaddr);
+
+ if (uaddr + size >= kernelbase || 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 >= kernelbase) {
+ 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 >= kernelbase) {
+ 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 >= kernelbase) {
+ 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 >= kernelbase) {
+ DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
+ cpu_core[curcpu].cpuc_dtrace_illval = (uintptr_t)uaddr;
+ return (0);
+ }
+ return (dtrace_fuword64_nocheck(uaddr));
+}
diff --git a/sys/cddl/dev/dtrace/i386/dtrace_subr.c b/sys/cddl/dev/dtrace/i386/dtrace_subr.c
new file mode 100644
index 000000000000..50515b643152
--- /dev/null
+++ b/sys/cddl/dev/dtrace/i386/dtrace_subr.c
@@ -0,0 +1,503 @@
+/*
+ * 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/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 <vm/pmap.h>
+
+extern uintptr_t kernelbase;
+extern uintptr_t dtrace_in_probe_addr;
+extern int dtrace_in_probe;
+
+int dtrace_invop(uintptr_t, uintptr_t *, uintptr_t);
+
+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);
+}
+
+void
+dtrace_toxic_ranges(void (*func)(uintptr_t base, uintptr_t limit))
+{
+ (*func)(0, kernelbase);
+}
+
+void
+dtrace_xcall(processorid_t cpu, dtrace_xcall_t func, void *arg)
+{
+ cpumask_t cpus;
+
+ critical_enter();
+
+ if (cpu == DTRACE_CPUALL)
+ cpus = all_cpus;
+ else
+ cpus = (cpumask_t) (1 << cpu);
+
+ /* If the current CPU is in the set, call the function directly: */
+ if ((cpus & (1 << curcpu)) != 0) {
+ (*func)(arg);
+
+ /* Mask the current CPU from the set */
+ cpus &= ~(1 << curcpu);
+ }
+
+ /* If there are any CPUs in the set, cross-call to those CPUs */
+ if (cpus != 0)
+ smp_rendezvous_cpus(cpus, NULL, func, smp_no_rendevous_barrier, arg);
+
+ critical_exit();
+}
+
+static void
+dtrace_sync_func(void)
+{
+}
+
+void
+dtrace_sync(void)
+{
+ dtrace_xcall(DTRACE_CPUALL, (dtrace_xcall_t)dtrace_sync_func, NULL);
+}
+
+#ifdef notyet
+int (*dtrace_fasttrap_probe_ptr)(struct regs *);
+int (*dtrace_pid_probe_ptr)(struct regs *);
+int (*dtrace_return_probe_ptr)(struct regs *);
+
+void
+dtrace_user_probe(struct regs *rp, caddr_t addr, processorid_t cpuid)
+{
+ krwlock_t *rwp;
+ proc_t *p = curproc;
+ extern void trap(struct regs *, caddr_t, processorid_t);
+
+ if (USERMODE(rp->r_cs) || (rp->r_ps & PS_VM)) {
+ if (curthread->t_cred != p->p_cred) {
+ cred_t *oldcred = curthread->t_cred;
+ /*
+ * DTrace accesses t_cred in probe context. t_cred
+ * must always be either NULL, or point to a valid,
+ * allocated cred structure.
+ */
+ curthread->t_cred = crgetcred();
+ crfree(oldcred);
+ }
+ }
+
+ if (rp->r_trapno == T_DTRACE_RET) {
+ uint8_t step = curthread->t_dtrace_step;
+ uint8_t ret = curthread->t_dtrace_ret;
+ uintptr_t npc = curthread->t_dtrace_npc;
+
+ if (curthread->t_dtrace_ast) {
+ aston(curthread);
+ curthread->t_sig_check = 1;
+ }
+
+ /*
+ * Clear all user tracing flags.
+ */
+ curthread->t_dtrace_ft = 0;
+
+ /*
+ * If we weren't expecting to take a return probe trap, kill
+ * the process as though it had just executed an unassigned
+ * trap instruction.
+ */
+ if (step == 0) {
+ tsignal(curthread, SIGILL);
+ return;
+ }
+
+ /*
+ * If we hit this trap unrelated to a return probe, we're
+ * just here to reset the AST flag since we deferred a signal
+ * until after we logically single-stepped the instruction we
+ * copied out.
+ */
+ if (ret == 0) {
+ rp->r_pc = npc;
+ return;
+ }
+
+ /*
+ * We need to wait until after we've called the
+ * dtrace_return_probe_ptr function pointer to set %pc.
+ */
+ rwp = &CPU->cpu_ft_lock;
+ rw_enter(rwp, RW_READER);
+ if (dtrace_return_probe_ptr != NULL)
+ (void) (*dtrace_return_probe_ptr)(rp);
+ rw_exit(rwp);
+ rp->r_pc = npc;
+
+ } else if (rp->r_trapno == T_DTRACE_PROBE) {
+ rwp = &CPU->cpu_ft_lock;
+ rw_enter(rwp, RW_READER);
+ if (dtrace_fasttrap_probe_ptr != NULL)
+ (void) (*dtrace_fasttrap_probe_ptr)(rp);
+ rw_exit(rwp);
+
+ } else if (rp->r_trapno == T_BPTFLT) {
+ uint8_t instr;
+ rwp = &CPU->cpu_ft_lock;
+
+ /*
+ * The DTrace fasttrap provider uses the breakpoint trap
+ * (int 3). We let DTrace take the first crack at handling
+ * this trap; if it's not a probe that DTrace knowns about,
+ * we call into the trap() routine to handle it like a
+ * breakpoint placed by a conventional debugger.
+ */
+ rw_enter(rwp, RW_READER);
+ if (dtrace_pid_probe_ptr != NULL &&
+ (*dtrace_pid_probe_ptr)(rp) == 0) {
+ rw_exit(rwp);
+ return;
+ }
+ rw_exit(rwp);
+
+ /*
+ * If the instruction that caused the breakpoint trap doesn't
+ * look like an int 3 anymore, it may be that this tracepoint
+ * was removed just after the user thread executed it. In
+ * that case, return to user land to retry the instuction.
+ */
+ if (fuword8((void *)(rp->r_pc - 1), &instr) == 0 &&
+ instr != FASTTRAP_INSTR) {
+ rp->r_pc--;
+ return;
+ }
+
+ trap(rp, addr, cpuid);
+
+ } else {
+ trap(rp, addr, cpuid);
+ }
+}
+
+void
+dtrace_safe_synchronous_signal(void)
+{
+ kthread_t *t = curthread;
+ struct regs *rp = lwptoregs(ttolwp(t));
+ size_t isz = t->t_dtrace_npc - t->t_dtrace_pc;
+
+ ASSERT(t->t_dtrace_on);
+
+ /*
+ * If we're not in the range of scratch addresses, we're not actually
+ * tracing user instructions so turn off the flags. If the instruction
+ * we copied out caused a synchonous trap, reset the pc back to its
+ * original value and turn off the flags.
+ */
+ if (rp->r_pc < t->t_dtrace_scrpc ||
+ rp->r_pc > t->t_dtrace_astpc + isz) {
+ t->t_dtrace_ft = 0;
+ } else if (rp->r_pc == t->t_dtrace_scrpc ||
+ rp->r_pc == t->t_dtrace_astpc) {
+ rp->r_pc = t->t_dtrace_pc;
+ t->t_dtrace_ft = 0;
+ }
+}
+
+int
+dtrace_safe_defer_signal(void)
+{
+ kthread_t *t = curthread;
+ struct regs *rp = lwptoregs(ttolwp(t));
+ size_t isz = t->t_dtrace_npc - t->t_dtrace_pc;
+
+ ASSERT(t->t_dtrace_on);
+
+ /*
+ * If we're not in the range of scratch addresses, we're not actually
+ * tracing user instructions so turn off the flags.
+ */
+ if (rp->r_pc < t->t_dtrace_scrpc ||
+ rp->r_pc > t->t_dtrace_astpc + isz) {
+ t->t_dtrace_ft = 0;
+ return (0);
+ }
+
+ /*
+ * If we've executed the original instruction, but haven't performed
+ * the jmp back to t->t_dtrace_npc or the clean up of any registers
+ * used to emulate %rip-relative instructions in 64-bit mode, do that
+ * here and take the signal right away. We detect this condition by
+ * seeing if the program counter is the range [scrpc + isz, astpc).
+ */
+ if (t->t_dtrace_astpc - rp->r_pc <
+ t->t_dtrace_astpc - t->t_dtrace_scrpc - isz) {
+#ifdef __amd64
+ /*
+ * If there is a scratch register and we're on the
+ * instruction immediately after the modified instruction,
+ * restore the value of that scratch register.
+ */
+ if (t->t_dtrace_reg != 0 &&
+ rp->r_pc == t->t_dtrace_scrpc + isz) {
+ switch (t->t_dtrace_reg) {
+ case REG_RAX:
+ rp->r_rax = t->t_dtrace_regv;
+ break;
+ case REG_RCX:
+ rp->r_rcx = t->t_dtrace_regv;
+ break;
+ case REG_R8:
+ rp->r_r8 = t->t_dtrace_regv;
+ break;
+ case REG_R9:
+ rp->r_r9 = t->t_dtrace_regv;
+ break;
+ }
+ }
+#endif
+ rp->r_pc = t->t_dtrace_npc;
+ t->t_dtrace_ft = 0;
+ return (0);
+ }
+
+ /*
+ * Otherwise, make sure we'll return to the kernel after executing
+ * the copied out instruction and defer the signal.
+ */
+ if (!t->t_dtrace_step) {
+ ASSERT(rp->r_pc < t->t_dtrace_astpc);
+ rp->r_pc += t->t_dtrace_astpc - t->t_dtrace_scrpc;
+ t->t_dtrace_step = 1;
+ }
+
+ t->t_dtrace_ast = 1;
+
+ return (1);
+}
+#endif
+
+static int64_t tgt_cpu_tsc;
+static int64_t hst_cpu_tsc;
+static int64_t tsc_skew[MAXCPU];
+
+static void
+dtrace_gethrtime_init_sync(void *arg)
+{
+#ifdef CHECK_SYNC
+ /*
+ * Delay this function from returning on one
+ * of the CPUs to check that the synchronisation
+ * works.
+ */
+ uintptr_t cpu = (uintptr_t) arg;
+
+ if (cpu == curcpu) {
+ int i;
+ for (i = 0; i < 1000000000; i++)
+ tgt_cpu_tsc = rdtsc();
+ tgt_cpu_tsc = 0;
+ }
+#endif
+}
+
+static void
+dtrace_gethrtime_init_cpu(void *arg)
+{
+ uintptr_t cpu = (uintptr_t) arg;
+
+ if (cpu == curcpu)
+ tgt_cpu_tsc = rdtsc();
+ else
+ hst_cpu_tsc = rdtsc();
+}
+
+static void
+dtrace_gethrtime_init(void *arg)
+{
+ cpumask_t map;
+ int i;
+ struct pcpu *cp;
+
+ /* The current CPU is the reference one. */
+ tsc_skew[curcpu] = 0;
+
+ for (i = 0; i <= mp_maxid; i++) {
+ if (i == curcpu)
+ continue;
+
+ if ((cp = pcpu_find(i)) == NULL)
+ continue;
+
+ map = 0;
+ map |= (1 << curcpu);
+ map |= (1 << i);
+
+ smp_rendezvous_cpus(map, dtrace_gethrtime_init_sync,
+ dtrace_gethrtime_init_cpu,
+ smp_no_rendevous_barrier, (void *)(uintptr_t) i);
+
+ tsc_skew[i] = tgt_cpu_tsc - hst_cpu_tsc;
+ }
+}
+
+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()
+{
+ return ((rdtsc() + tsc_skew[curcpu]) * (int64_t) 1000000000 / tsc_freq);
+}
+
+uint64_t
+dtrace_gethrestime(void)
+{
+ printf("%s(%d): XXX\n",__func__,__LINE__);
+ return (0);
+}
+
+/* Function to handle DTrace traps during probes. See i386/i386/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 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) {
+ /* General protection fault. */
+ case T_PROTFLT:
+ /* Flag an illegal operation. */
+ cpu_core[curcpu].cpuc_dtrace_flags |= CPU_DTRACE_ILLOP;
+
+ /*
+ * Offset the instruction pointer to the instruction
+ * following the one causing the fault.
+ */
+ frame->tf_eip += dtrace_instr_size((u_char *) frame->tf_eip);
+ return (1);
+ /* Page fault. */
+ case T_PAGEFLT:
+ /* Flag a bad address. */
+ cpu_core[curcpu].cpuc_dtrace_flags |= CPU_DTRACE_BADADDR;
+ cpu_core[curcpu].cpuc_dtrace_illval = rcr2();
+
+ /*
+ * Offset the instruction pointer to the instruction
+ * following the one causing the fault.
+ */
+ frame->tf_eip += dtrace_instr_size((u_char *) frame->tf_eip);
+ return (1);
+ default:
+ /* Handle all other traps in the usual way. */
+ break;
+ }
+ }
+
+ /* Handle the trap in the usual way. */
+ return (0);
+}
diff --git a/sys/cddl/dev/dtrace/i386/instr_size.c b/sys/cddl/dev/dtrace/i386/instr_size.c
new file mode 100644
index 000000000000..fb6af2d0abd0
--- /dev/null
+++ b/sys/cddl/dev/dtrace/i386/instr_size.c
@@ -0,0 +1,132 @@
+/*
+ * 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.
+ */
+
+/* Copyright (c) 1988 AT&T */
+/* All Rights Reserved */
+
+
+#if defined(sun)
+#pragma ident "@(#)instr_size.c 1.14 05/07/08 SMI"
+#endif
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/proc.h>
+#if defined(sun)
+#include <sys/cmn_err.h>
+#include <sys/archsystm.h>
+#include <sys/copyops.h>
+#include <vm/seg_enum.h>
+#include <sys/privregs.h>
+#else
+typedef u_int model_t;
+#define DATAMODEL_NATIVE 0
+int dtrace_instr_size(uchar_t *);
+#endif
+
+#include <dis_tables.h>
+
+/*
+ * This subsystem (with the minor exception of the instr_size() function) is
+ * is called from DTrace probe context. This imposes several requirements on
+ * the implementation:
+ *
+ * 1. External subsystems and functions may not be referenced. The one current
+ * exception is for cmn_err, but only to signal the detection of table
+ * errors. Assuming the tables are correct, no combination of input is to
+ * trigger a cmn_err call.
+ *
+ * 2. These functions can't be allowed to be traced. To prevent this,
+ * all functions in the probe path (everything except instr_size()) must
+ * have names that begin with "dtrace_".
+ */
+
+typedef enum dis_isize {
+ DIS_ISIZE_INSTR,
+ DIS_ISIZE_OPERAND
+} dis_isize_t;
+
+
+/*
+ * get a byte from instruction stream
+ */
+static int
+dtrace_dis_get_byte(void *p)
+{
+ int ret;
+ uchar_t **instr = p;
+
+ ret = **instr;
+ *instr += 1;
+
+ return (ret);
+}
+
+/*
+ * Returns either the size of a given instruction, in bytes, or the size of that
+ * instruction's memory access (if any), depending on the value of `which'.
+ * If a programming error in the tables is detected, the system will panic to
+ * ease diagnosis. Invalid instructions will not be flagged. They will appear
+ * to have an instruction size between 1 and the actual size, and will be
+ * reported as having no memory impact.
+ */
+/* ARGSUSED2 */
+static int
+dtrace_dis_isize(uchar_t *instr, dis_isize_t which, model_t model, int *rmindex)
+{
+ int sz;
+ dis86_t x;
+ uint_t mode = SIZE32;
+
+#if defined(sun)
+ mode = (model == DATAMODEL_LP64) ? SIZE64 : SIZE32;
+#endif
+
+ x.d86_data = (void **)&instr;
+ x.d86_get_byte = dtrace_dis_get_byte;
+ x.d86_check_func = NULL;
+
+ if (dtrace_disx86(&x, mode) != 0)
+ return (-1);
+
+ if (which == DIS_ISIZE_INSTR)
+ sz = x.d86_len; /* length of the instruction */
+ else
+ sz = x.d86_memsize; /* length of memory operand */
+
+ if (rmindex != NULL)
+ *rmindex = x.d86_rmindex;
+ return (sz);
+}
+
+int
+dtrace_instr_size(uchar_t *instr)
+{
+ return (dtrace_dis_isize(instr, DIS_ISIZE_INSTR, DATAMODEL_NATIVE,
+ NULL));
+}
diff --git a/sys/cddl/dev/fasttrap/fasttrap.c b/sys/cddl/dev/fasttrap/fasttrap.c
new file mode 100644
index 000000000000..df8309ea282c
--- /dev/null
+++ b/sys/cddl/dev/fasttrap/fasttrap.c
@@ -0,0 +1,2333 @@
+/*
+ * 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 2008 John Birrell <jb@freebsd.org>
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Copyright 2007 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/kdb.h>
+#include <sys/kernel.h>
+#include <sys/limits.h>
+#include <sys/linker.h>
+#include <sys/lock.h>
+#include <sys/module.h>
+#include <sys/proc.h>
+#include <sys/unistd.h>
+#include <sys/dtrace.h>
+#include <sys/dtrace_bsd.h>
+#include <sys/fasttrap_impl.h>
+
+/*
+ * User-Land Trap-Based Tracing
+ * ----------------------------
+ *
+ * The fasttrap provider allows DTrace consumers to instrument any user-level
+ * instruction to gather data; this includes probes with semantic
+ * signifigance like entry and return as well as simple offsets into the
+ * function. While the specific techniques used are very ISA specific, the
+ * methodology is generalizable to any architecture.
+ *
+ *
+ * The General Methodology
+ * -----------------------
+ *
+ * With the primary goal of tracing every user-land instruction and the
+ * limitation that we can't trust user space so don't want to rely on much
+ * information there, we begin by replacing the instructions we want to trace
+ * with trap instructions. Each instruction we overwrite is saved into a hash
+ * table keyed by process ID and pc address. When we enter the kernel due to
+ * this trap instruction, we need the effects of the replaced instruction to
+ * appear to have occurred before we proceed with the user thread's
+ * execution.
+ *
+ * Each user level thread is represented by a ulwp_t structure which is
+ * always easily accessible through a register. The most basic way to produce
+ * the effects of the instruction we replaced is to copy that instruction out
+ * to a bit of scratch space reserved in the user thread's ulwp_t structure
+ * (a sort of kernel-private thread local storage), set the PC to that
+ * scratch space and single step. When we reenter the kernel after single
+ * stepping the instruction we must then adjust the PC to point to what would
+ * normally be the next instruction. Of course, special care must be taken
+ * for branches and jumps, but these represent such a small fraction of any
+ * instruction set that writing the code to emulate these in the kernel is
+ * not too difficult.
+ *
+ * Return probes may require several tracepoints to trace every return site,
+ * and, conversely, each tracepoint may activate several probes (the entry
+ * and offset 0 probes, for example). To solve this muliplexing problem,
+ * tracepoints contain lists of probes to activate and probes contain lists
+ * of tracepoints to enable. If a probe is activated, it adds its ID to
+ * existing tracepoints or creates new ones as necessary.
+ *
+ * Most probes are activated _before_ the instruction is executed, but return
+ * probes are activated _after_ the effects of the last instruction of the
+ * function are visible. Return probes must be fired _after_ we have
+ * single-stepped the instruction whereas all other probes are fired
+ * beforehand.
+ *
+ *
+ * Lock Ordering
+ * -------------
+ *
+ * The lock ordering below -- both internally and with respect to the DTrace
+ * framework -- is a little tricky and bears some explanation. Each provider
+ * has a lock (ftp_mtx) that protects its members including reference counts
+ * for enabled probes (ftp_rcount), consumers actively creating probes
+ * (ftp_ccount) and USDT consumers (ftp_mcount); all three prevent a provider
+ * from being freed. A provider is looked up by taking the bucket lock for the
+ * provider hash table, and is returned with its lock held. The provider lock
+ * may be taken in functions invoked by the DTrace framework, but may not be
+ * held while calling functions in the DTrace framework.
+ *
+ * To ensure consistency over multiple calls to the DTrace framework, the
+ * creation lock (ftp_cmtx) should be held. Naturally, the creation lock may
+ * not be taken when holding the provider lock as that would create a cyclic
+ * lock ordering. In situations where one would naturally take the provider
+ * lock and then the creation lock, we instead up a reference count to prevent
+ * the provider from disappearing, drop the provider lock, and acquire the
+ * creation lock.
+ *
+ * Briefly:
+ * bucket lock before provider lock
+ * DTrace before provider lock
+ * creation lock before DTrace
+ * never hold the provider lock and creation lock simultaneously
+ */
+
+static d_open_t fasttrap_open;
+static d_ioctl_t fasttrap_ioctl;
+static int fasttrap_unload(void);
+static void fasttrap_load(void *);
+
+static struct cdevsw fasttrap_cdevsw = {
+ .d_version = D_VERSION,
+ .d_open = fasttrap_open,
+ .d_ioctl = fasttrap_ioctl,
+ .d_name = "fasttrap",
+};
+
+static struct cdev *fasttrap_cdev;
+static dtrace_provider_id_t fasttrap_id __used;
+static dtrace_meta_provider_id_t fasttrap_meta_id;
+
+/*
+ * When the fasttrap provider is loaded, fasttrap_max is set to either
+ * FASTTRAP_MAX_DEFAULT or the value for fasttrap-max-probes in the
+ * fasttrap.conf file. Each time a probe is created, fasttrap_total is
+ * incremented by the number of tracepoints that may be associated with that
+ * probe; fasttrap_total is capped at fasttrap_max.
+ */
+#define FASTTRAP_MAX_DEFAULT 250000
+static uint32_t fasttrap_max;
+static uint32_t fasttrap_total;
+
+fasttrap_hash_t fasttrap_tpoints;
+static fasttrap_hash_t fasttrap_provs;
+static fasttrap_hash_t fasttrap_procs;
+
+#define FASTTRAP_TPOINTS_DEFAULT_SIZE 0x4000
+#define FASTTRAP_PROVIDERS_DEFAULT_SIZE 0x100
+#define FASTTRAP_PROCS_DEFAULT_SIZE 0x100
+
+#define FASTTRAP_PID_NAME "pid"
+
+static struct callout_handle fasttrap_timeout = CALLOUT_HANDLE_INITIALIZER(&fasttrap_timeout);
+static struct mtx fasttrap_cleanup_mtx;
+MTX_SYSINIT(fasttrap_cleanup_mtx, &fasttrap_cleanup_mtx, "Fasttrap cleanup", MTX_DEF);
+static uint_t fasttrap_cleanup_work;
+
+/*
+ * Generation count on modifications to the global tracepoint lookup table.
+ */
+static volatile uint64_t fasttrap_mod_gen;
+
+static uint64_t fasttrap_pid_count; /* pid ref count */
+static struct mtx fasttrap_count_mtx; /* lock on ref count */
+MTX_SYSINIT(fasttrap_count_mtx, &fasttrap_count_mtx, "Fasttrap ref count", MTX_DEF);
+
+#define FASTTRAP_ENABLE_FAIL 1
+#define FASTTRAP_ENABLE_PARTIAL 2
+
+static int fasttrap_tracepoint_enable(proc_t *, fasttrap_probe_t *, uint_t);
+static void fasttrap_tracepoint_disable(proc_t *, fasttrap_probe_t *, uint_t);
+
+static fasttrap_provider_t *fasttrap_provider_lookup(pid_t, const char *,
+ const dtrace_pattr_t *);
+static void fasttrap_provider_free(fasttrap_provider_t *);
+static void fasttrap_provider_retire(pid_t, const char *, int);
+
+static fasttrap_proc_t *fasttrap_proc_lookup(pid_t);
+static void fasttrap_proc_release(fasttrap_proc_t *);
+
+#define FASTTRAP_PROVS_INDEX(pid, name) \
+ ((fasttrap_hash_str(name) + (pid)) & fasttrap_provs.fth_mask)
+
+#define FASTTRAP_PROCS_INDEX(pid) ((pid) & fasttrap_procs.fth_mask)
+
+static int
+fasttrap_highbit(ulong_t i)
+{
+ int h = 1;
+
+ if (i == 0)
+ return (0);
+#ifdef _LP64
+ if (i & 0xffffffff00000000ul) {
+ h += 32; i >>= 32;
+ }
+#endif
+ if (i & 0xffff0000) {
+ h += 16; i >>= 16;
+ }
+ if (i & 0xff00) {
+ h += 8; i >>= 8;
+ }
+ if (i & 0xf0) {
+ h += 4; i >>= 4;
+ }
+ if (i & 0xc) {
+ h += 2; i >>= 2;
+ }
+ if (i & 0x2) {
+ h += 1;
+ }
+ return (h);
+}
+
+static uint_t
+fasttrap_hash_str(const char *p)
+{
+ unsigned int g;
+ uint_t hval = 0;
+
+ while (*p) {
+ hval = (hval << 4) + *p++;
+ if ((g = (hval & 0xf0000000)) != 0)
+ hval ^= g >> 24;
+ hval &= ~g;
+ }
+ return (hval);
+}
+
+void
+fasttrap_sigtrap(proc_t *p, kthread_t *t, uintptr_t pc)
+{
+printf("%s(%d): DOODAD\n",__func__,__LINE__);
+#ifdef DOODAD
+ sigqueue_t *sqp = kmem_zalloc(sizeof (sigqueue_t), KM_SLEEP);
+
+ sqp->sq_info.si_signo = SIGTRAP;
+ sqp->sq_info.si_code = TRAP_DTRACE;
+ sqp->sq_info.si_addr = (caddr_t)pc;
+
+ PROC_LOCK(p);
+ sigaddqa(p, t, sqp);
+ PROC_UNLOCK(p);
+
+ if (t != NULL)
+ aston(t);
+#endif
+}
+
+/*
+ * This function ensures that no threads are actively using the memory
+ * associated with probes that were formerly live.
+ */
+static void
+fasttrap_mod_barrier(uint64_t gen)
+{
+printf("%s(%d): DOODAD\n",__func__,__LINE__);
+#ifdef DOODAD
+ int i;
+
+ if (gen < fasttrap_mod_gen)
+ return;
+
+ fasttrap_mod_gen++;
+
+ for (i = 0; i < MAXCPU; i++) {
+ mtx_lock(&cpu_core[i].cpuc_pid_lock);
+ mtx_unlock(&cpu_core[i].cpuc_pid_lock);
+ }
+#endif
+}
+
+/*
+ * This is the timeout's callback for cleaning up the providers and their
+ * probes.
+ */
+static void
+fasttrap_pid_cleanup_cb(void *data)
+{
+ fasttrap_provider_t **fpp, *fp;
+ fasttrap_bucket_t *bucket;
+ dtrace_provider_id_t provid;
+ int i, later = 0;
+
+ static volatile int in = 0;
+ ASSERT(in == 0);
+ in = 1;
+
+ mtx_lock(&fasttrap_cleanup_mtx);
+ while (fasttrap_cleanup_work) {
+ fasttrap_cleanup_work = 0;
+ mtx_unlock(&fasttrap_cleanup_mtx);
+
+ later = 0;
+
+ /*
+ * Iterate over all the providers trying to remove the marked
+ * ones. If a provider is marked but not retired, we just
+ * have to take a crack at removing it -- it's no big deal if
+ * we can't.
+ */
+ for (i = 0; i < fasttrap_provs.fth_nent; i++) {
+ bucket = &fasttrap_provs.fth_table[i];
+ mtx_lock(&bucket->ftb_mtx);
+ fpp = (fasttrap_provider_t **)&bucket->ftb_data;
+
+ while ((fp = *fpp) != NULL) {
+ if (!fp->ftp_marked) {
+ fpp = &fp->ftp_next;
+ continue;
+ }
+
+ mtx_lock(&fp->ftp_mtx);
+
+ /*
+ * If this provider has consumers actively
+ * creating probes (ftp_ccount) or is a USDT
+ * provider (ftp_mcount), we can't unregister
+ * or even condense.
+ */
+ if (fp->ftp_ccount != 0 ||
+ fp->ftp_mcount != 0) {
+ mtx_unlock(&fp->ftp_mtx);
+ fp->ftp_marked = 0;
+ continue;
+ }
+
+ if (!fp->ftp_retired || fp->ftp_rcount != 0)
+ fp->ftp_marked = 0;
+
+ mtx_unlock(&fp->ftp_mtx);
+
+ /*
+ * If we successfully unregister this
+ * provider we can remove it from the hash
+ * chain and free the memory. If our attempt
+ * to unregister fails and this is a retired
+ * provider, increment our flag to try again
+ * pretty soon. If we've consumed more than
+ * half of our total permitted number of
+ * probes call dtrace_condense() to try to
+ * clean out the unenabled probes.
+ */
+ provid = fp->ftp_provid;
+ if (dtrace_unregister(provid) != 0) {
+ if (fasttrap_total > fasttrap_max / 2)
+ (void) dtrace_condense(provid);
+ later += fp->ftp_marked;
+ fpp = &fp->ftp_next;
+ } else {
+ *fpp = fp->ftp_next;
+ fasttrap_provider_free(fp);
+ }
+ }
+ mtx_unlock(&bucket->ftb_mtx);
+ }
+
+ mtx_lock(&fasttrap_cleanup_mtx);
+ }
+
+ /*
+ * If we were unable to remove a retired provider, try again after
+ * a second. This situation can occur in certain circumstances where
+ * providers cannot be unregistered even though they have no probes
+ * enabled because of an execution of dtrace -l or something similar.
+ * If the timeout has been disabled (set to 1 because we're trying
+ * to detach), we set fasttrap_cleanup_work to ensure that we'll
+ * get a chance to do that work if and when the timeout is reenabled
+ * (if detach fails).
+ */
+ if (later > 0 && fasttrap_timeout.callout != (struct callout *)(uintptr_t)1)
+ fasttrap_timeout = timeout(&fasttrap_pid_cleanup_cb, NULL, hz);
+ else if (later > 0)
+ fasttrap_cleanup_work = 1;
+ else
+ fasttrap_timeout.callout = NULL;
+
+ mtx_unlock(&fasttrap_cleanup_mtx);
+ in = 0;
+}
+
+/*
+ * Activates the asynchronous cleanup mechanism.
+ */
+static void
+fasttrap_pid_cleanup(void)
+{
+ mtx_lock(&fasttrap_cleanup_mtx);
+ fasttrap_cleanup_work = 1;
+ if (fasttrap_timeout.callout == NULL)
+ fasttrap_timeout = timeout(&fasttrap_pid_cleanup_cb, NULL, 1);
+ mtx_unlock(&fasttrap_cleanup_mtx);
+}
+
+/* This function assumes that the process is already locked. */
+static void
+fasttrap_fork(proc_t *p, proc_t *cp)
+{
+ pid_t ppid = p->p_pid;
+ int i;
+
+#ifdef DOODAD
+ ASSERT(curproc == p);
+#else
+ if (curproc != p) printf("%s(%d): Warning curproc != p\n",__func__,__LINE__);
+#endif
+
+ /*
+ * This would be simpler and faster if we maintained per-process
+ * hash tables of enabled tracepoints. It could, however, potentially
+ * slow down execution of a tracepoint since we'd need to go
+ * through two levels of indirection. In the future, we should
+ * consider either maintaining per-process ancillary lists of
+ * enabled tracepoints or hanging a pointer to a per-process hash
+ * table of enabled tracepoints off the proc structure.
+ */
+
+ /*
+ * Iterate over every tracepoint looking for ones that belong to the
+ * parent process, and remove each from the child process.
+ */
+ for (i = 0; i < fasttrap_tpoints.fth_nent; i++) {
+ fasttrap_tracepoint_t *tp;
+ fasttrap_bucket_t *bucket = &fasttrap_tpoints.fth_table[i];
+
+ mtx_lock(&bucket->ftb_mtx);
+ for (tp = bucket->ftb_data; tp != NULL; tp = tp->ftt_next) {
+ if (tp->ftt_pid == ppid &&
+ tp->ftt_proc->ftpc_acount != 0) {
+printf("%s(%d): DOODAD\n",__func__,__LINE__);
+#ifdef DOODAD
+ int ret = fasttrap_tracepoint_remove(cp, tp);
+ ASSERT(ret == 0);
+#endif
+ }
+ }
+ mtx_unlock(&bucket->ftb_mtx);
+ }
+}
+
+/* This function assumes that the process is already locked. */
+static void
+fasttrap_exec_exit(proc_t *p)
+{
+ ASSERT(p == curproc);
+
+ /*
+ * We clean up the pid provider for this process here; user-land
+ * static probes are handled by the meta-provider remove entry point.
+ */
+ fasttrap_provider_retire(p->p_pid, FASTTRAP_PID_NAME, 0);
+}
+
+
+static void
+fasttrap_pid_provide(void *arg, dtrace_probedesc_t *desc)
+{
+ /*
+ * There are no "default" pid probes.
+ */
+printf("%s(%d): There are no default pid probes\n",__func__,__LINE__);
+}
+
+static int
+fasttrap_tracepoint_enable(proc_t *p, fasttrap_probe_t *probe, uint_t index)
+{
+ fasttrap_tracepoint_t *tp, *new_tp = NULL;
+ fasttrap_bucket_t *bucket;
+ fasttrap_id_t *id;
+ pid_t pid;
+ uintptr_t pc;
+
+ ASSERT(index < probe->ftp_ntps);
+
+ pid = probe->ftp_pid;
+ pc = probe->ftp_tps[index].fit_tp->ftt_pc;
+ id = &probe->ftp_tps[index].fit_id;
+
+ ASSERT(probe->ftp_tps[index].fit_tp->ftt_pid == pid);
+
+ /*
+ * Before we make any modifications, make sure we've imposed a barrier
+ * on the generation in which this probe was last modified.
+ */
+ fasttrap_mod_barrier(probe->ftp_gen);
+
+ bucket = &fasttrap_tpoints.fth_table[FASTTRAP_TPOINTS_INDEX(pid, pc)];
+
+ /*
+ * If the tracepoint has already been enabled, just add our id to the
+ * list of interested probes. This may be our second time through
+ * this path in which case we'll have constructed the tracepoint we'd
+ * like to install. If we can't find a match, and have an allocated
+ * tracepoint ready to go, enable that one now.
+ *
+ * A tracepoint whose process is defunct is also considered defunct.
+ */
+#ifdef DOODAD
+again:
+#endif
+ mtx_lock(&bucket->ftb_mtx);
+ for (tp = bucket->ftb_data; tp != NULL; tp = tp->ftt_next) {
+ if (tp->ftt_pid != pid || tp->ftt_pc != pc ||
+ tp->ftt_proc->ftpc_acount == 0)
+ continue;
+
+ /*
+ * Now that we've found a matching tracepoint, it would be
+ * a decent idea to confirm that the tracepoint is still
+ * enabled and the trap instruction hasn't been overwritten.
+ * Since this is a little hairy, we'll punt for now.
+ */
+
+ /*
+ * This can't be the first interested probe. We don't have
+ * to worry about another thread being in the midst of
+ * deleting this tracepoint (which would be the only valid
+ * reason for a tracepoint to have no interested probes)
+ * since we're holding P_PR_LOCK for this process.
+ */
+ ASSERT(tp->ftt_ids != NULL || tp->ftt_retids != NULL);
+
+ switch (id->fti_ptype) {
+ case DTFTP_ENTRY:
+ case DTFTP_OFFSETS:
+ case DTFTP_IS_ENABLED:
+ id->fti_next = tp->ftt_ids;
+#ifdef DOODAD
+ membar_producer();
+#endif
+ tp->ftt_ids = id;
+#ifdef DOODAD
+ membar_producer();
+#endif
+ break;
+
+ case DTFTP_RETURN:
+ case DTFTP_POST_OFFSETS:
+ id->fti_next = tp->ftt_retids;
+#ifdef DOODAD
+ membar_producer();
+#endif
+ tp->ftt_retids = id;
+#ifdef DOODAD
+ membar_producer();
+#endif
+ break;
+
+ default:
+ ASSERT(0);
+ }
+
+ mtx_unlock(&bucket->ftb_mtx);
+
+ if (new_tp != NULL) {
+ new_tp->ftt_ids = NULL;
+ new_tp->ftt_retids = NULL;
+ }
+
+ return (0);
+ }
+
+ /*
+ * If we have a good tracepoint ready to go, install it now while
+ * we have the lock held and no one can screw with us.
+ */
+ if (new_tp != NULL) {
+ int rc = 0;
+
+ new_tp->ftt_next = bucket->ftb_data;
+#ifdef DOODAD
+ membar_producer();
+#endif
+ bucket->ftb_data = new_tp;
+#ifdef DOODAD
+ membar_producer();
+#endif
+ mtx_unlock(&bucket->ftb_mtx);
+
+ /*
+ * Activate the tracepoint in the ISA-specific manner.
+ * If this fails, we need to report the failure, but
+ * indicate that this tracepoint must still be disabled
+ * by calling fasttrap_tracepoint_disable().
+ */
+ if (fasttrap_tracepoint_install(p, new_tp) != 0)
+ rc = FASTTRAP_ENABLE_PARTIAL;
+
+ /*
+ * Increment the count of the number of tracepoints active in
+ * the victim process.
+ */
+#ifdef DOODAD
+ ASSERT(p->p_proc_flag & P_PR_LOCK);
+ p->p_dtrace_count++;
+#endif
+
+ return (rc);
+ }
+
+ mtx_unlock(&bucket->ftb_mtx);
+
+ /*
+ * Initialize the tracepoint that's been preallocated with the probe.
+ */
+ new_tp = probe->ftp_tps[index].fit_tp;
+
+ ASSERT(new_tp->ftt_pid == pid);
+ ASSERT(new_tp->ftt_pc == pc);
+ ASSERT(new_tp->ftt_proc == probe->ftp_prov->ftp_proc);
+ ASSERT(new_tp->ftt_ids == NULL);
+ ASSERT(new_tp->ftt_retids == NULL);
+
+ switch (id->fti_ptype) {
+ case DTFTP_ENTRY:
+ case DTFTP_OFFSETS:
+ case DTFTP_IS_ENABLED:
+ id->fti_next = NULL;
+ new_tp->ftt_ids = id;
+ break;
+
+ case DTFTP_RETURN:
+ case DTFTP_POST_OFFSETS:
+ id->fti_next = NULL;
+ new_tp->ftt_retids = id;
+ break;
+
+ default:
+ ASSERT(0);
+ }
+
+printf("%s(%d): DOODAD\n",__func__,__LINE__);
+#ifdef DOODAD
+ /*
+ * If the ISA-dependent initialization goes to plan, go back to the
+ * beginning and try to install this freshly made tracepoint.
+ */
+ if (fasttrap_tracepoint_init(p, new_tp, pc, id->fti_ptype) == 0)
+ goto again;
+#endif
+
+ new_tp->ftt_ids = NULL;
+ new_tp->ftt_retids = NULL;
+
+ return (FASTTRAP_ENABLE_FAIL);
+}
+
+static void
+fasttrap_tracepoint_disable(proc_t *p, fasttrap_probe_t *probe, uint_t index)
+{
+ fasttrap_bucket_t *bucket;
+ fasttrap_provider_t *provider = probe->ftp_prov;
+ fasttrap_tracepoint_t **pp, *tp;
+ fasttrap_id_t *id, **idp;
+ pid_t pid;
+ uintptr_t pc;
+
+ ASSERT(index < probe->ftp_ntps);
+
+ pid = probe->ftp_pid;
+ pc = probe->ftp_tps[index].fit_tp->ftt_pc;
+ id = &probe->ftp_tps[index].fit_id;
+
+ ASSERT(probe->ftp_tps[index].fit_tp->ftt_pid == pid);
+
+ /*
+ * Find the tracepoint and make sure that our id is one of the
+ * ones registered with it.
+ */
+ bucket = &fasttrap_tpoints.fth_table[FASTTRAP_TPOINTS_INDEX(pid, pc)];
+ mtx_lock(&bucket->ftb_mtx);
+ for (tp = bucket->ftb_data; tp != NULL; tp = tp->ftt_next) {
+ if (tp->ftt_pid == pid && tp->ftt_pc == pc &&
+ tp->ftt_proc == provider->ftp_proc)
+ break;
+ }
+
+ /*
+ * If we somehow lost this tracepoint, we're in a world of hurt.
+ */
+ ASSERT(tp != NULL);
+
+ switch (id->fti_ptype) {
+ case DTFTP_ENTRY:
+ case DTFTP_OFFSETS:
+ case DTFTP_IS_ENABLED:
+ ASSERT(tp->ftt_ids != NULL);
+ idp = &tp->ftt_ids;
+ break;
+
+ case DTFTP_RETURN:
+ case DTFTP_POST_OFFSETS:
+ ASSERT(tp->ftt_retids != NULL);
+ idp = &tp->ftt_retids;
+ break;
+
+ default:
+ ASSERT(0);
+ }
+
+ while ((*idp)->fti_probe != probe) {
+ idp = &(*idp)->fti_next;
+ ASSERT(*idp != NULL);
+ }
+
+ id = *idp;
+ *idp = id->fti_next;
+#ifdef DOODAD
+ membar_producer();
+#endif
+
+ ASSERT(id->fti_probe == probe);
+
+ /*
+ * If there are other registered enablings of this tracepoint, we're
+ * all done, but if this was the last probe assocated with this
+ * this tracepoint, we need to remove and free it.
+ */
+ if (tp->ftt_ids != NULL || tp->ftt_retids != NULL) {
+
+ /*
+ * If the current probe's tracepoint is in use, swap it
+ * for an unused tracepoint.
+ */
+ if (tp == probe->ftp_tps[index].fit_tp) {
+ fasttrap_probe_t *tmp_probe;
+ fasttrap_tracepoint_t **tmp_tp;
+ uint_t tmp_index;
+
+ if (tp->ftt_ids != NULL) {
+ tmp_probe = tp->ftt_ids->fti_probe;
+ /* LINTED - alignment */
+ tmp_index = FASTTRAP_ID_INDEX(tp->ftt_ids);
+ tmp_tp = &tmp_probe->ftp_tps[tmp_index].fit_tp;
+ } else {
+ tmp_probe = tp->ftt_retids->fti_probe;
+ /* LINTED - alignment */
+ tmp_index = FASTTRAP_ID_INDEX(tp->ftt_retids);
+ tmp_tp = &tmp_probe->ftp_tps[tmp_index].fit_tp;
+ }
+
+ ASSERT(*tmp_tp != NULL);
+ ASSERT(*tmp_tp != probe->ftp_tps[index].fit_tp);
+ ASSERT((*tmp_tp)->ftt_ids == NULL);
+ ASSERT((*tmp_tp)->ftt_retids == NULL);
+
+ probe->ftp_tps[index].fit_tp = *tmp_tp;
+ *tmp_tp = tp;
+ }
+
+ mtx_unlock(&bucket->ftb_mtx);
+
+ /*
+ * Tag the modified probe with the generation in which it was
+ * changed.
+ */
+ probe->ftp_gen = fasttrap_mod_gen;
+ return;
+ }
+
+ mtx_unlock(&bucket->ftb_mtx);
+
+ /*
+ * We can't safely remove the tracepoint from the set of active
+ * tracepoints until we've actually removed the fasttrap instruction
+ * from the process's text. We can, however, operate on this
+ * tracepoint secure in the knowledge that no other thread is going to
+ * be looking at it since we hold P_PR_LOCK on the process if it's
+ * live or we hold the provider lock on the process if it's dead and
+ * gone.
+ */
+
+ /*
+ * We only need to remove the actual instruction if we're looking
+ * at an existing process
+ */
+ if (p != NULL) {
+ /*
+ * If we fail to restore the instruction we need to kill
+ * this process since it's in a completely unrecoverable
+ * state.
+ */
+printf("%s(%d): DOODAD\n",__func__,__LINE__);
+#ifdef DOODAD
+ if (fasttrap_tracepoint_remove(p, tp) != 0)
+ fasttrap_sigtrap(p, NULL, pc);
+#endif
+
+ /*
+ * Decrement the count of the number of tracepoints active
+ * in the victim process.
+ */
+#ifdef DOODAD
+ ASSERT(p->p_proc_flag & P_PR_LOCK);
+ p->p_dtrace_count--;
+#endif
+ }
+
+ /*
+ * Remove the probe from the hash table of active tracepoints.
+ */
+ mtx_lock(&bucket->ftb_mtx);
+ pp = (fasttrap_tracepoint_t **)&bucket->ftb_data;
+ ASSERT(*pp != NULL);
+ while (*pp != tp) {
+ pp = &(*pp)->ftt_next;
+ ASSERT(*pp != NULL);
+ }
+
+ *pp = tp->ftt_next;
+#ifdef DOODAD
+ membar_producer();
+#endif
+
+ mtx_unlock(&bucket->ftb_mtx);
+
+ /*
+ * Tag the modified probe with the generation in which it was changed.
+ */
+ probe->ftp_gen = fasttrap_mod_gen;
+}
+
+static void __unused
+fasttrap_enable_callbacks(void)
+{
+ /*
+ * We don't have to play the rw lock game here because we're
+ * providing something rather than taking something away --
+ * we can be sure that no threads have tried to follow this
+ * function pointer yet.
+ */
+ mtx_lock(&fasttrap_count_mtx);
+printf("%s(%d): DOODAD\n",__func__,__LINE__);
+#ifdef DOODAD
+ if (fasttrap_pid_count == 0) {
+ ASSERT(dtrace_pid_probe_ptr == NULL);
+ ASSERT(dtrace_return_probe_ptr == NULL);
+ dtrace_pid_probe_ptr = &fasttrap_pid_probe;
+ dtrace_return_probe_ptr = &fasttrap_return_probe;
+ }
+ ASSERT(dtrace_pid_probe_ptr == &fasttrap_pid_probe);
+ ASSERT(dtrace_return_probe_ptr == &fasttrap_return_probe);
+#endif
+ fasttrap_pid_count++;
+ mtx_unlock(&fasttrap_count_mtx);
+}
+
+static void __unused
+fasttrap_disable_callbacks(void)
+{
+ ASSERT(MUTEX_HELD(&cpu_lock));
+
+ mtx_lock(&fasttrap_count_mtx);
+printf("%s(%d): DOODAD\n",__func__,__LINE__);
+#ifdef DOODAD
+ ASSERT(fasttrap_pid_count > 0);
+ fasttrap_pid_count--;
+ if (fasttrap_pid_count == 0) {
+ cpu_t *cur, *cpu = CPU;
+
+ for (cur = cpu->cpu_next_onln; cur != cpu;
+ cur = cur->cpu_next_onln) {
+ rw_enter(&cur->cpu_ft_lock, RW_WRITER);
+ }
+
+ dtrace_pid_probe_ptr = NULL;
+ dtrace_return_probe_ptr = NULL;
+
+ for (cur = cpu->cpu_next_onln; cur != cpu;
+ cur = cur->cpu_next_onln) {
+ rw_exit(&cur->cpu_ft_lock);
+ }
+ }
+#endif
+ mtx_unlock(&fasttrap_count_mtx);
+}
+
+static void
+fasttrap_pid_enable(void *arg, dtrace_id_t id, void *parg)
+{
+ fasttrap_probe_t *probe = parg;
+ proc_t *p;
+ int i, rc;
+
+ ASSERT(probe != NULL);
+ ASSERT(!probe->ftp_enabled);
+ ASSERT(id == probe->ftp_id);
+ ASSERT(MUTEX_HELD(&cpu_lock));
+
+ /*
+ * Increment the count of enabled probes on this probe's provider;
+ * the provider can't go away while the probe still exists. We
+ * must increment this even if we aren't able to properly enable
+ * this probe.
+ */
+ mtx_lock(&probe->ftp_prov->ftp_mtx);
+ probe->ftp_prov->ftp_rcount++;
+ mtx_unlock(&probe->ftp_prov->ftp_mtx);
+
+ /*
+ * If this probe's provider is retired (meaning it was valid in a
+ * previously exec'ed incarnation of this address space), bail out. The
+ * provider can't go away while we're in this code path.
+ */
+ if (probe->ftp_prov->ftp_retired)
+ return;
+
+printf("%s(%d): DOODAD\n",__func__,__LINE__);
+#ifdef DOODAD
+ /*
+ * If we can't find the process, it may be that we're in the context of
+ * a fork in which the traced process is being born and we're copying
+ * USDT probes. Otherwise, the process is gone so bail.
+ */
+ if ((p = sprlock(probe->ftp_pid)) == NULL) {
+ if ((curproc->p_flag & SFORKING) == 0)
+ return;
+
+ mtx_lock(&pidlock);
+ p = prfind(probe->ftp_pid);
+
+ /*
+ * Confirm that curproc is indeed forking the process in which
+ * we're trying to enable probes.
+ */
+ ASSERT(p != NULL);
+ ASSERT(p->p_parent == curproc);
+ ASSERT(p->p_stat == SIDL);
+
+ mtx_lock(&p->p_lock);
+ mtx_unlock(&pidlock);
+
+ sprlock_proc(p);
+ }
+
+ ASSERT(!(p->p_flag & SVFORK));
+ mtx_unlock(&p->p_lock);
+#else
+ p = pfind(probe->ftp_pid);
+#endif
+
+ /*
+ * We have to enable the trap entry point before any user threads have
+ * the chance to execute the trap instruction we're about to place
+ * in their process's text.
+ */
+ fasttrap_enable_callbacks();
+
+ /*
+ * Enable all the tracepoints and add this probe's id to each
+ * tracepoint's list of active probes.
+ */
+ for (i = 0; i < probe->ftp_ntps; i++) {
+ if ((rc = fasttrap_tracepoint_enable(p, probe, i)) != 0) {
+ /*
+ * If enabling the tracepoint failed completely,
+ * we don't have to disable it; if the failure
+ * was only partial we must disable it.
+ */
+ if (rc == FASTTRAP_ENABLE_FAIL)
+ i--;
+ else
+ ASSERT(rc == FASTTRAP_ENABLE_PARTIAL);
+
+ /*
+ * Back up and pull out all the tracepoints we've
+ * created so far for this probe.
+ */
+ while (i >= 0) {
+ fasttrap_tracepoint_disable(p, probe, i);
+ i--;
+ }
+
+#ifdef DOODAD
+ mtx_lock(&p->p_lock);
+ sprunlock(p);
+#endif
+
+ /*
+ * Since we're not actually enabling this probe,
+ * drop our reference on the trap table entry.
+ */
+ fasttrap_disable_callbacks();
+ return;
+ }
+ }
+
+#ifdef DOODAD
+ mtx_lock(&p->p_lock);
+ sprunlock(p);
+#endif
+
+ probe->ftp_enabled = 1;
+}
+
+static void
+fasttrap_pid_disable(void *arg, dtrace_id_t id, void *parg)
+{
+printf("%s(%d): DOODAD\n",__func__,__LINE__);
+#ifdef DOODAD
+ fasttrap_probe_t *probe = parg;
+ fasttrap_provider_t *provider = probe->ftp_prov;
+ proc_t *p;
+ int i, whack = 0;
+
+ ASSERT(id == probe->ftp_id);
+
+ /*
+ * We won't be able to acquire a /proc-esque lock on the process
+ * iff the process is dead and gone. In this case, we rely on the
+ * provider lock as a point of mutual exclusion to prevent other
+ * DTrace consumers from disabling this probe.
+ */
+ if ((p = sprlock(probe->ftp_pid)) != NULL) {
+ ASSERT(!(p->p_flag & SVFORK));
+ mtx_unlock(&p->p_lock);
+ }
+
+ mtx_lock(&provider->ftp_mtx);
+
+ /*
+ * Disable all the associated tracepoints (for fully enabled probes).
+ */
+ if (probe->ftp_enabled) {
+ for (i = 0; i < probe->ftp_ntps; i++) {
+ fasttrap_tracepoint_disable(p, probe, i);
+ }
+ }
+
+ ASSERT(provider->ftp_rcount > 0);
+ provider->ftp_rcount--;
+
+ if (p != NULL) {
+ /*
+ * Even though we may not be able to remove it entirely, we
+ * mark this retired provider to get a chance to remove some
+ * of the associated probes.
+ */
+ if (provider->ftp_retired && !provider->ftp_marked)
+ whack = provider->ftp_marked = 1;
+ mtx_unlock(&provider->ftp_mtx);
+
+ mtx_lock(&p->p_lock);
+ sprunlock(p);
+ } else {
+ /*
+ * If the process is dead, we're just waiting for the
+ * last probe to be disabled to be able to free it.
+ */
+ if (provider->ftp_rcount == 0 && !provider->ftp_marked)
+ whack = provider->ftp_marked = 1;
+ mtx_unlock(&provider->ftp_mtx);
+ }
+
+ if (whack)
+ fasttrap_pid_cleanup();
+
+ if (!probe->ftp_enabled)
+ return;
+
+ probe->ftp_enabled = 0;
+
+ ASSERT(MUTEX_HELD(&cpu_lock));
+ fasttrap_disable_callbacks();
+#endif
+}
+
+static void
+fasttrap_pid_getargdesc(void *arg, dtrace_id_t id, void *parg,
+ dtrace_argdesc_t *desc)
+{
+printf("%s(%d): DOODAD\n",__func__,__LINE__);
+#ifdef DOODAD
+ fasttrap_probe_t *probe = parg;
+ char *str;
+ int i, ndx;
+
+ desc->dtargd_native[0] = '\0';
+ desc->dtargd_xlate[0] = '\0';
+
+ if (probe->ftp_prov->ftp_retired != 0 ||
+ desc->dtargd_ndx >= probe->ftp_nargs) {
+ desc->dtargd_ndx = DTRACE_ARGNONE;
+ return;
+ }
+
+ ndx = (probe->ftp_argmap != NULL) ?
+ probe->ftp_argmap[desc->dtargd_ndx] : desc->dtargd_ndx;
+
+ str = probe->ftp_ntypes;
+ for (i = 0; i < ndx; i++) {
+ str += strlen(str) + 1;
+ }
+
+ ASSERT(strlen(str + 1) < sizeof (desc->dtargd_native));
+ (void) strcpy(desc->dtargd_native, str);
+
+ if (probe->ftp_xtypes == NULL)
+ return;
+
+ str = probe->ftp_xtypes;
+ for (i = 0; i < desc->dtargd_ndx; i++) {
+ str += strlen(str) + 1;
+ }
+
+ ASSERT(strlen(str + 1) < sizeof (desc->dtargd_xlate));
+ (void) strcpy(desc->dtargd_xlate, str);
+#endif
+}
+
+static void
+fasttrap_pid_destroy(void *arg, dtrace_id_t id, void *parg)
+{
+printf("%s(%d): DOODAD\n",__func__,__LINE__);
+#ifdef DOODAD
+ fasttrap_probe_t *probe = parg;
+ int i;
+ size_t size;
+
+ ASSERT(probe != NULL);
+ ASSERT(!probe->ftp_enabled);
+ ASSERT(fasttrap_total >= probe->ftp_ntps);
+
+ atomic_add_32(&fasttrap_total, -probe->ftp_ntps);
+ size = offsetof(fasttrap_probe_t, ftp_tps[probe->ftp_ntps]);
+
+ if (probe->ftp_gen + 1 >= fasttrap_mod_gen)
+ fasttrap_mod_barrier(probe->ftp_gen);
+
+ for (i = 0; i < probe->ftp_ntps; i++) {
+ kmem_free(probe->ftp_tps[i].fit_tp,
+ sizeof (fasttrap_tracepoint_t));
+ }
+
+ kmem_free(probe, size);
+#endif
+}
+
+static const dtrace_pattr_t pid_attr = {
+{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_ISA },
+{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
+{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
+{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_ISA },
+{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
+};
+
+static dtrace_pops_t pid_pops = {
+ fasttrap_pid_provide,
+ NULL,
+ fasttrap_pid_enable,
+ fasttrap_pid_disable,
+ NULL,
+ NULL,
+ fasttrap_pid_getargdesc,
+ NULL /*fasttrap_pid_getarg*/,
+ NULL,
+ fasttrap_pid_destroy
+};
+
+static dtrace_pops_t usdt_pops = {
+ fasttrap_pid_provide,
+ NULL,
+ fasttrap_pid_enable,
+ fasttrap_pid_disable,
+ NULL,
+ NULL,
+ fasttrap_pid_getargdesc,
+ NULL /*fasttrap_usdt_getarg*/,
+ NULL,
+ fasttrap_pid_destroy
+};
+
+static fasttrap_proc_t *
+fasttrap_proc_lookup(pid_t pid)
+{
+ fasttrap_bucket_t *bucket;
+ fasttrap_proc_t *fprc, *new_fprc;
+
+ bucket = &fasttrap_procs.fth_table[FASTTRAP_PROCS_INDEX(pid)];
+ mtx_lock(&bucket->ftb_mtx);
+
+ for (fprc = bucket->ftb_data; fprc != NULL; fprc = fprc->ftpc_next) {
+ if (fprc->ftpc_pid == pid && fprc->ftpc_acount != 0) {
+ mtx_lock(&fprc->ftpc_mtx);
+ mtx_unlock(&bucket->ftb_mtx);
+ fprc->ftpc_rcount++;
+ atomic_add_64(&fprc->ftpc_acount, 1);
+ mtx_unlock(&fprc->ftpc_mtx);
+
+ return (fprc);
+ }
+ }
+
+ /*
+ * Drop the bucket lock so we don't try to perform a sleeping
+ * allocation under it.
+ */
+ mtx_unlock(&bucket->ftb_mtx);
+
+ new_fprc = kmem_zalloc(sizeof (fasttrap_proc_t), KM_SLEEP);
+ new_fprc->ftpc_pid = pid;
+ new_fprc->ftpc_rcount = 1;
+ new_fprc->ftpc_acount = 1;
+
+ mtx_lock(&bucket->ftb_mtx);
+
+ /*
+ * Take another lap through the list to make sure a proc hasn't
+ * been created for this pid while we weren't under the bucket lock.
+ */
+ for (fprc = bucket->ftb_data; fprc != NULL; fprc = fprc->ftpc_next) {
+ if (fprc->ftpc_pid == pid && fprc->ftpc_acount != 0) {
+ mtx_lock(&fprc->ftpc_mtx);
+ mtx_unlock(&bucket->ftb_mtx);
+ fprc->ftpc_rcount++;
+ atomic_add_64(&fprc->ftpc_acount, 1);
+ mtx_unlock(&fprc->ftpc_mtx);
+
+ kmem_free(new_fprc, sizeof (fasttrap_proc_t));
+
+ return (fprc);
+ }
+ }
+
+ new_fprc->ftpc_next = bucket->ftb_data;
+ bucket->ftb_data = new_fprc;
+
+ mtx_unlock(&bucket->ftb_mtx);
+
+ return (new_fprc);
+}
+
+static void
+fasttrap_proc_release(fasttrap_proc_t *proc)
+{
+ fasttrap_bucket_t *bucket;
+ fasttrap_proc_t *fprc, **fprcp;
+ pid_t pid = proc->ftpc_pid;
+
+ mtx_lock(&proc->ftpc_mtx);
+
+ ASSERT(proc->ftpc_rcount != 0);
+
+ if (--proc->ftpc_rcount != 0) {
+ mtx_unlock(&proc->ftpc_mtx);
+ return;
+ }
+
+ mtx_unlock(&proc->ftpc_mtx);
+
+ /*
+ * There should definitely be no live providers associated with this
+ * process at this point.
+ */
+ ASSERT(proc->ftpc_acount == 0);
+
+ bucket = &fasttrap_procs.fth_table[FASTTRAP_PROCS_INDEX(pid)];
+ mtx_lock(&bucket->ftb_mtx);
+
+ fprcp = (fasttrap_proc_t **)&bucket->ftb_data;
+ while ((fprc = *fprcp) != NULL) {
+ if (fprc == proc)
+ break;
+
+ fprcp = &fprc->ftpc_next;
+ }
+
+ /*
+ * Something strange has happened if we can't find the proc.
+ */
+ ASSERT(fprc != NULL);
+
+ *fprcp = fprc->ftpc_next;
+
+ mtx_unlock(&bucket->ftb_mtx);
+
+ kmem_free(fprc, sizeof (fasttrap_proc_t));
+}
+
+/*
+ * Lookup a fasttrap-managed provider based on its name and associated pid.
+ * If the pattr argument is non-NULL, this function instantiates the provider
+ * if it doesn't exist otherwise it returns NULL. The provider is returned
+ * with its lock held.
+ */
+static fasttrap_provider_t *
+fasttrap_provider_lookup(pid_t pid, const char *name,
+ const dtrace_pattr_t *pattr)
+{
+ fasttrap_provider_t *fp, *new_fp = NULL;
+ fasttrap_bucket_t *bucket;
+ char provname[DTRACE_PROVNAMELEN];
+ proc_t *p;
+#ifdef DOODAD
+ cred_t *cred;
+#endif
+
+ ASSERT(strlen(name) < sizeof (fp->ftp_name));
+ ASSERT(pattr != NULL);
+
+ bucket = &fasttrap_provs.fth_table[FASTTRAP_PROVS_INDEX(pid, name)];
+ mtx_lock(&bucket->ftb_mtx);
+
+ /*
+ * Take a lap through the list and return the match if we find it.
+ */
+ for (fp = bucket->ftb_data; fp != NULL; fp = fp->ftp_next) {
+ if (fp->ftp_pid == pid && strcmp(fp->ftp_name, name) == 0 &&
+ !fp->ftp_retired) {
+ mtx_lock(&fp->ftp_mtx);
+ mtx_unlock(&bucket->ftb_mtx);
+ return (fp);
+ }
+ }
+
+ /*
+ * Drop the bucket lock so we don't try to perform a sleeping
+ * allocation under it.
+ */
+ mtx_unlock(&bucket->ftb_mtx);
+
+#ifdef DOODAD
+ /*
+ * Make sure the process exists, isn't a child created as the result
+ * of a vfork(2), and isn't a zombie (but may be in fork).
+ */
+ mtx_lock(&pidlock);
+ if ((p = prfind(pid)) == NULL) {
+ mtx_unlock(&pidlock);
+ return (NULL);
+ }
+ mtx_lock(&p->p_lock);
+ mtx_unlock(&pidlock);
+ if (p->p_flag & (SVFORK | SEXITING)) {
+ mtx_unlock(&p->p_lock);
+ return (NULL);
+ }
+
+ /*
+ * Increment p_dtrace_probes so that the process knows to inform us
+ * when it exits or execs. fasttrap_provider_free() decrements this
+ * when we're done with this provider.
+ */
+ p->p_dtrace_probes++;
+
+ /*
+ * Grab the credentials for this process so we have
+ * something to pass to dtrace_register().
+ */
+ mtx_lock(&p->p_crlock);
+ crhold(p->p_cred);
+ cred = p->p_cred;
+ mtx_unlock(&p->p_crlock);
+ mtx_unlock(&p->p_lock);
+#else
+ p = pfind(pid);
+#endif
+
+ new_fp = kmem_zalloc(sizeof (fasttrap_provider_t), KM_SLEEP);
+ new_fp->ftp_pid = pid;
+ new_fp->ftp_proc = fasttrap_proc_lookup(pid);
+
+ ASSERT(new_fp->ftp_proc != NULL);
+
+ mtx_lock(&bucket->ftb_mtx);
+
+ /*
+ * Take another lap through the list to make sure a provider hasn't
+ * been created for this pid while we weren't under the bucket lock.
+ */
+ for (fp = bucket->ftb_data; fp != NULL; fp = fp->ftp_next) {
+ if (fp->ftp_pid == pid && strcmp(fp->ftp_name, name) == 0 &&
+ !fp->ftp_retired) {
+ mtx_lock(&fp->ftp_mtx);
+ mtx_unlock(&bucket->ftb_mtx);
+ fasttrap_provider_free(new_fp);
+#ifdef DOODAD
+ crfree(cred);
+#endif
+ return (fp);
+ }
+ }
+
+ (void) strcpy(new_fp->ftp_name, name);
+
+ /*
+ * Fail and return NULL if either the provider name is too long
+ * or we fail to register this new provider with the DTrace
+ * framework. Note that this is the only place we ever construct
+ * the full provider name -- we keep it in pieces in the provider
+ * structure.
+ */
+printf("%s(%d): provname '%s%u'\n", __func__, __LINE__, name, (uint_t)pid);
+ if (snprintf(provname, sizeof (provname), "%s%u", name, (uint_t)pid) >=
+ sizeof (provname) ||
+ dtrace_register(provname, pattr,
+ DTRACE_PRIV_PROC | DTRACE_PRIV_OWNER | DTRACE_PRIV_ZONEOWNER, NULL /*cred*/,
+ pattr == &pid_attr ? &pid_pops : &usdt_pops, new_fp,
+ &new_fp->ftp_provid) != 0) {
+printf("%s(%d): could not register!\n",__func__,__LINE__);
+ mtx_unlock(&bucket->ftb_mtx);
+ fasttrap_provider_free(new_fp);
+#ifdef DOODAD
+ crfree(cred);
+#endif
+ return (NULL);
+ }
+
+ new_fp->ftp_next = bucket->ftb_data;
+ bucket->ftb_data = new_fp;
+
+ mtx_lock(&new_fp->ftp_mtx);
+ mtx_unlock(&bucket->ftb_mtx);
+
+#ifdef DOODAD
+ crfree(cred);
+#endif
+ return (new_fp);
+}
+
+static void
+fasttrap_provider_free(fasttrap_provider_t *provider)
+{
+printf("%s(%d): DOODAD\n",__func__,__LINE__);
+#ifdef DOODAD
+ pid_t pid = provider->ftp_pid;
+ proc_t *p;
+#endif
+
+ /*
+ * There need to be no associated enabled probes, no consumers
+ * creating probes, and no meta providers referencing this provider.
+ */
+ ASSERT(provider->ftp_rcount == 0);
+ ASSERT(provider->ftp_ccount == 0);
+ ASSERT(provider->ftp_mcount == 0);
+
+ fasttrap_proc_release(provider->ftp_proc);
+
+ kmem_free(provider, sizeof (fasttrap_provider_t));
+
+#ifdef DOODAD
+ /*
+ * Decrement p_dtrace_probes on the process whose provider we're
+ * freeing. We don't have to worry about clobbering somone else's
+ * modifications to it because we have locked the bucket that
+ * corresponds to this process's hash chain in the provider hash
+ * table. Don't sweat it if we can't find the process.
+ */
+ mtx_lock(&pidlock);
+ if ((p = prfind(pid)) == NULL) {
+ mtx_unlock(&pidlock);
+ return;
+ }
+
+ mtx_lock(&p->p_lock);
+ mtx_unlock(&pidlock);
+
+ p->p_dtrace_probes--;
+ mtx_unlock(&p->p_lock);
+#endif
+}
+
+static void
+fasttrap_provider_retire(pid_t pid, const char *name, int mprov)
+{
+ fasttrap_provider_t *fp;
+ fasttrap_bucket_t *bucket;
+ dtrace_provider_id_t provid;
+
+ ASSERT(strlen(name) < sizeof (fp->ftp_name));
+
+ bucket = &fasttrap_provs.fth_table[FASTTRAP_PROVS_INDEX(pid, name)];
+ mtx_lock(&bucket->ftb_mtx);
+
+ for (fp = bucket->ftb_data; fp != NULL; fp = fp->ftp_next) {
+ if (fp->ftp_pid == pid && strcmp(fp->ftp_name, name) == 0 &&
+ !fp->ftp_retired)
+ break;
+ }
+
+ if (fp == NULL) {
+ mtx_unlock(&bucket->ftb_mtx);
+ return;
+ }
+
+ mtx_lock(&fp->ftp_mtx);
+ ASSERT(!mprov || fp->ftp_mcount > 0);
+ if (mprov && --fp->ftp_mcount != 0) {
+ mtx_unlock(&fp->ftp_mtx);
+ mtx_unlock(&bucket->ftb_mtx);
+ return;
+ }
+
+ /*
+ * Mark the provider to be removed in our post-processing step, mark it
+ * retired, and drop the active count on its proc. Marking it indicates
+ * that we should try to remove it; setting the retired flag indicates
+ * that we're done with this provider; dropping the active the proc
+ * releases our hold, and when this reaches zero (as it will during
+ * exit or exec) the proc and associated providers become defunct.
+ *
+ * We obviously need to take the bucket lock before the provider lock
+ * to perform the lookup, but we need to drop the provider lock
+ * before calling into the DTrace framework since we acquire the
+ * provider lock in callbacks invoked from the DTrace framework. The
+ * bucket lock therefore protects the integrity of the provider hash
+ * table.
+ */
+ atomic_add_64(&fp->ftp_proc->ftpc_acount, -1);
+ fp->ftp_retired = 1;
+ fp->ftp_marked = 1;
+ provid = fp->ftp_provid;
+ mtx_unlock(&fp->ftp_mtx);
+
+ /*
+ * We don't have to worry about invalidating the same provider twice
+ * since fasttrap_provider_lookup() will ignore provider that have
+ * been marked as retired.
+ */
+ dtrace_invalidate(provid);
+
+ mtx_unlock(&bucket->ftb_mtx);
+
+ fasttrap_pid_cleanup();
+}
+
+static int __unused
+fasttrap_uint32_cmp(const void *ap, const void *bp)
+{
+ return (*(const uint32_t *)ap - *(const uint32_t *)bp);
+}
+
+static int
+fasttrap_uint64_cmp(const void *ap, const void *bp)
+{
+ return (*(const uint64_t *)ap - *(const uint64_t *)bp);
+}
+
+static int __unused
+fasttrap_add_probe (fasttrap_probe_spec_t *pdata)
+{
+ fasttrap_provider_t *provider;
+ fasttrap_probe_t *pp;
+ fasttrap_tracepoint_t *tp;
+ char *name;
+ int i, aframes, whack;
+
+ /*
+ * There needs to be at least one desired trace point.
+ */
+ if (pdata->ftps_noffs == 0)
+ return (EINVAL);
+
+ switch (pdata->ftps_type) {
+ case DTFTP_ENTRY:
+ name = "entry";
+ aframes = FASTTRAP_ENTRY_AFRAMES;
+ break;
+ case DTFTP_RETURN:
+ name = "return";
+ aframes = FASTTRAP_RETURN_AFRAMES;
+ break;
+ case DTFTP_OFFSETS:
+ name = NULL;
+ break;
+ default:
+ return (EINVAL);
+ }
+
+ if ((provider = fasttrap_provider_lookup(pdata->ftps_pid,
+ FASTTRAP_PID_NAME, &pid_attr)) == NULL)
+ return (ESRCH);
+
+ /*
+ * Increment this reference count to indicate that a consumer is
+ * actively adding a new probe associated with this provider. This
+ * prevents the provider from being deleted -- we'll need to check
+ * for pending deletions when we drop this reference count.
+ */
+ provider->ftp_ccount++;
+ mtx_unlock(&provider->ftp_mtx);
+
+ /*
+ * Grab the creation lock to ensure consistency between calls to
+ * dtrace_probe_lookup() and dtrace_probe_create() in the face of
+ * other threads creating probes. We must drop the provider lock
+ * before taking this lock to avoid a three-way deadlock with the
+ * DTrace framework.
+ */
+ mtx_lock(&provider->ftp_cmtx);
+
+ if (name == NULL) {
+ for (i = 0; i < pdata->ftps_noffs; i++) {
+ char name_str[17];
+
+ (void) sprintf(name_str, "%llx",
+ (unsigned long long)pdata->ftps_offs[i]);
+
+ if (dtrace_probe_lookup(provider->ftp_provid,
+ pdata->ftps_mod, pdata->ftps_func, name_str) != 0)
+ continue;
+
+ atomic_add_32(&fasttrap_total, 1);
+
+ if (fasttrap_total > fasttrap_max) {
+ atomic_add_32(&fasttrap_total, -1);
+ goto no_mem;
+ }
+
+ pp = kmem_zalloc(sizeof (fasttrap_probe_t), KM_SLEEP);
+
+ pp->ftp_prov = provider;
+ pp->ftp_faddr = pdata->ftps_pc;
+ pp->ftp_fsize = pdata->ftps_size;
+ pp->ftp_pid = pdata->ftps_pid;
+ pp->ftp_ntps = 1;
+
+ tp = kmem_zalloc(sizeof (fasttrap_tracepoint_t),
+ KM_SLEEP);
+
+ tp->ftt_proc = provider->ftp_proc;
+ tp->ftt_pc = pdata->ftps_offs[i] + pdata->ftps_pc;
+ tp->ftt_pid = pdata->ftps_pid;
+
+ pp->ftp_tps[0].fit_tp = tp;
+ pp->ftp_tps[0].fit_id.fti_probe = pp;
+ pp->ftp_tps[0].fit_id.fti_ptype = pdata->ftps_type;
+
+ pp->ftp_id = dtrace_probe_create(provider->ftp_provid,
+ pdata->ftps_mod, pdata->ftps_func, name_str,
+ FASTTRAP_OFFSET_AFRAMES, pp);
+ }
+
+ } else if (dtrace_probe_lookup(provider->ftp_provid, pdata->ftps_mod,
+ pdata->ftps_func, name) == 0) {
+ atomic_add_32(&fasttrap_total, pdata->ftps_noffs);
+
+ if (fasttrap_total > fasttrap_max) {
+ atomic_add_32(&fasttrap_total, -pdata->ftps_noffs);
+ goto no_mem;
+ }
+
+ /*
+ * Make sure all tracepoint program counter values are unique.
+ * We later assume that each probe has exactly one tracepoint
+ * for a given pc.
+ */
+ qsort(pdata->ftps_offs, pdata->ftps_noffs,
+ sizeof (uint64_t), fasttrap_uint64_cmp);
+ for (i = 1; i < pdata->ftps_noffs; i++) {
+ if (pdata->ftps_offs[i] > pdata->ftps_offs[i - 1])
+ continue;
+
+ atomic_add_32(&fasttrap_total, -pdata->ftps_noffs);
+ goto no_mem;
+ }
+
+ ASSERT(pdata->ftps_noffs > 0);
+ pp = kmem_zalloc(offsetof(fasttrap_probe_t,
+ ftp_tps[pdata->ftps_noffs]), KM_SLEEP);
+
+ pp->ftp_prov = provider;
+ pp->ftp_faddr = pdata->ftps_pc;
+ pp->ftp_fsize = pdata->ftps_size;
+ pp->ftp_pid = pdata->ftps_pid;
+ pp->ftp_ntps = pdata->ftps_noffs;
+
+ for (i = 0; i < pdata->ftps_noffs; i++) {
+ tp = kmem_zalloc(sizeof (fasttrap_tracepoint_t),
+ KM_SLEEP);
+
+ tp->ftt_proc = provider->ftp_proc;
+ tp->ftt_pc = pdata->ftps_offs[i] + pdata->ftps_pc;
+ tp->ftt_pid = pdata->ftps_pid;
+
+ pp->ftp_tps[i].fit_tp = tp;
+ pp->ftp_tps[i].fit_id.fti_probe = pp;
+ pp->ftp_tps[i].fit_id.fti_ptype = pdata->ftps_type;
+ }
+
+ pp->ftp_id = dtrace_probe_create(provider->ftp_provid,
+ pdata->ftps_mod, pdata->ftps_func, name, aframes, pp);
+ }
+
+ mtx_unlock(&provider->ftp_cmtx);
+
+ /*
+ * We know that the provider is still valid since we incremented the
+ * creation reference count. If someone tried to clean up this provider
+ * while we were using it (e.g. because the process called exec(2) or
+ * exit(2)), take note of that and try to clean it up now.
+ */
+ mtx_lock(&provider->ftp_mtx);
+ provider->ftp_ccount--;
+ whack = provider->ftp_retired;
+ mtx_unlock(&provider->ftp_mtx);
+
+ if (whack)
+ fasttrap_pid_cleanup();
+
+ return (0);
+
+no_mem:
+ /*
+ * If we've exhausted the allowable resources, we'll try to remove
+ * this provider to free some up. This is to cover the case where
+ * the user has accidentally created many more probes than was
+ * intended (e.g. pid123:::).
+ */
+ mtx_unlock(&provider->ftp_cmtx);
+ mtx_lock(&provider->ftp_mtx);
+ provider->ftp_ccount--;
+ provider->ftp_marked = 1;
+ mtx_unlock(&provider->ftp_mtx);
+
+ fasttrap_pid_cleanup();
+
+ return (ENOMEM);
+}
+
+static void *
+fasttrap_meta_provide(void *arg, dtrace_helper_provdesc_t *dhpv, pid_t pid)
+{
+ fasttrap_provider_t *provider;
+
+ /*
+ * A 32-bit unsigned integer (like a pid for example) can be
+ * expressed in 10 or fewer decimal digits. Make sure that we'll
+ * have enough space for the provider name.
+ */
+ if (strlen(dhpv->dthpv_provname) + 10 >=
+ sizeof (provider->ftp_name)) {
+ printf("failed to instantiate provider %s: name too long to accomodate pid\n", dhpv->dthpv_provname);
+ return (NULL);
+ }
+
+ /*
+ * Don't let folks spoof the true pid provider.
+ */
+ if (strcmp(dhpv->dthpv_provname, FASTTRAP_PID_NAME) == 0) {
+ printf("failed to instantiate provider %s: %s is an invalid name\n", dhpv->dthpv_provname,
+ FASTTRAP_PID_NAME);
+ return (NULL);
+ }
+
+ /*
+ * The highest stability class that fasttrap supports is ISA; cap
+ * the stability of the new provider accordingly.
+ */
+ if (dhpv->dthpv_pattr.dtpa_provider.dtat_class > DTRACE_CLASS_ISA)
+ dhpv->dthpv_pattr.dtpa_provider.dtat_class = DTRACE_CLASS_ISA;
+ if (dhpv->dthpv_pattr.dtpa_mod.dtat_class > DTRACE_CLASS_ISA)
+ dhpv->dthpv_pattr.dtpa_mod.dtat_class = DTRACE_CLASS_ISA;
+ if (dhpv->dthpv_pattr.dtpa_func.dtat_class > DTRACE_CLASS_ISA)
+ dhpv->dthpv_pattr.dtpa_func.dtat_class = DTRACE_CLASS_ISA;
+ if (dhpv->dthpv_pattr.dtpa_name.dtat_class > DTRACE_CLASS_ISA)
+ dhpv->dthpv_pattr.dtpa_name.dtat_class = DTRACE_CLASS_ISA;
+ if (dhpv->dthpv_pattr.dtpa_args.dtat_class > DTRACE_CLASS_ISA)
+ dhpv->dthpv_pattr.dtpa_args.dtat_class = DTRACE_CLASS_ISA;
+
+ if ((provider = fasttrap_provider_lookup(pid, dhpv->dthpv_provname,
+ &dhpv->dthpv_pattr)) == NULL) {
+ printf("failed to instantiate provider %s for process %u\n", dhpv->dthpv_provname, (uint_t)pid);
+ return (NULL);
+ }
+
+ /*
+ * Up the meta provider count so this provider isn't removed until
+ * the meta provider has been told to remove it.
+ */
+ provider->ftp_mcount++;
+
+ mtx_unlock(&provider->ftp_mtx);
+
+ return (provider);
+}
+
+static void
+fasttrap_meta_create_probe(void *arg, void *parg,
+ dtrace_helper_probedesc_t *dhpb)
+{
+printf("%s(%d): DOODAD\n",__func__,__LINE__);
+#ifdef DOODAD
+ fasttrap_provider_t *provider = parg;
+ fasttrap_probe_t *pp;
+ fasttrap_tracepoint_t *tp;
+ int i, j;
+ uint32_t ntps;
+
+ /*
+ * Since the meta provider count is non-zero we don't have to worry
+ * about this provider disappearing.
+ */
+ ASSERT(provider->ftp_mcount > 0);
+
+ /*
+ * The offsets must be unique.
+ */
+ qsort(dhpb->dthpb_offs, dhpb->dthpb_noffs, sizeof (uint32_t),
+ fasttrap_uint32_cmp);
+ for (i = 1; i < dhpb->dthpb_noffs; i++) {
+ if (dhpb->dthpb_base + dhpb->dthpb_offs[i] <=
+ dhpb->dthpb_base + dhpb->dthpb_offs[i - 1])
+ return;
+ }
+
+ qsort(dhpb->dthpb_enoffs, dhpb->dthpb_nenoffs, sizeof (uint32_t),
+ fasttrap_uint32_cmp);
+ for (i = 1; i < dhpb->dthpb_nenoffs; i++) {
+ if (dhpb->dthpb_base + dhpb->dthpb_enoffs[i] <=
+ dhpb->dthpb_base + dhpb->dthpb_enoffs[i - 1])
+ return;
+ }
+
+ /*
+ * Grab the creation lock to ensure consistency between calls to
+ * dtrace_probe_lookup() and dtrace_probe_create() in the face of
+ * other threads creating probes.
+ */
+ mtx_lock(&provider->ftp_cmtx);
+
+ if (dtrace_probe_lookup(provider->ftp_provid, dhpb->dthpb_mod,
+ dhpb->dthpb_func, dhpb->dthpb_name) != 0) {
+ mtx_unlock(&provider->ftp_cmtx);
+ return;
+ }
+
+ ntps = dhpb->dthpb_noffs + dhpb->dthpb_nenoffs;
+ ASSERT(ntps > 0);
+
+ atomic_add_32(&fasttrap_total, ntps);
+
+ if (fasttrap_total > fasttrap_max) {
+ atomic_add_32(&fasttrap_total, -ntps);
+ mtx_unlock(&provider->ftp_cmtx);
+ return;
+ }
+
+ pp = kmem_zalloc(offsetof(fasttrap_probe_t, ftp_tps[ntps]), KM_SLEEP);
+
+ pp->ftp_prov = provider;
+ pp->ftp_pid = provider->ftp_pid;
+ pp->ftp_ntps = ntps;
+ pp->ftp_nargs = dhpb->dthpb_xargc;
+ pp->ftp_xtypes = dhpb->dthpb_xtypes;
+ pp->ftp_ntypes = dhpb->dthpb_ntypes;
+
+ /*
+ * First create a tracepoint for each actual point of interest.
+ */
+ for (i = 0; i < dhpb->dthpb_noffs; i++) {
+ tp = kmem_zalloc(sizeof (fasttrap_tracepoint_t), KM_SLEEP);
+
+ tp->ftt_proc = provider->ftp_proc;
+ tp->ftt_pc = dhpb->dthpb_base + dhpb->dthpb_offs[i];
+ tp->ftt_pid = provider->ftp_pid;
+
+ pp->ftp_tps[i].fit_tp = tp;
+ pp->ftp_tps[i].fit_id.fti_probe = pp;
+#ifdef __sparc
+ pp->ftp_tps[i].fit_id.fti_ptype = DTFTP_POST_OFFSETS;
+#else
+ pp->ftp_tps[i].fit_id.fti_ptype = DTFTP_OFFSETS;
+#endif
+ }
+
+ /*
+ * Then create a tracepoint for each is-enabled point.
+ */
+ for (j = 0; i < ntps; i++, j++) {
+ tp = kmem_zalloc(sizeof (fasttrap_tracepoint_t), KM_SLEEP);
+
+ tp->ftt_proc = provider->ftp_proc;
+ tp->ftt_pc = dhpb->dthpb_base + dhpb->dthpb_enoffs[j];
+ tp->ftt_pid = provider->ftp_pid;
+
+ pp->ftp_tps[i].fit_tp = tp;
+ pp->ftp_tps[i].fit_id.fti_probe = pp;
+ pp->ftp_tps[i].fit_id.fti_ptype = DTFTP_IS_ENABLED;
+ }
+
+ /*
+ * If the arguments are shuffled around we set the argument remapping
+ * table. Later, when the probe fires, we only remap the arguments
+ * if the table is non-NULL.
+ */
+ for (i = 0; i < dhpb->dthpb_xargc; i++) {
+ if (dhpb->dthpb_args[i] != i) {
+ pp->ftp_argmap = dhpb->dthpb_args;
+ break;
+ }
+ }
+
+ /*
+ * The probe is fully constructed -- register it with DTrace.
+ */
+ pp->ftp_id = dtrace_probe_create(provider->ftp_provid, dhpb->dthpb_mod,
+ dhpb->dthpb_func, dhpb->dthpb_name, FASTTRAP_OFFSET_AFRAMES, pp);
+
+ mtx_unlock(&provider->ftp_cmtx);
+#endif
+}
+
+static void
+fasttrap_meta_remove(void *arg, dtrace_helper_provdesc_t *dhpv, pid_t pid)
+{
+printf("%s(%d): \n",__func__,__LINE__);
+ /*
+ * Clean up the USDT provider. There may be active consumers of the
+ * provider busy adding probes, no damage will actually befall the
+ * provider until that count has dropped to zero. This just puts
+ * the provider on death row.
+ */
+ fasttrap_provider_retire(pid, dhpv->dthpv_provname, 1);
+}
+
+static dtrace_mops_t fasttrap_mops = {
+ fasttrap_meta_create_probe,
+ fasttrap_meta_provide,
+ fasttrap_meta_remove
+};
+
+static void
+fasttrap_load(void *dummy)
+{
+ int i;
+ int nent;
+
+ /* Create the /dev/dtrace/fasttrap entry. */
+ fasttrap_cdev = make_dev(&fasttrap_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600,
+ "dtrace/fasttrap");
+
+ /*
+ * Install our hooks into fork(2), exec(2), and exit(2).
+ */
+ dtrace_fasttrap_fork = fasttrap_fork;
+ dtrace_fasttrap_exit = fasttrap_exec_exit;
+ dtrace_fasttrap_exec = fasttrap_exec_exit;
+
+ fasttrap_max = FASTTRAP_MAX_DEFAULT;
+ fasttrap_total = 0;
+
+ /*
+ * Conjure up the tracepoints hashtable...
+ */
+ nent = FASTTRAP_TPOINTS_DEFAULT_SIZE; /* XXX sysctl? */
+
+ if (nent == 0 || nent > 0x1000000)
+ nent = FASTTRAP_TPOINTS_DEFAULT_SIZE;
+
+ if ((nent & (nent - 1)) == 0)
+ fasttrap_tpoints.fth_nent = nent;
+ else
+ fasttrap_tpoints.fth_nent = 1 << fasttrap_highbit(nent);
+ ASSERT(fasttrap_tpoints.fth_nent > 0);
+ fasttrap_tpoints.fth_mask = fasttrap_tpoints.fth_nent - 1;
+ fasttrap_tpoints.fth_table = kmem_zalloc(fasttrap_tpoints.fth_nent *
+ sizeof (fasttrap_bucket_t), KM_SLEEP);
+
+ for (i = 0; i < fasttrap_tpoints.fth_nent; i++)
+ mtx_init(&fasttrap_tpoints.fth_table[i].ftb_mtx,
+ "Fasttrap tpoints", NULL, MTX_DEF);
+
+ /*
+ * ... and the providers hash table...
+ */
+ nent = FASTTRAP_PROVIDERS_DEFAULT_SIZE;
+ if ((nent & (nent - 1)) == 0)
+ fasttrap_provs.fth_nent = nent;
+ else
+ fasttrap_provs.fth_nent = 1 << fasttrap_highbit(nent);
+ ASSERT(fasttrap_provs.fth_nent > 0);
+ fasttrap_provs.fth_mask = fasttrap_provs.fth_nent - 1;
+ fasttrap_provs.fth_table = kmem_zalloc(fasttrap_provs.fth_nent *
+ sizeof (fasttrap_bucket_t), KM_SLEEP);
+
+ for (i = 0; i < fasttrap_provs.fth_nent; i++)
+ mtx_init(&fasttrap_provs.fth_table[i].ftb_mtx,
+ "Fasttrap provs", NULL, MTX_DEF);
+
+ /*
+ * ... and the procs hash table.
+ */
+ nent = FASTTRAP_PROCS_DEFAULT_SIZE;
+ if ((nent & (nent - 1)) == 0)
+ fasttrap_procs.fth_nent = nent;
+ else
+ fasttrap_procs.fth_nent = 1 << fasttrap_highbit(nent);
+ ASSERT(fasttrap_procs.fth_nent > 0);
+ fasttrap_procs.fth_mask = fasttrap_procs.fth_nent - 1;
+ fasttrap_procs.fth_table = kmem_zalloc(fasttrap_procs.fth_nent *
+ sizeof (fasttrap_bucket_t), KM_SLEEP);
+
+ for (i = 0; i < fasttrap_procs.fth_nent; i++)
+ mtx_init(&fasttrap_procs.fth_table[i].ftb_mtx,
+ "Fasttrap procs", NULL, MTX_DEF);
+
+
+ (void) dtrace_meta_register("fasttrap", &fasttrap_mops, NULL,
+ &fasttrap_meta_id);
+}
+
+static int
+fasttrap_unload()
+{
+ int error = 0;
+ int i, fail = 0;
+ struct callout_handle tmp;
+
+ /* Reset our calback hooks. */
+ dtrace_fasttrap_fork = NULL;
+ dtrace_fasttrap_exec = NULL;
+ dtrace_fasttrap_exit = NULL;
+
+ /*
+ * Unregister the meta-provider to make sure no new fasttrap-
+ * managed providers come along while we're trying to close up
+ * shop. If we fail to detach, we'll need to re-register as a
+ * meta-provider. We can fail to unregister as a meta-provider
+ * if providers we manage still exist.
+ */
+ if (fasttrap_meta_id != DTRACE_METAPROVNONE &&
+ dtrace_meta_unregister(fasttrap_meta_id) != 0)
+ return (ENOENT);
+
+ /*
+ * Prevent any new timeouts from running by setting fasttrap_timeout
+ * to a non-zero value, and wait for the current timeout to complete.
+ */
+ mtx_lock(&fasttrap_cleanup_mtx);
+ fasttrap_cleanup_work = 0;
+
+ while (fasttrap_timeout.callout != (struct callout *)(uintptr_t)1) {
+ tmp = fasttrap_timeout;
+ fasttrap_timeout.callout = (struct callout *)(uintptr_t)1;
+
+ if (tmp.callout != NULL) {
+ mtx_unlock(&fasttrap_cleanup_mtx);
+ (void) untimeout(&fasttrap_pid_cleanup_cb, NULL, tmp);
+ mtx_lock(&fasttrap_cleanup_mtx);
+ }
+ }
+
+ fasttrap_cleanup_work = 0;
+ mtx_unlock(&fasttrap_cleanup_mtx);
+
+ /*
+ * Iterate over all of our providers. If there's still a process
+ * that corresponds to that pid, fail to detach.
+ */
+ for (i = 0; i < fasttrap_provs.fth_nent; i++) {
+ fasttrap_provider_t **fpp, *fp;
+ fasttrap_bucket_t *bucket = &fasttrap_provs.fth_table[i];
+
+ mtx_lock(&bucket->ftb_mtx);
+ fpp = (fasttrap_provider_t **)&bucket->ftb_data;
+ while ((fp = *fpp) != NULL) {
+ /*
+ * Acquire and release the lock as a simple way of
+ * waiting for any other consumer to finish with
+ * this provider. A thread must first acquire the
+ * bucket lock so there's no chance of another thread
+ * blocking on the provider's lock.
+ */
+ mtx_lock(&fp->ftp_mtx);
+ mtx_unlock(&fp->ftp_mtx);
+
+ if (dtrace_unregister(fp->ftp_provid) != 0) {
+ fail = 1;
+ fpp = &fp->ftp_next;
+ } else {
+ *fpp = fp->ftp_next;
+ fasttrap_provider_free(fp);
+ }
+ }
+
+ mtx_unlock(&bucket->ftb_mtx);
+ }
+
+ if (fail) {
+ uint_t work;
+ /*
+ * If we're failing to detach, we need to unblock timeouts
+ * and start a new timeout if any work has accumulated while
+ * we've been unsuccessfully trying to detach.
+ */
+ mtx_lock(&fasttrap_cleanup_mtx);
+ fasttrap_timeout.callout = NULL;
+ work = fasttrap_cleanup_work;
+ mtx_unlock(&fasttrap_cleanup_mtx);
+
+ if (work)
+ fasttrap_pid_cleanup();
+
+ (void) dtrace_meta_register("fasttrap", &fasttrap_mops, NULL,
+ &fasttrap_meta_id);
+
+ return (-1);
+ }
+
+#ifdef DEBUG
+ mtx_lock(&fasttrap_count_mtx);
+ ASSERT(fasttrap_pid_count == 0);
+ mtx_unlock(&fasttrap_count_mtx);
+#endif
+
+ for (i = 0; i < fasttrap_tpoints.fth_nent; i++)
+ mtx_destroy(&fasttrap_tpoints.fth_table[i].ftb_mtx);
+
+ kmem_free(fasttrap_tpoints.fth_table,
+ fasttrap_tpoints.fth_nent * sizeof (fasttrap_bucket_t));
+ fasttrap_tpoints.fth_nent = 0;
+
+ for (i = 0; i < fasttrap_provs.fth_nent; i++)
+ mtx_destroy(&fasttrap_provs.fth_table[i].ftb_mtx);
+
+ kmem_free(fasttrap_provs.fth_table,
+ fasttrap_provs.fth_nent * sizeof (fasttrap_bucket_t));
+ fasttrap_provs.fth_nent = 0;
+
+ for (i = 0; i < fasttrap_procs.fth_nent; i++)
+ mtx_destroy(&fasttrap_procs.fth_table[i].ftb_mtx);
+
+ kmem_free(fasttrap_procs.fth_table,
+ fasttrap_procs.fth_nent * sizeof (fasttrap_bucket_t));
+ fasttrap_procs.fth_nent = 0;
+
+ destroy_dev(fasttrap_cdev);
+
+ return (error);
+}
+
+static int
+fasttrap_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
+fasttrap_open(struct cdev *dev __unused, int oflags __unused, int devtype __unused, struct thread *td __unused)
+{
+ return (0);
+}
+
+static int
+fasttrap_ioctl(struct cdev *dev __unused, u_long cmd __unused, caddr_t addr __unused, int flags __unused, struct thread *td __unused)
+{
+printf("%s(%d): DOODAD\n",__func__,__LINE__);
+#ifdef DOODAD
+ if (!dtrace_attached())
+ return (EAGAIN);
+
+ if (cmd == FASTTRAPIOC_MAKEPROBE) {
+ fasttrap_probe_spec_t *uprobe = (void *)arg;
+ fasttrap_probe_spec_t *probe;
+ uint64_t noffs;
+ size_t size;
+ int ret;
+ char *c;
+
+ if (copyin(&uprobe->ftps_noffs, &noffs,
+ sizeof (uprobe->ftps_noffs)))
+ return (EFAULT);
+
+ /*
+ * Probes must have at least one tracepoint.
+ */
+ if (noffs == 0)
+ return (EINVAL);
+
+ size = sizeof (fasttrap_probe_spec_t) +
+ sizeof (probe->ftps_offs[0]) * (noffs - 1);
+
+ if (size > 1024 * 1024)
+ return (ENOMEM);
+
+ probe = kmem_alloc(size, KM_SLEEP);
+
+ if (copyin(uprobe, probe, size) != 0) {
+ kmem_free(probe, size);
+ return (EFAULT);
+ }
+
+ /*
+ * Verify that the function and module strings contain no
+ * funny characters.
+ */
+ for (c = &probe->ftps_func[0]; *c != '\0'; c++) {
+ if (*c < 0x20 || 0x7f <= *c) {
+ ret = EINVAL;
+ goto err;
+ }
+ }
+
+ for (c = &probe->ftps_mod[0]; *c != '\0'; c++) {
+ if (*c < 0x20 || 0x7f <= *c) {
+ ret = EINVAL;
+ goto err;
+ }
+ }
+
+ if (!PRIV_POLICY_CHOICE(cr, PRIV_ALL, B_FALSE)) {
+ proc_t *p;
+ pid_t pid = probe->ftps_pid;
+
+ mtx_lock(&pidlock);
+ /*
+ * Report an error if the process doesn't exist
+ * or is actively being birthed.
+ */
+ if ((p = prfind(pid)) == NULL || p->p_stat == SIDL) {
+ mtx_unlock(&pidlock);
+ return (ESRCH);
+ }
+ mtx_lock(&p->p_lock);
+ mtx_unlock(&pidlock);
+
+ if ((ret = priv_proc_cred_perm(cr, p, NULL,
+ VREAD | VWRITE)) != 0) {
+ mtx_unlock(&p->p_lock);
+ return (ret);
+ }
+
+ mtx_unlock(&p->p_lock);
+ }
+
+ ret = fasttrap_add_probe(probe);
+err:
+ kmem_free(probe, size);
+
+ return (ret);
+
+ } else if (cmd == FASTTRAPIOC_GETINSTR) {
+ fasttrap_instr_query_t instr;
+ fasttrap_tracepoint_t *tp;
+ uint_t index;
+ int ret;
+
+ if (copyin((void *)arg, &instr, sizeof (instr)) != 0)
+ return (EFAULT);
+
+ if (!PRIV_POLICY_CHOICE(cr, PRIV_ALL, B_FALSE)) {
+ proc_t *p;
+ pid_t pid = instr.ftiq_pid;
+
+ mtx_lock(&pidlock);
+ /*
+ * Report an error if the process doesn't exist
+ * or is actively being birthed.
+ */
+ if ((p = prfind(pid)) == NULL || p->p_stat == SIDL) {
+ mtx_unlock(&pidlock);
+ return (ESRCH);
+ }
+ mtx_lock(&p->p_lock);
+ mtx_unlock(&pidlock);
+
+ if ((ret = priv_proc_cred_perm(cr, p, NULL,
+ VREAD)) != 0) {
+ mtx_unlock(&p->p_lock);
+ return (ret);
+ }
+
+ mtx_unlock(&p->p_lock);
+ }
+
+ index = FASTTRAP_TPOINTS_INDEX(instr.ftiq_pid, instr.ftiq_pc);
+
+ mtx_lock(&fasttrap_tpoints.fth_table[index].ftb_mtx);
+ tp = fasttrap_tpoints.fth_table[index].ftb_data;
+ while (tp != NULL) {
+ if (instr.ftiq_pid == tp->ftt_pid &&
+ instr.ftiq_pc == tp->ftt_pc &&
+ tp->ftt_proc->ftpc_acount != 0)
+ break;
+
+ tp = tp->ftt_next;
+ }
+
+ if (tp == NULL) {
+ mtx_unlock(&fasttrap_tpoints.fth_table[index].ftb_mtx);
+ return (ENOENT);
+ }
+
+ bcopy(&tp->ftt_instr, &instr.ftiq_instr,
+ sizeof (instr.ftiq_instr));
+ mtx_unlock(&fasttrap_tpoints.fth_table[index].ftb_mtx);
+
+ if (copyout(&instr, (void *)arg, sizeof (instr)) != 0)
+ return (EFAULT);
+
+ return (0);
+ }
+#endif
+
+ return (EINVAL);
+}
+
+SYSINIT(fasttrap_load, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, fasttrap_load, NULL);
+SYSUNINIT(fasttrap_unload, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, fasttrap_unload, NULL);
+
+DEV_MODULE(fasttrap, fasttrap_modevent, NULL);
+MODULE_VERSION(fasttrap, 1);
+MODULE_DEPEND(fasttrap, dtrace, 1, 1, 1);
+MODULE_DEPEND(fasttrap, opensolaris, 1, 1, 1);
diff --git a/sys/cddl/dev/fbt/fbt.c b/sys/cddl/dev/fbt/fbt.c
new file mode 100644
index 000000000000..f3ddc83e1140
--- /dev/null
+++ b/sys/cddl/dev/fbt/fbt.c
@@ -0,0 +1,1411 @@
+/*
+ * 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
+ *
+ * $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/stdarg.h>
+
+#include <sys/dtrace.h>
+#include <sys/dtrace_bsd.h>
+
+MALLOC_DEFINE(M_FBT, "fbt", "Function Boundary Tracing");
+
+#define FBT_PUSHL_EBP 0x55
+#define FBT_MOVL_ESP_EBP0_V0 0x8b
+#define FBT_MOVL_ESP_EBP1_V0 0xec
+#define FBT_MOVL_ESP_EBP0_V1 0x89
+#define FBT_MOVL_ESP_EBP1_V1 0xe5
+#define FBT_REX_RSP_RBP 0x48
+
+#define FBT_POPL_EBP 0x5d
+#define FBT_RET 0xc3
+#define FBT_RET_IMM16 0xc2
+#define FBT_LEAVE 0xc9
+
+#ifdef __amd64__
+#define FBT_PATCHVAL 0xcc
+#else
+#define FBT_PATCHVAL 0xf0
+#endif
+
+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;
+ uint8_t *fbtp_patchpoint;
+ int8_t fbtp_rval;
+ uint8_t fbtp_patchval;
+ uint8_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 void
+fbt_doubletrap(void)
+{
+ fbt_probe_t *fbt;
+ int i;
+
+ for (i = 0; i < fbt_probetab_size; i++) {
+ fbt = fbt_probetab[i];
+
+ for (; fbt != NULL; fbt = fbt->fbtp_next)
+ *fbt->fbtp_patchpoint = fbt->fbtp_savedval;
+ }
+}
+
+static int
+fbt_invop(uintptr_t addr, uintptr_t *stack, uintptr_t rval)
+{
+ solaris_cpu_t *cpu = &solaris_cpu[curcpu];
+ uintptr_t stack0, stack1, stack2, stack3, stack4;
+ 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++;
+ if (fbt->fbtp_roffset == 0) {
+ int i = 0;
+ /*
+ * When accessing the arguments on the stack,
+ * we must protect against accessing beyond
+ * the stack. We can safely set NOFAULT here
+ * -- we know that interrupts are already
+ * disabled.
+ */
+ DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT);
+ cpu->cpu_dtrace_caller = stack[i++];
+ stack0 = stack[i++];
+ stack1 = stack[i++];
+ stack2 = stack[i++];
+ stack3 = stack[i++];
+ stack4 = stack[i++];
+ DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT |
+ CPU_DTRACE_BADADDR);
+
+ dtrace_probe(fbt->fbtp_id, stack0, stack1,
+ stack2, stack3, stack4);
+
+ cpu->cpu_dtrace_caller = 0;
+ } else {
+#ifdef __amd64__
+ /*
+ * On amd64, we instrument the ret, not the
+ * leave. We therefore need to set the caller
+ * to assure that the top frame of a stack()
+ * action is correct.
+ */
+ DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT);
+ cpu->cpu_dtrace_caller = stack[0];
+ DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT |
+ CPU_DTRACE_BADADDR);
+#endif
+
+ dtrace_probe(fbt->fbtp_id, fbt->fbtp_roffset,
+ rval, 0, 0, 0);
+ 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 j;
+ int size;
+ u_int8_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);
+
+ size = symval->size;
+
+ instr = (u_int8_t *) symval->value;
+ limit = (u_int8_t *) symval->value + symval->size;
+
+#ifdef __amd64__
+ while (instr < limit) {
+ if (*instr == FBT_PUSHL_EBP)
+ break;
+
+ if ((size = dtrace_instr_size(instr)) <= 0)
+ break;
+
+ instr += size;
+ }
+
+ if (instr >= limit || *instr != FBT_PUSHL_EBP) {
+ /*
+ * We either don't save the frame pointer in this
+ * function, or we ran into some disassembly
+ * screw-up. Either way, we bail.
+ */
+ return (0);
+ }
+#else
+ if (instr[0] != FBT_PUSHL_EBP)
+ return (0);
+
+ if (!(instr[1] == FBT_MOVL_ESP_EBP0_V0 &&
+ instr[2] == FBT_MOVL_ESP_EBP1_V0) &&
+ !(instr[1] == FBT_MOVL_ESP_EBP0_V1 &&
+ instr[2] == FBT_MOVL_ESP_EBP1_V1))
+ return (0);
+#endif
+
+ 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_rval = DTRACE_INVOP_PUSHL_EBP;
+ fbt->fbtp_savedval = *instr;
+ fbt->fbtp_patchval = FBT_PATCHVAL;
+ fbt->fbtp_symindx = symindx;
+
+ fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)];
+ fbt_probetab[FBT_ADDR2NDX(instr)] = fbt;
+
+ lf->fbt_nentries++;
+
+ retfbt = NULL;
+again:
+ if (instr >= limit)
+ return (0);
+
+ /*
+ * If this disassembly fails, then we've likely walked off into
+ * a jump table or some other unsuitable area. Bail out of the
+ * disassembly now.
+ */
+ if ((size = dtrace_instr_size(instr)) <= 0)
+ return (0);
+
+#ifdef __amd64__
+ /*
+ * We only instrument "ret" on amd64 -- we don't yet instrument
+ * ret imm16, largely because the compiler doesn't seem to
+ * (yet) emit them in the kernel...
+ */
+ if (*instr != FBT_RET) {
+ instr += size;
+ goto again;
+ }
+#else
+ if (!(size == 1 &&
+ (*instr == FBT_POPL_EBP || *instr == FBT_LEAVE) &&
+ (*(instr + 1) == FBT_RET ||
+ *(instr + 1) == FBT_RET_IMM16))) {
+ instr += size;
+ goto again;
+ }
+#endif
+
+ /*
+ * We (desperately) want to avoid erroneously instrumenting a
+ * jump table, especially given that our markers are pretty
+ * short: two bytes on x86, and just one byte on amd64. To
+ * determine if we're looking at a true instruction sequence
+ * or an inline jump table that happens to contain the same
+ * byte sequences, we resort to some heuristic sleeze: we
+ * treat this instruction as being contained within a pointer,
+ * and see if that pointer points to within the body of the
+ * function. If it does, we refuse to instrument it.
+ */
+ for (j = 0; j < sizeof (uintptr_t); j++) {
+ caddr_t check = (caddr_t) instr - j;
+ uint8_t *ptr;
+
+ if (check < symval->value)
+ break;
+
+ if (check + sizeof (caddr_t) > (caddr_t)limit)
+ continue;
+
+ ptr = *(uint8_t **)check;
+
+ if (ptr >= (uint8_t *) symval->value && ptr < limit) {
+ instr += size;
+ goto again;
+ }
+ }
+
+ /*
+ * 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, 3, 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;
+
+#ifndef __amd64__
+ if (*instr == FBT_POPL_EBP) {
+ fbt->fbtp_rval = DTRACE_INVOP_POPL_EBP;
+ } else {
+ ASSERT(*instr == FBT_LEAVE);
+ fbt->fbtp_rval = DTRACE_INVOP_LEAVE;
+ }
+ fbt->fbtp_roffset =
+ (uintptr_t)(instr - (uint8_t *) symval->value) + 1;
+
+#else
+ ASSERT(*instr == FBT_RET);
+ fbt->fbtp_rval = DTRACE_INVOP_RET;
+ fbt->fbtp_roffset =
+ (uintptr_t)(instr - (uint8_t *) symval->value);
+#endif
+
+ 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 += size;
+ 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;
+ }
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+static int
+fbt_ctfoff_init(modctl_t *lf, linker_ctf_t *lc)
+{
+ const Elf_Sym *symp = lc->symtab;;
+ const char *name;
+ 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;
+ }
+
+ if (symp->st_name < lc->strcnt)
+ name = lc->strtab + symp->st_name;
+ else
+ name = "(?)";
+
+ 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]++;
+ }
+
+ *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;
+
+ 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;
+ }
+
+ /* 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 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_doubletrap_func = fbt_doubletrap;
+ dtrace_invop_add(fbt_invop);
+
+ if (dtrace_register("fbt", &fbt_attr, DTRACE_PRIV_USER,
+ NULL, &fbt_pops, NULL, &fbt_id) != 0)
+ return;
+}
+
+
+static int
+fbt_unload()
+{
+ int error = 0;
+
+ /* De-register the invalid opcode handler. */
+ dtrace_invop_remove(fbt_invop);
+
+ dtrace_doubletrap_func = NULL;
+
+ /* 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/lockstat/lockstat.c b/sys/cddl/dev/lockstat/lockstat.c
new file mode 100644
index 000000000000..5a0bd63bec9e
--- /dev/null
+++ b/sys/cddl/dev/lockstat/lockstat.c
@@ -0,0 +1,289 @@
+/*
+ * 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 (c) 2008 Stacey Son <stacey@son.org>
+ *
+ * $FreeBSD$
+ *
+ */
+
+/*
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#define KDTRACE_HOOKS
+
+#include <sys/cdefs.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/limits.h>
+#include <sys/lock.h>
+#include <sys/linker.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+
+#include <sys/dtrace.h>
+#include <sys/lockstat.h>
+
+#if defined(__i386__) || defined(__amd64__)
+#define LOCKSTAT_AFRAMES 1
+#else
+#error "architecture not supported"
+#endif
+
+static d_open_t lockstat_open;
+static void lockstat_provide(void *, dtrace_probedesc_t *);
+static void lockstat_destroy(void *, dtrace_id_t, void *);
+static void lockstat_enable(void *, dtrace_id_t, void *);
+static void lockstat_disable(void *, dtrace_id_t, void *);
+static void lockstat_load(void *);
+static int lockstat_unload(void);
+
+
+typedef struct lockstat_probe {
+ char *lsp_func;
+ char *lsp_name;
+ int lsp_probe;
+ dtrace_id_t lsp_id;
+} lockstat_probe_t;
+
+#ifdef __FreeBSD__
+lockstat_probe_t lockstat_probes[] =
+{
+ /* Spin Locks */
+ { LS_MTX_SPIN_LOCK, LSS_ACQUIRE, LS_MTX_SPIN_LOCK_ACQUIRE, DTRACE_IDNONE },
+ { LS_MTX_SPIN_LOCK, LSS_SPIN, LS_MTX_SPIN_LOCK_SPIN, DTRACE_IDNONE },
+ { LS_MTX_SPIN_UNLOCK, LSS_RELEASE, LS_MTX_SPIN_UNLOCK_RELEASE, DTRACE_IDNONE },
+ /* Adaptive Locks */
+ { LS_MTX_LOCK, LSA_ACQUIRE, LS_MTX_LOCK_ACQUIRE, DTRACE_IDNONE },
+ { LS_MTX_LOCK, LSA_BLOCK, LS_MTX_LOCK_BLOCK, DTRACE_IDNONE },
+ { LS_MTX_LOCK, LSA_SPIN, LS_MTX_LOCK_SPIN, DTRACE_IDNONE },
+ { LS_MTX_UNLOCK, LSA_RELEASE, LS_MTX_UNLOCK_RELEASE, DTRACE_IDNONE },
+ { LS_MTX_TRYLOCK, LSA_ACQUIRE, LS_MTX_TRYLOCK_ACQUIRE, DTRACE_IDNONE },
+ /* Reader/Writer Locks */
+ { LS_RW_RLOCK, LSR_ACQUIRE, LS_RW_RLOCK_ACQUIRE, DTRACE_IDNONE },
+ { LS_RW_RLOCK, LSR_BLOCK, LS_RW_RLOCK_BLOCK, DTRACE_IDNONE },
+ { LS_RW_RLOCK, LSR_SPIN, LS_RW_RLOCK_SPIN, DTRACE_IDNONE },
+ { LS_RW_RUNLOCK, LSR_RELEASE, LS_RW_RUNLOCK_RELEASE, DTRACE_IDNONE },
+ { LS_RW_WLOCK, LSR_ACQUIRE, LS_RW_WLOCK_ACQUIRE, DTRACE_IDNONE },
+ { LS_RW_WLOCK, LSR_BLOCK, LS_RW_WLOCK_BLOCK, DTRACE_IDNONE },
+ { LS_RW_WLOCK, LSR_SPIN, LS_RW_WLOCK_SPIN, DTRACE_IDNONE },
+ { LS_RW_WUNLOCK, LSR_RELEASE, LS_RW_WUNLOCK_RELEASE, DTRACE_IDNONE },
+ { LS_RW_TRYUPGRADE, LSR_UPGRADE, LS_RW_TRYUPGRADE_UPGRADE, DTRACE_IDNONE },
+ { LS_RW_DOWNGRADE, LSR_DOWNGRADE, LS_RW_DOWNGRADE_DOWNGRADE, DTRACE_IDNONE },
+ /* Shared/Exclusive Locks */
+ { LS_SX_SLOCK, LSX_ACQUIRE, LS_SX_SLOCK_ACQUIRE, DTRACE_IDNONE },
+ { LS_SX_SLOCK, LSX_BLOCK, LS_SX_SLOCK_BLOCK, DTRACE_IDNONE },
+ { LS_SX_SLOCK, LSX_SPIN, LS_SX_SLOCK_SPIN, DTRACE_IDNONE },
+ { LS_SX_SUNLOCK, LSX_RELEASE, LS_SX_SUNLOCK_RELEASE, DTRACE_IDNONE },
+ { LS_SX_XLOCK, LSX_ACQUIRE, LS_SX_XLOCK_ACQUIRE, DTRACE_IDNONE },
+ { LS_SX_XLOCK, LSX_BLOCK, LS_SX_XLOCK_BLOCK, DTRACE_IDNONE },
+ { LS_SX_XLOCK, LSX_SPIN, LS_SX_XLOCK_SPIN, DTRACE_IDNONE },
+ { LS_SX_XUNLOCK, LSX_RELEASE, LS_SX_XUNLOCK_RELEASE, DTRACE_IDNONE },
+ { LS_SX_TRYUPGRADE, LSX_UPGRADE, LS_SX_TRYUPGRADE_UPGRADE, DTRACE_IDNONE },
+ { LS_SX_DOWNGRADE, LSX_DOWNGRADE, LS_SX_DOWNGRADE_DOWNGRADE, DTRACE_IDNONE },
+ /* Thread Locks */
+ { LS_THREAD_LOCK, LST_SPIN, LS_THREAD_LOCK_SPIN, DTRACE_IDNONE },
+ { NULL }
+};
+#else
+#error "OS not supported"
+#endif
+
+
+static struct cdevsw lockstat_cdevsw = {
+ .d_version = D_VERSION,
+ .d_open = lockstat_open,
+ .d_name = "lockstat",
+};
+
+static struct cdev *lockstat_cdev;
+static dtrace_provider_id_t lockstat_id;
+
+/*ARGSUSED*/
+static void
+lockstat_enable(void *arg, dtrace_id_t id, void *parg)
+{
+ lockstat_probe_t *probe = parg;
+
+ ASSERT(!lockstat_probemap[probe->lsp_probe]);
+
+ lockstat_probemap[probe->lsp_probe] = id;
+#ifdef DOODAD
+ membar_producer();
+#endif
+
+ lockstat_probe_func = dtrace_probe;
+#ifdef DOODAD
+ membar_producer();
+
+ lockstat_hot_patch();
+ membar_producer();
+#endif
+}
+
+/*ARGSUSED*/
+static void
+lockstat_disable(void *arg, dtrace_id_t id, void *parg)
+{
+ lockstat_probe_t *probe = parg;
+ int i;
+
+ ASSERT(lockstat_probemap[probe->lsp_probe]);
+
+ lockstat_probemap[probe->lsp_probe] = 0;
+#ifdef DOODAD
+ lockstat_hot_patch();
+ membar_producer();
+#endif
+
+ /*
+ * See if we have any probes left enabled.
+ */
+ for (i = 0; i < LS_NPROBES; i++) {
+ if (lockstat_probemap[i]) {
+ /*
+ * This probe is still enabled. We don't need to deal
+ * with waiting for all threads to be out of the
+ * lockstat critical sections; just return.
+ */
+ return;
+ }
+ }
+
+}
+
+/*ARGSUSED*/
+static int
+lockstat_open(struct cdev *dev __unused, int oflags __unused,
+ int devtype __unused, struct thread *td __unused)
+{
+ return (0);
+}
+
+/*ARGSUSED*/
+static void
+lockstat_provide(void *arg, dtrace_probedesc_t *desc)
+{
+ int i = 0;
+
+ for (i = 0; lockstat_probes[i].lsp_func != NULL; i++) {
+ lockstat_probe_t *probe = &lockstat_probes[i];
+
+ if (dtrace_probe_lookup(lockstat_id, "kernel",
+ probe->lsp_func, probe->lsp_name) != 0)
+ continue;
+
+ ASSERT(!probe->lsp_id);
+ probe->lsp_id = dtrace_probe_create(lockstat_id,
+ "kernel", probe->lsp_func, probe->lsp_name,
+ LOCKSTAT_AFRAMES, probe);
+ }
+}
+
+/*ARGSUSED*/
+static void
+lockstat_destroy(void *arg, dtrace_id_t id, void *parg)
+{
+ lockstat_probe_t *probe = parg;
+
+ ASSERT(!lockstat_probemap[probe->lsp_probe]);
+ probe->lsp_id = 0;
+}
+
+static dtrace_pattr_t lockstat_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_UNKNOWN },
+{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
+{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
+};
+
+static dtrace_pops_t lockstat_pops = {
+ lockstat_provide,
+ NULL,
+ lockstat_enable,
+ lockstat_disable,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ lockstat_destroy
+};
+
+static void
+lockstat_load(void *dummy)
+{
+ /* Create the /dev/dtrace/lockstat entry. */
+ lockstat_cdev = make_dev(&lockstat_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600,
+ "dtrace/lockstat");
+
+ if (dtrace_register("lockstat", &lockstat_attr, DTRACE_PRIV_USER,
+ NULL, &lockstat_pops, NULL, &lockstat_id) != 0)
+ return;
+}
+
+static int
+lockstat_unload()
+{
+ int error = 0;
+
+ if ((error = dtrace_unregister(lockstat_id)) != 0)
+ return (error);
+
+ destroy_dev(lockstat_cdev);
+
+ return (error);
+}
+
+/* ARGSUSED */
+static int
+lockstat_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);
+}
+
+SYSINIT(lockstat_load, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, lockstat_load, NULL);
+SYSUNINIT(lockstat_unload, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, lockstat_unload, NULL);
+
+DEV_MODULE(lockstat, lockstat_modevent, NULL);
+MODULE_VERSION(lockstat, 1);
+MODULE_DEPEND(lockstat, dtrace, 1, 1, 1);
+MODULE_DEPEND(lockstat, opensolaris, 1, 1, 1);
diff --git a/sys/cddl/dev/profile/profile.c b/sys/cddl/dev/profile/profile.c
new file mode 100644
index 000000000000..ea52e2ea3623
--- /dev/null
+++ b/sys/cddl/dev/profile/profile.c
@@ -0,0 +1,531 @@
+/*
+ * 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
+ *
+ * $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/poll.h>
+#include <sys/proc.h>
+#include <sys/selinfo.h>
+#include <sys/smp.h>
+#include <sys/uio.h>
+#include <sys/unistd.h>
+#include <machine/stdarg.h>
+
+#include <sys/cyclic.h>
+#include <sys/dtrace.h>
+#include <sys/dtrace_bsd.h>
+
+#define PROF_NAMELEN 15
+
+#define PROF_PROFILE 0
+#define PROF_TICK 1
+#define PROF_PREFIX_PROFILE "profile-"
+#define PROF_PREFIX_TICK "tick-"
+
+/*
+ * Regardless of platform, there are five artificial frames in the case of the
+ * profile provider:
+ *
+ * profile_fire
+ * cyclic_expire
+ * cyclic_fire
+ * [ cbe ]
+ * [ locore ]
+ *
+ * On amd64, there are two frames associated with locore: one in locore, and
+ * another in common interrupt dispatch code. (i386 has not been modified to
+ * use this common layer.) Further, on i386, the interrupted instruction
+ * appears as its own stack frame. All of this means that we need to add one
+ * frame for amd64, and then take one away for both amd64 and i386.
+ *
+ * On SPARC, the picture is further complicated because the compiler
+ * optimizes away tail-calls -- so the following frames are optimized away:
+ *
+ * profile_fire
+ * cyclic_expire
+ *
+ * This gives three frames. However, on DEBUG kernels, the cyclic_expire
+ * frame cannot be tail-call eliminated, yielding four frames in this case.
+ *
+ * All of the above constraints lead to the mess below. Yes, the profile
+ * provider should ideally figure this out on-the-fly by hiting one of its own
+ * probes and then walking its own stack trace. This is complicated, however,
+ * and the static definition doesn't seem to be overly brittle. Still, we
+ * allow for a manual override in case we get it completely wrong.
+ */
+#ifdef __amd64
+#define PROF_ARTIFICIAL_FRAMES 7
+#else
+#ifdef __i386
+#define PROF_ARTIFICIAL_FRAMES 6
+#else
+#ifdef __sparc
+#ifdef DEBUG
+#define PROF_ARTIFICIAL_FRAMES 4
+#else
+#define PROF_ARTIFICIAL_FRAMES 3
+#endif
+#endif
+#endif
+#endif
+
+typedef struct profile_probe {
+ char prof_name[PROF_NAMELEN];
+ dtrace_id_t prof_id;
+ int prof_kind;
+ hrtime_t prof_interval;
+ cyclic_id_t prof_cyclic;
+} profile_probe_t;
+
+typedef struct profile_probe_percpu {
+ hrtime_t profc_expected;
+ hrtime_t profc_interval;
+ profile_probe_t *profc_probe;
+} profile_probe_percpu_t;
+
+static d_open_t profile_open;
+static int profile_unload(void);
+static void profile_create(hrtime_t, char *, int);
+static void profile_destroy(void *, dtrace_id_t, void *);
+static void profile_enable(void *, dtrace_id_t, void *);
+static void profile_disable(void *, dtrace_id_t, void *);
+static void profile_load(void *);
+static void profile_provide(void *, dtrace_probedesc_t *);
+
+static int profile_rates[] = {
+ 97, 199, 499, 997, 1999,
+ 4001, 4999, 0, 0, 0,
+ 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0
+};
+
+static int profile_ticks[] = {
+ 1, 10, 100, 500, 1000,
+ 5000, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0
+};
+
+/*
+ * profile_max defines the upper bound on the number of profile probes that
+ * can exist (this is to prevent malicious or clumsy users from exhausing
+ * system resources by creating a slew of profile probes). At mod load time,
+ * this gets its value from PROFILE_MAX_DEFAULT or profile-max-probes if it's
+ * present in the profile.conf file.
+ */
+#define PROFILE_MAX_DEFAULT 1000 /* default max. number of probes */
+static uint32_t profile_max = PROFILE_MAX_DEFAULT;
+ /* maximum number of profile probes */
+static uint32_t profile_total; /* current number of profile probes */
+
+static struct cdevsw profile_cdevsw = {
+ .d_version = D_VERSION,
+ .d_open = profile_open,
+ .d_name = "profile",
+};
+
+static dtrace_pattr_t profile_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 profile_pops = {
+ profile_provide,
+ NULL,
+ profile_enable,
+ profile_disable,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ profile_destroy
+};
+
+static struct cdev *profile_cdev;
+static dtrace_provider_id_t profile_id;
+static hrtime_t profile_interval_min = NANOSEC / 5000; /* 5000 hz */
+static int profile_aframes = 0; /* override */
+
+static void
+profile_fire(void *arg)
+{
+ profile_probe_percpu_t *pcpu = arg;
+ profile_probe_t *prof = pcpu->profc_probe;
+ hrtime_t late;
+ solaris_cpu_t *c = &solaris_cpu[curcpu];
+
+ late = gethrtime() - pcpu->profc_expected;
+ pcpu->profc_expected += pcpu->profc_interval;
+
+ dtrace_probe(prof->prof_id, c->cpu_profile_pc,
+ c->cpu_profile_upc, late, 0, 0);
+}
+
+static void
+profile_tick(void *arg)
+{
+ profile_probe_t *prof = arg;
+ solaris_cpu_t *c = &solaris_cpu[curcpu];
+
+ dtrace_probe(prof->prof_id, c->cpu_profile_pc,
+ c->cpu_profile_upc, 0, 0, 0);
+}
+
+static void
+profile_create(hrtime_t interval, char *name, int kind)
+{
+ profile_probe_t *prof;
+
+ if (interval < profile_interval_min)
+ return;
+
+ if (dtrace_probe_lookup(profile_id, NULL, NULL, name) != 0)
+ return;
+
+ atomic_add_32(&profile_total, 1);
+ if (profile_total > profile_max) {
+ atomic_add_32(&profile_total, -1);
+ return;
+ }
+
+ prof = kmem_zalloc(sizeof (profile_probe_t), KM_SLEEP);
+ (void) strcpy(prof->prof_name, name);
+ prof->prof_interval = interval;
+ prof->prof_cyclic = CYCLIC_NONE;
+ prof->prof_kind = kind;
+ prof->prof_id = dtrace_probe_create(profile_id,
+ NULL, NULL, name,
+ profile_aframes ? profile_aframes : PROF_ARTIFICIAL_FRAMES, prof);
+}
+
+/*ARGSUSED*/
+static void
+profile_provide(void *arg, dtrace_probedesc_t *desc)
+{
+ int i, j, rate, kind;
+ hrtime_t val = 0, mult = 1, len = 0;
+ char *name, *suffix = NULL;
+
+ const struct {
+ char *prefix;
+ int kind;
+ } types[] = {
+ { PROF_PREFIX_PROFILE, PROF_PROFILE },
+ { PROF_PREFIX_TICK, PROF_TICK },
+ { 0, 0 }
+ };
+
+ const struct {
+ char *name;
+ hrtime_t mult;
+ } suffixes[] = {
+ { "ns", NANOSEC / NANOSEC },
+ { "nsec", NANOSEC / NANOSEC },
+ { "us", NANOSEC / MICROSEC },
+ { "usec", NANOSEC / MICROSEC },
+ { "ms", NANOSEC / MILLISEC },
+ { "msec", NANOSEC / MILLISEC },
+ { "s", NANOSEC / SEC },
+ { "sec", NANOSEC / SEC },
+ { "m", NANOSEC * (hrtime_t)60 },
+ { "min", NANOSEC * (hrtime_t)60 },
+ { "h", NANOSEC * (hrtime_t)(60 * 60) },
+ { "hour", NANOSEC * (hrtime_t)(60 * 60) },
+ { "d", NANOSEC * (hrtime_t)(24 * 60 * 60) },
+ { "day", NANOSEC * (hrtime_t)(24 * 60 * 60) },
+ { "hz", 0 },
+ { NULL }
+ };
+
+ if (desc == NULL) {
+ char n[PROF_NAMELEN];
+
+ /*
+ * If no description was provided, provide all of our probes.
+ */
+ for (i = 0; i < sizeof (profile_rates) / sizeof (int); i++) {
+ if ((rate = profile_rates[i]) == 0)
+ continue;
+
+ (void) snprintf(n, PROF_NAMELEN, "%s%d",
+ PROF_PREFIX_PROFILE, rate);
+ profile_create(NANOSEC / rate, n, PROF_PROFILE);
+ }
+
+ for (i = 0; i < sizeof (profile_ticks) / sizeof (int); i++) {
+ if ((rate = profile_ticks[i]) == 0)
+ continue;
+
+ (void) snprintf(n, PROF_NAMELEN, "%s%d",
+ PROF_PREFIX_TICK, rate);
+ profile_create(NANOSEC / rate, n, PROF_TICK);
+ }
+
+ return;
+ }
+
+ name = desc->dtpd_name;
+
+ for (i = 0; types[i].prefix != NULL; i++) {
+ len = strlen(types[i].prefix);
+
+ if (strncmp(name, types[i].prefix, len) != 0)
+ continue;
+ break;
+ }
+
+ if (types[i].prefix == NULL)
+ return;
+
+ kind = types[i].kind;
+ j = strlen(name) - len;
+
+ /*
+ * We need to start before any time suffix.
+ */
+ for (j = strlen(name); j >= len; j--) {
+ if (name[j] >= '0' && name[j] <= '9')
+ break;
+ suffix = &name[j];
+ }
+
+ ASSERT(suffix != NULL);
+
+ /*
+ * Now determine the numerical value present in the probe name.
+ */
+ for (; j >= len; j--) {
+ if (name[j] < '0' || name[j] > '9')
+ return;
+
+ val += (name[j] - '0') * mult;
+ mult *= (hrtime_t)10;
+ }
+
+ if (val == 0)
+ return;
+
+ /*
+ * Look-up the suffix to determine the multiplier.
+ */
+ for (i = 0, mult = 0; suffixes[i].name != NULL; i++) {
+ if (strcasecmp(suffixes[i].name, suffix) == 0) {
+ mult = suffixes[i].mult;
+ break;
+ }
+ }
+
+ if (suffixes[i].name == NULL && *suffix != '\0')
+ return;
+
+ if (mult == 0) {
+ /*
+ * The default is frequency-per-second.
+ */
+ val = NANOSEC / val;
+ } else {
+ val *= mult;
+ }
+
+ profile_create(val, name, kind);
+}
+
+/* ARGSUSED */
+static void
+profile_destroy(void *arg, dtrace_id_t id, void *parg)
+{
+ profile_probe_t *prof = parg;
+
+ ASSERT(prof->prof_cyclic == CYCLIC_NONE);
+ kmem_free(prof, sizeof (profile_probe_t));
+
+ ASSERT(profile_total >= 1);
+ atomic_add_32(&profile_total, -1);
+}
+
+/*ARGSUSED*/
+static void
+profile_online(void *arg, cpu_t *cpu, cyc_handler_t *hdlr, cyc_time_t *when)
+{
+ profile_probe_t *prof = arg;
+ profile_probe_percpu_t *pcpu;
+
+ pcpu = kmem_zalloc(sizeof (profile_probe_percpu_t), KM_SLEEP);
+ pcpu->profc_probe = prof;
+
+ hdlr->cyh_func = profile_fire;
+ hdlr->cyh_arg = pcpu;
+
+ when->cyt_interval = prof->prof_interval;
+ when->cyt_when = gethrtime() + when->cyt_interval;
+
+ pcpu->profc_expected = when->cyt_when;
+ pcpu->profc_interval = when->cyt_interval;
+}
+
+/*ARGSUSED*/
+static void
+profile_offline(void *arg, cpu_t *cpu, void *oarg)
+{
+ profile_probe_percpu_t *pcpu = oarg;
+
+ ASSERT(pcpu->profc_probe == arg);
+ kmem_free(pcpu, sizeof (profile_probe_percpu_t));
+}
+
+/* ARGSUSED */
+static void
+profile_enable(void *arg, dtrace_id_t id, void *parg)
+{
+ profile_probe_t *prof = parg;
+ cyc_omni_handler_t omni;
+ cyc_handler_t hdlr;
+ cyc_time_t when;
+
+ ASSERT(prof->prof_interval != 0);
+ ASSERT(MUTEX_HELD(&cpu_lock));
+
+ if (prof->prof_kind == PROF_TICK) {
+ hdlr.cyh_func = profile_tick;
+ hdlr.cyh_arg = prof;
+
+ when.cyt_interval = prof->prof_interval;
+ when.cyt_when = gethrtime() + when.cyt_interval;
+ } else {
+ ASSERT(prof->prof_kind == PROF_PROFILE);
+ omni.cyo_online = profile_online;
+ omni.cyo_offline = profile_offline;
+ omni.cyo_arg = prof;
+ }
+
+ if (prof->prof_kind == PROF_TICK) {
+ prof->prof_cyclic = cyclic_add(&hdlr, &when);
+ } else {
+ prof->prof_cyclic = cyclic_add_omni(&omni);
+ }
+}
+
+/* ARGSUSED */
+static void
+profile_disable(void *arg, dtrace_id_t id, void *parg)
+{
+ profile_probe_t *prof = parg;
+
+ ASSERT(prof->prof_cyclic != CYCLIC_NONE);
+ ASSERT(MUTEX_HELD(&cpu_lock));
+
+ cyclic_remove(prof->prof_cyclic);
+ prof->prof_cyclic = CYCLIC_NONE;
+}
+
+static void
+profile_load(void *dummy)
+{
+ /* Create the /dev/dtrace/profile entry. */
+ profile_cdev = make_dev(&profile_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600,
+ "dtrace/profile");
+
+ if (dtrace_register("profile", &profile_attr, DTRACE_PRIV_USER,
+ NULL, &profile_pops, NULL, &profile_id) != 0)
+ return;
+}
+
+
+static int
+profile_unload()
+{
+ int error = 0;
+
+ if ((error = dtrace_unregister(profile_id)) != 0)
+ return (error);
+
+ destroy_dev(profile_cdev);
+
+ return (error);
+}
+
+/* ARGSUSED */
+static int
+profile_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);
+}
+
+/* ARGSUSED */
+static int
+profile_open(struct cdev *dev __unused, int oflags __unused, int devtype __unused, struct thread *td __unused)
+{
+ return (0);
+}
+
+SYSINIT(profile_load, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, profile_load, NULL);
+SYSUNINIT(profile_unload, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, profile_unload, NULL);
+
+DEV_MODULE(profile, profile_modevent, NULL);
+MODULE_VERSION(profile, 1);
+MODULE_DEPEND(profile, dtrace, 1, 1, 1);
+MODULE_DEPEND(profile, cyclic, 1, 1, 1);
+MODULE_DEPEND(profile, opensolaris, 1, 1, 1);
diff --git a/sys/cddl/dev/prototype.c b/sys/cddl/dev/prototype.c
new file mode 100644
index 000000000000..1f7f8b5f0c2d
--- /dev/null
+++ b/sys/cddl/dev/prototype.c
@@ -0,0 +1,144 @@
+/*
+ * This file is freeware. You are free to use it and add your own
+ * license.
+ *
+ * $FreeBSD$
+ *
+ */
+
+#include <sys/cdefs.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+
+#include <sys/dtrace.h>
+
+static d_open_t prototype_open;
+static int prototype_unload(void);
+static void prototype_getargdesc(void *, dtrace_id_t, void *, dtrace_argdesc_t *);
+static void prototype_provide(void *, dtrace_probedesc_t *);
+static void prototype_destroy(void *, dtrace_id_t, void *);
+static void prototype_enable(void *, dtrace_id_t, void *);
+static void prototype_disable(void *, dtrace_id_t, void *);
+static void prototype_load(void *);
+
+static struct cdevsw prototype_cdevsw = {
+ .d_version = D_VERSION,
+ .d_open = prototype_open,
+ .d_name = "prototype",
+};
+
+static dtrace_pattr_t prototype_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 prototype_pops = {
+ prototype_provide,
+ NULL,
+ prototype_enable,
+ prototype_disable,
+ NULL,
+ NULL,
+ prototype_getargdesc,
+ NULL,
+ NULL,
+ prototype_destroy
+};
+
+static struct cdev *prototype_cdev;
+static dtrace_provider_id_t prototype_id;
+
+static void
+prototype_getargdesc(void *arg, dtrace_id_t id, void *parg, dtrace_argdesc_t *desc)
+{
+}
+
+static void
+prototype_provide(void *arg, dtrace_probedesc_t *desc)
+{
+}
+
+static void
+prototype_destroy(void *arg, dtrace_id_t id, void *parg)
+{
+}
+
+static void
+prototype_enable(void *arg, dtrace_id_t id, void *parg)
+{
+}
+
+static void
+prototype_disable(void *arg, dtrace_id_t id, void *parg)
+{
+}
+
+static void
+prototype_load(void *dummy)
+{
+ /* Create the /dev/dtrace/prototype entry. */
+ prototype_cdev = make_dev(&prototype_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600,
+ "dtrace/prototype");
+
+ if (dtrace_register("prototype", &prototype_attr, DTRACE_PRIV_USER,
+ NULL, &prototype_pops, NULL, &prototype_id) != 0)
+ return;
+}
+
+
+static int
+prototype_unload()
+{
+ int error = 0;
+
+ if ((error = dtrace_unregister(prototype_id)) != 0)
+ return (error);
+
+ destroy_dev(prototype_cdev);
+
+ return (error);
+}
+
+static int
+prototype_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
+prototype_open(struct cdev *dev __unused, int oflags __unused, int devtype __unused, struct thread *td __unused)
+{
+ return (0);
+}
+
+SYSINIT(prototype_load, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, prototype_load, NULL);
+SYSUNINIT(prototype_unload, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, prototype_unload, NULL);
+
+DEV_MODULE(prototype, prototype_modevent, NULL);
+MODULE_VERSION(prototype, 1);
+MODULE_DEPEND(prototype, dtrace, 1, 1, 1);
+MODULE_DEPEND(prototype, opensolaris, 1, 1, 1);
diff --git a/sys/cddl/dev/sdt/sdt.c b/sys/cddl/dev/sdt/sdt.c
new file mode 100644
index 000000000000..d5d41724593f
--- /dev/null
+++ b/sys/cddl/dev/sdt/sdt.c
@@ -0,0 +1,254 @@
+/*
+ * 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
+ *
+ * $FreeBSD$
+ *
+ */
+
+#ifndef KDTRACE_HOOKS
+#define KDTRACE_HOOKS
+#endif
+
+#include <sys/cdefs.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/limits.h>
+#include <sys/lock.h>
+#include <sys/linker.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+
+#include <sys/dtrace.h>
+#include <sys/sdt.h>
+
+#define SDT_ADDR2NDX(addr) (((uintptr_t)(addr)) >> 4)
+
+static d_open_t sdt_open;
+static int sdt_unload(void);
+static void sdt_getargdesc(void *, dtrace_id_t, void *, dtrace_argdesc_t *);
+static void sdt_provide_probes(void *, dtrace_probedesc_t *);
+static void sdt_destroy(void *, dtrace_id_t, void *);
+static void sdt_enable(void *, dtrace_id_t, void *);
+static void sdt_disable(void *, dtrace_id_t, void *);
+static void sdt_load(void *);
+
+static struct cdevsw sdt_cdevsw = {
+ .d_version = D_VERSION,
+ .d_open = sdt_open,
+ .d_name = "sdt",
+};
+
+static dtrace_pattr_t sdt_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 sdt_pops = {
+ sdt_provide_probes,
+ NULL,
+ sdt_enable,
+ sdt_disable,
+ NULL,
+ NULL,
+ sdt_getargdesc,
+ NULL,
+ NULL,
+ sdt_destroy
+};
+
+static struct cdev *sdt_cdev;
+
+static int
+sdt_argtype_callback(struct sdt_argtype *argtype, void *arg)
+{
+ dtrace_argdesc_t *desc = arg;
+
+ if (desc->dtargd_ndx == argtype->ndx) {
+ desc->dtargd_mapping = desc->dtargd_ndx; /* XXX */
+ strlcpy(desc->dtargd_native, argtype->type,
+ sizeof(desc->dtargd_native));
+ desc->dtargd_xlate[0] = '\0'; /* XXX */
+ }
+
+ return (0);
+}
+
+static void
+sdt_getargdesc(void *arg, dtrace_id_t id, void *parg, dtrace_argdesc_t *desc)
+{
+ struct sdt_probe *probe = parg;
+
+ if (desc->dtargd_ndx < probe->n_args)
+ (void) (sdt_argtype_listall(probe, sdt_argtype_callback, desc));
+ else
+ desc->dtargd_ndx = DTRACE_ARGNONE;
+
+ return;
+}
+
+static int
+sdt_probe_callback(struct sdt_probe *probe, void *arg __unused)
+{
+ struct sdt_provider *prov = probe->prov;
+ char mod[64];
+ char func[64];
+ char name[64];
+
+ /*
+ * Unfortunately this is necessary because the Solaris DTrace
+ * code mixes consts and non-consts with casts to override
+ * the incompatibilies. On FreeBSD, we use strict warnings
+ * in gcc, so we have to respect const vs non-const.
+ */
+ strlcpy(mod, probe->mod, sizeof(mod));
+ strlcpy(func, probe->func, sizeof(func));
+ strlcpy(name, probe->name, sizeof(name));
+
+ if (dtrace_probe_lookup(prov->id, mod, func, name) != 0)
+ return (0);
+
+ (void) dtrace_probe_create(prov->id, probe->mod, probe->func,
+ probe->name, 0, probe);
+
+ return (0);
+}
+
+static int
+sdt_provider_entry(struct sdt_provider *prov, void *arg)
+{
+ return (sdt_probe_listall(prov, sdt_probe_callback, NULL));
+}
+
+static void
+sdt_provide_probes(void *arg, dtrace_probedesc_t *desc)
+{
+ if (desc != NULL)
+ return;
+
+ (void) sdt_provider_listall(sdt_provider_entry, NULL);
+}
+
+static void
+sdt_destroy(void *arg, dtrace_id_t id, void *parg)
+{
+ /* Nothing to do here. */
+}
+
+static void
+sdt_enable(void *arg, dtrace_id_t id, void *parg)
+{
+ struct sdt_probe *probe = parg;
+
+ probe->id = id;
+}
+
+static void
+sdt_disable(void *arg, dtrace_id_t id, void *parg)
+{
+ struct sdt_probe *probe = parg;
+
+ probe->id = 0;
+}
+
+static int
+sdt_provider_reg_callback(struct sdt_provider *prov, void *arg __unused)
+{
+ return (dtrace_register(prov->name, &sdt_attr, DTRACE_PRIV_USER,
+ NULL, &sdt_pops, NULL, (dtrace_provider_id_t *) &prov->id));
+}
+
+static void
+sdt_load(void *dummy)
+{
+ /* Create the /dev/dtrace/sdt entry. */
+ sdt_cdev = make_dev(&sdt_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600,
+ "dtrace/sdt");
+
+ sdt_probe_func = dtrace_probe;
+
+ (void) sdt_provider_listall(sdt_provider_reg_callback, NULL);
+}
+
+static int
+sdt_provider_unreg_callback(struct sdt_provider *prov, void *arg __unused)
+{
+ return (dtrace_unregister(prov->id));
+}
+
+static int
+sdt_unload()
+{
+ int error = 0;
+
+ sdt_probe_func = sdt_probe_stub;
+
+ (void) sdt_provider_listall(sdt_provider_unreg_callback, NULL);
+
+ destroy_dev(sdt_cdev);
+
+ return (error);
+}
+
+/* ARGSUSED */
+static int
+sdt_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);
+}
+
+/* ARGSUSED */
+static int
+sdt_open(struct cdev *dev __unused, int oflags __unused, int devtype __unused, struct thread *td __unused)
+{
+ return (0);
+}
+
+SYSINIT(sdt_load, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, sdt_load, NULL);
+SYSUNINIT(sdt_unload, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, sdt_unload, NULL);
+
+DEV_MODULE(sdt, sdt_modevent, NULL);
+MODULE_VERSION(sdt, 1);
+MODULE_DEPEND(sdt, dtrace, 1, 1, 1);
+MODULE_DEPEND(sdt, opensolaris, 1, 1, 1);
diff --git a/sys/cddl/dev/systrace/systrace.c b/sys/cddl/dev/systrace/systrace.c
new file mode 100644
index 000000000000..e54f776576d5
--- /dev/null
+++ b/sys/cddl/dev/systrace/systrace.c
@@ -0,0 +1,341 @@
+/*
+ * 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
+ *
+ * $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/poll.h>
+#include <sys/proc.h>
+#include <sys/selinfo.h>
+#include <sys/smp.h>
+#include <sys/sysproto.h>
+#include <sys/sysent.h>
+#include <sys/uio.h>
+#include <sys/unistd.h>
+#include <machine/stdarg.h>
+
+#include <sys/dtrace.h>
+
+#ifdef LINUX_SYSTRACE
+#include <linux.h>
+#include <linux_syscall.h>
+#include <linux_proto.h>
+#include <linux_syscallnames.c>
+#include <linux_systrace.c>
+extern struct sysent linux_sysent[];
+#define DEVNAME "dtrace/linsystrace"
+#define PROVNAME "linsyscall"
+#define MAXSYSCALL LINUX_SYS_MAXSYSCALL
+#define SYSCALLNAMES linux_syscallnames
+#define SYSENT linux_sysent
+#else
+/*
+ * The syscall arguments are processed into a DTrace argument array
+ * using a generated function. See sys/kern/makesyscalls.sh.
+ */
+#include <sys/syscall.h>
+#include <kern/systrace_args.c>
+extern const char *syscallnames[];
+#define DEVNAME "dtrace/systrace"
+#define PROVNAME "syscall"
+#define MAXSYSCALL SYS_MAXSYSCALL
+#define SYSCALLNAMES syscallnames
+#define SYSENT sysent
+#endif
+
+#define SYSTRACE_ARTIFICIAL_FRAMES 1
+
+#define SYSTRACE_SHIFT 16
+#define SYSTRACE_ISENTRY(x) ((int)(x) >> SYSTRACE_SHIFT)
+#define SYSTRACE_SYSNUM(x) ((int)(x) & ((1 << SYSTRACE_SHIFT) - 1))
+#define SYSTRACE_ENTRY(id) ((1 << SYSTRACE_SHIFT) | (id))
+#define SYSTRACE_RETURN(id) (id)
+
+#if ((1 << SYSTRACE_SHIFT) <= MAXSYSCALL)
+#error 1 << SYSTRACE_SHIFT must exceed number of system calls
+#endif
+
+static d_open_t systrace_open;
+static int systrace_unload(void);
+static void systrace_getargdesc(void *, dtrace_id_t, void *, dtrace_argdesc_t *);
+static void systrace_provide(void *, dtrace_probedesc_t *);
+static void systrace_destroy(void *, dtrace_id_t, void *);
+static void systrace_enable(void *, dtrace_id_t, void *);
+static void systrace_disable(void *, dtrace_id_t, void *);
+static void systrace_load(void *);
+
+static struct cdevsw systrace_cdevsw = {
+ .d_version = D_VERSION,
+ .d_open = systrace_open,
+#ifdef LINUX_SYSTRACE
+ .d_name = "linsystrace",
+#else
+ .d_name = "systrace",
+#endif
+};
+
+static union {
+ const char **p_constnames;
+ char **pp_syscallnames;
+} uglyhack = { SYSCALLNAMES };
+
+static dtrace_pattr_t systrace_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 systrace_pops = {
+ systrace_provide,
+ NULL,
+ systrace_enable,
+ systrace_disable,
+ NULL,
+ NULL,
+ systrace_getargdesc,
+ NULL,
+ NULL,
+ systrace_destroy
+};
+
+static struct cdev *systrace_cdev;
+static dtrace_provider_id_t systrace_id;
+
+#if !defined(LINUX_SYSTRACE)
+/*
+ * Probe callback function.
+ *
+ * Note: This function is called for _all_ syscalls, regardless of which sysent
+ * array the syscall comes from. It could be a standard syscall or a
+ * compat syscall from something like Linux.
+ */
+static void
+systrace_probe(u_int32_t id, int sysnum, struct sysent *sysent, void *params)
+{
+ int n_args = 0;
+ u_int64_t uargs[8];
+
+ /*
+ * Check if this syscall has an argument conversion function
+ * registered.
+ */
+ if (sysent->sy_systrace_args_func != NULL)
+ /*
+ * Convert the syscall parameters using the registered
+ * function.
+ */
+ (*sysent->sy_systrace_args_func)(sysnum, params, uargs, &n_args);
+ else
+ /*
+ * Use the built-in system call argument conversion
+ * function to translate the syscall structure fields
+ * into the array of 64-bit values that DTrace
+ * expects.
+ */
+ systrace_args(sysnum, params, uargs, &n_args);
+
+ /* Process the probe using the converted argments. */
+ dtrace_probe(id, uargs[0], uargs[1], uargs[2], uargs[3], uargs[4]);
+}
+#endif
+
+static void
+systrace_getargdesc(void *arg, dtrace_id_t id, void *parg, dtrace_argdesc_t *desc)
+{
+ int sysnum = SYSTRACE_SYSNUM((uintptr_t)parg);
+
+ systrace_setargdesc(sysnum, desc->dtargd_ndx, desc->dtargd_native,
+ sizeof(desc->dtargd_native));
+
+ if (desc->dtargd_native[0] == '\0')
+ desc->dtargd_ndx = DTRACE_ARGNONE;
+
+ return;
+}
+
+static void
+systrace_provide(void *arg, dtrace_probedesc_t *desc)
+{
+ int i;
+
+ if (desc != NULL)
+ return;
+
+ for (i = 0; i < MAXSYSCALL; i++) {
+ if (dtrace_probe_lookup(systrace_id, NULL,
+ uglyhack.pp_syscallnames[i], "entry") != 0)
+ continue;
+
+ (void) dtrace_probe_create(systrace_id, NULL, uglyhack.pp_syscallnames[i],
+ "entry", SYSTRACE_ARTIFICIAL_FRAMES,
+ (void *)((uintptr_t)SYSTRACE_ENTRY(i)));
+ (void) dtrace_probe_create(systrace_id, NULL, uglyhack.pp_syscallnames[i],
+ "return", SYSTRACE_ARTIFICIAL_FRAMES,
+ (void *)((uintptr_t)SYSTRACE_RETURN(i)));
+ }
+}
+
+static void
+systrace_destroy(void *arg, dtrace_id_t id, void *parg)
+{
+#ifdef DEBUG
+ int sysnum = SYSTRACE_SYSNUM((uintptr_t)parg);
+
+ /*
+ * There's nothing to do here but assert that we have actually been
+ * disabled.
+ */
+ if (SYSTRACE_ISENTRY((uintptr_t)parg)) {
+ ASSERT(sysent[sysnum].sy_entry == 0);
+ } else {
+ ASSERT(sysent[sysnum].sy_return == 0);
+ }
+#endif
+}
+
+static void
+systrace_enable(void *arg, dtrace_id_t id, void *parg)
+{
+ int sysnum = SYSTRACE_SYSNUM((uintptr_t)parg);
+
+ if (SYSENT[sysnum].sy_systrace_args_func == NULL)
+ SYSENT[sysnum].sy_systrace_args_func = systrace_args;
+
+ if (SYSTRACE_ISENTRY((uintptr_t)parg))
+ SYSENT[sysnum].sy_entry = id;
+ else
+ SYSENT[sysnum].sy_return = id;
+}
+
+static void
+systrace_disable(void *arg, dtrace_id_t id, void *parg)
+{
+ int sysnum = SYSTRACE_SYSNUM((uintptr_t)parg);
+
+ SYSENT[sysnum].sy_entry = 0;
+ SYSENT[sysnum].sy_return = 0;
+}
+
+static void
+systrace_load(void *dummy)
+{
+ /* Create the /dev/dtrace/systrace entry. */
+ systrace_cdev = make_dev(&systrace_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600,
+ DEVNAME);
+
+ if (dtrace_register(PROVNAME, &systrace_attr, DTRACE_PRIV_USER,
+ NULL, &systrace_pops, NULL, &systrace_id) != 0)
+ return;
+
+#if !defined(LINUX_SYSTRACE)
+ systrace_probe_func = systrace_probe;
+#endif
+}
+
+
+static int
+systrace_unload()
+{
+ int error = 0;
+
+ if ((error = dtrace_unregister(systrace_id)) != 0)
+ return (error);
+
+#if !defined(LINUX_SYSTRACE)
+ systrace_probe_func = NULL;
+#endif
+
+ destroy_dev(systrace_cdev);
+
+ return (error);
+}
+
+static int
+systrace_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
+systrace_open(struct cdev *dev __unused, int oflags __unused, int devtype __unused, struct thread *td __unused)
+{
+ return (0);
+}
+
+SYSINIT(systrace_load, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, systrace_load, NULL);
+SYSUNINIT(systrace_unload, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, systrace_unload, NULL);
+
+#ifdef LINUX_SYSTRACE
+DEV_MODULE(linsystrace, systrace_modevent, NULL);
+MODULE_VERSION(linsystrace, 1);
+MODULE_DEPEND(linsystrace, linux, 1, 1, 1);
+MODULE_DEPEND(linsystrace, systrace, 1, 1, 1);
+MODULE_DEPEND(linsystrace, dtrace, 1, 1, 1);
+MODULE_DEPEND(linsystrace, opensolaris, 1, 1, 1);
+#else
+DEV_MODULE(systrace, systrace_modevent, NULL);
+MODULE_VERSION(systrace, 1);
+MODULE_DEPEND(systrace, dtrace, 1, 1, 1);
+MODULE_DEPEND(systrace, opensolaris, 1, 1, 1);
+#endif
diff --git a/sys/conf/Makefile.amd64 b/sys/conf/Makefile.amd64
index 1032a51ac334..6c737eb2b4e7 100644
--- a/sys/conf/Makefile.amd64
+++ b/sys/conf/Makefile.amd64
@@ -32,7 +32,8 @@ S= ../../..
.include "$S/conf/kern.pre.mk"
DDB_ENABLED!= grep DDB opt_ddb.h || true
-.if !empty(DDB_ENABLED)
+DTR_ENABLED!= grep KDTRACE_FRAME opt_kdtrace.h || true
+.if !empty(DDB_ENABLED) || !empty(DTR_ENABLED)
CFLAGS+= -fno-omit-frame-pointer
.endif
diff --git a/sys/conf/NOTES b/sys/conf/NOTES
index 80c4d0c794b7..f24ae326cad0 100644
--- a/sys/conf/NOTES
+++ b/sys/conf/NOTES
@@ -356,6 +356,12 @@ options DDB_NUMSYM
options GDB
#
+# Enable the kernel DTrace hooks which are required to load the DTrace
+# kernel modules.
+#
+options KDTRACE_HOOKS
+
+#
# SYSCTL_DEBUG enables a 'sysctl' debug tree that can be used to dump the
# contents of the registered sysctl nodes on the console. It is disabled by
# default because it generates excessively verbose console output that can
diff --git a/sys/conf/files b/sys/conf/files
index e2111bb11101..5e53ab8bb03a 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -1443,6 +1443,7 @@ kern/kern_cpu.c standard
kern/kern_cpuset.c standard
kern/kern_context.c standard
kern/kern_descrip.c standard
+kern/kern_dtrace.c optional kdtrace_hooks
kern/kern_environment.c standard
kern/kern_event.c standard
kern/kern_exec.c standard
@@ -1526,6 +1527,7 @@ kern/subr_rman.c standard
kern/subr_rtc.c optional genclock
kern/subr_sbuf.c standard
kern/subr_scanf.c standard
+kern/kern_sdt.c optional kdtrace_hooks
kern/subr_sleepqueue.c standard
kern/subr_smp.c standard
kern/subr_stack.c optional ddb | stack
@@ -1538,7 +1540,7 @@ kern/sys_generic.c standard
kern/sys_pipe.c standard
kern/sys_process.c standard
kern/sys_socket.c standard
-kern/syscalls.c optional witness | invariants
+kern/syscalls.c optional witness | invariants | kdtrace_hooks
kern/sysv_ipc.c standard
kern/sysv_msg.c optional sysvmsg
kern/sysv_sem.c optional sysvsem
@@ -1671,7 +1673,8 @@ net/rtsock.c standard
net/slcompress.c optional netgraph_vjc | ppp | sl | sppp | \
netgraph_sppp
net/zlib.c optional crypto | geom_uzip | ipsec | \
- mxge | ppp_deflate | netgraph_deflate
+ mxge | ppp_deflate | netgraph_deflate | \
+ ddb_ctf
net80211/ieee80211.c optional wlan
net80211/ieee80211_acl.c optional wlan_acl
net80211/ieee80211_amrr.c optional wlan_amrr
diff --git a/sys/conf/kern.post.mk b/sys/conf/kern.post.mk
index 1eca596c1694..f712776056f2 100644
--- a/sys/conf/kern.post.mk
+++ b/sys/conf/kern.post.mk
@@ -86,6 +86,9 @@ ${FULLKERNEL}: ${SYSTEM_DEP} vers.o
@rm -f ${.TARGET}
@echo linking ${.TARGET}
${SYSTEM_LD}
+.if defined(CTFMERGE)
+ ${SYSTEM_CTFMERGE}
+.endif
.if !defined(DEBUG)
${OBJCOPY} --strip-debug ${.TARGET}
.endif
diff --git a/sys/conf/kern.pre.mk b/sys/conf/kern.pre.mk
index a5139f14afe4..79ec5b8196c5 100644
--- a/sys/conf/kern.pre.mk
+++ b/sys/conf/kern.pre.mk
@@ -31,6 +31,8 @@ _MINUS_O= -O2
. endif
. if ${MACHINE_ARCH} == "amd64"
COPTFLAGS?=-O2 -frename-registers -pipe
+. elif ${MACHINE_ARCH} == "sparc64"
+COPTFLAGS?=-pipe
. else
COPTFLAGS?=${_MINUS_O} -pipe
. endif
@@ -79,6 +81,9 @@ INCLUDES+= -I$S/dev/twa
# ... and XFS
INCLUDES+= -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs
+# ... and OpenSolaris
+INCLUDES+= -I$S/contrib/opensolaris/compat
+
.endif
CFLAGS= ${COPTFLAGS} ${C_DIALECT} ${DEBUG} ${CWARNFLAGS}
@@ -126,6 +131,12 @@ NORMAL_C_NOWERROR= ${CC} -c ${CFLAGS} ${PROF} ${.IMPSRC}
NORMAL_M= ${AWK} -f $S/tools/makeobjops.awk ${.IMPSRC} -c ; \
${CC} -c ${CFLAGS} ${WERROR} ${PROF} ${.PREFIX}.c
+.if defined(CTFCONVERT)
+NORMAL_CTFCONVERT= ${CTFCONVERT} ${CTFFLAGS} ${.TARGET}
+.else
+NORMAL_CTFCONVERT=
+.endif
+
NORMAL_LINT= ${LINT} ${LINTFLAGS} ${CFLAGS:M-[DIU]*} ${.IMPSRC}
GEN_CFILES= $S/$M/$M/genassym.c ${MFILES:T:S/.m$/.c/}
@@ -134,6 +145,10 @@ SYSTEM_DEP= Makefile ${SYSTEM_OBJS}
SYSTEM_OBJS= locore.o ${MDOBJS} ${OBJS}
SYSTEM_OBJS+= ${SYSTEM_CFILES:.c=.o}
SYSTEM_OBJS+= hack.So
+.if defined(CTFMERGE)
+SYSTEM_CTFMERGE= ${CTFMERGE} ${CTFFLAGS} -o ${.TARGET} ${SYSTEM_OBJS} vers.o
+LD+= -g
+.endif
SYSTEM_LD= @${LD} -Bdynamic -T $S/conf/ldscript.$M \
-warn-common -export-dynamic -dynamic-linker /red/herring \
-o ${.TARGET} -X ${SYSTEM_OBJS} vers.o
diff --git a/sys/conf/options b/sys/conf/options
index 691a93162558..1c0af8214866 100644
--- a/sys/conf/options
+++ b/sys/conf/options
@@ -50,6 +50,7 @@ TWA_FLASH_FIRMWARE opt_twa.h
DDB
DDB_CAPTURE_DEFAULTBUFSIZE opt_ddb.h
DDB_CAPTURE_MAXBUFSIZE opt_ddb.h
+DDB_CTF opt_ddb.h
DDB_NUMSYM opt_ddb.h
GDB
KDB opt_global.h
@@ -104,6 +105,7 @@ GEOM_UZIP opt_geom.h
GEOM_VIRSTOR opt_geom.h
GEOM_VOL opt_geom.h
GEOM_ZERO opt_geom.h
+KDTRACE_HOOKS opt_kdtrace.h
KSE opt_global.h
KSTACK_MAX_PAGES
KSTACK_PAGES
diff --git a/sys/conf/options.amd64 b/sys/conf/options.amd64
index 79bc8162369f..2ea539d79ee8 100644
--- a/sys/conf/options.amd64
+++ b/sys/conf/options.amd64
@@ -64,6 +64,7 @@ DEV_ATPIC opt_atpic.h
# Debugging
STOP_NMI opt_cpu.h
+KDTRACE_FRAME opt_kdtrace.h
# BPF just-in-time compiler
BPF_JITTER opt_bpf.h
diff --git a/sys/i386/conf/GENERIC b/sys/i386/conf/GENERIC
index edaa02ecb168..4e62744bb5bb 100644
--- a/sys/i386/conf/GENERIC
+++ b/sys/i386/conf/GENERIC
@@ -64,6 +64,9 @@ options KBD_INSTALL_CDEV # install a CDEV entry in /dev
options ADAPTIVE_GIANT # Giant mutex is adaptive.
options STOP_NMI # Stop CPUS using NMI instead of IPI
options AUDIT # Security event auditing
+options PRINTF_BUFR_SIZE=128 # Prevent printf output being interspersed.
+options KDTRACE_HOOKS # DTrace hooks required to load DTrace modules
+options DDB_CTF # Load CTF data too.
# To make an SMP kernel, the next two lines are needed
options SMP # Symmetric MultiProcessor Kernel
diff --git a/sys/i386/i386/exception.s b/sys/i386/i386/exception.s
index 259327d67207..0ff425e7fed8 100644
--- a/sys/i386/i386/exception.s
+++ b/sys/i386/i386/exception.s
@@ -31,6 +31,7 @@
*/
#include "opt_apic.h"
+#include "opt_kdtrace.h"
#include "opt_npx.h"
#include <machine/asmacros.h>
@@ -40,7 +41,24 @@
#include "assym.s"
#define SEL_RPL_MASK 0x0003
+#define GSEL_KPL 0x0020 /* GSEL(GCODE_SEL, SEL_KPL) */
+
+#ifdef KDTRACE_HOOKS
+ .bss
+ .globl dtrace_invop_jump_addr
+ .align 4
+ .type dtrace_invop_jump_addr, @object
+ .size dtrace_invop_jump_addr, 4
+dtrace_invop_jump_addr:
+ .zero 4
+ .globl dtrace_invop_calltrap_addr
+ .align 4
+ .type dtrace_invop_calltrap_addr, @object
+ .size dtrace_invop_calltrap_addr, 4
+dtrace_invop_calltrap_addr:
+ .zero 8
+#endif
.text
/*****************************************************************************/
@@ -88,8 +106,10 @@ IDTVEC(ofl)
pushl $0; TRAP(T_OFLOW)
IDTVEC(bnd)
pushl $0; TRAP(T_BOUND)
+#ifndef KDTRACE_HOOKS
IDTVEC(ill)
pushl $0; TRAP(T_PRIVINFLT)
+#endif
IDTVEC(dna)
pushl $0; TRAP(T_DNA)
IDTVEC(fpusegm)
@@ -146,6 +166,45 @@ calltrap:
jmp doreti
/*
+ * Privileged instruction fault.
+ */
+#ifdef KDTRACE_HOOKS
+ SUPERALIGN_TEXT
+IDTVEC(ill)
+ /* Check if there is no DTrace hook registered. */
+ cmpl $0,dtrace_invop_jump_addr
+ je norm_ill
+
+ /* Check if this is a user fault. */
+ cmpl $GSEL_KPL, 4(%esp) /* Check the code segment. */
+
+ /* If so, just handle it as a normal trap. */
+ jne norm_ill
+
+ /*
+ * This is a kernel instruction fault that might have been caused
+ * by a DTrace provider.
+ */
+ pushal /* Push all registers onto the stack. */
+
+ /*
+ * Set our jump address for the jump back in the event that
+ * the exception wasn't caused by DTrace at all.
+ */
+ movl $norm_ill, dtrace_invop_calltrap_addr
+
+ /* Jump to the code hooked in by DTrace. */
+ jmpl *dtrace_invop_jump_addr
+
+ /*
+ * Process the instruction fault in the normal way.
+ */
+norm_ill:
+ pushl $0
+ TRAP(T_PRIVINFLT)
+#endif
+
+/*
* SYSCALL CALL GATE (old entry point for a.out binaries)
*
* The intersegment call has been set up to specify one dummy parameter.
diff --git a/sys/i386/i386/local_apic.c b/sys/i386/i386/local_apic.c
index f35be2fbb8bb..72cfd1713462 100644
--- a/sys/i386/i386/local_apic.c
+++ b/sys/i386/i386/local_apic.c
@@ -35,6 +35,7 @@
__FBSDID("$FreeBSD$");
#include "opt_hwpmc_hooks.h"
+#include "opt_kdtrace.h"
#include "opt_ddb.h"
@@ -65,6 +66,11 @@ __FBSDID("$FreeBSD$");
#include <ddb/ddb.h>
#endif
+#ifdef KDTRACE_HOOKS
+#include <sys/dtrace_bsd.h>
+cyclic_clock_func_t lapic_cyclic_clock_func[MAXCPU];
+#endif
+
/* Sanity checks on IDT vectors. */
CTASSERT(APIC_IO_INTS + APIC_NUM_IOINTS == APIC_TIMER_INT);
CTASSERT(APIC_TIMER_INT < APIC_LOCAL_INTS);
@@ -670,6 +676,17 @@ lapic_handle_timer(struct trapframe *frame)
(*la->la_timer_count)++;
critical_enter();
+#ifdef KDTRACE_HOOKS
+ /*
+ * If the DTrace hooks are configured and a callback function
+ * has been registered, then call it to process the high speed
+ * timers.
+ */
+ int cpu = PCPU_GET(cpuid);
+ if (lapic_cyclic_clock_func[cpu] != NULL)
+ (*lapic_cyclic_clock_func[cpu])(frame);
+#endif
+
/* Fire hardclock at hz. */
la->la_hard_ticks += hz;
if (la->la_hard_ticks >= lapic_timer_hz) {
diff --git a/sys/i386/i386/trap.c b/sys/i386/i386/trap.c
index 7be4d499813c..99a9d4c0db9a 100644
--- a/sys/i386/i386/trap.c
+++ b/sys/i386/i386/trap.c
@@ -49,6 +49,7 @@ __FBSDID("$FreeBSD$");
#include "opt_hwpmc_hooks.h"
#include "opt_isa.h"
#include "opt_kdb.h"
+#include "opt_kdtrace.h"
#include "opt_ktrace.h"
#include "opt_npx.h"
#include "opt_trap.h"
@@ -102,6 +103,26 @@ __FBSDID("$FreeBSD$");
#include <machine/clock.h>
#endif
+#ifdef KDTRACE_HOOKS
+#include <sys/dtrace_bsd.h>
+
+/*
+ * This is a hook which is initialised by the dtrace module
+ * to handle traps which might occur during DTrace probe
+ * execution.
+ */
+dtrace_trap_func_t dtrace_trap_func;
+
+dtrace_doubletrap_func_t dtrace_doubletrap_func;
+
+/*
+ * This is a hook which is initialised by the systrace module
+ * when it is loaded. This keeps the DTrace syscall provider
+ * implementation opaque.
+ */
+systrace_probe_func_t systrace_probe_func;
+#endif
+
extern void trap(struct trapframe *frame);
extern void syscall(struct trapframe *frame);
@@ -219,6 +240,25 @@ trap(struct trapframe *frame)
goto out;
#endif
+#ifdef KDTRACE_HOOKS
+ /*
+ * 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 the probe, the no-fault
+ * flag is cleared and finally re-scheduling is enabled.
+ *
+ * If the DTrace kernel module has registered a trap handler,
+ * call it and if it returns non-zero, assume that it has
+ * handled the trap and modified the trap frame so that this
+ * function can return normally.
+ */
+ if ((type == T_PROTFLT || type == T_PAGEFLT) &&
+ dtrace_trap_func != NULL)
+ if ((*dtrace_trap_func)(frame, type))
+ goto out;
+#endif
+
if ((frame->tf_eflags & PSL_I) == 0) {
/*
* Buggy application or kernel code has disabled
@@ -916,6 +956,10 @@ trap_fatal(frame, eva)
void
dblfault_handler()
{
+#ifdef KDTRACE_HOOKS
+ if (dtrace_doubletrap_func != NULL)
+ (*dtrace_doubletrap_func)();
+#endif
printf("\nFatal double fault:\n");
printf("eip = 0x%x\n", PCPU_GET(common_tss.tss_eip));
printf("esp = 0x%x\n", PCPU_GET(common_tss.tss_esp));
@@ -1031,9 +1075,34 @@ syscall(struct trapframe *frame)
PTRACESTOP_SC(p, td, S_PT_SCE);
+#ifdef KDTRACE_HOOKS
+ /*
+ * If the systrace module has registered it's probe
+ * callback and if there is a probe active for the
+ * syscall 'entry', process the probe.
+ */
+ if (systrace_probe_func != NULL && callp->sy_entry != 0)
+ (*systrace_probe_func)(callp->sy_entry, code, callp,
+ args);
+#endif
+
AUDIT_SYSCALL_ENTER(code, td);
error = (*callp->sy_call)(td, args);
AUDIT_SYSCALL_EXIT(error, td);
+
+ /* Save the latest error return value. */
+ td->td_errno = error;
+
+#ifdef KDTRACE_HOOKS
+ /*
+ * If the systrace module has registered it's probe
+ * callback and if there is a probe active for the
+ * syscall 'return', process the probe.
+ */
+ if (systrace_probe_func != NULL && callp->sy_return != 0)
+ (*systrace_probe_func)(callp->sy_return, code, callp,
+ args);
+#endif
}
switch (error) {
diff --git a/sys/i386/include/param.h b/sys/i386/include/param.h
index 0f67c271a7a1..09c0855f9fdc 100644
--- a/sys/i386/include/param.h
+++ b/sys/i386/include/param.h
@@ -65,7 +65,7 @@
#endif
#define MID_MACHINE MID_I386
-#ifdef SMP
+#if defined(SMP) || defined(KLD_MODULE)
#define MAXCPU 16
#else
#define MAXCPU 1
diff --git a/sys/i386/linux/Makefile b/sys/i386/linux/Makefile
index bd6b32a7d4a8..46d5a319b7c8 100644
--- a/sys/i386/linux/Makefile
+++ b/sys/i386/linux/Makefile
@@ -12,4 +12,6 @@ linux_sysent.c linux_syscall.h linux_proto.h: ../../kern/makesyscalls.sh \
-mv -f linux_sysent.c linux_sysent.c.bak
-mv -f linux_syscall.h linux_syscall.h.bak
-mv -f linux_proto.h linux_proto.h.bak
+ -mv -f linux_systrace.c linux_systrace.c.bak
+ -mv -f linux_syscallnames.c linux_syscallnames.c.bak
sh ../../kern/makesyscalls.sh syscalls.master syscalls.conf
diff --git a/sys/i386/linux/syscalls.conf b/sys/i386/linux/syscalls.conf
index 87dfbfe7589a..1bc02bafd3c8 100644
--- a/sys/i386/linux/syscalls.conf
+++ b/sys/i386/linux/syscalls.conf
@@ -1,5 +1,5 @@
# $FreeBSD$
-sysnames="/dev/null"
+sysnames="linux_syscallnames.c"
sysproto="linux_proto.h"
sysproto_h=_LINUX_SYSPROTO_H_
syshdr="linux_syscall.h"
@@ -8,4 +8,4 @@ sysmk="/dev/null"
syscallprefix="LINUX_SYS_"
switchname="linux_sysent"
namesname="linux_syscallnames"
-systrace="/dev/null"
+systrace="linux_systrace.c"
diff --git a/sys/ia64/include/param.h b/sys/ia64/include/param.h
index c71c6e5a7bf3..cfc198cf99f8 100644
--- a/sys/ia64/include/param.h
+++ b/sys/ia64/include/param.h
@@ -78,8 +78,8 @@
#define MACHINE_ARCH "ia64"
#endif
-#ifdef SMP
-#define MAXCPU 4
+#if defined(SMP) || defined(KLD_MODULE)
+#define MAXCPU 4
#else
#define MAXCPU 1
#endif
diff --git a/sys/kern/kern_ctf.c b/sys/kern/kern_ctf.c
new file mode 100644
index 000000000000..254b8204df39
--- /dev/null
+++ b/sys/kern/kern_ctf.c
@@ -0,0 +1,325 @@
+/*-
+ * Copyright (c) 2008 John Birrell <jb@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Note this file is included by both link_elf.c and link_elf_obj.c.
+ *
+ * The CTF header structure definition can't be used here because it's
+ * (annoyingly) covered by the CDDL. We will just use a few bytes from
+ * it as an integer array where we 'know' what they mean.
+ */
+#define CTF_HDR_SIZE 36
+#define CTF_HDR_STRTAB_U32 7
+#define CTF_HDR_STRLEN_U32 8
+
+#ifdef DDB_CTF
+static void *
+z_alloc(void *nil, u_int items, u_int size)
+{
+ void *ptr;
+
+ ptr = malloc(items * size, M_TEMP, M_NOWAIT);
+ return ptr;
+}
+
+static void
+z_free(void *nil, void *ptr)
+{
+ free(ptr, M_TEMP);
+}
+
+#endif
+
+static int
+link_elf_ctf_get(linker_file_t lf, linker_ctf_t *lc)
+{
+#ifdef DDB_CTF
+ Elf_Ehdr *hdr = NULL;
+ Elf_Shdr *shdr = NULL;
+ caddr_t ctftab = NULL;
+ caddr_t raw = NULL;
+ caddr_t shstrtab = NULL;
+ elf_file_t ef = (elf_file_t) lf;
+ int flags;
+ int i;
+ int nbytes;
+ int resid;
+ int vfslocked;
+ size_t sz;
+ struct nameidata nd;
+ struct thread *td = curthread;
+ uint8_t ctf_hdr[CTF_HDR_SIZE];
+#endif
+ int error = 0;
+
+ if (lf == NULL || lc == NULL)
+ return (EINVAL);
+
+ /* Set the defaults for no CTF present. That's not a crime! */
+ bzero(lc, sizeof(*lc));
+
+#ifdef DDB_CTF
+ /*
+ * First check if we've tried to load CTF data previously and the
+ * CTF ELF section wasn't found. We flag that condition by setting
+ * ctfcnt to -1. See below.
+ */
+ if (ef->ctfcnt < 0)
+ return (0);
+
+ /* Now check if we've already loaded the CTF data.. */
+ if (ef->ctfcnt > 0) {
+ /* We only need to load once. */
+ lc->ctftab = ef->ctftab;
+ lc->ctfcnt = ef->ctfcnt;
+ lc->symtab = ef->ddbsymtab;
+ lc->strtab = ef->ddbstrtab;
+ lc->strcnt = ef->ddbstrcnt;
+ lc->nsym = ef->ddbsymcnt;
+ lc->ctfoffp = (uint32_t **) &ef->ctfoff;
+ lc->typoffp = (uint32_t **) &ef->typoff;
+ lc->typlenp = &ef->typlen;
+ return (0);
+ }
+
+ /*
+ * We need to try reading the CTF data. Flag no CTF data present
+ * by default and if we actually succeed in reading it, we'll
+ * update ctfcnt to the number of bytes read.
+ */
+ ef->ctfcnt = -1;
+
+ NDINIT(&nd, LOOKUP, FOLLOW | MPSAFE, UIO_SYSSPACE, lf->pathname, td);
+ flags = FREAD;
+ error = vn_open(&nd, &flags, 0, NULL);
+ if (error)
+ return (error);
+ vfslocked = NDHASGIANT(&nd);
+ NDFREE(&nd, NDF_ONLY_PNBUF);
+
+ /* Allocate memory for the FLF header. */
+ if ((hdr = malloc(sizeof(*hdr), M_LINKER, M_WAITOK)) == NULL) {
+ error = ENOMEM;
+ goto out;
+ }
+
+ /* Read the ELF header. */
+ if ((error = vn_rdwr(UIO_READ, nd.ni_vp, hdr, sizeof(*hdr),
+ 0, UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED, &resid,
+ td)) != 0)
+ goto out;
+
+ /* Sanity check. */
+ if (!IS_ELF(*hdr)) {
+ error = ENOEXEC;
+ goto out;
+ }
+
+ nbytes = hdr->e_shnum * hdr->e_shentsize;
+ if (nbytes == 0 || hdr->e_shoff == 0 ||
+ hdr->e_shentsize != sizeof(Elf_Shdr)) {
+ error = ENOEXEC;
+ goto out;
+ }
+
+ /* Allocate memory for all the section headers */
+ if ((shdr = malloc(nbytes, M_LINKER, M_WAITOK)) == NULL) {
+ error = ENOMEM;
+ goto out;
+ }
+
+ /* Read all the section headers */
+ if ((error = vn_rdwr(UIO_READ, nd.ni_vp, (caddr_t)shdr, nbytes,
+ hdr->e_shoff, UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED,
+ &resid, td)) != 0)
+ goto out;
+
+ /*
+ * We need to search for the CTF section by name, so if the
+ * section names aren't present, then we can't locate the
+ * .SUNW_ctf section containing the CTF data.
+ */
+ if (hdr->e_shstrndx == 0 || shdr[hdr->e_shstrndx].sh_type != SHT_STRTAB)
+ goto out;
+
+ /* Allocate memory to buffer the section header strings. */
+ if ((shstrtab = malloc(shdr[hdr->e_shstrndx].sh_size, M_LINKER,
+ M_WAITOK)) == NULL) {
+ error = ENOMEM;
+ goto out;
+ }
+
+ /* Read the section header strings. */
+ if ((error = vn_rdwr(UIO_READ, nd.ni_vp, shstrtab,
+ shdr[hdr->e_shstrndx].sh_size, shdr[hdr->e_shstrndx].sh_offset,
+ UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED, &resid,
+ td)) != 0)
+ goto out;
+
+ /* Search for the section containing the CTF data. */
+ for (i = 0; i < hdr->e_shnum; i++)
+ if (strcmp(".SUNW_ctf", shstrtab + shdr[i].sh_name) == 0)
+ break;
+
+ /* Check if the CTF section wasn't found. */
+ if (i >= hdr->e_shnum)
+ goto out;
+
+ /* Read the CTF header. */
+ if ((error = vn_rdwr(UIO_READ, nd.ni_vp, ctf_hdr, sizeof(ctf_hdr),
+ shdr[i].sh_offset, UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred,
+ NOCRED, &resid, td)) != 0)
+ goto out;
+
+ /* Check the CTF magic number. (XXX check for big endian!) */
+ if (ctf_hdr[0] != 0xf1 || ctf_hdr[1] != 0xcf)
+ goto out;
+
+ /* Check if version 2. */
+ if (ctf_hdr[2] != 2)
+ goto out;
+
+ /* Check if the data is compressed. */
+ if ((ctf_hdr[3] & 0x1) != 0) {
+ uint32_t *u32 = (uint32_t *) ctf_hdr;
+
+ /*
+ * The last two fields in the CTF header are the offset
+ * from the end of the header to the start of the string
+ * data and the length of that string data. se this
+ * information to determine the decompressed CTF data
+ * buffer required.
+ */
+ sz = u32[CTF_HDR_STRTAB_U32] + u32[CTF_HDR_STRLEN_U32] +
+ sizeof(ctf_hdr);
+
+ /*
+ * Allocate memory for the compressed CTF data, including
+ * the header (which isn't compressed).
+ */
+ if ((raw = malloc(shdr[i].sh_size, M_LINKER, M_WAITOK)) == NULL) {
+ error = ENOMEM;
+ goto out;
+ }
+ } else {
+ /*
+ * The CTF data is not compressed, so the ELF section
+ * size is the same as the buffer size required.
+ */
+ sz = shdr[i].sh_size;
+ }
+
+ /*
+ * Allocate memory to buffer the CTF data in it's decompressed
+ * form.
+ */
+ if ((ctftab = malloc(sz, M_LINKER, M_WAITOK)) == NULL) {
+ error = ENOMEM;
+ goto out;
+ }
+
+ /*
+ * Read the CTF data into the raw buffer if compressed, or
+ * directly into the CTF buffer otherwise.
+ */
+ if ((error = vn_rdwr(UIO_READ, nd.ni_vp, raw == NULL ? ctftab : raw,
+ shdr[i].sh_size, shdr[i].sh_offset, UIO_SYSSPACE, IO_NODELOCKED,
+ td->td_ucred, NOCRED, &resid, td)) != 0)
+ goto out;
+
+ /* Check if decompression is required. */
+ if (raw != NULL) {
+ z_stream zs;
+ int ret;
+
+ /*
+ * The header isn't compressed, so copy that into the
+ * CTF buffer first.
+ */
+ bcopy(ctf_hdr, ctftab, sizeof(ctf_hdr));
+
+ /* Initialise the zlib structure. */
+ bzero(&zs, sizeof(zs));
+ zs.zalloc = z_alloc;
+ zs.zfree = z_free;
+
+ if (inflateInit(&zs) != Z_OK) {
+ error = EIO;
+ goto out;
+ }
+
+ zs.avail_in = shdr[i].sh_size - sizeof(ctf_hdr);
+ zs.next_in = ((uint8_t *) raw) + sizeof(ctf_hdr);
+ zs.avail_out = sz - sizeof(ctf_hdr);
+ zs.next_out = ((uint8_t *) ctftab) + sizeof(ctf_hdr);
+ if ((ret = inflate(&zs, Z_FINISH)) != Z_STREAM_END) {
+ printf("%s(%d): zlib inflate returned %d\n", __func__, __LINE__, ret);
+ error = EIO;
+ goto out;
+ }
+ }
+
+ /* Got the CTF data! */
+ ef->ctftab = ctftab;
+ ef->ctfcnt = shdr[i].sh_size;
+
+ /* We'll retain the memory allocated for the CTF data. */
+ ctftab = NULL;
+
+ /* Let the caller use the CTF data read. */
+ lc->ctftab = ef->ctftab;
+ lc->ctfcnt = ef->ctfcnt;
+ lc->symtab = ef->ddbsymtab;
+ lc->strtab = ef->ddbstrtab;
+ lc->strcnt = ef->ddbstrcnt;
+ lc->nsym = ef->ddbsymcnt;
+ lc->ctfoffp = (uint32_t **) &ef->ctfoff;
+ lc->typoffp = (uint32_t **) &ef->typoff;
+ lc->typlenp = &ef->typlen;
+
+out:
+ VOP_UNLOCK(nd.ni_vp, 0, td);
+ vn_close(nd.ni_vp, FREAD, td->td_ucred, td);
+ VFS_UNLOCK_GIANT(vfslocked);
+
+ if (hdr != NULL)
+ free(hdr, M_LINKER);
+ if (shdr != NULL)
+ free(shdr, M_LINKER);
+ if (shstrtab != NULL)
+ free(shstrtab, M_LINKER);
+ if (ctftab != NULL)
+ free(ctftab, M_LINKER);
+ if (raw != NULL)
+ free(raw, M_LINKER);
+#else
+ error = EOPNOTSUPP;
+#endif
+
+ return (error);
+}
diff --git a/sys/kern/kern_dtrace.c b/sys/kern/kern_dtrace.c
new file mode 100644
index 000000000000..145535201e2f
--- /dev/null
+++ b/sys/kern/kern_dtrace.c
@@ -0,0 +1,108 @@
+/*-
+ * Copyright (c) 2007-2008 John Birrell <jb@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_kdb.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/eventhandler.h>
+#include <sys/kdb.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/proc.h>
+#include <sys/dtrace_bsd.h>
+
+#define KDTRACE_PROC_SIZE 64
+#define KDTRACE_PROC_ZERO 8
+#define KDTRACE_THREAD_SIZE 256
+#define KDTRACE_THREAD_ZERO 64
+
+MALLOC_DEFINE(M_KDTRACE, "kdtrace", "DTrace hooks");
+
+/* Return the DTrace process data size compiled in the kernel hooks. */
+size_t
+kdtrace_proc_size()
+{
+ return(KDTRACE_PROC_SIZE);
+}
+
+static void
+kdtrace_proc_ctor(void *arg __unused, struct proc *p)
+{
+ p->p_dtrace = malloc(KDTRACE_PROC_SIZE, M_KDTRACE, M_WAITOK);
+
+ bzero(p->p_dtrace, KDTRACE_PROC_ZERO);
+}
+
+static void
+kdtrace_proc_dtor(void *arg __unused, struct proc *p)
+{
+ if (p->p_dtrace != NULL) {
+ free(p->p_dtrace, M_KDTRACE);
+ p->p_dtrace = NULL;
+ }
+}
+
+/* Return the DTrace thread data size compiled in the kernel hooks. */
+size_t
+kdtrace_thread_size()
+{
+ return(KDTRACE_THREAD_SIZE);
+}
+
+static void
+kdtrace_thread_ctor(void *arg __unused, struct thread *td)
+{
+ td->td_dtrace = malloc(KDTRACE_THREAD_SIZE, M_KDTRACE, M_WAITOK);
+
+ bzero(td->td_dtrace, KDTRACE_THREAD_ZERO);
+}
+
+static void
+kdtrace_thread_dtor(void *arg __unused, struct thread *td)
+{
+ if (td->td_dtrace != NULL) {
+ free(td->td_dtrace, M_KDTRACE);
+ td->td_dtrace = NULL;
+ }
+}
+
+/*
+ * Initialise the kernel DTrace hooks.
+ */
+static void
+init_dtrace(void *dummy __unused)
+{
+ EVENTHANDLER_REGISTER(process_ctor, kdtrace_proc_ctor, NULL, EVENTHANDLER_PRI_ANY);
+ EVENTHANDLER_REGISTER(process_dtor, kdtrace_proc_dtor, NULL, EVENTHANDLER_PRI_ANY);
+ EVENTHANDLER_REGISTER(thread_ctor, kdtrace_thread_ctor, NULL, EVENTHANDLER_PRI_ANY);
+ EVENTHANDLER_REGISTER(thread_dtor, kdtrace_thread_dtor, NULL, EVENTHANDLER_PRI_ANY);
+}
+
+SYSINIT(kdtrace, SI_SUB_KDTRACE, SI_ORDER_FIRST, init_dtrace, NULL);
diff --git a/sys/kern/kern_exec.c b/sys/kern/kern_exec.c
index 22f1cc173071..81902adfc7d5 100644
--- a/sys/kern/kern_exec.c
+++ b/sys/kern/kern_exec.c
@@ -28,6 +28,7 @@
__FBSDID("$FreeBSD$");
#include "opt_hwpmc_hooks.h"
+#include "opt_kdtrace.h"
#include "opt_ktrace.h"
#include "opt_mac.h"
@@ -53,6 +54,7 @@ __FBSDID("$FreeBSD$");
#include <sys/pioctl.h>
#include <sys/namei.h>
#include <sys/resourcevar.h>
+#include <sys/sdt.h>
#include <sys/sf_buf.h>
#include <sys/syscallsubr.h>
#include <sys/sysent.h>
@@ -82,6 +84,19 @@ __FBSDID("$FreeBSD$");
#include <security/audit/audit.h>
#include <security/mac/mac_framework.h>
+#ifdef KDTRACE_HOOKS
+#include <sys/dtrace_bsd.h>
+dtrace_execexit_func_t dtrace_fasttrap_exec;
+#endif
+
+SDT_PROVIDER_DECLARE(proc);
+SDT_PROBE_DEFINE(proc, kernel, , exec);
+SDT_PROBE_ARGTYPE(proc, kernel, , exec, 0, "char *");
+SDT_PROBE_DEFINE(proc, kernel, , exec_failure);
+SDT_PROBE_ARGTYPE(proc, kernel, , exec_failure, 0, "int");
+SDT_PROBE_DEFINE(proc, kernel, , exec_success);
+SDT_PROBE_ARGTYPE(proc, kernel, , exec_success, 0, "char *");
+
MALLOC_DEFINE(M_PARGS, "proc-args", "Process arguments");
static int sysctl_kern_ps_strings(SYSCTL_HANDLER_ARGS);
@@ -348,6 +363,8 @@ do_execve(td, args, mac_p)
imgp->image_header = NULL;
+ SDT_PROBE(proc, kernel, , exec, args->fname, 0, 0, 0, 0 );
+
/*
* Translate the file name. namei() returns a vnode pointer
* in ni_vp amoung other things.
@@ -658,6 +675,15 @@ interpret:
textvp = p->p_textvp;
p->p_textvp = ndp->ni_vp;
+#ifdef KDTRACE_HOOKS
+ /*
+ * Tell the DTrace fasttrap provider about the exec if it
+ * has declared an interest.
+ */
+ if (dtrace_fasttrap_exec)
+ dtrace_fasttrap_exec(p);
+#endif
+
/*
* Notify others that we exec'd, and clear the P_INEXEC flag
* as we're now a bona fide freshly-execed process.
@@ -729,6 +755,9 @@ done1:
else
crfree(newcred);
VOP_UNLOCK(imgp->vp, 0, td);
+
+ SDT_PROBE(proc, kernel, , exec_success, args->fname, 0, 0, 0, 0);
+
/*
* Handle deferred decrement of ref counts.
*/
@@ -791,6 +820,8 @@ exec_fail:
p->p_flag &= ~P_INEXEC;
PROC_UNLOCK(p);
+ SDT_PROBE(proc, kernel, , exec_failure, error, 0, 0, 0, 0);
+
done2:
#ifdef MAC
mac_execve_exit(imgp);
diff --git a/sys/kern/kern_exit.c b/sys/kern/kern_exit.c
index 4f8a70e4e5e4..1c9bf4c115eb 100644
--- a/sys/kern/kern_exit.c
+++ b/sys/kern/kern_exit.c
@@ -38,6 +38,7 @@
__FBSDID("$FreeBSD$");
#include "opt_compat.h"
+#include "opt_kdtrace.h"
#include "opt_ktrace.h"
#include "opt_mac.h"
@@ -65,6 +66,7 @@ __FBSDID("$FreeBSD$");
#include <sys/ptrace.h>
#include <sys/acct.h> /* for acct_process() function prototype */
#include <sys/filedesc.h>
+#include <sys/sdt.h>
#include <sys/shm.h>
#include <sys/sem.h>
#ifdef KTRACE
@@ -82,6 +84,15 @@ __FBSDID("$FreeBSD$");
#include <vm/vm_page.h>
#include <vm/uma.h>
+#ifdef KDTRACE_HOOKS
+#include <sys/dtrace_bsd.h>
+dtrace_execexit_func_t dtrace_fasttrap_exit;
+#endif
+
+SDT_PROVIDER_DECLARE(proc);
+SDT_PROBE_DEFINE(proc, kernel, , exit);
+SDT_PROBE_ARGTYPE(proc, kernel, , exit, 0, "int");
+
/* Required to be non-static for SysVR4 emulator */
MALLOC_DEFINE(M_ZOMBIE, "zombie", "zombie proc status");
@@ -449,11 +460,29 @@ retry:
PROC_LOCK(p);
p->p_xstat = rv;
p->p_xthread = td;
+
+#ifdef KDTRACE_HOOKS
+ /*
+ * Tell the DTrace fasttrap provider about the exit if it
+ * has declared an interest.
+ */
+ if (dtrace_fasttrap_exit)
+ dtrace_fasttrap_exit(p);
+#endif
+
/*
* Notify interested parties of our demise.
*/
KNOTE_LOCKED(&p->p_klist, NOTE_EXIT);
+#ifdef KDTRACE_HOOKS
+ int reason = CLD_EXITED;
+ if (WCOREDUMP(rv))
+ reason = CLD_DUMPED;
+ else if (WIFSIGNALED(rv))
+ reason = CLD_KILLED;
+ SDT_PROBE(proc, kernel, , exit, reason, 0, 0, 0, 0);
+#endif
/*
* Just delete all entries in the p_klist. At this point we won't
* report any more events, and there are nasty race conditions that
diff --git a/sys/kern/kern_fork.c b/sys/kern/kern_fork.c
index 2d15339a4894..71fe803c4653 100644
--- a/sys/kern/kern_fork.c
+++ b/sys/kern/kern_fork.c
@@ -37,6 +37,7 @@
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
+#include "opt_kdtrace.h"
#include "opt_ktrace.h"
#include "opt_mac.h"
@@ -63,6 +64,7 @@ __FBSDID("$FreeBSD$");
#include <sys/ktr.h>
#include <sys/ktrace.h>
#include <sys/unistd.h>
+#include <sys/sdt.h>
#include <sys/sx.h>
#include <sys/signalvar.h>
@@ -75,6 +77,16 @@ __FBSDID("$FreeBSD$");
#include <vm/vm_extern.h>
#include <vm/uma.h>
+#ifdef KDTRACE_HOOKS
+#include <sys/dtrace_bsd.h>
+dtrace_fork_func_t dtrace_fasttrap_fork;
+#endif
+
+SDT_PROVIDER_DECLARE(proc);
+SDT_PROBE_DEFINE(proc, kernel, , create);
+SDT_PROBE_ARGTYPE(proc, kernel, , create, 0, "struct proc *");
+SDT_PROBE_ARGTYPE(proc, kernel, , create, 1, "struct proc *");
+SDT_PROBE_ARGTYPE(proc, kernel, , create, 2, "int");
#ifndef _SYS_SYSPROTO_H_
struct fork_args {
@@ -620,6 +632,15 @@ again:
p2->p_pfsflags = p1->p_pfsflags;
}
+#ifdef KDTRACE_HOOKS
+ /*
+ * Tell the DTrace fasttrap provider about the new process
+ * if it has registered an interest.
+ */
+ if (dtrace_fasttrap_fork)
+ dtrace_fasttrap_fork(p1, p2);
+#endif
+
/*
* This begins the section where we must prevent the parent
* from being swapped.
@@ -707,6 +728,8 @@ again:
* Tell any interested parties about the new process.
*/
knote_fork(&p1->p_klist, p2->p_pid);
+ SDT_PROBE(proc, kernel, , create, p2, p1, flags, 0, 0);
+
/*
* Preserve synchronization semantics of vfork. If waiting for
* child to exec or exit, set P_PPWAIT on child, and sleep on our
diff --git a/sys/kern/kern_linker.c b/sys/kern/kern_linker.c
index de18e9e5cf79..3c114525adcf 100644
--- a/sys/kern/kern_linker.c
+++ b/sys/kern/kern_linker.c
@@ -97,6 +97,12 @@ linker_file_t linker_kernel_file;
static struct sx kld_sx; /* kernel linker lock */
+/*
+ * Load counter used by clients to determine if a linker file has been
+ * re-loaded. This counter is incremented for each file load.
+ */
+static int loadcnt;
+
static linker_class_list_t classes;
static linker_file_list_t linker_files;
static int next_file_id = 1;
@@ -535,7 +541,7 @@ linker_make_file(const char *pathname, linker_class_t lc)
KLD_LOCK_ASSERT();
filename = linker_basename(pathname);
- KLD_DPF(FILE, ("linker_make_file: new file, filename=%s\n", filename));
+ KLD_DPF(FILE, ("linker_make_file: new file, filename='%s' for pathname='%s'\n", filename, pathname));
lf = (linker_file_t)kobj_create((kobj_class_t)lc, M_LINKER, M_WAITOK);
if (lf == NULL)
return (NULL);
@@ -543,9 +549,13 @@ linker_make_file(const char *pathname, linker_class_t lc)
lf->userrefs = 0;
lf->flags = 0;
lf->filename = linker_strdup(filename);
+ lf->pathname = linker_strdup(pathname);
LINKER_GET_NEXT_FILE_ID(lf->id);
lf->ndeps = 0;
lf->deps = NULL;
+ lf->loadcnt = ++loadcnt;
+ lf->sdt_probes = NULL;
+ lf->sdt_nprobes = 0;
STAILQ_INIT(&lf->common);
TAILQ_INIT(&lf->modules);
TAILQ_INSERT_TAIL(&linker_files, lf, link);
@@ -630,10 +640,20 @@ linker_file_unload(linker_file_t file, int flags)
free(file->filename, M_LINKER);
file->filename = NULL;
}
+ if (file->pathname) {
+ free(file->pathname, M_LINKER);
+ file->pathname = NULL;
+ }
kobj_delete((kobj_t) file, M_LINKER);
return (0);
}
+int
+linker_ctf_get(linker_file_t file, linker_ctf_t *lc)
+{
+ return (LINKER_CTF_GET(file, lc));
+}
+
static int
linker_file_add_dependency(linker_file_t file, linker_file_t dep)
{
@@ -677,6 +697,16 @@ linker_file_lookup_set(linker_file_t file, const char *name,
return (error);
}
+/*
+ * List all functions in a file.
+ */
+int
+linker_file_function_listall(linker_file_t lf,
+ linker_function_nameval_callback_t callback_func, void *arg)
+{
+ return (LINKER_EACH_FUNCTION_NAMEVAL(lf, callback_func, arg));
+}
+
caddr_t
linker_file_lookup_symbol(linker_file_t file, const char *name, int deps)
{
@@ -995,7 +1025,13 @@ kern_kldunload(struct thread *td, int fileid, int flags)
lf = linker_find_file_by_id(fileid);
if (lf) {
KLD_DPF(FILE, ("kldunload: lf->userrefs=%d\n", lf->userrefs));
- if (lf->userrefs == 0) {
+
+ /* Check if there are DTrace probes enabled on this file. */
+ if (lf->nenabled > 0) {
+ printf("kldunload: attempt to unload file that has"
+ " DTrace probes enabled\n");
+ error = EBUSY;
+ } else if (lf->userrefs == 0) {
/*
* XXX: maybe LINKER_UNLOAD_FORCE should override ?
*/
@@ -1116,15 +1152,18 @@ kldstat(struct thread *td, struct kldstat_args *uap)
{
struct kld_file_stat stat;
linker_file_t lf;
- int error, namelen;
+ int error, namelen, version, version_num;
/*
* Check the version of the user's structure.
*/
- error = copyin(uap->stat, &stat, sizeof(struct kld_file_stat));
- if (error)
+ if ((error = copyin(&uap->stat->version, &version, sizeof(version))) != 0)
return (error);
- if (stat.version != sizeof(struct kld_file_stat))
+ if (version == sizeof(struct kld_file_stat_1))
+ version_num = 1;
+ else if (version == sizeof(struct kld_file_stat))
+ version_num = 2;
+ else
return (EINVAL);
#ifdef MAC
@@ -1140,6 +1179,7 @@ kldstat(struct thread *td, struct kldstat_args *uap)
return (ENOENT);
}
+ /* Version 1 fields: */
namelen = strlen(lf->filename) + 1;
if (namelen > MAXPATHLEN)
namelen = MAXPATHLEN;
@@ -1148,6 +1188,13 @@ kldstat(struct thread *td, struct kldstat_args *uap)
stat.id = lf->id;
stat.address = lf->address;
stat.size = lf->size;
+ if (version_num > 1) {
+ /* Version 2 fields: */
+ namelen = strlen(lf->pathname) + 1;
+ if (namelen > MAXPATHLEN)
+ namelen = MAXPATHLEN;
+ bcopy(lf->pathname, &stat.pathname[0], namelen);
+ }
KLD_UNLOCK();
td->td_retval[0] = 0;
diff --git a/sys/kern/kern_malloc.c b/sys/kern/kern_malloc.c
index 20c9fff09dfe..fc7e79409135 100644
--- a/sys/kern/kern_malloc.c
+++ b/sys/kern/kern_malloc.c
@@ -46,6 +46,7 @@
__FBSDID("$FreeBSD$");
#include "opt_ddb.h"
+#include "opt_kdtrace.h"
#include "opt_vm.h"
#include <sys/param.h>
@@ -86,6 +87,12 @@ __FBSDID("$FreeBSD$");
#include <ddb/ddb.h>
+#ifdef KDTRACE_HOOKS
+#include <sys/dtrace_bsd.h>
+
+dtrace_malloc_probe_func_t dtrace_malloc_probe;
+#endif
+
/*
* When realloc() is called, if the new size is sufficiently smaller than
* the old size, realloc() will allocate a new, smaller block to avoid
@@ -255,6 +262,17 @@ malloc_type_zone_allocated(struct malloc_type *mtp, unsigned long size,
}
if (zindx != -1)
mtsp->mts_size |= 1 << zindx;
+
+#ifdef KDTRACE_HOOKS
+ if (dtrace_malloc_probe != NULL) {
+ uint32_t probe_id = mtip->mti_probes[DTMALLOC_PROBE_MALLOC];
+ if (probe_id != 0)
+ (dtrace_malloc_probe)(probe_id,
+ (uintptr_t) mtp, (uintptr_t) mtip,
+ (uintptr_t) mtsp, size, zindx);
+ }
+#endif
+
critical_exit();
}
@@ -283,6 +301,17 @@ malloc_type_freed(struct malloc_type *mtp, unsigned long size)
mtsp = &mtip->mti_stats[curcpu];
mtsp->mts_memfreed += size;
mtsp->mts_numfrees++;
+
+#ifdef KDTRACE_HOOKS
+ if (dtrace_malloc_probe != NULL) {
+ uint32_t probe_id = mtip->mti_probes[DTMALLOC_PROBE_FREE];
+ if (probe_id != 0)
+ (dtrace_malloc_probe)(probe_id,
+ (uintptr_t) mtp, (uintptr_t) mtip,
+ (uintptr_t) mtsp, size, 0);
+ }
+#endif
+
critical_exit();
}
@@ -804,6 +833,40 @@ SYSCTL_PROC(_kern, OID_AUTO, malloc_stats, CTLFLAG_RD|CTLTYPE_STRUCT,
SYSCTL_INT(_kern, OID_AUTO, malloc_count, CTLFLAG_RD, &kmemcount, 0,
"Count of kernel malloc types");
+void
+malloc_type_list(malloc_type_list_func_t *func, void *arg)
+{
+ struct malloc_type *mtp, **bufmtp;
+ int count, i;
+ size_t buflen;
+
+ mtx_lock(&malloc_mtx);
+restart:
+ mtx_assert(&malloc_mtx, MA_OWNED);
+ count = kmemcount;
+ mtx_unlock(&malloc_mtx);
+
+ buflen = sizeof(struct malloc_type *) * count;
+ bufmtp = malloc(buflen, M_TEMP, M_WAITOK);
+
+ mtx_lock(&malloc_mtx);
+
+ if (count < kmemcount) {
+ free(bufmtp, M_TEMP);
+ goto restart;
+ }
+
+ for (mtp = kmemstatistics, i = 0; mtp != NULL; mtp = mtp->ks_next, i++)
+ bufmtp[i] = mtp;
+
+ mtx_unlock(&malloc_mtx);
+
+ for (i = 0; i < count; i++)
+ (func)(bufmtp[i], arg);
+
+ free(bufmtp, M_TEMP);
+}
+
#ifdef DDB
DB_SHOW_COMMAND(malloc, db_show_malloc)
{
diff --git a/sys/kern/kern_proc.c b/sys/kern/kern_proc.c
index 20604a6635bc..6debfe2c5d4e 100644
--- a/sys/kern/kern_proc.c
+++ b/sys/kern/kern_proc.c
@@ -33,6 +33,7 @@
__FBSDID("$FreeBSD$");
#include "opt_ddb.h"
+#include "opt_kdtrace.h"
#include "opt_ktrace.h"
#include "opt_kstack_pages.h"
#include "opt_stack.h"
@@ -54,6 +55,7 @@ __FBSDID("$FreeBSD$");
#include <sys/sysctl.h>
#include <sys/filedesc.h>
#include <sys/tty.h>
+#include <sys/sdt.h>
#include <sys/signalvar.h>
#include <sys/sx.h>
#include <sys/user.h>
@@ -76,6 +78,35 @@ __FBSDID("$FreeBSD$");
#include <vm/vm_object.h>
#include <vm/uma.h>
+SDT_PROVIDER_DEFINE(proc);
+SDT_PROBE_DEFINE(proc, kernel, ctor, entry);
+SDT_PROBE_ARGTYPE(proc, kernel, ctor, entry, 0, "struct proc *");
+SDT_PROBE_ARGTYPE(proc, kernel, ctor, entry, 1, "int");
+SDT_PROBE_ARGTYPE(proc, kernel, ctor, entry, 2, "void *");
+SDT_PROBE_ARGTYPE(proc, kernel, ctor, entry, 3, "int");
+SDT_PROBE_DEFINE(proc, kernel, ctor, return);
+SDT_PROBE_ARGTYPE(proc, kernel, ctor, return, 0, "struct proc *");
+SDT_PROBE_ARGTYPE(proc, kernel, ctor, return, 1, "int");
+SDT_PROBE_ARGTYPE(proc, kernel, ctor, return, 2, "void *");
+SDT_PROBE_ARGTYPE(proc, kernel, ctor, return, 3, "int");
+SDT_PROBE_DEFINE(proc, kernel, dtor, entry);
+SDT_PROBE_ARGTYPE(proc, kernel, dtor, entry, 0, "struct proc *");
+SDT_PROBE_ARGTYPE(proc, kernel, dtor, entry, 1, "int");
+SDT_PROBE_ARGTYPE(proc, kernel, dtor, entry, 2, "void *");
+SDT_PROBE_ARGTYPE(proc, kernel, dtor, entry, 3, "struct thread *");
+SDT_PROBE_DEFINE(proc, kernel, dtor, return);
+SDT_PROBE_ARGTYPE(proc, kernel, dtor, return, 0, "struct proc *");
+SDT_PROBE_ARGTYPE(proc, kernel, dtor, return, 1, "int");
+SDT_PROBE_ARGTYPE(proc, kernel, dtor, return, 2, "void *");
+SDT_PROBE_DEFINE(proc, kernel, init, entry);
+SDT_PROBE_ARGTYPE(proc, kernel, init, entry, 0, "struct proc *");
+SDT_PROBE_ARGTYPE(proc, kernel, init, entry, 1, "int");
+SDT_PROBE_ARGTYPE(proc, kernel, init, entry, 2, "int");
+SDT_PROBE_DEFINE(proc, kernel, init, return);
+SDT_PROBE_ARGTYPE(proc, kernel, init, return, 0, "struct proc *");
+SDT_PROBE_ARGTYPE(proc, kernel, init, return, 1, "int");
+SDT_PROBE_ARGTYPE(proc, kernel, init, return, 2, "int");
+
MALLOC_DEFINE(M_PGRP, "pgrp", "process group header");
MALLOC_DEFINE(M_SESSION, "session", "session header");
static MALLOC_DEFINE(M_PROC, "proc", "Proc structures");
@@ -143,7 +174,9 @@ proc_ctor(void *mem, int size, void *arg, int flags)
struct proc *p;
p = (struct proc *)mem;
+ SDT_PROBE(proc, kernel, ctor, entry, p, size, arg, flags, 0);
EVENTHANDLER_INVOKE(process_ctor, p);
+ SDT_PROBE(proc, kernel, ctor, return, p, size, arg, flags, 0);
return (0);
}
@@ -158,7 +191,8 @@ proc_dtor(void *mem, int size, void *arg)
/* INVARIANTS checks go here */
p = (struct proc *)mem;
- td = FIRST_THREAD_IN_PROC(p);
+ td = FIRST_THREAD_IN_PROC(p);
+ SDT_PROBE(proc, kernel, dtor, entry, p, size, arg, td, 0);
if (td != NULL) {
#ifdef INVARIANTS
KASSERT((p->p_numthreads == 1),
@@ -177,6 +211,7 @@ proc_dtor(void *mem, int size, void *arg)
EVENTHANDLER_INVOKE(process_dtor, p);
if (p->p_ksi != NULL)
KASSERT(! KSI_ONQ(p->p_ksi), ("SIGCHLD queue"));
+ SDT_PROBE(proc, kernel, dtor, return, p, size, arg, 0, 0);
}
/*
@@ -188,6 +223,7 @@ proc_init(void *mem, int size, int flags)
struct proc *p;
p = (struct proc *)mem;
+ SDT_PROBE(proc, kernel, init, entry, p, size, flags, 0, 0);
p->p_sched = (struct p_sched *)&p[1];
bzero(&p->p_mtx, sizeof(struct mtx));
mtx_init(&p->p_mtx, "process lock", NULL, MTX_DEF | MTX_DUPOK);
@@ -195,6 +231,7 @@ proc_init(void *mem, int size, int flags)
TAILQ_INIT(&p->p_threads); /* all threads in proc */
EVENTHANDLER_INVOKE(process_init, p);
p->p_stats = pstats_alloc();
+ SDT_PROBE(proc, kernel, init, return, p, size, flags, 0, 0);
return (0);
}
diff --git a/sys/kern/kern_sdt.c b/sys/kern/kern_sdt.c
new file mode 100644
index 000000000000..495a6a05bc3e
--- /dev/null
+++ b/sys/kern/kern_sdt.c
@@ -0,0 +1,273 @@
+/*-
+ * Copyright 2006-2008 John Birrell <jb@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ *
+ * Backend for the Statically Defined Tracing (SDT) kernel support. This is
+ * required to allow a module to load even though DTrace kernel support may
+ * not be present. A module may be built with SDT probes in it which are
+ * registered and deregistered via SYSINIT/SYSUNINIT.
+ *
+ */
+
+#include "opt_kdtrace.h"
+
+#include <sys/cdefs.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/linker.h>
+#include <sys/lock.h>
+#include <sys/proc.h>
+#include <sys/sx.h>
+#include <sys/sdt.h>
+
+/*
+ * This is the list of statically defined tracing providers.
+ */
+static TAILQ_HEAD(sdt_provider_list_head, sdt_provider) sdt_provider_list;
+
+/*
+ * Mutex to serialise access to the SDT provider list.
+ */
+static struct sx sdt_sx;
+
+/*
+ * Hook for the DTrace probe function. The 'sdt' provider will set this
+ * to dtrace_probe when it loads.
+ */
+sdt_probe_func_t sdt_probe_func = sdt_probe_stub;
+
+/*
+ * This is a stub for probe calls in case kernel DTrace support isn't
+ * compiled in. It should never get called because there is no DTrace
+ * support to enable it.
+ */
+void
+sdt_probe_stub(u_int32_t id, uintptr_t arg0, uintptr_t arg1,
+ uintptr_t arg2, uintptr_t arg3, uintptr_t arg4)
+{
+ printf("sdt_probe_stub: Why did this get called?\n");
+}
+
+/*
+ * Called from SYSINIT to register a provider.
+ */
+void
+sdt_provider_register(void *arg)
+{
+ struct sdt_provider *prov = arg;
+
+ sx_xlock(&sdt_sx);
+
+ TAILQ_INSERT_TAIL(&sdt_provider_list, prov, prov_entry);
+
+ TAILQ_INIT(&prov->probe_list);
+
+ sx_xunlock(&sdt_sx);
+}
+
+/*
+ * Called from SYSUNINIT to de-register a provider.
+ */
+void
+sdt_provider_deregister(void *arg)
+{
+ struct sdt_provider *prov = arg;
+
+ sx_xlock(&sdt_sx);
+
+ TAILQ_REMOVE(&sdt_provider_list, prov, prov_entry);
+
+ sx_xunlock(&sdt_sx);
+}
+
+/*
+ * Called from SYSINIT to register a statically defined trace probe.
+ */
+void
+sdt_probe_register(void *arg)
+{
+ struct sdt_probe *probe = arg;
+
+ /*
+ * Check the reference structure version. Only version 1 is
+ * supported at the moment.
+ */
+ if (probe->version != sizeof(struct sdt_probe)) {
+ printf("%s:%s:%s has version %d when %d required\n", probe->mod, probe->func, probe->name, probe->version, (int) sizeof(struct sdt_probe));
+ return;
+ }
+
+ sx_xlock(&sdt_sx);
+
+ TAILQ_INSERT_TAIL(&probe->prov->probe_list, probe, probe_entry);
+
+ TAILQ_INIT(&probe->argtype_list);
+
+ probe->state = SDT_INIT;
+
+ sx_xunlock(&sdt_sx);
+}
+
+/*
+ * Called from SYSUNINIT to de-register a statically defined trace probe.
+ */
+void
+sdt_probe_deregister(void *arg)
+{
+ struct sdt_probe *probe = arg;
+
+ sx_xlock(&sdt_sx);
+
+ if (probe->state == SDT_INIT) {
+ TAILQ_REMOVE(&probe->prov->probe_list, probe, probe_entry);
+ probe->state = SDT_UNINIT;
+ }
+
+ sx_xunlock(&sdt_sx);
+}
+
+/*
+ * Called from SYSINIT to register a statically defined trace probe argument.
+ */
+void
+sdt_argtype_register(void *arg)
+{
+ struct sdt_argtype *argtype = arg;
+
+ sx_xlock(&sdt_sx);
+
+ TAILQ_INSERT_TAIL(&argtype->probe->argtype_list, argtype, argtype_entry);
+
+ argtype->probe->n_args++;
+
+ sx_xunlock(&sdt_sx);
+}
+
+/*
+ * Called from SYSUNINIT to de-register a statically defined trace probe argument.
+ */
+void
+sdt_argtype_deregister(void *arg)
+{
+ struct sdt_argtype *argtype = arg;
+
+ sx_xlock(&sdt_sx);
+
+ TAILQ_REMOVE(&argtype->probe->argtype_list, argtype, argtype_entry);
+
+ sx_xunlock(&sdt_sx);
+}
+
+static void
+sdt_init(void *arg)
+{
+ sx_init_flags(&sdt_sx, "Statically Defined Tracing", SX_NOWITNESS);
+
+ TAILQ_INIT(&sdt_provider_list);
+}
+
+SYSINIT(sdt, SI_SUB_KDTRACE, SI_ORDER_FIRST, sdt_init, NULL);
+
+static void
+sdt_uninit(void *arg)
+{
+ sx_destroy(&sdt_sx);
+}
+
+SYSUNINIT(sdt, SI_SUB_KDTRACE, SI_ORDER_FIRST, sdt_uninit, NULL);
+
+/*
+ * List statically defined tracing providers.
+ */
+int
+sdt_provider_listall(sdt_provider_listall_func_t callback_func,void *arg)
+{
+ int error = 0;
+ struct sdt_provider *prov;
+
+ sx_xlock(&sdt_sx);
+
+ TAILQ_FOREACH(prov, &sdt_provider_list, prov_entry) {
+ if ((error = callback_func(prov, arg)) != 0)
+ break;
+ }
+
+ sx_xunlock(&sdt_sx);
+
+ return (error);
+}
+
+/*
+ * List statically defined tracing probes.
+ */
+int
+sdt_probe_listall(struct sdt_provider *prov,
+ sdt_probe_listall_func_t callback_func,void *arg)
+{
+ int error = 0;
+ int locked;
+ struct sdt_probe *probe;
+
+ locked = sx_xlocked(&sdt_sx);
+ if (!locked)
+ sx_xlock(&sdt_sx);
+
+ TAILQ_FOREACH(probe, &prov->probe_list, probe_entry) {
+ if ((error = callback_func(probe, arg)) != 0)
+ break;
+ }
+
+ if (!locked)
+ sx_xunlock(&sdt_sx);
+
+ return (error);
+}
+
+/*
+ * List statically defined tracing probe arguments.
+ */
+int
+sdt_argtype_listall(struct sdt_probe *probe,
+ sdt_argtype_listall_func_t callback_func,void *arg)
+{
+ int error = 0;
+ int locked;
+ struct sdt_argtype *argtype;
+
+ locked = sx_xlocked(&sdt_sx);
+ if (!locked)
+ sx_xlock(&sdt_sx);
+
+ TAILQ_FOREACH(argtype, &probe->argtype_list, argtype_entry) {
+ if ((error = callback_func(argtype, arg)) != 0)
+ break;
+ }
+
+ if (!locked)
+ sx_xunlock(&sdt_sx);
+
+ return (error);
+}
diff --git a/sys/kern/link_elf.c b/sys/kern/link_elf.c
index e32a0a41027e..99e0ff5edfe0 100644
--- a/sys/kern/link_elf.c
+++ b/sys/kern/link_elf.c
@@ -27,6 +27,7 @@
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
+#include "opt_ddb.h"
#include "opt_gdb.h"
#include "opt_mac.h"
@@ -62,6 +63,10 @@ __FBSDID("$FreeBSD$");
#include <sys/link_elf.h>
+#ifdef DDB_CTF
+#include <net/zlib.h>
+#endif
+
#include "linker_if.h"
#define MAXSEGS 4
@@ -98,11 +103,18 @@ typedef struct elf_file {
long ddbstrcnt; /* number of bytes in string table */
caddr_t symbase; /* malloc'ed symbold base */
caddr_t strbase; /* malloc'ed string base */
+ caddr_t ctftab; /* CTF table */
+ long ctfcnt; /* number of bytes in CTF table */
+ caddr_t ctfoff; /* CTF offset table */
+ caddr_t typoff; /* Type offset table */
+ long typlen; /* Number of type entries. */
#ifdef GDB
struct link_map gdb; /* hooks for gdb */
#endif
} *elf_file_t;
+#include <kern/kern_ctf.c>
+
static int link_elf_link_common_finish(linker_file_t);
static int link_elf_link_preload(linker_class_t cls,
const char*, linker_file_t*);
@@ -121,6 +133,9 @@ static int link_elf_lookup_set(linker_file_t, const char *,
static int link_elf_each_function_name(linker_file_t,
int (*)(const char *, void *),
void *);
+static int link_elf_each_function_nameval(linker_file_t,
+ linker_function_nameval_callback_t,
+ void *);
static void link_elf_reloc_local(linker_file_t);
static Elf_Addr elf_lookup(linker_file_t lf, Elf_Size symidx, int deps);
@@ -134,6 +149,8 @@ static kobj_method_t link_elf_methods[] = {
KOBJMETHOD(linker_link_preload_finish, link_elf_link_preload_finish),
KOBJMETHOD(linker_lookup_set, link_elf_lookup_set),
KOBJMETHOD(linker_each_function_name, link_elf_each_function_name),
+ KOBJMETHOD(linker_each_function_nameval, link_elf_each_function_nameval),
+ KOBJMETHOD(linker_ctf_get, link_elf_ctf_get),
{ 0, 0 }
};
@@ -580,7 +597,7 @@ link_elf_load_file(linker_class_t cls, const char* filename,
goto out;
}
#ifdef MAC
- error = mac_check_kld_load(curthread->td_ucred, nd.ni_vp);
+ error = mac_kld_check_load(curthread->td_ucred, nd.ni_vp);
if (error) {
firstpage = NULL;
goto out;
@@ -915,6 +932,12 @@ link_elf_unload_file(linker_file_t file)
free(ef->symbase, M_LINKER);
if (ef->strbase)
free(ef->strbase, M_LINKER);
+ if (ef->ctftab)
+ free(ef->ctftab, M_LINKER);
+ if (ef->ctfoff)
+ free(ef->ctfoff, M_LINKER);
+ if (ef->typoff)
+ free(ef->typoff, M_LINKER);
}
static void
@@ -1232,6 +1255,30 @@ link_elf_each_function_name(linker_file_t file,
return (0);
}
+static int
+link_elf_each_function_nameval(linker_file_t file,
+ linker_function_nameval_callback_t callback, void *opaque)
+{
+ linker_symval_t symval;
+ elf_file_t ef = (elf_file_t)file;
+ const Elf_Sym* symp;
+ int i, error;
+
+ /* Exhaustive search */
+ for (i = 0, symp = ef->ddbsymtab; i < ef->ddbsymcnt; i++, symp++) {
+ if (symp->st_value != 0 &&
+ ELF_ST_TYPE(symp->st_info) == STT_FUNC) {
+ error = link_elf_symbol_values(file, (c_linker_sym_t) symp, &symval);
+ if (error)
+ return (error);
+ error = callback(file, i, &symval, opaque);
+ if (error)
+ return (error);
+ }
+ }
+ return (0);
+}
+
#ifdef __ia64__
/*
* Each KLD has its own GP. The GP value for each load module is given by
diff --git a/sys/kern/link_elf_obj.c b/sys/kern/link_elf_obj.c
index af6defaef80a..61aeebb2a475 100644
--- a/sys/kern/link_elf_obj.c
+++ b/sys/kern/link_elf_obj.c
@@ -58,6 +58,10 @@ __FBSDID("$FreeBSD$");
#include <sys/link_elf.h>
+#ifdef DDB_CTF
+#include <net/zlib.h>
+#endif
+
#include "linker_if.h"
typedef struct {
@@ -106,8 +110,16 @@ typedef struct elf_file {
caddr_t shstrtab; /* Section name string table */
long shstrcnt; /* number of bytes in string table */
+ caddr_t ctftab; /* CTF table */
+ long ctfcnt; /* number of bytes in CTF table */
+ caddr_t ctfoff; /* CTF offset table */
+ caddr_t typoff; /* Type offset table */
+ long typlen; /* Number of type entries. */
+
} *elf_file_t;
+#include <kern/kern_ctf.c>
+
static int link_elf_link_preload(linker_class_t cls,
const char *, linker_file_t *);
static int link_elf_link_preload_finish(linker_file_t);
@@ -124,6 +136,9 @@ static int link_elf_lookup_set(linker_file_t, const char *,
void ***, void ***, int *);
static int link_elf_each_function_name(linker_file_t,
int (*)(const char *, void *), void *);
+static int link_elf_each_function_nameval(linker_file_t,
+ linker_function_nameval_callback_t,
+ void *);
static void link_elf_reloc_local(linker_file_t);
static Elf_Addr elf_obj_lookup(linker_file_t lf, Elf_Size symidx, int deps);
@@ -138,6 +153,8 @@ static kobj_method_t link_elf_methods[] = {
KOBJMETHOD(linker_link_preload_finish, link_elf_link_preload_finish),
KOBJMETHOD(linker_lookup_set, link_elf_lookup_set),
KOBJMETHOD(linker_each_function_name, link_elf_each_function_name),
+ KOBJMETHOD(linker_each_function_nameval, link_elf_each_function_nameval),
+ KOBJMETHOD(linker_ctf_get, link_elf_ctf_get),
{ 0, 0 }
};
@@ -417,7 +434,7 @@ link_elf_load_file(linker_class_t cls, const char *filename,
goto out;
}
#ifdef MAC
- error = mac_check_kld_load(td->td_ucred, nd.ni_vp);
+ error = mac_kld_check_load(td->td_ucred, nd.ni_vp);
if (error) {
goto out;
}
@@ -820,6 +837,12 @@ link_elf_unload_file(linker_file_t file)
free(ef->relatab, M_LINKER);
if (ef->progtab)
free(ef->progtab, M_LINKER);
+ if (ef->ctftab)
+ free(ef->ctftab, M_LINKER);
+ if (ef->ctfoff)
+ free(ef->ctfoff, M_LINKER);
+ if (ef->typoff)
+ free(ef->typoff, M_LINKER);
if (file->filename != NULL)
preload_delete_name(file->filename);
/* XXX reclaim module memory? */
@@ -852,6 +875,12 @@ link_elf_unload_file(linker_file_t file)
free(ef->ddbstrtab, M_LINKER);
if (ef->shstrtab)
free(ef->shstrtab, M_LINKER);
+ if (ef->ctftab)
+ free(ef->ctftab, M_LINKER);
+ if (ef->ctfoff)
+ free(ef->ctfoff, M_LINKER);
+ if (ef->typoff)
+ free(ef->typoff, M_LINKER);
}
static const char *
@@ -1073,6 +1102,30 @@ link_elf_each_function_name(linker_file_t file,
return (0);
}
+static int
+link_elf_each_function_nameval(linker_file_t file,
+ linker_function_nameval_callback_t callback, void *opaque)
+{
+ linker_symval_t symval;
+ elf_file_t ef = (elf_file_t)file;
+ const Elf_Sym* symp;
+ int i, error;
+
+ /* Exhaustive search */
+ for (i = 0, symp = ef->ddbsymtab; i < ef->ddbsymcnt; i++, symp++) {
+ if (symp->st_value != 0 &&
+ ELF_ST_TYPE(symp->st_info) == STT_FUNC) {
+ error = link_elf_symbol_values(file, (c_linker_sym_t) symp, &symval);
+ if (error)
+ return (error);
+ error = callback(file, i, &symval, opaque);
+ if (error)
+ return (error);
+ }
+ }
+ return (0);
+}
+
/*
* Symbol lookup function that can be used when the symbol index is known (ie
* in relocations). It uses the symbol index instead of doing a fully fledged
diff --git a/sys/kern/linker_if.m b/sys/kern/linker_if.m
index 84d56d2d6f7e..aadcf048bc4b 100644
--- a/sys/kern/linker_if.m
+++ b/sys/kern/linker_if.m
@@ -64,6 +64,17 @@ METHOD int each_function_name {
};
#
+# Call the callback with each specified function and it's value
+# defined in the file.
+# Stop and return the error if the callback returns an error.
+#
+METHOD int each_function_nameval {
+ linker_file_t file;
+ linker_function_nameval_callback_t callback;
+ void* opaque;
+};
+
+#
# Search for a linker set in a file. Return a pointer to the first
# entry (which is itself a pointer), and the number of entries.
# "stop" points to the entry beyond the last valid entry.
@@ -85,6 +96,15 @@ METHOD void unload {
};
#
+# Load CTF data if necessary and if there is a .SUNW_ctf section
+# in the ELF file, returning info in the linker CTF structure.
+#
+METHOD int ctf_get {
+ linker_file_t file;
+ linker_ctf_t *lc;
+};
+
+#
# Load a file, returning the new linker_file_t in *result. If
# the class does not recognise the file type, zero should be
# returned, without modifying *result. If the file is
diff --git a/sys/kern/makesyscalls.sh b/sys/kern/makesyscalls.sh
index e8722abd0767..0b6bbaee7952 100644
--- a/sys/kern/makesyscalls.sh
+++ b/sys/kern/makesyscalls.sh
@@ -34,10 +34,11 @@ sysent="sysent.switch.$$"
sysinc="sysinc.switch.$$"
sysarg="sysarg.switch.$$"
sysprotoend="sysprotoend.$$"
+systracetmp="systrace.$$"
-trap "rm $sysaue $sysdcl $syscompat $syscompatdcl $syscompat4 $syscompat4dcl $syscompat6 $syscompat6dcl $sysent $sysinc $sysarg $sysprotoend" 0
+trap "rm $sysaue $sysdcl $syscompat $syscompatdcl $syscompat4 $syscompat4dcl $syscompat6 $syscompat6dcl $sysent $sysinc $sysarg $sysprotoend $systracetmp" 0
-touch $sysaue $sysdcl $syscompat $syscompatdcl $syscompat4 $syscompat4dcl $syscompat6 $syscompat6dcl $sysent $sysinc $sysarg $sysprotoend
+touch $sysaue $sysdcl $syscompat $syscompatdcl $syscompat4 $syscompat4dcl $syscompat6 $syscompat6dcl $sysent $sysinc $sysarg $sysprotoend $systracetmp
case $# in
0) echo "usage: $0 input-file <config-file>" 1>&2
@@ -82,6 +83,7 @@ s/\$//g
syshdr = \"$syshdr\"
sysmk = \"$sysmk\"
systrace = \"$systrace\"
+ systracetmp = \"$systracetmp\"
compat = \"$compat\"
compat4 = \"$compat4\"
compat6 = \"$compat6\"
@@ -160,6 +162,9 @@ s/\$//g
printf "\tint64_t *iarg = (int64_t *) uarg;\n" > systrace
printf "\tswitch (sysnum) {\n" > systrace
+ printf "static void\nsystrace_setargdesc(int sysnum, int ndx, char *desc, size_t descsz)\n{\n\tconst char *p = NULL;\n" > systracetmp
+ printf "\tswitch (sysnum) {\n" > systracetmp
+
next
}
NF == 0 || $1 ~ /^;/ {
@@ -317,10 +322,13 @@ s/\$//g
|| $3 == "NOIMPL" || $3 == "NOSTD" {
parseline()
printf("\t/* %s */\n\tcase %d: {\n", funcname, syscall) > systrace
+ printf("\t/* %s */\n\tcase %d:\n", funcname, syscall) > systracetmp
if (argc > 0) {
+ printf("\t\tswitch(ndx) {\n") > systracetmp
printf("\t\tstruct %s *p = params;\n", argalias) > systrace
for (i = 1; i <= argc; i++) {
- if (index(argtype[i], "*") > 0 || argtype[i] == "caddr_t")
+ printf("\t\tcase %d:\n\t\t\tp = \"%s\";\n\t\t\tbreak;\n", i - 1, argtype[i]) > systracetmp
+ if (index(argtype[i], "*") > 0 || argtype[i] == "caddr_t" || argtype[i] == "l_handler_t")
printf("\t\tuarg[%d] = (intptr_t) p->%s; /* %s */\n", \
i - 1, \
argname[i], argtype[i]) > systrace
@@ -333,8 +341,10 @@ s/\$//g
i - 1, \
argname[i], argtype[i]) > systrace
}
+ printf("\t\tdefault:\n\t\t\tbreak;\n\t\t};\n") > systracetmp
}
printf("\t\t*n_args = %d;\n\t\tbreak;\n\t}\n", argc) > systrace
+ printf("\t\tbreak;\n") > systracetmp
if ((!nosys || funcname != "nosys") && \
(funcname != "lkmnosys") && (funcname != "lkmressys")) {
if (argc != 0 && $3 != "NOARGS" && $3 != "NOPROTO") {
@@ -529,6 +539,7 @@ s/\$//g
printf("#define\t%sMAXSYSCALL\t%d\n", syscallprefix, syscall) \
> syshdr
printf "\tdefault:\n\t\t*n_args = 0;\n\t\tbreak;\n\t};\n}\n" > systrace
+ printf "\tdefault:\n\t\tbreak;\n\t};\n\tif (p != NULL)\n\t\tstrlcpy(desc, p, descsz);\n}\n" > systracetmp
} '
cat $sysinc $sysent >> $syssw
@@ -537,4 +548,5 @@ cat $sysarg $sysdcl \
$syscompat4 $syscompat4dcl \
$syscompat6 $syscompat6dcl \
$sysaue $sysprotoend > $sysproto
+cat $systracetmp >> $systrace
diff --git a/sys/kern/sched_4bsd.c b/sys/kern/sched_4bsd.c
index f844e60539d2..56f69bd34af8 100644
--- a/sys/kern/sched_4bsd.c
+++ b/sys/kern/sched_4bsd.c
@@ -36,6 +36,7 @@
__FBSDID("$FreeBSD$");
#include "opt_hwpmc_hooks.h"
+#include "opt_kdtrace.h"
#include <sys/param.h>
#include <sys/systm.h>
@@ -60,6 +61,12 @@ __FBSDID("$FreeBSD$");
#include <sys/pmckern.h>
#endif
+#ifdef KDTRACE_HOOKS
+#include <sys/dtrace_bsd.h>
+int dtrace_vtime_active;
+dtrace_vtime_switch_func_t dtrace_vtime_switch_func;
+#endif
+
/*
* INVERSE_ESTCPU_WEIGHT is only suitable for statclock() frequencies in
* the range 100-256 Hz (approximately).
@@ -890,6 +897,15 @@ sched_switch(struct thread *td, struct thread *newtd, int flags)
PMC_SWITCH_CONTEXT(td, PMC_FN_CSW_OUT);
#endif
+#ifdef KDTRACE_HOOKS
+ /*
+ * If DTrace has set the active vtime enum to anything
+ * other than INACTIVE (0), then it should have set the
+ * function to call.
+ */
+ if (dtrace_vtime_active)
+ (*dtrace_vtime_switch_func)(newtd);
+#endif
/* I feel sleepy */
cpu_switch(td, newtd, td->td_lock);
/*
diff --git a/sys/kern/sched_ule.c b/sys/kern/sched_ule.c
index bd8e655fcb7a..bf8394c33028 100644
--- a/sys/kern/sched_ule.c
+++ b/sys/kern/sched_ule.c
@@ -39,6 +39,7 @@
__FBSDID("$FreeBSD$");
#include "opt_hwpmc_hooks.h"
+#include "opt_kdtrace.h"
#include "opt_sched.h"
#include <sys/param.h>
@@ -69,6 +70,12 @@ __FBSDID("$FreeBSD$");
#include <sys/pmckern.h>
#endif
+#ifdef KDTRACE_HOOKS
+#include <sys/dtrace_bsd.h>
+int dtrace_vtime_active;
+dtrace_vtime_switch_func_t dtrace_vtime_switch_func;
+#endif
+
#include <machine/cpu.h>
#include <machine/smp.h>
@@ -1918,6 +1925,16 @@ sched_switch(struct thread *td, struct thread *newtd, int flags)
PMC_SWITCH_CONTEXT(td, PMC_FN_CSW_OUT);
#endif
TDQ_LOCKPTR(tdq)->mtx_lock = (uintptr_t)newtd;
+
+#ifdef KDTRACE_HOOKS
+ /*
+ * If DTrace has set the active vtime enum to anything
+ * other than INACTIVE (0), then it should have set the
+ * function to call.
+ */
+ if (dtrace_vtime_active)
+ (*dtrace_vtime_switch_func)(newtd);
+#endif
cpu_switch(td, newtd, mtx);
/*
* We may return from cpu_switch on a different cpu. However,
diff --git a/sys/kern/subr_smp.c b/sys/kern/subr_smp.c
index 75378f8dad36..375aed7eb4ab 100644
--- a/sys/kern/subr_smp.c
+++ b/sys/kern/subr_smp.c
@@ -74,6 +74,9 @@ u_int mp_maxid;
SYSCTL_NODE(_kern, OID_AUTO, smp, CTLFLAG_RD, NULL, "Kernel SMP");
+SYSCTL_INT(_kern_smp, OID_AUTO, maxid, CTLFLAG_RD, &mp_maxid, 0,
+ "Max CPU ID.");
+
SYSCTL_INT(_kern_smp, OID_AUTO, maxcpus, CTLFLAG_RD, &mp_maxcpus, 0,
"Max number of CPUs that the system was compiled for.");
@@ -104,10 +107,11 @@ SYSCTL_INT(_kern_smp, OID_AUTO, forward_roundrobin_enabled, CTLFLAG_RW,
"Forwarding of roundrobin to all other CPUs");
/* Variables needed for SMP rendezvous. */
-static void (*smp_rv_setup_func)(void *arg);
-static void (*smp_rv_action_func)(void *arg);
-static void (*smp_rv_teardown_func)(void *arg);
-static void *smp_rv_func_arg;
+static volatile cpumask_t smp_rv_cpumask;
+static void (*volatile smp_rv_setup_func)(void *arg);
+static void (*volatile smp_rv_action_func)(void *arg);
+static void (*volatile smp_rv_teardown_func)(void *arg);
+static void * volatile smp_rv_func_arg;
static volatile int smp_rv_waiters[3];
/*
@@ -286,6 +290,14 @@ restart_cpus(cpumask_t map)
return 1;
}
+void
+smp_no_rendevous_barrier(void *dummy)
+{
+#ifdef SMP
+ KASSERT((!smp_started),("smp_no_rendevous called and smp is started"));
+#endif
+}
+
/*
* All-CPU rendezvous. CPUs are signalled, all execute the setup function
* (if specified), rendezvous, execute the action function (if specified),
@@ -298,41 +310,57 @@ restart_cpus(cpumask_t map)
void
smp_rendezvous_action(void)
{
+ cpumask_t map = smp_rv_cpumask;
+ int i, ncpus = 0;
+ void* local_func_arg = smp_rv_func_arg;
+ void (*local_setup_func)(void*) = smp_rv_setup_func;
+ void (*local_action_func)(void*) = smp_rv_action_func;
+ void (*local_teardown_func)(void*) = smp_rv_teardown_func;
+
+ for (i = 0; i < MAXCPU; i++)
+ if (((1 << i) & map) != 0 && pcpu_find(i) != NULL)
+ ncpus++;
/* Ensure we have up-to-date values. */
atomic_add_acq_int(&smp_rv_waiters[0], 1);
- while (smp_rv_waiters[0] < mp_ncpus)
+ while (smp_rv_waiters[0] < ncpus)
cpu_spinwait();
/* setup function */
- if (smp_rv_setup_func != NULL)
- smp_rv_setup_func(smp_rv_func_arg);
+ if (local_setup_func != smp_no_rendevous_barrier) {
+ if (smp_rv_setup_func != NULL)
+ smp_rv_setup_func(smp_rv_func_arg);
- /* spin on entry rendezvous */
- atomic_add_int(&smp_rv_waiters[1], 1);
- while (smp_rv_waiters[1] < mp_ncpus)
- cpu_spinwait();
+ /* spin on entry rendezvous */
+ atomic_add_int(&smp_rv_waiters[1], 1);
+ while (smp_rv_waiters[1] < ncpus)
+ cpu_spinwait();
+ }
/* action function */
- if (smp_rv_action_func != NULL)
- smp_rv_action_func(smp_rv_func_arg);
+ if (local_action_func != NULL)
+ local_action_func(local_func_arg);
/* spin on exit rendezvous */
atomic_add_int(&smp_rv_waiters[2], 1);
- while (smp_rv_waiters[2] < mp_ncpus)
+ if (local_teardown_func == smp_no_rendevous_barrier)
+ return;
+ while (smp_rv_waiters[2] < ncpus)
cpu_spinwait();
/* teardown function */
- if (smp_rv_teardown_func != NULL)
- smp_rv_teardown_func(smp_rv_func_arg);
+ if (local_teardown_func != NULL)
+ local_teardown_func(local_func_arg);
}
void
-smp_rendezvous(void (* setup_func)(void *),
- void (* action_func)(void *),
- void (* teardown_func)(void *),
- void *arg)
+smp_rendezvous_cpus(cpumask_t map,
+ void (* setup_func)(void *),
+ void (* action_func)(void *),
+ void (* teardown_func)(void *),
+ void *arg)
{
+ int i, ncpus = 0;
if (!smp_started) {
if (setup_func != NULL)
@@ -343,11 +371,16 @@ smp_rendezvous(void (* setup_func)(void *),
teardown_func(arg);
return;
}
+
+ for (i = 0; i < MAXCPU; i++)
+ if (((1 << i) & map) != 0 && pcpu_find(i) != NULL)
+ ncpus++;
/* obtain rendezvous lock */
mtx_lock_spin(&smp_ipi_mtx);
/* set static function pointers */
+ smp_rv_cpumask = map & ~(1 << curcpu);
smp_rv_setup_func = setup_func;
smp_rv_action_func = action_func;
smp_rv_teardown_func = teardown_func;
@@ -357,14 +390,29 @@ smp_rendezvous(void (* setup_func)(void *),
atomic_store_rel_int(&smp_rv_waiters[0], 0);
/* signal other processors, which will enter the IPI with interrupts off */
- ipi_all_but_self(IPI_RENDEZVOUS);
+ ipi_selected(map, IPI_RENDEZVOUS);
- /* call executor function */
- smp_rendezvous_action();
+ /* Check if the current CPU is in the map */
+ if ((map & (1 << curcpu)) != 0)
+ /* call executor function for the current CPU */
+ smp_rendezvous_action();
+
+ if (teardown_func == smp_no_rendevous_barrier)
+ while (atomic_load_acq_int(&smp_rv_waiters[2]) < ncpus)
+ cpu_spinwait();
/* release lock */
mtx_unlock_spin(&smp_ipi_mtx);
}
+
+void
+smp_rendezvous(void (* setup_func)(void *),
+ void (* action_func)(void *),
+ void (* teardown_func)(void *),
+ void *arg)
+{
+ smp_rendezvous_cpus(all_cpus, setup_func, action_func, teardown_func, arg);
+}
#else /* !SMP */
/*
@@ -383,9 +431,24 @@ SYSINIT(cpu_mp_setvariables, SI_SUB_TUNABLES, SI_ORDER_FIRST,
mp_setvariables_for_up, NULL);
void
-smp_rendezvous(void (* setup_func)(void *),
- void (* action_func)(void *),
- void (* teardown_func)(void *),
+smp_rendezvous_cpus(cpumask_t map,
+ void (*setup_func)(void *),
+ void (*action_func)(void *),
+ void (*teardown_func)(void *),
+ void *arg)
+{
+ if (setup_func != NULL)
+ setup_func(arg);
+ if (action_func != NULL)
+ action_func(arg);
+ if (teardown_func != NULL)
+ teardown_func(arg);
+}
+
+void
+smp_rendezvous(void (*setup_func)(void *),
+ void (*action_func)(void *),
+ void (*teardown_func)(void *),
void *arg)
{
diff --git a/sys/kern/systrace_args.c b/sys/kern/systrace_args.c
index 917c21870f2d..de89d3f03cf0 100644
--- a/sys/kern/systrace_args.c
+++ b/sys/kern/systrace_args.c
@@ -2941,3 +2941,4720 @@ systrace_args(int sysnum, void *params, u_int64_t *uarg, int *n_args)
break;
};
}
+static void
+systrace_setargdesc(int sysnum, int ndx, char *desc, size_t descsz)
+{
+ const char *p = NULL;
+ switch (sysnum) {
+ /* nosys */
+ case 0:
+ break;
+ /* sys_exit */
+ case 1:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* fork */
+ case 2:
+ break;
+ /* read */
+ case 3:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "void *";
+ break;
+ case 2:
+ p = "size_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* write */
+ case 4:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "const void *";
+ break;
+ case 2:
+ p = "size_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* open */
+ case 5:
+ switch(ndx) {
+ case 0:
+ p = "char *";
+ break;
+ case 1:
+ p = "int";
+ break;
+ case 2:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* close */
+ case 6:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* wait4 */
+ case 7:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "int *";
+ break;
+ case 2:
+ p = "int";
+ break;
+ case 3:
+ p = "struct rusage *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* link */
+ case 9:
+ switch(ndx) {
+ case 0:
+ p = "char *";
+ break;
+ case 1:
+ p = "char *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* unlink */
+ case 10:
+ switch(ndx) {
+ case 0:
+ p = "char *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* chdir */
+ case 12:
+ switch(ndx) {
+ case 0:
+ p = "char *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* fchdir */
+ case 13:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* mknod */
+ case 14:
+ switch(ndx) {
+ case 0:
+ p = "char *";
+ break;
+ case 1:
+ p = "int";
+ break;
+ case 2:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* chmod */
+ case 15:
+ switch(ndx) {
+ case 0:
+ p = "char *";
+ break;
+ case 1:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* chown */
+ case 16:
+ switch(ndx) {
+ case 0:
+ p = "char *";
+ break;
+ case 1:
+ p = "int";
+ break;
+ case 2:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* obreak */
+ case 17:
+ switch(ndx) {
+ case 0:
+ p = "char *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* getpid */
+ case 20:
+ break;
+ /* mount */
+ case 21:
+ switch(ndx) {
+ case 0:
+ p = "char *";
+ break;
+ case 1:
+ p = "char *";
+ break;
+ case 2:
+ p = "int";
+ break;
+ case 3:
+ p = "caddr_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* unmount */
+ case 22:
+ switch(ndx) {
+ case 0:
+ p = "char *";
+ break;
+ case 1:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* setuid */
+ case 23:
+ switch(ndx) {
+ case 0:
+ p = "uid_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* getuid */
+ case 24:
+ break;
+ /* geteuid */
+ case 25:
+ break;
+ /* ptrace */
+ case 26:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "pid_t";
+ break;
+ case 2:
+ p = "caddr_t";
+ break;
+ case 3:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* recvmsg */
+ case 27:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "struct msghdr *";
+ break;
+ case 2:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* sendmsg */
+ case 28:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "struct msghdr *";
+ break;
+ case 2:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* recvfrom */
+ case 29:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "caddr_t";
+ break;
+ case 2:
+ p = "size_t";
+ break;
+ case 3:
+ p = "int";
+ break;
+ case 4:
+ p = "struct sockaddr *__restrict";
+ break;
+ case 5:
+ p = "__socklen_t *__restrict";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* accept */
+ case 30:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "struct sockaddr *__restrict";
+ break;
+ case 2:
+ p = "__socklen_t *__restrict";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* getpeername */
+ case 31:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "struct sockaddr *__restrict";
+ break;
+ case 2:
+ p = "__socklen_t *__restrict";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* getsockname */
+ case 32:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "struct sockaddr *__restrict";
+ break;
+ case 2:
+ p = "__socklen_t *__restrict";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* access */
+ case 33:
+ switch(ndx) {
+ case 0:
+ p = "char *";
+ break;
+ case 1:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* chflags */
+ case 34:
+ switch(ndx) {
+ case 0:
+ p = "char *";
+ break;
+ case 1:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* fchflags */
+ case 35:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* sync */
+ case 36:
+ break;
+ /* kill */
+ case 37:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* getppid */
+ case 39:
+ break;
+ /* dup */
+ case 41:
+ switch(ndx) {
+ case 0:
+ p = "u_int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* pipe */
+ case 42:
+ break;
+ /* getegid */
+ case 43:
+ break;
+ /* profil */
+ case 44:
+ switch(ndx) {
+ case 0:
+ p = "caddr_t";
+ break;
+ case 1:
+ p = "size_t";
+ break;
+ case 2:
+ p = "size_t";
+ break;
+ case 3:
+ p = "u_int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* ktrace */
+ case 45:
+ switch(ndx) {
+ case 0:
+ p = "const char *";
+ break;
+ case 1:
+ p = "int";
+ break;
+ case 2:
+ p = "int";
+ break;
+ case 3:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* getgid */
+ case 47:
+ break;
+ /* getlogin */
+ case 49:
+ switch(ndx) {
+ case 0:
+ p = "char *";
+ break;
+ case 1:
+ p = "u_int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* setlogin */
+ case 50:
+ switch(ndx) {
+ case 0:
+ p = "char *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* acct */
+ case 51:
+ switch(ndx) {
+ case 0:
+ p = "char *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* sigaltstack */
+ case 53:
+ switch(ndx) {
+ case 0:
+ p = "stack_t *";
+ break;
+ case 1:
+ p = "stack_t *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* ioctl */
+ case 54:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "u_long";
+ break;
+ case 2:
+ p = "caddr_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* reboot */
+ case 55:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* revoke */
+ case 56:
+ switch(ndx) {
+ case 0:
+ p = "char *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* symlink */
+ case 57:
+ switch(ndx) {
+ case 0:
+ p = "char *";
+ break;
+ case 1:
+ p = "char *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* readlink */
+ case 58:
+ switch(ndx) {
+ case 0:
+ p = "char *";
+ break;
+ case 1:
+ p = "char *";
+ break;
+ case 2:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* execve */
+ case 59:
+ switch(ndx) {
+ case 0:
+ p = "char *";
+ break;
+ case 1:
+ p = "char **";
+ break;
+ case 2:
+ p = "char **";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* umask */
+ case 60:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* chroot */
+ case 61:
+ switch(ndx) {
+ case 0:
+ p = "char *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* msync */
+ case 65:
+ switch(ndx) {
+ case 0:
+ p = "void *";
+ break;
+ case 1:
+ p = "size_t";
+ break;
+ case 2:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* vfork */
+ case 66:
+ break;
+ /* sbrk */
+ case 69:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* sstk */
+ case 70:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* ovadvise */
+ case 72:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* munmap */
+ case 73:
+ switch(ndx) {
+ case 0:
+ p = "void *";
+ break;
+ case 1:
+ p = "size_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* mprotect */
+ case 74:
+ switch(ndx) {
+ case 0:
+ p = "const void *";
+ break;
+ case 1:
+ p = "size_t";
+ break;
+ case 2:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* madvise */
+ case 75:
+ switch(ndx) {
+ case 0:
+ p = "void *";
+ break;
+ case 1:
+ p = "size_t";
+ break;
+ case 2:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* mincore */
+ case 78:
+ switch(ndx) {
+ case 0:
+ p = "const void *";
+ break;
+ case 1:
+ p = "size_t";
+ break;
+ case 2:
+ p = "char *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* getgroups */
+ case 79:
+ switch(ndx) {
+ case 0:
+ p = "u_int";
+ break;
+ case 1:
+ p = "gid_t *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* setgroups */
+ case 80:
+ switch(ndx) {
+ case 0:
+ p = "u_int";
+ break;
+ case 1:
+ p = "gid_t *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* getpgrp */
+ case 81:
+ break;
+ /* setpgid */
+ case 82:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* setitimer */
+ case 83:
+ switch(ndx) {
+ case 0:
+ p = "u_int";
+ break;
+ case 1:
+ p = "struct itimerval *";
+ break;
+ case 2:
+ p = "struct itimerval *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* swapon */
+ case 85:
+ switch(ndx) {
+ case 0:
+ p = "char *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* getitimer */
+ case 86:
+ switch(ndx) {
+ case 0:
+ p = "u_int";
+ break;
+ case 1:
+ p = "struct itimerval *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* getdtablesize */
+ case 89:
+ break;
+ /* dup2 */
+ case 90:
+ switch(ndx) {
+ case 0:
+ p = "u_int";
+ break;
+ case 1:
+ p = "u_int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* fcntl */
+ case 92:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "int";
+ break;
+ case 2:
+ p = "long";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* select */
+ case 93:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "fd_set *";
+ break;
+ case 2:
+ p = "fd_set *";
+ break;
+ case 3:
+ p = "fd_set *";
+ break;
+ case 4:
+ p = "struct timeval *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* fsync */
+ case 95:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* setpriority */
+ case 96:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "int";
+ break;
+ case 2:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* socket */
+ case 97:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "int";
+ break;
+ case 2:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* connect */
+ case 98:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "caddr_t";
+ break;
+ case 2:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* getpriority */
+ case 100:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* bind */
+ case 104:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "caddr_t";
+ break;
+ case 2:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* setsockopt */
+ case 105:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "int";
+ break;
+ case 2:
+ p = "int";
+ break;
+ case 3:
+ p = "caddr_t";
+ break;
+ case 4:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* listen */
+ case 106:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* gettimeofday */
+ case 116:
+ switch(ndx) {
+ case 0:
+ p = "struct timeval *";
+ break;
+ case 1:
+ p = "struct timezone *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* getrusage */
+ case 117:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "struct rusage *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* getsockopt */
+ case 118:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "int";
+ break;
+ case 2:
+ p = "int";
+ break;
+ case 3:
+ p = "caddr_t";
+ break;
+ case 4:
+ p = "int *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* readv */
+ case 120:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "struct iovec *";
+ break;
+ case 2:
+ p = "u_int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* writev */
+ case 121:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "struct iovec *";
+ break;
+ case 2:
+ p = "u_int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* settimeofday */
+ case 122:
+ switch(ndx) {
+ case 0:
+ p = "struct timeval *";
+ break;
+ case 1:
+ p = "struct timezone *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* fchown */
+ case 123:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "int";
+ break;
+ case 2:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* fchmod */
+ case 124:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* setreuid */
+ case 126:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* setregid */
+ case 127:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* rename */
+ case 128:
+ switch(ndx) {
+ case 0:
+ p = "char *";
+ break;
+ case 1:
+ p = "char *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* flock */
+ case 131:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* mkfifo */
+ case 132:
+ switch(ndx) {
+ case 0:
+ p = "char *";
+ break;
+ case 1:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* sendto */
+ case 133:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "caddr_t";
+ break;
+ case 2:
+ p = "size_t";
+ break;
+ case 3:
+ p = "int";
+ break;
+ case 4:
+ p = "caddr_t";
+ break;
+ case 5:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* shutdown */
+ case 134:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* socketpair */
+ case 135:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "int";
+ break;
+ case 2:
+ p = "int";
+ break;
+ case 3:
+ p = "int *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* mkdir */
+ case 136:
+ switch(ndx) {
+ case 0:
+ p = "char *";
+ break;
+ case 1:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* rmdir */
+ case 137:
+ switch(ndx) {
+ case 0:
+ p = "char *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* utimes */
+ case 138:
+ switch(ndx) {
+ case 0:
+ p = "char *";
+ break;
+ case 1:
+ p = "struct timeval *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* adjtime */
+ case 140:
+ switch(ndx) {
+ case 0:
+ p = "struct timeval *";
+ break;
+ case 1:
+ p = "struct timeval *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* setsid */
+ case 147:
+ break;
+ /* quotactl */
+ case 148:
+ switch(ndx) {
+ case 0:
+ p = "char *";
+ break;
+ case 1:
+ p = "int";
+ break;
+ case 2:
+ p = "int";
+ break;
+ case 3:
+ p = "caddr_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* nlm_syscall */
+ case 154:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "int";
+ break;
+ case 2:
+ p = "int";
+ break;
+ case 3:
+ p = "char **";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* nfssvc */
+ case 155:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "caddr_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* lgetfh */
+ case 160:
+ switch(ndx) {
+ case 0:
+ p = "char *";
+ break;
+ case 1:
+ p = "struct fhandle *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* getfh */
+ case 161:
+ switch(ndx) {
+ case 0:
+ p = "char *";
+ break;
+ case 1:
+ p = "struct fhandle *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* getdomainname */
+ case 162:
+ switch(ndx) {
+ case 0:
+ p = "char *";
+ break;
+ case 1:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* setdomainname */
+ case 163:
+ switch(ndx) {
+ case 0:
+ p = "char *";
+ break;
+ case 1:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* uname */
+ case 164:
+ switch(ndx) {
+ case 0:
+ p = "struct utsname *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* sysarch */
+ case 165:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "char *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* rtprio */
+ case 166:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "pid_t";
+ break;
+ case 2:
+ p = "struct rtprio *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* semsys */
+ case 169:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "int";
+ break;
+ case 2:
+ p = "int";
+ break;
+ case 3:
+ p = "int";
+ break;
+ case 4:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* msgsys */
+ case 170:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "int";
+ break;
+ case 2:
+ p = "int";
+ break;
+ case 3:
+ p = "int";
+ break;
+ case 4:
+ p = "int";
+ break;
+ case 5:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* shmsys */
+ case 171:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "int";
+ break;
+ case 2:
+ p = "int";
+ break;
+ case 3:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* freebsd6_pread */
+ case 173:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "void *";
+ break;
+ case 2:
+ p = "size_t";
+ break;
+ case 3:
+ p = "int";
+ break;
+ case 4:
+ p = "off_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* freebsd6_pwrite */
+ case 174:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "const void *";
+ break;
+ case 2:
+ p = "size_t";
+ break;
+ case 3:
+ p = "int";
+ break;
+ case 4:
+ p = "off_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* ntp_adjtime */
+ case 176:
+ switch(ndx) {
+ case 0:
+ p = "struct timex *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* setgid */
+ case 181:
+ switch(ndx) {
+ case 0:
+ p = "gid_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* setegid */
+ case 182:
+ switch(ndx) {
+ case 0:
+ p = "gid_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* seteuid */
+ case 183:
+ switch(ndx) {
+ case 0:
+ p = "uid_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* stat */
+ case 188:
+ switch(ndx) {
+ case 0:
+ p = "char *";
+ break;
+ case 1:
+ p = "struct stat *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* fstat */
+ case 189:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "struct stat *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* lstat */
+ case 190:
+ switch(ndx) {
+ case 0:
+ p = "char *";
+ break;
+ case 1:
+ p = "struct stat *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* pathconf */
+ case 191:
+ switch(ndx) {
+ case 0:
+ p = "char *";
+ break;
+ case 1:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* fpathconf */
+ case 192:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* getrlimit */
+ case 194:
+ switch(ndx) {
+ case 0:
+ p = "u_int";
+ break;
+ case 1:
+ p = "struct rlimit *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* setrlimit */
+ case 195:
+ switch(ndx) {
+ case 0:
+ p = "u_int";
+ break;
+ case 1:
+ p = "struct rlimit *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* getdirentries */
+ case 196:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "char *";
+ break;
+ case 2:
+ p = "u_int";
+ break;
+ case 3:
+ p = "long *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* freebsd6_mmap */
+ case 197:
+ switch(ndx) {
+ case 0:
+ p = "caddr_t";
+ break;
+ case 1:
+ p = "size_t";
+ break;
+ case 2:
+ p = "int";
+ break;
+ case 3:
+ p = "int";
+ break;
+ case 4:
+ p = "int";
+ break;
+ case 5:
+ p = "int";
+ break;
+ case 6:
+ p = "off_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* nosys */
+ case 198:
+ break;
+ /* freebsd6_lseek */
+ case 199:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "int";
+ break;
+ case 2:
+ p = "off_t";
+ break;
+ case 3:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* freebsd6_truncate */
+ case 200:
+ switch(ndx) {
+ case 0:
+ p = "char *";
+ break;
+ case 1:
+ p = "int";
+ break;
+ case 2:
+ p = "off_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* freebsd6_ftruncate */
+ case 201:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "int";
+ break;
+ case 2:
+ p = "off_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* __sysctl */
+ case 202:
+ switch(ndx) {
+ case 0:
+ p = "int *";
+ break;
+ case 1:
+ p = "u_int";
+ break;
+ case 2:
+ p = "void *";
+ break;
+ case 3:
+ p = "size_t *";
+ break;
+ case 4:
+ p = "void *";
+ break;
+ case 5:
+ p = "size_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* mlock */
+ case 203:
+ switch(ndx) {
+ case 0:
+ p = "const void *";
+ break;
+ case 1:
+ p = "size_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* munlock */
+ case 204:
+ switch(ndx) {
+ case 0:
+ p = "const void *";
+ break;
+ case 1:
+ p = "size_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* undelete */
+ case 205:
+ switch(ndx) {
+ case 0:
+ p = "char *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* futimes */
+ case 206:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "struct timeval *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* getpgid */
+ case 207:
+ switch(ndx) {
+ case 0:
+ p = "pid_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* poll */
+ case 209:
+ switch(ndx) {
+ case 0:
+ p = "struct pollfd *";
+ break;
+ case 1:
+ p = "u_int";
+ break;
+ case 2:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* lkmnosys */
+ case 210:
+ break;
+ /* lkmnosys */
+ case 211:
+ break;
+ /* lkmnosys */
+ case 212:
+ break;
+ /* lkmnosys */
+ case 213:
+ break;
+ /* lkmnosys */
+ case 214:
+ break;
+ /* lkmnosys */
+ case 215:
+ break;
+ /* lkmnosys */
+ case 216:
+ break;
+ /* lkmnosys */
+ case 217:
+ break;
+ /* lkmnosys */
+ case 218:
+ break;
+ /* lkmnosys */
+ case 219:
+ break;
+ /* __semctl */
+ case 220:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "int";
+ break;
+ case 2:
+ p = "int";
+ break;
+ case 3:
+ p = "union semun *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* semget */
+ case 221:
+ switch(ndx) {
+ case 0:
+ p = "key_t";
+ break;
+ case 1:
+ p = "int";
+ break;
+ case 2:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* semop */
+ case 222:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "struct sembuf *";
+ break;
+ case 2:
+ p = "size_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* msgctl */
+ case 224:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "int";
+ break;
+ case 2:
+ p = "struct msqid_ds *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* msgget */
+ case 225:
+ switch(ndx) {
+ case 0:
+ p = "key_t";
+ break;
+ case 1:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* msgsnd */
+ case 226:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "const void *";
+ break;
+ case 2:
+ p = "size_t";
+ break;
+ case 3:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* msgrcv */
+ case 227:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "void *";
+ break;
+ case 2:
+ p = "size_t";
+ break;
+ case 3:
+ p = "long";
+ break;
+ case 4:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* shmat */
+ case 228:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "const void *";
+ break;
+ case 2:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* shmctl */
+ case 229:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "int";
+ break;
+ case 2:
+ p = "struct shmid_ds *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* shmdt */
+ case 230:
+ switch(ndx) {
+ case 0:
+ p = "const void *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* shmget */
+ case 231:
+ switch(ndx) {
+ case 0:
+ p = "key_t";
+ break;
+ case 1:
+ p = "size_t";
+ break;
+ case 2:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* clock_gettime */
+ case 232:
+ switch(ndx) {
+ case 0:
+ p = "clockid_t";
+ break;
+ case 1:
+ p = "struct timespec *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* clock_settime */
+ case 233:
+ switch(ndx) {
+ case 0:
+ p = "clockid_t";
+ break;
+ case 1:
+ p = "const struct timespec *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* clock_getres */
+ case 234:
+ switch(ndx) {
+ case 0:
+ p = "clockid_t";
+ break;
+ case 1:
+ p = "struct timespec *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* ktimer_create */
+ case 235:
+ switch(ndx) {
+ case 0:
+ p = "clockid_t";
+ break;
+ case 1:
+ p = "struct sigevent *";
+ break;
+ case 2:
+ p = "int *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* ktimer_delete */
+ case 236:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* ktimer_settime */
+ case 237:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "int";
+ break;
+ case 2:
+ p = "const struct itimerspec *";
+ break;
+ case 3:
+ p = "struct itimerspec *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* ktimer_gettime */
+ case 238:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "struct itimerspec *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* ktimer_getoverrun */
+ case 239:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* nanosleep */
+ case 240:
+ switch(ndx) {
+ case 0:
+ p = "const struct timespec *";
+ break;
+ case 1:
+ p = "struct timespec *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* ntp_gettime */
+ case 248:
+ switch(ndx) {
+ case 0:
+ p = "struct ntptimeval *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* minherit */
+ case 250:
+ switch(ndx) {
+ case 0:
+ p = "void *";
+ break;
+ case 1:
+ p = "size_t";
+ break;
+ case 2:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* rfork */
+ case 251:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* openbsd_poll */
+ case 252:
+ switch(ndx) {
+ case 0:
+ p = "struct pollfd *";
+ break;
+ case 1:
+ p = "u_int";
+ break;
+ case 2:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* issetugid */
+ case 253:
+ break;
+ /* lchown */
+ case 254:
+ switch(ndx) {
+ case 0:
+ p = "char *";
+ break;
+ case 1:
+ p = "int";
+ break;
+ case 2:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* aio_read */
+ case 255:
+ switch(ndx) {
+ case 0:
+ p = "struct aiocb *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* aio_write */
+ case 256:
+ switch(ndx) {
+ case 0:
+ p = "struct aiocb *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* lio_listio */
+ case 257:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "struct aiocb *const *";
+ break;
+ case 2:
+ p = "int";
+ break;
+ case 3:
+ p = "struct sigevent *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* getdents */
+ case 272:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "char *";
+ break;
+ case 2:
+ p = "size_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* lchmod */
+ case 274:
+ switch(ndx) {
+ case 0:
+ p = "char *";
+ break;
+ case 1:
+ p = "mode_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* lchown */
+ case 275:
+ switch(ndx) {
+ case 0:
+ p = "char *";
+ break;
+ case 1:
+ p = "uid_t";
+ break;
+ case 2:
+ p = "gid_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* lutimes */
+ case 276:
+ switch(ndx) {
+ case 0:
+ p = "char *";
+ break;
+ case 1:
+ p = "struct timeval *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* msync */
+ case 277:
+ switch(ndx) {
+ case 0:
+ p = "void *";
+ break;
+ case 1:
+ p = "size_t";
+ break;
+ case 2:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* nstat */
+ case 278:
+ switch(ndx) {
+ case 0:
+ p = "char *";
+ break;
+ case 1:
+ p = "struct nstat *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* nfstat */
+ case 279:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "struct nstat *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* nlstat */
+ case 280:
+ switch(ndx) {
+ case 0:
+ p = "char *";
+ break;
+ case 1:
+ p = "struct nstat *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* preadv */
+ case 289:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "struct iovec *";
+ break;
+ case 2:
+ p = "u_int";
+ break;
+ case 3:
+ p = "off_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* pwritev */
+ case 290:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "struct iovec *";
+ break;
+ case 2:
+ p = "u_int";
+ break;
+ case 3:
+ p = "off_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* fhopen */
+ case 298:
+ switch(ndx) {
+ case 0:
+ p = "const struct fhandle *";
+ break;
+ case 1:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* fhstat */
+ case 299:
+ switch(ndx) {
+ case 0:
+ p = "const struct fhandle *";
+ break;
+ case 1:
+ p = "struct stat *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* modnext */
+ case 300:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* modstat */
+ case 301:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "struct module_stat *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* modfnext */
+ case 302:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* modfind */
+ case 303:
+ switch(ndx) {
+ case 0:
+ p = "const char *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* kldload */
+ case 304:
+ switch(ndx) {
+ case 0:
+ p = "const char *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* kldunload */
+ case 305:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* kldfind */
+ case 306:
+ switch(ndx) {
+ case 0:
+ p = "const char *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* kldnext */
+ case 307:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* kldstat */
+ case 308:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "struct kld_file_stat *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* kldfirstmod */
+ case 309:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* getsid */
+ case 310:
+ switch(ndx) {
+ case 0:
+ p = "pid_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* setresuid */
+ case 311:
+ switch(ndx) {
+ case 0:
+ p = "uid_t";
+ break;
+ case 1:
+ p = "uid_t";
+ break;
+ case 2:
+ p = "uid_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* setresgid */
+ case 312:
+ switch(ndx) {
+ case 0:
+ p = "gid_t";
+ break;
+ case 1:
+ p = "gid_t";
+ break;
+ case 2:
+ p = "gid_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* aio_return */
+ case 314:
+ switch(ndx) {
+ case 0:
+ p = "struct aiocb *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* aio_suspend */
+ case 315:
+ switch(ndx) {
+ case 0:
+ p = "struct aiocb *const *";
+ break;
+ case 1:
+ p = "int";
+ break;
+ case 2:
+ p = "const struct timespec *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* aio_cancel */
+ case 316:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "struct aiocb *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* aio_error */
+ case 317:
+ switch(ndx) {
+ case 0:
+ p = "struct aiocb *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* oaio_read */
+ case 318:
+ switch(ndx) {
+ case 0:
+ p = "struct oaiocb *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* oaio_write */
+ case 319:
+ switch(ndx) {
+ case 0:
+ p = "struct oaiocb *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* olio_listio */
+ case 320:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "struct oaiocb *const *";
+ break;
+ case 2:
+ p = "int";
+ break;
+ case 3:
+ p = "struct osigevent *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* yield */
+ case 321:
+ break;
+ /* mlockall */
+ case 324:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* munlockall */
+ case 325:
+ break;
+ /* __getcwd */
+ case 326:
+ switch(ndx) {
+ case 0:
+ p = "u_char *";
+ break;
+ case 1:
+ p = "u_int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* sched_setparam */
+ case 327:
+ switch(ndx) {
+ case 0:
+ p = "pid_t";
+ break;
+ case 1:
+ p = "const struct sched_param *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* sched_getparam */
+ case 328:
+ switch(ndx) {
+ case 0:
+ p = "pid_t";
+ break;
+ case 1:
+ p = "struct sched_param *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* sched_setscheduler */
+ case 329:
+ switch(ndx) {
+ case 0:
+ p = "pid_t";
+ break;
+ case 1:
+ p = "int";
+ break;
+ case 2:
+ p = "const struct sched_param *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* sched_getscheduler */
+ case 330:
+ switch(ndx) {
+ case 0:
+ p = "pid_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* sched_yield */
+ case 331:
+ break;
+ /* sched_get_priority_max */
+ case 332:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* sched_get_priority_min */
+ case 333:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* sched_rr_get_interval */
+ case 334:
+ switch(ndx) {
+ case 0:
+ p = "pid_t";
+ break;
+ case 1:
+ p = "struct timespec *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* utrace */
+ case 335:
+ switch(ndx) {
+ case 0:
+ p = "const void *";
+ break;
+ case 1:
+ p = "size_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* kldsym */
+ case 337:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "int";
+ break;
+ case 2:
+ p = "void *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* jail */
+ case 338:
+ switch(ndx) {
+ case 0:
+ p = "struct jail *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* sigprocmask */
+ case 340:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "const sigset_t *";
+ break;
+ case 2:
+ p = "sigset_t *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* sigsuspend */
+ case 341:
+ switch(ndx) {
+ case 0:
+ p = "const sigset_t *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* sigpending */
+ case 343:
+ switch(ndx) {
+ case 0:
+ p = "sigset_t *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* sigtimedwait */
+ case 345:
+ switch(ndx) {
+ case 0:
+ p = "const sigset_t *";
+ break;
+ case 1:
+ p = "siginfo_t *";
+ break;
+ case 2:
+ p = "const struct timespec *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* sigwaitinfo */
+ case 346:
+ switch(ndx) {
+ case 0:
+ p = "const sigset_t *";
+ break;
+ case 1:
+ p = "siginfo_t *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* __acl_get_file */
+ case 347:
+ switch(ndx) {
+ case 0:
+ p = "const char *";
+ break;
+ case 1:
+ p = "acl_type_t";
+ break;
+ case 2:
+ p = "struct acl *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* __acl_set_file */
+ case 348:
+ switch(ndx) {
+ case 0:
+ p = "const char *";
+ break;
+ case 1:
+ p = "acl_type_t";
+ break;
+ case 2:
+ p = "struct acl *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* __acl_get_fd */
+ case 349:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "acl_type_t";
+ break;
+ case 2:
+ p = "struct acl *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* __acl_set_fd */
+ case 350:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "acl_type_t";
+ break;
+ case 2:
+ p = "struct acl *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* __acl_delete_file */
+ case 351:
+ switch(ndx) {
+ case 0:
+ p = "const char *";
+ break;
+ case 1:
+ p = "acl_type_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* __acl_delete_fd */
+ case 352:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "acl_type_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* __acl_aclcheck_file */
+ case 353:
+ switch(ndx) {
+ case 0:
+ p = "const char *";
+ break;
+ case 1:
+ p = "acl_type_t";
+ break;
+ case 2:
+ p = "struct acl *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* __acl_aclcheck_fd */
+ case 354:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "acl_type_t";
+ break;
+ case 2:
+ p = "struct acl *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* extattrctl */
+ case 355:
+ switch(ndx) {
+ case 0:
+ p = "const char *";
+ break;
+ case 1:
+ p = "int";
+ break;
+ case 2:
+ p = "const char *";
+ break;
+ case 3:
+ p = "int";
+ break;
+ case 4:
+ p = "const char *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* extattr_set_file */
+ case 356:
+ switch(ndx) {
+ case 0:
+ p = "const char *";
+ break;
+ case 1:
+ p = "int";
+ break;
+ case 2:
+ p = "const char *";
+ break;
+ case 3:
+ p = "void *";
+ break;
+ case 4:
+ p = "size_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* extattr_get_file */
+ case 357:
+ switch(ndx) {
+ case 0:
+ p = "const char *";
+ break;
+ case 1:
+ p = "int";
+ break;
+ case 2:
+ p = "const char *";
+ break;
+ case 3:
+ p = "void *";
+ break;
+ case 4:
+ p = "size_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* extattr_delete_file */
+ case 358:
+ switch(ndx) {
+ case 0:
+ p = "const char *";
+ break;
+ case 1:
+ p = "int";
+ break;
+ case 2:
+ p = "const char *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* aio_waitcomplete */
+ case 359:
+ switch(ndx) {
+ case 0:
+ p = "struct aiocb **";
+ break;
+ case 1:
+ p = "struct timespec *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* getresuid */
+ case 360:
+ switch(ndx) {
+ case 0:
+ p = "uid_t *";
+ break;
+ case 1:
+ p = "uid_t *";
+ break;
+ case 2:
+ p = "uid_t *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* getresgid */
+ case 361:
+ switch(ndx) {
+ case 0:
+ p = "gid_t *";
+ break;
+ case 1:
+ p = "gid_t *";
+ break;
+ case 2:
+ p = "gid_t *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* kqueue */
+ case 362:
+ break;
+ /* kevent */
+ case 363:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "struct kevent *";
+ break;
+ case 2:
+ p = "int";
+ break;
+ case 3:
+ p = "struct kevent *";
+ break;
+ case 4:
+ p = "int";
+ break;
+ case 5:
+ p = "const struct timespec *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* lkmressys */
+ case 370:
+ break;
+ /* extattr_set_fd */
+ case 371:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "int";
+ break;
+ case 2:
+ p = "const char *";
+ break;
+ case 3:
+ p = "void *";
+ break;
+ case 4:
+ p = "size_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* extattr_get_fd */
+ case 372:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "int";
+ break;
+ case 2:
+ p = "const char *";
+ break;
+ case 3:
+ p = "void *";
+ break;
+ case 4:
+ p = "size_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* extattr_delete_fd */
+ case 373:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "int";
+ break;
+ case 2:
+ p = "const char *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* __setugid */
+ case 374:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* nfsclnt */
+ case 375:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "caddr_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* eaccess */
+ case 376:
+ switch(ndx) {
+ case 0:
+ p = "char *";
+ break;
+ case 1:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* nmount */
+ case 378:
+ switch(ndx) {
+ case 0:
+ p = "struct iovec *";
+ break;
+ case 1:
+ p = "unsigned int";
+ break;
+ case 2:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* kse_exit */
+ case 379:
+ break;
+ /* kse_wakeup */
+ case 380:
+ switch(ndx) {
+ case 0:
+ p = "struct kse_mailbox *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* kse_create */
+ case 381:
+ switch(ndx) {
+ case 0:
+ p = "struct kse_mailbox *";
+ break;
+ case 1:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* kse_thr_interrupt */
+ case 382:
+ switch(ndx) {
+ case 0:
+ p = "struct kse_thr_mailbox *";
+ break;
+ case 1:
+ p = "int";
+ break;
+ case 2:
+ p = "long";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* kse_release */
+ case 383:
+ switch(ndx) {
+ case 0:
+ p = "struct timespec *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* __mac_get_proc */
+ case 384:
+ switch(ndx) {
+ case 0:
+ p = "struct mac *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* __mac_set_proc */
+ case 385:
+ switch(ndx) {
+ case 0:
+ p = "struct mac *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* __mac_get_fd */
+ case 386:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "struct mac *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* __mac_get_file */
+ case 387:
+ switch(ndx) {
+ case 0:
+ p = "const char *";
+ break;
+ case 1:
+ p = "struct mac *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* __mac_set_fd */
+ case 388:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "struct mac *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* __mac_set_file */
+ case 389:
+ switch(ndx) {
+ case 0:
+ p = "const char *";
+ break;
+ case 1:
+ p = "struct mac *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* kenv */
+ case 390:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "const char *";
+ break;
+ case 2:
+ p = "char *";
+ break;
+ case 3:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* lchflags */
+ case 391:
+ switch(ndx) {
+ case 0:
+ p = "const char *";
+ break;
+ case 1:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* uuidgen */
+ case 392:
+ switch(ndx) {
+ case 0:
+ p = "struct uuid *";
+ break;
+ case 1:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* sendfile */
+ case 393:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "int";
+ break;
+ case 2:
+ p = "off_t";
+ break;
+ case 3:
+ p = "size_t";
+ break;
+ case 4:
+ p = "struct sf_hdtr *";
+ break;
+ case 5:
+ p = "off_t *";
+ break;
+ case 6:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* mac_syscall */
+ case 394:
+ switch(ndx) {
+ case 0:
+ p = "const char *";
+ break;
+ case 1:
+ p = "int";
+ break;
+ case 2:
+ p = "void *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* getfsstat */
+ case 395:
+ switch(ndx) {
+ case 0:
+ p = "struct statfs *";
+ break;
+ case 1:
+ p = "long";
+ break;
+ case 2:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* statfs */
+ case 396:
+ switch(ndx) {
+ case 0:
+ p = "char *";
+ break;
+ case 1:
+ p = "struct statfs *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* fstatfs */
+ case 397:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "struct statfs *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* fhstatfs */
+ case 398:
+ switch(ndx) {
+ case 0:
+ p = "const struct fhandle *";
+ break;
+ case 1:
+ p = "struct statfs *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* ksem_close */
+ case 400:
+ switch(ndx) {
+ case 0:
+ p = "semid_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* ksem_post */
+ case 401:
+ switch(ndx) {
+ case 0:
+ p = "semid_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* ksem_wait */
+ case 402:
+ switch(ndx) {
+ case 0:
+ p = "semid_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* ksem_trywait */
+ case 403:
+ switch(ndx) {
+ case 0:
+ p = "semid_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* ksem_init */
+ case 404:
+ switch(ndx) {
+ case 0:
+ p = "semid_t *";
+ break;
+ case 1:
+ p = "unsigned int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* ksem_open */
+ case 405:
+ switch(ndx) {
+ case 0:
+ p = "semid_t *";
+ break;
+ case 1:
+ p = "const char *";
+ break;
+ case 2:
+ p = "int";
+ break;
+ case 3:
+ p = "mode_t";
+ break;
+ case 4:
+ p = "unsigned int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* ksem_unlink */
+ case 406:
+ switch(ndx) {
+ case 0:
+ p = "const char *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* ksem_getvalue */
+ case 407:
+ switch(ndx) {
+ case 0:
+ p = "semid_t";
+ break;
+ case 1:
+ p = "int *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* ksem_destroy */
+ case 408:
+ switch(ndx) {
+ case 0:
+ p = "semid_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* __mac_get_pid */
+ case 409:
+ switch(ndx) {
+ case 0:
+ p = "pid_t";
+ break;
+ case 1:
+ p = "struct mac *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* __mac_get_link */
+ case 410:
+ switch(ndx) {
+ case 0:
+ p = "const char *";
+ break;
+ case 1:
+ p = "struct mac *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* __mac_set_link */
+ case 411:
+ switch(ndx) {
+ case 0:
+ p = "const char *";
+ break;
+ case 1:
+ p = "struct mac *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* extattr_set_link */
+ case 412:
+ switch(ndx) {
+ case 0:
+ p = "const char *";
+ break;
+ case 1:
+ p = "int";
+ break;
+ case 2:
+ p = "const char *";
+ break;
+ case 3:
+ p = "void *";
+ break;
+ case 4:
+ p = "size_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* extattr_get_link */
+ case 413:
+ switch(ndx) {
+ case 0:
+ p = "const char *";
+ break;
+ case 1:
+ p = "int";
+ break;
+ case 2:
+ p = "const char *";
+ break;
+ case 3:
+ p = "void *";
+ break;
+ case 4:
+ p = "size_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* extattr_delete_link */
+ case 414:
+ switch(ndx) {
+ case 0:
+ p = "const char *";
+ break;
+ case 1:
+ p = "int";
+ break;
+ case 2:
+ p = "const char *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* __mac_execve */
+ case 415:
+ switch(ndx) {
+ case 0:
+ p = "char *";
+ break;
+ case 1:
+ p = "char **";
+ break;
+ case 2:
+ p = "char **";
+ break;
+ case 3:
+ p = "struct mac *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* sigaction */
+ case 416:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "const struct sigaction *";
+ break;
+ case 2:
+ p = "struct sigaction *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* sigreturn */
+ case 417:
+ switch(ndx) {
+ case 0:
+ p = "const struct __ucontext *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* getcontext */
+ case 421:
+ switch(ndx) {
+ case 0:
+ p = "struct __ucontext *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* setcontext */
+ case 422:
+ switch(ndx) {
+ case 0:
+ p = "const struct __ucontext *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* swapcontext */
+ case 423:
+ switch(ndx) {
+ case 0:
+ p = "struct __ucontext *";
+ break;
+ case 1:
+ p = "const struct __ucontext *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* swapoff */
+ case 424:
+ switch(ndx) {
+ case 0:
+ p = "const char *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* __acl_get_link */
+ case 425:
+ switch(ndx) {
+ case 0:
+ p = "const char *";
+ break;
+ case 1:
+ p = "acl_type_t";
+ break;
+ case 2:
+ p = "struct acl *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* __acl_set_link */
+ case 426:
+ switch(ndx) {
+ case 0:
+ p = "const char *";
+ break;
+ case 1:
+ p = "acl_type_t";
+ break;
+ case 2:
+ p = "struct acl *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* __acl_delete_link */
+ case 427:
+ switch(ndx) {
+ case 0:
+ p = "const char *";
+ break;
+ case 1:
+ p = "acl_type_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* __acl_aclcheck_link */
+ case 428:
+ switch(ndx) {
+ case 0:
+ p = "const char *";
+ break;
+ case 1:
+ p = "acl_type_t";
+ break;
+ case 2:
+ p = "struct acl *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* sigwait */
+ case 429:
+ switch(ndx) {
+ case 0:
+ p = "const sigset_t *";
+ break;
+ case 1:
+ p = "int *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* thr_create */
+ case 430:
+ switch(ndx) {
+ case 0:
+ p = "ucontext_t *";
+ break;
+ case 1:
+ p = "long *";
+ break;
+ case 2:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* thr_exit */
+ case 431:
+ switch(ndx) {
+ case 0:
+ p = "long *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* thr_self */
+ case 432:
+ switch(ndx) {
+ case 0:
+ p = "long *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* thr_kill */
+ case 433:
+ switch(ndx) {
+ case 0:
+ p = "long";
+ break;
+ case 1:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* _umtx_lock */
+ case 434:
+ switch(ndx) {
+ case 0:
+ p = "struct umtx *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* _umtx_unlock */
+ case 435:
+ switch(ndx) {
+ case 0:
+ p = "struct umtx *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* jail_attach */
+ case 436:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* extattr_list_fd */
+ case 437:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "int";
+ break;
+ case 2:
+ p = "void *";
+ break;
+ case 3:
+ p = "size_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* extattr_list_file */
+ case 438:
+ switch(ndx) {
+ case 0:
+ p = "const char *";
+ break;
+ case 1:
+ p = "int";
+ break;
+ case 2:
+ p = "void *";
+ break;
+ case 3:
+ p = "size_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* extattr_list_link */
+ case 439:
+ switch(ndx) {
+ case 0:
+ p = "const char *";
+ break;
+ case 1:
+ p = "int";
+ break;
+ case 2:
+ p = "void *";
+ break;
+ case 3:
+ p = "size_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* kse_switchin */
+ case 440:
+ switch(ndx) {
+ case 0:
+ p = "struct kse_thr_mailbox *";
+ break;
+ case 1:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* ksem_timedwait */
+ case 441:
+ switch(ndx) {
+ case 0:
+ p = "semid_t";
+ break;
+ case 1:
+ p = "const struct timespec *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* thr_suspend */
+ case 442:
+ switch(ndx) {
+ case 0:
+ p = "const struct timespec *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* thr_wake */
+ case 443:
+ switch(ndx) {
+ case 0:
+ p = "long";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* kldunloadf */
+ case 444:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* audit */
+ case 445:
+ switch(ndx) {
+ case 0:
+ p = "const void *";
+ break;
+ case 1:
+ p = "u_int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* auditon */
+ case 446:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "void *";
+ break;
+ case 2:
+ p = "u_int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* getauid */
+ case 447:
+ switch(ndx) {
+ case 0:
+ p = "uid_t *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* setauid */
+ case 448:
+ switch(ndx) {
+ case 0:
+ p = "uid_t *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* getaudit */
+ case 449:
+ switch(ndx) {
+ case 0:
+ p = "struct auditinfo *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* setaudit */
+ case 450:
+ switch(ndx) {
+ case 0:
+ p = "struct auditinfo *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* getaudit_addr */
+ case 451:
+ switch(ndx) {
+ case 0:
+ p = "struct auditinfo_addr *";
+ break;
+ case 1:
+ p = "u_int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* setaudit_addr */
+ case 452:
+ switch(ndx) {
+ case 0:
+ p = "struct auditinfo_addr *";
+ break;
+ case 1:
+ p = "u_int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* auditctl */
+ case 453:
+ switch(ndx) {
+ case 0:
+ p = "char *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* _umtx_op */
+ case 454:
+ switch(ndx) {
+ case 0:
+ p = "void *";
+ break;
+ case 1:
+ p = "int";
+ break;
+ case 2:
+ p = "u_long";
+ break;
+ case 3:
+ p = "void *";
+ break;
+ case 4:
+ p = "void *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* thr_new */
+ case 455:
+ switch(ndx) {
+ case 0:
+ p = "struct thr_param *";
+ break;
+ case 1:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* sigqueue */
+ case 456:
+ switch(ndx) {
+ case 0:
+ p = "pid_t";
+ break;
+ case 1:
+ p = "int";
+ break;
+ case 2:
+ p = "void *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* kmq_open */
+ case 457:
+ switch(ndx) {
+ case 0:
+ p = "const char *";
+ break;
+ case 1:
+ p = "int";
+ break;
+ case 2:
+ p = "mode_t";
+ break;
+ case 3:
+ p = "const struct mq_attr *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* kmq_setattr */
+ case 458:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "const struct mq_attr *";
+ break;
+ case 2:
+ p = "struct mq_attr *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* kmq_timedreceive */
+ case 459:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "char *";
+ break;
+ case 2:
+ p = "size_t";
+ break;
+ case 3:
+ p = "unsigned *";
+ break;
+ case 4:
+ p = "const struct timespec *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* kmq_timedsend */
+ case 460:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "const char *";
+ break;
+ case 2:
+ p = "size_t";
+ break;
+ case 3:
+ p = "unsigned";
+ break;
+ case 4:
+ p = "const struct timespec *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* kmq_notify */
+ case 461:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "const struct sigevent *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* kmq_unlink */
+ case 462:
+ switch(ndx) {
+ case 0:
+ p = "const char *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* abort2 */
+ case 463:
+ switch(ndx) {
+ case 0:
+ p = "const char *";
+ break;
+ case 1:
+ p = "int";
+ break;
+ case 2:
+ p = "void **";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* thr_set_name */
+ case 464:
+ switch(ndx) {
+ case 0:
+ p = "long";
+ break;
+ case 1:
+ p = "const char *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* aio_fsync */
+ case 465:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "struct aiocb *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* rtprio_thread */
+ case 466:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "lwpid_t";
+ break;
+ case 2:
+ p = "struct rtprio *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* sctp_peeloff */
+ case 471:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "uint32_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* sctp_generic_sendmsg */
+ case 472:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "caddr_t";
+ break;
+ case 2:
+ p = "int";
+ break;
+ case 3:
+ p = "caddr_t";
+ break;
+ case 4:
+ p = "__socklen_t";
+ break;
+ case 5:
+ p = "struct sctp_sndrcvinfo *";
+ break;
+ case 6:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* sctp_generic_sendmsg_iov */
+ case 473:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "struct iovec *";
+ break;
+ case 2:
+ p = "int";
+ break;
+ case 3:
+ p = "caddr_t";
+ break;
+ case 4:
+ p = "__socklen_t";
+ break;
+ case 5:
+ p = "struct sctp_sndrcvinfo *";
+ break;
+ case 6:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* sctp_generic_recvmsg */
+ case 474:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "struct iovec *";
+ break;
+ case 2:
+ p = "int";
+ break;
+ case 3:
+ p = "struct sockaddr *";
+ break;
+ case 4:
+ p = "__socklen_t *";
+ break;
+ case 5:
+ p = "struct sctp_sndrcvinfo *";
+ break;
+ case 6:
+ p = "int *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* pread */
+ case 475:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "void *";
+ break;
+ case 2:
+ p = "size_t";
+ break;
+ case 3:
+ p = "off_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* pwrite */
+ case 476:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "const void *";
+ break;
+ case 2:
+ p = "size_t";
+ break;
+ case 3:
+ p = "off_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* mmap */
+ case 477:
+ switch(ndx) {
+ case 0:
+ p = "caddr_t";
+ break;
+ case 1:
+ p = "size_t";
+ break;
+ case 2:
+ p = "int";
+ break;
+ case 3:
+ p = "int";
+ break;
+ case 4:
+ p = "int";
+ break;
+ case 5:
+ p = "off_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* lseek */
+ case 478:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "off_t";
+ break;
+ case 2:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* truncate */
+ case 479:
+ switch(ndx) {
+ case 0:
+ p = "char *";
+ break;
+ case 1:
+ p = "off_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* ftruncate */
+ case 480:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "off_t";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* thr_kill2 */
+ case 481:
+ switch(ndx) {
+ case 0:
+ p = "pid_t";
+ break;
+ case 1:
+ p = "long";
+ break;
+ case 2:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ default:
+ break;
+ };
+ if (p != NULL)
+ strlcpy(desc, p, descsz);
+}
diff --git a/sys/modules/Makefile b/sys/modules/Makefile
index 847cb91b3e48..756d2d986451 100644
--- a/sys/modules/Makefile
+++ b/sys/modules/Makefile
@@ -66,6 +66,7 @@ SUBDIR= ${_3dfx} \
cue \
cxgb \
${_cx} \
+ ${_cyclic} \
dc \
dcons \
dcons_crom \
@@ -73,6 +74,7 @@ SUBDIR= ${_3dfx} \
${_digi} \
${_dpt} \
${_drm} \
+ ${_dtrace} \
dummynet \
${_ed} \
${_elink} \
@@ -204,6 +206,7 @@ SUBDIR= ${_3dfx} \
${_nvram} \
${_nwfs} \
${_oltr} \
+ ${_opensolaris} \
${_padlock} \
patm \
${_pccard} \
@@ -390,8 +393,14 @@ _coff= coff
_cp= cp
_cpufreq= cpufreq
_cs= cs
+.if ${MK_CDDL} != "no" || defined(ALL_MODULES)
+_cyclic= cyclic
+.endif
_digi= digi
_drm= drm
+.if ${MK_CDDL} != "no" || defined(ALL_MODULES)
+_dtrace= dtrace
+.endif
_ed= ed
_elink= elink
_em= em
@@ -423,6 +432,9 @@ _nsp= nsp
_nwfs= nwfs
.endif
_oltr= oltr
+.if ${MK_CDDL} != "no" || defined(ALL_MODULES)
+_opensolaris= opensolaris
+.endif
_pccard= pccard
_pcfclock= pcfclock
_pecoff= pecoff
@@ -518,8 +530,14 @@ _cmx= cmx
_ciss= ciss
_coretemp= coretemp
_cpufreq= cpufreq
+.if ${MK_CDDL} != "no" || defined(ALL_MODULES)
+_cyclic= cyclic
+.endif
_digi= digi
_drm= drm
+.if ${MK_CDDL} != "no" || defined(ALL_MODULES)
+_dtrace= dtrace
+.endif
_ed= ed
_et= et
_em= em
@@ -550,6 +568,9 @@ _nfe= nfe
_nve= nve
_nvram= nvram
_nxge= nxge
+.if ${MK_CDDL} != "no" || defined(ALL_MODULES)
+_opensolaris= opensolaris
+.endif
_pccard= pccard
_rdma= rdma
_safe= safe
diff --git a/sys/modules/cyclic/Makefile b/sys/modules/cyclic/Makefile
new file mode 100644
index 000000000000..db99488e138b
--- /dev/null
+++ b/sys/modules/cyclic/Makefile
@@ -0,0 +1,19 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../cddl/dev/cyclic
+
+KMOD= cyclic
+SRCS= cyclic.c
+
+SRCS+= vnode_if.h
+
+CFLAGS+= -I${.CURDIR}/../../cddl/compat/opensolaris \
+ -I${.CURDIR}/../../cddl/contrib/opensolaris/uts/common \
+ -I${.CURDIR}/../.. \
+ -I${.CURDIR}/../../cddl/dev/cyclic/${MACHINE_ARCH}
+
+CFLAGS+= -DDEBUG=1
+
+IGNORE_PRAGMA= 1
+
+.include <bsd.kmod.mk>
diff --git a/sys/modules/cyclic_test/Makefile b/sys/modules/cyclic_test/Makefile
new file mode 100644
index 000000000000..e4a7c8679adb
--- /dev/null
+++ b/sys/modules/cyclic_test/Makefile
@@ -0,0 +1,16 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../cddl/dev/cyclic
+
+KMOD= cyclic_test
+SRCS= cyclic_test.c
+
+#SRCS+= bus_if.h device_if.h vnode_if.h
+
+CFLAGS+= -I${.CURDIR}/../../cddl/compat/opensolaris \
+ -I${.CURDIR}/../../cddl/contrib/opensolaris/uts/common \
+ -I${.CURDIR}/../..
+
+IGNORE_PRAGMA= 1
+
+.include <bsd.kmod.mk>
diff --git a/sys/modules/dtrace/Makefile b/sys/modules/dtrace/Makefile
new file mode 100644
index 000000000000..45c2b2f0d406
--- /dev/null
+++ b/sys/modules/dtrace/Makefile
@@ -0,0 +1,21 @@
+# $FreeBSD$
+
+.include <bsd.own.mk>
+.include "Makefile.inc"
+
+SUBDIR= dtmalloc \
+ dtrace \
+ dtraceall \
+ dtrace_test \
+ profile \
+ prototype \
+ sdt \
+ systrace
+
+.if ${MACHINE_ARCH} == "amd64"
+SUBDIR+= fbt
+.elif ${MACHINE_ARCH} == "i386"
+SUBDIR+= fbt
+.endif
+
+.include <bsd.subdir.mk>
diff --git a/sys/modules/dtrace/Makefile.inc b/sys/modules/dtrace/Makefile.inc
new file mode 100644
index 000000000000..c35764b2998b
--- /dev/null
+++ b/sys/modules/dtrace/Makefile.inc
@@ -0,0 +1,29 @@
+# $FreeBSD$
+
+IGNORE_PRAGMA= 1
+
+load :
+ -kldload cyclic
+ -kldload dtrace
+.if ${MACHINE_ARCH} == "i386"
+ -kldload sdt
+ -kldload lockstat
+ -kldload fbt
+ -kldload prototype
+.endif
+ -kldload profile
+ -kldload systrace
+ kldstat
+
+unload :
+ -kldunload systrace
+ -kldunload profile
+.if ${MACHINE_ARCH} == "i386"
+ -kldunload prototype
+ -kldunload fbt
+ -kldunload lockstat
+ -kldunload sdt
+.endif
+ -kldunload dtrace
+ -kldunload cyclic
+ kldstat
diff --git a/sys/modules/dtrace/dtmalloc/Makefile b/sys/modules/dtrace/dtmalloc/Makefile
new file mode 100644
index 000000000000..efb260763fa2
--- /dev/null
+++ b/sys/modules/dtrace/dtmalloc/Makefile
@@ -0,0 +1,13 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../../cddl/dev/dtmalloc
+
+KMOD= dtmalloc
+SRCS= dtmalloc.c
+SRCS+= vnode_if.h
+
+CFLAGS+= -I${.CURDIR}/../../../cddl/compat/opensolaris \
+ -I${.CURDIR}/../../../cddl/contrib/opensolaris/uts/common \
+ -I${.CURDIR}/../../..
+
+.include <bsd.kmod.mk>
diff --git a/sys/modules/dtrace/dtrace/Makefile b/sys/modules/dtrace/dtrace/Makefile
new file mode 100644
index 000000000000..5d16800a8fd1
--- /dev/null
+++ b/sys/modules/dtrace/dtrace/Makefile
@@ -0,0 +1,44 @@
+# $FreeBSD$
+
+ARCHDIR= ${MACHINE_ARCH}
+
+.PATH: ${.CURDIR}/../../../cddl/contrib/opensolaris/uts/common/dtrace
+.PATH: ${.CURDIR}/../../../cddl/kern
+.PATH: ${.CURDIR}/../../../cddl/dev/dtrace
+.PATH: ${.CURDIR}/../../../cddl/dev/dtrace/${ARCHDIR}
+
+KMOD= dtrace
+SRCS= dtrace.c \
+ dtrace_asm.S \
+ dtrace_subr.c
+
+.if ${MACHINE_ARCH} == "amd64" || ${MACHINE_ARCH} == "i386"
+SRCS+= dis_tables.c \
+ instr_size.c
+.endif
+
+SRCS+= bus_if.h device_if.h vnode_if.h
+
+# Needed for dtrace_asm.S
+SRCS+= assym.s
+
+# These are needed for assym.s
+SRCS+= opt_compat.h opt_kstack_pages.h opt_nfs.h
+
+.if ${MACHINE_ARCH} == "i386"
+SRCS+= opt_apic.h
+.endif
+
+CFLAGS+= -I${.CURDIR}/../../../cddl/compat/opensolaris \
+ -I${.CURDIR}/../../../cddl/dev/dtrace \
+ -I${.CURDIR}/../../../cddl/dev/dtrace/${ARCHDIR} \
+ -I${.CURDIR}/../../../cddl/contrib/opensolaris/uts/common \
+ -I${.CURDIR}/../../.. -DDIS_MEM
+
+CFLAGS+= -DSMP -DDEBUG
+
+EXPORT_SYMS= dtrace_register \
+ dtrace_unregister \
+ dtrace_probe_lookup
+
+.include <bsd.kmod.mk>
diff --git a/sys/modules/dtrace/dtrace_test/Makefile b/sys/modules/dtrace/dtrace_test/Makefile
new file mode 100644
index 000000000000..bc096f3a7398
--- /dev/null
+++ b/sys/modules/dtrace/dtrace_test/Makefile
@@ -0,0 +1,14 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../../cddl/dev/dtrace
+
+KMOD= dtrace_test
+SRCS= dtrace_test.c
+
+SRCS+= vnode_if.h
+
+CFLAGS+= -I${.CURDIR}/../../..
+
+CFLAGS+= -D_KERNEL
+
+.include <bsd.kmod.mk>
diff --git a/sys/modules/dtrace/dtraceall/Makefile b/sys/modules/dtrace/dtraceall/Makefile
new file mode 100644
index 000000000000..6f098024611c
--- /dev/null
+++ b/sys/modules/dtrace/dtraceall/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+KMOD= dtraceall
+SRCS= dtraceall.c
+
+CFLAGS+= -I${.CURDIR}/../../..
+
+.include <bsd.kmod.mk>
diff --git a/sys/modules/dtrace/dtraceall/dtraceall.c b/sys/modules/dtrace/dtraceall/dtraceall.c
new file mode 100644
index 000000000000..4f7bdfed5613
--- /dev/null
+++ b/sys/modules/dtrace/dtraceall/dtraceall.c
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2008 John Birrell <jb@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/errno.h>
+
+static int
+dtraceall_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);
+}
+
+DEV_MODULE(dtraceall, dtraceall_modevent, NULL);
+MODULE_VERSION(dtraceall, 1);
+
+/* All the DTrace modules should be dependencies here: */
+MODULE_DEPEND(dtraceall, cyclic, 1, 1, 1);
+MODULE_DEPEND(dtraceall, opensolaris, 1, 1, 1);
+MODULE_DEPEND(dtraceall, dtrace, 1, 1, 1);
+MODULE_DEPEND(dtraceall, dtmalloc, 1, 1, 1);
+#if defined(__amd64__) || defined(__i386__)
+MODULE_DEPEND(dtraceall, fbt, 1, 1, 1);
+#endif
+MODULE_DEPEND(dtraceall, sdt, 1, 1, 1);
+MODULE_DEPEND(dtraceall, systrace, 1, 1, 1);
+MODULE_DEPEND(dtraceall, profile, 1, 1, 1);
diff --git a/sys/modules/dtrace/fasttrap/Makefile b/sys/modules/dtrace/fasttrap/Makefile
new file mode 100644
index 000000000000..104fd9af4454
--- /dev/null
+++ b/sys/modules/dtrace/fasttrap/Makefile
@@ -0,0 +1,19 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../../cddl/dev/fasttrap
+
+KMOD= fasttrap
+SRCS= fasttrap.c
+SRCS+= vnode_if.h
+
+CFLAGS+= -I${.CURDIR}/../../../cddl/compat/opensolaris \
+ -I${.CURDIR}/../../../cddl/contrib/opensolaris/uts/common \
+ -I${.CURDIR}/../../..
+
+.if ${MACHINE_ARCH} == "amd64" || ${MACHINE_ARCH} == "i386"
+CFLAGS+= -I${.CURDIR}/../../../cddl/contrib/opensolaris/uts/intel
+.endif
+
+CFLAGS+= -DSMP -DDEBUG
+
+.include <bsd.kmod.mk>
diff --git a/sys/modules/dtrace/fbt/Makefile b/sys/modules/dtrace/fbt/Makefile
new file mode 100644
index 000000000000..9b69b224a227
--- /dev/null
+++ b/sys/modules/dtrace/fbt/Makefile
@@ -0,0 +1,13 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../../cddl/dev/fbt
+
+KMOD= fbt
+SRCS= fbt.c
+SRCS+= vnode_if.h
+
+CFLAGS+= -I${.CURDIR}/../../../cddl/compat/opensolaris \
+ -I${.CURDIR}/../../../cddl/contrib/opensolaris/uts/common \
+ -I${.CURDIR}/../../..
+
+.include <bsd.kmod.mk>
diff --git a/sys/modules/dtrace/linsystrace/Makefile b/sys/modules/dtrace/linsystrace/Makefile
new file mode 100644
index 000000000000..fb9deb6eeb4e
--- /dev/null
+++ b/sys/modules/dtrace/linsystrace/Makefile
@@ -0,0 +1,16 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../../cddl/dev/systrace
+
+KMOD= linsystrace
+SRCS= systrace.c
+SRCS+= vnode_if.h
+
+CFLAGS+= -I${.CURDIR}/../../../cddl/compat/opensolaris \
+ -I${.CURDIR}/../../../cddl/contrib/opensolaris/uts/common \
+ -I${.CURDIR}/../../../${MACHINE_ARCH}/linux \
+ -I${.CURDIR}/../../..
+
+CFLAGS+= -DLINUX_SYSTRACE
+
+.include <bsd.kmod.mk>
diff --git a/sys/modules/dtrace/lockstat/Makefile b/sys/modules/dtrace/lockstat/Makefile
new file mode 100644
index 000000000000..d387dab96751
--- /dev/null
+++ b/sys/modules/dtrace/lockstat/Makefile
@@ -0,0 +1,14 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../../cddl/dev/lockstat
+
+KMOD= lockstat
+SRCS= lockstat.c
+
+SRCS+= vnode_if.h
+
+CFLAGS+= -I${.CURDIR}/../../../cddl/compat/opensolaris \
+ -I${.CURDIR}/../../../cddl/contrib/opensolaris/uts/common \
+ -I${.CURDIR}/../../..
+
+.include <bsd.kmod.mk>
diff --git a/sys/modules/dtrace/profile/Makefile b/sys/modules/dtrace/profile/Makefile
new file mode 100644
index 000000000000..22cd10d6c9e8
--- /dev/null
+++ b/sys/modules/dtrace/profile/Makefile
@@ -0,0 +1,13 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../../cddl/dev/profile
+
+KMOD= profile
+SRCS= profile.c
+SRCS+= vnode_if.h
+
+CFLAGS+= -I${.CURDIR}/../../../cddl/compat/opensolaris \
+ -I${.CURDIR}/../../../cddl/contrib/opensolaris/uts/common \
+ -I${.CURDIR}/../../..
+
+.include <bsd.kmod.mk>
diff --git a/sys/modules/dtrace/prototype/Makefile b/sys/modules/dtrace/prototype/Makefile
new file mode 100644
index 000000000000..49cc0a89b273
--- /dev/null
+++ b/sys/modules/dtrace/prototype/Makefile
@@ -0,0 +1,13 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../../cddl/dev
+
+KMOD= prototype
+SRCS= prototype.c
+SRCS+= vnode_if.h
+
+CFLAGS+= -I${.CURDIR}/../../../cddl/compat/opensolaris \
+ -I${.CURDIR}/../../../cddl/contrib/opensolaris/uts/common \
+ -I${.CURDIR}/../../..
+
+.include <bsd.kmod.mk>
diff --git a/sys/modules/dtrace/sdt/Makefile b/sys/modules/dtrace/sdt/Makefile
new file mode 100644
index 000000000000..d531d789b803
--- /dev/null
+++ b/sys/modules/dtrace/sdt/Makefile
@@ -0,0 +1,13 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../../cddl/dev/sdt
+
+KMOD= sdt
+SRCS= sdt.c
+SRCS+= vnode_if.h
+
+CFLAGS+= -I${.CURDIR}/../../../cddl/compat/opensolaris \
+ -I${.CURDIR}/../../../cddl/contrib/opensolaris/uts/common \
+ -I${.CURDIR}/../../..
+
+.include <bsd.kmod.mk>
diff --git a/sys/modules/dtrace/systrace/Makefile b/sys/modules/dtrace/systrace/Makefile
new file mode 100644
index 000000000000..d85a365705ee
--- /dev/null
+++ b/sys/modules/dtrace/systrace/Makefile
@@ -0,0 +1,13 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../../cddl/dev/systrace
+
+KMOD= systrace
+SRCS= systrace.c
+SRCS+= vnode_if.h
+
+CFLAGS+= -I${.CURDIR}/../../../cddl/compat/opensolaris \
+ -I${.CURDIR}/../../../cddl/contrib/opensolaris/uts/common \
+ -I${.CURDIR}/../../..
+
+.include <bsd.kmod.mk>
diff --git a/sys/modules/opensolaris/Makefile b/sys/modules/opensolaris/Makefile
new file mode 100644
index 000000000000..5e2195ac8b8f
--- /dev/null
+++ b/sys/modules/opensolaris/Makefile
@@ -0,0 +1,23 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../cddl/compat/opensolaris/kern
+
+KMOD= opensolaris
+SRCS= opensolaris.c \
+ opensolaris_cmn_err.c \
+ opensolaris_kmem.c
+
+.if ${MACHINE_ARCH} == "i386" || ${MACHINE_ARCH} == "amd64" || ${MACHINE_ARCH} == "ia64"
+.PATH: ${.CURDIR}/../../cddl/contrib/opensolaris/common/atomic/${MACHINE_ARCH}
+SRCS+= atomic.S
+.else
+SRCS+= opensolaris_atomic.c
+.endif
+
+CFLAGS+= -I${.CURDIR}/../../cddl/compat/opensolaris \
+ -I${.CURDIR}/../../cddl/contrib/opensolaris/uts/common \
+ -I${.CURDIR}/../..
+
+IGNORE_PRAGMA= 1
+
+.include <bsd.kmod.mk>
diff --git a/sys/modules/zfs/Makefile b/sys/modules/zfs/Makefile
index 8ed90e3b5347..0cd4361e4c65 100644
--- a/sys/modules/zfs/Makefile
+++ b/sys/modules/zfs/Makefile
@@ -4,11 +4,13 @@ KMOD= zfs
SRCS= vnode_if.h
-.PATH: ${.CURDIR}/../../cddl/contrib/opensolaris/common/acl
+SUNW= ${.CURDIR}/../../cddl/contrib/opensolaris
+
+.PATH: ${SUNW}/common/acl
SRCS+= acl_common.c
-.PATH: ${.CURDIR}/../../cddl/contrib/opensolaris/common/avl
+.PATH: ${SUNW}/common/avl
SRCS+= avl.c
-.PATH: ${.CURDIR}/../../cddl/contrib/opensolaris/common/nvpair
+.PATH: ${SUNW}/common/nvpair
SRCS+= nvpair.c
.PATH: ${.CURDIR}/../../cddl/compat/opensolaris/kern
@@ -22,28 +24,28 @@ SRCS+= opensolaris_vfs.c
SRCS+= opensolaris_zone.c
.if ${MACHINE_ARCH} == "i386" || ${MACHINE_ARCH} == "amd64" || ${MACHINE_ARCH} == "ia64" || ${MACHINE_ARCH} == "sparc64"
-.PATH: ${.CURDIR}/../../cddl/contrib/opensolaris/common/atomic/${MACHINE_ARCH}
+.PATH: ${SUNW}/common/atomic/${MACHINE_ARCH}
SRCS+= atomic.S
.else
.PATH: ${.CURDIR}/../../cddl/compat/opensolaris/kern
SRCS+= opensolaris_atomic.c
.endif
-.PATH: ${.CURDIR}/../../cddl/contrib/opensolaris/uts/common/fs
+.PATH: ${SUNW}/uts/common/fs
SRCS+= gfs.c
-.PATH: ${.CURDIR}/../../cddl/contrib/opensolaris/uts/common/os
+.PATH: ${SUNW}/uts/common/os
SRCS+= callb.c
SRCS+= list.c
SRCS+= nvpair_alloc_system.c
SRCS+= taskq.c
-.PATH: ${.CURDIR}/../../cddl/contrib/opensolaris/uts/common/rpc
+.PATH: ${SUNW}/uts/common/rpc
SRCS+= xdr.c
SRCS+= xdr_array.c
SRCS+= xdr_mem.c
-.PATH: ${.CURDIR}/../../cddl/contrib/opensolaris/uts/common/zmod
+.PATH: ${SUNW}/uts/common/zmod
SRCS+= adler32.c
SRCS+= crc32.c
SRCS+= deflate.c
@@ -55,9 +57,9 @@ SRCS+= zmod.c
SRCS+= zmod_subr.c
SRCS+= zutil.c
-.PATH: ${.CURDIR}/../../cddl/contrib/opensolaris/common/zfs
-.include "${.CURDIR}/../../cddl/contrib/opensolaris/uts/common/Makefile.files"
-.PATH: ${.CURDIR}/../../cddl/contrib/opensolaris/uts/common/fs/zfs
+.PATH: ${SUNW}/common/zfs
+.include "${SUNW}/uts/common/Makefile.files"
+.PATH: ${SUNW}/uts/common/fs/zfs
ZFS_SRCS= ${ZFS_OBJS:C/.o$/.c/}
SRCS+= ${ZFS_SRCS}
SRCS+= vdev_geom.c
@@ -68,28 +70,31 @@ SRCS+= vdev_geom.c
# Use FreeBSD's namecache.
CFLAGS+=-DFREEBSD_NAMECACHE
-CWARNFLAGS=-Wall
-CWARNFLAGS+=-Wno-unknown-pragmas
-CWARNFLAGS+=-Wno-missing-braces
-CWARNFLAGS+=-Wno-parentheses
-CWARNFLAGS+=-Wno-uninitialized
-CWARNFLAGS+=-Wno-unused
-CWARNFLAGS+=-Wno-switch
-
-CFLAGS+=-D_SOLARIS_C_SOURCE
-
CFLAGS+=-I${.CURDIR}/../../cddl/compat/opensolaris
-CFLAGS+=-I${.CURDIR}/../../cddl/contrib/opensolaris/uts/common/fs/zfs
-CFLAGS+=-I${.CURDIR}/../../cddl/contrib/opensolaris/uts/common/zmod
-CFLAGS+=-I${.CURDIR}/../../cddl/contrib/opensolaris/uts/common
+CFLAGS+=-I${SUNW}/uts/common/fs/zfs
+CFLAGS+=-I${SUNW}/uts/common/zmod
+CFLAGS+=-I${SUNW}/uts/common
CFLAGS+=-I${.CURDIR}/../..
-CFLAGS+=-I${.CURDIR}/../../cddl/contrib/opensolaris/common/zfs
-CFLAGS+=-I${.CURDIR}/../../cddl/contrib/opensolaris/common
+CFLAGS+=-I${SUNW}/common/zfs
+CFLAGS+=-I${SUNW}/common
CFLAGS+=-I${.CURDIR}/../../../include
-
-WARNS?=1
+CFLAGS+=-DBUILDING_ZFS
#CFLAGS+=-DDEBUG=1
#DEBUG_FLAGS=-g
.include <bsd.kmod.mk>
+
+CWARNFLAGS+=-Wno-unknown-pragmas
+CWARNFLAGS+=-Wno-missing-prototypes
+CWARNFLAGS+=-Wno-undef
+CWARNFLAGS+=-Wno-strict-prototypes
+CWARNFLAGS+=-Wno-cast-qual
+CWARNFLAGS+=-Wno-parentheses
+CWARNFLAGS+=-Wno-redundant-decls
+CWARNFLAGS+=-Wno-missing-braces
+CWARNFLAGS+=-Wno-uninitialized
+CWARNFLAGS+=-Wno-unused
+CWARNFLAGS+=-Wno-inline
+CWARNFLAGS+=-Wno-switch
+CWARNFLAGS+=-Wno-pointer-arith
diff --git a/sys/powerpc/include/param.h b/sys/powerpc/include/param.h
index 6a2018eab0d8..549e0b9da24f 100644
--- a/sys/powerpc/include/param.h
+++ b/sys/powerpc/include/param.h
@@ -67,8 +67,8 @@
#endif
#define MID_MACHINE MID_POWERPC
-#ifdef SMP
-#define MAXCPU 2
+#if defined(SMP) || defined(KLD_MODULE)
+#define MAXCPU 2
#else
#define MAXCPU 1
#endif /* SMP */
diff --git a/sys/sparc64/include/param.h b/sys/sparc64/include/param.h
index dd86233a943b..dd8fb1cbb431 100644
--- a/sys/sparc64/include/param.h
+++ b/sys/sparc64/include/param.h
@@ -59,7 +59,7 @@
#endif
#define MID_MACHINE MID_SPARC64
-#ifdef SMP
+#if defined(SMP) || defined(KLD_MODULE)
#define MAXCPU 16
#else
#define MAXCPU 1
diff --git a/sys/sun4v/include/param.h b/sys/sun4v/include/param.h
index 6e1d389b304c..a4fe98d3cf7b 100644
--- a/sys/sun4v/include/param.h
+++ b/sys/sun4v/include/param.h
@@ -59,7 +59,7 @@
#endif
#define MID_MACHINE MID_SPARC64
-#ifdef SMP
+#if defined(SMP) || defined(KLD_MODULE)
#define MAXCPU 32
#else
#define MAXCPU 1
diff --git a/sys/sys/dtrace_bsd.h b/sys/sys/dtrace_bsd.h
new file mode 100644
index 000000000000..e0adbe624a90
--- /dev/null
+++ b/sys/sys/dtrace_bsd.h
@@ -0,0 +1,111 @@
+/*-
+ * Copyright (c) 2007-2008 John Birrell (jb@freebsd.org)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ *
+ * This file contains BSD shims for Sun's DTrace code.
+ */
+
+#ifndef _SYS_DTRACE_BSD_H
+#define _SYS_DTRACE_BSD_H
+
+/* Forward definitions: */
+struct trapframe;
+struct thread;
+
+/*
+ * Cyclic clock function type definition used to hook the cyclic
+ * subsystem into the appropriate timer interrupt.
+ */
+typedef void (*cyclic_clock_func_t)(struct trapframe *);
+
+/*
+ * These external variables are actually machine-dependent, so
+ * they might not actually exist.
+ *
+ * Defining them here avoids a proliferation of header files.
+ */
+extern cyclic_clock_func_t lapic_cyclic_clock_func[];
+
+/*
+ * The dtrace module handles traps that occur during a DTrace probe.
+ * This type definition is used in the trap handler to provide a
+ * hook for the dtrace module to register it's handler with.
+ */
+typedef int (*dtrace_trap_func_t)(struct trapframe *, u_int);
+
+int dtrace_trap(struct trapframe *, u_int);
+
+extern dtrace_trap_func_t dtrace_trap_func;
+
+/* Used by the machine dependent trap() code. */
+typedef int (*dtrace_invop_func_t)(uintptr_t, uintptr_t *, uintptr_t);
+typedef void (*dtrace_doubletrap_func_t)(void);
+
+/* Global variables in trap.c */
+extern dtrace_invop_func_t dtrace_invop_func;
+extern dtrace_doubletrap_func_t dtrace_doubletrap_func;
+
+/* Virtual time hook function type. */
+typedef void (*dtrace_vtime_switch_func_t)(struct thread *);
+
+extern int dtrace_vtime_active;
+extern dtrace_vtime_switch_func_t dtrace_vtime_switch_func;
+
+/* The fasttrap module hooks into the fork, exit and exit. */
+typedef void (*dtrace_fork_func_t)(struct proc *, struct proc *);
+typedef void (*dtrace_execexit_func_t)(struct proc *);
+
+/* Global variable in kern_fork.c */
+extern dtrace_fork_func_t dtrace_fasttrap_fork;
+
+/* Global variable in kern_exec.c */
+extern dtrace_execexit_func_t dtrace_fasttrap_exec;
+
+/* Global variable in kern_exit.c */
+extern dtrace_execexit_func_t dtrace_fasttrap_exit;
+
+/* The dtmalloc provider hooks into malloc. */
+typedef void (*dtrace_malloc_probe_func_t)(u_int32_t, uintptr_t arg0,
+ uintptr_t arg1, uintptr_t arg2, uintptr_t arg3, uintptr_t arg4);
+
+extern dtrace_malloc_probe_func_t dtrace_malloc_probe;
+
+/*
+ * Functions which allow the dtrace module to check that the kernel
+ * hooks have been compiled with sufficient space for it's private
+ * structures.
+ */
+size_t kdtrace_proc_size(void);
+size_t kdtrace_thread_size(void);
+
+/*
+ * OpenSolaris compatible time functions returning nanoseconds.
+ * On OpenSolaris these return hrtime_t which we define as uint64_t.
+ */
+uint64_t dtrace_gethrtime(void);
+uint64_t dtrace_gethrestime(void);
+
+#endif /* _SYS_DTRACE_BSD_H */
diff --git a/sys/sys/elf.h b/sys/sys/elf.h
index 54df9336ab9c..b2d6b77c98e2 100644
--- a/sys/sys/elf.h
+++ b/sys/sys/elf.h
@@ -38,8 +38,4 @@
#include <sys/elf32.h>
#include <sys/elf64.h>
-#ifdef _SOLARIS_C_SOURCE
-#include <sys/_elf_solaris.h>
-#endif
-
#endif /* !_SYS_ELF_H_ */
diff --git a/sys/sys/elf_common.h b/sys/sys/elf_common.h
index b941fd70aaf3..ef2ab0fd21da 100644
--- a/sys/sys/elf_common.h
+++ b/sys/sys/elf_common.h
@@ -431,6 +431,9 @@ typedef struct {
#define STV_INTERNAL 0x1 /* Special meaning in relocatable objects. */
#define STV_HIDDEN 0x2 /* Not visible. */
#define STV_PROTECTED 0x3 /* Visible but not preemptible. */
+#define STV_EXPORTED 0x4
+#define STV_SINGLETON 0x5
+#define STV_ELIMINATE 0x6
/* Special symbol table indexes. */
#define STN_UNDEF 0 /* Undefined symbol index. */
diff --git a/sys/sys/kdb.h b/sys/sys/kdb.h
index b45cd70d136c..d0bac955b813 100644
--- a/sys/sys/kdb.h
+++ b/sys/sys/kdb.h
@@ -105,5 +105,6 @@ extern const char * volatile kdb_why;
#define KDB_WHY_MAC "mac" /* MAC Framework. */
#define KDB_WHY_POWERPC "powerpc" /* Unhandled powerpc intr. */
#define KDB_WHY_UNIONFS "unionfs" /* Unionfs bug. */
+#define KDB_WHY_DTRACE "dtrace" /* DTrace action entered debugger. */
#endif /* !_SYS_KDB_H_ */
diff --git a/sys/sys/kernel.h b/sys/sys/kernel.h
index 9150d8153fe9..90cf72a287fe 100644
--- a/sys/sys/kernel.h
+++ b/sys/sys/kernel.h
@@ -68,9 +68,7 @@ extern int stathz; /* statistics clock's frequency */
extern int profhz; /* profiling clock's frequency */
extern int profprocs; /* number of process's profiling */
extern int ticks;
-#ifndef _SOLARIS_C_SOURCE
extern int lbolt; /* once a second sleep address */
-#endif
#endif /* _KERNEL */
@@ -115,6 +113,7 @@ enum sysinit_sub_id {
SI_SUB_EVENTHANDLER = 0x1C00000, /* eventhandler init */
SI_SUB_KLD = 0x2000000, /* KLD and module setup */
SI_SUB_CPU = 0x2100000, /* CPU resource(s)*/
+ SI_SUB_KDTRACE = 0x2140000, /* Kernel dtrace hooks */
SI_SUB_MAC = 0x2180000, /* TrustedBSD MAC subsystem */
SI_SUB_MAC_POLICY = 0x21C0000, /* TrustedBSD MAC policies */
SI_SUB_MAC_LATE = 0x21D0000, /* TrustedBSD MAC subsystem */
@@ -123,6 +122,8 @@ enum sysinit_sub_id {
SI_SUB_DDB_SERVICES = 0x2380000, /* capture, scripting, etc. */
SI_SUB_RUN_QUEUE = 0x2400000, /* set up run queue*/
SI_SUB_KTRACE = 0x2480000, /* ktrace */
+ SI_SUB_OPENSOLARIS = 0x2490000, /* OpenSolaris compatibility */
+ SI_SUB_CYCLIC = 0x24A0000, /* Cyclic timers */
SI_SUB_AUDIT = 0x24C0000, /* audit */
SI_SUB_CREATE_INIT = 0x2500000, /* create init process*/
SI_SUB_SCHED_IDLE = 0x2600000, /* required idle procs */
@@ -133,6 +134,9 @@ enum sysinit_sub_id {
SI_SUB_DEVFS = 0x2F00000, /* devfs ready for devices */
SI_SUB_INIT_IF = 0x3000000, /* prep for net interfaces */
SI_SUB_NETGRAPH = 0x3010000, /* Let Netgraph initialize */
+ SI_SUB_DTRACE = 0x3020000, /* DTrace subsystem */
+ SI_SUB_DTRACE_PROVIDER = 0x3048000, /* DTrace providers */
+ SI_SUB_DTRACE_ANON = 0x308C000, /* DTrace anon enabling */
SI_SUB_DRIVERS = 0x3100000, /* Let Drivers initialize */
SI_SUB_CONFIGURE = 0x3800000, /* Configure devices */
SI_SUB_VFS = 0x4000000, /* virtual filesystem*/
@@ -155,7 +159,6 @@ enum sysinit_sub_id {
SI_SUB_ROOT_CONF = 0xb000000, /* Find root devices */
SI_SUB_DUMP_CONF = 0xb200000, /* Find dump devices */
SI_SUB_RAID = 0xb380000, /* Configure GEOM classes */
- SI_SUB_MOUNT_ROOT = 0xb400000, /* root mount*/
SI_SUB_SWAP = 0xc000000, /* swap */
SI_SUB_INTRINSIC_POST = 0xd000000, /* proc 0 cleanup*/
SI_SUB_SYSCALLS = 0xd800000, /* register system calls */
diff --git a/sys/sys/linker.h b/sys/sys/linker.h
index c4903b09d8ce..4ba7a40072ac 100644
--- a/sys/sys/linker.h
+++ b/sys/sys/linker.h
@@ -59,6 +59,8 @@ typedef struct linker_symval {
size_t size;
} linker_symval_t;
+typedef int (*linker_function_nameval_callback_t)(linker_file_t, int, linker_symval_t *, void *);
+
struct common_symbol {
STAILQ_ENTRY(common_symbol) link;
char* name;
@@ -73,6 +75,7 @@ struct linker_file {
#define LINKER_FILE_LINKED 0x1 /* file has been fully linked */
TAILQ_ENTRY(linker_file) link; /* list of all loaded files */
char* filename; /* file which was loaded */
+ char* pathname; /* file name with full path */
int id; /* unique id */
caddr_t address; /* load address */
size_t size; /* size of file */
@@ -81,6 +84,18 @@ struct linker_file {
STAILQ_HEAD(, common_symbol) common; /* list of common symbols */
TAILQ_HEAD(, module) modules; /* modules in this file */
TAILQ_ENTRY(linker_file) loaded; /* preload dependency support */
+ int loadcnt; /* load counter value */
+
+ /*
+ * Function Boundary Tracing (FBT) or Statically Defined Tracing (SDT)
+ * fields.
+ */
+ int nenabled; /* number of enabled probes. */
+ int fbt_nentries; /* number of fbt entries created. */
+ void *sdt_probes;
+ int sdt_nentries;
+ size_t sdt_nprobes;
+ size_t sdt_size;
};
/*
@@ -141,6 +156,12 @@ int linker_file_lookup_set(linker_file_t _file, const char *_name,
void *_start, void *_stop, int *_count);
/*
+ * List all functions in a file.
+ */
+int linker_file_function_listall(linker_file_t,
+ linker_function_nameval_callback_t, void *);
+
+/*
* Functions soley for use by the linker class handlers.
*/
int linker_add_class(linker_class_t _cls);
@@ -246,6 +267,20 @@ int elf_reloc_local(linker_file_t _lf, Elf_Addr base, const void *_rel, int _typ
const Elf_Sym *elf_get_sym(linker_file_t _lf, Elf_Size _symidx);
const char *elf_get_symname(linker_file_t _lf, Elf_Size _symidx);
+typedef struct linker_ctf {
+ const uint8_t *ctftab; /* Decompressed CTF data. */
+ int ctfcnt; /* Number of CTF data bytes. */
+ const Elf_Sym *symtab; /* Ptr to the symbol table. */
+ int nsym; /* Number of symbols. */
+ const char *strtab; /* Ptr to the string table. */
+ int strcnt; /* Number of string bytes. */
+ uint32_t **ctfoffp; /* Ptr to array of obj/fnc offsets. */
+ uint32_t **typoffp; /* Ptr to array of type offsets. */
+ long *typlenp; /* Ptr to number of type data entries. */
+} linker_ctf_t;
+
+int linker_ctf_get(linker_file_t, linker_ctf_t *);
+
int elf_cpu_load_file(linker_file_t);
int elf_cpu_unload_file(linker_file_t);
@@ -253,15 +288,28 @@ int elf_cpu_unload_file(linker_file_t);
#define ELF_RELOC_REL 1
#define ELF_RELOC_RELA 2
+/*
+ * This is version 1 of the KLD file status structure. It is identified
+ * by its _size_ in the version field.
+ */
+struct kld_file_stat_1 {
+ int version; /* set to sizeof(struct kld_file_stat_1) */
+ char name[MAXPATHLEN];
+ int refs;
+ int id;
+ caddr_t address; /* load address */
+ size_t size; /* size in bytes */
+};
#endif /* _KERNEL */
struct kld_file_stat {
- int version; /* set to sizeof(linker_file_stat) */
+ int version; /* set to sizeof(struct kld_file_stat) */
char name[MAXPATHLEN];
int refs;
int id;
caddr_t address; /* load address */
size_t size; /* size in bytes */
+ char pathname[MAXPATHLEN];
};
struct kld_sym_lookup {
diff --git a/sys/sys/malloc.h b/sys/sys/malloc.h
index e59b9ac64560..33b287f83d92 100644
--- a/sys/sys/malloc.h
+++ b/sys/sys/malloc.h
@@ -80,7 +80,16 @@ struct malloc_type_stats {
uint64_t _mts_reserved3; /* Reserved field. */
};
+/*
+ * Index definitions for the mti_probes[] array.
+ */
+#define DTMALLOC_PROBE_MALLOC 0
+#define DTMALLOC_PROBE_FREE 1
+#define DTMALLOC_PROBE_MAX 2
+
struct malloc_type_internal {
+ uint32_t mti_probes[DTMALLOC_PROBE_MAX];
+ /* DTrace probe ID array. */
struct malloc_type_stats mti_stats[MAXCPU];
};
@@ -173,7 +182,11 @@ MALLOC_DECLARE(M_IOV);
extern struct mtx malloc_mtx;
-/* XXX struct malloc_type is unused for contig*(). */
+/*
+ * Function type used when iterating over the list of malloc types.
+ */
+typedef void malloc_type_list_func_t(struct malloc_type *, void *);
+
void contigfree(void *addr, unsigned long size, struct malloc_type *type);
void *contigmalloc(unsigned long size, struct malloc_type *type, int flags,
vm_paddr_t low, vm_paddr_t high, unsigned long alignment,
@@ -184,6 +197,7 @@ void malloc_init(void *);
int malloc_last_fail(void);
void malloc_type_allocated(struct malloc_type *type, unsigned long size);
void malloc_type_freed(struct malloc_type *type, unsigned long size);
+void malloc_type_list(malloc_type_list_func_t *, void *);
void malloc_uninit(void *);
void *realloc(void *addr, unsigned long size, struct malloc_type *type,
int flags);
diff --git a/sys/sys/param.h b/sys/sys/param.h
index 55d5c673edc0..788c9409bd00 100644
--- a/sys/sys/param.h
+++ b/sys/sys/param.h
@@ -305,20 +305,4 @@ __END_DECLS
#define ctodb(db) /* calculates pages to devblks */ \
((db) << (PAGE_SHIFT - DEV_BSHIFT))
-/*
- * Solaris compatibility definitions.
- */
-#ifdef _SOLARIS_C_SOURCE
-#define PAGESIZE PAGE_SIZE
-
-/*
- * The OpenSolaris version is set according to the version last imported
- * from http://dlc.sun.com/osol/on/downloads/current/. In FreeBSD header
- * files it can be used to determine the level of compatibility that the
- * FreeBSD headers provide to OpenSolaris code. Perhaps one day there
- * will be a really, really Single Unix Specification.
- */
-#define __OpenSolaris_version 20060731
-#endif
-
#endif /* _SYS_PARAM_H_ */
diff --git a/sys/sys/proc.h b/sys/sys/proc.h
index 0714c30c77bc..f66f68ad770a 100644
--- a/sys/sys/proc.h
+++ b/sys/sys/proc.h
@@ -164,6 +164,8 @@ struct trapframe;
struct turnstile;
struct mqueue_notifier;
struct cpuset;
+struct kdtrace_proc;
+struct kdtrace_thread;
/*
* Here we define the two structures used for process information.
@@ -301,6 +303,8 @@ struct thread {
uint64_t td_incruntime; /* (t) Cpu ticks to transfer to proc. */
struct cpuset *td_cpuset; /* (t) CPU affinity mask. */
struct file *td_fpop; /* (k) file referencing cdev under op */
+ struct kdtrace_thread *td_dtrace; /* (*) DTrace-specific data. */
+ int td_errno; /* Error returned by last syscall. */
};
struct mtx *thread_lock_block(struct thread *);
@@ -587,6 +591,7 @@ struct proc {
struct p_sched *p_sched; /* (*) Scheduler-specific data. */
STAILQ_HEAD(, ktr_request) p_ktr; /* (o) KTR event queue. */
LIST_HEAD(, mqueue_notifier) p_mqnotifier; /* (c) mqueue notifiers.*/
+ struct kdtrace_proc *p_dtrace; /* (*) DTrace-specific data. */
};
#define p_session p_pgrp->pg_session
diff --git a/sys/sys/sdt.h b/sys/sys/sdt.h
new file mode 100644
index 000000000000..2bf2f2de77af
--- /dev/null
+++ b/sys/sys/sdt.h
@@ -0,0 +1,179 @@
+/*-
+ * Copyright 2006-2008 John Birrell <jb@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ *
+ * Statically Defined Tracing (SDT) definitions.
+ *
+ */
+
+#ifndef _SYS_SDT_H
+#define _SYS_SDT_H
+
+/* Stub these for the time being. */
+#define DTRACE_PROBE(name)
+#define DTRACE_PROBE1(name, type1, arg1)
+#define DTRACE_PROBE2(name, type1, arg1, type2, arg2)
+#define DTRACE_PROBE3(name, type1, arg1, type2, arg2, type3, arg3)
+#define DTRACE_PROBE4(name, type1, arg1, type2, arg2, type3, arg3, type4, arg4)
+
+#ifndef _KERNEL
+
+/* The promise of things to come. Worlds to explore. People to meet. Things to do. */
+
+#else
+
+#ifndef KDTRACE_HOOKS
+
+#define SDT_PROVIDER_DEFINE(prov)
+#define SDT_PROVIDER_DECLARE(prov)
+#define SDT_PROBE_DEFINE(prov, mod, func, name)
+#define SDT_PROBE_DECLARE(prov, mod, func, name)
+#define SDT_PROBE(prov, mod, func, name, arg0, arg1, arg2, arg3, arg4)
+#define SDT_PROBE_ARGTYPE(prov, mod, func, name, num, type)
+
+#else
+
+/*
+ * This type definition must match that of dtrace_probe. It is defined this
+ * way to avoid having to rely on CDDL code.
+ */
+typedef void (*sdt_probe_func_t)(u_int32_t, uintptr_t arg0, uintptr_t arg1,
+ uintptr_t arg2, uintptr_t arg3, uintptr_t arg4);
+
+/*
+ * The hook for the probe function. See kern_sdt.c which defaults this to
+ * it's own stub. The 'sdt' provider will set it to dtrace_probe when it
+ * loads.
+ */
+extern sdt_probe_func_t sdt_probe_func;
+
+typedef enum {
+ SDT_UNINIT = 1,
+ SDT_INIT,
+} sdt_state_t;
+
+struct sdt_probe;
+struct sdt_provider;
+
+struct sdt_argtype {
+ int ndx; /* Argument index. */
+ const char *type; /* Argument type string. */
+ TAILQ_ENTRY(sdt_argtype)
+ argtype_entry; /* Argument type list entry. */
+ struct sdt_probe
+ *probe; /* Ptr to the probe structure. */
+};
+
+struct sdt_probe {
+ int version; /* Set to sizeof(struct sdt_ref). */
+ sdt_state_t state;
+ struct sdt_provider
+ *prov; /* Ptr to the provider structure. */
+ TAILQ_ENTRY(sdt_probe)
+ probe_entry; /* SDT probe list entry. */
+ TAILQ_HEAD(argtype_list_head, sdt_argtype) argtype_list;
+ const char *mod;
+ const char *func;
+ const char *name;
+ id_t id; /* DTrace probe ID. */
+ int n_args; /* Number of arguments. */
+};
+
+struct sdt_provider {
+ const char *name; /* Provider name. */
+ TAILQ_ENTRY(sdt_provider)
+ prov_entry; /* SDT provider list entry. */
+ TAILQ_HEAD(probe_list_head, sdt_probe) probe_list;
+ uintptr_t id; /* DTrace provider ID. */
+};
+
+#define SDT_PROVIDER_DEFINE(prov) \
+ struct sdt_provider sdt_provider_##prov[1] = { \
+ { #prov, { NULL, NULL }, { NULL, NULL } } \
+ }; \
+ SYSINIT(sdt_provider_##prov##_init, SI_SUB_KDTRACE, \
+ SI_ORDER_SECOND, sdt_provider_register, \
+ sdt_provider_##prov ); \
+ SYSUNINIT(sdt_provider_##prov##_uninit, SI_SUB_KDTRACE, \
+ SI_ORDER_SECOND, sdt_provider_deregister, \
+ sdt_provider_##prov )
+
+#define SDT_PROVIDER_DECLARE(prov) \
+ extern struct sdt_provider sdt_provider_##prov[1]
+
+#define SDT_PROBE_DEFINE(prov, mod, func, name) \
+ struct sdt_probe sdt_##prov##_##mod##_##func##_##name[1] = { \
+ { sizeof(struct sdt_probe), 0, sdt_provider_##prov, \
+ { NULL, NULL }, { NULL, NULL }, #mod, #func, #name, 0, 0 } \
+ }; \
+ SYSINIT(sdt_##prov##_##mod##_##func##_##name##_init, SI_SUB_KDTRACE, \
+ SI_ORDER_SECOND + 1, sdt_probe_register, \
+ sdt_##prov##_##mod##_##func##_##name ); \
+ SYSUNINIT(sdt_##prov##_##mod##_##func##_##name##_uninit, \
+ SI_SUB_KDTRACE, SI_ORDER_SECOND + 1, sdt_probe_deregister, \
+ sdt_##prov##_##mod##_##func##_##name )
+
+#define SDT_PROBE_DECLARE(prov, mod, func, name) \
+ extern struct sdt_probe sdt_##prov##_##mod##_##func##_##name[1]
+
+#define SDT_PROBE(prov, mod, func, name, arg0, arg1, arg2, arg3, arg4) \
+ if (sdt_##prov##_##mod##_##func##_##name->id) \
+ (*sdt_probe_func)(sdt_##prov##_##mod##_##func##_##name->id, \
+ (uintptr_t) arg0, (uintptr_t) arg1, (uintptr_t) arg2, \
+ (uintptr_t) arg3, (uintptr_t) arg4)
+
+#define SDT_PROBE_ARGTYPE(prov, mod, func, name, num, type) \
+ struct sdt_argtype sdt_##prov##_##mod##_##func##_##name##num[1] \
+ = { { num, type, { NULL, NULL }, \
+ sdt_##prov##_##mod##_##func##_##name } \
+ }; \
+ SYSINIT(sdt_##prov##_##mod##_##func##_##name##num##_init, \
+ SI_SUB_KDTRACE, SI_ORDER_SECOND + 2, sdt_argtype_register, \
+ sdt_##prov##_##mod##_##func##_##name##num ); \
+ SYSUNINIT(sdt_##prov##_##mod##_##func##_##name##num##_uninit, \
+ SI_SUB_KDTRACE, SI_ORDER_SECOND + 2, sdt_argtype_deregister, \
+ sdt_##prov##_##mod##_##func##_##name##num )
+
+typedef int (*sdt_argtype_listall_func_t)(struct sdt_argtype *, void *);
+typedef int (*sdt_probe_listall_func_t)(struct sdt_probe *, void *);
+typedef int (*sdt_provider_listall_func_t)(struct sdt_provider *, void *);
+
+void sdt_argtype_deregister(void *);
+void sdt_argtype_register(void *);
+void sdt_probe_deregister(void *);
+void sdt_probe_register(void *);
+void sdt_provider_deregister(void *);
+void sdt_provider_register(void *);
+void sdt_probe_stub(u_int32_t, uintptr_t arg0, uintptr_t arg1, uintptr_t arg2,
+ uintptr_t arg3, uintptr_t arg4);
+int sdt_argtype_listall(struct sdt_probe *, sdt_argtype_listall_func_t, void *);
+int sdt_probe_listall(struct sdt_provider *, sdt_probe_listall_func_t, void *);
+int sdt_provider_listall(sdt_provider_listall_func_t,void *);
+
+#endif /* KDTRACE_HOOKS */
+
+#endif /* _KERNEL */
+
+#endif /* _SYS_SDT_H */
diff --git a/sys/sys/smp.h b/sys/sys/smp.h
index f2b2f523213f..2fe44c27820f 100644
--- a/sys/sys/smp.h
+++ b/sys/sys/smp.h
@@ -100,6 +100,7 @@ void forward_roundrobin(void);
int restart_cpus(cpumask_t);
int stop_cpus(cpumask_t);
void smp_rendezvous_action(void);
+void smp_no_rendevous_barrier(void *);
extern struct mtx smp_ipi_mtx;
#endif /* SMP */
@@ -107,6 +108,11 @@ void smp_rendezvous(void (*)(void *),
void (*)(void *),
void (*)(void *),
void *arg);
+void smp_rendezvous_cpus(cpumask_t,
+ void (*)(void *),
+ void (*)(void *),
+ void (*)(void *),
+ void *arg);
#endif /* !LOCORE */
#endif /* _KERNEL */
#endif /* _SYS_SMP_H_ */
diff --git a/sys/sys/stat.h b/sys/sys/stat.h
index d3572c34e0ea..10a3739cc440 100644
--- a/sys/sys/stat.h
+++ b/sys/sys/stat.h
@@ -304,14 +304,6 @@ struct nstat {
#endif /* __BSD_VISIBLE */
-/*
- * Solaris compatibility definitions.
- */
-#ifdef _SOLARIS_C_SOURCE
-#define stat64 stat
-#define fstat64 fstat
-#endif
-
#ifndef _KERNEL
__BEGIN_DECLS
#if __BSD_VISIBLE
diff --git a/sys/sys/sysent.h b/sys/sys/sysent.h
index e0ea4b02fa0d..e712fc54c4a1 100644
--- a/sys/sys/sysent.h
+++ b/sys/sys/sysent.h
@@ -48,7 +48,7 @@ typedef void (*systrace_probe_func_t)(u_int32_t, int, struct sysent *, void *);
* Used by loaded syscalls to convert arguments to a DTrace array
* of 64-bit arguments.
*/
-typedef void (*systrace_args_func_t)(void *, u_int64_t *, int *);
+typedef void (*systrace_args_func_t)(int, void *, u_int64_t *, int *);
extern systrace_probe_func_t systrace_probe_func;
diff --git a/sys/sys/systm.h b/sys/sys/systm.h
index 10946db37c9c..9410b8728ab7 100644
--- a/sys/sys/systm.h
+++ b/sys/sys/systm.h
@@ -217,12 +217,6 @@ u_long casuword(volatile u_long *p, u_long oldval, u_long newval);
void realitexpire(void *);
-/*
- * Cyclic clock function type definition used to hook the cyclic
- * subsystem into the appropriate timer interrupt.
- */
-typedef void (*cyclic_clock_func_t)(void);
-
void hardclock(int usermode, uintfptr_t pc);
void hardclock_cpu(int usermode);
void softclock(void *);
diff --git a/sys/sys/time.h b/sys/sys/time.h
index 0350c203eeb9..657f0d49b409 100644
--- a/sys/sys/time.h
+++ b/sys/sys/time.h
@@ -304,8 +304,6 @@ int ratecheck(struct timeval *, const struct timeval *);
void timevaladd(struct timeval *t1, const struct timeval *t2);
void timevalsub(struct timeval *t1, const struct timeval *t2);
int tvtohz(struct timeval *tv);
-uint64_t dtrace_gethrtime(void);
-uint64_t dtrace_gethrestime(void);
#else /* !_KERNEL */
#include <time.h>
@@ -324,27 +322,4 @@ __END_DECLS
#endif /* !_KERNEL */
-/*
- * Solaris compatibility definitions.
- */
-#ifdef _SOLARIS_C_SOURCE
-/*
- * Definitions for commonly used resolutions.
- */
-#define SEC 1
-#define MILLISEC 1000
-#define MICROSEC 1000000
-#define NANOSEC 1000000000
-
-typedef longlong_t hrtime_t;
-
-#ifndef _KERNEL
-static __inline hrtime_t gethrtime(void) {
- struct timespec ts;
- clock_gettime(CLOCK_UPTIME,&ts);
- return (((u_int64_t) ts.tv_sec) * NANOSEC + ts.tv_nsec);
-}
-#endif
-#endif /* _SOLARIS_C_SOURCE */
-
#endif /* !_SYS_TIME_H_ */
diff --git a/sys/sys/types.h b/sys/sys/types.h
index 37b8b0534831..929407f49237 100644
--- a/sys/sys/types.h
+++ b/sys/sys/types.h
@@ -301,36 +301,6 @@ typedef struct vm_page *vm_page_t;
#endif /* !_KERNEL */
/*
- * Solaris compatibility definitions.
- */
-#ifdef _SOLARIS_C_SOURCE
-typedef u_int uint_t;
-typedef u_char uchar_t;
-typedef u_short ushort_t;
-typedef u_long ulong_t;
-
-typedef long long longlong_t;
-typedef unsigned long long u_longlong_t;
-
-typedef off_t off64_t;
-
-typedef id_t taskid_t;
-typedef id_t projid_t;
-typedef id_t poolid_t;
-typedef id_t zoneid_t;
-typedef id_t ctid_t;
-
-#ifndef _KERNEL
-#if defined(__XOPEN_OR_POSIX)
-typedef enum { _B_FALSE, _B_TRUE } boolean_t;
-#else
-typedef enum { B_FALSE, B_TRUE } boolean_t;
-#endif /* defined(__XOPEN_OR_POSIX) */
-#endif
-
-#endif /* _SOLARIS_C_SOURCE */
-
-/*
* The following are all things that really shouldn't exist in this header,
* since its purpose is to provide typedefs, not miscellaneous doodads.
*/