2026-08-17
When a PCIe device fires an MSI (Message Signaled Interrupt), it's really just a memory write to a magic address in the 0xFEE00000 range. The bits of the address and data encode the target APIC ID, the vector, and the delivery mode. The device picks who gets interrupted. That's a problem.
A malicious or buggy device can craft an MSI targeting any core with any vector — including NMI, SMI, or vector 2 (which some hypervisors use for internal signaling). Before interrupt remapping, a rogue PCIe card could inject a spurious NMI into the BSP and crash the machine, or hijack a hypercall vector. VT-d and AMD-Vi fixed this by putting a translation table in front of the interrupt path.
How the Interrupt Remapping Table (IRT) works:
Real-world example: On Linux, boot with intremap=off and check dmesg | grep DMAR. You'll see the kernel refuse to enable x2APIC — because x2APIC mandates interrupt remapping. That's not a Linux quirk; the x2APIC spec requires it, because x2APIC's 32-bit APIC ID field can't fit in the legacy MSI address format. The IRT is the only way to route to APIC IDs above 255.
Rule of thumb: Each IRT entry is 128 bits (16 bytes). A system supporting 64K interrupt sources needs 1 MB of remap table. The IOMMU caches recent entries in an IRT cache (like a TLB for interrupts); a miss adds ~50-100 ns to interrupt delivery. For a NIC doing 1M interrupts/sec, that's 50-100 ms/sec of extra latency — invisible in practice, but the reason vendors size the IRT cache generously (typically 32-64 entries).
The IRT also enables posted interrupts in virtualization: the IOMMU can route a device interrupt directly into a guest VM's virtual APIC without a VM exit, by remapping the MSI to a special posted-interrupt descriptor.
