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;
}