hachem.wtf
aboutsummaryrefslogtreecommitdiff
path: root/boot/idt.zig
diff options
context:
space:
mode:
authorhachem <im@hachem.wtf>2026-09-10 08:23:52 +0200
committerhachem <im@hachem.wtf>2026-09-10 08:23:52 +0200
commit8fc9aa8d84486c561776cda8f94e4dfa897a5d4d (patch)
tree76d7fc5df77b80a1387e5c5fee88d5fda8315bcc /boot/idt.zig
parentba4714f311e58b59043797ebd5c17caeccc74050 (diff)
feat: gdt/idt initialization
Diffstat (limited to 'boot/idt.zig')
-rw-r--r--boot/idt.zig167
1 files changed, 167 insertions, 0 deletions
diff --git a/boot/idt.zig b/boot/idt.zig
new file mode 100644
index 0000000..adff4b3
--- /dev/null
+++ b/boot/idt.zig
@@ -0,0 +1,167 @@
+// the idt is for the 32 cpu excpetion vectors, if i dont implement this,
+// any fault triple-falts and the machine just fucking dies for some reason
+// this just maps interupts and halts ig
+//
+// see: https://wiki.osdev.org/Interrupt_Descriptor_Table
+
+const gdt = @import("gdt.zig");
+const serial = @import("serial.zig");
+
+// 64bit interrupt gate
+const Gate = packed struct {
+ offset_low: u16,
+ selector: u16,
+ ist: u8,
+ type_attr: u8, // 0x8E = present, ring 0, 64-bit interrupt gate
+ offset_mid: u16,
+ offset_high: u32,
+ reserved: u32,
+};
+
+const Idtr = packed struct {
+ limit: u16,
+ base: u64,
+};
+
+var idt = [_]Gate{@bitCast(@as(u128, 0))} ** 256;
+var idtr: Idtr = undefined;
+
+// what the common stub leaves on the stack, low address -> high.
+// `mov rdi, rsp` hands the handler a pointer to this
+const Frame = extern struct {
+ r15: u64,
+ r14: u64,
+ r13: u64,
+ r12: u64,
+ r11: u64,
+ r10: u64,
+ r9: u64,
+ r8: u64,
+ rbp: u64,
+ rdi: u64,
+ rsi: u64,
+ rdx: u64,
+ rcx: u64,
+ rbx: u64,
+ rax: u64,
+ vector: u64,
+ error_code: u64,
+ // pushed by the CPU on the exception:
+ rip: u64,
+ cs: u64,
+ rflags: u64,
+ rsp: u64,
+ ss: u64,
+};
+
+const names = [_][]const u8{
+ "divide by zero", "debug",
+ "non-maskable interrupt", "breakpoint",
+ "overflow", "bound range exceeded",
+ "invalid opcode", "device not available",
+ "double fault", "coprocessor segment overrun",
+ "invalid TSS", "segment not present",
+ "stack-segment fault", "general protection fault",
+ "page fault", "reserved",
+ "x87 floating-point", "alignment check",
+ "machine check", "SIMD floating-point",
+ "virtualization", "control protection",
+};
+
+fn name(vector: u64) []const u8 {
+ return if (vector < names.len) names[vector] else "unknown";
+}
+
+// only these vectors push a real error code, the rest the stub pushes a 0
+// so the frame layout is uniform
+fn hasErrorCode(comptime vector: u8) bool {
+ return switch (vector) {
+ 8, 10, 11, 12, 13, 14, 17, 21 => true,
+ else => false,
+ };
+}
+
+// one naked (kinky) entry stub per vector, so normalise the stack (dummy error code if
+// needed), push the vector number, jump to the shared save and dispatch path
+fn stub(comptime vector: u8) fn () callconv(.naked) void {
+ return struct {
+ fn entry() callconv(.naked) void {
+ if (comptime !hasErrorCode(vector)) asm volatile ("pushq $0");
+ asm volatile ("pushq %[v]\n jmp isrCommon"
+ :
+ : [v] "i" (@as(u32, vector)),
+ );
+ }
+ }.entry;
+}
+
+// Save the GP registers into a Frame, hand its address to the Zig handler
+export fn isrCommon() callconv(.naked) void {
+ asm volatile (
+ \\ push %rax
+ \\ push %rbx
+ \\ push %rcx
+ \\ push %rdx
+ \\ push %rsi
+ \\ push %rdi
+ \\ push %rbp
+ \\ push %r8
+ \\ push %r9
+ \\ push %r10
+ \\ push %r11
+ \\ push %r12
+ \\ push %r13
+ \\ push %r14
+ \\ push %r15
+ \\ mov %rsp, %rdi
+ \\ call exceptionHandler
+ );
+}
+
+fn writeHex(value: u64) void {
+ const digits = "0123456789abcdef";
+ var buf: [18]u8 = undefined;
+ buf[0] = '0';
+ buf[1] = 'x';
+ var i: usize = 0;
+ while (i < 16) : (i += 1) {
+ const shift: u6 = @intCast((15 - i) * 4);
+ buf[2 + i] = digits[(value >> shift) & 0xF];
+ }
+ serial.write(&buf);
+}
+
+export fn exceptionHandler(frame: *Frame) callconv(.c) noreturn {
+ serial.write("\r\n!!! CPU DID AN OOPSIE: ");
+ serial.write(name(frame.vector));
+ serial.write("\r\n vector="); writeHex(frame.vector);
+ serial.write(" err="); writeHex(frame.error_code);
+ serial.write("\r\n rip="); writeHex(frame.rip);
+ serial.write(" cs="); writeHex(frame.cs);
+ serial.write("\r\n rflags="); writeHex(frame.rflags);
+ serial.write(" rsp="); writeHex(frame.rsp);
+ serial.write("\r\n");
+ while (true) asm volatile ("hlt");
+}
+
+fn setGate(vector: u8, handler: u64) void {
+ idt[vector] = .{
+ .offset_low = @truncate(handler),
+ .selector = gdt.KERNEL_CODE,
+ .ist = 0,
+ .type_attr = 0x8E,
+ .offset_mid = @truncate(handler >> 16),
+ .offset_high = @truncate(handler >> 32),
+ .reserved = 0,
+ };
+}
+
+// fill the exception vectors and load the IDT
+pub fn init() void {
+ inline for (0..32) |v| setGate(v, @intFromPtr(&stub(v)));
+ idtr = .{ .limit = @sizeOf(@TypeOf(idt)) - 1, .base = @intFromPtr(&idt) };
+ asm volatile ("lidt (%[idtr])"
+ :
+ : [idtr] "r" (&idtr),
+ : .{ .memory = true });
+}