From ea9d20a250df65e1f52110f0d20f25f97cc60fa4 Mon Sep 17 00:00:00 2001 From: "Justin C. Miller" Date: Sun, 1 Aug 2021 14:03:10 -0700 Subject: [PATCH] [panic] Add separate kernel-mode panic handler Created the framework for using different loadable panic handlers, loaded by the bootloader. Initial panic handler is panic.serial, which contains its own serial driver and stacktrace code. Other related changes: - Asserts are now based on the NMI handler - panic handlers get installed as the NMI interrupt handler - Changed symbol table generation: now use nm's own demangling and sorting, and include symbol size in the table - Move the linker script argument out of the kernel target, and into the kernel's specific module, so that other programs (ie, panic handlers) can use the kernel target as well - Some asm changes to boot.s to help GDB see stack frames - but this might not actually be all that useful - Renamed user_rsp to just rsp in cpu_state - everything in there is describing the 'user' state --- configs/custom.ninja | 6 +- configs/debug.toml | 1 - scripts/build_symbol_table.py | 43 +++++------ src/boot/main.cpp | 17 +++-- src/boot/paging.cpp | 9 +++ src/boot/paging.h | 11 ++- src/include/kernel_args.h | 3 +- src/include/pointer_manipulation.h | 2 + src/kernel/assert.cpp | 21 ------ src/kernel/boot.s | 13 ++-- src/kernel/cpu.h | 2 +- src/kernel/debug.cpp | 33 +------- src/kernel/debug.h | 1 - src/kernel/idt.cpp | 22 +++++- src/kernel/idt.h | 4 + src/kernel/interrupt_isrs.inc | 2 +- src/kernel/interrupts.cpp | 12 +-- src/kernel/interrupts.h | 2 + src/kernel/interrupts.s | 4 +- src/kernel/io.h | 2 +- src/kernel/kernel.ld | 2 +- src/kernel/main.cpp | 12 +-- src/kernel/module.toml | 5 +- src/kernel/panic.serial/display.cpp | 87 ++++++++++++++++++++++ src/kernel/panic.serial/display.h | 24 ++++++ src/kernel/panic.serial/entry.s | 18 +++++ src/kernel/panic.serial/main.cpp | 27 +++++++ src/kernel/panic.serial/module.toml | 16 ++++ src/kernel/panic.serial/panic.serial.ld | 20 +++++ src/kernel/panic.serial/serial.cpp | 55 ++++++++++++++ src/kernel/panic.serial/serial.h | 23 ++++++ src/kernel/panic.serial/symbol_table.cpp | 28 +++++++ src/kernel/panic.serial/symbol_table.h | 32 ++++++++ src/kernel/symbol_table.cpp | 49 ------------ src/kernel/symbol_table.h | 34 --------- src/libraries/kutil/assert.cpp | 14 +--- src/libraries/kutil/include/kutil/assert.h | 31 +++++--- 37 files changed, 462 insertions(+), 225 deletions(-) delete mode 100644 src/kernel/assert.cpp create mode 100644 src/kernel/panic.serial/display.cpp create mode 100644 src/kernel/panic.serial/display.h create mode 100644 src/kernel/panic.serial/entry.s create mode 100644 src/kernel/panic.serial/main.cpp create mode 100644 src/kernel/panic.serial/module.toml create mode 100644 src/kernel/panic.serial/panic.serial.ld create mode 100644 src/kernel/panic.serial/serial.cpp create mode 100644 src/kernel/panic.serial/serial.h create mode 100644 src/kernel/panic.serial/symbol_table.cpp create mode 100644 src/kernel/panic.serial/symbol_table.h delete mode 100644 src/kernel/symbol_table.cpp delete mode 100644 src/kernel/symbol_table.h diff --git a/configs/custom.ninja b/configs/custom.ninja index e9cad24..5be0fd1 100644 --- a/configs/custom.ninja +++ b/configs/custom.ninja @@ -1,7 +1,7 @@ rule makest description = Making symbol table - command = nm $in | ${source_root}/scripts/build_symbol_table.py $out + command = nm -n -S --demangle $in | ${source_root}/scripts/build_symbol_table.py $out rule makefat description = Creating $name @@ -45,6 +45,9 @@ build ${build_root}/fatroot/testapp.elf : cp ${build_root}/user/testapp.elf build ${build_root}/fatroot/drv.uefi_fb.elf : cp ${build_root}/user/drv.uefi_fb.elf name = UEFI framebuffer driver to FAT image +build ${build_root}/fatroot/panic.serial.elf : cp ${build_root}/kernel/panic.serial.elf + name = UEFI framebuffer driver to FAT image + build ${build_root}/fatroot/srv.init.elf : cp ${build_root}/user/srv.init.elf name = Init server to FAT image @@ -56,6 +59,7 @@ build ${build_root}/jsix.img : makefat | $ ${build_root}/fatroot/drv.uefi_fb.elf $ ${build_root}/fatroot/srv.init.elf $ ${build_root}/fatroot/jsix.elf $ + ${build_root}/fatroot/panic.serial.elf $ ${build_root}/fatroot/efi/boot/bootx64.efi name = jsix.img diff --git a/configs/debug.toml b/configs/debug.toml index d4a57dc..8dc69ac 100644 --- a/configs/debug.toml +++ b/configs/debug.toml @@ -80,7 +80,6 @@ cxxflags = [ ldflags = [ "${ldflags}", - "-T", "${source_root}/src/kernel/kernel.ld", "-g", "-nostdlib", "-Bstatic", diff --git a/scripts/build_symbol_table.py b/scripts/build_symbol_table.py index 516a072..9871ade 100755 --- a/scripts/build_symbol_table.py +++ b/scripts/build_symbol_table.py @@ -4,37 +4,37 @@ # is as follows: # # : 8 bytes -# : 16 * N bytes +# : 24 * N bytes # : variable # # Each index entry has the format # : 8 bytes +# : 8 bytes # : 8 bytes # # Name offsets are from the start of the symbol table as a whole. (ie, # where is located.) +import re + +sym_re = re.compile(r'([0-9a-fA-F]{16}) ([0-9a-fA-F]{16}) [tTvVwW] (.*)') + def parse_syms(infile): """Take the output of the `nm` command, and parse it into a tuple representing the symbols in the text segment of the binary. Returns a list of (address, symbol_name).""" - from cxxfilt import demangle, InvalidName - syms = [] for line in sys.stdin: - addr, t, mangled = line.split() - if t not in "tTvVwW": continue + match = sym_re.match(line) + if not match: continue - try: - name = demangle(mangled) - except InvalidName: - continue + addr = int(match.group(1), base=16) + size = int(match.group(2), base=16) + name = match.group(3) + syms.append([addr, size, name, 0]) - addr = int(addr, base=16) - syms.append((addr, name)) - - return sorted(syms) + return syms def write_table(syms, outfile): @@ -46,29 +46,26 @@ def write_table(syms, outfile): outfile.write(struct.pack("@Q", len(syms))) index_pos = outfile.tell() - outfile.seek(struct.calcsize("@QQ") * len(syms), 1) + outfile.seek(struct.calcsize("@QQQ") * len(syms), 1) nul = b'\0' - positions = {} for s in syms: - addr, name = s - positions[addr] = outfile.tell() - - data = name.encode('utf-8') - outfile.write(name.encode('utf-8')) + s[3] = outfile.tell() + outfile.write(s[2].encode('utf-8')) outfile.write(nul) outfile.seek(index_pos) for s in syms: addr = s[0] - pos = positions[addr] - outfile.write(struct.pack("@QQ", addr, pos)) + size = s[1] + pos = s[3] + outfile.write(struct.pack("@QQQ", addr, size, pos)) if __name__ == "__main__": import sys if len(sys.argv) != 2: - print(f"Usage: {sys.argv[0]} ") + print(f"Usage: nm -n -S --demangle | {sys.argv[0]} ") sys.exit(1) outfile = open(sys.argv[1], "wb") diff --git a/src/boot/main.cpp b/src/boot/main.cpp index d56638a..b9b25e2 100644 --- a/src/boot/main.cpp +++ b/src/boot/main.cpp @@ -33,6 +33,8 @@ const loader::program_desc kern_desc = {L"kernel", L"jsix.elf"}; const loader::program_desc init_desc = {L"init server", L"srv.init.elf"}; const loader::program_desc fb_driver = {L"UEFI framebuffer driver", L"drv.uefi_fb.elf"}; +const loader::program_desc panic_handler = {L"Serial panic handler", L"panic.serial.elf"}; + const loader::program_desc extra_programs[] = { {L"test application", L"testapp.elf"}, }; @@ -86,16 +88,17 @@ load_resources(init::args *args, video::screen *screen, uefi::handle image, uefi loader::load_module(disk, fb_driver); } - args->symbol_table = loader::load_file(disk, {L"symbol table", L"symbol_table.dat"}); + buffer symbol_table = loader::load_file(disk, {L"symbol table", L"symbol_table.dat"}); + args->symbol_table = reinterpret_cast(symbol_table.pointer); args->kernel = loader::load_program(disk, kern_desc, true); args->init = loader::load_program(disk, init_desc); + args->panic = loader::load_program(disk, panic_handler); for (auto &desc : extra_programs) loader::load_module(disk, desc); loader::verify_kernel_header(*args->kernel); - } memory::efi_mem_map @@ -144,15 +147,14 @@ efi_main(uefi::handle image, uefi::system_table *st) status_bar status {screen}; // Switch to fb status display - // Map the kernel to the appropriate address - init::program &kernel = *args->kernel; - for (auto §ion : kernel.sections) - paging::map_section(args, section); + // Map the kernel and panic handler to the appropriate addresses + paging::map_program(args, *args->kernel); + paging::map_program(args, *args->panic); memory::fix_frame_blocks(args); init::entrypoint kentry = - reinterpret_cast(kernel.entrypoint); + reinterpret_cast(args->kernel->entrypoint); //status.next(); hw::setup_control_regs(); @@ -161,7 +163,6 @@ efi_main(uefi::handle image, uefi::system_table *st) change_pointer(args); change_pointer(args->pml4); - change_pointer(args->symbol_table.pointer); change_pointer(args->kernel); change_pointer(args->kernel->sections.pointer); diff --git a/src/boot/paging.cpp b/src/boot/paging.cpp index d07cda4..8e700fa 100644 --- a/src/boot/paging.cpp +++ b/src/boot/paging.cpp @@ -265,5 +265,14 @@ map_section( has_flag(section.type, section_flags::execute)); } +void +map_program( + kernel::init::args *args, + kernel::init::program &program) +{ + for (auto §ion : program.sections) + paging::map_section(args, section); +} + } // namespace paging } // namespace boot diff --git a/src/boot/paging.h b/src/boot/paging.h index 16db0c4..8be7880 100644 --- a/src/boot/paging.h +++ b/src/boot/paging.h @@ -50,14 +50,13 @@ void map_pages( uintptr_t phys, uintptr_t virt, size_t count, bool write_flag, bool exe_flag); -/// Map a program section in physical memory to its virtual address in the -/// given page tables. +/// Map the sections of a program in physical memory to their virtual memory +/// addresses in the given page tables. /// \arg args The kernel args struct, used for the page table cache and pml4 -/// \arg section The program section to load -void map_section( +/// \arg program The program to load +void map_program( kernel::init::args *args, - const kernel::init::program_section §ion); - + kernel::init::program &program); } // namespace paging } // namespace boot diff --git a/src/include/kernel_args.h b/src/include/kernel_args.h index 54c2387..73ba20d 100644 --- a/src/include/kernel_args.h +++ b/src/include/kernel_args.h @@ -126,9 +126,10 @@ struct args program *kernel; program *init; - counted symbol_table; + program *panic; allocation_register *allocations; uintptr_t modules; + uintptr_t symbol_table; void *runtime_services; void *acpi_table; diff --git a/src/include/pointer_manipulation.h b/src/include/pointer_manipulation.h index ae1b1bf..a41e36d 100644 --- a/src/include/pointer_manipulation.h +++ b/src/include/pointer_manipulation.h @@ -2,6 +2,8 @@ /// Helper functions and types for doing type-safe byte-wise pointer math. #pragma once +#include + /// Return a pointer offset from `input` by `offset` bytes. /// \tparam T Cast the return value to a pointer to `T` /// \tparam S The type pointed to by the `input` pointer diff --git a/src/kernel/assert.cpp b/src/kernel/assert.cpp deleted file mode 100644 index 2e9c83d..0000000 --- a/src/kernel/assert.cpp +++ /dev/null @@ -1,21 +0,0 @@ -#include "kutil/assert.h" -#include "console.h" - -[[noreturn]] void -__kernel_assert(const char *file, unsigned line, const char *message) -{ - console *cons = console::get(); - if (cons) { - cons->set_color(9 , 0); - cons->puts("\n\n ERROR: "); - cons->puts(message); - cons->puts("\n "); - cons->puts(file); - cons->puts(":"); - cons->put_dec(line); - cons->puts("\n"); - } - - while (1) __asm__ ("hlt"); - __asm__ ( "int $0xe4" ); -} diff --git a/src/kernel/boot.s b/src/kernel/boot.s index 51093f5..d42293f 100644 --- a/src/kernel/boot.s +++ b/src/kernel/boot.s @@ -17,16 +17,18 @@ _kernel_header: section .text align 16 global _kernel_start:function (_kernel_start.end - _kernel_start) +global _kernel_start.real _kernel_start: + push rbp ; Never executed, fake function prelude + mov rbp, rsp ; to calm down gdb + +.real: cli mov rsp, idle_stack_end - mov qword [rsp + 0x00], 0 ; signal end of stack with 0 return address - mov qword [rsp + 0x08], 0 ; and a few extra entries in case of stack - mov qword [rsp + 0x10], 0 ; problems - mov qword [rsp + 0x18], 0 - + sub rsp, 16 mov rbp, rsp + extern kernel_main call kernel_main @@ -54,4 +56,3 @@ idle_stack_begin: global idle_stack_end idle_stack_end: - resq 4 diff --git a/src/kernel/cpu.h b/src/kernel/cpu.h index d963430..9e5ab01 100644 --- a/src/kernel/cpu.h +++ b/src/kernel/cpu.h @@ -15,7 +15,7 @@ struct cpu_state uint64_t r15, r14, r13, r12, r11, r10, r9, r8; uint64_t rdi, rsi, rbp, rbx, rdx, rcx, rax; uint64_t interrupt, errorcode; - uint64_t rip, cs, rflags, user_rsp, ss; + uint64_t rip, cs, rflags, rsp, ss; }; /// Per-cpu state data. If you change this, remember to update the assembly diff --git a/src/kernel/debug.cpp b/src/kernel/debug.cpp index ec1aa5e..e848055 100644 --- a/src/kernel/debug.cpp +++ b/src/kernel/debug.cpp @@ -4,7 +4,6 @@ #include "gdt.h" #include "objects/process.h" #include "objects/thread.h" -#include "symbol_table.h" size_t __counter_syscall_enter = 0; size_t __counter_syscall_sysret = 0; @@ -43,7 +42,7 @@ print_regs(const cpu_state ®s) cons->puts("\n\n"); print_regL("rbp", regs.rbp); - print_regM("rsp", regs.user_rsp); + print_regM("rsp", regs.rsp); print_regR("sp0", cpu.rsp0); print_regL("rip", regs.rip); @@ -59,41 +58,13 @@ struct frame uintptr_t return_addr; }; -void -print_stacktrace(int skip) -{ - console *cons = console::get(); - symbol_table *syms = symbol_table::get(); - - frame *fp = nullptr; - int fi = -skip; - __asm__ __volatile__ ( "mov %%rbp, %0" : "=r" (fp) ); - - while (fp && fp->return_addr) { - if (fi++ >= 0) { - const symbol_table::entry *e = syms ? syms->find_symbol(fp->return_addr) : nullptr; - const char *name = e ? e->name : ""; - cons->printf(" frame %2d:", fi-1); - - cons->set_color(5); - cons->printf(" %016llx", fp->return_addr); - cons->set_color(); - - cons->set_color(6); - cons->printf(" %s\n", name); - cons->set_color(); - } - fp = fp->prev; - } -} - void print_stack(const cpu_state ®s) { console *cons = console::get(); cons->puts("\nStack:\n"); - uint64_t sp = regs.user_rsp; + uint64_t sp = regs.rsp; while (sp <= regs.rbp) { cons->printf("%016x: %016x\n", sp, *reinterpret_cast(sp)); sp += sizeof(uint64_t); diff --git a/src/kernel/debug.h b/src/kernel/debug.h index 58e62ad..e7b9e04 100644 --- a/src/kernel/debug.h +++ b/src/kernel/debug.h @@ -21,7 +21,6 @@ extern size_t __counter_syscall_sysret; void print_regs(const cpu_state ®s); void print_stack(const cpu_state ®s); -void print_stacktrace(int skip); #define print_regL(name, value) cons->printf(" %s: %016lx", name, (value)); #define print_regM(name, value) cons->printf(" %s: %016lx", name, (value)); diff --git a/src/kernel/idt.cpp b/src/kernel/idt.cpp index 1205b40..92d303c 100644 --- a/src/kernel/idt.cpp +++ b/src/kernel/idt.cpp @@ -8,10 +8,12 @@ extern "C" { void idt_write(const void *idt_ptr); #define ISR(i, s, name) extern void name (); +#define NISR(i, s, name) #define EISR(i, s, name) extern void name (); #define IRQ(i, q, name) extern void name (); #include "interrupt_isrs.inc" #undef IRQ +#undef NISR #undef EISR #undef ISR } @@ -21,7 +23,13 @@ extern "C" { // the previous initialization. static kutil::no_construct __g_bsp_idt_storage; IDT &g_bsp_idt = __g_bsp_idt_storage.value; +void (*__nmi_handler)(); +void +IDT::set_nmi_handler(uintptr_t address) +{ + __nmi_handler = reinterpret_cast(address); +} IDT::IDT() { @@ -32,7 +40,9 @@ IDT::IDT() #define ISR(i, s, name) set(i, & name, 0x08, 0x8e); #define EISR(i, s, name) set(i, & name, 0x08, 0x8e); #define IRQ(i, q, name) set(i, & name, 0x08, 0x8e); +#define NISR(i, s, name) set(i, __nmi_handler, 0x08, 0x8e); #include "interrupt_isrs.inc" +#undef NISR #undef IRQ #undef EISR #undef ISR @@ -55,10 +65,12 @@ void IDT::add_ist_entries() { #define ISR(i, s, name) if (s) { set_ist(i, s); } +#define NISR(i, s, name) if (s) { set_ist(i, s); } #define EISR(i, s, name) if (s) { set_ist(i, s); } #define IRQ(i, q, name) #include "interrupt_isrs.inc" #undef IRQ +#undef NISR #undef EISR #undef ISR } @@ -66,16 +78,20 @@ IDT::add_ist_entries() uint8_t IDT::used_ist_entries() const { - uint8_t entries = 0; + constexpr uint8_t entries = -#define ISR(i, s, name) if (s) { entries |= (1 << s); } -#define EISR(i, s, name) if (s) { entries |= (1 << s); } +#define ISR(i, s, name) ((s) ? (1 << s) : 0) | +#define NISR(i, s, name) ((s) ? (1 << s) : 0) | +#define EISR(i, s, name) ((s) ? (1 << s) : 0) | #define IRQ(i, q, name) #include "interrupt_isrs.inc" #undef IRQ +#undef NISR #undef EISR #undef ISR + 0; + return entries; } diff --git a/src/kernel/idt.h b/src/kernel/idt.h index 9ff28eb..fb74a7d 100644 --- a/src/kernel/idt.h +++ b/src/kernel/idt.h @@ -11,6 +11,10 @@ public: /// Get the currently running CPU's IDT static IDT & current(); + /// Set the global NMI handler. Must happen before creating + /// any IDTs. + static void set_nmi_handler(uintptr_t address); + /// Install this IDT to the current CPU void install() const; diff --git a/src/kernel/interrupt_isrs.inc b/src/kernel/interrupt_isrs.inc index fe2f1b0..2cd76b7 100644 --- a/src/kernel/interrupt_isrs.inc +++ b/src/kernel/interrupt_isrs.inc @@ -1,6 +1,6 @@ ISR (0x00, 0, isrDivideByZero) ISR (0x01, 0, isrDebug) -ISR (0x02, 1, isrNMI) +NISR(0x02, 1, isrNMI) ISR (0x03, 0, isrBreakpoint) ISR (0x04, 0, isrOverflow) ISR (0x05, 0, isrBRE) diff --git a/src/kernel/interrupts.cpp b/src/kernel/interrupts.cpp index 2713457..b57e377 100644 --- a/src/kernel/interrupts.cpp +++ b/src/kernel/interrupts.cpp @@ -47,9 +47,11 @@ get_irq(unsigned vector) switch (vector) { #define ISR(i, s, name) #define EISR(i, s, name) +#define NISR(i, s, name) #define IRQ(i, q, name) case i : return q; #include "interrupt_isrs.inc" #undef IRQ +#undef NISR #undef EISR #undef ISR @@ -118,7 +120,7 @@ isr_handler(cpu_state *regs) print_regR("dr7", dr); print_regL("rip", regs->rip); - print_regM("rsp", regs->user_rsp); + print_regM("rsp", regs->rsp); print_regM("fla", regs->rflags); _halt(); } @@ -130,7 +132,7 @@ isr_handler(cpu_state *regs) cons->set_color(); print_regs(*regs); - print_stacktrace(2); + //print_stacktrace(2); _halt(); break; @@ -199,7 +201,7 @@ isr_handler(cpu_state *regs) if (regs->errorcode & 0x10) cons->puts(" ip"); cons->puts("\n"); print_regs(*regs); - print_stacktrace(2); + //print_stacktrace(2); _halt(); } break; @@ -219,7 +221,7 @@ isr_handler(cpu_state *regs) case isr::isrAssert: { cons->set_color(); print_regs(*regs); - print_stacktrace(2); + //print_stacktrace(2); } _halt(); break; @@ -269,7 +271,7 @@ isr_handler(cpu_state *regs) regs->interrupt, regs->errorcode); print_regs(*regs); - print_stacktrace(2); + //print_stacktrace(2); _halt(); } diff --git a/src/kernel/interrupts.h b/src/kernel/interrupts.h index 2a8c35f..cec9521 100644 --- a/src/kernel/interrupts.h +++ b/src/kernel/interrupts.h @@ -8,11 +8,13 @@ enum class isr : uint8_t { #define ISR(i, s, name) name = i, +#define NISR(i, s, name) name = i, #define EISR(i, s, name) name = i, #define IRQ(i, q, name) name = i, #include "interrupt_isrs.inc" #undef IRQ #undef EISR +#undef NISR #undef ISR _zero = 0 diff --git a/src/kernel/interrupts.s b/src/kernel/interrupts.s index e867529..6db01b1 100644 --- a/src/kernel/interrupts.s +++ b/src/kernel/interrupts.s @@ -71,7 +71,9 @@ isr_handler_return: %define EISR(i, s, name) EMIT_EISR name, i ; ISR with error code %define ISR(i, s, name) EMIT_ISR name, i -%define IRQ(i, q, name) EMIT_IRQ name, i +%define IRQ(i, q, name) EMIT_IRQ name, i + +%define NISR(i, s, name) ; We don't emit a handler for NMI section .isrs %include "interrupt_isrs.inc" diff --git a/src/kernel/io.h b/src/kernel/io.h index ebfe6fe..0291180 100644 --- a/src/kernel/io.h +++ b/src/kernel/io.h @@ -20,4 +20,4 @@ void io_wait(unsigned times = 1); } -const uint16_t COM1 = 0x03f8; +constexpr uint16_t COM1 = 0x03f8; diff --git a/src/kernel/kernel.ld b/src/kernel/kernel.ld index 025ac17..3c4807d 100755 --- a/src/kernel/kernel.ld +++ b/src/kernel/kernel.ld @@ -1,4 +1,4 @@ -ENTRY(_kernel_start) +ENTRY(_kernel_start.real) SECTIONS { . = 0xFFFF800000000000; diff --git a/src/kernel/main.cpp b/src/kernel/main.cpp index 92bbcbb..2e0c291 100644 --- a/src/kernel/main.cpp +++ b/src/kernel/main.cpp @@ -24,7 +24,6 @@ #include "objects/vm_area.h" #include "scheduler.h" #include "serial.h" -#include "symbol_table.h" #include "syscall.h" #include "tss.h" #include "vm_space.h" @@ -43,8 +42,6 @@ extern "C" { void init_ap_trampoline(void*, cpu_data *, void (*)()); } -extern void __kernel_assert(const char *, unsigned, const char *); - using namespace kernel; volatile size_t ap_startup_count; @@ -82,7 +79,10 @@ run_constructors() void kernel_main(init::args *args) { - kutil::assert_set_callback(__kernel_assert); + if (args->panic) { + IDT::set_nmi_handler(args->panic->entrypoint); + kutil::assert::symbol_table = args->symbol_table | memory::page_offset; + } init_console(); logger_init(); @@ -128,10 +128,6 @@ kernel_main(init::args *args) cpu->tss->create_ist_stacks(cpu->idt->used_ist_entries()); - if (args->symbol_table.count) { - new symbol_table {args->symbol_table.pointer, args->symbol_table.count}; - } - syscall_initialize(); device_manager &devices = device_manager::get(); diff --git a/src/kernel/module.toml b/src/kernel/module.toml index a944b51..6a67163 100644 --- a/src/kernel/module.toml +++ b/src/kernel/module.toml @@ -7,7 +7,6 @@ includes = ["src/kernel"] sources = [ "apic.cpp", "ap_startup.s", - "assert.cpp", "boot.s", "clock.cpp", "console.cpp", @@ -40,7 +39,6 @@ sources = [ "pci.cpp", "scheduler.cpp", "serial.cpp", - "symbol_table.cpp", "syscall.cpp", "syscall.s", "syscalls/channel.cpp", @@ -54,3 +52,6 @@ sources = [ "tss.cpp", "vm_space.cpp", ] + +[variables] +ldflags = ["${ldflags}", "-T", "${source_root}/src/kernel/kernel.ld"] diff --git a/src/kernel/panic.serial/display.cpp b/src/kernel/panic.serial/display.cpp new file mode 100644 index 0000000..c6dc057 --- /dev/null +++ b/src/kernel/panic.serial/display.cpp @@ -0,0 +1,87 @@ +#include "cpu.h" +#include "display.h" +#include "kutil/printf.h" +#include "serial.h" +#include "symbol_table.h" + +namespace panic { + +void +print_header(serial_port &out, const char *message) +{ + out.write("\n\n \e[5;31mPANIC:\e[0;1;31m "); + out.write(message); + out.write("\n \e[0;31m=====================================================================\n"); +} + +void +print_callstack(serial_port &out, symbol_table &syms, frame const *fp) +{ + char message[512]; + unsigned count = 0; + + while (fp && fp->return_addr) { + char const *name = syms.find_symbol(fp->return_addr); + if (!name) + name = ""; + + snprintf(message, sizeof(message), + " \e[0;33mframe %2d: <0x%016lx> \e[1;33m%s\n", + count++, fp->return_addr, name); + + out.write(message); + fp = fp->prev; + } +} + +static void +print_reg(serial_port &out, const char *name, uint64_t val, const char *color) +{ + char message[512]; + + snprintf(message, sizeof(message), " \e[0;%sm%-3s %016llx\e[0m", color, name, val); + out.write(message); +} + +void +print_cpu_state(serial_port &out, const cpu_state ®s) +{ + out.write("\e[0m\n"); + + // Row 1 + print_reg(out, "rsp", regs.rsp, "1;34"); + print_reg(out, "rax", regs.rax, "0;37"); + print_reg(out, "r8", regs.r8, "0;37"); + print_reg(out, "r9", regs.r9, "0;37"); + out.write("\n"); + + // Row 2 + print_reg(out, "rbp", regs.rbp, "1;34"); + print_reg(out, "rbx", regs.rbx, "0;37"); + print_reg(out, "r10", regs.r10, "0;37"); + print_reg(out, "r11", regs.r11, "0;37"); + out.write("\n"); + + // Row 3 + print_reg(out, "rdi", regs.rdi, "1;34"); + print_reg(out, "rcx", regs.rcx, "0;37"); + print_reg(out, "r12", regs.r12, "0;37"); + print_reg(out, "r13", regs.r13, "0;37"); + out.write("\n"); + + // Row 4 + print_reg(out, "rsi", regs.rdi, "1;34"); + print_reg(out, "rdx", regs.rcx, "0;37"); + print_reg(out, "r14", regs.r12, "0;37"); + print_reg(out, "r15", regs.r13, "0;37"); + out.write("\n"); + + // Row 4 + print_reg(out, "rip", regs.rip, "1;35"); + 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"); +} + +} // namespace panic diff --git a/src/kernel/panic.serial/display.h b/src/kernel/panic.serial/display.h new file mode 100644 index 0000000..fe2f73c --- /dev/null +++ b/src/kernel/panic.serial/display.h @@ -0,0 +1,24 @@ +#pragma once +/// \file display.h +/// Panic info display functions + +#include + +struct cpu_state; + +namespace panic { + +class serial_port; +class symbol_table; + +struct frame +{ + frame *prev; + uintptr_t return_addr; +}; + +void print_header(serial_port &out, const char *message); +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 new file mode 100644 index 0000000..c4bdb6d --- /dev/null +++ b/src/kernel/panic.serial/entry.s @@ -0,0 +1,18 @@ +%include "push_all.inc" + +section .text + +extern panic_handler + +global _panic_entry +_panic_entry: + cli + push 0 ; NMI doesn't push an error code + push 2 ; NMI is int 2 + push_all + mov rdx, rsp + + mov rax, [rsp + REGS.rip] + push rax + + jmp panic_handler diff --git a/src/kernel/panic.serial/main.cpp b/src/kernel/panic.serial/main.cpp new file mode 100644 index 0000000..6a85b24 --- /dev/null +++ b/src/kernel/panic.serial/main.cpp @@ -0,0 +1,27 @@ +#include "display.h" +#include "serial.h" +#include "symbol_table.h" + +struct cpu_state; + +extern "C" +void panic_handler( + const char *message, + const void *symbol_data, + const cpu_state *regs) +{ + panic::serial_port com1(panic::COM1); + panic::symbol_table syms(symbol_data); + + panic::frame const *fp = nullptr; + asm ( "mov %%rbp, %0" : "=r" (fp) ); + + // Skip the panic handler itself + if (fp) fp = fp->prev; + + print_header(com1, message); + print_callstack(com1, syms, fp); + print_cpu_state(com1, *regs); + + while (1) asm ("hlt"); +} diff --git a/src/kernel/panic.serial/module.toml b/src/kernel/panic.serial/module.toml new file mode 100644 index 0000000..6aa6fff --- /dev/null +++ b/src/kernel/panic.serial/module.toml @@ -0,0 +1,16 @@ +name = "panic.serial" +kind = "exe" +output = "panic.serial.elf" +targets = ["kernel"] +deps = ["kutil", "elf"] +includes = ["src/kernel/panic.serial", "src/kernel"] +sources = [ + "display.cpp", + "entry.s", + "main.cpp", + "serial.cpp", + "symbol_table.cpp", +] + +[variables] +ldflags = ["${ldflags}", "-T", "${source_root}/src/kernel/panic.serial/panic.serial.ld"] diff --git a/src/kernel/panic.serial/panic.serial.ld b/src/kernel/panic.serial/panic.serial.ld new file mode 100644 index 0000000..0da39aa --- /dev/null +++ b/src/kernel/panic.serial/panic.serial.ld @@ -0,0 +1,20 @@ +ENTRY(_panic_entry) +SECTIONS +{ + . = 0xFFFF800080000000; + + .text ALIGN(4096) : { + *(.text*) + } + + .data ALIGN(4096) : { + *(.data*) + *(.rodata*) + } + + .bss ALIGN(4096) : { + __bss_start = .; + *(.bss*) + __bss_end = .; + } +} diff --git a/src/kernel/panic.serial/serial.cpp b/src/kernel/panic.serial/serial.cpp new file mode 100644 index 0000000..ed575d3 --- /dev/null +++ b/src/kernel/panic.serial/serial.cpp @@ -0,0 +1,55 @@ +#include "serial.h" + +namespace panic { + +// register offsets +constexpr uint16_t THR = 0; // Write +constexpr uint16_t RBR = 0; // Read +constexpr uint16_t IER = 1; +constexpr uint16_t FCR = 2; // Write +constexpr uint16_t IIR = 2; // Read +constexpr uint16_t LCR = 3; +constexpr uint16_t MCR = 4; +constexpr uint16_t LSR = 5; +constexpr uint16_t MSR = 6; + +constexpr uint16_t DLL = 0; // DLAB == 1 +constexpr uint16_t DLH = 1; // DLAB == 1 + +inline void outb(uint16_t port, uint8_t val) { + asm ( "outb %0, %1" :: "a"(val), "Nd"(port) ); +} + +inline uint8_t inb(uint16_t port) { + uint8_t val; + asm ( "inb %1, %0" : "=a"(val) : "Nd"(port) ); + return val; +} + + +serial_port::serial_port(uint16_t port) : + m_port(port) +{ + outb(port + IER, 0x00); // Disable all interrupts + outb(port + LCR, 0x80); // Enable the Divisor Latch Access Bit + outb(port + DLL, 0x01); // Divisor low byte: 1 (115200 baud) + outb(port + DLH, 0x00); // Divisor high byte + outb(port + LCR, 0x03); // 8-N-1, diable DLAB + outb(port + FCR, 0xe7); // Clear and enable FIFO, enable 64 byte, 56 byte trigger + outb(port + MCR, 0x0b); // Data terminal ready, Request to send, aux output 2 (irq enable) +} + +inline bool read_ready(uint16_t port) { return (inb(port + LSR) & 0x01) != 0; } +inline bool write_ready(uint16_t port) { return (inb(port + LSR) & 0x20) != 0; } + +void +serial_port::write(const char *s) +{ + char const *p = s; + while (p && *p) { + while (!write_ready(m_port)) outb(0x80, 0); + outb(m_port, *p++); + } +} + +} // namespace panic diff --git a/src/kernel/panic.serial/serial.h b/src/kernel/panic.serial/serial.h new file mode 100644 index 0000000..653b579 --- /dev/null +++ b/src/kernel/panic.serial/serial.h @@ -0,0 +1,23 @@ +#pragma once +/// \file panic_serial.h +/// Non-interrupt-driven serial 'driver' for panic handling +#include + +namespace panic { + +class serial_port +{ +public: + /// Constructor. + /// \arg port The IO address of the serial port + serial_port(uint16_t port); + + void write(const char *s); + +private: + uint16_t m_port; +}; + +constexpr uint16_t COM1 = 0x03f8; + +} // namespace panic diff --git a/src/kernel/panic.serial/symbol_table.cpp b/src/kernel/panic.serial/symbol_table.cpp new file mode 100644 index 0000000..00d0977 --- /dev/null +++ b/src/kernel/panic.serial/symbol_table.cpp @@ -0,0 +1,28 @@ +#include "pointer_manipulation.h" +#include "symbol_table.h" + +namespace panic { + +symbol_table::symbol_table(const void *data) : + m_data(data) +{ + const size_t *count = reinterpret_cast(data); + m_entries = { + .pointer = reinterpret_cast(count+1), + .count = *count, + }; +} + +const char * +symbol_table::find_symbol(uintptr_t addr) const +{ + // TODO: binary search + for (auto &e : m_entries) { + if (addr >= e.address && addr < e.address + e.size) + return reinterpret_cast(m_data) + e.name; + } + + return nullptr; +} + +} // namespace panic diff --git a/src/kernel/panic.serial/symbol_table.h b/src/kernel/panic.serial/symbol_table.h new file mode 100644 index 0000000..3abc66f --- /dev/null +++ b/src/kernel/panic.serial/symbol_table.h @@ -0,0 +1,32 @@ +#pragma once + +#include +#include "counted.h" + +namespace panic { + +class symbol_table +{ +public: + /// Constructor. + /// \arg data Pointer to the start of the symbol_table data. + symbol_table(const void *data); + + /// Find the name of the symbol at the given address + /// \args addr Address to search for + /// \returns Name of the symbol if found, or null + const char * find_symbol(uintptr_t addr) const; + +private: + struct entry + { + uintptr_t address; + size_t size; + uintptr_t name; + }; + + const void *m_data; + counted m_entries; +}; + +} // namespace panic diff --git a/src/kernel/symbol_table.cpp b/src/kernel/symbol_table.cpp deleted file mode 100644 index 1e927cd..0000000 --- a/src/kernel/symbol_table.cpp +++ /dev/null @@ -1,49 +0,0 @@ -#include "kutil/assert.h" -#include "kutil/memory.h" - -#include "log.h" -#include "symbol_table.h" - -symbol_table * symbol_table::s_instance = nullptr; - -symbol_table::symbol_table(const void *data, size_t size) -{ - m_data = kutil::kalloc(size); - kassert(m_data, "Failed to allocate for symbol table"); - kutil::memcpy(m_data, data, size); - - uint64_t *values = reinterpret_cast(m_data); - m_entries = *values++; - m_index = reinterpret_cast(values); - - for (size_t i = 0; i < m_entries; ++i) { - uint64_t offset = reinterpret_cast(m_index[i].name); - m_index[i].name = reinterpret_cast(m_data) + offset; - } - - if (!s_instance) - s_instance = this; - - log::info(logs::boot, "Loaded %d symbol table entries at %llx", m_entries, m_data); -} - -symbol_table::~symbol_table() -{ - kutil::kfree(m_data); -} - -const symbol_table::entry * -symbol_table::find_symbol(uintptr_t addr) const -{ - if (!m_entries) - return nullptr; - - // TODO: binary search - for (size_t i = 0; i < m_entries; ++i) { - if (m_index[i].address > addr) { - return i ? &m_index[i - 1] : nullptr; - } - } - - return &m_index[m_entries - 1]; -} diff --git a/src/kernel/symbol_table.h b/src/kernel/symbol_table.h deleted file mode 100644 index 222fb1a..0000000 --- a/src/kernel/symbol_table.h +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once - -#include - -class symbol_table -{ -public: - struct entry - { - uintptr_t address; - char *name; - }; - - static symbol_table * get() { return s_instance; } - - /// Constructor. - /// \arg data Pointer to the start of the symbol_table data. - /// \arg size Size of the data, in bytes - symbol_table(const void *data, size_t size); - - ~symbol_table(); - - /// Find the closest symbol address before the given address - /// \args addr Address to search for - /// \returns Actual address of the symbol - const entry * find_symbol(uintptr_t addr) const; - -private: - size_t m_entries; - entry *m_index; - void *m_data; - - static symbol_table *s_instance; -}; diff --git a/src/libraries/kutil/assert.cpp b/src/libraries/kutil/assert.cpp index ed53467..2e6a890 100644 --- a/src/libraries/kutil/assert.cpp +++ b/src/libraries/kutil/assert.cpp @@ -1,16 +1,10 @@ #include "kutil/assert.h" namespace kutil { +namespace assert { -assert_callback __kernel_assert_p = nullptr; - -assert_callback -assert_set_callback(assert_callback f) -{ - assert_callback old = __kernel_assert_p; - __kernel_assert_p = f; - return old; -} - +uint32_t *apic_icr = reinterpret_cast(0xffffc000fee00300); +uintptr_t symbol_table = 0; +} // namespace assert } // namespace kutil diff --git a/src/libraries/kutil/include/kutil/assert.h b/src/libraries/kutil/include/kutil/assert.h index a70f665..b1cd5be 100644 --- a/src/libraries/kutil/include/kutil/assert.h +++ b/src/libraries/kutil/include/kutil/assert.h @@ -1,18 +1,29 @@ #pragma once +#include + namespace kutil { +namespace assert { -using assert_callback = - void (*) (const char *file, unsigned line, const char *message); +constexpr uint32_t send_nmi_command = + (4 << 8) | // Delivery mode NMI + (1 << 14) | // assert level high + (1 << 18); // destination self -/// Set the global kernel assert callback -/// \args f The new callback -/// \returns The old callback -assert_callback assert_set_callback(assert_callback f); - -extern assert_callback __kernel_assert_p; +extern uint32_t *apic_icr; +extern uintptr_t symbol_table; +} // namespace assert } // namespace kutil - -#define kassert(stmt, message) do { if(!(stmt)) { ::kutil::__kernel_assert_p(__FILE__, __LINE__, (message)); }} while(0); +#define kassert(stmt, message) \ + do { \ + if(!(stmt)) { \ + register const char *m asm("rdi"); \ + register uintptr_t i asm("rsi"); \ + asm volatile ("mov %1, %0" : "=r"(m) : "r"(message)); \ + asm volatile ("mov %1, %0" : "=r"(i) : "r"(kutil::assert::symbol_table)); \ + *kutil::assert::apic_icr = kutil::assert::send_nmi_command; \ + while (1) __asm__ __volatile__ ("hlt"); \ + } \ + } while(0);