[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:
@@ -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 "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<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"
|
||||
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"
|
||||
|
||||
Reference in New Issue
Block a user