This seems like the easier approach from my RFC yesterday. Instead of trying to make ich9 interrupt mapping look like piix, just add a new slot-to-irq function so we can not only get bus 0 devices, but root ports fixed as well. The change from piix mapping is subtle, but required for device assignment. Additionally, fix the PIRQn initialization value, enabling the interrupts for boot ROMs. Thanks,
Alex
---
Alex Williamson (2): q35: Enable all PIRQn IRQs at startup q35: Add new PCI slot to irq routing function
src/pciinit.c | 41 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 36 insertions(+), 5 deletions(-)
We seem to use the IRQEN bit of the PIRQn registers interchangeably to select APIC mode or to disable an IRQ. I can't decide if we're intending to disable the IRQ or select APIC mode here, but in either case it prevents PIC mode assigned devices from working. When seabios writes IRQEN to these registers, qemu interprets that as APIC mode, so while the boot ROM driver is waiting for an interrupt on ISA compatible IRQ 10 or 11, KVM is injecting interrupts to APIC pins 16 - 23. Devices on the root bus use PIRQE:H while the root ports use PIRQA:D. Enable them all so we don't limit where we support boot ROMs. The guest will later disable unused IRQs with the ACPI _DIS method.
Signed-off-by: Alex Williamson alex.williamson@redhat.com --- src/pciinit.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/src/pciinit.c b/src/pciinit.c index a406bbd..857e8af 100644 --- a/src/pciinit.c +++ b/src/pciinit.c @@ -143,11 +143,9 @@ void mch_isa_bridge_init(struct pci_device *dev, void *arg) /* activate irq remapping in LPC */
/* PIRQ[A-D] routing */ - pci_config_writeb(bdf, ICH9_LPC_PIRQA_ROUT + i, - irq | ICH9_LPC_PIRQ_ROUT_IRQEN); + pci_config_writeb(bdf, ICH9_LPC_PIRQA_ROUT + i, irq); /* PIRQ[E-H] routing */ - pci_config_writeb(bdf, ICH9_LPC_PIRQE_ROUT + i, - irq | ICH9_LPC_PIRQ_ROUT_IRQEN); + pci_config_writeb(bdf, ICH9_LPC_PIRQE_ROUT + i, irq); } outb(elcr[0], ICH9_LPC_PORT_ELCR1); outb(elcr[1], ICH9_LPC_PORT_ELCR2);
q35/ich9 doesn't use the same interrupt mapping function as i440fx/piix. PIRQA:D and PIRQE:H are programmed identically, but we start at index 0, not index -1. Slots 25 through 31 are also programmed independently.
When running qemu w/o this patch, a device at address 0:6.0 will have its PCI interrupt line register programmed with irq 10 (as seen by info pci), but it actually uses irq 11 (as reported the guest). Half of the interrupt lines are misprogrammedi like this. Functionally, a fully emulated qemu guest doesn't care much, but when we try to use device assignment, we really need to know the correct irqs.
Signed-off-by: Alex Williamson alex.williamson@redhat.com --- src/pciinit.c | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-)
diff --git a/src/pciinit.c b/src/pciinit.c index 857e8af..ddac7e7 100644 --- a/src/pciinit.c +++ b/src/pciinit.c @@ -91,8 +91,10 @@ const u8 pci_irqs[4] = { 10, 10, 11, 11 };
+static int (*pci_slot_get_irq)(struct pci_device *pci, int pin); + // Return the global irq number corresponding to a host bus device irq pin. -static int pci_slot_get_irq(struct pci_device *pci, int pin) +static int piix_pci_slot_get_irq(struct pci_device *pci, int pin) { int slot_addend = 0;
@@ -104,6 +106,33 @@ static int pci_slot_get_irq(struct pci_device *pci, int pin) return pci_irqs[(pin - 1 + slot_addend) & 3]; }
+static int mch_pci_slot_get_irq(struct pci_device *pci, int pin) +{ + int irq, slot_addend = 0; + + while (pci->parent != NULL) { + slot_addend += pci_bdf_to_dev(pci->bdf); + pci = pci->parent; + } + slot_addend += pci_bdf_to_dev(pci->bdf); + + switch (slot_addend) { + /* Slots 0-24 rotate slot:pin mapping similar to piix above, but + with a different starting index - see q35-acpi-dsdt.dsl */ + case 0 ... 24: + irq = pci_irqs[(pin - 1 + slot_addend) & 3]; + break; + /* Slots 25-31 all use LNKA mapping (or LNKE, but A:D = E:H) */ + case 25 ... 31: + irq = pci_irqs[pin - 1]; + break; + default: + irq = 0; + } + + return irq; +} + /* PIIX3/PIIX4 PCI to ISA bridge */ static void piix_isa_bridge_init(struct pci_device *pci, void *arg) { @@ -292,6 +321,8 @@ void i440fx_mem_addr_init(struct pci_device *dev, void *arg) pcimem_start = 0x80000000; else if (RamSize <= 0xc0000000) pcimem_start = 0xc0000000; + + pci_slot_get_irq = piix_pci_slot_get_irq; }
void mch_mem_addr_init(struct pci_device *dev, void *arg) @@ -310,6 +341,8 @@ void mch_mem_addr_init(struct pci_device *dev, void *arg)
/* setup pci i/o window (above mmconfig) */ pcimem_start = addr + size; + + pci_slot_get_irq = mch_pci_slot_get_irq; }
static const struct pci_device_id pci_platform_tbl[] = {
On Tue, 2013-01-22 at 15:12 -0700, Alex Williamson wrote:
q35/ich9 doesn't use the same interrupt mapping function as i440fx/piix. PIRQA:D and PIRQE:H are programmed identically, but we start at index 0, not index -1. Slots 25 through 31 are also programmed independently.
When running qemu w/o this patch, a device at address 0:6.0 will have its PCI interrupt line register programmed with irq 10 (as seen by info pci), but it actually uses irq 11 (as reported the guest). Half of the interrupt lines are misprogrammedi like this. Functionally, a fully emulated qemu guest doesn't care much, but when we try to use device assignment, we really need to know the correct irqs.
Signed-off-by: Alex Williamson alex.williamson@redhat.com
src/pciinit.c | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-)
diff --git a/src/pciinit.c b/src/pciinit.c index 857e8af..ddac7e7 100644 --- a/src/pciinit.c +++ b/src/pciinit.c @@ -91,8 +91,10 @@ const u8 pci_irqs[4] = { 10, 10, 11, 11 };
+static int (*pci_slot_get_irq)(struct pci_device *pci, int pin);
// Return the global irq number corresponding to a host bus device irq pin. -static int pci_slot_get_irq(struct pci_device *pci, int pin) +static int piix_pci_slot_get_irq(struct pci_device *pci, int pin) { int slot_addend = 0;
@@ -104,6 +106,33 @@ static int pci_slot_get_irq(struct pci_device *pci, int pin) return pci_irqs[(pin - 1 + slot_addend) & 3]; }
+static int mch_pci_slot_get_irq(struct pci_device *pci, int pin) +{
- int irq, slot_addend = 0;
- while (pci->parent != NULL) {
slot_addend += pci_bdf_to_dev(pci->bdf);
pci = pci->parent;
- }
- slot_addend += pci_bdf_to_dev(pci->bdf);
- switch (slot_addend) {
Nak, I'm not accounting for the bridges properly here.
- /* Slots 0-24 rotate slot:pin mapping similar to piix above, but
with a different starting index - see q35-acpi-dsdt.dsl */
- case 0 ... 24:
irq = pci_irqs[(pin - 1 + slot_addend) & 3];
break;
- /* Slots 25-31 all use LNKA mapping (or LNKE, but A:D = E:H) */
- case 25 ... 31:
irq = pci_irqs[pin - 1];
break;
- default:
irq = 0;
- }
- return irq;
+}
/* PIIX3/PIIX4 PCI to ISA bridge */ static void piix_isa_bridge_init(struct pci_device *pci, void *arg) { @@ -292,6 +321,8 @@ void i440fx_mem_addr_init(struct pci_device *dev, void *arg) pcimem_start = 0x80000000; else if (RamSize <= 0xc0000000) pcimem_start = 0xc0000000;
- pci_slot_get_irq = piix_pci_slot_get_irq;
}
void mch_mem_addr_init(struct pci_device *dev, void *arg) @@ -310,6 +341,8 @@ void mch_mem_addr_init(struct pci_device *dev, void *arg)
/* setup pci i/o window (above mmconfig) */ pcimem_start = addr + size;
- pci_slot_get_irq = mch_pci_slot_get_irq;
}
static const struct pci_device_id pci_platform_tbl[] = {
q35/ich9 doesn't use the same interrupt mapping function as i440fx/piix. PIRQA:D and PIRQE:H are programmed identically, but we start at index 0, not index -1. Slots 25 through 31 are also programmed independently.
When running qemu w/o this patch, a device at address 0:6.0 will have its PCI interrupt line register programmed with irq 10 (as seen by info pci), but it actually uses irq 11 (as reported the guest). Half of the interrupt lines are misprogrammedi like this. Functionally, a fully emulated qemu guest doesn't care much, but when we try to use device assignment, we really need to know the correct irqs.
Signed-off-by: Alex Williamson alex.williamson@redhat.com ---
v2: separate the root bus slot for the switch statement, always 0-31
src/pciinit.c | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-)
diff --git a/src/pciinit.c b/src/pciinit.c index 857e8af..8675adb 100644 --- a/src/pciinit.c +++ b/src/pciinit.c @@ -91,8 +91,10 @@ const u8 pci_irqs[4] = { 10, 10, 11, 11 };
+static int (*pci_slot_get_irq)(struct pci_device *pci, int pin); + // Return the global irq number corresponding to a host bus device irq pin. -static int pci_slot_get_irq(struct pci_device *pci, int pin) +static int piix_pci_slot_get_irq(struct pci_device *pci, int pin) { int slot_addend = 0;
@@ -104,6 +106,31 @@ static int pci_slot_get_irq(struct pci_device *pci, int pin) return pci_irqs[(pin - 1 + slot_addend) & 3]; }
+static int mch_pci_slot_get_irq(struct pci_device *pci, int pin) +{ + int irq, slot, pin_addend = 0; + + while (pci->parent != NULL) { + pin_addend += pci_bdf_to_dev(pci->bdf); + pci = pci->parent; + } + slot = pci_bdf_to_dev(pci->bdf); + + switch (slot) { + /* Slots 0-24 rotate slot:pin mapping similar to piix above, but + with a different starting index - see q35-acpi-dsdt.dsl */ + case 0 ... 24: + irq = pci_irqs[(pin - 1 + pin_addend + slot) & 3]; + break; + /* Slots 25-31 all use LNKA mapping (or LNKE, but A:D = E:H) */ + case 25 ... 31: + irq = pci_irqs[(pin - 1 + pin_addend) & 3]; + break; + } + + return irq; +} + /* PIIX3/PIIX4 PCI to ISA bridge */ static void piix_isa_bridge_init(struct pci_device *pci, void *arg) { @@ -292,6 +319,8 @@ void i440fx_mem_addr_init(struct pci_device *dev, void *arg) pcimem_start = 0x80000000; else if (RamSize <= 0xc0000000) pcimem_start = 0xc0000000; + + pci_slot_get_irq = piix_pci_slot_get_irq; }
void mch_mem_addr_init(struct pci_device *dev, void *arg) @@ -310,6 +339,8 @@ void mch_mem_addr_init(struct pci_device *dev, void *arg)
/* setup pci i/o window (above mmconfig) */ pcimem_start = addr + size; + + pci_slot_get_irq = mch_pci_slot_get_irq; }
static const struct pci_device_id pci_platform_tbl[] = {