]> git.saurik.com Git - apple/xnu.git/blobdiff - libsyscall/wrappers/kdebug_trace.c
xnu-3789.41.3.tar.gz
[apple/xnu.git] / libsyscall / wrappers / kdebug_trace.c
index 4867f9b5184b69c94fd42e574e52126b4d2957bd..8234f45825b441e89fd7cdb5bed11b9b898ece79 100644 (file)
  */
 
 #include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdatomic.h>
 #include <machine/cpu_capabilities.h>
 #include <sys/kdebug.h>
+#include <sys/kdebug_signpost.h>
 #include <sys/errno.h>
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <mach/mach.h>
+#include <mach/mach_vm.h>
 
-#define CLASS_MASK      0xff000000
-#define CLASS_OFFSET    24
-#define SUBCLASS_MASK   0x00ff0000
-#define SUBCLASS_OFFSET 16
+extern int __kdebug_typefilter(void** addr, size_t* size);
+extern int __kdebug_trace64(uint32_t code, uint64_t arg1, uint64_t arg2,
+       uint64_t arg3, uint64_t arg4);
+extern uint64_t __kdebug_trace_string(uint32_t debugid, uint64_t str_id,
+       const char *str);
 
-#define EXTRACT_CLASS(debugid)          ((uint8_t)(((debugid) & CLASS_MASK) >> CLASS_OFFSET))
-#define EXTRACT_SUBCLASS(debugid)       ( (uint8_t) ( ((debugid) & SUBCLASS_MASK) >> SUBCLASS_OFFSET ) )
+static int kdebug_signpost_internal(uint32_t debugid, uintptr_t arg1,
+       uintptr_t arg2, uintptr_t arg3, uintptr_t arg4);
 
-extern int __kdebug_trace64(uint32_t code, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4);
+/*
+ * GENERAL API DESIGN NOTE!
+ *
+ * Trace API's are expected to avoid performing checks until tracing has
+ * been enabled. This includes checks that might cause error codes to be
+ * returned.
+ *
+ * Trace invocations via wrapper and syscall must have the same behavior.
+ *
+ * Note that the userspace API is chosing to optimize fastpath, non-error
+ * performance by eliding validation of each debugid. This means that error
+ * cases which could have been caught in userspace will make a syscall
+ * before returning with the correct error code. This tradeoff in performance
+ * is intentional.
+ */
 
-int
-kdebug_trace(uint32_t code, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4)
+void *
+kdebug_typefilter(void)
+{
+       static void* typefilter;
+
+       /* We expect kdebug_typefilter_bitmap to be valid (the if is not executed) */
+       if (__builtin_expect(!typefilter, 0)) {
+               // Map the typefilter if it can be mapped.
+               void* ptr = NULL;
+               size_t ptr_size = 0;
+
+               if (__kdebug_typefilter(&ptr, &ptr_size) == 0) {
+                       void* old_value = NULL;
+                       if (ptr && !atomic_compare_exchange_strong((void* _Atomic volatile *)&typefilter, &old_value, ptr)) {
+                               mach_vm_deallocate(mach_task_self(), (mach_vm_offset_t)ptr, KDBG_TYPEFILTER_BITMAP_SIZE);
+                       }
+               }
+       }
+
+       return typefilter;
+}
+
+bool
+kdebug_is_enabled(uint32_t debugid)
 {
-       uint8_t code_class;
-       volatile uint32_t *kdebug_enable_address = (volatile uint32_t *)(uintptr_t)(_COMM_PAGE_KDEBUG_ENABLE);
+       uint32_t state = *((volatile uint32_t *)(uintptr_t)(_COMM_PAGE_KDEBUG_ENABLE));
+
+       if (state == 0) {
+               return FALSE;
+       }
+
+       if ((state & KDEBUG_COMMPAGE_ENABLE_TYPEFILTER) > 0) {
+               /*
+                * Typefilter rules...
+                *
+                * If no typefilter is available (even if due to error),
+                * debugids are allowed. 
+                *
+                * The typefilter will always allow DBG_TRACE; this is a kernel
+                * invariant. There is no need for an explicit check here.
+                *
+                * NOTE: The typefilter will always allow DBG_TRACE, but
+                * it is not legal to inject DBG_TRACE via kdebug_trace.
+                * Attempts to do so will not be detected here, but will be
+                * detected in the kernel, and an error will be returned. Per
+                * the API design note at the top of this file, this is a
+                * deliberate choice.
+                */
+               uint8_t* typefilter = kdebug_typefilter();
+               if (typefilter && isset(typefilter, KDBG_EXTRACT_CSC(debugid)) == 0) {
+                       return FALSE;
+               }
+       }
+
+       return TRUE;
+}
 
-       /*
-        * This filtering is also done in the kernel, but we also do it here so that errors
-        * are returned in all cases, not just when the system call is actually performed.
-        */
-       code_class = EXTRACT_CLASS(code);
-       switch (code_class) {
-               case DBG_TRACE:
-                       errno = EPERM;
-                       return -1;
+int
+kdebug_trace(uint32_t debugid, uint64_t arg1, uint64_t arg2, uint64_t arg3,
+             uint64_t arg4)
+{
+       if (!kdebug_is_enabled(debugid)) {
+               return 0;
        }
 
-       if (*kdebug_enable_address == 0) {
+       return __kdebug_trace64(debugid, arg1, arg2, arg3, arg4);
+}
+
+uint64_t
+kdebug_trace_string(uint32_t debugid, uint64_t str_id, const char *str)
+{
+       if (!kdebug_is_enabled(debugid)) {
                return 0;
        }
-       
-       return __kdebug_trace64(code, arg1, arg2, arg3, arg4);
+
+       if ((int64_t)str_id == -1) {
+               errno = EINVAL;
+               return (uint64_t)-1;
+       }
+
+       if (str_id == 0 && str == NULL) {
+               errno = EINVAL;
+               return (uint64_t)-1;
+       }
+
+       return __kdebug_trace_string(debugid, str_id, str);
+}
+
+static int
+kdebug_signpost_internal(uint32_t debugid, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3, uintptr_t arg4)
+{
+       if (KDBG_EXTRACT_CSC(debugid) != 0) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       debugid |= APPSDBG_CODE(DBG_APP_SIGNPOST, 0);
+
+       return kdebug_trace(debugid, arg1, arg2, arg3, arg4);
+}
+
+int
+kdebug_signpost(uint32_t code, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3, uintptr_t arg4)
+{
+       return kdebug_signpost_internal(code << KDBG_CODE_OFFSET, arg1, arg2, arg3, arg4);
+}
+
+int
+kdebug_signpost_start(uint32_t code, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3, uintptr_t arg4)
+{
+       return kdebug_signpost_internal((code << KDBG_CODE_OFFSET) | DBG_FUNC_START, arg1, arg2, arg3, arg4);
+}
+
+int
+kdebug_signpost_end(uint32_t code, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3, uintptr_t arg4)
+{
+       return kdebug_signpost_internal((code << KDBG_CODE_OFFSET) | DBG_FUNC_END, arg1, arg2, arg3, arg4);
 }