ia64/xen-unstable

annotate tools/firmware/hvmloader/hvmloader.c @ 13656:480436ef6255

This patch adds a 32bit gateway to the Bochs BIOS. The 32 bit code is
compiled with gcc and linked into the hvmloader as a byte-array.
Hvmloader allocates memory (rounded up to next 64kb) from the e820
table
below 4GB, copies and relocates the 32bit code in the allocated area
and
copies a jumptable (located in a section '.biosjumptable') pointing to
the 'exported' functions into the Bochs BIOS's memory area to link the
two code sections. The memory area has been reserved and can be
identified with the signature '___JMPT'.

In the Bochs BIOS only stub functions are provided. These load the
index
of a particular function in the jump table, switch to protected mode
and
call the function in the high memory area. The stack is prepared such
that functions compiled by gcc can just pick the parameters from the
stack as usual - this means that the 16bit real-mode return address is
taken off the stack. The stub functions should have the same signature
as those in 32bit space. For ABI compatibility reasons parameters
inside
the Bochs BIOS stubs should all be 32bit wide.

This patch includes a test function that calls three gcc-compiled
functions in the high memory area and displays their success status.
Simple tests are done doing multiplication and addition of 32-bit
numbers and reading and modification of a static variable. These
functions test the interface and the relocation code. The test code is
removed in patch part 3.

Signed-off-by: Stefan Berger <stefanb@us.ibm.com>
author kaf24@localhost.localdomain
date Fri Jan 26 16:30:48 2007 +0000 (2007-01-26)
parents b258c7587d8d
children 1721f90e1422
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));
kaf24@13656 317 highbios_setup();
kfraser@12548 318
kfraser@12548 319 apic_setup();
kfraser@12554 320 pci_setup();
kaf24@12574 321
kfraser@12600 322 if ( (get_vcpu_nr() > 1) || get_apic_mode() )
kaf24@12574 323 create_mp_tables();
kfraser@12548 324
kfraser@12548 325 if ( cirrus_check() )
kfraser@12548 326 {
kfraser@12554 327 printf("Loading Cirrus VGABIOS ...\n");
kfraser@12548 328 memcpy((void *)VGABIOS_PHYSICAL_ADDRESS,
kfraser@12548 329 vgabios_cirrusvga, sizeof(vgabios_cirrusvga));
kfraser@12548 330 }
kfraser@12548 331 else
kfraser@12548 332 {
kfraser@12554 333 printf("Loading Standard VGABIOS ...\n");
kfraser@12548 334 memcpy((void *)VGABIOS_PHYSICAL_ADDRESS,
kfraser@12548 335 vgabios_stdvga, sizeof(vgabios_stdvga));
kfraser@12548 336 }
kfraser@12548 337
Tim@13140 338 if ( must_load_nic() )
Tim@13140 339 {
Tim@13140 340 printf("Loading ETHERBOOT ...\n");
Tim@13140 341 memcpy((void *)ETHERBOOT_PHYSICAL_ADDRESS,
Tim@13140 342 etherboot, sizeof(etherboot));
Tim@13140 343 }
Tim@13140 344
kfraser@12548 345 if ( get_acpi_enabled() != 0 )
kfraser@12548 346 {
kfraser@12554 347 printf("Loading ACPI ...\n");
kaf24@12574 348 acpi_sz = acpi_build_tables((uint8_t *)ACPI_PHYSICAL_ADDRESS);
kfraser@12634 349 ASSERT((ACPI_PHYSICAL_ADDRESS + acpi_sz) <= 0xF0000);
kfraser@12548 350 }
kfraser@12548 351
kfraser@12548 352 if ( check_amd() )
kfraser@12548 353 {
kfraser@12548 354 /* AMD implies this is SVM */
kfraser@12554 355 printf("SVM go ...\n");
kfraser@12548 356 vmmcall(SVM_VMMCALL_RESET_TO_REALMODE, 0, 0, 0, 0, 0);
kfraser@12548 357 }
kfraser@12548 358 else
kfraser@12548 359 {
kfraser@12554 360 printf("Loading VMXAssist ...\n");
kfraser@12548 361 memcpy((void *)VMXASSIST_PHYSICAL_ADDRESS,
kfraser@12548 362 vmxassist, sizeof(vmxassist));
kfraser@12548 363
kfraser@12554 364 printf("VMX go ...\n");
kfraser@12548 365 __asm__ __volatile__(
kfraser@12548 366 "jmp *%%eax"
kfraser@12548 367 : : "a" (VMXASSIST_PHYSICAL_ADDRESS), "d" (0)
kfraser@12548 368 );
kfraser@12548 369 }
kfraser@12548 370
kfraser@12554 371 printf("Failed to invoke ROMBIOS\n");
kfraser@12548 372 return 0;
kfraser@12548 373 }
kfraser@12548 374
kfraser@12548 375 /*
kfraser@12548 376 * Local variables:
kfraser@12548 377 * mode: C
kfraser@12548 378 * c-set-style: "BSD"
kfraser@12548 379 * c-basic-offset: 4
kfraser@12548 380 * tab-width: 4
kfraser@12548 381 * indent-tabs-mode: nil
kfraser@12548 382 * End:
kfraser@12548 383 */