ia64/xen-unstable

annotate tools/firmware/hvmloader/hvmloader.c @ 12554:a8d31d5ce258

[HVM] Move PCI and PCI-ISA bridge setup to hvmloader.
Signed-off-by: Keir Fraser <keir@xensource.com>
author kfraser@localhost.localdomain
date Wed Nov 22 17:50:20 2006 +0000 (2006-11-22)
parents 40a61d01e9dc
children d37b210bb8a7
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@12057 26 #include "acpi_utils.h"
kfraser@11115 27 #include "smbios.h"
kfraser@12548 28 #include "config.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
kfraser@12548 37 #define VMXASSIST_PHYSICAL_ADDRESS 0x000D0000
kfraser@12548 38 #define ROMBIOS_PHYSICAL_ADDRESS 0x000F0000
kaf24@8708 39
kaf24@8708 40 /* invoke SVM's paged realmode support */
kfraser@12548 41 #define SVM_VMMCALL_RESET_TO_REALMODE 0x80000001
kaf24@8708 42
kaf24@8708 43 /*
kaf24@8708 44 * C runtime start off
kaf24@8708 45 */
kaf24@8708 46 asm(
kfraser@12548 47 " .text \n"
kfraser@12548 48 " .globl _start \n"
kfraser@12548 49 "_start: \n"
kfraser@12548 50 " cld \n"
kfraser@12548 51 " cli \n"
kfraser@12548 52 " lgdt gdt_desr \n"
kfraser@12548 53 " movl $stack_top, %esp \n"
kfraser@12548 54 " movl %esp, %ebp \n"
kfraser@12548 55 " call main \n"
kfraser@12548 56 " ud2 \n"
kfraser@12548 57 " \n"
kfraser@12548 58 "gdt_desr: \n"
kfraser@12548 59 " .word gdt_end - gdt - 1 \n"
kfraser@12548 60 " .long gdt \n"
kfraser@12548 61 " \n"
kfraser@12548 62 " .align 8 \n"
kfraser@12548 63 "gdt: \n"
kfraser@12548 64 " .quad 0x0000000000000000 \n"
kfraser@12548 65 " .quad 0x00CF92000000FFFF \n"
kfraser@12548 66 " .quad 0x00CF9A000000FFFF \n"
kfraser@12548 67 "gdt_end: \n"
kfraser@12548 68 " \n"
kfraser@12548 69 " .bss \n"
kfraser@12548 70 " .align 8 \n"
kfraser@12548 71 "stack: \n"
kfraser@12548 72 " .skip 0x4000 \n"
kfraser@12548 73 "stack_top: \n"
kfraser@12548 74 );
kaf24@8708 75
kaf24@8708 76 extern int get_acpi_enabled(void);
kaf24@8708 77 extern int acpi_madt_update(unsigned char* acpi_start);
kaf24@10575 78 extern void create_mp_tables(void);
kaf24@10575 79 struct hvm_info_table *get_hvm_info_table(void);
kaf24@8708 80
kfraser@10976 81 static int
kaf24@8708 82 cirrus_check(void)
kaf24@8708 83 {
kfraser@12548 84 outw(0x3C4, 0x9206);
kfraser@12548 85 return inb(0x3C5) == 0x12;
kaf24@8708 86 }
kaf24@8708 87
kfraser@10976 88 static int
kfraser@10893 89 vmmcall(int function, int edi, int esi, int edx, int ecx, int ebx)
kaf24@8708 90 {
kfraser@12548 91 int eax;
kaf24@8708 92
kfraser@12548 93 __asm__ __volatile__ (
kfraser@12548 94 ".byte 0x0F,0x01,0xD9"
kfraser@12548 95 : "=a" (eax)
kfraser@12548 96 : "a"(function),
kfraser@12548 97 "b"(ebx), "c"(ecx), "d"(edx), "D"(edi), "S"(esi) );
kfraser@12548 98 return eax;
kaf24@8708 99 }
kaf24@8708 100
kfraser@10976 101 static int
kaf24@8708 102 check_amd(void)
kaf24@8708 103 {
kfraser@12548 104 char id[12];
kaf24@8708 105
kfraser@12548 106 __asm__ __volatile__ (
kfraser@12548 107 "cpuid"
kfraser@12548 108 : "=b" (*(int *)(&id[0])),
kfraser@12548 109 "=c" (*(int *)(&id[8])),
kfraser@12548 110 "=d" (*(int *)(&id[4]))
kfraser@12548 111 : "a" (0) );
kfraser@12548 112 return __builtin_memcmp(id, "AuthenticAMD", 12) == 0;
kaf24@8708 113 }
kaf24@8708 114
kfraser@10976 115 static void
kfraser@10976 116 wrmsr(uint32_t idx, uint64_t v)
kfraser@10976 117 {
kfraser@12548 118 __asm__ __volatile__ (
kfraser@12548 119 "wrmsr"
kfraser@12548 120 : : "c" (idx), "a" ((uint32_t)v), "d" ((uint32_t)(v>>32)) );
kfraser@10976 121 }
kfraser@10976 122
kfraser@10976 123 static void
kfraser@10976 124 init_hypercalls(void)
kfraser@10976 125 {
kfraser@12548 126 uint32_t eax, ebx, ecx, edx;
kfraser@12548 127 unsigned long i;
kfraser@12554 128 char signature[13];
kfraser@12548 129 xen_extraversion_t extraversion;
kfraser@10976 130
kfraser@12548 131 cpuid(0x40000000, &eax, &ebx, &ecx, &edx);
kfraser@10976 132
kfraser@12548 133 *(uint32_t *)(signature + 0) = ebx;
kfraser@12548 134 *(uint32_t *)(signature + 4) = ecx;
kfraser@12548 135 *(uint32_t *)(signature + 8) = edx;
kfraser@12548 136 signature[12] = '\0';
kfraser@10976 137
kfraser@12548 138 if ( strcmp("XenVMMXenVMM", signature) || (eax < 0x40000002) )
kfraser@12548 139 {
kfraser@12554 140 printf("FATAL: Xen hypervisor not detected\n");
kfraser@12548 141 __asm__ __volatile__( "ud2" );
kfraser@12548 142 }
kfraser@10976 143
kfraser@12554 144 /* Fill in hypercall transfer pages. */
kfraser@12548 145 cpuid(0x40000002, &eax, &ebx, &ecx, &edx);
kfraser@12548 146 for ( i = 0; i < eax; i++ )
kfraser@12548 147 wrmsr(ebx, HYPERCALL_PHYSICAL_ADDRESS + (i << 12) + i);
kfraser@12548 148
kfraser@12554 149 /* Print version information. */
kfraser@12554 150 cpuid(0x40000001, &eax, &ebx, &ecx, &edx);
kfraser@12548 151 hypercall_xen_version(XENVER_extraversion, extraversion);
kfraser@12554 152 printf("Detected Xen v%u.%u%s\n", eax >> 16, eax & 0xffff, extraversion);
kfraser@10976 153 }
kfraser@10976 154
kfraser@12548 155 static void apic_setup(void)
kaf24@8708 156 {
kfraser@12548 157 volatile uint32_t *ioregsel;
kfraser@12548 158 volatile uint32_t *iowin;
kaf24@8708 159
kfraser@12548 160 /* IOAPIC memory-mapped access window registers. */
kfraser@12548 161 ioregsel = (volatile uint32_t *)(IOAPIC_BASE_ADDRESS + 0x00);
kfraser@12548 162 iowin = (volatile uint32_t *)(IOAPIC_BASE_ADDRESS + 0x10);
kaf24@8708 163
kfraser@12548 164 /* Set the IOAPIC ID to tha static value used in the MP/ACPI tables. */
kfraser@12548 165 *ioregsel = 0;
kfraser@12548 166 *iowin = IOAPIC_ID;
kaf24@8708 167 }
kaf24@8708 168
kfraser@12554 169 static void pci_setup(void)
kfraser@12554 170 {
kfraser@12554 171 uint32_t devfn, bar_reg, bar_data, bar_sz, cmd;
kfraser@12554 172 uint32_t *base, io_base = 0xc000, mem_base = HVM_BELOW_4G_MMIO_START;
kfraser@12554 173 uint16_t class, vendor_id, device_id;
kfraser@12554 174 unsigned int bar, pin, link, isa_irq;
kfraser@12554 175
kfraser@12554 176 /* Program PCI-ISA bridge with appropriate link routes. */
kfraser@12554 177 link = 0;
kfraser@12554 178 for ( isa_irq = 0; isa_irq < 15; isa_irq++ )
kfraser@12554 179 {
kfraser@12554 180 if ( !(PCI_ISA_IRQ_MASK & (1U << isa_irq)) )
kfraser@12554 181 continue;
kfraser@12554 182 pci_writeb(PCI_ISA_DEVFN, 0x60 + link, isa_irq);
kfraser@12554 183 printf("PCI-ISA link %u routed to IRQ%u\n", link, isa_irq);
kfraser@12554 184 if ( link++ == 4 )
kfraser@12554 185 break;
kfraser@12554 186 }
kfraser@12554 187
kfraser@12554 188 /* Program ELCR to match PCI-wired IRQs. */
kfraser@12554 189 outb(0x4d0, (uint8_t)(PCI_ISA_IRQ_MASK >> 0));
kfraser@12554 190 outb(0x4d1, (uint8_t)(PCI_ISA_IRQ_MASK >> 8));
kfraser@12554 191
kfraser@12554 192 /* Scan the PCI bus and map resources. */
kfraser@12554 193 for ( devfn = 0; devfn < 128; devfn++ )
kfraser@12554 194 {
kfraser@12554 195 class = pci_readw(devfn, PCI_CLASS_DEVICE);
kfraser@12554 196 vendor_id = pci_readw(devfn, PCI_VENDOR_ID);
kfraser@12554 197 device_id = pci_readw(devfn, PCI_DEVICE_ID);
kfraser@12554 198 if ( (vendor_id == 0xffff) && (device_id == 0xffff) )
kfraser@12554 199 continue;
kfraser@12554 200
kfraser@12554 201 ASSERT((devfn != PCI_ISA_DEVFN) ||
kfraser@12554 202 ((vendor_id == 0x8086) && (device_id == 0x7000)));
kfraser@12554 203
kfraser@12554 204 switch ( class )
kfraser@12554 205 {
kfraser@12554 206 case 0x0680:
kfraser@12554 207 ASSERT((vendor_id == 0x8086) && (device_id == 0x7113));
kfraser@12554 208 /*
kfraser@12554 209 * PIIX4 ACPI PM. Special device with special PCI config space.
kfraser@12554 210 * No ordinary BARs.
kfraser@12554 211 */
kfraser@12554 212 pci_writew(devfn, 0x20, 0x0000); /* No smb bus IO enable */
kfraser@12554 213 pci_writew(devfn, 0x22, 0x0000);
kfraser@12554 214 pci_writew(devfn, 0x3c, 0x0009); /* Hardcoded IRQ9 */
kfraser@12554 215 pci_writew(devfn, 0x3d, 0x0001);
kfraser@12554 216 break;
kfraser@12554 217 case 0x0101:
kfraser@12554 218 /* PIIX3 IDE */
kfraser@12554 219 ASSERT((vendor_id == 0x8086) && (device_id == 0x7010));
kfraser@12554 220 pci_writew(devfn, 0x40, 0x8000); /* enable IDE0 */
kfraser@12554 221 pci_writew(devfn, 0x42, 0x8000); /* enable IDE1 */
kfraser@12554 222 /* fall through */
kfraser@12554 223 default:
kfraser@12554 224 /* Default memory mappings. */
kfraser@12554 225 for ( bar = 0; bar < 7; bar++ )
kfraser@12554 226 {
kfraser@12554 227 bar_reg = PCI_BASE_ADDRESS_0 + 4*bar;
kfraser@12554 228 if ( bar == 6 )
kfraser@12554 229 bar_reg = PCI_ROM_ADDRESS;
kfraser@12554 230
kfraser@12554 231 bar_data = pci_readl(devfn, bar_reg);
kfraser@12554 232
kfraser@12554 233 pci_writel(devfn, bar_reg, ~0);
kfraser@12554 234 bar_sz = pci_readl(devfn, bar_reg);
kfraser@12554 235 if ( bar_sz == 0 )
kfraser@12554 236 continue;
kfraser@12554 237
kfraser@12554 238 if ( (bar_data & PCI_BASE_ADDRESS_SPACE) ==
kfraser@12554 239 PCI_BASE_ADDRESS_SPACE_MEMORY )
kfraser@12554 240 {
kfraser@12554 241 base = &mem_base;
kfraser@12554 242 bar_sz &= PCI_BASE_ADDRESS_MEM_MASK;
kfraser@12554 243 bar_data &= ~PCI_BASE_ADDRESS_MEM_MASK;
kfraser@12554 244 }
kfraser@12554 245 else
kfraser@12554 246 {
kfraser@12554 247 base = &io_base;
kfraser@12554 248 bar_sz &= PCI_BASE_ADDRESS_IO_MASK & 0xffff;
kfraser@12554 249 bar_data &= ~PCI_BASE_ADDRESS_IO_MASK;
kfraser@12554 250 }
kfraser@12554 251 bar_sz &= ~(bar_sz - 1);
kfraser@12554 252
kfraser@12554 253 *base = (*base + bar_sz - 1) & ~(bar_sz - 1);
kfraser@12554 254 bar_data |= *base;
kfraser@12554 255 *base += bar_sz;
kfraser@12554 256
kfraser@12554 257 pci_writel(devfn, bar_reg, bar_data);
kfraser@12554 258 printf("pci dev %02x:%x bar %02x size %08x: %08x\n",
kfraser@12554 259 devfn>>3, devfn&7, bar_reg, bar_sz, bar_data);
kfraser@12554 260
kfraser@12554 261 /* Now enable the memory or I/O mapping. */
kfraser@12554 262 cmd = pci_readw(devfn, PCI_COMMAND);
kfraser@12554 263 if ( (bar_reg == PCI_ROM_ADDRESS) ||
kfraser@12554 264 ((bar_data & PCI_BASE_ADDRESS_SPACE) ==
kfraser@12554 265 PCI_BASE_ADDRESS_SPACE_MEMORY) )
kfraser@12554 266 cmd |= PCI_COMMAND_MEMORY;
kfraser@12554 267 else
kfraser@12554 268 cmd |= PCI_COMMAND_IO;
kfraser@12554 269 pci_writew(devfn, PCI_COMMAND, cmd);
kfraser@12554 270 }
kfraser@12554 271 break;
kfraser@12554 272 }
kfraser@12554 273
kfraser@12554 274 /* Map the interrupt. */
kfraser@12554 275 pin = pci_readb(devfn, PCI_INTERRUPT_PIN);
kfraser@12554 276 if ( pin != 0 )
kfraser@12554 277 {
kfraser@12554 278 /* This is the barber's pole mapping used by Xen. */
kfraser@12554 279 link = ((pin - 1) + (devfn >> 3)) & 3;
kfraser@12554 280 isa_irq = pci_readb(PCI_ISA_DEVFN, 0x60 + link);
kfraser@12554 281 pci_writeb(devfn, PCI_INTERRUPT_LINE, isa_irq);
kfraser@12554 282 printf("pci dev %02x:%x INT%c->IRQ%u\n",
kfraser@12554 283 devfn>>3, devfn&7, 'A'+pin-1, isa_irq);
kfraser@12554 284 }
kfraser@12554 285 }
kfraser@12554 286 }
kfraser@12554 287
kfraser@12548 288 int main(void)
kfraser@12548 289 {
kfraser@12554 290 printf("HVM Loader\n");
kfraser@12548 291
kfraser@12548 292 init_hypercalls();
kfraser@12548 293
kfraser@12554 294 printf("Writing SMBIOS tables ...\n");
kfraser@12548 295 hvm_write_smbios_tables();
kfraser@12548 296
kfraser@12554 297 printf("Loading ROMBIOS ...\n");
kfraser@12548 298 memcpy((void *)ROMBIOS_PHYSICAL_ADDRESS, rombios, sizeof(rombios));
kfraser@12548 299
kfraser@12548 300 apic_setup();
kfraser@12554 301 pci_setup();
kfraser@12548 302 create_mp_tables();
kfraser@12548 303
kfraser@12548 304 if ( cirrus_check() )
kfraser@12548 305 {
kfraser@12554 306 printf("Loading Cirrus VGABIOS ...\n");
kfraser@12548 307 memcpy((void *)VGABIOS_PHYSICAL_ADDRESS,
kfraser@12548 308 vgabios_cirrusvga, sizeof(vgabios_cirrusvga));
kfraser@12548 309 }
kfraser@12548 310 else
kfraser@12548 311 {
kfraser@12554 312 printf("Loading Standard VGABIOS ...\n");
kfraser@12548 313 memcpy((void *)VGABIOS_PHYSICAL_ADDRESS,
kfraser@12548 314 vgabios_stdvga, sizeof(vgabios_stdvga));
kfraser@12548 315 }
kfraser@12548 316
kfraser@12548 317 if ( get_acpi_enabled() != 0 )
kfraser@12548 318 {
kfraser@12554 319 printf("Loading ACPI ...\n");
kfraser@12548 320 acpi_madt_update((unsigned char *) acpi);
kfraser@12548 321 if ( (ACPI_PHYSICAL_ADDRESS + sizeof(acpi)) <= 0xF0000 )
kfraser@12548 322 {
kfraser@12548 323 unsigned char *freemem = (unsigned char *)
kfraser@12548 324 (ACPI_PHYSICAL_ADDRESS + sizeof(acpi));
kfraser@12548 325 /*
kfraser@12548 326 * Make sure acpi table does not overlap rombios
kfraser@12548 327 * currently acpi less than 8K will be OK.
kfraser@12548 328 */
kfraser@12548 329 memcpy((void *)ACPI_PHYSICAL_ADDRESS, acpi,
kfraser@12548 330 sizeof(acpi));
kfraser@12548 331 acpi_update((unsigned char *)ACPI_PHYSICAL_ADDRESS,
kfraser@12548 332 sizeof(acpi),
kfraser@12548 333 (unsigned char *)0xF0000,
kfraser@12548 334 &freemem);
kfraser@12548 335 }
kfraser@12548 336 }
kfraser@12548 337
kfraser@12548 338 if ( check_amd() )
kfraser@12548 339 {
kfraser@12548 340 /* AMD implies this is SVM */
kfraser@12554 341 printf("SVM go ...\n");
kfraser@12548 342 vmmcall(SVM_VMMCALL_RESET_TO_REALMODE, 0, 0, 0, 0, 0);
kfraser@12548 343 }
kfraser@12548 344 else
kfraser@12548 345 {
kfraser@12554 346 printf("Loading VMXAssist ...\n");
kfraser@12548 347 memcpy((void *)VMXASSIST_PHYSICAL_ADDRESS,
kfraser@12548 348 vmxassist, sizeof(vmxassist));
kfraser@12548 349
kfraser@12554 350 printf("VMX go ...\n");
kfraser@12548 351 __asm__ __volatile__(
kfraser@12548 352 "jmp *%%eax"
kfraser@12548 353 : : "a" (VMXASSIST_PHYSICAL_ADDRESS), "d" (0)
kfraser@12548 354 );
kfraser@12548 355 }
kfraser@12548 356
kfraser@12554 357 printf("Failed to invoke ROMBIOS\n");
kfraser@12548 358 return 0;
kfraser@12548 359 }
kfraser@12548 360
kfraser@12548 361 /*
kfraser@12548 362 * Local variables:
kfraser@12548 363 * mode: C
kfraser@12548 364 * c-set-style: "BSD"
kfraser@12548 365 * c-basic-offset: 4
kfraser@12548 366 * tab-width: 4
kfraser@12548 367 * indent-tabs-mode: nil
kfraser@12548 368 * End:
kfraser@12548 369 */