| 1 | /* |
|---|
| 2 | * mp_tables.c: Dynamically writes MP table info into the ROMBIOS. |
|---|
| 3 | * |
|---|
| 4 | * In order to work with various VCPU counts, this code reads the VCPU count |
|---|
| 5 | * for the HVM partition and creates the correct MP tables for the VCPU count |
|---|
| 6 | * and places the information into a predetermined location set aside in the |
|---|
| 7 | * ROMBIOS during build time. |
|---|
| 8 | * |
|---|
| 9 | * Please note that many of the values, such as the CPU's |
|---|
| 10 | * family/model/stepping, are hard-coded based upon the values that were used |
|---|
| 11 | * in the ROMBIOS and may need to be modified or calculated dynamically to |
|---|
| 12 | * correspond with what an HVM guest's CPUID returns. |
|---|
| 13 | * |
|---|
| 14 | * Travis Betak, travis.betak@amd.com |
|---|
| 15 | * Copyright (c) 2006, AMD. |
|---|
| 16 | * |
|---|
| 17 | * This program is free software; you can redistribute it and/or modify it |
|---|
| 18 | * under the terms and conditions of the GNU General Public License, |
|---|
| 19 | * version 2, as published by the Free Software Foundation. |
|---|
| 20 | * |
|---|
| 21 | * This program is distributed in the hope it will be useful, but WITHOUT |
|---|
| 22 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|---|
| 23 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
|---|
| 24 | * more details. |
|---|
| 25 | * |
|---|
| 26 | * You should have received a copy of the GNU General Public License along with |
|---|
| 27 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple |
|---|
| 28 | * Place - Suite 330, Boston, MA 02111-1307 USA. |
|---|
| 29 | */ |
|---|
| 30 | |
|---|
| 31 | #include "config.h" |
|---|
| 32 | |
|---|
| 33 | /* FIXME find a header that already has types defined!!! */ |
|---|
| 34 | typedef unsigned char uint8_t; |
|---|
| 35 | typedef signed char int8_t; |
|---|
| 36 | typedef unsigned short uint16_t; |
|---|
| 37 | typedef signed short int16_t; |
|---|
| 38 | typedef unsigned int uint32_t; |
|---|
| 39 | typedef signed int int32_t; |
|---|
| 40 | #ifdef __i386__ |
|---|
| 41 | typedef unsigned long long uint64_t; |
|---|
| 42 | typedef signed long long int64_t; |
|---|
| 43 | #else |
|---|
| 44 | typedef unsigned long uint64_t; |
|---|
| 45 | typedef signed long int64_t; |
|---|
| 46 | #endif |
|---|
| 47 | |
|---|
| 48 | /* number of non-processor MP table entries */ |
|---|
| 49 | #define NR_NONPROC_ENTRIES 18 |
|---|
| 50 | |
|---|
| 51 | #define ENTRY_TYPE_PROCESSOR 0 |
|---|
| 52 | #define ENTRY_TYPE_BUS 1 |
|---|
| 53 | #define ENTRY_TYPE_IOAPIC 2 |
|---|
| 54 | #define ENTRY_TYPE_IO_INTR 3 |
|---|
| 55 | #define ENTRY_TYPE_LOCAL_INTR 4 |
|---|
| 56 | |
|---|
| 57 | #define CPU_FLAG_ENABLED 0x01 |
|---|
| 58 | #define CPU_FLAG_BSP 0x02 |
|---|
| 59 | |
|---|
| 60 | /* TODO change this to correspond with what the guest's see's from CPUID */ |
|---|
| 61 | #define CPU_SIG_FAMILY 0x06 |
|---|
| 62 | #define CPU_SIG_MODEL 0x00 |
|---|
| 63 | #define CPU_SIG_STEPPING 0x00 |
|---|
| 64 | #define CPU_SIGNATURE ((CPU_SIG_FAMILY << 8) \ |
|---|
| 65 | | (CPU_SIG_MODEL << 4) \ |
|---|
| 66 | | (CPU_SIG_STEPPING)) |
|---|
| 67 | #define CPU_FEATURE_FPU (1U << 0) |
|---|
| 68 | #define CPU_FEATURE_MCE (1U << 7) |
|---|
| 69 | #define CPU_FEATURE_CX8 (1U << 8) |
|---|
| 70 | #define CPU_FEATURE_APIC (1U << 9) |
|---|
| 71 | #define CPU_FEATURES (CPU_FEATURE_FPU | CPU_FEATURE_APIC) |
|---|
| 72 | |
|---|
| 73 | #define BUS_TYPE_LENGTH 6 |
|---|
| 74 | #define BUS_TYPE_STR_ISA "ISA " |
|---|
| 75 | #define BUS_ID_ISA 0 |
|---|
| 76 | |
|---|
| 77 | #define INTR_TYPE_INT 0 |
|---|
| 78 | #define INTR_TYPE_NMI 1 |
|---|
| 79 | #define INTR_TYPE_SMI 2 |
|---|
| 80 | #define INTR_TYPE_EXTINT 3 |
|---|
| 81 | |
|---|
| 82 | #define INTR_MAX_NR 16 |
|---|
| 83 | |
|---|
| 84 | #include "util.h" |
|---|
| 85 | |
|---|
| 86 | extern int get_vcpu_nr(void); /* for the guest's VCPU count */ |
|---|
| 87 | |
|---|
| 88 | /* |
|---|
| 89 | * The following structures are defined in the MuliProcessor Specifiation v1.4 |
|---|
| 90 | */ |
|---|
| 91 | |
|---|
| 92 | /* MP Floating Pointer Structure */ |
|---|
| 93 | struct mp_floating_pointer_struct { |
|---|
| 94 | uint8_t signature[4]; |
|---|
| 95 | uint32_t mp_table; |
|---|
| 96 | uint8_t length; |
|---|
| 97 | uint8_t revision; |
|---|
| 98 | uint8_t checksum; |
|---|
| 99 | uint8_t feature[5]; |
|---|
| 100 | }; |
|---|
| 101 | |
|---|
| 102 | /* MP Configuration Table */ |
|---|
| 103 | struct mp_config_table { |
|---|
| 104 | uint8_t signature[4]; |
|---|
| 105 | uint16_t length; |
|---|
| 106 | uint8_t revision; |
|---|
| 107 | uint8_t checksum; |
|---|
| 108 | uint8_t oem_id[8]; |
|---|
| 109 | uint8_t vendor_id[12]; |
|---|
| 110 | uint32_t oem_table; |
|---|
| 111 | uint16_t oem_table_sz; |
|---|
| 112 | uint16_t nr_entries; |
|---|
| 113 | uint32_t lapic; |
|---|
| 114 | uint16_t extended_length; |
|---|
| 115 | uint8_t extended_checksum; |
|---|
| 116 | uint8_t reserved; |
|---|
| 117 | }; |
|---|
| 118 | |
|---|
| 119 | /* MP Processor Entry */ |
|---|
| 120 | struct mp_proc_entry { |
|---|
| 121 | uint8_t type; |
|---|
| 122 | uint8_t lapic_id; |
|---|
| 123 | uint8_t lapic_version; |
|---|
| 124 | uint8_t cpu_flags; |
|---|
| 125 | uint32_t cpu_signature; |
|---|
| 126 | uint32_t feature_flags; |
|---|
| 127 | uint8_t reserved[8]; |
|---|
| 128 | }; |
|---|
| 129 | |
|---|
| 130 | /* MP Bus Entry */ |
|---|
| 131 | struct mp_bus_entry { |
|---|
| 132 | uint8_t type; |
|---|
| 133 | uint8_t bus_id; |
|---|
| 134 | uint8_t bus_type_str[6]; |
|---|
| 135 | }; |
|---|
| 136 | |
|---|
| 137 | /* MP IOAPIC Entry */ |
|---|
| 138 | struct mp_ioapic_entry { |
|---|
| 139 | uint8_t type; |
|---|
| 140 | uint8_t ioapic_id; |
|---|
| 141 | uint8_t ioapic_version; |
|---|
| 142 | uint8_t ioapic_flags; |
|---|
| 143 | uint32_t ioapic_addr; |
|---|
| 144 | }; |
|---|
| 145 | |
|---|
| 146 | /* MP IO Interrupt Entry */ |
|---|
| 147 | struct mp_io_intr_entry { |
|---|
| 148 | uint8_t type; |
|---|
| 149 | uint8_t intr_type; |
|---|
| 150 | uint16_t io_intr_flags; |
|---|
| 151 | uint8_t src_bus_id; |
|---|
| 152 | uint8_t src_bus_irq; |
|---|
| 153 | uint8_t dst_ioapic_id; |
|---|
| 154 | uint8_t dst_ioapic_intin; |
|---|
| 155 | }; |
|---|
| 156 | |
|---|
| 157 | /* MP Local Interrupt Entry */ |
|---|
| 158 | struct mp_local_intr_entry { |
|---|
| 159 | uint8_t type; |
|---|
| 160 | uint8_t intr_type; |
|---|
| 161 | uint16_t local_intr_flags; |
|---|
| 162 | uint8_t src_bus_id; |
|---|
| 163 | uint8_t src_bus_irq; |
|---|
| 164 | uint8_t dst_lapic_id; |
|---|
| 165 | uint8_t dst_lapic_lintin; |
|---|
| 166 | }; |
|---|
| 167 | |
|---|
| 168 | |
|---|
| 169 | void fill_mp_config_table(struct mp_config_table *mpct, int length) |
|---|
| 170 | { |
|---|
| 171 | int vcpu_nr, i; |
|---|
| 172 | uint8_t checksum; |
|---|
| 173 | |
|---|
| 174 | vcpu_nr = get_vcpu_nr(); |
|---|
| 175 | |
|---|
| 176 | /* fill in the MP configuration table signature, "PCMP" */ |
|---|
| 177 | mpct->signature[0] = 'P'; |
|---|
| 178 | mpct->signature[1] = 'C'; |
|---|
| 179 | mpct->signature[2] = 'M'; |
|---|
| 180 | mpct->signature[3] = 'P'; |
|---|
| 181 | |
|---|
| 182 | mpct->length = length; |
|---|
| 183 | |
|---|
| 184 | mpct->revision = 4; |
|---|
| 185 | |
|---|
| 186 | /* fill in the OEM ID string, "_HVMCPU_" */ |
|---|
| 187 | mpct->oem_id[0] = '_'; mpct->oem_id[3] = 'M'; mpct->oem_id[6] = 'U'; |
|---|
| 188 | mpct->oem_id[1] = 'H'; mpct->oem_id[4] = 'C'; mpct->oem_id[7] = '_'; |
|---|
| 189 | mpct->oem_id[2] = 'V'; mpct->oem_id[5] = 'P'; |
|---|
| 190 | |
|---|
| 191 | /* fill in the Vendor ID string, "XEN " */ |
|---|
| 192 | mpct->vendor_id[0] = 'X'; mpct->vendor_id[6] = ' '; |
|---|
| 193 | mpct->vendor_id[1] = 'E'; mpct->vendor_id[7] = ' '; |
|---|
| 194 | mpct->vendor_id[2] = 'N'; mpct->vendor_id[8] = ' '; |
|---|
| 195 | mpct->vendor_id[3] = ' '; mpct->vendor_id[9] = ' '; |
|---|
| 196 | mpct->vendor_id[4] = ' '; mpct->vendor_id[10] = ' '; |
|---|
| 197 | mpct->vendor_id[5] = ' '; mpct->vendor_id[11] = ' '; |
|---|
| 198 | |
|---|
| 199 | mpct->oem_table = 0; |
|---|
| 200 | mpct->oem_table_sz = 0; |
|---|
| 201 | |
|---|
| 202 | mpct->nr_entries = vcpu_nr + NR_NONPROC_ENTRIES; |
|---|
| 203 | |
|---|
| 204 | mpct->lapic = LAPIC_BASE_ADDRESS; |
|---|
| 205 | mpct->extended_length = 0; |
|---|
| 206 | mpct->extended_checksum = 0; |
|---|
| 207 | |
|---|
| 208 | /* Finally, fill in the checksum. */ |
|---|
| 209 | mpct->checksum = checksum = 0; |
|---|
| 210 | for ( i = 0; i < length; i++ ) |
|---|
| 211 | checksum += ((uint8_t *)(mpct))[i]; |
|---|
| 212 | mpct->checksum = -checksum; |
|---|
| 213 | } |
|---|
| 214 | |
|---|
| 215 | /* fills in an MP processor entry for VCPU 'vcpu_id' */ |
|---|
| 216 | void fill_mp_proc_entry(struct mp_proc_entry *mppe, int vcpu_id) |
|---|
| 217 | { |
|---|
| 218 | mppe->type = ENTRY_TYPE_PROCESSOR; |
|---|
| 219 | mppe->lapic_id = LAPIC_ID(vcpu_id); |
|---|
| 220 | mppe->lapic_version = 0x11; |
|---|
| 221 | mppe->cpu_flags = CPU_FLAG_ENABLED; |
|---|
| 222 | if ( vcpu_id == 0 ) |
|---|
| 223 | mppe->cpu_flags |= CPU_FLAG_BSP; |
|---|
| 224 | mppe->cpu_signature = CPU_SIGNATURE; |
|---|
| 225 | mppe->feature_flags = CPU_FEATURES; |
|---|
| 226 | } |
|---|
| 227 | |
|---|
| 228 | |
|---|
| 229 | /* fills in an MP bus entry of type 'type' and bus ID 'bus_id' */ |
|---|
| 230 | void fill_mp_bus_entry(struct mp_bus_entry *mpbe, int bus_id, const char *type) |
|---|
| 231 | { |
|---|
| 232 | int i; |
|---|
| 233 | |
|---|
| 234 | mpbe->type = ENTRY_TYPE_BUS; |
|---|
| 235 | mpbe->bus_id = bus_id; |
|---|
| 236 | for ( i = 0; i < BUS_TYPE_LENGTH; i++ ) |
|---|
| 237 | mpbe->bus_type_str[i] = type[i]; /* FIXME length check? */ |
|---|
| 238 | } |
|---|
| 239 | |
|---|
| 240 | |
|---|
| 241 | /* fills in an MP IOAPIC entry for IOAPIC 'ioapic_id' */ |
|---|
| 242 | void fill_mp_ioapic_entry(struct mp_ioapic_entry *mpie) |
|---|
| 243 | { |
|---|
| 244 | mpie->type = ENTRY_TYPE_IOAPIC; |
|---|
| 245 | mpie->ioapic_id = IOAPIC_ID; |
|---|
| 246 | mpie->ioapic_version = IOAPIC_VERSION; |
|---|
| 247 | mpie->ioapic_flags = 1; /* enabled */ |
|---|
| 248 | mpie->ioapic_addr = IOAPIC_BASE_ADDRESS; |
|---|
| 249 | } |
|---|
| 250 | |
|---|
| 251 | |
|---|
| 252 | /* fills in an IO interrupt entry for IOAPIC 'ioapic_id' */ |
|---|
| 253 | void fill_mp_io_intr_entry( |
|---|
| 254 | struct mp_io_intr_entry *mpiie, |
|---|
| 255 | int src_bus_id, int src_bus_irq, int ioapic_id, int dst_ioapic_intin) |
|---|
| 256 | { |
|---|
| 257 | mpiie->type = ENTRY_TYPE_IO_INTR; |
|---|
| 258 | mpiie->intr_type = INTR_TYPE_INT; |
|---|
| 259 | mpiie->io_intr_flags = (PCI_ISA_IRQ_MASK & (1U<<src_bus_irq)) ? 0xf : 0x0; |
|---|
| 260 | mpiie->src_bus_id = src_bus_id; |
|---|
| 261 | mpiie->src_bus_irq = src_bus_irq; |
|---|
| 262 | mpiie->dst_ioapic_id = ioapic_id; |
|---|
| 263 | mpiie->dst_ioapic_intin = dst_ioapic_intin; |
|---|
| 264 | } |
|---|
| 265 | |
|---|
| 266 | |
|---|
| 267 | /* fill in the mp floating processor structure */ |
|---|
| 268 | void fill_mpfps(struct mp_floating_pointer_struct *mpfps, uint32_t mpct) |
|---|
| 269 | { |
|---|
| 270 | int i; |
|---|
| 271 | uint8_t checksum; |
|---|
| 272 | |
|---|
| 273 | |
|---|
| 274 | mpfps->signature[0] = '_'; |
|---|
| 275 | mpfps->signature[1] = 'M'; |
|---|
| 276 | mpfps->signature[2] = 'P'; |
|---|
| 277 | mpfps->signature[3] = '_'; |
|---|
| 278 | |
|---|
| 279 | mpfps->mp_table = mpct; |
|---|
| 280 | mpfps->length = 1; |
|---|
| 281 | mpfps->revision = 4; |
|---|
| 282 | mpfps->checksum = 0; |
|---|
| 283 | for (i = 0; i < 5; ++i) |
|---|
| 284 | mpfps->feature[i] = 0; |
|---|
| 285 | |
|---|
| 286 | /* compute the checksum for our new table */ |
|---|
| 287 | checksum = 0; |
|---|
| 288 | for ( i = 0; i < sizeof(struct mp_floating_pointer_struct); i++ ) |
|---|
| 289 | checksum += ((uint8_t *)(mpfps))[i]; |
|---|
| 290 | mpfps->checksum = -checksum; |
|---|
| 291 | } |
|---|
| 292 | |
|---|
| 293 | |
|---|
| 294 | /* |
|---|
| 295 | * find_mp_table_start - searchs through BIOS memory for '___HVMMP' signature |
|---|
| 296 | * |
|---|
| 297 | * The '___HVMMP' signature is created by the ROMBIOS and designates a chunk |
|---|
| 298 | * of space inside the ROMBIOS that is safe for us to write our MP table info |
|---|
| 299 | */ |
|---|
| 300 | void* get_mp_table_start(void) |
|---|
| 301 | { |
|---|
| 302 | char *bios_mem; |
|---|
| 303 | |
|---|
| 304 | for ( bios_mem = (char *)ROMBIOS_BEGIN; |
|---|
| 305 | bios_mem != (char *)ROMBIOS_END; |
|---|
| 306 | bios_mem++ ) |
|---|
| 307 | { |
|---|
| 308 | if ( strncmp(bios_mem, "___HVMMP", 8) == 0) |
|---|
| 309 | return bios_mem; |
|---|
| 310 | } |
|---|
| 311 | |
|---|
| 312 | return NULL; |
|---|
| 313 | } |
|---|
| 314 | |
|---|
| 315 | |
|---|
| 316 | /* recalculate the new ROMBIOS checksum after adding MP tables */ |
|---|
| 317 | void reset_bios_checksum(void) |
|---|
| 318 | { |
|---|
| 319 | uint32_t i; |
|---|
| 320 | uint8_t checksum; |
|---|
| 321 | |
|---|
| 322 | checksum = 0; |
|---|
| 323 | for (i = 0; i < ROMBIOS_MAXOFFSET; ++i) |
|---|
| 324 | checksum += ((uint8_t *)(ROMBIOS_BEGIN))[i]; |
|---|
| 325 | |
|---|
| 326 | *((uint8_t *)(ROMBIOS_BEGIN + ROMBIOS_MAXOFFSET)) = -checksum; |
|---|
| 327 | } |
|---|
| 328 | |
|---|
| 329 | |
|---|
| 330 | /* create_mp_tables - creates MP tables for the guest based upon config data */ |
|---|
| 331 | void create_mp_tables(void) |
|---|
| 332 | { |
|---|
| 333 | void *mp_table_base; |
|---|
| 334 | char *p; |
|---|
| 335 | int vcpu_nr, i, length; |
|---|
| 336 | |
|---|
| 337 | vcpu_nr = get_vcpu_nr(); |
|---|
| 338 | |
|---|
| 339 | printf("Creating MP tables ...\n"); |
|---|
| 340 | |
|---|
| 341 | /* Find the 'safe' place in ROMBIOS for the MP tables. */ |
|---|
| 342 | mp_table_base = get_mp_table_start(); |
|---|
| 343 | if ( mp_table_base == NULL ) |
|---|
| 344 | { |
|---|
| 345 | printf("Couldn't find start point for MP tables\n"); |
|---|
| 346 | return; |
|---|
| 347 | } |
|---|
| 348 | |
|---|
| 349 | p = mp_table_base + sizeof(struct mp_config_table); |
|---|
| 350 | |
|---|
| 351 | for ( i = 0; i < vcpu_nr; i++ ) |
|---|
| 352 | { |
|---|
| 353 | fill_mp_proc_entry((struct mp_proc_entry *)p, i); |
|---|
| 354 | p += sizeof(struct mp_proc_entry); |
|---|
| 355 | } |
|---|
| 356 | |
|---|
| 357 | fill_mp_bus_entry((struct mp_bus_entry *)p, BUS_ID_ISA, BUS_TYPE_STR_ISA); |
|---|
| 358 | p += sizeof(struct mp_bus_entry); |
|---|
| 359 | |
|---|
| 360 | fill_mp_ioapic_entry((struct mp_ioapic_entry *)p); |
|---|
| 361 | p += sizeof(struct mp_ioapic_entry); |
|---|
| 362 | |
|---|
| 363 | for ( i = 0; i < 16; i++ ) |
|---|
| 364 | { |
|---|
| 365 | if ( i == 2 ) continue; /* skip the slave PIC connection */ |
|---|
| 366 | fill_mp_io_intr_entry((struct mp_io_intr_entry *)p, |
|---|
| 367 | BUS_ID_ISA, i, IOAPIC_ID, (i == 0) ? 2 : i); |
|---|
| 368 | p += sizeof(struct mp_io_intr_entry); |
|---|
| 369 | } |
|---|
| 370 | |
|---|
| 371 | length = p - (char *)mp_table_base; |
|---|
| 372 | |
|---|
| 373 | /* find the next 16-byte boundary to place the mp floating pointer */ |
|---|
| 374 | while ( (unsigned long)p & 0xF ) |
|---|
| 375 | p++; |
|---|
| 376 | |
|---|
| 377 | fill_mpfps((struct mp_floating_pointer_struct *)p, |
|---|
| 378 | (uint32_t)mp_table_base); |
|---|
| 379 | |
|---|
| 380 | fill_mp_config_table((struct mp_config_table *)mp_table_base, length); |
|---|
| 381 | reset_bios_checksum(); |
|---|
| 382 | } |
|---|