From 5cf80ebb4391cc03a6bbc2d41302c6d416ff9598 Mon Sep 17 00:00:00 2001 From: Stefan Lankes Date: Thu, 6 Aug 2026 15:09:58 +0200 Subject: [PATCH] fix(pci): configure legacy interrupts level triggered and active low A legacy PCI interrupt is signalled level triggered and active low, while the IOAPIC defaults to the edge triggered, active high behavior of the ISA interrupts. A passed-through device therefore stops delivering interrupts after its first one, because the host only unmasks it once the guest has completed a level triggered interrupt. Correct the redirection entry where a legacy interrupt line is handed out. --- src/arch/x86_64/kernel/apic.rs | 20 ++++++++++++++++++++ src/drivers/pci.rs | 9 +++++++++ 2 files changed, 29 insertions(+) diff --git a/src/arch/x86_64/kernel/apic.rs b/src/arch/x86_64/kernel/apic.rs index 7c33db2720..b70042b7f8 100644 --- a/src/arch/x86_64/kernel/apic.rs +++ b/src/arch/x86_64/kernel/apic.rs @@ -613,6 +613,26 @@ fn ioapic_set_interrupt(irq: u8, apicid: u8, enabled: bool) { ioapic_write(IOAPIC_REG_TABLE + off + 1, ioredirect_upper); } +/// Reconfigures an interrupt line for a legacy PCI interrupt. +/// +/// [`init_ioapic`] sets up every line for the edge triggered, active high +/// behavior of the ISA interrupts. PCI signals its interrupts level triggered +/// and active low instead. +#[cfg(feature = "pci")] +pub(crate) fn ioapic_set_pci_interrupt(irq: u8, apicid: u8) { + assert!(irq <= 24); + + let off = u32::from(irq * 2); + let ioredirect_upper = u32::from(apicid) << 24; + // Vector, active low (bit 13) and level triggered (bit 15). + let ioredirect_lower = u32::from(0x20 + irq) | (1 << 13) | (1 << 15); + + debug!("Configuring irq {irq} as level triggered, active low"); + + ioapic_write(IOAPIC_REG_TABLE + off, ioredirect_lower); + ioapic_write(IOAPIC_REG_TABLE + off + 1, ioredirect_upper); +} + pub fn init_local_apic() { // Mask out all interrupts we don't need right now. local_apic_write(IA32_X2APIC_LVT_TIMER, APIC_LVT_MASK); diff --git a/src/drivers/pci.rs b/src/drivers/pci.rs index a59aaacfe7..85f007d3cc 100644 --- a/src/drivers/pci.rs +++ b/src/drivers/pci.rs @@ -181,6 +181,15 @@ impl PciDevice { return None; } + // A legacy PCI interrupt is signaled level triggered and active + // low, while the IOAPIC defaults to the edge triggered, active + // high behavior of the ISA interrupts. Without the correction a + // passed-through device stops delivering interrupts after its + // first one, because the host only unmasks the interrupt once the + // guest has completed a level triggered one. + #[cfg(target_arch = "x86_64")] + crate::arch::kernel::apic::ioapic_set_pci_interrupt(line, 0); + Some(line) } 5.. => {