/*
* 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, see .
*/
#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:
*/