]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/dev/chud/chud_bsd_callback.c
xnu-1456.1.26.tar.gz
[apple/xnu.git] / bsd / dev / chud / chud_bsd_callback.c
index bfb0aeb617ffbcf589adec81832d3a686507cfb4..6fad80050c93cb44088c1a300795fb7f24abfda1 100644 (file)
 #include <sys/proc.h> /* proc_t */
 #include <sys/systm.h> /* struct sysent */
 #include <sys/sysproto.h>
+#include <sys/kdebug.h>        /* KDEBUG_ENABLE_CHUD */
+#include <libkern/OSAtomic.h>
+
+#ifdef __ppc__
+#include <ppc/savearea.h>
+
+#define FM_ARG0                                0x38ULL // offset from r1 to first argument
+#define SPILLED_WORD_COUNT     7               // number of 32-bit words spilled to the stack
+
+extern struct savearea * find_user_regs( thread_t act);
+#endif
 
 #pragma mark **** kern debug ****
-typedef void (*chudxnu_kdebug_callback_func_t)(uint32_t debugid, uint32_t arg0, uint32_t arg1, uint32_t arg2, uint32_t arg3, uint32_t arg4);
-static chudxnu_kdebug_callback_func_t kdebug_callback_fn = NULL;
+typedef void (*chudxnu_kdebug_callback_func_t)(uint32_t debugid, uintptr_t arg0, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3, uintptr_t arg4);
+static void chud_null_kdebug(uint32_t debugid, uintptr_t arg0, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3, uintptr_t arg4);
+static chudxnu_kdebug_callback_func_t kdebug_callback_fn = chud_null_kdebug;
 
 kern_return_t chudxnu_kdebug_callback_enter(chudxnu_kdebug_callback_func_t);
 kern_return_t chudxnu_kdebug_callback_cancel(void);
 
 extern void kdbg_control_chud(int val, void *fn);
