Skip to content

Commit 8514e14

Browse files
committed
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.
1 parent d0854f9 commit 8514e14

2 files changed

Lines changed: 28 additions & 0 deletions

File tree

src/arch/x86_64/kernel/apic.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -613,6 +613,25 @@ fn ioapic_set_interrupt(irq: u8, apicid: u8, enabled: bool) {
613613
ioapic_write(IOAPIC_REG_TABLE + off + 1, ioredirect_upper);
614614
}
615615

616+
/// Reconfigures an interrupt line for a legacy PCI interrupt.
617+
///
618+
/// [`init_ioapic`] sets up every line for the edge triggered, active high
619+
/// behavior of the ISA interrupts. PCI signals its interrupts level triggered
620+
/// and active low instead.
621+
pub(crate) fn ioapic_set_pci_interrupt(irq: u8, apicid: u8) {
622+
assert!(irq <= 24);
623+
624+
let off = u32::from(irq * 2);
625+
let ioredirect_upper = u32::from(apicid) << 24;
626+
// Vector, active low (bit 13) and level triggered (bit 15).
627+
let ioredirect_lower = u32::from(0x20 + irq) | (1 << 13) | (1 << 15);
628+
629+
debug!("Configuring irq {irq} as level triggered, active low");
630+
631+
ioapic_write(IOAPIC_REG_TABLE + off, ioredirect_lower);
632+
ioapic_write(IOAPIC_REG_TABLE + off + 1, ioredirect_upper);
633+
}
634+
616635
pub fn init_local_apic() {
617636
// Mask out all interrupts we don't need right now.
618637
local_apic_write(IA32_X2APIC_LVT_TIMER, APIC_LVT_MASK);

src/drivers/pci.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,15 @@ impl<T: ConfigRegionAccess> PciDevice<T> {
181181
return None;
182182
}
183183

184+
// A legacy PCI interrupt is signaled level triggered and active
185+
// low, while the IOAPIC defaults to the edge triggered, active
186+
// high behavior of the ISA interrupts. Without the correction a
187+
// passed-through device stops delivering interrupts after its
188+
// first one, because the host only unmasks the interrupt once the
189+
// guest has completed a level triggered one.
190+
#[cfg(target_arch = "x86_64")]
191+
crate::arch::kernel::apic::ioapic_set_pci_interrupt(line, 0);
192+
184193
Some(line)
185194
}
186195
5.. => {

0 commit comments

Comments
 (0)