[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:
Justin C. Miller
2022-01-08 01:00:43 -08:00
parent a3fff889d1
commit eeef23c2b7
12 changed files with 134 additions and 30 deletions

View File

@@ -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"