* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* #pragma ident "@(#)systrace.c 1.6 06/09/19 SMI" */
-#if !defined(__APPLE__)
-#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
-#else
-
#ifdef KERNEL
#ifndef _KERNEL
#define _KERNEL /* Solaris vs. Darwin */
#endif
#endif
-#define MACH__POSIX_C_SOURCE_PRIVATE 1 /* pulls in suitable savearea from mach/ppc/thread_status.h */
#include <kern/thread.h>
#include <mach/thread_status.h>
+
/* XXX All of these should really be derived from syscall_sw.h */
-#if defined(__i386__) || defined (__x86_64__)
+#if defined (__x86_64__)
#define SYSCALL_CLASS_SHIFT 24
#define SYSCALL_CLASS_MASK (0xFF << SYSCALL_CLASS_SHIFT)
#define SYSCALL_NUMBER_MASK (~SYSCALL_CLASS_MASK)
#define I386_SYSCALL_NUMBER_MASK (0xFFFF)
-
-typedef x86_saved_state_t savearea_t;
#endif
#include <sys/param.h>
#include <sys/conf.h>
#include <sys/user.h>
-#if defined (__ppc__) || defined (__ppc64__)
-#define SYSTRACE_ARTIFICIAL_FRAMES 3
-#define MACHTRACE_ARTIFICIAL_FRAMES 4
-#elif defined(__i386__) || defined (__x86_64__)
+#include <machine/pal_routines.h>
+
+#if defined (__x86_64__)
#define SYSTRACE_ARTIFICIAL_FRAMES 2
#define MACHTRACE_ARTIFICIAL_FRAMES 3
#else
#define LOADABLE_SYSCALL(a) 0 /* Not pertinent to Darwin. */
#define LOADED_SYSCALL(a) 1 /* Not pertinent to Darwin. */
+extern lck_attr_t* dtrace_lck_attr;
+extern lck_grp_t* dtrace_lck_grp;
+static lck_mtx_t dtrace_systrace_lock; /* probe state lock */
+
systrace_sysent_t *systrace_sysent = NULL;
-void (*systrace_probe)(dtrace_id_t, uint64_t, uint64_t,
- uint64_t, uint64_t, uint64_t, uint64_t, uint64_t, uint64_t);
+void (*systrace_probe)(dtrace_id_t, uint64_t, uint64_t, uint64_t, uint64_t, uint64_t);
+
+static uint64_t systrace_getarg(void *, dtrace_id_t, void *, int, int);
void
systrace_stub(dtrace_id_t id, uint64_t arg0, uint64_t arg1,
- uint64_t arg2, uint64_t arg3, uint64_t arg4, uint64_t arg5, uint64_t arg6, uint64_t arg7)
+ uint64_t arg2, uint64_t arg3, uint64_t arg4)
{
-#pragma unused(id,arg0,arg1,arg2,arg3,arg4,arg5,arg6,arg7)
+#pragma unused(id,arg0,arg1,arg2,arg3,arg4)
}
-
int32_t
dtrace_systrace_syscall(struct proc *pp, void *uap, int *rv)
{
- boolean_t flavor;
- unsigned short code;
+ unsigned short code; /* The system call number */
systrace_sysent_t *sy;
dtrace_id_t id;
#endif
syscall_arg_t *ip = (syscall_arg_t *)uap;
-#if defined (__ppc__) || defined (__ppc64__)
- {
- savearea_t *regs = (savearea_t *)find_user_regs(current_thread());
-
- flavor = (((unsigned int)regs->save_r0) == 0)? 1: 0;
-
- if (flavor)
- code = regs->save_r3;
- else
- code = regs->save_r0;
-
- /*
- * FIXME: unix_syscall screens for "unsafe calls" and instead calls nosys(), *not* sysent[code] !
- */
- }
-#elif defined(__i386__) || defined (__x86_64__)
-#pragma unused(flavor)
+#if defined (__x86_64__)
{
+ pal_register_cache_state(current_thread(), VALID);
x86_saved_state_t *tagged_regs = (x86_saved_state_t *)find_user_regs(current_thread());
if (is_saved_state64(tagged_regs)) {
sy = (code >= NUM_SYSENT) ? &systrace_sysent[63] : &systrace_sysent[code];
if ((id = sy->stsy_entry) != DTRACE_IDNONE) {
+ uthread_t uthread = (uthread_t)get_bsdthread_info(current_thread());
+ if (uthread)
+ uthread->t_dtrace_syscall_args = (void *)ip;
+
if (ip)
- (*systrace_probe)(id, *ip, *(ip+1), *(ip+2), *(ip+3), *(ip+4), *(ip+5), *(ip+6), *(ip+7));
+ (*systrace_probe)(id, *ip, *(ip+1), *(ip+2), *(ip+3), *(ip+4));
else
- (*systrace_probe)(id, 0, 0, 0, 0, 0, 0, 0, 0);
+ (*systrace_probe)(id, 0, 0, 0, 0, 0);
+
+ if (uthread)
+ uthread->t_dtrace_syscall_args = (void *)0;
}
#if 0 /* XXX */
/*
+ * APPLE NOTE: Not implemented.
* We want to explicitly allow DTrace consumers to stop a process
* before it actually executes the meat of the syscall.
*/
munged_rv1 = ((u_int)rv[1]);
break;
case _SYSCALL_RET_OFF_T:
+ case _SYSCALL_RET_UINT64_T:
munged_rv0 = *(u_int64_t *)rv;
munged_rv1 = 0LL;
break;
*
* This change was made 4/23/2003 according to the DTrace project's putback log."
*/
- (*systrace_probe)(id, munged_rv0, munged_rv0, munged_rv1, (uint64_t)rval, 0, 0, 0, 0);
+ (*systrace_probe)(id, munged_rv0, munged_rv0, munged_rv1, (uint64_t)rval, 0);
}
return (rval);
munged_rv1 = ((u_int)rv[1]);
break;
case _SYSCALL_RET_OFF_T:
+ case _SYSCALL_RET_UINT64_T:
munged_rv0 = *(u_int64_t *)rv;
munged_rv1 = 0LL;
break;
munged_rv1 = 0LL;
}
- (*systrace_probe)(id, munged_rv0, munged_rv0, munged_rv1, (uint64_t)rval, 0, 0, 0, 0);
+ (*systrace_probe)(id, munged_rv0, munged_rv0, munged_rv1, (uint64_t)rval, 0);
}
}
-#endif /* __APPLE__ */
#define SYSTRACE_SHIFT 16
#define SYSTRACE_ISENTRY(x) ((int)(x) >> SYSTRACE_SHIFT)
static dev_info_t *systrace_devi;
static dtrace_provider_id_t systrace_id;
-#if !defined (__APPLE__)
-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
+/*
+ * APPLE NOTE: Avoid name clash with Darwin automagic conf symbol.
+ * See balanced undef below.
+ */
+#define systrace_init _systrace_init
- s->stsy_underlying = a->sy_callc;
- }
-}
-#else
-#define systrace_init _systrace_init /* Avoid name clash with Darwin automagic conf symbol */
static void
systrace_init(struct sysent *actual, systrace_sysent_t **interposed)
{
s->stsy_underlying = a->sy_callc;
s->stsy_return_type = a->sy_return_type;
}
+ lck_mtx_init(&dtrace_systrace_lock, dtrace_lck_grp, dtrace_lck_attr);
}
-#endif /* __APPLE__ */
/*ARGSUSED*/
static void
#endif
}
}
-#if defined(__APPLE__)
#undef systrace_init
-#endif
/*ARGSUSED*/
static void
}
/*ARGSUSED*/
-static void
+static int
systrace_enable(void *arg, dtrace_id_t id, void *parg)
{
#pragma unused(arg) /* __APPLE__ */
if (enabled) {
ASSERT(sysent[sysnum].sy_callc == dtrace_systrace_syscall);
- return;
+ return(0);
}
-
- (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
+
+ lck_mtx_lock(&dtrace_systrace_lock);
+ if (sysent[sysnum].sy_callc == systrace_sysent[sysnum].stsy_underlying) {
+ vm_offset_t dss = (vm_offset_t)&dtrace_systrace_syscall;
+ ml_nofault_copy((vm_offset_t)&dss, (vm_offset_t)&sysent[sysnum].sy_callc, sizeof(vm_offset_t));
+ }
+ lck_mtx_unlock(&dtrace_systrace_lock);
+ return (0);
}
/*ARGSUSED*/
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);
+ lck_mtx_lock(&dtrace_systrace_lock);
+ if (sysent[sysnum].sy_callc == dtrace_systrace_syscall)
+ ml_nofault_copy((vm_offset_t)&systrace_sysent[sysnum].stsy_underlying, (vm_offset_t)&sysent[sysnum].sy_callc, sizeof(systrace_sysent[sysnum].stsy_underlying));
+ lck_mtx_unlock(&dtrace_systrace_lock);
#ifdef _SYSCALL32_IMPL
(void) casptr(&sysent32[sysnum].sy_callc,
NULL,
NULL,
NULL,
- NULL,
+ systrace_getarg,
NULL,
systrace_destroy
};
return (DDI_FAILURE);
}
-#if !defined(__APPLE__)
- 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);
- }
-#else
systrace_probe = (void(*))&dtrace_probe;
membar_enter();
ddi_remove_minor_node(devi, NULL);
return (DDI_FAILURE);
}
-#endif /* __APPLE__ */
ddi_report_dev(devi);
systrace_devi = devi;
return (DDI_SUCCESS);
}
+
+/*
+ * APPLE NOTE: systrace_detach not implemented
+ */
#if !defined(__APPLE__)
static int
systrace_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
systrace_probe = systrace_stub;
return (DDI_SUCCESS);
}
+#endif /* __APPLE__ */
-/*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));
-}
-#else
typedef kern_return_t (*mach_call_t)(void *);
-/* XXX From #include <kern/syscall_sw.h> which may be changed for 64 bit! */
-typedef void mach_munge_t(const void *, void *);
+/* APPLE NOTE: From #include <kern/syscall_sw.h> which may be changed for 64 bit! */
+typedef void mach_munge_t(void *);
typedef struct {
- int mach_trap_arg_count;
- int (*mach_trap_function)(void);
-#if defined(__i386__)
- boolean_t mach_trap_stack;
-#else
- mach_munge_t *mach_trap_arg_munge32; /* system call arguments for 32-bit */
- mach_munge_t *mach_trap_arg_munge64; /* system call arguments for 64-bit */
+ int mach_trap_arg_count;
+ kern_return_t (*mach_trap_function)(void *);
+#if defined(__arm64__) || defined(__x86_64__)
+ mach_munge_t *mach_trap_arg_munge32; /* system call arguments for 32-bit */
#endif
-#if !MACH_ASSERT
- int mach_trap_unused;
-#else
- const char* mach_trap_name;
-#endif /* !MACH_ASSERT */
+ int mach_trap_u32_words;
+#if MACH_ASSERT
+ const char* mach_trap_name;
+#endif /* MACH_ASSERT */
} mach_trap_t;
-extern mach_trap_t mach_trap_table[];
+extern const mach_trap_t mach_trap_table[]; /* syscall_sw.h now declares this as const */
extern int mach_trap_count;
extern const char *mach_syscall_name_table[];
#error 1 << SYSTRACE_SHIFT must exceed number of Mach traps
#endif
-typedef systrace_sysent_t machtrace_sysent_t;
+typedef struct machtrace_sysent {
+ dtrace_id_t stsy_entry;
+ dtrace_id_t stsy_return;
+ kern_return_t (*stsy_underlying)(void *);
+ int32_t stsy_return_type;
+} machtrace_sysent_t;
static machtrace_sysent_t *machtrace_sysent = NULL;
void (*machtrace_probe)(dtrace_id_t, uint64_t, uint64_t,
uint64_t, uint64_t, uint64_t);
+static uint64_t machtrace_getarg(void *, dtrace_id_t, void *, int, int);
+
static dev_info_t *machtrace_devi;
static dtrace_provider_id_t machtrace_id;
static kern_return_t
dtrace_machtrace_syscall(struct mach_call_args *args)
{
- boolean_t flavor;
- unsigned short code;
+ int code; /* The mach call number */
machtrace_sysent_t *sy;
dtrace_id_t id;
syscall_arg_t *ip = (syscall_arg_t *)args;
mach_call_t mach_call;
-#if defined (__ppc__) || defined (__ppc64__)
- {
- savearea_t *regs = (savearea_t *)find_user_regs(current_thread());
-
- flavor = (((unsigned int)regs->save_r0) == 0)? 1: 0;
-
- if (flavor)
- code = -regs->save_r3;
- else
- code = -regs->save_r0;
- }
-#elif defined(__i386__) || defined (__x86_64__)
-#pragma unused(flavor)
+#if defined (__x86_64__)
{
+ pal_register_cache_state(current_thread(), VALID);
x86_saved_state_t *tagged_regs = (x86_saved_state_t *)find_user_regs(current_thread());
if (is_saved_state64(tagged_regs)) {
sy = &machtrace_sysent[code];
- if ((id = sy->stsy_entry) != DTRACE_IDNONE)
+ if ((id = sy->stsy_entry) != DTRACE_IDNONE) {
+ uthread_t uthread = (uthread_t)get_bsdthread_info(current_thread());
+
+ if (uthread)
+ uthread->t_dtrace_syscall_args = (void *)ip;
+
(*machtrace_probe)(id, *ip, *(ip+1), *(ip+2), *(ip+3), *(ip+4));
+
+ if (uthread)
+ uthread->t_dtrace_syscall_args = (void *)0;
+ }
#if 0 /* XXX */
/*
+ * APPLE NOTE: Not implemented.
* We want to explicitly allow DTrace consumers to stop a process
* before it actually executes the meat of the syscall.
*/
}
static void
-machtrace_init(mach_trap_t *actual, machtrace_sysent_t **interposed)
+machtrace_init(const mach_trap_t *actual, machtrace_sysent_t **interposed)
{
machtrace_sysent_t *msysent = *interposed;
int i;
}
for (i = 0; i < NSYSCALL; i++) {
- mach_trap_t *a = &actual[i];
+ const mach_trap_t *a = &actual[i];
machtrace_sysent_t *s = &msysent[i];
if (LOADABLE_SYSCALL(a) && !LOADED_SYSCALL(a))
continue;
- if ((mach_call_t)(a->mach_trap_function) == (mach_call_t)(dtrace_machtrace_syscall))
+ if (a->mach_trap_function == (mach_call_t)(dtrace_machtrace_syscall))
continue;
- s->stsy_underlying = (sy_call_t *)a->mach_trap_function;
+ s->stsy_underlying = a->mach_trap_function;
}
}
}
/*ARGSUSED*/
-static void
+static int
machtrace_enable(void *arg, dtrace_id_t id, void *parg)
{
#pragma unused(arg) /* __APPLE__ */
}
if (enabled) {
- ASSERT(sysent[sysnum].sy_callc == (void *)dtrace_machtrace_syscall);
- return;
+ ASSERT(mach_trap_table[sysnum].mach_trap_function == (void *)dtrace_machtrace_syscall);
+ return(0);
+ }
+
+ lck_mtx_lock(&dtrace_systrace_lock);
+
+ if (mach_trap_table[sysnum].mach_trap_function == machtrace_sysent[sysnum].stsy_underlying) {
+ vm_offset_t dss = (vm_offset_t)&dtrace_machtrace_syscall;
+ ml_nofault_copy((vm_offset_t)&dss, (vm_offset_t)&mach_trap_table[sysnum].mach_trap_function, sizeof(vm_offset_t));
}
- (void) casptr(&mach_trap_table[sysnum].mach_trap_function,
- (void *)machtrace_sysent[sysnum].stsy_underlying,
- (void *)dtrace_machtrace_syscall);
+ lck_mtx_unlock(&dtrace_systrace_lock);
+
+ return(0);
}
/*ARGSUSED*/
machtrace_sysent[sysnum].stsy_return == DTRACE_IDNONE);
if (disable) {
- (void) casptr(&mach_trap_table[sysnum].mach_trap_function,
- (void *)dtrace_machtrace_syscall,
- (void *)machtrace_sysent[sysnum].stsy_underlying);
+ lck_mtx_lock(&dtrace_systrace_lock);
+
+ if (mach_trap_table[sysnum].mach_trap_function == (mach_call_t)dtrace_machtrace_syscall) {
+ ml_nofault_copy((vm_offset_t)&machtrace_sysent[sysnum].stsy_underlying, (vm_offset_t)&mach_trap_table[sysnum].mach_trap_function, sizeof(vm_offset_t));
+ }
+ lck_mtx_unlock(&dtrace_systrace_lock);
}
if (SYSTRACE_ISENTRY((uintptr_t)parg)) {
NULL,
NULL,
NULL,
- NULL,
+ machtrace_getarg,
NULL,
machtrace_destroy
};
return (DDI_FAILURE);
}
-#if !defined(__APPLE__)
- machtrace_probe = (void (*)())dtrace_probe;
- membar_enter();
-
- if (ddi_create_minor_node(devi, "machtrace", S_IFCHR, 0,
- DDI_PSEUDO, NULL) == DDI_FAILURE ||
- dtrace_register("mach_trap", &machtrace_attr, DTRACE_PRIV_USER, NULL,
- &machtrace_pops, NULL, &machtrace_id) != 0) {
- machtrace_probe = systrace_stub;
-#else
machtrace_probe = dtrace_probe;
membar_enter();
dtrace_register("mach_trap", &machtrace_attr, DTRACE_PRIV_USER, NULL,
&machtrace_pops, NULL, &machtrace_id) != 0) {
machtrace_probe = (void (*))&systrace_stub;
-#endif /* __APPLE__ */
ddi_remove_minor_node(devi, NULL);
return (DDI_FAILURE);
}
panic("systrace_init: called twice!\n");
}
#undef SYSTRACE_MAJOR
-#endif /* __APPLE__ */
+
+static uint64_t
+systrace_getarg(void *arg, dtrace_id_t id, void *parg, int argno, int aframes)
+{
+#pragma unused(arg,id,parg,aframes) /* __APPLE__ */
+ uint64_t val = 0;
+ syscall_arg_t *stack = (syscall_arg_t *)NULL;
+
+ uthread_t uthread = (uthread_t)get_bsdthread_info(current_thread());
+
+ if (uthread)
+ stack = (syscall_arg_t *)uthread->t_dtrace_syscall_args;
+
+ if (!stack)
+ return(0);
+
+ DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT);
+ /* dtrace_probe arguments arg0 .. arg4 are 64bits wide */
+ val = (uint64_t)*(stack+argno);
+ DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT);
+ return (val);
+}
+
+
+static uint64_t
+machtrace_getarg(void *arg, dtrace_id_t id, void *parg, int argno, int aframes)
+{
+#pragma unused(arg,id,parg,aframes) /* __APPLE__ */
+ uint64_t val = 0;
+ syscall_arg_t *stack = (syscall_arg_t *)NULL;
+
+ uthread_t uthread = (uthread_t)get_bsdthread_info(current_thread());
+
+ if (uthread)
+ stack = (syscall_arg_t *)uthread->t_dtrace_syscall_args;
+
+ if (!stack)
+ return(0);
+
+ DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT);
+ /* dtrace_probe arguments arg0 .. arg4 are 64bits wide */
+ val = (uint64_t)*(stack+argno);
+ DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT);
+ return (val);
+}
+