+
+
+#if DEVELOPMENT || DEBUG
+
+static int
+sysctl_panic_test SYSCTL_HANDLER_ARGS
+{
+#pragma unused(arg1, arg2)
+ int rval = 0;
+ char str[32] = "entry prelog postlog postcore";
+
+ rval = sysctl_handle_string(oidp, str, sizeof(str), req);
+
+ if (rval == 0 && req->newptr) {
+ if (strncmp("entry", str, strlen("entry")) == 0) {
+ panic_with_options(0, NULL, DEBUGGER_OPTION_RECURPANIC_ENTRY, "test recursive panic at entry");
+ } else if (strncmp("prelog", str, strlen("prelog")) == 0) {
+ panic_with_options(0, NULL, DEBUGGER_OPTION_RECURPANIC_PRELOG, "test recursive panic prior to writing a paniclog");
+ } else if (strncmp("postlog", str, strlen("postlog")) == 0) {
+ panic_with_options(0, NULL, DEBUGGER_OPTION_RECURPANIC_POSTLOG, "test recursive panic subsequent to paniclog");
+ } else if (strncmp("postcore", str, strlen("postcore")) == 0) {
+ panic_with_options(0, NULL, DEBUGGER_OPTION_RECURPANIC_POSTCORE, "test recursive panic subsequent to on-device core");
+ }
+ }
+
+ return rval;
+}
+
+static int
+sysctl_debugger_test SYSCTL_HANDLER_ARGS
+{
+#pragma unused(arg1, arg2)
+ int rval = 0;
+ char str[32] = "entry prelog postlog postcore";
+
+ rval = sysctl_handle_string(oidp, str, sizeof(str), req);
+
+ if (rval == 0 && req->newptr) {
+ if (strncmp("entry", str, strlen("entry")) == 0) {
+ DebuggerWithContext(0, NULL, "test recursive panic via debugger at entry", DEBUGGER_OPTION_RECURPANIC_ENTRY);
+ } else if (strncmp("prelog", str, strlen("prelog")) == 0) {
+ DebuggerWithContext(0, NULL, "test recursive panic via debugger prior to writing a paniclog", DEBUGGER_OPTION_RECURPANIC_PRELOG);
+ } else if (strncmp("postlog", str, strlen("postlog")) == 0) {
+ DebuggerWithContext(0, NULL, "test recursive panic via debugger subsequent to paniclog", DEBUGGER_OPTION_RECURPANIC_POSTLOG);
+ } else if (strncmp("postcore", str, strlen("postcore")) == 0) {
+ DebuggerWithContext(0, NULL, "test recursive panic via debugger subsequent to on-device core", DEBUGGER_OPTION_RECURPANIC_POSTCORE);
+ }
+ }
+
+ return rval;
+}
+
+decl_lck_spin_data(, spinlock_panic_test_lock)
+
+__attribute__((noreturn))
+static void
+spinlock_panic_test_acquire_spinlock(void * arg __unused, wait_result_t wres __unused)
+{
+ lck_spin_lock(&spinlock_panic_test_lock);
+ while (1) { ; }
+}
+
+static int
+sysctl_spinlock_panic_test SYSCTL_HANDLER_ARGS
+{
+#pragma unused(oidp, arg1, arg2)
+ if (req->newlen == 0)
+ return EINVAL;
+
+ thread_t panic_spinlock_thread;
+ /* Initialize panic spinlock */
+ lck_grp_t * panic_spinlock_grp;
+ lck_grp_attr_t * panic_spinlock_grp_attr;
+ lck_attr_t * panic_spinlock_attr;
+
+ panic_spinlock_grp_attr = lck_grp_attr_alloc_init();
+ panic_spinlock_grp = lck_grp_alloc_init("panic_spinlock", panic_spinlock_grp_attr);
+ panic_spinlock_attr = lck_attr_alloc_init();
+
+ lck_spin_init(&spinlock_panic_test_lock, panic_spinlock_grp, panic_spinlock_attr);
+
+
+ /* Create thread to acquire spinlock */
+ if (kernel_thread_start(spinlock_panic_test_acquire_spinlock, NULL, &panic_spinlock_thread) != KERN_SUCCESS) {
+ return EBUSY;
+ }
+
+ /* Try to acquire spinlock -- should panic eventually */
+ lck_spin_lock(&spinlock_panic_test_lock);
+ while(1) { ; }
+}
+
+__attribute__((noreturn))
+static void
+simultaneous_panic_worker
+(void * arg, wait_result_t wres __unused)
+{
+ atomic_int *start_panic = (atomic_int *)arg;
+
+ while (!atomic_load(start_panic)) { ; }
+ panic("SIMULTANEOUS PANIC TEST: INITIATING PANIC FROM CPU %d", cpu_number());
+ __builtin_unreachable();
+}
+
+static int
+sysctl_simultaneous_panic_test SYSCTL_HANDLER_ARGS
+{
+#pragma unused(oidp, arg1, arg2)
+ if (req->newlen == 0)
+ return EINVAL;
+
+ int i = 0, threads_to_create = 2 * processor_count;
+ atomic_int start_panic = 0;
+ unsigned int threads_created = 0;
+ thread_t new_panic_thread;
+
+ for (i = threads_to_create; i > 0; i--) {
+ if (kernel_thread_start(simultaneous_panic_worker, (void *) &start_panic, &new_panic_thread) == KERN_SUCCESS) {
+ threads_created++;
+ }
+ }
+
+ /* FAIL if we couldn't create at least processor_count threads */
+ if (threads_created < processor_count) {
+ panic("SIMULTANEOUS PANIC TEST: FAILED TO CREATE ENOUGH THREADS, ONLY CREATED %d (of %d)",
+ threads_created, threads_to_create);
+ }
+
+ atomic_exchange(&start_panic, 1);
+ while (1) { ; }
+}
+
+SYSCTL_PROC(_debug, OID_AUTO, panic_test, CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_KERN | CTLFLAG_MASKED, 0, 0, sysctl_panic_test, "A", "panic test");
+SYSCTL_PROC(_debug, OID_AUTO, debugger_test, CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_KERN | CTLFLAG_MASKED, 0, 0, sysctl_debugger_test, "A", "debugger test");
+SYSCTL_PROC(_debug, OID_AUTO, spinlock_panic_test, CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_KERN | CTLFLAG_MASKED, 0, 0, sysctl_spinlock_panic_test, "A", "spinlock panic test");
+SYSCTL_PROC(_debug, OID_AUTO, simultaneous_panic_test, CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_KERN | CTLFLAG_MASKED, 0, 0, sysctl_simultaneous_panic_test, "A", "simultaneous panic test");
+
+
+#endif /* DEVELOPMENT || DEBUG */
+
+const uint32_t thread_groups_supported = 0;
+
+STATIC int
+sysctl_thread_groups_supported (__unused struct sysctl_oid *oidp, __unused void *arg1, __unused int arg2, struct sysctl_req *req)
+{
+ int value = thread_groups_supported;
+ return sysctl_io_number(req, value, sizeof(value), NULL, NULL);
+}
+
+SYSCTL_PROC(_kern, OID_AUTO, thread_groups_supported, CTLFLAG_RD | CTLFLAG_LOCKED | CTLFLAG_KERN,
+ 0, 0, &sysctl_thread_groups_supported, "I", "thread groups supported");
+
+static int
+sysctl_grade_cputype SYSCTL_HANDLER_ARGS
+{
+#pragma unused(arg1, arg2, oidp)
+ int error = 0;
+ int type_tuple[2] = {};
+ int return_value = 0;
+
+ error = SYSCTL_IN(req, &type_tuple, sizeof(type_tuple));
+
+ if (error) {
+ return error;
+ }
+
+ return_value = grade_binary(type_tuple[0], type_tuple[1]);
+
+ error = SYSCTL_OUT(req, &return_value, sizeof(return_value));
+
+ if (error) {
+ return error;
+ }
+
+ return error;
+}
+
+SYSCTL_PROC(_kern, OID_AUTO, grade_cputype,
+ CTLFLAG_RW|CTLFLAG_ANYBODY|CTLFLAG_MASKED|CTLFLAG_LOCKED|CTLTYPE_OPAQUE,
+ 0, 0, &sysctl_grade_cputype, "S",
+ "grade value of cpu_type_t+cpu_sub_type_t");