diff options
| -rw-r--r-- | Makefile | 8 | ||||
| -rw-r--r-- | boot/boot.zig | 13 | ||||
| -rw-r--r-- | boot/serial.zig | 50 |
3 files changed, 62 insertions, 9 deletions
@@ -41,13 +41,9 @@ iso: kernel limine/limine iso_root -o $(ISO) ./limine/limine bios-install $(ISO) -# for now this is just going to be a headless run -# all I need this for is to check for kernel panics -# and limine related failures. +# boot it in a real QEMU window. serial goes back later. run: iso - $(QEMU) -M q35 -m 256M -cdrom $(ISO) -boot d \ - -display none -serial stdio -no-reboot \ - -no-shutdown + $(QEMU) -M q35 -m 256M -cdrom $(ISO) -boot d -no-reboot -no-shutdown clean: rm -rf iso_root zig-out .zig-cache $(ISO) diff --git a/boot/boot.zig b/boot/boot.zig index 0f6c92c..a7822af 100644 --- a/boot/boot.zig +++ b/boot/boot.zig @@ -1,12 +1,17 @@ const std = @import("std"); const limine = @import("limine.zig"); +const serial = @import("serial.zig"); // We need a panic handler otherwise zig won't be happy, // std usually formats a message through std.Io.Writer which -// emits SSE instructions which I haven't enabled. For now -// we just halt. +// emits SSE instructions which I haven't enabled. Ours +// dumps the message to serial, then halts. pub const panic = std.debug.FullPanic(struct { - fn halt(_: []const u8, _: ?usize) noreturn { + fn halt(msg: []const u8, _: ?usize) noreturn { + serial.init(); + serial.write("\n!!! uhhhh engine kaput : "); + serial.write(msg); + serial.write("\n"); while (true) asm volatile ("hlt"); } }.halt); @@ -18,5 +23,7 @@ export var requests_end: limine.RequestsEndMarker linksection(".limine_requests_ // ENTRY(_start) export fn _start() callconv(.c) noreturn { + serial.init(); + serial.write("if you see this, it means revel didn't shit the bed"); while (true) asm volatile ("hlt"); } diff --git a/boot/serial.zig b/boot/serial.zig new file mode 100644 index 0000000..cd6bd13 --- /dev/null +++ b/boot/serial.zig @@ -0,0 +1,50 @@ +// This is a minimal 16550 UART driver for COM1, this is +// mainly going to be used for debugging since its really +// simple to setup. Fun fact: i was drunk when i wrote this +// +// see: https://wiki.osdev.org/Serial_Ports + +const COM1: u16 = 0x3F8; + +inline fn outb(port: u16, val: u8) void { + asm volatile ("outb %[val], %[port]" + : + : [val] "{al}" (val), + [port] "N{dx}" (port), + ); +} + +inline fn inb(port: u16) u8 { + return asm volatile ("inb %[port], %[result]" + : [result] "={al}" (-> u8), + : [port] "N{dx}" (port), + ); +} + +// 38400 buard +// 8 data bits +// no parity +// one stop bit +// FIFO on +// Idempotent so it should be relatively safe to call +// from the panic handler. +pub fn init() void { + outb(COM1 + 1, 0x00); // no interrupts + outb(COM1 + 3, 0x80); // DLAB on: the next two writes set the baud divisor + outb(COM1 + 0, 0x03); // divisor 3 -> 38400 baud (low byte) + outb(COM1 + 1, 0x00); // divisor high byte + outb(COM1 + 3, 0x03); // DLAB off, 8N1 + outb(COM1 + 2, 0xC7); // enable + clear FIFOs, 14-byte trigger + outb(COM1 + 4, 0x0B); // RTS/DSR set +} + +// COM1+5 (bit 5) = transmit holding register and +// spin until it's clear to send +fn putc(c: u8) void { + while (inb(COM1 + 5) & 0x20 == 0) {} + outb(COM1, c); +} + +pub fn write(s: []const u8) void { + for (s) |c| putc(c); +} |
