+ va_list panic_str_args;
+
+ va_start(panic_str_args, str);
+ panic_trap_to_debugger(str, &panic_str_args, 0, NULL, 0, NULL, (unsigned long)(char *)__builtin_return_address(0));
+ va_end(panic_str_args);
+}
+
+void
+panic_with_options(unsigned int reason, void *ctx, uint64_t debugger_options_mask, const char *str, ...)
+{
+ va_list panic_str_args;
+
+ va_start(panic_str_args, str);
+ panic_trap_to_debugger(str, &panic_str_args, reason, ctx, (debugger_options_mask & ~DEBUGGER_INTERNAL_OPTIONS_MASK),
+ NULL, (unsigned long)(char *)__builtin_return_address(0));
+ va_end(panic_str_args);
+}
+
+#if defined (__x86_64__)
+/*
+ * panic_with_thread_context() is used on x86 platforms to specify a different thread that should be backtraced in the paniclog.
+ * We don't generally need this functionality on embedded platforms because embedded platforms include a panic time stackshot
+ * from customer devices. We plumb the thread pointer via the debugger trap mechanism and backtrace the kernel stack from the
+ * thread when writing the panic log.
+ *
+ * NOTE: panic_with_thread_context() should be called with an explicit thread reference held on the passed thread.
+ */
+void
+panic_with_thread_context(unsigned int reason, void *ctx, uint64_t debugger_options_mask, thread_t thread, const char *str, ...)
+{
+ va_list panic_str_args;
+ __assert_only os_ref_count_t th_ref_count;
+
+ assert_thread_magic(thread);
+ th_ref_count = os_ref_get_count(&thread->ref_count);
+ assertf(th_ref_count > 0, "panic_with_thread_context called with invalid thread %p with refcount %u", thread, th_ref_count);
+
+ /* Take a reference on the thread so it doesn't disappear by the time we try to backtrace it */
+ thread_reference(thread);
+
+ va_start(panic_str_args, str);
+ panic_trap_to_debugger(str, &panic_str_args, reason, ctx, ((debugger_options_mask & ~DEBUGGER_INTERNAL_OPTIONS_MASK) | DEBUGGER_INTERNAL_OPTION_THREAD_BACKTRACE),
+ thread, (unsigned long)(char *)__builtin_return_address(0));
+
+ va_end(panic_str_args);
+}
+#endif /* defined (__x86_64__) */
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wmissing-noreturn"
+void
+panic_trap_to_debugger(const char *panic_format_str, va_list *panic_args, unsigned int reason, void *ctx,
+ uint64_t panic_options_mask, void *panic_data_ptr, unsigned long panic_caller)
+{
+#pragma clang diagnostic pop
+
+#if defined(__x86_64__) && (DEVELOPMENT || DEBUG)
+ /* Turn off I/O tracing once we've panicked */
+ mmiotrace_enabled = 0;
+#endif
+
+ if (ml_wants_panic_trap_to_debugger()) {
+ ml_panic_trap_to_debugger(panic_format_str, panic_args, reason, ctx, panic_options_mask, panic_caller);
+ __builtin_trap();
+ }
+
+ CPUDEBUGGERCOUNT++;
+
+ if (CPUDEBUGGERCOUNT > max_debugger_entry_count) {
+ static boolean_t in_panic_kprintf = FALSE;
+
+ /* Notify any listeners that we've started a panic */
+ PEHaltRestart(kPEPanicBegin);
+
+ if (!in_panic_kprintf) {
+ in_panic_kprintf = TRUE;
+ kprintf("Detected nested debugger entry count exceeding %d\n",
+ max_debugger_entry_count);
+ in_panic_kprintf = FALSE;
+ }
+
+ if (!panicDebugging) {
+ kdp_machine_reboot_type(kPEPanicRestartCPU, panic_options_mask);
+ }
+
+ panic_spin_forever();
+ }
+
+#if DEVELOPMENT || DEBUG
+ DEBUGGER_DEBUGGING_NESTED_PANIC_IF_REQUESTED((panic_options_mask & DEBUGGER_OPTION_RECURPANIC_ENTRY));
+#endif
+
+ PE_panic_hook(panic_format_str);