Built device interrupts

This commit is contained in:
2026-07-03 12:57:40 +01:00
parent c8e89e8115
commit 5ea521d054
12 changed files with 367 additions and 23 deletions
+36 -11
View File
@@ -1,13 +1,30 @@
//! Interrupt Descriptor Table and the CPU-exception handlers. Without this, any
//! fault (a stray pointer, a bad page-table entry) triple-faults and silently
//! resets the machine. With it, the CPU vectors into our stubs, which capture the
//! register state and hand it to a reporter that prints what went wrong.
//! Interrupt Descriptor Table, CPU-exception handlers, and device-interrupt
//! dispatch. Without this, any fault (a stray pointer, a bad page-table entry)
//! triple-faults and silently resets the machine. With it, the CPU vectors into
//! our stubs, which capture the register state and hand it to a dispatcher.
//!
//! Only the 32 architecture-defined exception vectors are wired up here; device
//! interrupts (the APIC, timer, keyboard) come later.
//! Vectors split in two: 0-31 are CPU exceptions (terminal — reported and
//! halted); 32+ are device interrupts (a registered handler runs, the APIC is
//! acknowledged, and we return to the interrupted code).
const gdt = @import("gdt.zig");
const tss = @import("tss.zig");
const apic = @import("apic.zig");
/// Highest vector we install a gate/stub for (exceptions 0-31 plus the device
/// range 32-47, which covers the timer and the spurious vector).
const gate_count = 48;
/// A device-interrupt handler. It doesn't get the trap frame (a timer or keyboard
/// handler doesn't need the interrupted registers); add that if one ever does.
pub const Handler = *const fn () void;
var handlers = [_]?Handler{null} ** 256;
/// Register `handler` for a device-interrupt `vector` (>= 32).
pub fn setHandler(vector: usize, handler: Handler) void {
handlers[vector] = handler;
}
/// The register + trap frame the ISR stubs build on the stack, laid out so the
/// lowest address (where RSP points when we call the handler) is the first field.
@@ -101,9 +118,10 @@ fn setGate(vector: usize, handler: u64) void {
};
}
/// Point the first 32 vectors at the stubs defined in isr.s and load the IDT.
/// Point every installed vector at its stub (isr.s) and load the IDT.
pub fn init() void {
inline for (0..32) |vector| {
@setEvalBranchQuota(20000); // comptimePrint across all the gates adds up
inline for (0..gate_count) |vector| {
const stub = @extern(*const anyopaque, .{ .name = std.fmt.comptimePrint("isr{d}", .{vector}) });
setGate(vector, @intFromPtr(stub));
}
@@ -118,9 +136,16 @@ pub fn init() void {
}
/// Called by isr_common (isr.s) with a pointer to the trap frame. Exported so the
/// assembly stubs can `call` it by name.
export fn exceptionHandler(state: *const CpuState) callconv(.c) void {
on_fault(state);
/// assembly stubs can `call` it by name. Exceptions are terminal; device
/// interrupts run their handler, get acknowledged, and return.
export fn interruptDispatch(state: *const CpuState) callconv(.c) void {
if (state.vector < 32) {
on_fault(state); // CPU exception — never returns
} else if (handlers[state.vector]) |handler| {
handler();
apic.eoi();
}
// else: spurious/unhandled device interrupt — don't acknowledge it
}
const std = @import("std");