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);