X-Git-Url: http://v3vee.org/palacios/gitweb/gitweb.cgi?a=blobdiff_plain;f=palacios%2Fsrc%2Fdevices%2Fmptable.c;h=dc1390e65879812c877201fd1c758791d619e2e1;hb=60ad6a41c6d0ee08ed689e8505eb0c3df0c2a289;hp=64cf96f65a053610551075c8aa71f3b532e4dcc5;hpb=9d406e84682878859284200ae00251d27c53c845;p=palacios.git diff --git a/palacios/src/devices/mptable.c b/palacios/src/devices/mptable.c index 64cf96f..dc1390e 100644 --- a/palacios/src/devices/mptable.c +++ b/palacios/src/devices/mptable.c @@ -21,6 +21,7 @@ #include #include + /* The guest bios is compiled with blank space for am MP table at a default address. A cookie value is temporarily placed @@ -30,7 +31,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 +76,7 @@ #define BUS_ISA "ISA " +#define BUS_PCI "PCI " #define INT_TYPE_INT 0 #define INT_TYPE_NMI 1 @@ -214,6 +219,7 @@ struct mp_table_local_interrupt_assignment { +#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; @@ -292,7 +303,7 @@ static int write_pointer(void * target, uint32_t mptable_gpa) { -static int write_mptable(void * target, uint32_t numcores) { +static int write_mptable(void * target, uint32_t numcores, int have_ioapic, int have_pci) { uint32_t i = 0; uint8_t sum = 0; uint8_t core = 0; @@ -315,12 +326,16 @@ 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 - header->entry_count = numcores + 18; + // numcores entries for apics, one entry for ioapic (if it exists) + // one entry for isa bus (if ioapic exists), one entry for pci bus (if exists), + // 16 entries for isa irqs (if ioapic exists) + num_slots*num_intr pci irqs + // (if ioapic and pci exist) + header->entry_count = numcores + !!have_ioapic + !!have_ioapic + !!have_pci + + 16*(!!have_ioapic) + NUM_PCI_SLOTS * 4 * (!!have_pci) * (!!have_ioapic); + header->lapic_addr = LAPIC_ADDR; // now we arrange the processors; - for (core = 0; core < numcores; core++) { proc = (struct mp_table_processor *)cur; memset((void *)proc, 0, sizeof(struct mp_table_processor)); @@ -343,46 +358,126 @@ static int write_mptable(void * target, uint32_t numcores) { cur += sizeof(struct mp_table_processor); } - // next comes the ISA bas + // PCI bus is always zero + if (have_pci) { + 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); + } + + // 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 = 0; + 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); - - memset((void *)ioapic, 0, sizeof(struct mp_table_ioapic)); - ioapic->entry_type = ENTRY_IOAPIC; - ioapic->ioapic_id = numcores; - ioapic->ioapic_version = IOAPIC_VERSION; - ioapic->ioapic_flags.en = 1; - ioapic->ioapic_address = IOAPIC_ADDR; - - for (irq = 0; irq < 16; irq++) { + if (have_ioapic) { + ioapic = (struct mp_table_ioapic *)cur; + cur += sizeof(struct mp_table_ioapic); + + memset((void *)ioapic, 0, sizeof(struct mp_table_ioapic)); + ioapic->entry_type = ENTRY_IOAPIC; + ioapic->ioapic_id = numcores; + ioapic->ioapic_version = IOAPIC_VERSION; + ioapic->ioapic_flags.en = 1; + 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. + if (have_ioapic) { + for (irq = 0; irq < 16; irq++) { + uint8_t dst_irq = irq; + + if (irq == 0) { + dst_irq = 2; + } else if (irq == 2) { + continue; + } + 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; + interrupt->source_bus_id = 1; interrupt->source_bus_irq = irq; interrupt->dest_ioapic_id = numcores; - interrupt->dest_ioapic_intn = irq; - + interrupt->dest_ioapic_intn = dst_irq; + cur += sizeof(struct mp_table_io_interrupt_assignment); + } } - + + if (have_pci && have_ioapic) { + // 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(VM_NONE, VCORE_NONE, "PCI0, slot %d, irq %d maps to irq %d\n",slot,intr,dst_irq); + } + } + } + // now we can set the length; header->base_table_length = (cur - (uint8_t *)header); + V3_Print(VM_NONE, VCORE_NONE, "MPtable size: %u\n",header->base_table_length); + // checksum calculation header->checksum = 0; sum = 0; @@ -390,49 +485,95 @@ static int write_mptable(void * target, uint32_t numcores) { sum += ((uint8_t *)target)[i]; } header->checksum = (255 - sum) + 1; - - return 0; } + +static v3_cfg_tree_t *find_first_peer_device_of_class(v3_cfg_tree_t *themptablenode, char *theclass) +{ + v3_cfg_tree_t *p=themptablenode->parent; + v3_cfg_tree_t *c; + + + if (p==NULL) { + return NULL; + } + + for (c=v3_xml_child(p,"device"); + c && strcasecmp(v3_cfg_val(c,"class"),theclass); + c=v3_xml_next(c)) { + } + + return c; +} + + + + static int mptable_init(struct v3_vm_info * vm, v3_cfg_tree_t * cfg) { void * target = NULL; + int have_pci = find_first_peer_device_of_class(cfg,"pci")!=NULL; + int have_piix3 = find_first_peer_device_of_class(cfg,"piix3")!=NULL; + int have_apic = find_first_peer_device_of_class(cfg,"lapic")!=NULL; + int have_ioapic = find_first_peer_device_of_class(cfg,"ioapic")!=NULL; + + uint32_t num_cores = vm->num_cores; + +#ifdef V3_CONFIG_HVM + num_cores = v3_get_hvm_ros_cores(vm); +#endif + + if (!have_apic) { + PrintError(vm, VCORE_NONE, "Attempt to instantiate MPTABLE but machine has no apics!\n"); + return -1; + } + + if (!have_ioapic) { + PrintError(vm, VCORE_NONE, "Attempt to instantiate MPTABLE without ioapic - will try, but this won't end well\n"); + } + + if (have_pci && (!have_piix3 || !have_ioapic)) { + PrintError(vm, VCORE_NONE, "Attempt to instantiate MPTABLE with a PCI Bus, but without either a piix3 or an ioapic\n"); + return -1; + } + if (v3_gpa_to_hva(&(vm->cores[0]), BIOS_MP_TABLE_DEFAULT_LOCATION, (addr_t *)&target) == -1) { - PrintError("Cannot inject mptable due to unmapped bios!\n"); + PrintError(vm, VCORE_NONE, "Cannot inject mptable due to unmapped bios!\n"); return -1; } if (!check_for_cookie(target)) { - PrintError("Cookie mismatch in writing mptable, aborting (probably wrong guest BIOS).\n"); - return -1; + PrintError(vm, VCORE_NONE, "Cookie mismatch in writing mptable, aborting (probably just wrong guest BIOS, so this is not a hard error).\n"); + // we pretend we were sucesssful + return 0; } - if (vm->num_cores > 32) { - PrintError("No support for >32 cores in writing MP table, aborting.\n"); + if (num_cores > 32) { + PrintError(vm, VCORE_NONE, "No support for >32 cores in writing MP table, aborting.\n"); return -1; } - V3_Print("Constructing mptable for %u cores at %p\n", vm->num_cores, target); + V3_Print(vm, VCORE_NONE, "Constructing mptable for %u cores at %p\n", num_cores, target); if (write_pointer(target, BIOS_MP_TABLE_DEFAULT_LOCATION + sizeof(struct mp_floating_pointer)) == -1) { - PrintError("Unable to write mptable floating pointer, aborting.\n"); + PrintError(vm, VCORE_NONE, "Unable to write mptable floating pointer, aborting.\n"); return -1; } if (!check_pointer(target)) { - PrintError("Failed to inject mptable floating pointer correctly --- checksum fails\n"); + PrintError(vm, VCORE_NONE, "Failed to inject mptable floating pointer correctly --- checksum fails\n"); return -1; } - if (write_mptable(target + sizeof(struct mp_floating_pointer), vm->num_cores) == -1) { - PrintError("Cannot inject mptable configuration header and entries\n"); + if (write_mptable(target + sizeof(struct mp_floating_pointer), num_cores, have_ioapic, have_pci)) { + PrintError(vm, VCORE_NONE, "Cannot inject mptable configuration header and entries\n"); return -1; } if (!check_table(target + sizeof(struct mp_floating_pointer))) { - PrintError("Failed to inject mptable configuration header and entries correctly --- checksum fails\n"); + PrintError(vm, VCORE_NONE, "Failed to inject mptable configuration header and entries correctly --- checksum fails\n"); return -1; }