-extern unsigned int kdebug_enable;
+
+static void chud_null_kdebug(uint32_t debugid __unused, uintptr_t arg0 __unused,
+       uintptr_t arg1 __unused, uintptr_t arg2 __unused, uintptr_t arg3 __unused, 
+       uintptr_t arg4 __unused) {
+       return;
+}
 
 static void
 chudxnu_private_kdebug_callback(
-       unsigned int debugid,
-       unsigned int arg0,
-       unsigned int arg1,
-       unsigned int arg2,
-       unsigned int arg3,
-       unsigned int arg4)
+       uint32_t debugid,
+       uintptr_t arg0,
+       uintptr_t arg1,
+       uintptr_t arg2,
+       uintptr_t arg3,
+       uintptr_t arg4)
 {
     chudxnu_kdebug_callback_func_t fn = kdebug_callback_fn;
     
@@ -65,39 +82,132 @@ chudxnu_private_kdebug_callback(
 __private_extern__ kern_return_t
 chudxnu_kdebug_callback_enter(chudxnu_kdebug_callback_func_t func)
 {
-    kdebug_callback_fn = func;
-
-    kdbg_control_chud(TRUE, (void *)chudxnu_private_kdebug_callback);
-    kdebug_enable |= 0x10;
-
-    return KERN_SUCCESS;
+       /* Atomically set the callback. */
+       if(OSCompareAndSwapPtr(chud_null_kdebug, func, 
+               (void * volatile *)&kdebug_callback_fn)) {
+               
+               kdbg_control_chud(TRUE, (void *)chudxnu_private_kdebug_callback);
+               OSBitOrAtomic((UInt32)KDEBUG_ENABLE_CHUD, (volatile UInt32 *)&kdebug_enable);
+               
+               return KERN_SUCCESS;
+       }
+       return KERN_FAILURE;
 }
 
 __private_extern__ kern_return_t
 chudxnu_kdebug_callback_cancel(void)
 {
-    kdebug_callback_fn = NULL;
-    kdbg_control_chud(FALSE, NULL);
-    kdebug_enable &= ~(0x10);
+       OSBitAndAtomic((UInt32)~(KDEBUG_ENABLE_CHUD), (volatile UInt32 *)&kdebug_enable);
+       kdbg_control_chud(FALSE, NULL);
+
+       chudxnu_kdebug_callback_func_t old = kdebug_callback_fn;
+
+       while(!OSCompareAndSwapPtr(old, chud_null_kdebug, 
+               (void * volatile *)&kdebug_callback_fn)) {
+               old = kdebug_callback_fn;
+       }
 
     return KERN_SUCCESS;
 }
 
 #pragma mark **** CHUD syscall ****
-typedef kern_return_t (*chudxnu_syscall_callback_func_t)(uint32_t code, uint32_t arg0, uint32_t arg1, uint32_t arg2, uint32_t arg3, uint32_t arg4);
-static chudxnu_syscall_callback_func_t syscall_callback_fn = NULL;
+typedef kern_return_t (*chudxnu_syscall_callback_func_t)(uint64_t code, uint64_t arg0, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4);
+
+static kern_return_t chud_null_syscall(uint64_t code, uint64_t arg0, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4);
+static chudxnu_syscall_callback_func_t syscall_callback_fn = chud_null_syscall;
 
 kern_return_t chudxnu_syscall_callback_enter(chudxnu_syscall_callback_func_t func);
 kern_return_t chudxnu_syscall_callback_cancel(void);
 
+static kern_return_t chud_null_syscall(uint64_t code __unused, 
+       uint64_t arg0 __unused, uint64_t arg1 __unused, uint64_t arg2 __unused, 
+       uint64_t arg3 __unused, uint64_t arg4 __unused) {
+       return (kern_return_t)EINVAL;
+}
+
+/*
+ * chud
+ *
+ * Performs performance-related tasks.  A private interface registers a handler for this
+ * system call.  The implementation is in the CHUDProf kernel extension.
+ *
+ * chud() is a callback style system call used by the CHUD Tools suite of performance tools.  If the CHUD 
+ * kexts are not loaded, this system call will always return EINVAL.  The CHUD kexts contain the 
+ * implementation of the system call.
+ * 
+ * The current behavior of the chud() system call is as follows: 
+ *
+ * Parameters: p       (ignored)
+ *             uap     User argument descriptor (see below)
+ *             retval  return value of fn (the function returned by syscall_callback_fn) 
+ *
+ * Indirect parameters:        uap->code       Selects the operation to do.  This is broken down into a
+ *                                     16-bit facility and a 16-bit action.
+ *
+ * The rest of the indirect parameters depend on the facility and the action that is selected:
+ *
+ * Facility: 1    Amber instruction tracer
+ *     Action: 1       Indicate that a new thread has been created.  No arguments are used.
+ *
+ *     Action: 2       Indicate that a thread is about to exit.  No arguments are used.
+ *
+ * Facility: 2   Not Supported for this system call
+ *
+ * Facility: 3  CHUD Trace facility
+ *     Action: 1       Record a backtrace of the calling process into the CHUD Trace facility sample
+ *                     buffer.
+ *             
+ *                     uap->arg1       Number of frames to skip
+ *                     uap->arg2       Pointer to a uint64_t containing a timestamp for the
+ *                                     beginning of the sample.  NULL uses the current time.
+ *                     uap->arg3       Pointer to a uint64_t containing a timestamp for the end
+ *                                     of the sample.  NULL uses the current time.
+ *                     uap->arg4       Pointer to auxiliary data to be recorded with the sample
+ *                     uap->arg5       Size of the auxiliary data pointed to by arg4.
+ *             
+ * Returns:    EINVAL          If syscall_callback_fn returns an invalid function
+ *             KERN_SUCCESS    Success
+ *             KERN_FAILURE    Generic failure
+ *             KERN_NO_SPACE   Auxiliary data is too large (only used by Facility: 3)
+ *
+ * Implicit returns:   retval          return value of fn (the function returned by syscall_callback_fn)
+ */
 int
-chud(__unused proc_t p, struct chud_args *uap, register_t *retval)
+chud(__unused proc_t p, struct chud_args *uap, int32_t *retval)
 {
     chudxnu_syscall_callback_func_t fn = syscall_callback_fn;
     
        if(!fn) {
                return EINVAL;
        }
+
+#ifdef __ppc__
+       // ppc32 user land spills 2.5 64-bit args (5 x 32-bit) to the stack
+       // here we have to copy them out.  r1 is the stack pointer in this world.
+       // the offset is calculated according to the PPC32 ABI
+       // Important: this only happens for 32-bit user threads
+
+       if(!IS_64BIT_PROCESS(p)) {
+               struct savearea *regs = find_user_regs(current_thread());
+               if(!regs) {
+                       return EINVAL;
+               }
+
+               // %r1 is the stack pointer on ppc32
+               uint32_t stackPointer = regs->save_r1;
+
+               // calculate number of bytes spilled to the stack
+               uint32_t spilledSize = sizeof(struct chud_args) - (sizeof(uint32_t) * SPILLED_WORD_COUNT);
+
+               // obtain offset to arguments spilled onto user-thread stack
+               user_addr_t incomingAddr = (user_addr_t)stackPointer + FM_ARG0;
+
+               // destination is halfway through arg3
+               uint8_t *dstAddr = (uint8_t*)(&(uap->arg3)) + sizeof(uint32_t);
+               
+               copyin(incomingAddr, dstAddr, spilledSize);
+       }
+#endif
        
        *retval = fn(uap->code, uap->arg1, uap->arg2, uap->arg3, uap->arg4, uap->arg5);
                
@@ -107,13 +217,82 @@ chud(__unused proc_t p, struct chud_args *uap, register_t *retval)
 __private_extern__ kern_return_t 
 chudxnu_syscall_callback_enter(chudxnu_syscall_callback_func_t func)
 {
-       syscall_callback_fn = func;
-    return KERN_SUCCESS;
+       if(OSCompareAndSwapPtr(chud_null_syscall, func, 
+               (void * volatile *)&syscall_callback_fn)) {
+               return KERN_SUCCESS;
+       }
+       return KERN_FAILURE;
 }
 
 __private_extern__ kern_return_t
 chudxnu_syscall_callback_cancel(void)
 {
-       syscall_callback_fn = NULL;
+       chudxnu_syscall_callback_func_t old = syscall_callback_fn;
+
+       while(!OSCompareAndSwapPtr(old, chud_null_syscall,
+               (void * volatile *)&syscall_callback_fn)) {
+               old = syscall_callback_fn;
+       }
+
     return KERN_SUCCESS;
 }
+
+/* DTrace callback */
+typedef kern_return_t (*chudxnu_dtrace_callback_t)(uint64_t selector, 
+       uint64_t *args, uint32_t count);
+int chudxnu_dtrace_callback(uint64_t selector, uint64_t *args, uint32_t count);
+kern_return_t chudxnu_dtrace_callback_enter(chudxnu_dtrace_callback_t fn);
+void chudxnu_dtrace_callback_cancel(void);
+
+int
+chud_null_dtrace(uint64_t selector, uint64_t *args, uint32_t count);
+
+static chudxnu_dtrace_callback_t 
+       dtrace_callback = (chudxnu_dtrace_callback_t) chud_null_dtrace;
+
+int
+chud_null_dtrace(uint64_t selector __unused, uint64_t *args __unused, 
+       uint32_t count __unused) {
+       return ENXIO;
+}
+
+int
+chudxnu_dtrace_callback(uint64_t selector, uint64_t *args, uint32_t count)
+{
+       /* If no callback is hooked up, let's return ENXIO */
+       int ret = ENXIO;
+
+       /* Make a local stack copy of the function ptr */
+       chudxnu_dtrace_callback_t fn = dtrace_callback;
+
+       if(fn) {
+               ret = fn(selector, args, count);
+       }
+
+       return ret;
+}
+
+__private_extern__ kern_return_t
+chudxnu_dtrace_callback_enter(chudxnu_dtrace_callback_t fn)
+{
+       /* Atomically enter the call back */
+       if(!OSCompareAndSwapPtr(chud_null_dtrace, fn, 
+               (void * volatile *) &dtrace_callback)) {
+               return KERN_FAILURE;
+       }
+
+       return KERN_SUCCESS;
+}
+
+__private_extern__ void
+chudxnu_dtrace_callback_cancel(void)
+{
+       chudxnu_dtrace_callback_t old_fn = dtrace_callback;
+
+       /* Atomically clear the call back */
+       while(!OSCompareAndSwapPtr(old_fn, chud_null_dtrace, 
+               (void * volatile *) &dtrace_callback)) {
+               old_fn = dtrace_callback;
+       }
+}
+