diff --git a/src/boot/main.cpp b/src/boot/main.cpp index 9c2573a..75bc84b 100644 --- a/src/boot/main.cpp +++ b/src/boot/main.cpp @@ -114,7 +114,7 @@ load_resources(bootproto::args *args, video::screen *screen, uefi::handle image, continue; util::buffer symbol_table = loader::load_file(disk, d); - args->symbol_table = reinterpret_cast(symbol_table.pointer); + args->symbol_table = symbol_table.pointer; break; } diff --git a/src/kernel/ap_startup.s b/src/kernel/ap_startup.s index ebf5915..e43a627 100644 --- a/src/kernel/ap_startup.s +++ b/src/kernel/ap_startup.s @@ -139,11 +139,11 @@ init_ap_trampoline: ret extern long_ap_startup -global ap_idle +global ap_idle:function (ap_idle.end - ap_idle) ap_idle: call long_ap_startup sti .hang: hlt jmp .hang - +.end: diff --git a/src/kernel/assert.cpp b/src/kernel/assert.cpp index 97a1756..e805eb7 100644 --- a/src/kernel/assert.cpp +++ b/src/kernel/assert.cpp @@ -3,7 +3,7 @@ namespace panic { uint32_t *apic_icr = reinterpret_cast(0xffffc000fee00300); -uintptr_t symbol_table = 0; +void const *symbol_table = nullptr; } // namespace panic diff --git a/src/kernel/assert.h b/src/kernel/assert.h index e71921b..436166f 100644 --- a/src/kernel/assert.h +++ b/src/kernel/assert.h @@ -1,16 +1,17 @@ #pragma once #include +#include "cpu.h" namespace panic { constexpr uint32_t send_nmi_command = (4 << 8) | // Delivery mode NMI (1 << 14) | // assert level high - (1 << 18); // destination self + (2 << 18); // destination all extern uint32_t *apic_icr; -extern uintptr_t symbol_table; +extern void const *symbol_table; __attribute__ ((always_inline)) inline void panic( @@ -19,19 +20,23 @@ inline void panic( const char *file = __builtin_FILE(), uint64_t line = __builtin_LINE()) { - register uintptr_t syms asm("rdi"); - register const char *m asm("rsi"); - register const char *fn asm("rdx"); - register const char *fi asm("rcx"); - register uint64_t l asm("r8"); + cpu_data &cpu = current_cpu(); - asm volatile ("mov %1, %0" : "=r"(syms) : "r"(symbol_table)); - asm volatile ("mov %1, %0" : "=r"(m) : "r"(message)); - asm volatile ("mov %1, %0" : "=r"(fn) : "r"(function)); - asm volatile ("mov %1, %0" : "=r"(fi) : "r"(file)); - asm volatile ("mov %1, %0" : "=r"(l) : "r"(line)); + // Grab the global panic block for ourselves + cpu.panic = __atomic_exchange_n(&g_panic_data_p, nullptr, __ATOMIC_ACQ_REL); + + // If we aren't the first CPU to panic, cpu.panic will be null + if (cpu.panic) { + cpu.panic->symbol_data = symbol_table; + cpu.panic->message = message; + cpu.panic->function = function; + cpu.panic->file = file; + cpu.panic->line = line; + cpu.panic->cpus = g_num_cpus; + + *apic_icr = send_nmi_command; + } - *apic_icr = send_nmi_command; while (1) asm ("hlt"); } diff --git a/src/kernel/cpu.cpp b/src/kernel/cpu.cpp index 64d1c47..64fb482 100644 --- a/src/kernel/cpu.cpp +++ b/src/kernel/cpu.cpp @@ -1,4 +1,5 @@ #include +#include #include "assert.h" #include "cpu.h" @@ -12,6 +13,11 @@ #include "syscall.h" #include "tss.h" +unsigned g_num_cpus = 1; + +panic_data g_panic_data; +panic_data *g_panic_data_p = &g_panic_data; + cpu_data g_bsp_cpu_data; void @@ -37,9 +43,18 @@ cpu_validate() #undef CPU_FEATURE_REQ } +void +global_cpu_init() +{ + memset(&g_panic_data, 0, sizeof(g_panic_data)); +} + void cpu_early_init(cpu_data *cpu) { + if (cpu->index == 0) + global_cpu_init(); + cpu->idt->install(); cpu->gdt->install(); diff --git a/src/kernel/cpu.h b/src/kernel/cpu.h index 453fecc..6c9571b 100644 --- a/src/kernel/cpu.h +++ b/src/kernel/cpu.h @@ -18,6 +18,20 @@ struct cpu_state uint64_t rip, cs, rflags, rsp, ss; }; +/// Kernel-wide panic information +struct panic_data +{ + void const * symbol_data; + char const * message; + char const * function; + char const * file; + uint32_t line; + uint16_t cpus; +}; + +extern unsigned g_num_cpus; +extern panic_data *g_panic_data_p; + /// Per-cpu state data. If you change this, remember to update the assembly /// version in 'tasking.inc' struct cpu_data @@ -39,6 +53,7 @@ struct cpu_data // Members beyond this point do not appear in // the assembly version lapic *apic; + panic_data *panic; }; extern "C" cpu_data * _current_gsbase(); diff --git a/src/kernel/main.cpp b/src/kernel/main.cpp index bf41298..b2e8c9d 100644 --- a/src/kernel/main.cpp +++ b/src/kernel/main.cpp @@ -82,7 +82,7 @@ kernel_main(bootproto::args *args) { if (args->panic) { IDT::set_nmi_handler(args->panic->entrypoint); - panic::symbol_table = args->symbol_table | mem::linear_offset; + panic::symbol_table = util::offset_pointer(args->symbol_table, mem::linear_offset); } init_console(); @@ -149,7 +149,7 @@ kernel_main(bootproto::args *args) apic->calibrate_timer(); const auto &apic_ids = devices.get_apic_ids(); - unsigned num_cpus = start_aps(*apic, apic_ids, args->pml4); + g_num_cpus = start_aps(*apic, apic_ids, args->pml4); interrupts_enable(); g_com1.handle_interrupt(); @@ -178,7 +178,7 @@ kernel_main(bootproto::args *args) } */ - scheduler *sched = new scheduler {num_cpus}; + scheduler *sched = new scheduler {g_num_cpus}; scheduler_ready = true; // Load the init server diff --git a/src/kernel/panic.serial/display.cpp b/src/kernel/panic.serial/display.cpp index a53b537..f6094c5 100644 --- a/src/kernel/panic.serial/display.cpp +++ b/src/kernel/panic.serial/display.cpp @@ -7,6 +7,8 @@ namespace panicking { +const char *clear = "\e[0m\n"; + void print_header( serial_port &out, @@ -15,7 +17,8 @@ print_header( const char *file, uint64_t line) { - out.write("\n\n\e[5;31m PANIC:\e[0;1;31m "); + out.write(clear); + out.write("\n\e[5;31m PANIC:\e[0;1;31m "); if (message) { out.write(message); out.write("\n "); @@ -28,8 +31,16 @@ print_header( char linestr[6]; snprintf(linestr, sizeof(linestr), "%ld", line); out.write(linestr); +} - out.write("\n \e[0;31m===================================================================================\n"); +void +print_cpu(serial_port &out, cpu_data &cpu) +{ + out.write("\n \e[0;31m==[ CPU: "); + char cpuid[7]; + snprintf(cpuid, sizeof(cpuid), "%4x", cpu.id); + out.write(cpuid); + out.write(" ]====================================================================\n"); } void @@ -64,7 +75,7 @@ print_reg(serial_port &out, const char *name, uint64_t val, const char *color) void print_cpu_state(serial_port &out, const cpu_state ®s) { - out.write("\e[0m\n"); + out.write(clear); // Row 1 print_reg(out, "rsp", regs.rsp, "1;34"); @@ -99,7 +110,7 @@ print_cpu_state(serial_port &out, const cpu_state ®s) print_reg(out, "ss", regs.ss, "1;33"); print_reg(out, "cs", regs.cs, "1;33"); print_reg(out, "flg", regs.rflags, "0;37"); - out.write("\e[0m\n"); + out.write(clear); } } // namespace panicking diff --git a/src/kernel/panic.serial/display.h b/src/kernel/panic.serial/display.h index 02f0fcc..93c8604 100644 --- a/src/kernel/panic.serial/display.h +++ b/src/kernel/panic.serial/display.h @@ -5,6 +5,7 @@ #include struct cpu_state; +struct cpu_data; namespace panicking { @@ -24,6 +25,7 @@ void print_header( const char *file, uint64_t line); +void print_cpu(serial_port &out, cpu_data &cpu); void print_callstack(serial_port &out, symbol_table &syms, frame const *fp); void print_cpu_state(serial_port &out, const cpu_state ®s); diff --git a/src/kernel/panic.serial/entry.s b/src/kernel/panic.serial/entry.s index 3bd157f..b05caef 100644 --- a/src/kernel/panic.serial/entry.s +++ b/src/kernel/panic.serial/entry.s @@ -10,9 +10,17 @@ _panic_entry: push 0 ; NMI doesn't push an error code push 2 ; NMI is int 2 push_all + check_swap_gs + mov r9, rsp mov rax, [rsp + REGS.rip] push rax jmp panic_handler + + +global _current_gsbase +_current_gsbase: + mov rax, [gs:0] + ret diff --git a/src/kernel/panic.serial/main.cpp b/src/kernel/panic.serial/main.cpp index 45c7181..1ae26f4 100644 --- a/src/kernel/panic.serial/main.cpp +++ b/src/kernel/panic.serial/main.cpp @@ -1,12 +1,26 @@ -#include "kutil/spinlock.h" +#include +#include -#include "assert.h" +#include "cpu.h" #include "display.h" +#include "io.h" #include "serial.h" #include "symbol_table.h" struct cpu_state; +bool main_cpu_done = false; +bool asserting_locked = false; +unsigned remaining = 0; + +util::no_construct __com1_storage; +panicking::serial_port &com1 = __com1_storage.value; + +util::no_construct __syms_storage; +panicking::symbol_table &syms = __syms_storage.value; + +constexpr int order = __ATOMIC_ACQ_REL; + extern "C" void panic_handler( const void *symbol_data, @@ -16,15 +30,49 @@ void panic_handler( uint64_t line, const cpu_state *regs) { - panicking::serial_port com1(panicking::COM1); - panicking::symbol_table syms(symbol_data); + cpu_data &cpu = current_cpu(); + panic_data *panic = cpu.panic; + + // If we're not running on the CPU that panicked, wait + // for it to finish + if (!panic) { + while (!main_cpu_done); + } else { + new (&com1) panicking::serial_port {panicking::COM1}; + new (&syms) panicking::symbol_table {panic->symbol_data}; + remaining = panic->cpus; + } + + while (__atomic_test_and_set(&asserting_locked, order)) + asm ("pause"); panicking::frame const *fp = nullptr; asm ( "mov %%rbp, %0" : "=r" (fp) ); - print_header(com1, message, function, file, line); + if (panic) + print_header(com1, panic->message, panic->function, + panic->file, panic->line); + + print_cpu(com1, cpu); print_callstack(com1, syms, fp); print_cpu_state(com1, *regs); + __atomic_clear(&asserting_locked, order); + + // If we're running on the CPU that panicked, tell the + // others we have finished + if (panic) + main_cpu_done = true; + + if (__atomic_sub_fetch(&remaining, 1, order) == 0) { + // No remaining CPUs, if we're running on QEMU, + // tell it to exit + constexpr uint32_t exit_code = 0; + asm ( "out %0, %1" :: "a"(exit_code), "Nd"(0xf4) ); + } + while (1) asm ("hlt"); } + +#define NDEBUG +#include "../cpprt.cpp" diff --git a/src/libraries/bootproto/include/bootproto/kernel.h b/src/libraries/bootproto/include/bootproto/kernel.h index 3bc79b3..c5c9d97 100644 --- a/src/libraries/bootproto/include/bootproto/kernel.h +++ b/src/libraries/bootproto/include/bootproto/kernel.h @@ -133,7 +133,7 @@ struct args program *panic; allocation_register *allocations; uintptr_t modules; - uintptr_t symbol_table; + void const *symbol_table; void *runtime_services; void *acpi_table;