mirror of
https://github.com/justinian/jsix.git
synced 2025-12-09 16:04:32 -08:00
[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
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -80,7 +80,6 @@ cxxflags = [
|
||||
|
||||
ldflags = [
|
||||
"${ldflags}",
|
||||
"-T", "${source_root}/src/kernel/kernel.ld",
|
||||
"-g",
|
||||
"-nostdlib",
|
||||
"-Bstatic",
|
||||
|
||||
@@ -4,37 +4,37 @@
|
||||
# is as follows:
|
||||
#
|
||||
# <num_entires> : 8 bytes
|
||||
# <index> : 16 * N bytes
|
||||
# <index> : 24 * N bytes
|
||||
# <name data> : variable
|
||||
#
|
||||
# Each index entry has the format
|
||||
# <symbol address> : 8 bytes
|
||||
# <symbol size> : 8 bytes
|
||||
# <offset of name> : 8 bytes
|
||||
#
|
||||
# Name offsets are from the start of the symbol table as a whole. (ie,
|
||||
# where <num_entries> 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]} <output>")
|
||||
print(f"Usage: nm -n -S --demangle | {sys.argv[0]} <output>")
|
||||
sys.exit(1)
|
||||
|
||||
outfile = open(sys.argv[1], "wb")
|
||||
|
||||
@@ -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<uintptr_t>(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<init::entrypoint>(kernel.entrypoint);
|
||||
reinterpret_cast<init::entrypoint>(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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -126,9 +126,10 @@ struct args
|
||||
|
||||
program *kernel;
|
||||
program *init;
|
||||
counted<void> symbol_table;
|
||||
program *panic;
|
||||
allocation_register *allocations;
|
||||
uintptr_t modules;
|
||||
uintptr_t symbol_table;
|
||||
|
||||
void *runtime_services;
|
||||
void *acpi_table;
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
/// Helper functions and types for doing type-safe byte-wise pointer math.
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/// 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
|
||||
|
||||
@@ -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" );
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<uint64_t *>(sp));
|
||||
sp += sizeof(uint64_t);
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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<IDT> __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<void(*)()>(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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -20,4 +20,4 @@ void io_wait(unsigned times = 1);
|
||||
|
||||
}
|
||||
|
||||
const uint16_t COM1 = 0x03f8;
|
||||
constexpr uint16_t COM1 = 0x03f8;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
ENTRY(_kernel_start)
|
||||
ENTRY(_kernel_start.real)
|
||||
SECTIONS
|
||||
{
|
||||
. = 0xFFFF800000000000;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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"]
|
||||
|
||||
87
src/kernel/panic.serial/display.cpp
Normal file
87
src/kernel/panic.serial/display.cpp
Normal file
@@ -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 = "<unknown>";
|
||||
|
||||
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
|
||||
24
src/kernel/panic.serial/display.h
Normal file
24
src/kernel/panic.serial/display.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
/// \file display.h
|
||||
/// Panic info display functions
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
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);
|
||||
|
||||
}
|
||||
18
src/kernel/panic.serial/entry.s
Normal file
18
src/kernel/panic.serial/entry.s
Normal file
@@ -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
|
||||
27
src/kernel/panic.serial/main.cpp
Normal file
27
src/kernel/panic.serial/main.cpp
Normal file
@@ -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");
|
||||
}
|
||||
16
src/kernel/panic.serial/module.toml
Normal file
16
src/kernel/panic.serial/module.toml
Normal file
@@ -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"]
|
||||
20
src/kernel/panic.serial/panic.serial.ld
Normal file
20
src/kernel/panic.serial/panic.serial.ld
Normal file
@@ -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 = .;
|
||||
}
|
||||
}
|
||||
55
src/kernel/panic.serial/serial.cpp
Normal file
55
src/kernel/panic.serial/serial.cpp
Normal file
@@ -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
|
||||
23
src/kernel/panic.serial/serial.h
Normal file
23
src/kernel/panic.serial/serial.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
/// \file panic_serial.h
|
||||
/// Non-interrupt-driven serial 'driver' for panic handling
|
||||
#include <stdint.h>
|
||||
|
||||
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
|
||||
28
src/kernel/panic.serial/symbol_table.cpp
Normal file
28
src/kernel/panic.serial/symbol_table.cpp
Normal file
@@ -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<const size_t*>(data);
|
||||
m_entries = {
|
||||
.pointer = reinterpret_cast<entry const*>(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<const char*>(m_data) + e.name;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace panic
|
||||
32
src/kernel/panic.serial/symbol_table.h
Normal file
32
src/kernel/panic.serial/symbol_table.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#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<entry const> m_entries;
|
||||
};
|
||||
|
||||
} // namespace panic
|
||||
@@ -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<uint64_t*>(m_data);
|
||||
m_entries = *values++;
|
||||
m_index = reinterpret_cast<entry*>(values);
|
||||
|
||||
for (size_t i = 0; i < m_entries; ++i) {
|
||||
uint64_t offset = reinterpret_cast<uint64_t>(m_index[i].name);
|
||||
m_index[i].name = reinterpret_cast<char*>(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];
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
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;
|
||||
};
|
||||
@@ -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<uint32_t*>(0xffffc000fee00300);
|
||||
uintptr_t symbol_table = 0;
|
||||
|
||||
} // namespace assert
|
||||
} // namespace kutil
|
||||
|
||||
@@ -1,18 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
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);
|
||||
|
||||
Reference in New Issue
Block a user