IOAPIC Mysteries: IRQ Pin Assertion Register
On x86, the IOAPIC is an interrupt controller that takes incoming interrupts from interrupt pins and converts them to Message Signaled Interrupts (MSIs). If you zoom out from the details, the IOAPIC is essentially a table that has one MSI address/data pair for each interrupt pin that connects to it. Simple.
Recently, I have been modernizing some IOAPIC code. Thomas Prescher poked me in code review about this feature bit of the IOAPIC that is documented in the Intel PCH specification (page 412):
The Pin Assertion Register they refer to is not documented. The original IOAPIC specification does not even mention this feature bit, let alone the register. What is this register?
The first clue we found was the Intel® 82806AA PCI 64 Hub (P64H) specification (page 41) from 2001 that explained the register:
So the idea is that a device writes to this IOAPIC register, which is typically at address 0xFEC00020, to trigger a “virtual” interrupt pin. Weird.
At this point, we were not sure what this was good for. This feature could not be used for level-triggered interrupts, because this interrupt delivery method only had a “interrupt now!” command instead of assert/deassert. But asserting and deasserting would be necessary to emulate level-triggered interrupts. If you need edge-triggered interrupts and can do a data write, you can just send an MSI directly.
The final clue is from this blog post:
When the PCIe device needs to submit an MSI interrupt request, it will write the data in the Message Data register to the 0xFEC00020 address of the PCI bus domain. At this time, this memory write request writes the data into The IRQ Pin Assertion Register of I/O APIC, and the I/O APIC will finally send this MSI interrupt request to the Local APIC, and then the Local APIC will pass the INTR# signal to the CPU. Submit an interrupt request.
😱 So the way this feature works is that PCI devices send MSI messages to the IOAPIC to trigger a virtual interrupt pin. The IOAPIC then checks its table entry for this virtual pin and sends an actual MSI to the LAPIC. What is even going on?
We have questions:
- Has anyone seen an IOAPIC that actually supports the IRQ Pin Assertion Register? Please tell us!
- What happens if you use the register for virtual interrupt pin that is configured as level-triggered in the IOAPIC?
- Why was this ever useful on x86 given that it requires both a MSI-capable PCI device and a Local APIC, which receives MSIs directly?
If you have information, please share it with us via Twitter or email (see below). We are confused.
Update 2022-05-03
Checkout this thread on Twitter for additional information:
The IOAPIC always delivers interrupts via the Pin Assertion Register as level-triggered, regardless of the configuration.
The best source for the role of the Pin Assertion Register so far is the Intel® Server System S7000FC4UR Technical Product Specification:
The “Fake MSI” scheme allows PCI Express devices running on a legacy operating system to use the MSI mechanism to generate INTx compatible interrupts. This is accomplished by targeting the MSI memory write to an I/OxAPIC. […] When Fake MSI is enabled, the PCI Express devices generate a memory transaction with an address equal to I/OxAPIC_MEM_BAR + 0x20 (PAR) and a 32-bit data equal to the interrupt vector number corresponding to the device. This information is stored in the device’s MSI address and data registers, and would be initialized by the system firmware (BIOS) prior to booting a non-MSI aware operating system.