diff --git a/src/arch/x86_shared/device/local_apic.rs b/src/arch/x86_shared/device/local_apic.rs index b6afe02afe..93a1372d9a 100644 --- a/src/arch/x86_shared/device/local_apic.rs +++ b/src/arch/x86_shared/device/local_apic.rs @@ -69,13 +69,27 @@ impl LocalApic { } } + /// LAPIC Spurious Interrupt Vector. + /// + /// Must not overlap with exception vectors (0โ€“31) or generic IRQ vectors + /// (32โ€“254). Vector 255 is the conventional choice per Intel SDM Vol 3 + /// ยง 10.9 ("Spurious Interrupt"). + /// + /// The previous value was 0x00 (divide-error exception), meaning any LAPIC + /// spurious interrupt was misrouted to the divide-by-zero handler โ€” a + /// latent bug that causes undefined behaviour on real hardware. + const SPURIOUS_VECTOR: u32 = 0xFF; + + /// Spurious Interrupt Vector Register value: vector | bit 8 (APIC enable). + const SVR_VALUE: u32 = Self::SPURIOUS_VECTOR | 0x100; + unsafe fn init_ap(&mut self) { unsafe { if self.x2 { wrmsr(IA32_APIC_BASE, rdmsr(IA32_APIC_BASE) | (1 << 10)); - wrmsr(IA32_X2APIC_SIVR, 0x100); + wrmsr(IA32_X2APIC_SIVR, u64::from(Self::SVR_VALUE)); } else { - self.write(0xF0, 0x100); + self.write(0xF0, Self::SVR_VALUE); } self.setup_error_int(); //self.setup_timer(); diff --git a/src/arch/x86_shared/idt.rs b/src/arch/x86_shared/idt.rs index 500645855d..c159da6261 100644 --- a/src/arch/x86_shared/idt.rs +++ b/src/arch/x86_shared/idt.rs @@ -208,6 +208,10 @@ fn init_generic(cpu_id: LogicalCpuId, idt: &mut Idt, backup_stack_end: usize) { }); } + // Override vector 0xFF (255) with the LAPIC spurious interrupt handler. + // The generic stub loop above fills 32..=255, so this must come after. + current_idt[0xFF].set_func(irq::lapic_spurious); + // reserve bits 31:0, i.e. the first 32 interrupts, which are reserved for exceptions *current_reservations[0].get_mut() |= 0x0000_0000_FFFF_FFFF; @@ -251,6 +255,7 @@ fn init_generic(cpu_id: LogicalCpuId, idt: &mut Idt, backup_stack_end: usize) { idt.set_reserved_mut(IpiKind::Switch as u8, true); idt.set_reserved_mut(IpiKind::Tlb as u8, true); idt.set_reserved_mut(IpiKind::Pit as u8, true); + idt.set_reserved_mut(0xFF, true); #[cfg(target_arch = "x86")] { diff --git a/src/arch/x86_shared/interrupt/irq.rs b/src/arch/x86_shared/interrupt/irq.rs index a0b376bc0d..441bf49bf9 100644 --- a/src/arch/x86_shared/interrupt/irq.rs +++ b/src/arch/x86_shared/interrupt/irq.rs @@ -26,6 +26,7 @@ pub enum IrqMethod { static SPURIOUS_COUNT_IRQ7: AtomicUsize = AtomicUsize::new(0); static SPURIOUS_COUNT_IRQ15: AtomicUsize = AtomicUsize::new(0); +static SPURIOUS_COUNT_LAPIC: AtomicUsize = AtomicUsize::new(0); pub fn spurious_count_irq7() -> usize { SPURIOUS_COUNT_IRQ7.load(Ordering::Relaxed) @@ -33,20 +34,21 @@ pub fn spurious_count_irq7() -> usize { pub fn spurious_count_irq15() -> usize { SPURIOUS_COUNT_IRQ15.load(Ordering::Relaxed) } +pub fn spurious_count_lapic() -> usize { + SPURIOUS_COUNT_LAPIC.load(Ordering::Relaxed) +} pub fn spurious_count() -> usize { - spurious_count_irq7() + spurious_count_irq15() + spurious_count_irq7() + spurious_count_irq15() + spurious_count_lapic() } pub fn spurious_irq_resource(_token: &mut CleanLockToken) -> syscall::Result> { - match irq_method() { - IrqMethod::Apic => Ok(Vec::from(&b"(not implemented for APIC yet)"[..])), - IrqMethod::Pic => Ok(format!( - "{}\tIRQ7\n{}\tIRQ15\n{}\ttotal\n", - spurious_count_irq7(), - spurious_count_irq15(), - spurious_count() - ) - .into_bytes()), - } + Ok(format!( + "{}\tIRQ7\n{}\tIRQ15\n{}\tLAPIC\n{}\ttotal\n", + spurious_count_irq7(), + spurious_count_irq15(), + spurious_count_lapic(), + spurious_count() + ) + .into_bytes()) } static IRQ_METHOD: AtomicUsize = AtomicUsize::new(IrqMethod::Pic as usize); @@ -320,6 +322,10 @@ interrupt!(lapic_error, || { unsafe { lapic_eoi() }; }); +interrupt!(lapic_spurious, || { + SPURIOUS_COUNT_LAPIC.fetch_add(1, Ordering::Relaxed); +}); + interrupt_error!(generic_irq, |_stack, code| { let mut token = unsafe { CleanLockToken::new() };