debuggers.hg

view tools/firmware/hvmloader/hvmloader.c @ 16996:42369d21641d

hvm: Etherboot for E100 NIC
Signed-off-by: Ian Jackson <ian.jackson@eu.citrix.com>
Signed-off-by: Yosuke Iwamatsu <y-iwamatsu@ab.jp.nec.com>
author Keir Fraser <keir.fraser@citrix.com>
date Thu Jan 31 16:23:35 2008 +0000 (2008-01-31)
parents b006c58b055e
children 9d0e86d8c1d1
line source
1 /*
2 * hvmloader.c: HVM ROMBIOS/VGABIOS/ACPI/VMXAssist image loader.
3 *
4 * Leendert van Doorn, leendert@watson.ibm.com
5 * Copyright (c) 2005, International Business Machines Corporation.
6 *
7 * Copyright (c) 2006, Keir Fraser, XenSource Inc.
8 *
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms and conditions of the GNU General Public License,
11 * version 2, as published by the Free Software Foundation.
12 *
13 * This program is distributed in the hope it will be useful, but WITHOUT
14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
16 * more details.
17 *
18 * You should have received a copy of the GNU General Public License along with
19 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
20 * Place - Suite 330, Boston, MA 02111-1307 USA.
21 */
23 #include "roms.h"
24 #include "acpi/acpi2_0.h"
25 #include "hypercall.h"
26 #include "util.h"
27 #include "config.h"
28 #include "apic_regs.h"
29 #include "pci_regs.h"
30 #include "e820.h"
31 #include <xen/version.h>
32 #include <xen/hvm/params.h>
34 asm(
35 " .text \n"
36 " .globl _start \n"
37 "_start: \n"
38 /* C runtime kickoff. */
39 " cld \n"
40 " cli \n"
41 " movl $stack_top,%esp \n"
42 " movl %esp,%ebp \n"
43 " movl %eax,initial_eax \n"
44 " call main \n"
45 /* Relocate real-mode trampoline to 0x0. */
46 " mov $trampoline_start,%esi \n"
47 " xor %edi,%edi \n"
48 " mov $trampoline_end,%ecx \n"
49 " sub %esi,%ecx \n"
50 " rep movsb \n"
51 /* Load real-mode compatible segment state (base 0x0000, limit 0xffff). */
52 " lgdt gdt_desr \n"
53 " mov $0x0010,%ax \n"
54 " mov %ax,%ds \n"
55 " mov %ax,%es \n"
56 " mov %ax,%fs \n"
57 " mov %ax,%gs \n"
58 " mov %ax,%ss \n"
59 /* Initialise all 32-bit GPRs to zero. */
60 " xor %eax,%eax \n"
61 " xor %ebx,%ebx \n"
62 " xor %ecx,%ecx \n"
63 " xor %edx,%edx \n"
64 " xor %esp,%esp \n"
65 " xor %ebp,%ebp \n"
66 " xor %esi,%esi \n"
67 " xor %edi,%edi \n"
68 /* Enter real mode, reload all segment registers and IDT. */
69 " ljmp $0x8,$0x0 \n"
70 "trampoline_start: .code16 \n"
71 " mov %eax,%cr0 \n"
72 " ljmp $0,$1f-trampoline_start\n"
73 "1: mov %ax,%ds \n"
74 " mov %ax,%es \n"
75 " mov %ax,%fs \n"
76 " mov %ax,%gs \n"
77 " mov %ax,%ss \n"
78 " lidt 1f-trampoline_start \n"
79 " ljmp $0xf000,$0xfff0 \n"
80 "1: .word 0x3ff,0,0 \n"
81 "trampoline_end: .code32 \n"
82 " \n"
83 "gdt_desr: \n"
84 " .word gdt_end - gdt - 1 \n"
85 " .long gdt \n"
86 " \n"
87 " .align 8 \n"
88 "gdt: \n"
89 " .quad 0x0000000000000000 \n"
90 " .quad 0x00009a000000ffff \n" /* Ring 0 code, base 0 limit 0xffff */
91 " .quad 0x000092000000ffff \n" /* Ring 0 data, base 0 limit 0xffff */
92 "gdt_end: \n"
93 " \n"
94 " .bss \n"
95 " .align 8 \n"
96 "stack: \n"
97 " .skip 0x4000 \n"
98 "stack_top: \n"
99 );
101 static unsigned int initial_eax;
103 void create_mp_tables(void);
104 int hvm_write_smbios_tables(void);
106 static int
107 cirrus_check(void)
108 {
109 outw(0x3C4, 0x9206);
110 return inb(0x3C5) == 0x12;
111 }
113 static int
114 check_amd(void)
115 {
116 char id[12];
118 __asm__ __volatile__ (
119 "cpuid"
120 : "=b" (*(int *)(&id[0])),
121 "=c" (*(int *)(&id[8])),
122 "=d" (*(int *)(&id[4]))
123 : "a" (0) );
124 return __builtin_memcmp(id, "AuthenticAMD", 12) == 0;
125 }
127 static int
128 use_vmxassist(void)
129 {
130 return !check_amd() && !initial_eax;
131 }
133 static void
134 wrmsr(uint32_t idx, uint64_t v)
135 {
136 __asm__ __volatile__ (
137 "wrmsr"
138 : : "c" (idx), "a" ((uint32_t)v), "d" ((uint32_t)(v>>32)) );
139 }
141 static void
142 init_hypercalls(void)
143 {
144 uint32_t eax, ebx, ecx, edx;
145 unsigned long i;
146 char signature[13];
147 xen_extraversion_t extraversion;
149 cpuid(0x40000000, &eax, &ebx, &ecx, &edx);
151 *(uint32_t *)(signature + 0) = ebx;
152 *(uint32_t *)(signature + 4) = ecx;
153 *(uint32_t *)(signature + 8) = edx;
154 signature[12] = '\0';
156 if ( strcmp("XenVMMXenVMM", signature) || (eax < 0x40000002) )
157 {
158 printf("FATAL: Xen hypervisor not detected\n");
159 __asm__ __volatile__( "ud2" );
160 }
162 /* Fill in hypercall transfer pages. */
163 cpuid(0x40000002, &eax, &ebx, &ecx, &edx);
164 for ( i = 0; i < eax; i++ )
165 wrmsr(ebx, HYPERCALL_PHYSICAL_ADDRESS + (i << 12) + i);
167 /* Print version information. */
168 cpuid(0x40000001, &eax, &ebx, &ecx, &edx);
169 hypercall_xen_version(XENVER_extraversion, extraversion);
170 printf("Detected Xen v%u.%u%s\n", eax >> 16, eax & 0xffff, extraversion);
171 }
173 static void apic_setup(void)
174 {
175 /* Set the IOAPIC ID to tha static value used in the MP/ACPI tables. */
176 ioapic_write(0x00, IOAPIC_ID);
178 /* Set up Virtual Wire mode. */
179 lapic_write(APIC_SPIV, APIC_SPIV_APIC_ENABLED | 0xFF);
180 lapic_write(APIC_LVT0, APIC_MODE_EXTINT << 8);
181 lapic_write(APIC_LVT1, APIC_MODE_NMI << 8);
182 }
184 static void pci_setup(void)
185 {
186 uint32_t base, devfn, bar_reg, bar_data, bar_sz, cmd;
187 uint16_t class, vendor_id, device_id;
188 unsigned int bar, pin, link, isa_irq;
190 /* Resources assignable to PCI devices via BARs. */
191 struct resource {
192 uint32_t base, max;
193 } *resource;
194 struct resource mem_resource = { 0xf0000000, 0xfc000000 };
195 struct resource io_resource = { 0xc000, 0x10000 };
197 /* Create a list of device BARs in descending order of size. */
198 struct bars {
199 uint32_t devfn, bar_reg, bar_sz;
200 } *bars = (struct bars *)0xc0000;
201 unsigned int i, nr_bars = 0;
203 /* Program PCI-ISA bridge with appropriate link routes. */
204 isa_irq = 0;
205 for ( link = 0; link < 4; link++ )
206 {
207 do { isa_irq = (isa_irq + 1) & 15;
208 } while ( !(PCI_ISA_IRQ_MASK & (1U << isa_irq)) );
209 pci_writeb(PCI_ISA_DEVFN, 0x60 + link, isa_irq);
210 printf("PCI-ISA link %u routed to IRQ%u\n", link, isa_irq);
211 }
213 /* Program ELCR to match PCI-wired IRQs. */
214 outb(0x4d0, (uint8_t)(PCI_ISA_IRQ_MASK >> 0));
215 outb(0x4d1, (uint8_t)(PCI_ISA_IRQ_MASK >> 8));
217 /* Scan the PCI bus and map resources. */
218 for ( devfn = 0; devfn < 128; devfn++ )
219 {
220 class = pci_readw(devfn, PCI_CLASS_DEVICE);
221 vendor_id = pci_readw(devfn, PCI_VENDOR_ID);
222 device_id = pci_readw(devfn, PCI_DEVICE_ID);
223 if ( (vendor_id == 0xffff) && (device_id == 0xffff) )
224 continue;
226 ASSERT((devfn != PCI_ISA_DEVFN) ||
227 ((vendor_id == 0x8086) && (device_id == 0x7000)));
229 switch ( class )
230 {
231 case 0x0680:
232 ASSERT((vendor_id == 0x8086) && (device_id == 0x7113));
233 /*
234 * PIIX4 ACPI PM. Special device with special PCI config space.
235 * No ordinary BARs.
236 */
237 pci_writew(devfn, 0x20, 0x0000); /* No smb bus IO enable */
238 pci_writew(devfn, 0x22, 0x0000);
239 pci_writew(devfn, 0x3c, 0x0009); /* Hardcoded IRQ9 */
240 pci_writew(devfn, 0x3d, 0x0001);
241 break;
242 case 0x0101:
243 /* PIIX3 IDE */
244 ASSERT((vendor_id == 0x8086) && (device_id == 0x7010));
245 pci_writew(devfn, 0x40, 0x8000); /* enable IDE0 */
246 pci_writew(devfn, 0x42, 0x8000); /* enable IDE1 */
247 /* fall through */
248 default:
249 /* Default memory mappings. */
250 for ( bar = 0; bar < 7; bar++ )
251 {
252 bar_reg = PCI_BASE_ADDRESS_0 + 4*bar;
253 if ( bar == 6 )
254 bar_reg = PCI_ROM_ADDRESS;
256 bar_data = pci_readl(devfn, bar_reg);
257 pci_writel(devfn, bar_reg, ~0);
258 bar_sz = pci_readl(devfn, bar_reg);
259 pci_writel(devfn, bar_reg, bar_data);
260 if ( bar_sz == 0 )
261 continue;
263 bar_sz &= (((bar_data & PCI_BASE_ADDRESS_SPACE) ==
264 PCI_BASE_ADDRESS_SPACE_MEMORY) ?
265 PCI_BASE_ADDRESS_MEM_MASK :
266 (PCI_BASE_ADDRESS_IO_MASK & 0xffff));
267 bar_sz &= ~(bar_sz - 1);
269 for ( i = 0; i < nr_bars; i++ )
270 if ( bars[i].bar_sz < bar_sz )
271 break;
273 if ( i != nr_bars )
274 memmove(&bars[i+1], &bars[i], (nr_bars-i) * sizeof(*bars));
276 bars[i].devfn = devfn;
277 bars[i].bar_reg = bar_reg;
278 bars[i].bar_sz = bar_sz;
280 nr_bars++;
281 }
282 break;
283 }
285 /* Map the interrupt. */
286 pin = pci_readb(devfn, PCI_INTERRUPT_PIN);
287 if ( pin != 0 )
288 {
289 /* This is the barber's pole mapping used by Xen. */
290 link = ((pin - 1) + (devfn >> 3)) & 3;
291 isa_irq = pci_readb(PCI_ISA_DEVFN, 0x60 + link);
292 pci_writeb(devfn, PCI_INTERRUPT_LINE, isa_irq);
293 printf("pci dev %02x:%x INT%c->IRQ%u\n",
294 devfn>>3, devfn&7, 'A'+pin-1, isa_irq);
295 }
296 }
298 /* Assign iomem and ioport resources in descending order of size. */
299 for ( i = 0; i < nr_bars; i++ )
300 {
301 devfn = bars[i].devfn;
302 bar_reg = bars[i].bar_reg;
303 bar_sz = bars[i].bar_sz;
305 bar_data = pci_readl(devfn, bar_reg);
307 if ( (bar_data & PCI_BASE_ADDRESS_SPACE) ==
308 PCI_BASE_ADDRESS_SPACE_MEMORY )
309 {
310 resource = &mem_resource;
311 bar_data &= ~PCI_BASE_ADDRESS_MEM_MASK;
312 }
313 else
314 {
315 resource = &io_resource;
316 bar_data &= ~PCI_BASE_ADDRESS_IO_MASK;
317 }
319 base = (resource->base + bar_sz - 1) & ~(bar_sz - 1);
320 bar_data |= base;
321 base += bar_sz;
323 if ( (base < resource->base) || (base > resource->max) )
324 {
325 printf("pci dev %02x:%x bar %02x size %08x: no space for "
326 "resource!\n", devfn>>3, devfn&7, bar_reg, bar_sz);
327 continue;
328 }
330 resource->base = base;
332 pci_writel(devfn, bar_reg, bar_data);
333 printf("pci dev %02x:%x bar %02x size %08x: %08x\n",
334 devfn>>3, devfn&7, bar_reg, bar_sz, bar_data);
336 /* Now enable the memory or I/O mapping. */
337 cmd = pci_readw(devfn, PCI_COMMAND);
338 if ( (bar_reg == PCI_ROM_ADDRESS) ||
339 ((bar_data & PCI_BASE_ADDRESS_SPACE) ==
340 PCI_BASE_ADDRESS_SPACE_MEMORY) )
341 cmd |= PCI_COMMAND_MEMORY;
342 else
343 cmd |= PCI_COMMAND_IO;
344 pci_writew(devfn, PCI_COMMAND, cmd);
345 }
346 }
348 static int must_load_extboot(void)
349 {
350 return (inb(0x404) == 1);
351 }
353 /*
354 * Scan the PCI bus for the first NIC supported by etherboot, and copy
355 * the corresponding rom data to *copy_rom_dest. Returns the length of the
356 * selected rom, or 0 if no NIC found.
357 */
358 static int scan_etherboot_nic(void *copy_rom_dest)
359 {
360 static struct etherboots_table_entry {
361 char *name;
362 void *etherboot_rom;
363 int etherboot_sz;
364 uint16_t vendor, device;
365 } etherboots_table[] = {
366 #define ETHERBOOT_ROM(name, vendor, device) \
367 { #name, etherboot_##name, sizeof(etherboot_##name), vendor, device },
368 ETHERBOOT_ROM_LIST
369 { 0 }
370 };
372 uint32_t devfn;
373 uint16_t class, vendor_id, device_id;
374 struct etherboots_table_entry *eb;
376 for ( devfn = 0; devfn < 128; devfn++ )
377 {
378 class = pci_readw(devfn, PCI_CLASS_DEVICE);
379 vendor_id = pci_readw(devfn, PCI_VENDOR_ID);
380 device_id = pci_readw(devfn, PCI_DEVICE_ID);
382 if ( (vendor_id == 0xffff) && (device_id == 0xffff) )
383 continue;
385 if ( class != 0x0200 ) /* Not a NIC */
386 continue;
388 for ( eb = etherboots_table; eb->name; eb++ )
389 if (eb->vendor == vendor_id &&
390 eb->device == device_id)
391 goto found;
392 }
394 return 0;
396 found:
397 printf("Loading %s Etherboot PXE ROM ...\n", eb->name);
398 memcpy(copy_rom_dest, eb->etherboot_rom, eb->etherboot_sz);
399 return eb->etherboot_sz;
400 }
402 /* Replace possibly erroneous memory-size CMOS fields with correct values. */
403 static void cmos_write_memory_size(void)
404 {
405 struct e820entry *map = HVM_E820;
406 int i, nr = *HVM_E820_NR;
407 uint32_t base_mem = 640, ext_mem = 0, alt_mem = 0;
409 for ( i = 0; i < nr; i++ )
410 if ( (map[i].addr >= 0x100000) && (map[i].type == E820_RAM) )
411 break;
413 if ( i != nr )
414 {
415 alt_mem = ext_mem = map[i].addr + map[i].size;
416 ext_mem = (ext_mem > 0x0100000) ? (ext_mem - 0x0100000) >> 10 : 0;
417 if ( ext_mem > 0xffff )
418 ext_mem = 0xffff;
419 alt_mem = (alt_mem > 0x1000000) ? (alt_mem - 0x1000000) >> 16 : 0;
420 }
422 /* All BIOSes: conventional memory (CMOS *always* reports 640kB). */
423 cmos_outb(0x15, (uint8_t)(base_mem >> 0));
424 cmos_outb(0x16, (uint8_t)(base_mem >> 8));
426 /* All BIOSes: extended memory (1kB chunks above 1MB). */
427 cmos_outb(0x17, (uint8_t)( ext_mem >> 0));
428 cmos_outb(0x18, (uint8_t)( ext_mem >> 8));
429 cmos_outb(0x30, (uint8_t)( ext_mem >> 0));
430 cmos_outb(0x31, (uint8_t)( ext_mem >> 8));
432 /* Some BIOSes: alternative extended memory (64kB chunks above 16MB). */
433 cmos_outb(0x34, (uint8_t)( alt_mem >> 0));
434 cmos_outb(0x35, (uint8_t)( alt_mem >> 8));
435 }
437 int main(void)
438 {
439 int acpi_sz = 0, vgabios_sz = 0, etherboot_sz = 0, rombios_sz, smbios_sz;
440 int extboot_sz = 0;
442 printf("HVM Loader\n");
444 init_hypercalls();
446 printf("Writing SMBIOS tables ...\n");
447 smbios_sz = hvm_write_smbios_tables();
449 printf("Loading ROMBIOS ...\n");
450 rombios_sz = sizeof(rombios);
451 if ( rombios_sz > 0x10000 )
452 rombios_sz = 0x10000;
453 memcpy((void *)ROMBIOS_PHYSICAL_ADDRESS, rombios, rombios_sz);
454 highbios_setup();
456 apic_setup();
457 pci_setup();
459 if ( (get_vcpu_nr() > 1) || get_apic_mode() )
460 create_mp_tables();
462 if ( cirrus_check() )
463 {
464 printf("Loading Cirrus VGABIOS ...\n");
465 memcpy((void *)VGABIOS_PHYSICAL_ADDRESS,
466 vgabios_cirrusvga, sizeof(vgabios_cirrusvga));
467 vgabios_sz = sizeof(vgabios_cirrusvga);
468 }
469 else
470 {
471 printf("Loading Standard VGABIOS ...\n");
472 memcpy((void *)VGABIOS_PHYSICAL_ADDRESS,
473 vgabios_stdvga, sizeof(vgabios_stdvga));
474 vgabios_sz = sizeof(vgabios_stdvga);
475 }
477 etherboot_sz = scan_etherboot_nic((void*)ETHERBOOT_PHYSICAL_ADDRESS);
479 if ( must_load_extboot() )
480 {
481 printf("Loading EXTBOOT ...\n");
482 memcpy((void *)EXTBOOT_PHYSICAL_ADDRESS,
483 extboot, sizeof(extboot));
484 extboot_sz = sizeof(extboot);
485 }
487 if ( get_acpi_enabled() )
488 {
489 printf("Loading ACPI ...\n");
490 acpi_sz = acpi_build_tables((uint8_t *)ACPI_PHYSICAL_ADDRESS);
491 ASSERT((ACPI_PHYSICAL_ADDRESS + acpi_sz) <= 0xF0000);
492 }
494 cmos_write_memory_size();
496 printf("BIOS map:\n");
497 if ( vgabios_sz )
498 printf(" %05x-%05x: VGA BIOS\n",
499 VGABIOS_PHYSICAL_ADDRESS,
500 VGABIOS_PHYSICAL_ADDRESS + vgabios_sz - 1);
501 if ( etherboot_sz )
502 printf(" %05x-%05x: Etherboot ROM\n",
503 ETHERBOOT_PHYSICAL_ADDRESS,
504 ETHERBOOT_PHYSICAL_ADDRESS + etherboot_sz - 1);
505 if ( extboot_sz )
506 printf(" %05x-%05x: Extboot ROM\n",
507 EXTBOOT_PHYSICAL_ADDRESS,
508 EXTBOOT_PHYSICAL_ADDRESS + extboot_sz - 1);
509 if ( use_vmxassist() )
510 printf(" %05x-%05x: VMXAssist\n",
511 VMXASSIST_PHYSICAL_ADDRESS,
512 VMXASSIST_PHYSICAL_ADDRESS + sizeof(vmxassist) - 1);
513 if ( smbios_sz )
514 printf(" %05x-%05x: SMBIOS tables\n",
515 SMBIOS_PHYSICAL_ADDRESS,
516 SMBIOS_PHYSICAL_ADDRESS + smbios_sz - 1);
517 if ( acpi_sz )
518 printf(" %05x-%05x: ACPI tables\n",
519 ACPI_PHYSICAL_ADDRESS,
520 ACPI_PHYSICAL_ADDRESS + acpi_sz - 1);
521 if ( rombios_sz )
522 printf(" %05x-%05x: Main BIOS\n",
523 ROMBIOS_PHYSICAL_ADDRESS,
524 ROMBIOS_PHYSICAL_ADDRESS + rombios_sz - 1);
526 if ( use_vmxassist() )
527 {
528 printf("Loading VMXAssist ...\n");
529 memcpy((void *)VMXASSIST_PHYSICAL_ADDRESS,
530 vmxassist, sizeof(vmxassist));
532 printf("VMX go ...\n");
533 __asm__ __volatile__(
534 "jmp *%%eax"
535 : : "a" (VMXASSIST_PHYSICAL_ADDRESS), "d" (0)
536 );
537 }
539 printf("Invoking ROMBIOS ...\n");
540 return 0;
541 }
543 /*
544 * Local variables:
545 * mode: C
546 * c-set-style: "BSD"
547 * c-basic-offset: 4
548 * tab-width: 4
549 * indent-tabs-mode: nil
550 * End:
551 */