Set the correct IOAPIC INTIN pin number for pci bus interrupt sources during MP spec table generation (on emulators). Currently, the pin number is set to the interrupt line field in the device's configuration space, which is set to either IRQ 10 or 11 as the boot rom driver expects. This works since qemu maps all ISA compatible PIC IRQs onto IOAPIC pins 0-15, including PIRQs. But it will break if, for some reason, the IRQ is routed to something other than the INTLINE value using, for eg. ACPI _CRS. This patch ensures the pin number is set to the correct value (16-23) that the INTx pin is routed to in APIC mode, in agreement with the ACPI _PRT provided routing.
Tested on a Linux 5.17.2 guest on qemu with the pci=nomsi and acpi=noirq boot parameters to force MP table parsing for interrupt sources.
Signed-off-by: Jay Khandkar jaykhandkar2002@gmail.com --- src/fw/mptable.c | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-)
diff --git a/src/fw/mptable.c b/src/fw/mptable.c index 47385cc..3a7b02f 100644 --- a/src/fw/mptable.c +++ b/src/fw/mptable.c @@ -18,6 +18,33 @@ #include "util.h" // MaxCountCPUs #include "x86.h" // cpuid
+/* table to obtain IOAPIC INTIN pin # for a certain pci slot and pin */ + +/* daisy chaining in line with acpi _PRT */ +#define SLOT_GSIE {0x14, 0x15, 0x16, 0x17} +#define SLOT_GSIF {0x15, 0x16, 0x17, 0x14} +#define SLOT_GSIG {0x16, 0x17, 0x14, 0x15} +#define SLOT_GSIH {0x17, 0x14, 0x15, 0x16} +#define SLOT_GSIA {0x10, 0x11, 0x12, 0x13} + +u8 pirq_intin[32][4] = { SLOT_GSIE, SLOT_GSIF, SLOT_GSIG, SLOT_GSIH, + SLOT_GSIE, SLOT_GSIF, SLOT_GSIG, SLOT_GSIH, + SLOT_GSIE, SLOT_GSIF, SLOT_GSIG, SLOT_GSIH, + SLOT_GSIE, SLOT_GSIF, SLOT_GSIG, SLOT_GSIH, + SLOT_GSIE, SLOT_GSIF, SLOT_GSIG, SLOT_GSIH, + SLOT_GSIE, SLOT_GSIF, SLOT_GSIG, SLOT_GSIH, + SLOT_GSIE, + + /* INTA -> PIRQA for slot 25 - 31, but 30 + see the default value of D<N>IR */ + SLOT_GSIA, SLOT_GSIA, SLOT_GSIA, SLOT_GSIA, + SLOT_GSIA, + + /* PCIe->PCI bridge. use PIRQ[E-H] */ + SLOT_GSIE, + + SLOT_GSIA }; + void mptable_setup(void) { @@ -114,7 +141,6 @@ mptable_setup(void) if (pci_bdf_to_bus(bdf) != 0) break; int pin = pci_config_readb(bdf, PCI_INTERRUPT_PIN); - int irq = pci_config_readb(bdf, PCI_INTERRUPT_LINE); if (pin == 0) continue; if (dev != pci_bdf_to_busdev(bdf)) { @@ -131,7 +157,7 @@ mptable_setup(void) intsrc->srcbus = pci_bdf_to_bus(bdf); /* PCI bus */ intsrc->srcbusirq = (pci_bdf_to_dev(bdf) << 2) | (pin - 1); intsrc->dstapic = ioapic_id; - intsrc->dstirq = irq; + intsrc->dstirq = pirq_intin[pci_bdf_to_dev(bdf)][pin - 1]; intsrc++; }