lapic: fix spurious interrupt vector from 0x00 to 0xFF
The LAPIC Spurious Interrupt Vector Register (SVR) was set to 0x100, meaning bit 8 (APIC enable) was set but the vector field was 0x00. Vector 0 is the divide-error exception handler, so any LAPIC spurious interrupt was misrouted to divide_by_zero — a latent bug. Fix: set SVR to 0x1FF (vector 0xFF + enable bit). Vector 255 is the conventional spurious vector per Intel SDM Vol 3 §10.9. Add a dedicated lapic_spurious IDT handler at vector 0xFF that increments a counter and returns WITHOUT sending EOI (spurious interrupts must not be EOId per the spec). Override the generic interrupt stub at position 255 in the IDT and reserve bit 255. Also fix spurious_irq_resource to report LAPIC spurious count for both PIC and APIC modes instead of returning a placeholder string.
This commit is contained in:
@@ -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();
|
||||
|
||||
@@ -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")]
|
||||
{
|
||||
|
||||
@@ -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<Vec<u8>> {
|
||||
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() };
|
||||
|
||||
|
||||
Reference in New Issue
Block a user