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
#define BUS_ISA "ISA "
+#define BUS_PCI "PCI "
#define INT_TYPE_INT 0
#define INT_TYPE_NMI 1
+#define PCI 1
+#define NUM_PCI_SLOTS 8
static inline int check_for_cookie(void * target) {
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;
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;
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);
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;
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;
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;slot<NUM_PCI_SLOTS;slot++) {
+ for (intr=0;intr<4;intr++) {
+
+ uint8_t dst_irq = pci_irq[(slot+intr)%4];
+
+ interrupt = (struct mp_table_io_interrupt_assignment *)cur;
+ memset((void *)interrupt, 0, sizeof(struct mp_table_io_interrupt_assignment));
+
+ interrupt->entry_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;
sum += ((uint8_t *)target)[i];
}
header->checksum = (255 - sum) + 1;
-
-
return 0;
}
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;
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;
}
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;