ia64/xen-unstable

annotate tools/firmware/hvmloader/hvmloader.c @ 19035:b169db55faf3

hvmloader: remove videoram allocation; move reserve base to 0xfc000000.

Signed-off-by: Stefano Stabellini <stefano.stabellini@eu.citrix.com>
author Keir Fraser <keir.fraser@citrix.com>
date Tue Jan 13 15:42:47 2009 +0000 (2009-01-13)
parents b84345ee0d41
children f3240cd3cd2b
rev   line source
kaf24@8708 1 /*
keir@16992 2 * hvmloader.c: HVM bootloader.
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 */
kfraser@14959 22
kaf24@8708 23 #include "roms.h"
kfraser@14959 24 #include "acpi/acpi2_0.h"
kfraser@10976 25 #include "hypercall.h"
kfraser@10976 26 #include "util.h"
kfraser@12548 27 #include "config.h"
kaf24@12571 28 #include "apic_regs.h"
kfraser@12554 29 #include "pci_regs.h"
keir@15303 30 #include "e820.h"
keir@17374 31 #include "option_rom.h"
kfraser@10976 32 #include <xen/version.h>
kfraser@11081 33 #include <xen/hvm/params.h>
keir@19022 34 #include <xen/memory.h>
kaf24@8708 35
keir@17463 36 asm (
kfraser@12548 37 " .text \n"
kfraser@12548 38 " .globl _start \n"
kfraser@12548 39 "_start: \n"
kfraser@14373 40 /* C runtime kickoff. */
kfraser@12548 41 " cld \n"
kfraser@12548 42 " cli \n"
kfraser@14373 43 " movl $stack_top,%esp \n"
kfraser@14373 44 " movl %esp,%ebp \n"
kfraser@14373 45 " call main \n"
kfraser@14373 46 /* Relocate real-mode trampoline to 0x0. */
kfraser@14373 47 " mov $trampoline_start,%esi \n"
kfraser@14373 48 " xor %edi,%edi \n"
kfraser@14373 49 " mov $trampoline_end,%ecx \n"
kfraser@14373 50 " sub %esi,%ecx \n"
kfraser@14373 51 " rep movsb \n"
kfraser@14373 52 /* Load real-mode compatible segment state (base 0x0000, limit 0xffff). */
kfraser@12548 53 " lgdt gdt_desr \n"
kfraser@14373 54 " mov $0x0010,%ax \n"
kfraser@14373 55 " mov %ax,%ds \n"
kfraser@14373 56 " mov %ax,%es \n"
kfraser@14373 57 " mov %ax,%fs \n"
kfraser@14373 58 " mov %ax,%gs \n"
kfraser@14373 59 " mov %ax,%ss \n"
kfraser@14391 60 /* Initialise all 32-bit GPRs to zero. */
kfraser@14391 61 " xor %eax,%eax \n"
kfraser@14391 62 " xor %ebx,%ebx \n"
kfraser@14391 63 " xor %ecx,%ecx \n"
kfraser@14391 64 " xor %edx,%edx \n"
kfraser@14391 65 " xor %esp,%esp \n"
kfraser@14391 66 " xor %ebp,%ebp \n"
kfraser@14391 67 " xor %esi,%esi \n"
kfraser@14391 68 " xor %edi,%edi \n"
kfraser@14373 69 /* Enter real mode, reload all segment registers and IDT. */
kfraser@14391 70 " ljmp $0x8,$0x0 \n"
kfraser@14373 71 "trampoline_start: .code16 \n"
kfraser@14373 72 " mov %eax,%cr0 \n"
kfraser@14373 73 " ljmp $0,$1f-trampoline_start\n"
kfraser@14391 74 "1: mov %ax,%ds \n"
kfraser@14373 75 " mov %ax,%es \n"
kfraser@14373 76 " mov %ax,%fs \n"
kfraser@14373 77 " mov %ax,%gs \n"
kfraser@14373 78 " mov %ax,%ss \n"
kfraser@14373 79 " lidt 1f-trampoline_start \n"
kfraser@14373 80 " ljmp $0xf000,$0xfff0 \n"
kfraser@14373 81 "1: .word 0x3ff,0,0 \n"
kfraser@14373 82 "trampoline_end: .code32 \n"
kfraser@12548 83 " \n"
kfraser@12548 84 "gdt_desr: \n"
kfraser@12548 85 " .word gdt_end - gdt - 1 \n"
kfraser@12548 86 " .long gdt \n"
kfraser@12548 87 " \n"
kfraser@12548 88 " .align 8 \n"
kfraser@12548 89 "gdt: \n"
kfraser@12548 90 " .quad 0x0000000000000000 \n"
kfraser@14373 91 " .quad 0x00009a000000ffff \n" /* Ring 0 code, base 0 limit 0xffff */
kfraser@14373 92 " .quad 0x000092000000ffff \n" /* Ring 0 data, base 0 limit 0xffff */
kfraser@12548 93 "gdt_end: \n"
kfraser@12548 94 " \n"
kfraser@12548 95 " .bss \n"
kfraser@12548 96 " .align 8 \n"
kfraser@12548 97 "stack: \n"
kfraser@12548 98 " .skip 0x4000 \n"
kfraser@12548 99 "stack_top: \n"
keir@17467 100 " .text \n"
kfraser@12548 101 );
kaf24@8708 102
keir@19022 103 unsigned long pci_mem_start = PCI_MEM_START;
keir@19022 104 unsigned long pci_mem_end = PCI_MEM_END;
keir@19022 105
keir@17694 106 static enum { VGA_none, VGA_std, VGA_cirrus } virtual_vga = VGA_none;
kaf24@8708 107
keir@18621 108 static void init_hypercalls(void)
kfraser@10976 109 {
kfraser@12548 110 uint32_t eax, ebx, ecx, edx;
kfraser@12548 111 unsigned long i;
kfraser@12554 112 char signature[13];
kfraser@12548 113 xen_extraversion_t extraversion;
keir@18621 114 uint32_t base;
kfraser@10976 115
keir@18621 116 for ( base = 0x40000000; base < 0x40001000; base += 0x100 )
keir@18621 117 {
keir@18621 118 cpuid(base, &eax, &ebx, &ecx, &edx);
kfraser@10976 119
keir@18621 120 *(uint32_t *)(signature + 0) = ebx;
keir@18621 121 *(uint32_t *)(signature + 4) = ecx;
keir@18621 122 *(uint32_t *)(signature + 8) = edx;
keir@18621 123 signature[12] = '\0';
keir@18621 124
keir@18621 125 if ( !strcmp("XenVMMXenVMM", signature) )
keir@18621 126 break;
keir@18621 127 }
keir@18621 128
keir@18621 129 BUG_ON(strcmp("XenVMMXenVMM", signature) || ((eax - base) < 2));
kfraser@10976 130
kfraser@12554 131 /* Fill in hypercall transfer pages. */
keir@18621 132 cpuid(base + 2, &eax, &ebx, &ecx, &edx);
kfraser@12548 133 for ( i = 0; i < eax; i++ )
kfraser@12548 134 wrmsr(ebx, HYPERCALL_PHYSICAL_ADDRESS + (i << 12) + i);
kfraser@12548 135
kfraser@12554 136 /* Print version information. */
keir@18621 137 cpuid(base + 1, &eax, &ebx, &ecx, &edx);
kfraser@12548 138 hypercall_xen_version(XENVER_extraversion, extraversion);
kfraser@12554 139 printf("Detected Xen v%u.%u%s\n", eax >> 16, eax & 0xffff, extraversion);
kfraser@10976 140 }
kfraser@10976 141
kfraser@12548 142 static void apic_setup(void)
kaf24@8708 143 {
kaf24@12571 144 /* Set the IOAPIC ID to tha static value used in the MP/ACPI tables. */
kaf24@12571 145 ioapic_write(0x00, IOAPIC_ID);
kaf24@8708 146
kaf24@12571 147 /* Set up Virtual Wire mode. */
kaf24@12571 148 lapic_write(APIC_SPIV, APIC_SPIV_APIC_ENABLED | 0xFF);
kaf24@12571 149 lapic_write(APIC_LVT0, APIC_MODE_EXTINT << 8);
kaf24@12571 150 lapic_write(APIC_LVT1, APIC_MODE_NMI << 8);
kaf24@8708 151 }
kaf24@8708 152
kfraser@12554 153 static void pci_setup(void)
kfraser@12554 154 {
keir@19022 155 uint32_t base, devfn, bar_reg, bar_data, bar_sz, cmd, mmio_total = 0;
kfraser@12554 156 uint16_t class, vendor_id, device_id;
kfraser@12554 157 unsigned int bar, pin, link, isa_irq;
kfraser@12554 158
keir@16835 159 /* Resources assignable to PCI devices via BARs. */
keir@16835 160 struct resource {
keir@16835 161 uint32_t base, max;
keir@19022 162 } *resource, mem_resource, io_resource;
keir@16835 163
keir@16834 164 /* Create a list of device BARs in descending order of size. */
keir@16834 165 struct bars {
keir@16834 166 uint32_t devfn, bar_reg, bar_sz;
keir@17694 167 } *bars = (struct bars *)SCRATCH_PHYSICAL_ADDRESS;
keir@16834 168 unsigned int i, nr_bars = 0;
keir@16834 169
kfraser@12554 170 /* Program PCI-ISA bridge with appropriate link routes. */
kfraser@15581 171 isa_irq = 0;
kfraser@15581 172 for ( link = 0; link < 4; link++ )
kfraser@12554 173 {
kfraser@15581 174 do { isa_irq = (isa_irq + 1) & 15;
kfraser@15581 175 } while ( !(PCI_ISA_IRQ_MASK & (1U << isa_irq)) );
kfraser@12554 176 pci_writeb(PCI_ISA_DEVFN, 0x60 + link, isa_irq);
kfraser@12554 177 printf("PCI-ISA link %u routed to IRQ%u\n", link, isa_irq);
kfraser@12554 178 }
kfraser@12554 179
kfraser@12554 180 /* Program ELCR to match PCI-wired IRQs. */
kfraser@12554 181 outb(0x4d0, (uint8_t)(PCI_ISA_IRQ_MASK >> 0));
kfraser@12554 182 outb(0x4d1, (uint8_t)(PCI_ISA_IRQ_MASK >> 8));
kfraser@12554 183
kfraser@12554 184 /* Scan the PCI bus and map resources. */
kfraser@12554 185 for ( devfn = 0; devfn < 128; devfn++ )
kfraser@12554 186 {
kfraser@12554 187 class = pci_readw(devfn, PCI_CLASS_DEVICE);
kfraser@12554 188 vendor_id = pci_readw(devfn, PCI_VENDOR_ID);
kfraser@12554 189 device_id = pci_readw(devfn, PCI_DEVICE_ID);
kfraser@12554 190 if ( (vendor_id == 0xffff) && (device_id == 0xffff) )
kfraser@12554 191 continue;
kfraser@12554 192
kfraser@12554 193 ASSERT((devfn != PCI_ISA_DEVFN) ||
kfraser@12554 194 ((vendor_id == 0x8086) && (device_id == 0x7000)));
kfraser@12554 195
kfraser@12554 196 switch ( class )
kfraser@12554 197 {
keir@17694 198 case 0x0300:
keir@17694 199 if ( (vendor_id == 0x1234) && (device_id == 0x1111) )
keir@17694 200 virtual_vga = VGA_std;
keir@17694 201 if ( (vendor_id == 0x1013) && (device_id == 0xb8) )
keir@17694 202 virtual_vga = VGA_cirrus;
keir@17694 203 break;
kfraser@12554 204 case 0x0680:
keir@17698 205 /* PIIX4 ACPI PM. Special device with special PCI config space. */
kfraser@12554 206 ASSERT((vendor_id == 0x8086) && (device_id == 0x7113));
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:
keir@17911 213 if ( vendor_id == 0x8086 )
keir@17911 214 {
keir@17911 215 /* Intel ICHs since PIIX3: enable IDE legacy mode. */
keir@17911 216 pci_writew(devfn, 0x40, 0x8000); /* enable IDE0 */
keir@17911 217 pci_writew(devfn, 0x42, 0x8000); /* enable IDE1 */
keir@17911 218 }
keir@17698 219 break;
keir@17698 220 }
kfraser@12554 221
keir@17698 222 /* Map the I/O memory and port resources. */
keir@17698 223 for ( bar = 0; bar < 7; bar++ )
keir@17698 224 {
keir@17698 225 bar_reg = PCI_BASE_ADDRESS_0 + 4*bar;
keir@17698 226 if ( bar == 6 )
keir@17698 227 bar_reg = PCI_ROM_ADDRESS;
kfraser@12554 228
keir@17698 229 bar_data = pci_readl(devfn, bar_reg);
keir@17698 230 pci_writel(devfn, bar_reg, ~0);
keir@17698 231 bar_sz = pci_readl(devfn, bar_reg);
keir@17698 232 pci_writel(devfn, bar_reg, bar_data);
keir@17698 233 if ( bar_sz == 0 )
keir@17698 234 continue;
keir@16834 235
keir@17698 236 bar_sz &= (((bar_data & PCI_BASE_ADDRESS_SPACE) ==
keir@17698 237 PCI_BASE_ADDRESS_SPACE_MEMORY) ?
keir@17698 238 PCI_BASE_ADDRESS_MEM_MASK :
keir@17698 239 (PCI_BASE_ADDRESS_IO_MASK & 0xffff));
keir@17698 240 bar_sz &= ~(bar_sz - 1);
keir@16834 241
keir@17698 242 for ( i = 0; i < nr_bars; i++ )
keir@17698 243 if ( bars[i].bar_sz < bar_sz )
keir@17698 244 break;
keir@17698 245
keir@17698 246 if ( i != nr_bars )
keir@17698 247 memmove(&bars[i+1], &bars[i], (nr_bars-i) * sizeof(*bars));
keir@17698 248
keir@17698 249 bars[i].devfn = devfn;
keir@17698 250 bars[i].bar_reg = bar_reg;
keir@17698 251 bars[i].bar_sz = bar_sz;
keir@17698 252
keir@19022 253 if ( (bar_data & PCI_BASE_ADDRESS_SPACE) ==
keir@19022 254 PCI_BASE_ADDRESS_SPACE_MEMORY )
keir@19022 255 mmio_total += bar_sz;
keir@19022 256
keir@17698 257 nr_bars++;
keir@18405 258
keir@18405 259 /* Skip the upper-half of the address for a 64-bit BAR. */
keir@18405 260 if ( (bar_data & (PCI_BASE_ADDRESS_SPACE |
keir@18405 261 PCI_BASE_ADDRESS_MEM_TYPE_MASK)) ==
keir@18405 262 (PCI_BASE_ADDRESS_SPACE_MEMORY |
keir@18405 263 PCI_BASE_ADDRESS_MEM_TYPE_64) )
keir@18405 264 bar++;
kfraser@12554 265 }
kfraser@12554 266
kfraser@12554 267 /* Map the interrupt. */
kfraser@12554 268 pin = pci_readb(devfn, PCI_INTERRUPT_PIN);
kfraser@12554 269 if ( pin != 0 )
kfraser@12554 270 {
kfraser@12554 271 /* This is the barber's pole mapping used by Xen. */
kfraser@12554 272 link = ((pin - 1) + (devfn >> 3)) & 3;
kfraser@12554 273 isa_irq = pci_readb(PCI_ISA_DEVFN, 0x60 + link);
kfraser@12554 274 pci_writeb(devfn, PCI_INTERRUPT_LINE, isa_irq);
kfraser@12554 275 printf("pci dev %02x:%x INT%c->IRQ%u\n",
kfraser@12554 276 devfn>>3, devfn&7, 'A'+pin-1, isa_irq);
kfraser@12554 277 }
keir@18921 278
keir@18921 279 /* Enable bus mastering. */
keir@18921 280 cmd = pci_readw(devfn, PCI_COMMAND);
keir@18921 281 cmd |= PCI_COMMAND_MASTER;
keir@18921 282 pci_writew(devfn, PCI_COMMAND, cmd);
kfraser@12554 283 }
keir@16834 284
keir@19022 285 while ( (mmio_total > (pci_mem_end - pci_mem_start)) &&
keir@19022 286 ((pci_mem_start << 1) != 0) )
keir@19022 287 pci_mem_start <<= 1;
keir@19022 288
keir@19022 289 while ( (pci_mem_start >> PAGE_SHIFT) < hvm_info->low_mem_pgend )
keir@19022 290 {
keir@19022 291 struct xen_add_to_physmap xatp;
keir@19022 292 if ( hvm_info->high_mem_pgend == 0 )
keir@19022 293 hvm_info->high_mem_pgend = 1ull << (32 - PAGE_SHIFT);
keir@19022 294 xatp.domid = DOMID_SELF;
keir@19022 295 xatp.space = XENMAPSPACE_gmfn;
keir@19022 296 xatp.idx = --hvm_info->low_mem_pgend;
keir@19022 297 xatp.gpfn = hvm_info->high_mem_pgend++;
keir@19022 298 if ( hypercall_memory_op(XENMEM_add_to_physmap, &xatp) != 0 )
keir@19022 299 BUG();
keir@19022 300 }
keir@19022 301
keir@19022 302 mem_resource.base = pci_mem_start;
keir@19022 303 mem_resource.max = pci_mem_end;
keir@19022 304 io_resource.base = 0xc000;
keir@19022 305 io_resource.max = 0x10000;
keir@19022 306
keir@16834 307 /* Assign iomem and ioport resources in descending order of size. */
keir@16834 308 for ( i = 0; i < nr_bars; i++ )
keir@16834 309 {
keir@16834 310 devfn = bars[i].devfn;
keir@16834 311 bar_reg = bars[i].bar_reg;
keir@16834 312 bar_sz = bars[i].bar_sz;
keir@16834 313
keir@16834 314 bar_data = pci_readl(devfn, bar_reg);
keir@16834 315
keir@16834 316 if ( (bar_data & PCI_BASE_ADDRESS_SPACE) ==
keir@16834 317 PCI_BASE_ADDRESS_SPACE_MEMORY )
keir@16834 318 {
keir@16835 319 resource = &mem_resource;
keir@16834 320 bar_data &= ~PCI_BASE_ADDRESS_MEM_MASK;
keir@16834 321 }
keir@16834 322 else
keir@16834 323 {
keir@16835 324 resource = &io_resource;
keir@16834 325 bar_data &= ~PCI_BASE_ADDRESS_IO_MASK;
keir@16834 326 }
keir@16834 327
keir@16835 328 base = (resource->base + bar_sz - 1) & ~(bar_sz - 1);
keir@16835 329 bar_data |= base;
keir@16835 330 base += bar_sz;
keir@16835 331
keir@16835 332 if ( (base < resource->base) || (base > resource->max) )
keir@16835 333 {
keir@16835 334 printf("pci dev %02x:%x bar %02x size %08x: no space for "
keir@16835 335 "resource!\n", devfn>>3, devfn&7, bar_reg, bar_sz);
keir@16835 336 continue;
keir@16835 337 }
keir@16835 338
keir@16835 339 resource->base = base;
keir@16834 340
keir@16834 341 pci_writel(devfn, bar_reg, bar_data);
keir@16835 342 printf("pci dev %02x:%x bar %02x size %08x: %08x\n",
keir@16835 343 devfn>>3, devfn&7, bar_reg, bar_sz, bar_data);
keir@16834 344
keir@16834 345 /* Now enable the memory or I/O mapping. */
keir@16834 346 cmd = pci_readw(devfn, PCI_COMMAND);
keir@16834 347 if ( (bar_reg == PCI_ROM_ADDRESS) ||
keir@16834 348 ((bar_data & PCI_BASE_ADDRESS_SPACE) ==
keir@16834 349 PCI_BASE_ADDRESS_SPACE_MEMORY) )
keir@16834 350 cmd |= PCI_COMMAND_MEMORY;
keir@16834 351 else
keir@16834 352 cmd |= PCI_COMMAND_IO;
keir@16834 353 pci_writew(devfn, PCI_COMMAND, cmd);
keir@16834 354 }
kfraser@12554 355 }
kfraser@12554 356
keir@16960 357 /*
keir@18827 358 * Scan the list of Option ROMs at @roms for one which supports
keir@18828 359 * PCI (@vendor_id, @device_id) found at slot @devfn. If one is found,
keir@18828 360 * copy it to @dest and return its size rounded up to a multiple 2kB. This
keir@18994 361 * function will not copy ROMs beyond address OPTIONROM_PHYSICAL_END.
keir@16960 362 */
keir@18827 363 #define round_option_rom(x) (((x) + 2047) & ~2047)
keir@18827 364 static int scan_option_rom(
keir@18828 365 uint8_t devfn, uint16_t vendor_id, uint16_t device_id,
keir@18828 366 void *roms, uint32_t dest)
keir@16960 367 {
keir@17374 368 struct option_rom_header *rom;
keir@17374 369 struct option_rom_pnp_header *pnph;
keir@17374 370 struct option_rom_pci_header *pcih;
keir@17374 371 uint8_t csum;
keir@17374 372 int i;
keir@16960 373
keir@18827 374 static uint32_t orom_ids[64];
keir@18827 375 static int nr_roms;
keir@16960 376
keir@18827 377 /* Avoid duplicate ROMs. */
keir@18827 378 for ( i = 0; i < nr_roms; i++ )
keir@18827 379 if ( orom_ids[i] == (vendor_id | ((uint32_t)device_id << 16)) )
keir@18827 380 return 0;
keir@17374 381
keir@18827 382 rom = roms;
keir@18827 383 for ( ; ; )
keir@18827 384 {
keir@18827 385 /* Invalid signature means we're out of option ROMs. */
keir@18827 386 if ( strncmp((char *)rom->signature, "\x55\xaa", 2) ||
keir@18827 387 (rom->rom_size == 0) )
keir@18827 388 break;
keir@17374 389
keir@18827 390 /* Invalid checksum means we're out of option ROMs. */
keir@18827 391 csum = 0;
keir@18827 392 for ( i = 0; i < (rom->rom_size * 512); i++ )
keir@18827 393 csum += ((uint8_t *)rom)[i];
keir@18827 394 if ( csum != 0 )
keir@18827 395 break;
keir@17374 396
keir@18827 397 /* Check the PCI PnP header (if any) for a match. */
keir@18827 398 pcih = (struct option_rom_pci_header *)
keir@18827 399 ((char *)rom + rom->pci_header_offset);
keir@18827 400 if ( (rom->pci_header_offset != 0) &&
keir@18827 401 !strncmp((char *)pcih->signature, "PCIR", 4) &&
keir@18827 402 (pcih->vendor_id == vendor_id) &&
keir@18827 403 (pcih->device_id == device_id) )
keir@18827 404 goto found;
keir@18827 405
keir@18827 406 rom = (struct option_rom_header *)
keir@18827 407 ((char *)rom + rom->rom_size * 512);
keir@16960 408 }
keir@16960 409
keir@16960 410 return 0;
keir@16960 411
keir@16960 412 found:
keir@17412 413 /* Find the PnP expansion header (if any). */
keir@17412 414 pnph = ((rom->expansion_header_offset != 0)
keir@17412 415 ? ((struct option_rom_pnp_header *)
keir@17412 416 ((char *)rom + rom->expansion_header_offset))
keir@17412 417 : ((struct option_rom_pnp_header *)NULL));
keir@17430 418 while ( (pnph != NULL) && strncmp((char *)pnph->signature, "$PnP", 4) )
keir@17412 419 pnph = ((pnph->next_header_offset != 0)
keir@17412 420 ? ((struct option_rom_pnp_header *)
keir@17412 421 ((char *)rom + pnph->next_header_offset))
keir@17412 422 : ((struct option_rom_pnp_header *)NULL));
keir@17412 423
keir@18827 424 printf("Loading PCI Option ROM ...\n");
keir@17374 425 if ( (pnph != NULL) && (pnph->manufacturer_name_offset != 0) )
keir@17374 426 printf(" - Manufacturer: %s\n",
keir@17374 427 (char *)rom + pnph->manufacturer_name_offset);
keir@17374 428 if ( (pnph != NULL) && (pnph->product_name_offset != 0) )
keir@17374 429 printf(" - Product name: %s\n",
keir@17374 430 (char *)rom + pnph->product_name_offset);
keir@18827 431
keir@18994 432 if ( (dest + rom->rom_size * 512 + 1) > OPTIONROM_PHYSICAL_END )
keir@18827 433 {
keir@18827 434 printf("Option ROM size %x exceeds available space\n",
keir@18827 435 rom->rom_size * 512);
keir@18827 436 return 0;
keir@18827 437 }
keir@18827 438
keir@18827 439 orom_ids[nr_roms++] = vendor_id | ((uint32_t)device_id << 16);
keir@18827 440 memcpy((void *)dest, rom, rom->rom_size * 512);
keir@18828 441 *(uint8_t *)(dest + rom->rom_size * 512) = devfn;
keir@18828 442 return round_option_rom(rom->rom_size * 512 + 1);
keir@18827 443 }
keir@18827 444
keir@18827 445 /*
keir@18827 446 * Scan the PCI bus for the first NIC supported by etherboot, and copy
keir@18827 447 * the corresponding rom data to *copy_rom_dest. Returns the length of the
keir@18827 448 * selected rom, or 0 if no NIC found.
keir@18827 449 */
keir@18827 450 static int scan_etherboot_nic(uint32_t copy_rom_dest)
keir@18827 451 {
keir@18828 452 uint8_t devfn;
keir@18827 453 uint16_t class, vendor_id, device_id;
keir@18827 454
keir@18827 455 for ( devfn = 0; devfn < 128; devfn++ )
keir@18827 456 {
keir@18827 457 class = pci_readw(devfn, PCI_CLASS_DEVICE);
keir@18827 458 vendor_id = pci_readw(devfn, PCI_VENDOR_ID);
keir@18827 459 device_id = pci_readw(devfn, PCI_DEVICE_ID);
keir@18827 460
keir@18827 461 /* We're only interested in NICs. */
keir@18827 462 if ( (vendor_id != 0xffff) &&
keir@18827 463 (device_id != 0xffff) &&
keir@18827 464 (class == 0x0200) )
keir@18827 465 return scan_option_rom(
keir@18828 466 devfn, vendor_id, device_id, etherboot, copy_rom_dest);
keir@18827 467 }
keir@18827 468
keir@18827 469 return 0;
keir@18827 470 }
keir@18827 471
keir@18827 472 /*
keir@18827 473 * Scan the PCI bus for the devices that have an option ROM, and copy
keir@18827 474 * the corresponding rom data to rom_phys_addr.
keir@18827 475 */
keir@18827 476 static int pci_load_option_roms(uint32_t rom_base_addr)
keir@18827 477 {
keir@18828 478 uint32_t option_rom_addr, rom_phys_addr = rom_base_addr;
keir@18827 479 uint16_t vendor_id, device_id;
keir@18828 480 uint8_t devfn, class;
keir@18827 481
keir@18827 482 for ( devfn = 0; devfn < 128; devfn++ )
keir@18827 483 {
keir@18827 484 class = pci_readb(devfn, PCI_CLASS_DEVICE + 1);
keir@18827 485 vendor_id = pci_readw(devfn, PCI_VENDOR_ID);
keir@18827 486 device_id = pci_readw(devfn, PCI_DEVICE_ID);
keir@18827 487
keir@18827 488 if ( (vendor_id == 0xffff) && (device_id == 0xffff) )
keir@18827 489 continue;
keir@18827 490
keir@18827 491 /*
keir@18827 492 * Currently only scan options from mass storage devices and serial
keir@18827 493 * bus controller (Fibre Channel included).
keir@18827 494 */
keir@18827 495 if ( (class != 0x1) && (class != 0xc) )
keir@18827 496 continue;
keir@18827 497
keir@18827 498 option_rom_addr = pci_readl(devfn, PCI_ROM_ADDRESS);
keir@18827 499 if ( !option_rom_addr )
keir@18827 500 continue;
keir@18827 501
keir@18827 502 /* Ensure Expansion Bar is enabled before copying */
keir@18827 503 pci_writel(devfn, PCI_ROM_ADDRESS, option_rom_addr | 0x1);
keir@18827 504
keir@18827 505 rom_phys_addr += scan_option_rom(
keir@18828 506 devfn, vendor_id, device_id,
keir@18828 507 (void *)(option_rom_addr & ~2047), rom_phys_addr);
keir@18827 508
keir@18827 509 /* Restore the default original value of Expansion Bar */
keir@18827 510 pci_writel(devfn, PCI_ROM_ADDRESS, option_rom_addr);
keir@18827 511 }
keir@18827 512
keir@18827 513 return rom_phys_addr - rom_base_addr;
keir@16960 514 }
keir@16960 515
keir@14449 516 /* Replace possibly erroneous memory-size CMOS fields with correct values. */
keir@14449 517 static void cmos_write_memory_size(void)
keir@14449 518 {
keir@19019 519 uint32_t base_mem = 640, ext_mem, alt_mem;
keir@14449 520
keir@19019 521 alt_mem = ext_mem = hvm_info->low_mem_pgend << PAGE_SHIFT;
keir@19019 522 ext_mem = (ext_mem > 0x0100000) ? (ext_mem - 0x0100000) >> 10 : 0;
keir@19019 523 if ( ext_mem > 0xffff )
keir@19019 524 ext_mem = 0xffff;
keir@19019 525 alt_mem = (alt_mem > 0x1000000) ? (alt_mem - 0x1000000) >> 16 : 0;
keir@14449 526
keir@14877 527 /* All BIOSes: conventional memory (CMOS *always* reports 640kB). */
keir@14449 528 cmos_outb(0x15, (uint8_t)(base_mem >> 0));
keir@14449 529 cmos_outb(0x16, (uint8_t)(base_mem >> 8));
keir@14449 530
keir@14449 531 /* All BIOSes: extended memory (1kB chunks above 1MB). */
keir@14449 532 cmos_outb(0x17, (uint8_t)( ext_mem >> 0));
keir@14449 533 cmos_outb(0x18, (uint8_t)( ext_mem >> 8));
keir@14449 534 cmos_outb(0x30, (uint8_t)( ext_mem >> 0));
keir@14449 535 cmos_outb(0x31, (uint8_t)( ext_mem >> 8));
keir@14449 536
keir@14449 537 /* Some BIOSes: alternative extended memory (64kB chunks above 16MB). */
keir@14449 538 cmos_outb(0x34, (uint8_t)( alt_mem >> 0));
keir@14449 539 cmos_outb(0x35, (uint8_t)( alt_mem >> 8));
Tim@13140 540 }
Tim@13140 541
keir@18394 542 static uint16_t init_xen_platform_io_base(void)
keir@18030 543 {
keir@18030 544 struct bios_info *bios_info = (struct bios_info *)ACPI_PHYSICAL_ADDRESS;
keir@18030 545 uint32_t devfn, bar_data;
keir@18030 546 uint16_t vendor_id, device_id;
keir@18030 547
keir@18394 548 bios_info->xen_pfiob = 0;
keir@18394 549
keir@18030 550 for ( devfn = 0; devfn < 128; devfn++ )
keir@18030 551 {
keir@18030 552 vendor_id = pci_readw(devfn, PCI_VENDOR_ID);
keir@18030 553 device_id = pci_readw(devfn, PCI_DEVICE_ID);
keir@18030 554 if ( (vendor_id != 0x5853) || (device_id != 0x0001) )
keir@18030 555 continue;
keir@18030 556 bar_data = pci_readl(devfn, PCI_BASE_ADDRESS_0);
keir@18030 557 bios_info->xen_pfiob = bar_data & PCI_BASE_ADDRESS_IO_MASK;
keir@18030 558 }
keir@18394 559
keir@18394 560 return bios_info->xen_pfiob;
keir@18030 561 }
keir@18030 562
keir@18994 563 /*
keir@18994 564 * Set up an empty TSS area for virtual 8086 mode to use.
keir@18901 565 * The only important thing is that it musn't have any bits set
keir@18994 566 * in the interrupt redirection bitmap, so all zeros will do.
keir@18994 567 */
keir@18901 568 static void init_vm86_tss(void)
keir@18901 569 {
keir@19019 570 void *tss;
keir@18901 571 struct xen_hvm_param p;
keir@18901 572
keir@19019 573 tss = mem_alloc(128, 128);
keir@19019 574 memset(tss, 0, 128);
keir@18901 575 p.domid = DOMID_SELF;
keir@18901 576 p.index = HVM_PARAM_VM86_TSS;
keir@19019 577 p.value = virt_to_phys(tss);
keir@18901 578 hypercall_hvm_op(HVMOP_set_param, &p);
keir@19019 579 printf("vm86 TSS at %08lx\n", virt_to_phys(tss));
keir@18901 580 }
keir@18901 581
keir@19017 582 /* Create an E820 table based on memory parameters provided in hvm_info. */
keir@19017 583 static void build_e820_table(void)
keir@18994 584 {
keir@19017 585 struct e820entry *e820 = E820;
keir@19017 586 unsigned int nr = 0;
keir@19017 587
keir@19017 588 /* 0x0-0x9FC00: Ordinary RAM. */
keir@19017 589 e820[nr].addr = 0x0;
keir@19017 590 e820[nr].size = 0x9FC00;
keir@19017 591 e820[nr].type = E820_RAM;
keir@19017 592 nr++;
keir@19017 593
keir@19017 594 /* 0x9FC00-0xA0000: Extended BIOS Data Area (EBDA). */
keir@19017 595 e820[nr].addr = 0x9FC00;
keir@19017 596 e820[nr].size = 0x400;
keir@19017 597 e820[nr].type = E820_RESERVED;
keir@19017 598 nr++;
keir@19017 599
keir@19017 600 /*
keir@19017 601 * Following regions are standard regions of the PC memory map.
keir@19017 602 * They are not covered by e820 regions. OSes will not use as RAM.
keir@19017 603 * 0xA0000-0xC0000: VGA memory-mapped I/O. Not covered by E820.
keir@19017 604 * 0xC0000-0xE0000: 16-bit devices, expansion ROMs (inc. vgabios).
keir@19017 605 * TODO: free pages which turn out to be unused.
keir@19017 606 */
keir@19017 607
keir@19017 608 /*
keir@19017 609 * 0xE0000-0x0F0000: PC-specific area. We place various tables here.
keir@19017 610 * 0xF0000-0x100000: System BIOS.
keir@19017 611 * TODO: free pages which turn out to be unused.
keir@19017 612 */
keir@19017 613 e820[nr].addr = 0xE0000;
keir@19017 614 e820[nr].size = 0x20000;
keir@19017 615 e820[nr].type = E820_RESERVED;
keir@19017 616 nr++;
keir@19017 617
keir@19017 618 /* Low RAM goes here. Reserve space for special pages. */
keir@19017 619 BUG_ON((hvm_info->low_mem_pgend << PAGE_SHIFT) < (2u << 20));
keir@19017 620 e820[nr].addr = 0x100000;
keir@19017 621 e820[nr].size = (hvm_info->low_mem_pgend << PAGE_SHIFT) - e820[nr].addr;
keir@19017 622 e820[nr].type = E820_RAM;
keir@19017 623 nr++;
keir@19017 624
keir@19035 625 /*
keir@19035 626 * Explicitly reserve space for special pages.
keir@19035 627 * This space starts at RESERVED_MEMBASE an extends to cover various
keir@19035 628 * fixed hardware mappings (e.g., LAPIC, IOAPIC, default SVGA framebuffer).
keir@19035 629 */
keir@19019 630 e820[nr].addr = RESERVED_MEMBASE;
keir@19019 631 e820[nr].size = (uint32_t)-e820[nr].addr;
keir@19019 632 e820[nr].type = E820_RESERVED;
keir@19019 633 nr++;
keir@19017 634
keir@19017 635 if ( hvm_info->high_mem_pgend )
keir@19017 636 {
keir@19017 637 e820[nr].addr = ((uint64_t)1 << 32);
keir@19017 638 e820[nr].size =
keir@19017 639 ((uint64_t)hvm_info->high_mem_pgend << PAGE_SHIFT) - e820[nr].addr;
keir@19017 640 e820[nr].type = E820_RAM;
keir@19017 641 nr++;
keir@19017 642 }
keir@19017 643
keir@18994 644 *E820_NR = nr;
keir@18994 645 }
keir@18994 646
kfraser@12548 647 int main(void)
kfraser@12548 648 {
keir@18827 649 int option_rom_sz = 0, vgabios_sz = 0, etherboot_sz = 0;
keir@18827 650 int rombios_sz, smbios_sz;
keir@19035 651 uint32_t etherboot_phys_addr, option_rom_phys_addr;
keir@18394 652 uint16_t xen_pfiob;
kaf24@12574 653
kfraser@12554 654 printf("HVM Loader\n");
kfraser@12548 655
kfraser@12548 656 init_hypercalls();
kfraser@12548 657
keir@17363 658 printf("CPU speed is %u MHz\n", get_cpu_mhz());
keir@17363 659
keir@19022 660 apic_setup();
keir@19022 661 pci_setup();
keir@19022 662
keir@17463 663 smp_initialise();
keir@17463 664
keir@18358 665 perform_tests();
keir@18358 666
kfraser@12554 667 printf("Writing SMBIOS tables ...\n");
kfraser@14959 668 smbios_sz = hvm_write_smbios_tables();
kfraser@12548 669
kfraser@12554 670 printf("Loading ROMBIOS ...\n");
kfraser@14959 671 rombios_sz = sizeof(rombios);
kfraser@14959 672 if ( rombios_sz > 0x10000 )
kfraser@14959 673 rombios_sz = 0x10000;
kfraser@14959 674 memcpy((void *)ROMBIOS_PHYSICAL_ADDRESS, rombios, rombios_sz);
kaf24@13656 675 highbios_setup();
kfraser@12548 676
keir@19015 677 if ( (hvm_info->nr_vcpus > 1) || hvm_info->apic_mode )
kaf24@12574 678 create_mp_tables();
kfraser@12548 679
keir@17694 680 switch ( virtual_vga )
kfraser@12548 681 {
keir@17694 682 case VGA_cirrus:
kfraser@12554 683 printf("Loading Cirrus VGABIOS ...\n");
kfraser@12548 684 memcpy((void *)VGABIOS_PHYSICAL_ADDRESS,
kfraser@12548 685 vgabios_cirrusvga, sizeof(vgabios_cirrusvga));
keir@18827 686 vgabios_sz = round_option_rom(sizeof(vgabios_cirrusvga));
keir@17694 687 break;
keir@17694 688 case VGA_std:
kfraser@12554 689 printf("Loading Standard VGABIOS ...\n");
kfraser@12548 690 memcpy((void *)VGABIOS_PHYSICAL_ADDRESS,
kfraser@12548 691 vgabios_stdvga, sizeof(vgabios_stdvga));
keir@18827 692 vgabios_sz = round_option_rom(sizeof(vgabios_stdvga));
keir@17694 693 break;
keir@17694 694 default:
keir@17694 695 printf("No emulated VGA adaptor ...\n");
keir@17694 696 break;
kfraser@12548 697 }
kfraser@12548 698
keir@18827 699 etherboot_phys_addr = VGABIOS_PHYSICAL_ADDRESS + vgabios_sz;
keir@18994 700 if ( etherboot_phys_addr < OPTIONROM_PHYSICAL_ADDRESS )
keir@18994 701 etherboot_phys_addr = OPTIONROM_PHYSICAL_ADDRESS;
keir@18827 702 etherboot_sz = scan_etherboot_nic(etherboot_phys_addr);
keir@18827 703
keir@18827 704 option_rom_phys_addr = etherboot_phys_addr + etherboot_sz;
keir@18827 705 option_rom_sz = pci_load_option_roms(option_rom_phys_addr);
Tim@13140 706
keir@19015 707 if ( hvm_info->acpi_enabled )
kfraser@12548 708 {
kfraser@12554 709 printf("Loading ACPI ...\n");
keir@18191 710 acpi_build_tables();
kfraser@12548 711 }
kfraser@12548 712
keir@18901 713 init_vm86_tss();
keir@18901 714
keir@14449 715 cmos_write_memory_size();
keir@14449 716
kfraser@14959 717 printf("BIOS map:\n");
kfraser@14959 718 if ( vgabios_sz )
kfraser@14959 719 printf(" %05x-%05x: VGA BIOS\n",
kfraser@14959 720 VGABIOS_PHYSICAL_ADDRESS,
kfraser@14959 721 VGABIOS_PHYSICAL_ADDRESS + vgabios_sz - 1);
kfraser@14959 722 if ( etherboot_sz )
kfraser@14959 723 printf(" %05x-%05x: Etherboot ROM\n",
keir@18826 724 etherboot_phys_addr,
keir@18826 725 etherboot_phys_addr + etherboot_sz - 1);
keir@18827 726 if ( option_rom_sz )
keir@18827 727 printf(" %05x-%05x: PCI Option ROMs\n",
keir@18827 728 option_rom_phys_addr,
keir@18827 729 option_rom_phys_addr + option_rom_sz - 1);
kfraser@14959 730 if ( smbios_sz )
kfraser@14959 731 printf(" %05x-%05x: SMBIOS tables\n",
kfraser@14959 732 SMBIOS_PHYSICAL_ADDRESS,
kfraser@14959 733 SMBIOS_PHYSICAL_ADDRESS + smbios_sz - 1);
kfraser@14959 734 if ( rombios_sz )
kfraser@14959 735 printf(" %05x-%05x: Main BIOS\n",
kfraser@14959 736 ROMBIOS_PHYSICAL_ADDRESS,
kfraser@14959 737 ROMBIOS_PHYSICAL_ADDRESS + rombios_sz - 1);
kfraser@14959 738
keir@18394 739 xen_pfiob = init_xen_platform_io_base();
keir@18030 740
keir@19019 741 build_e820_table();
keir@19019 742
kfraser@14373 743 printf("Invoking ROMBIOS ...\n");
kfraser@12548 744 return 0;
kfraser@12548 745 }
kfraser@12548 746
kfraser@12548 747 /*
kfraser@12548 748 * Local variables:
kfraser@12548 749 * mode: C
kfraser@12548 750 * c-set-style: "BSD"
kfraser@12548 751 * c-basic-offset: 4
kfraser@12548 752 * tab-width: 4
kfraser@12548 753 * indent-tabs-mode: nil
kfraser@12548 754 * End:
kfraser@12548 755 */