From: Peter Dinda Date: Thu, 10 Nov 2011 16:30:27 +0000 (-0700) Subject: PCI interrupt delivery via ioapic X-Git-Url: http://v3vee.org/palacios/gitweb/gitweb.cgi?p=palacios.git;a=commitdiff_plain;h=eb2dd607efd35a1521ae6a707e0a2178dd1f74ee PCI interrupt delivery via ioapic This adds two changes: 1) correct construction of an MP table that contains PCI routing entries 2) PCI interrupt delivery to ioapic pins 16-19 in compliance with that mptable. PCI interrupts are now delivered both by PIRQ (if enabled) to the relevant PIC pins, and directly to the relevant IOAPIC pins --- diff --git a/palacios/src/devices/mptable.c b/palacios/src/devices/mptable.c index 3b6a463..cca7a44 100644 --- a/palacios/src/devices/mptable.c +++ b/palacios/src/devices/mptable.c @@ -30,7 +30,10 @@ Currently, we set up n identical processors (based on number of cores in guest info), with apics 0..n-1, and - ioapic as n. + ioapic as n. The ISA interrupt lines map to pins 0..15 + of the first ioapic. PCI bus lines map to pins 16..19 + of the first ioapic. The system supports virtual wire + compability mode and symmetric mode. PIC mode is not supported. The expectation is that the target will have 8 bytes (for ___HVMMP signature) followed by 896 bytes of space @@ -72,6 +75,7 @@ #define BUS_ISA "ISA " +#define BUS_PCI "PCI " #define INT_TYPE_INT 0 #define INT_TYPE_NMI 1 @@ -214,6 +218,8 @@ struct mp_table_local_interrupt_assignment { +#define PCI 1 +#define NUM_PCI_SLOTS 8 static inline int check_for_cookie(void * target) { @@ -275,6 +281,11 @@ static int write_pointer(void * target, uint32_t mptable_gpa) { p->pointer = mptable_gpa; p->length = 1; // length in 16 byte chunks p->spec_rev = SPEC_REV; + + // The remaining zeros indicate that an MP config table is present + // and that virtual wire mode is implemented (not PIC mode) + // Either virtual wire or PIC must be implemented in addition to + // symmetric I/O mode // checksum calculation p->checksum = 0; @@ -315,8 +326,14 @@ static int write_mptable(void * target, uint32_t numcores) { memcpy(header->oem_id, OEM_ID, 8); memcpy(header->prod_id, PROD_ID, 12); - // n processors, 1 ioapic, 1 isa bus, 16 IRQs = 18+n +#if PCI + // n processors, 1 ioapic, 1 pci bus, 1 isa bus, 16 IRQ, 4*NUM_SLOTS = 19 + 4*numslots+ n + header->entry_count = numcores + 19 + 4 * NUM_PCI_SLOTS; +#else + // n processors, 1 ioapic, 1 isa bus, 16 IRQ INTs = 18+n header->entry_count = numcores + 18; +#endif + header->lapic_addr = LAPIC_ADDR; // now we arrange the processors; @@ -343,15 +360,27 @@ static int write_mptable(void * target, uint32_t numcores) { cur += sizeof(struct mp_table_processor); } - // next comes the ISA bas +#if PCI + // PCI bus is always zero bus = (struct mp_table_bus *)cur; cur += sizeof(struct mp_table_bus); memset((void *)bus, 0, sizeof(struct mp_table_bus)); bus->entry_type = ENTRY_BUS; bus->bus_id = 0; + memcpy(bus->bus_type, BUS_PCI, 6); +#endif + + // next comes the ISA bus (bus one) + bus = (struct mp_table_bus *)cur; + cur += sizeof(struct mp_table_bus); + + memset((void *)bus, 0, sizeof(struct mp_table_bus)); + bus->entry_type = ENTRY_BUS; + bus->bus_id = 1; memcpy(bus->bus_type, BUS_ISA, 6); + // next comes the IOAPIC ioapic = (struct mp_table_ioapic *)cur; cur += sizeof(struct mp_table_ioapic); @@ -364,10 +393,19 @@ static int write_mptable(void * target, uint32_t numcores) { ioapic->ioapic_address = IOAPIC_ADDR; + + // LEGACY ISA IRQ mappings // The MPTABLE IRQ mappings are kind of odd. // We don't include a bus IRQ 2, and instead remap Bus IRQ 0 to dest irq 2 - - + // The idea here is that the timer hooks to 2, while the PIC hooks + // to zero in ExtInt mode. This makes it possible to do virtual wire + // mode via the ioapic. + // + // Note that the timer connects to pin 2 of the IOAPIC. Sadly, + // the timer is unaware of this and just raises irq 0. The ioapic + // transforms this to a pin 2 interrupt. If we want the PIC + // to be able to channel interrupts via pin 0, we need a separate + // path. for (irq = 0; irq < 16; irq++) { uint8_t dst_irq = irq; @@ -384,7 +422,7 @@ static int write_mptable(void * target, uint32_t numcores) { interrupt->interrupt_type = INT_TYPE_INT; interrupt->flags.po = INT_POLARITY_DEFAULT; interrupt->flags.el = INT_TRIGGER_DEFAULT; - interrupt->source_bus_id = 0; + interrupt->source_bus_id = 1; interrupt->source_bus_irq = irq; interrupt->dest_ioapic_id = numcores; interrupt->dest_ioapic_intn = dst_irq; @@ -392,10 +430,55 @@ static int write_mptable(void * target, uint32_t numcores) { cur += sizeof(struct mp_table_io_interrupt_assignment); } +#if PCI + { + // Interrupt redirection entries for PCI bus + // + // We need an entry for each slot+pci interrupt + // There can be 32 slots, each of which can use 4 interrupts + // Thus there are 128 entries + // + // In this simple setup, we map + // slot i, intr j (both zero based) to pci_irq[(i+j)%4] + + int slot, intr; + static uint8_t pci_irq[4] = {16,17,18,19}; + + for (slot=0;slotentry_type = ENTRY_IOINT; + interrupt->interrupt_type = INT_TYPE_INT; + interrupt->flags.po = INT_POLARITY_DEFAULT; + interrupt->flags.el = INT_TRIGGER_DEFAULT; + interrupt->source_bus_id = 0; + // Yes, this is how you encode the slot and pin of a PCI device + // As we all know, bits are expensive + // We can have as many as 32 slots, but to get that large, + // we would need to tweak the bios's landing zone for the mptable + interrupt->source_bus_irq = (slot<<2) | intr ; + interrupt->dest_ioapic_id = numcores; + interrupt->dest_ioapic_intn = dst_irq; + + cur += sizeof(struct mp_table_io_interrupt_assignment); + + //V3_Print("PCI0, slot %d, irq %d maps to irq %d\n",slot,intr,dst_irq); + } + } + } +#endif + // now we can set the length; header->base_table_length = (cur - (uint8_t *)header); + V3_Print("MPtable size: %u\n",header->base_table_length); + // checksum calculation header->checksum = 0; sum = 0; @@ -403,8 +486,6 @@ static int write_mptable(void * target, uint32_t numcores) { sum += ((uint8_t *)target)[i]; } header->checksum = (255 - sum) + 1; - - return 0; } diff --git a/palacios/src/devices/piix3.c b/palacios/src/devices/piix3.c index 8752943..b482b48 100644 --- a/palacios/src/devices/piix3.c +++ b/palacios/src/devices/piix3.c @@ -74,7 +74,7 @@ struct top_of_mem_reg { uint8_t value; struct { uint8_t rsvd1 : 1; - uint8_t isadma_reg_fwd_en : 1; + uint8_t isadma_reg_fwd_en : 1; uint8_t piix_rsvd : 1; uint8_t isadma_lo_bios_fwd_en : 1; uint8_t top_of_mem : 4; @@ -377,19 +377,28 @@ static int raise_pci_irq(struct pci_device * pci_dev, void * dev_data) { struct piix3_config_space * piix3_cfg = (struct piix3_config_space *)(piix3_pci->config_data); int intr_pin = pci_dev->config_header.intr_pin - 1; int irq_index = (intr_pin + pci_dev->dev_num - 1) & 0x3; - - //PrintError("Raising PCI IRQ %d, %p\n", piix3_cfg->pirq_rc[irq_index], piix3->vm); - + + /* + PrintError("Raising PCI dev %d intr %d via IOAPIC as IRQ %d and via PIRQ as IRQ %d on VM %p\n", + pci_dev->dev_num, pci_dev->config_header.intr_pin, + 16+irq_index, + piix3_cfg->pirq_rc[irq_index], piix3->vm); + */ + + // deliver first by PIRQ, if it exists + // if (piix3_cfg->pirq_rc[irq_index] < 16) { v3_raise_irq(piix3->vm, piix3_cfg->pirq_rc[irq_index] & 0xf); } else { - PrintError("Tried to raise interrupt on disabled PIRQ entry (%d)\n", irq_index); - PrintError("\tpirq_rc values: 0=0x%x, 1=0x%x, 2=0x%x, 3=0x%x\n", - piix3_cfg->pirq_rc[0], piix3_cfg->pirq_rc[1], - piix3_cfg->pirq_rc[2], piix3_cfg->pirq_rc[3]); - return -1; + // not an error } + // deliver next via the PCI0 to ioapic mapping defined in the + // mptable (ioapic, pins 16->19 are used for PCI0) + // ideally this would check to verify that an ioapic is actually available + v3_raise_irq(piix3->vm, 16+irq_index); + + return 0; } @@ -403,15 +412,15 @@ static int lower_pci_irq(struct pci_device * pci_dev, void * dev_data) { int irq_index = (intr_pin + pci_dev->dev_num - 1) & 0x3; // PrintError("Lowering PCI IRQ %d\n", piix3_cfg->pirq_rc[irq_index]); + + // First, lower the pin on the ioapic + v3_lower_irq(piix3->vm, irq_index+16); + // Next, lower whatever we asserted by the PIRQs if (piix3_cfg->pirq_rc[irq_index] < 16) { v3_lower_irq(piix3->vm, piix3_cfg->pirq_rc[irq_index] & 0xf); } else { - PrintError("Tried to lower interrupt on disabled PIRQ entry (%d)\n", irq_index); - PrintError("\tpirq_rc values: 0=0x%x, 1=0x%x, 2=0x%x, 3=0x%x\n", - piix3_cfg->pirq_rc[0], piix3_cfg->pirq_rc[1], - piix3_cfg->pirq_rc[2], piix3_cfg->pirq_rc[3]); - return -1; + // not an error } return 0;