[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:
Justin C. Miller
2021-08-01 14:03:10 -07:00
parent fce22b0d35
commit ea9d20a250
37 changed files with 462 additions and 225 deletions

View File

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

View File

@@ -80,7 +80,6 @@ cxxflags = [
ldflags = [
"${ldflags}",
"-T", "${source_root}/src/kernel/kernel.ld",
"-g",
"-nostdlib",
"-Bstatic",

View File

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

View File

@@ -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 &section : 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);

View File

@@ -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 &section : program.sections)
paging::map_section(args, section);
}
} // namespace paging
} // namespace boot

View File

@@ -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 &section);
kernel::init::program &program);
} // namespace paging
} // namespace boot

View File

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

View File

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

View File

@@ -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" );
}

View File

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

View File

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

View File

@@ -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 &regs)
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 &regs)
{
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);

View File

@@ -21,7 +21,6 @@ extern size_t __counter_syscall_sysret;
void print_regs(const cpu_state &regs);
void print_stack(const cpu_state &regs);
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));

View File

@@ -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;
}

View File

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

View File

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

View File

@@ -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();
}

View File

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

View File

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

View File

@@ -20,4 +20,4 @@ void io_wait(unsigned times = 1);
}
const uint16_t COM1 = 0x03f8;
constexpr uint16_t COM1 = 0x03f8;

View File

@@ -1,4 +1,4 @@
ENTRY(_kernel_start)
ENTRY(_kernel_start.real)
SECTIONS
{
. = 0xFFFF800000000000;

View File

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

View File

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

View 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 &regs)
{
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

View 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 &regs);
}

View 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

View 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");
}

View 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"]

View 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 = .;
}
}

View 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

View 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

View 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

View 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

View File

@@ -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];
}

View File

@@ -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;
};

View File

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

View File

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