ia64/xen-unstable

annotate tools/firmware/hvmloader/hvmloader.c @ 13140:b258c7587d8d

[HVM] Add Etherboot option ROM, for network-booting HVM guests.
Signed-off-by: Tim Deegan <Tim.Deegan@xensource.com>
author Tim Deegan <Tim.Deegan@xensource.com>
date Wed Dec 20 11:59:54 2006 +0000 (2006-12-20)
parents 514ed4f0e5da
children 480436ef6255
rev   line source
kaf24@8708 1 /*
kaf24@8708 2 * hvmloader.c: HVM ROMBIOS/VGABIOS/ACPI/VMXAssist image loader.
kaf24@8708 3 *
kaf24@8708 4 * Leendert van Doorn, leendert@watson.ibm.com
kaf24@8708 5 * Copyright (c) 2005, International Business Machines Corporation.
kaf24@8708 6 *
kfraser@12554 7 * Copyright (c) 2006, Keir Fraser, XenSource Inc.
kfraser@12554 8 *
kaf24@8708 9 * This program is free software; you can redistribute it and/or modify it
kaf24@8708 10 * under the terms and conditions of the GNU General Public License,
kaf24@8708 11 * version 2, as published by the Free Software Foundation.
kaf24@8708 12 *
kaf24@8708 13 * This program is distributed in the hope it will be useful, but WITHOUT
kaf24@8708 14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
kaf24@8708 15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
kaf24@8708 16 * more details.
kaf24@8708 17 *
kaf24@8708 18 * You should have received a copy of the GNU General Public License along with
kaf24@8708 19 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
kaf24@8708 20 * Place - Suite 330, Boston, MA 02111-1307 USA.
kaf24@8708 21 */
kaf24@8708 22 #include "roms.h"
kfraser@12057 23 #include "acpi/acpi2_0.h" /* for ACPI_PHYSICAL_ADDRESS */
kfraser@10976 24 #include "hypercall.h"
kfraser@10976 25 #include "util.h"
kfraser@11115 26 #include "smbios.h"
kfraser@12548 27 #include "config.h"
kaf24@12571 28 #include "apic_regs.h"
kfraser@12554 29 #include "pci_regs.h"
kfraser@10976 30 #include <xen/version.h>
kfraser@11081 31 #include <xen/hvm/params.h>
kfraser@12554 32 #include <xen/hvm/e820.h>
kaf24@8708 33
kaf24@8708 34 /* memory map */
kfraser@12548 35 #define HYPERCALL_PHYSICAL_ADDRESS 0x00080000
kfraser@12548 36 #define VGABIOS_PHYSICAL_ADDRESS 0x000C0000
Tim@13140 37 #define ETHERBOOT_PHYSICAL_ADDRESS 0x000C8000
kfraser@12548 38 #define VMXASSIST_PHYSICAL_ADDRESS 0x000D0000
kfraser@12548 39 #define ROMBIOS_PHYSICAL_ADDRESS 0x000F0000
kaf24@8708 40
kaf24@8708 41 /* invoke SVM's paged realmode support */
kfraser@12548 42 #define SVM_VMMCALL_RESET_TO_REALMODE 0x80000001
kaf24@8708 43
kaf24@8708 44 /*
kaf24@8708 45 * C runtime start off
kaf24@8708 46 */
kaf24@8708 47 asm(
kfraser@12548 48 " .text \n"
kfraser@12548 49 " .globl _start \n"
kfraser@12548 50 "_start: \n"
kfraser@12548 51 " cld \n"
kfraser@12548 52 " cli \n"
kfraser@12548 53 " lgdt gdt_desr \n"
kfraser@12548 54 " movl $stack_top, %esp \n"
kfraser@12548 55 " movl %esp, %ebp \n"
kfraser@12548 56 " call main \n"
kfraser@12548 57 " ud2 \n"
kfraser@12548 58 " \n"
kfraser@12548 59 "gdt_desr: \n"
kfraser@12548 60 " .word gdt_end - gdt - 1 \n"
kfraser@12548 61 " .long gdt \n"
kfraser@12548 62 " \n"
kfraser@12548 63 " .align 8 \n"
kfraser@12548 64 "gdt: \n"
kfraser@12548 65 " .quad 0x0000000000000000 \n"
kfraser@12548 66 " .quad 0x00CF92000000FFFF \n"
kfraser@12548 67 " .quad 0x00CF9A000000FFFF \n"
kfraser@12548 68 "gdt_end: \n"
kfraser@12548 69 " \n"
kfraser@12548 70 " .bss \n"
kfraser@12548 71 " .align 8 \n"
kfraser@12548 72 "stack: \n"
kfraser@12548 73 " .skip 0x4000 \n"
kfraser@12548 74 "stack_top: \n"
kfraser@12548 75 );
kaf24@8708 76
kaf24@10575 77 extern void create_mp_tables(void);
kaf24@8708 78
kfraser@10976 79 static int
kaf24@8708 80 cirrus_check(void)
kaf24@8708 81 {
kfraser@12548 82 outw(0x3C4, 0x9206);
kfraser@12548 83 return inb(0x3C5) == 0x12;
kaf24@8708 84 }
kaf24@8708 85
kfraser@10976 86 static int
kfraser@10893 87 vmmcall(int function, int edi, int esi, int edx, int ecx, int ebx)
kaf24@8708 88 {
kfraser@12548 89 int eax;
kaf24@8708 90
kfraser@12548 91 __asm__ __volatile__ (
kfraser@12548 92 ".byte 0x0F,0x01,0xD9"
kfraser@12548 93 : "=a" (eax)
kfraser@12548 94 : "a"(function),
kfraser@12548 95 "b"(ebx), "c"(ecx), "d"(edx), "D"(edi), "S"(esi) );
kfraser@12548 96 return eax;
kaf24@8708 97 }
kaf24@8708 98
kfraser@10976 99 static int
kaf24@8708 100 check_amd(void)
kaf24@8708 101 {
kfraser@12548 102 char id[12];
kaf24@8708 103
kfraser@12548 104 __asm__ __volatile__ (
kfraser@12548 105 "cpuid"
kfraser@12548 106 : "=b" (*(int *)(&id[0])),
kfraser@12548 107 "=c" (*(int *)(&id[8])),
kfraser@12548 108 "=d" (*(int *)(&id[4]))
kfraser@12548 109 : "a" (0) );
kfraser@12548 110 return __builtin_memcmp(id, "AuthenticAMD", 12) == 0;
kaf24@8708 111 }
kaf24@8708 112
kfraser@10976 113 static void
kfraser@10976 114 wrmsr(uint32_t idx, uint64_t v)
kfraser@10976 115 {
kfraser@12548 116 __asm__ __volatile__ (
kfraser@12548 117 "wrmsr"
kfraser@12548 118 : : "c" (idx), "a" ((uint32_t)v), "d" ((uint32_t)(v>>32)) );
kfraser@10976 119 }
kfraser@10976 120
kfraser@10976 121 static void
kfraser@10976 122 init_hypercalls(void)
kfraser@10976 123 {
kfraser@12548 124 uint32_t eax, ebx, ecx, edx;
kfraser@12548 125 unsigned long i;
kfraser@12554 126 char signature[13];
kfraser@12548 127 xen_extraversion_t extraversion;
kfraser@10976 128
kfraser@12548 129 cpuid(0x40000000, &eax, &ebx, &ecx, &edx);
kfraser@10976 130
kfraser@12548 131 *(uint32_t *)(signature + 0) = ebx;
kfraser@12548 132 *(uint32_t *)(signature + 4) = ecx;
kfraser@12548 133 *(uint32_t *)(signature + 8) = edx;
kfraser@12548 134 signature[12] = '\0';
kfraser@10976 135
kfraser@12548 136 if ( strcmp("XenVMMXenVMM", signature) || (eax < 0x40000002) )
kfraser@12548 137 {
kfraser@12554 138 printf("FATAL: Xen hypervisor not detected\n");
kfraser@12548 139 __asm__ __volatile__( "ud2" );
kfraser@12548 140 }
kfraser@10976 141
kfraser@12554 142 /* Fill in hypercall transfer pages. */
kfraser@12548 143 cpuid(0x40000002, &eax, &ebx, &ecx, &edx);
kfraser@12548 144 for ( i = 0; i < eax; i++ )
kfraser@12548 145 wrmsr(ebx, HYPERCALL_PHYSICAL_ADDRESS + (i << 12) + i);
kfraser@12548 146
kfraser@12554 147 /* Print version information. */
kfraser@12554 148 cpuid(0x40000001, &eax, &ebx, &ecx, &edx);
kfraser@12548 149 hypercall_xen_version(XENVER_extraversion, extraversion);
kfraser@12554 150 printf("Detected Xen v%u.%u%s\n", eax >> 16, eax & 0xffff, extraversion);
kfraser@10976 151 }
kfraser@10976 152
kfraser@12548 153 static void apic_setup(void)
kaf24@8708 154 {
kaf24@12571 155 /* Set the IOAPIC ID to tha static value used in the MP/ACPI tables. */
kaf24@12571 156 ioapic_write(0x00, IOAPIC_ID);
kaf24@8708 157
kaf24@12571 158 /* Set up Virtual Wire mode. */
kaf24@12571 159 lapic_write(APIC_SPIV, APIC_SPIV_APIC_ENABLED | 0xFF);
kaf24@12571 160 lapic_write(APIC_LVT0, APIC_MODE_EXTINT << 8);
kaf24@12571 161 lapic_write(APIC_LVT1, APIC_MODE_NMI << 8);
kaf24@8708 162 }
kaf24@8708 163
kfraser@12554 164 static void pci_setup(void)
kfraser@12554 165 {
kfraser@12554 166 uint32_t devfn, bar_reg, bar_data, bar_sz, cmd;
kfraser@12554 167 uint32_t *base, io_base = 0xc000, mem_base = HVM_BELOW_4G_MMIO_START;
kfraser@12554 168 uint16_t class, vendor_id, device_id;
kfraser@12554 169 unsigned int bar, pin, link, isa_irq;
kfraser@12554 170
kfraser@12554 171 /* Program PCI-ISA bridge with appropriate link routes. */
kfraser@12554 172 link = 0;
kfraser@12554 173 for ( isa_irq = 0; isa_irq < 15; isa_irq++ )
kfraser@12554 174 {
kfraser@12554 175 if ( !(PCI_ISA_IRQ_MASK & (1U << isa_irq)) )
kfraser@12554 176 continue;
kfraser@12554 177 pci_writeb(PCI_ISA_DEVFN, 0x60 + link, isa_irq);
kfraser@12554 178 printf("PCI-ISA link %u routed to IRQ%u\n", link, isa_irq);
kfraser@12554 179 if ( link++ == 4 )
kfraser@12554 180 break;
kfraser@12554 181 }
kfraser@12554 182
kfraser@12554 183 /* Program ELCR to match PCI-wired IRQs. */
kfraser@12554 184 outb(0x4d0, (uint8_t)(PCI_ISA_IRQ_MASK >> 0));
kfraser@12554 185 outb(0x4d1, (uint8_t)(PCI_ISA_IRQ_MASK >> 8));
kfraser@12554 186
kfraser@12554 187 /* Scan the PCI bus and map resources. */
kfraser@12554 188 for ( devfn = 0; devfn < 128; devfn++ )
kfraser@12554 189 {
kfraser@12554 190 class = pci_readw(devfn, PCI_CLASS_DEVICE);
kfraser@12554 191 vendor_id = pci_readw(devfn, PCI_VENDOR_ID);
kfraser@12554 192 device_id = pci_readw(devfn, PCI_DEVICE_ID);
kfraser@12554 193 if ( (vendor_id == 0xffff) && (device_id == 0xffff) )
kfraser@12554 194 continue;
kfraser@12554 195
kfraser@12554 196 ASSERT((devfn != PCI_ISA_DEVFN) ||
kfraser@12554 197 ((vendor_id == 0x8086) && (device_id == 0x7000)));
kfraser@12554 198
kfraser@12554 199 switch ( class )
kfraser@12554 200 {
kfraser@12554 201 case 0x0680:
kfraser@12554 202 ASSERT((vendor_id == 0x8086) && (device_id == 0x7113));
kfraser@12554 203 /*
kfraser@12554 204 * PIIX4 ACPI PM. Special device with special PCI config space.
kfraser@12554 205 * No ordinary BARs.
kfraser@12554 206 */
kfraser@12554 207 pci_writew(devfn, 0x20, 0x0000); /* No smb bus IO enable */
kfraser@12554 208 pci_writew(devfn, 0x22, 0x0000);
kfraser@12554 209 pci_writew(devfn, 0x3c, 0x0009); /* Hardcoded IRQ9 */
kfraser@12554 210 pci_writew(devfn, 0x3d, 0x0001);
kfraser@12554 211 break;
kfraser@12554 212 case 0x0101:
kfraser@12554 213 /* PIIX3 IDE */
kfraser@12554 214 ASSERT((vendor_id == 0x8086) && (device_id == 0x7010));
kfraser@12554 215 pci_writew(devfn, 0x40, 0x8000); /* enable IDE0 */
kfraser@12554 216 pci_writew(devfn, 0x42, 0x8000); /* enable IDE1 */
kfraser@12554 217 /* fall through */
kfraser@12554 218 default:
kfraser@12554 219 /* Default memory mappings. */
kfraser@12554 220 for ( bar = 0; bar < 7; bar++ )
kfraser@12554 221 {
kfraser@12554 222 bar_reg = PCI_BASE_ADDRESS_0 + 4*bar;
kfraser@12554 223 if ( bar == 6 )
kfraser@12554 224 bar_reg = PCI_ROM_ADDRESS;
kfraser@12554 225
kfraser@12554 226 bar_data = pci_readl(devfn, bar_reg);
kfraser@12554 227
kfraser@12554 228 pci_writel(devfn, bar_reg, ~0);
kfraser@12554 229 bar_sz = pci_readl(devfn, bar_reg);
kfraser@12554 230 if ( bar_sz == 0 )
kfraser@12554 231 continue;
kfraser@12554 232
kfraser@12554 233 if ( (bar_data & PCI_BASE_ADDRESS_SPACE) ==
kfraser@12554 234 PCI_BASE_ADDRESS_SPACE_MEMORY )
kfraser@12554 235 {
kfraser@12554 236 base = &mem_base;
kfraser@12554 237 bar_sz &= PCI_BASE_ADDRESS_MEM_MASK;
kfraser@12554 238 bar_data &= ~PCI_BASE_ADDRESS_MEM_MASK;
kfraser@12554 239 }
kfraser@12554 240 else
kfraser@12554 241 {
kfraser@12554 242 base = &io_base;
kfraser@12554 243 bar_sz &= PCI_BASE_ADDRESS_IO_MASK & 0xffff;
kfraser@12554 244 bar_data &= ~PCI_BASE_ADDRESS_IO_MASK;
kfraser@12554 245 }
kfraser@12554 246 bar_sz &= ~(bar_sz - 1);
kfraser@12554 247
kfraser@12554 248 *base = (*base + bar_sz - 1) & ~(bar_sz - 1);
kfraser@12554 249 bar_data |= *base;
kfraser@12554 250 *base += bar_sz;
kfraser@12554 251
kfraser@12554 252 pci_writel(devfn, bar_reg, bar_data);
kfraser@12554 253 printf("pci dev %02x:%x bar %02x size %08x: %08x\n",
kfraser@12554 254 devfn>>3, devfn&7, bar_reg, bar_sz, bar_data);
kfraser@12554 255
kfraser@12554 256 /* Now enable the memory or I/O mapping. */
kfraser@12554 257 cmd = pci_readw(devfn, PCI_COMMAND);
kfraser@12554 258 if ( (bar_reg == PCI_ROM_ADDRESS) ||
kfraser@12554 259 ((bar_data & PCI_BASE_ADDRESS_SPACE) ==
kfraser@12554 260 PCI_BASE_ADDRESS_SPACE_MEMORY) )
kfraser@12554 261 cmd |= PCI_COMMAND_MEMORY;
kfraser@12554 262 else
kfraser@12554 263 cmd |= PCI_COMMAND_IO;
kfraser@12554 264 pci_writew(devfn, PCI_COMMAND, cmd);
kfraser@12554 265 }
kfraser@12554 266 break;
kfraser@12554 267 }
kfraser@12554 268
kfraser@12554 269 /* Map the interrupt. */
kfraser@12554 270 pin = pci_readb(devfn, PCI_INTERRUPT_PIN);
kfraser@12554 271 if ( pin != 0 )
kfraser@12554 272 {
kfraser@12554 273 /* This is the barber's pole mapping used by Xen. */
kfraser@12554 274 link = ((pin - 1) + (devfn >> 3)) & 3;
kfraser@12554 275 isa_irq = pci_readb(PCI_ISA_DEVFN, 0x60 + link);
kfraser@12554 276 pci_writeb(devfn, PCI_INTERRUPT_LINE, isa_irq);
kfraser@12554 277 printf("pci dev %02x:%x INT%c->IRQ%u\n",
kfraser@12554 278 devfn>>3, devfn&7, 'A'+pin-1, isa_irq);
kfraser@12554 279 }
kfraser@12554 280 }
kfraser@12554 281 }
kfraser@12554 282
Tim@13140 283 static
Tim@13140 284 int must_load_nic(void)
Tim@13140 285 {
Tim@13140 286 /* If the network card is in the boot order, load the Etherboot
Tim@13140 287 * option ROM. Read the boot order bytes from CMOS and check
Tim@13140 288 * if any of them are 0x4. */
Tim@13140 289 uint8_t boot_order;
Tim@13140 290
Tim@13140 291 /* Read CMOS register 0x3d (boot choices 0 and 1) */
Tim@13140 292 outb(0x70, 0x3d);
Tim@13140 293 boot_order = inb(0x71);
Tim@13140 294 if ( (boot_order & 0xf) == 0x4 || (boot_order & 0xf0) == 0x40 )
Tim@13140 295 return 1;
Tim@13140 296 /* Read CMOS register 0x38 (boot choice 2 and FDD test flag) */
Tim@13140 297 outb(0x70, 0x38);
Tim@13140 298 boot_order = inb(0x71);
Tim@13140 299 if ( (boot_order & 0xf0) == 0x40 )
Tim@13140 300 return 1;
Tim@13140 301 return 0;
Tim@13140 302 }
Tim@13140 303
kfraser@12548 304 int main(void)
kfraser@12548 305 {
kaf24@12574 306 int acpi_sz;
kaf24@12574 307
kfraser@12554 308 printf("HVM Loader\n");
kfraser@12548 309
kfraser@12548 310 init_hypercalls();
kfraser@12548 311
kfraser@12554 312 printf("Writing SMBIOS tables ...\n");
kfraser@12548 313 hvm_write_smbios_tables();
kfraser@12548 314
kfraser@12554 315 printf("Loading ROMBIOS ...\n");
kfraser@12548 316 memcpy((void *)ROMBIOS_PHYSICAL_ADDRESS, rombios, sizeof(rombios));
kfraser@12548 317
kfraser@12548 318 apic_setup();
kfraser@12554 319 pci_setup();
kaf24@12574 320
kfraser@12600 321 if ( (get_vcpu_nr() > 1) || get_apic_mode() )
kaf24@12574 322 create_mp_tables();
kfraser@12548 323
kfraser@12548 324 if ( cirrus_check() )
kfraser@12548 325 {
kfraser@12554 326 printf("Loading Cirrus VGABIOS ...\n");
kfraser@12548 327 memcpy((void *)VGABIOS_PHYSICAL_ADDRESS,
kfraser@12548 328 vgabios_cirrusvga, sizeof(vgabios_cirrusvga));
kfraser@12548 329 }
kfraser@12548 330 else
kfraser@12548 331 {
kfraser@12554 332 printf("Loading Standard VGABIOS ...\n");
kfraser@12548 333 memcpy((void *)VGABIOS_PHYSICAL_ADDRESS,
kfraser@12548 334 vgabios_stdvga, sizeof(vgabios_stdvga));
kfraser@12548 335 }
kfraser@12548 336
Tim@13140 337 if ( must_load_nic() )
Tim@13140 338 {
Tim@13140 339 printf("Loading ETHERBOOT ...\n");
Tim@13140 340 memcpy((void *)ETHERBOOT_PHYSICAL_ADDRESS,
Tim@13140 341 etherboot, sizeof(etherboot));
Tim@13140 342 }
Tim@13140 343
kfraser@12548 344 if ( get_acpi_enabled() != 0 )
kfraser@12548 345 {
kfraser@12554 346 printf("Loading ACPI ...\n");
kaf24@12574 347 acpi_sz = acpi_build_tables((uint8_t *)ACPI_PHYSICAL_ADDRESS);
kfraser@12634 348 ASSERT((ACPI_PHYSICAL_ADDRESS + acpi_sz) <= 0xF0000);
kfraser@12548 349 }
kfraser@12548 350
kfraser@12548 351 if ( check_amd() )
kfraser@12548 352 {
kfraser@12548 353 /* AMD implies this is SVM */
kfraser@12554 354 printf("SVM go ...\n");
kfraser@12548 355 vmmcall(SVM_VMMCALL_RESET_TO_REALMODE, 0, 0, 0, 0, 0);
kfraser@12548 356 }
kfraser@12548 357 else
kfraser@12548 358 {
kfraser@12554 359 printf("Loading VMXAssist ...\n");
kfraser@12548 360 memcpy((void *)VMXASSIST_PHYSICAL_ADDRESS,
kfraser@12548 361 vmxassist, sizeof(vmxassist));
kfraser@12548 362
kfraser@12554 363 printf("VMX go ...\n");
kfraser@12548 364 __asm__ __volatile__(
kfraser@12548 365 "jmp *%%eax"
kfraser@12548 366 : : "a" (VMXASSIST_PHYSICAL_ADDRESS), "d" (0)
kfraser@12548 367 );
kfraser@12548 368 }
kfraser@12548 369
kfraser@12554 370 printf("Failed to invoke ROMBIOS\n");
kfraser@12548 371 return 0;
kfraser@12548 372 }
kfraser@12548 373
kfraser@12548 374 /*
kfraser@12548 375 * Local variables:
kfraser@12548 376 * mode: C
kfraser@12548 377 * c-set-style: "BSD"
kfraser@12548 378 * c-basic-offset: 4
kfraser@12548 379 * tab-width: 4
kfraser@12548 380 * indent-tabs-mode: nil
kfraser@12548 381 * End:
kfraser@12548 382 */