mirror of
https://github.com/justinian/jsix.git
synced 2025-12-10 00:14:32 -08:00
[panic] Have panics stop all cores
Kernel panics previously only stopped the calling core. This commit re-implements the panic system to allow us to stop all cores on a panic. Changes include: - panic now sends an NMI to all cores. This means we can't control the contents of their registers, so panic information has been moved to a global struct, and the panicking cpu sets the pointer to that data in its cpu_data. - the panic_handler is now set up with mutexes to print appropriately and only initialize objects once. - copying _current_gsbase into the panic handler, and #including the cpprt.cpp file (so that we can define NDEBUG and not have it try to link the assert code back in) - making the symbol data pointer in kargs an actual pointer again, not an address - and carrying that through to the panic handler - the number of cpus is now saved globally in the kernel as g_num_cpus
This commit is contained in:
@@ -114,7 +114,7 @@ load_resources(bootproto::args *args, video::screen *screen, uefi::handle image,
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
util::buffer symbol_table = loader::load_file(disk, d);
|
util::buffer symbol_table = loader::load_file(disk, d);
|
||||||
args->symbol_table = reinterpret_cast<uintptr_t>(symbol_table.pointer);
|
args->symbol_table = symbol_table.pointer;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -139,11 +139,11 @@ init_ap_trampoline:
|
|||||||
ret
|
ret
|
||||||
|
|
||||||
extern long_ap_startup
|
extern long_ap_startup
|
||||||
global ap_idle
|
global ap_idle:function (ap_idle.end - ap_idle)
|
||||||
ap_idle:
|
ap_idle:
|
||||||
call long_ap_startup
|
call long_ap_startup
|
||||||
sti
|
sti
|
||||||
.hang:
|
.hang:
|
||||||
hlt
|
hlt
|
||||||
jmp .hang
|
jmp .hang
|
||||||
|
.end:
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
namespace panic {
|
namespace panic {
|
||||||
|
|
||||||
uint32_t *apic_icr = reinterpret_cast<uint32_t*>(0xffffc000fee00300);
|
uint32_t *apic_icr = reinterpret_cast<uint32_t*>(0xffffc000fee00300);
|
||||||
uintptr_t symbol_table = 0;
|
void const *symbol_table = nullptr;
|
||||||
|
|
||||||
} // namespace panic
|
} // namespace panic
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,17 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include "cpu.h"
|
||||||
|
|
||||||
namespace panic {
|
namespace panic {
|
||||||
|
|
||||||
constexpr uint32_t send_nmi_command =
|
constexpr uint32_t send_nmi_command =
|
||||||
(4 << 8) | // Delivery mode NMI
|
(4 << 8) | // Delivery mode NMI
|
||||||
(1 << 14) | // assert level high
|
(1 << 14) | // assert level high
|
||||||
(1 << 18); // destination self
|
(2 << 18); // destination all
|
||||||
|
|
||||||
extern uint32_t *apic_icr;
|
extern uint32_t *apic_icr;
|
||||||
extern uintptr_t symbol_table;
|
extern void const *symbol_table;
|
||||||
|
|
||||||
__attribute__ ((always_inline))
|
__attribute__ ((always_inline))
|
||||||
inline void panic(
|
inline void panic(
|
||||||
@@ -19,19 +20,23 @@ inline void panic(
|
|||||||
const char *file = __builtin_FILE(),
|
const char *file = __builtin_FILE(),
|
||||||
uint64_t line = __builtin_LINE())
|
uint64_t line = __builtin_LINE())
|
||||||
{
|
{
|
||||||
register uintptr_t syms asm("rdi");
|
cpu_data &cpu = current_cpu();
|
||||||
register const char *m asm("rsi");
|
|
||||||
register const char *fn asm("rdx");
|
|
||||||
register const char *fi asm("rcx");
|
|
||||||
register uint64_t l asm("r8");
|
|
||||||
|
|
||||||
asm volatile ("mov %1, %0" : "=r"(syms) : "r"(symbol_table));
|
// Grab the global panic block for ourselves
|
||||||
asm volatile ("mov %1, %0" : "=r"(m) : "r"(message));
|
cpu.panic = __atomic_exchange_n(&g_panic_data_p, nullptr, __ATOMIC_ACQ_REL);
|
||||||
asm volatile ("mov %1, %0" : "=r"(fn) : "r"(function));
|
|
||||||
asm volatile ("mov %1, %0" : "=r"(fi) : "r"(file));
|
// If we aren't the first CPU to panic, cpu.panic will be null
|
||||||
asm volatile ("mov %1, %0" : "=r"(l) : "r"(line));
|
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");
|
while (1) asm ("hlt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#include "assert.h"
|
#include "assert.h"
|
||||||
#include "cpu.h"
|
#include "cpu.h"
|
||||||
@@ -12,6 +13,11 @@
|
|||||||
#include "syscall.h"
|
#include "syscall.h"
|
||||||
#include "tss.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;
|
cpu_data g_bsp_cpu_data;
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -37,9 +43,18 @@ cpu_validate()
|
|||||||
#undef CPU_FEATURE_REQ
|
#undef CPU_FEATURE_REQ
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
global_cpu_init()
|
||||||
|
{
|
||||||
|
memset(&g_panic_data, 0, sizeof(g_panic_data));
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
cpu_early_init(cpu_data *cpu)
|
cpu_early_init(cpu_data *cpu)
|
||||||
{
|
{
|
||||||
|
if (cpu->index == 0)
|
||||||
|
global_cpu_init();
|
||||||
|
|
||||||
cpu->idt->install();
|
cpu->idt->install();
|
||||||
cpu->gdt->install();
|
cpu->gdt->install();
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,20 @@ struct cpu_state
|
|||||||
uint64_t rip, cs, rflags, rsp, ss;
|
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
|
/// Per-cpu state data. If you change this, remember to update the assembly
|
||||||
/// version in 'tasking.inc'
|
/// version in 'tasking.inc'
|
||||||
struct cpu_data
|
struct cpu_data
|
||||||
@@ -39,6 +53,7 @@ struct cpu_data
|
|||||||
// Members beyond this point do not appear in
|
// Members beyond this point do not appear in
|
||||||
// the assembly version
|
// the assembly version
|
||||||
lapic *apic;
|
lapic *apic;
|
||||||
|
panic_data *panic;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern "C" cpu_data * _current_gsbase();
|
extern "C" cpu_data * _current_gsbase();
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ kernel_main(bootproto::args *args)
|
|||||||
{
|
{
|
||||||
if (args->panic) {
|
if (args->panic) {
|
||||||
IDT::set_nmi_handler(args->panic->entrypoint);
|
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();
|
init_console();
|
||||||
@@ -149,7 +149,7 @@ kernel_main(bootproto::args *args)
|
|||||||
apic->calibrate_timer();
|
apic->calibrate_timer();
|
||||||
|
|
||||||
const auto &apic_ids = devices.get_apic_ids();
|
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();
|
interrupts_enable();
|
||||||
g_com1.handle_interrupt();
|
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;
|
scheduler_ready = true;
|
||||||
|
|
||||||
// Load the init server
|
// Load the init server
|
||||||
|
|||||||
@@ -7,6 +7,8 @@
|
|||||||
|
|
||||||
namespace panicking {
|
namespace panicking {
|
||||||
|
|
||||||
|
const char *clear = "\e[0m\n";
|
||||||
|
|
||||||
void
|
void
|
||||||
print_header(
|
print_header(
|
||||||
serial_port &out,
|
serial_port &out,
|
||||||
@@ -15,7 +17,8 @@ print_header(
|
|||||||
const char *file,
|
const char *file,
|
||||||
uint64_t line)
|
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) {
|
if (message) {
|
||||||
out.write(message);
|
out.write(message);
|
||||||
out.write("\n ");
|
out.write("\n ");
|
||||||
@@ -28,8 +31,16 @@ print_header(
|
|||||||
char linestr[6];
|
char linestr[6];
|
||||||
snprintf(linestr, sizeof(linestr), "%ld", line);
|
snprintf(linestr, sizeof(linestr), "%ld", line);
|
||||||
out.write(linestr);
|
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
|
void
|
||||||
@@ -64,7 +75,7 @@ print_reg(serial_port &out, const char *name, uint64_t val, const char *color)
|
|||||||
void
|
void
|
||||||
print_cpu_state(serial_port &out, const cpu_state ®s)
|
print_cpu_state(serial_port &out, const cpu_state ®s)
|
||||||
{
|
{
|
||||||
out.write("\e[0m\n");
|
out.write(clear);
|
||||||
|
|
||||||
// Row 1
|
// Row 1
|
||||||
print_reg(out, "rsp", regs.rsp, "1;34");
|
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, "ss", regs.ss, "1;33");
|
||||||
print_reg(out, "cs", regs.cs, "1;33");
|
print_reg(out, "cs", regs.cs, "1;33");
|
||||||
print_reg(out, "flg", regs.rflags, "0;37");
|
print_reg(out, "flg", regs.rflags, "0;37");
|
||||||
out.write("\e[0m\n");
|
out.write(clear);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace panicking
|
} // namespace panicking
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
struct cpu_state;
|
struct cpu_state;
|
||||||
|
struct cpu_data;
|
||||||
|
|
||||||
namespace panicking {
|
namespace panicking {
|
||||||
|
|
||||||
@@ -24,6 +25,7 @@ void print_header(
|
|||||||
const char *file,
|
const char *file,
|
||||||
uint64_t line);
|
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_callstack(serial_port &out, symbol_table &syms, frame const *fp);
|
||||||
void print_cpu_state(serial_port &out, const cpu_state ®s);
|
void print_cpu_state(serial_port &out, const cpu_state ®s);
|
||||||
|
|
||||||
|
|||||||
@@ -10,9 +10,17 @@ _panic_entry:
|
|||||||
push 0 ; NMI doesn't push an error code
|
push 0 ; NMI doesn't push an error code
|
||||||
push 2 ; NMI is int 2
|
push 2 ; NMI is int 2
|
||||||
push_all
|
push_all
|
||||||
|
check_swap_gs
|
||||||
|
|
||||||
mov r9, rsp
|
mov r9, rsp
|
||||||
|
|
||||||
mov rax, [rsp + REGS.rip]
|
mov rax, [rsp + REGS.rip]
|
||||||
push rax
|
push rax
|
||||||
|
|
||||||
jmp panic_handler
|
jmp panic_handler
|
||||||
|
|
||||||
|
|
||||||
|
global _current_gsbase
|
||||||
|
_current_gsbase:
|
||||||
|
mov rax, [gs:0]
|
||||||
|
ret
|
||||||
|
|||||||
@@ -1,12 +1,26 @@
|
|||||||
#include "kutil/spinlock.h"
|
#include <new>
|
||||||
|
#include <util/no_construct.h>
|
||||||
|
|
||||||
#include "assert.h"
|
#include "cpu.h"
|
||||||
#include "display.h"
|
#include "display.h"
|
||||||
|
#include "io.h"
|
||||||
#include "serial.h"
|
#include "serial.h"
|
||||||
#include "symbol_table.h"
|
#include "symbol_table.h"
|
||||||
|
|
||||||
struct cpu_state;
|
struct cpu_state;
|
||||||
|
|
||||||
|
bool main_cpu_done = false;
|
||||||
|
bool asserting_locked = false;
|
||||||
|
unsigned remaining = 0;
|
||||||
|
|
||||||
|
util::no_construct<panicking::serial_port> __com1_storage;
|
||||||
|
panicking::serial_port &com1 = __com1_storage.value;
|
||||||
|
|
||||||
|
util::no_construct<panicking::symbol_table> __syms_storage;
|
||||||
|
panicking::symbol_table &syms = __syms_storage.value;
|
||||||
|
|
||||||
|
constexpr int order = __ATOMIC_ACQ_REL;
|
||||||
|
|
||||||
extern "C"
|
extern "C"
|
||||||
void panic_handler(
|
void panic_handler(
|
||||||
const void *symbol_data,
|
const void *symbol_data,
|
||||||
@@ -16,15 +30,49 @@ void panic_handler(
|
|||||||
uint64_t line,
|
uint64_t line,
|
||||||
const cpu_state *regs)
|
const cpu_state *regs)
|
||||||
{
|
{
|
||||||
panicking::serial_port com1(panicking::COM1);
|
cpu_data &cpu = current_cpu();
|
||||||
panicking::symbol_table syms(symbol_data);
|
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;
|
panicking::frame const *fp = nullptr;
|
||||||
asm ( "mov %%rbp, %0" : "=r" (fp) );
|
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_callstack(com1, syms, fp);
|
||||||
print_cpu_state(com1, *regs);
|
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");
|
while (1) asm ("hlt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define NDEBUG
|
||||||
|
#include "../cpprt.cpp"
|
||||||
|
|||||||
@@ -133,7 +133,7 @@ struct args
|
|||||||
program *panic;
|
program *panic;
|
||||||
allocation_register *allocations;
|
allocation_register *allocations;
|
||||||
uintptr_t modules;
|
uintptr_t modules;
|
||||||
uintptr_t symbol_table;
|
void const *symbol_table;
|
||||||
|
|
||||||
void *runtime_services;
|
void *runtime_services;
|
||||||
void *acpi_table;
|
void *acpi_table;
|
||||||
|
|||||||
Reference in New Issue
Block a user