+/*
+ * Typefilter(s)
+ *
+ * A typefilter is a 8KB bitmap that is used to selectively filter events
+ * being recorded. It is able to individually address every class & subclass.
+ *
+ * There is a shared typefilter in the kernel which is lazily allocated. Once
+ * allocated, the shared typefilter is never deallocated. The shared typefilter
+ * is also mapped on demand into userspace processes that invoke kdebug_trace
+ * API from Libsyscall. When mapped into a userspace process, the memory is
+ * read only, and does not have a fixed address.
+ *
+ * It is a requirement that the kernel's shared typefilter always pass DBG_TRACE
+ * events. This is enforced automatically, by having the needed bits set any
+ * time the shared typefilter is mutated.
+ */
+
+typedef uint8_t* typefilter_t;
+
+static typefilter_t kdbg_typefilter;
+static mach_port_t kdbg_typefilter_memory_entry;
+
+/*
+ * There are 3 combinations of page sizes:
+ *
+ * 4KB / 4KB
+ * 4KB / 16KB
+ * 16KB / 16KB
+ *
+ * The typefilter is exactly 8KB. In the first two scenarios, we would like
+ * to use 2 pages exactly; in the third scenario we must make certain that
+ * a full page is allocated so we do not inadvertantly share 8KB of random
+ * data to userspace. The round_page_32 macro rounds to kernel page size.
+ */
+#define TYPEFILTER_ALLOC_SIZE MAX(round_page_32(KDBG_TYPEFILTER_BITMAP_SIZE), KDBG_TYPEFILTER_BITMAP_SIZE)
+
+static typefilter_t
+typefilter_create(void)
+{
+ typefilter_t tf;
+ if (KERN_SUCCESS == kmem_alloc(kernel_map, (vm_offset_t*)&tf, TYPEFILTER_ALLOC_SIZE, VM_KERN_MEMORY_DIAG)) {
+ memset(&tf[KDBG_TYPEFILTER_BITMAP_SIZE], 0, TYPEFILTER_ALLOC_SIZE - KDBG_TYPEFILTER_BITMAP_SIZE);
+ return tf;
+ }
+ return NULL;
+}
+
+static void
+typefilter_deallocate(typefilter_t tf)
+{
+ assert(tf != NULL);
+ assert(tf != kdbg_typefilter);
+ kmem_free(kernel_map, (vm_offset_t)tf, TYPEFILTER_ALLOC_SIZE);
+}
+
+static void
+typefilter_copy(typefilter_t dst, typefilter_t src)
+{
+ assert(src != NULL);
+ assert(dst != NULL);
+ memcpy(dst, src, KDBG_TYPEFILTER_BITMAP_SIZE);
+}
+
+static void
+typefilter_reject_all(typefilter_t tf)
+{
+ assert(tf != NULL);
+ memset(tf, 0, KDBG_TYPEFILTER_BITMAP_SIZE);
+}
+
+static void
+typefilter_allow_all(typefilter_t tf)
+{
+ assert(tf != NULL);
+ memset(tf, ~0, KDBG_TYPEFILTER_BITMAP_SIZE);
+}
+
+static void
+typefilter_allow_class(typefilter_t tf, uint8_t class)
+{
+ assert(tf != NULL);
+ const uint32_t BYTES_PER_CLASS = 256 / 8; // 256 subclasses, 1 bit each
+ memset(&tf[class * BYTES_PER_CLASS], 0xFF, BYTES_PER_CLASS);
+}
+
+static void
+typefilter_allow_csc(typefilter_t tf, uint16_t csc)
+{
+ assert(tf != NULL);
+ setbit(tf, csc);
+}
+
+static bool
+typefilter_is_debugid_allowed(typefilter_t tf, uint32_t id)
+{
+ assert(tf != NULL);
+ return isset(tf, KDBG_EXTRACT_CSC(id));
+}
+
+static mach_port_t
+typefilter_create_memory_entry(typefilter_t tf)
+{
+ assert(tf != NULL);
+
+ mach_port_t memory_entry = MACH_PORT_NULL;
+ memory_object_size_t size = TYPEFILTER_ALLOC_SIZE;
+
+ mach_make_memory_entry_64(kernel_map,
+ &size,
+ (memory_object_offset_t)tf,
+ VM_PROT_READ,
+ &memory_entry,
+ MACH_PORT_NULL);
+
+ return memory_entry;
+}
+
+static int kdbg_copyin_typefilter(user_addr_t addr, size_t size);
+static void kdbg_enable_typefilter(void);
+static void kdbg_disable_typefilter(void);
+
+/*
+ * External prototypes
+ */
+
+void task_act_iterate_wth_args(task_t, void (*)(thread_t, void *), void *);
+int cpu_number(void); /* XXX <machine/...> include path broken */
+void commpage_update_kdebug_state(void); /* XXX sign */
+
+extern int log_leaks;
+
+/*
+ * This flag is for testing purposes only -- it's highly experimental and tools
+ * have not been updated to support it.
+ */
+static bool kdbg_continuous_time = false;
+
+static inline uint64_t
+kdbg_timestamp(void)
+{
+ if (kdbg_continuous_time) {
+ return mach_continuous_time();
+ } else {
+ return mach_absolute_time();
+ }
+}
+
+static int kdbg_debug = 0;
+
+#if KDEBUG_MOJO_TRACE
+#include <sys/kdebugevents.h>
+static void kdebug_serial_print( /* forward */
+ uint32_t, uint32_t, uint64_t,
+ uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
+#endif