New files:
- include/arch/x86/lapic.h — LAPIC register definitions and API
- include/arch/x86/ioapic.h — IOAPIC register definitions and API
- src/arch/x86/lapic.c — Local APIC driver: init, EOI, MMIO access,
timer calibration via PIT channel 2, pic_disable()
- src/arch/x86/ioapic.c — I/O APIC driver: init, IRQ routing,
mask/unmask per-IRQ line
Changes:
- vmm.h: Add VMM_FLAG_PWT, VMM_FLAG_PCD, VMM_FLAG_NOCACHE for MMIO
- vmm.c: Translate PWT/PCD flags to x86 PTE bits in vmm_flags_to_x86
- arch_platform.c: Init LAPIC+IOAPIC after syscall_init, route ISA
IRQs (timer=32, kbd=33, ATA=46) through IOAPIC, disable PIC only
after IOAPIC routes are live
- idt.c: Send EOI BEFORE handler callback (critical: schedule() in
timer handler context-switches away; deferred EOI blocks LAPIC).
Add IDT gate for spurious vector 255; skip EOI for spurious
interrupts per Intel spec.
- interrupts.S: Add ISR stub for vector 255 (LAPIC spurious)
- timer.c: Use LAPIC periodic timer when available, fallback to PIT
Key design decisions:
- LAPIC MMIO mapped at 0xC0200000 (above kernel _end, below heap)
- IOAPIC MMIO mapped at 0xC0201000
- Both mapped with PCD+PWT (cache-disable) to prevent MMIO caching
- PIC disabled only AFTER IOAPIC routes configured (avoids IRQ gap)
- EOI sent before handler to prevent LAPIC starvation on context switch
- Spurious vector 255 has IDT entry but no EOI (Intel requirement)
- LAPIC timer calibrated against PIT channel 2 (~10ms measurement)
Bugs fixed during development:
- VA 0xC0100000 overlapped kernel text — moved to 0xC0200000
- pic_disable() inside lapic_init() caused IRQ gap — moved to caller
- EOI after handler blocked LAPIC when schedule() context-switched
- Missing IDT entry for vector 255 caused triple fault on spurious IRQ
Passes: make, cppcheck, QEMU smoke test (all init tests OK).