Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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:

RangePurpose
0–31CPU exceptions (reserved by Intel)
32–255Hardware 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:

InterruptSource
0Divide by zero
6Invalid opcode
8Double fault
13General protection fault
14Page fault
32Timer (IRQ0)
33Keyboard (IRQ1)
34–47Other 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.
  • sti is 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.