/* * mp_tables.c: Dynamically writes MP table info into the ROMBIOS. * * In order to work with various VCPU counts, this code reads the VCPU count * for the HVM partition and creates the correct MP tables for the VCPU count * and places the information into a predetermined location set aside in the * ROMBIOS during build time. * * Please note that many of the values, such as the CPU's * family/model/stepping, are hard-coded based upon the values that were used * in the ROMBIOS and may need to be modified or calculated dynamically to * correspond with what an HVM guest's CPUID returns. * * Travis Betak, travis.betak@amd.com * Copyright (c) 2006, AMD. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 59 Temple * Place - Suite 330, Boston, MA 02111-1307 USA. */ #include #include "config.h" /* number of non-processor MP table entries */ #define NR_NONPROC_ENTRIES 18 #define ENTRY_TYPE_PROCESSOR 0 #define ENTRY_TYPE_BUS 1 #define ENTRY_TYPE_IOAPIC 2 #define ENTRY_TYPE_IO_INTR 3 #define ENTRY_TYPE_LOCAL_INTR 4 #define CPU_FLAG_ENABLED 0x01 #define CPU_FLAG_BSP 0x02 /* TODO change this to correspond with what the guest's see's from CPUID */ #define CPU_SIG_FAMILY 0x06 #define CPU_SIG_MODEL 0x00 #define CPU_SIG_STEPPING 0x00 #define CPU_SIGNATURE ((CPU_SIG_FAMILY << 8) \ | (CPU_SIG_MODEL << 4) \ | (CPU_SIG_STEPPING)) #define CPU_FEATURE_FPU (1U << 0) #define CPU_FEATURE_MCE (1U << 7) #define CPU_FEATURE_CX8 (1U << 8) #define CPU_FEATURE_APIC (1U << 9) #define CPU_FEATURES (CPU_FEATURE_FPU | CPU_FEATURE_APIC) #define BUS_TYPE_LENGTH 6 #define BUS_TYPE_STR_ISA "ISA " #define BUS_ID_ISA 0 #define INTR_TYPE_INT 0 #define INTR_TYPE_NMI 1 #define INTR_TYPE_SMI 2 #define INTR_TYPE_EXTINT 3 #define INTR_MAX_NR 16 #include "util.h" /* * The following structures are defined in the MuliProcessor Specifiation v1.4 */ /* MP Floating Pointer Structure */ struct mp_floating_pointer_struct { uint8_t signature[4]; uint32_t mp_table; uint8_t length; uint8_t revision; uint8_t checksum; uint8_t feature[5]; }; /* MP Configuration Table */ struct mp_config_table { uint8_t signature[4]; uint16_t length; uint8_t revision; uint8_t checksum; uint8_t oem_id[8]; uint8_t vendor_id[12]; uint32_t oem_table; uint16_t oem_table_sz; uint16_t nr_entries; uint32_t lapic; uint16_t extended_length; uint8_t extended_checksum; uint8_t reserved; }; /* MP Processor Entry */ struct mp_proc_entry { uint8_t type; uint8_t lapic_id; uint8_t lapic_version; uint8_t cpu_flags; uint32_t cpu_signature; uint32_t feature_flags; uint8_t reserved[8]; }; /* MP Bus Entry */ struct mp_bus_entry { uint8_t type; uint8_t bus_id; uint8_t bus_type_str[6]; }; /* MP IOAPIC Entry */ struct mp_ioapic_entry { uint8_t type; uint8_t ioapic_id; uint8_t ioapic_version; uint8_t ioapic_flags; uint32_t ioapic_addr; }; /* MP IO Interrupt Entry */ struct mp_io_intr_entry { uint8_t type; uint8_t intr_type; uint16_t io_intr_flags; uint8_t src_bus_id; uint8_t src_bus_irq; uint8_t dst_ioapic_id; uint8_t dst_ioapic_intin; }; /* MP Local Interrupt Entry */ struct mp_local_intr_entry { uint8_t type; uint8_t intr_type; uint16_t local_intr_flags; uint8_t src_bus_id; uint8_t src_bus_irq; uint8_t dst_lapic_id; uint8_t dst_lapic_lintin; }; static void fill_mp_config_table(struct mp_config_table *mpct, int length) { int vcpu_nr, i; uint8_t checksum; vcpu_nr = hvm_info->nr_vcpus; /* fill in the MP configuration table signature, "PCMP" */ mpct->signature[0] = 'P'; mpct->signature[1] = 'C'; mpct->signature[2] = 'M'; mpct->signature[3] = 'P'; mpct->length = length; mpct->revision = 4; /* fill in the OEM ID string, "_HVMCPU_" */ mpct->oem_id[0] = '_'; mpct->oem_id[3] = 'M'; mpct->oem_id[6] = 'U'; mpct->oem_id[1] = 'H'; mpct->oem_id[4] = 'C'; mpct->oem_id[7] = '_'; mpct->oem_id[2] = 'V'; mpct->oem_id[5] = 'P'; /* fill in the Vendor ID string, "XEN " */ mpct->vendor_id[0] = 'X'; mpct->vendor_id[6] = ' '; mpct->vendor_id[1] = 'E'; mpct->vendor_id[7] = ' '; mpct->vendor_id[2] = 'N'; mpct->vendor_id[8] = ' '; mpct->vendor_id[3] = ' '; mpct->vendor_id[9] = ' '; mpct->vendor_id[4] = ' '; mpct->vendor_id[10] = ' '; mpct->vendor_id[5] = ' '; mpct->vendor_id[11] = ' '; mpct->oem_table = 0; mpct->oem_table_sz = 0; mpct->nr_entries = vcpu_nr + NR_NONPROC_ENTRIES; mpct->lapic = LAPIC_BASE_ADDRESS; mpct->extended_length = 0; mpct->extended_checksum = 0; /* Finally, fill in the checksum. */ mpct->checksum = checksum = 0; for ( i = 0; i < length; i++ ) checksum += ((uint8_t *)(mpct))[i]; mpct->checksum = -checksum; } /* fills in an MP processor entry for VCPU 'vcpu_id' */ static void fill_mp_proc_entry(struct mp_proc_entry *mppe, int vcpu_id) { mppe->type = ENTRY_TYPE_PROCESSOR; mppe->lapic_id = LAPIC_ID(vcpu_id); mppe->lapic_version = 0x11; mppe->cpu_flags = CPU_FLAG_ENABLED; if ( vcpu_id == 0 ) mppe->cpu_flags |= CPU_FLAG_BSP; mppe->cpu_signature = CPU_SIGNATURE; mppe->feature_flags = CPU_FEATURES; } /* fills in an MP bus entry of type 'type' and bus ID 'bus_id' */ static void fill_mp_bus_entry( struct mp_bus_entry *mpbe, int bus_id, const char *type) { int i; mpbe->type = ENTRY_TYPE_BUS; mpbe->bus_id = bus_id; for ( i = 0; i < BUS_TYPE_LENGTH; i++ ) mpbe->bus_type_str[i] = type[i]; /* FIXME length check? */ } /* fills in an MP IOAPIC entry for IOAPIC 'ioapic_id' */ static void fill_mp_ioapic_entry(struct mp_ioapic_entry *mpie) { mpie->type = ENTRY_TYPE_IOAPIC; mpie->ioapic_id = IOAPIC_ID; mpie->ioapic_version = IOAPIC_VERSION; mpie->ioapic_flags = 1; /* enabled */ mpie->ioapic_addr = IOAPIC_BASE_ADDRESS; } /* fill in the mp floating processor structure */ static void fill_mpfps(struct mp_floating_pointer_struct *mpfps, uint32_t mpct) { int i; uint8_t checksum; mpfps->signature[0] = '_'; mpfps->signature[1] = 'M'; mpfps->signature[2] = 'P'; mpfps->signature[3] = '_'; mpfps->mp_table = mpct; mpfps->length = 1; mpfps->revision = 4; mpfps->checksum = 0; for (i = 0; i < 5; ++i) mpfps->feature[i] = 0; /* compute the checksum for our new table */ checksum = 0; for ( i = 0; i < sizeof(struct mp_floating_pointer_struct); i++ ) checksum += ((uint8_t *)(mpfps))[i]; mpfps->checksum = -checksum; } /* create_mp_tables - creates MP tables for the guest based upon config data */ unsigned long create_mp_tables(void *_mpfps) { char *p; int vcpu_nr, i, length; void *base; struct mp_io_intr_entry *mpiie; struct mp_floating_pointer_struct *mpfps; vcpu_nr = hvm_info->nr_vcpus; printf("Creating MP tables ...\n"); if ( _mpfps == NULL ) { int sz; sz = sizeof(struct mp_floating_pointer_struct); sz += sizeof(struct mp_config_table); sz += sizeof(struct mp_proc_entry) * vcpu_nr; sz += sizeof(struct mp_bus_entry); sz += sizeof(struct mp_ioapic_entry); sz += sizeof(struct mp_io_intr_entry) * 16; _mpfps = mem_alloc(sz, 0); } mpfps = _mpfps; base = &mpfps[1]; p = base + sizeof(struct mp_config_table); for ( i = 0; i < vcpu_nr; i++ ) { fill_mp_proc_entry((struct mp_proc_entry *)p, i); p += sizeof(struct mp_proc_entry); } fill_mp_bus_entry((struct mp_bus_entry *)p, BUS_ID_ISA, BUS_TYPE_STR_ISA); p += sizeof(struct mp_bus_entry); fill_mp_ioapic_entry((struct mp_ioapic_entry *)p); p += sizeof(struct mp_ioapic_entry); /* I/O interrupt assignment: IOAPIC pin 0 is connected to 8259 ExtInt. */ mpiie = (struct mp_io_intr_entry *)p; memset(mpiie, 0, sizeof(*mpiie)); mpiie->type = ENTRY_TYPE_IO_INTR; mpiie->intr_type = INTR_TYPE_EXTINT; mpiie->dst_ioapic_id = IOAPIC_ID; p += sizeof(*mpiie); /* I/O interrupt assignment for every legacy 8259 interrupt source. */ for ( i = 0; i < 16; i++ ) { if ( i == 2 ) continue; /* skip the slave PIC connection */ mpiie = (struct mp_io_intr_entry *)p; mpiie->type = ENTRY_TYPE_IO_INTR; mpiie->intr_type = INTR_TYPE_INT; mpiie->io_intr_flags = (PCI_ISA_IRQ_MASK & (1U << i)) ? 0xf : 0x0; mpiie->src_bus_id = BUS_ID_ISA; mpiie->src_bus_irq = i; mpiie->dst_ioapic_id = IOAPIC_ID; mpiie->dst_ioapic_intin = (i == 0) ? 2 : i; p += sizeof(*mpiie); } length = p - (char *)base; fill_mp_config_table((struct mp_config_table *)base, length); fill_mpfps(mpfps, (uint32_t)base); return (unsigned long)mpfps; } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */