Interrupt Handling
Interrupt handling ties together three separate hardware/software systems: the IDT, the PIC, and the exception handlers. Each has its own chapter, but this page explains how they fit together.
The Three Pieces
Hardware Event (key press, timer, fault)
│
▼
┌─────────┐
│ PIC │ Routes hardware IRQs to specific interrupt numbers
└────┬────┘ (after remapping: IRQ0 → INT32, IRQ1 → INT33, ...)
│
▼
┌─────────┐
│ IDT │ Table of 256 entries: "when interrupt N fires, call function F"
└────┬────┘
│
▼
┌─────────────────┐
│ Handler Function │ ASM wrapper → Rust function → EOI → iretd
└─────────────────┘
Interrupt Numbers
The x86 CPU has 256 possible interrupt numbers:
| Range | Purpose |
|---|---|
| 0–31 | CPU exceptions (reserved by Intel) |
| 32–255 | Hardware interrupts and software interrupts |
The CPU exceptions (0–31) are fixed — divide by zero always fires interrupt 0, page fault always fires interrupt 14, etc.
Hardware interrupts (from PIC) are configurable. By default the BIOS maps them to 8–15, which conflicts with CPU exceptions. We remap them to 32–47 to avoid this.
After remapping:
| Interrupt | Source |
|---|---|
| 0 | Divide by zero |
| 6 | Invalid opcode |
| 8 | Double fault |
| 13 | General protection fault |
| 14 | Page fault |
| 32 | Timer (IRQ0) |
| 33 | Keyboard (IRQ1) |
| 34–47 | Other IRQs |
Initialisation Order
The order in kernel_main matters:
#![allow(unused)]
fn main() {
idt::init(); // Set up the IDT with all handlers
pic::remap(); // Remap PIC IRQs to 32–47
idt::enable_interrupts(); // Run 'sti' — interrupts now live
}
Why this order?
- IDT must be populated before enabling interrupts. If an interrupt fires with an empty IDT, the CPU triple-faults.
- PIC must be remapped before enabling interrupts. If the timer fires at interrupt 8 (default) and we have a double fault handler there, chaos ensues.
stiis last — only turn interrupts on when everything is ready.
Default Handler
Every IDT slot that isn’t explicitly handled gets a default handler:
#![allow(unused)]
fn main() {
for i in 1..256 {
if i != 0 && i != 6 && i != 8 && i != 13 && i != 14 && i != 33 {
IDT.entries[i].set_handler(default_interrupt_handler);
}
}
}
The default handler displays “UNHANDLED INTERRUPT!” in red and halts. Without this, any unexpected interrupt (like the timer) causes a triple fault and reboots the machine. This was the root cause of the reboot bug — see the Debugging: The Reboot Problem chapter.