From 7dfdbe10ec1ac5bcce7f7a1ffda063f8abcbdb1e Mon Sep 17 00:00:00 2001 From: ktkk Date: Tue, 12 May 2026 14:43:14 +0000 Subject: [PATCH 1/2] Add the IDT table --- src/idt.zig | 142 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/main.zig | 21 ++++++++ 2 files changed, 163 insertions(+) create mode 100644 src/idt.zig diff --git a/src/idt.zig b/src/idt.zig new file mode 100644 index 0000000..f19dc37 --- /dev/null +++ b/src/idt.zig @@ -0,0 +1,142 @@ +const std = @import("std"); + +pub const InterruptStackFrame = extern struct { + instruction_ptr: u64, + code_segment: u64, + cpu_flags: u64, + stack_ptr: u64, + stack_segment: u64, +}; + +const HandlerFunction = *const fn (_: *InterruptStackFrame) callconv(.{ .x86_interrupt = .{} }) void; + +fn HandlerFunctionWithErrorCode(comptime Return: type) type { + if (Return != void and Return != noreturn) { + @compileError("Return should either be void or noreturn"); + } + + return *const fn (_: *InterruptStackFrame, error_code: u32) callconv(.{ .x86_interrupt = .{} }) Return; +} + +pub const InterruptDescriptorTableEntryType = enum(u8) { + division_error = 0, + debug = 1, + non_maskable_interrupt = 2, + breakpoint = 3, + overflow = 4, + bound_range_exceeded = 5, + invalid_opcode = 6, + device_not_available = 7, + double_fault = 8, + coprocessor_segment_overrun = 9, + invalid_tss = 10, + segment_not_present = 11, + stack_segment_fault = 12, + general_protection_fault = 13, + page_fault = 14, + x87_floating_point_exception = 16, + alignment_check = 17, + machine_check = 18, + simd_floating_point = 19, + virtualization = 20, + control_protection_exception = 21, + hypervisor_injection_exception = 28, + vmm_communication_exception = 29, + security_exception = 30, + + const Self = @This(); + + pub fn HandlerFunctionType(self: Self) type { + return switch (self) { + .double_fault, .machine_check => HandlerFunctionWithErrorCode(noreturn), + .invalid_tss, .segment_not_present, .stack_segment_fault, .general_protection_fault, .page_fault, .alignment_check, .control_protection_exception, .vmm_communication_exception, .security_exception => HandlerFunctionWithErrorCode(void), + else => HandlerFunction, + }; + } +}; + +pub const InterruptDescriptorTable = extern struct { + table: [Length]Entry, + + const Length = std.math.maxInt(u8) + 1; + + const Entry = packed struct { + ptr_low: u16, + options: Options, + ptr_mid: u16, + ptr_high: u32, + reserved: u32, + + pub const missing: Entry = .{ + .ptr_low = 0, + .options = .minimal, + .ptr_mid = 0, + .ptr_high = 0, + .reserved = 0, + }; + + const Options = packed struct { + code_segment: u16, + bit_set: std.bit_set.IntegerBitSet(16), + + pub const minimal: Options = .{ + .code_segment = 0, + .bit_set = .{ .mask = 0b1110_0000_0000 }, + }; + + pub fn setCodeSelector(self: *align(2) Options, segment: u16) void { + self.code_segment = segment; + } + + pub fn setPresent(self: *align(2) Options, present: bool) void { + self.bit_set.setValue(15, present); + } + }; + + fn setHandler(self: *Entry, handler_addr: u64) void { + self.ptr_low = @truncate(handler_addr); + self.ptr_mid = @truncate(handler_addr >> 16); + self.ptr_high = @truncate(handler_addr >> 32); + + self.options = .minimal; + const segment = asm volatile ( + \\ mov %%cs, %[segment] + : [segment] "=&r" (-> u16) + : + ); + self.options.setCodeSelector(segment); + self.options.setPresent(true); + + std.log.info("Setting handler {any}\n", .{ self.* }); + } + }; + + pub const empty: InterruptDescriptorTable = .{ + .table = [_]Entry{.missing} ** Length, + }; + + pub fn setEntry( + self: *InterruptDescriptorTable, + comptime entry_type: InterruptDescriptorTableEntryType, + handler: InterruptDescriptorTableEntryType.HandlerFunctionType(entry_type), + ) void { + self.table[@intFromEnum(entry_type)].setHandler(@intFromPtr(handler)); + } + + pub fn load(self: *InterruptDescriptorTable) void { + const DescriptorTablePointer = packed struct { + limit: u16, + base: u64, + }; + const idt: DescriptorTablePointer = .{ + .limit = @sizeOf(@TypeOf(self.*)) - 1, + .base = @intFromPtr(self), + }; + + asm volatile ( + \\ lidt (%[idt]) + : + : [idt] "r" (idt), + ); + } +}; diff --git a/src/main.zig b/src/main.zig index dd4f020..256ff39 100644 --- a/src/main.zig +++ b/src/main.zig @@ -1,7 +1,11 @@ const lazy = @import("utils/Lazy.zig").lazy; const Console = @import("Console.zig"); + const MultibootInfo = @import("multiboot.zig").Info; +const InterruptDescriptorTable = @import("idt.zig").InterruptDescriptorTable; +const InterruptStackFrame = @import("idt.zig").InterruptStackFrame; + const MultibootHeader = packed struct { magic: i32, flags: i32, @@ -37,6 +41,8 @@ export fn _start() callconv(.naked) noreturn { var console = lazy(Console, .init); +var idt: InterruptDescriptorTable = .empty; + fn kmain(magic: u32, info: *const MultibootInfo) callconv(.c) void { std.debug.assert(magic == MAGIC); @@ -49,9 +55,24 @@ fn kmain(magic: u32, info: *const MultibootInfo) callconv(.c) void { std.log.info("Booted with cmdline \"{s}\"", .{info.cmdline}); } + idt.setEntry(.breakpoint, breakpointHandler); + idt.load(); + + //asm volatile ( + // \\ int3 + // : + // : + //); + hang(); } +fn breakpointHandler(stack_frame: *InterruptStackFrame) callconv(.{ .x86_interrupt = .{} }) void { + _ = stack_frame; + + std.log.err("breakpoint\n", .{}); +} + fn hang() noreturn { while (true) asm volatile ("hlt"); } From f8abe365e97eaa262e5591b999a40333c34c2ea9 Mon Sep 17 00:00:00 2001 From: ktkk Date: Thu, 8 Oct 2026 13:40:53 +0000 Subject: [PATCH 2/2] Fix IDT entries to be 32bit --- src/idt.zig | 65 +++++++++++++++++++--------------------------------- src/main.zig | 6 ++--- 2 files changed, 27 insertions(+), 44 deletions(-) diff --git a/src/idt.zig b/src/idt.zig index f19dc37..b5d2b50 100644 --- a/src/idt.zig +++ b/src/idt.zig @@ -1,11 +1,11 @@ const std = @import("std"); pub const InterruptStackFrame = extern struct { - instruction_ptr: u64, - code_segment: u64, - cpu_flags: u64, - stack_ptr: u64, - stack_segment: u64, + instruction_ptr: u32, + code_segment: u32, + cpu_flags: u32, + stack_ptr: u32, + stack_segment: u32, }; const HandlerFunction = *const fn (_: *InterruptStackFrame) callconv(.{ .x86_interrupt = .{} }) void; @@ -48,7 +48,7 @@ pub const InterruptDescriptorTableEntryType = enum(u8) { pub fn HandlerFunctionType(self: Self) type { return switch (self) { - .double_fault, .machine_check => HandlerFunctionWithErrorCode(noreturn), + .double_fault => HandlerFunctionWithErrorCode(noreturn), .invalid_tss, .segment_not_present, .stack_segment_fault, .general_protection_fault, .page_fault, .alignment_check, .control_protection_exception, .vmm_communication_exception, .security_exception => HandlerFunctionWithErrorCode(void), else => HandlerFunction, }; @@ -62,52 +62,33 @@ pub const InterruptDescriptorTable = extern struct { const Entry = packed struct { ptr_low: u16, - options: Options, - ptr_mid: u16, - ptr_high: u32, - reserved: u32, + selector: u16, + reserved: u8 = 0, + attributes: std.bit_set.IntegerBitSet(8) = .initEmpty(), + ptr_high: u16, pub const missing: Entry = .{ .ptr_low = 0, - .options = .minimal, - .ptr_mid = 0, + .selector = 0, + .attributes = .initEmpty(), .ptr_high = 0, - .reserved = 0, }; - const Options = packed struct { - code_segment: u16, - bit_set: std.bit_set.IntegerBitSet(16), - - pub const minimal: Options = .{ - .code_segment = 0, - .bit_set = .{ .mask = 0b1110_0000_0000 }, - }; - - pub fn setCodeSelector(self: *align(2) Options, segment: u16) void { - self.code_segment = segment; - } - - pub fn setPresent(self: *align(2) Options, present: bool) void { - self.bit_set.setValue(15, present); - } - }; - - fn setHandler(self: *Entry, handler_addr: u64) void { + fn setHandler(self: *Entry, handler_addr: u32) void { self.ptr_low = @truncate(handler_addr); - self.ptr_mid = @truncate(handler_addr >> 16); - self.ptr_high = @truncate(handler_addr >> 32); + self.ptr_high = @truncate(handler_addr >> 16); - self.options = .minimal; - const segment = asm volatile ( + const selector = asm volatile ( \\ mov %%cs, %[segment] : [segment] "=&r" (-> u16) : ); - self.options.setCodeSelector(segment); - self.options.setPresent(true); + self.selector = selector; - std.log.info("Setting handler {any}\n", .{ self.* }); + self.attributes.set(1); + self.attributes.set(2); + self.attributes.set(3); + self.attributes.set(7); } }; @@ -126,7 +107,7 @@ pub const InterruptDescriptorTable = extern struct { pub fn load(self: *InterruptDescriptorTable) void { const DescriptorTablePointer = packed struct { limit: u16, - base: u64, + base: u32, }; const idt: DescriptorTablePointer = .{ .limit = @sizeOf(@TypeOf(self.*)) - 1, @@ -136,7 +117,9 @@ pub const InterruptDescriptorTable = extern struct { asm volatile ( \\ lidt (%[idt]) : - : [idt] "r" (idt), + : [idt] "r" (&idt), + : .{ .memory = true } ); } }; + diff --git a/src/main.zig b/src/main.zig index 256ff39..fdeab7f 100644 --- a/src/main.zig +++ b/src/main.zig @@ -64,13 +64,13 @@ fn kmain(magic: u32, info: *const MultibootInfo) callconv(.c) void { // : //); + std.log.info("We did not crash!", .{}); + hang(); } fn breakpointHandler(stack_frame: *InterruptStackFrame) callconv(.{ .x86_interrupt = .{} }) void { - _ = stack_frame; - - std.log.err("breakpoint\n", .{}); + std.log.err("breakpoint\n{}", .{stack_frame.*}); } fn hang() noreturn {