ia64/xen-unstable

view tools/firmware/hvmloader/hvmloader.c @ 14449:cf32c9e54c8f

General hvmloader cleanups and write memory fields of CMOS with
correct values.
Signed-off-by: Keir Fraser <keir@xensource.com>
author Keir Fraser <keir@xensource.com>
date Fri Mar 16 23:34:24 2007 +0000 (2007-03-16)
parents f233456b89d5
children ce2b25e1c8f6
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 */
22 #include "roms.h"
23 #include "acpi/acpi2_0.h" /* for ACPI_PHYSICAL_ADDRESS */
24 #include "hypercall.h"
25 #include "util.h"
26 #include "smbios.h"
27 #include "config.h"
28 #include "apic_regs.h"
29 #include "pci_regs.h"
30 #include <xen/version.h>
31 #include <xen/hvm/params.h>
33 /* memory map */
34 #define HYPERCALL_PHYSICAL_ADDRESS 0x00080000
35 #define VGABIOS_PHYSICAL_ADDRESS 0x000C0000
36 #define ETHERBOOT_PHYSICAL_ADDRESS 0x000C8000
37 #define VMXASSIST_PHYSICAL_ADDRESS 0x000D0000
38 #define ROMBIOS_PHYSICAL_ADDRESS 0x000F0000
40 asm(
41 " .text \n"
42 " .globl _start \n"
43 "_start: \n"
44 /* C runtime kickoff. */
45 " cld \n"
46 " cli \n"
47 " movl $stack_top,%esp \n"
48 " movl %esp,%ebp \n"
49 " call main \n"
50 /* Relocate real-mode trampoline to 0x0. */
51 " mov $trampoline_start,%esi \n"
52 " xor %edi,%edi \n"
53 " mov $trampoline_end,%ecx \n"
54 " sub %esi,%ecx \n"
55 " rep movsb \n"
56 /* Load real-mode compatible segment state (base 0x0000, limit 0xffff). */
57 " lgdt gdt_desr \n"
58 " mov $0x0010,%ax \n"
59 " mov %ax,%ds \n"
60 " mov %ax,%es \n"
61 " mov %ax,%fs \n"
62 " mov %ax,%gs \n"
63 " mov %ax,%ss \n"
64 /* Initialise all 32-bit GPRs to zero. */
65 " xor %eax,%eax \n"
66 " xor %ebx,%ebx \n"
67 " xor %ecx,%ecx \n"
68 " xor %edx,%edx \n"
69 " xor %esp,%esp \n"
70 " xor %ebp,%ebp \n"
71 " xor %esi,%esi \n"
72 " xor %edi,%edi \n"
73 /* Enter real mode, reload all segment registers and IDT. */
74 " ljmp $0x8,$0x0 \n"
75 "trampoline_start: .code16 \n"
76 " mov %eax,%cr0 \n"
77 " ljmp $0,$1f-trampoline_start\n"
78 "1: mov %ax,%ds \n"
79 " mov %ax,%es \n"
80 " mov %ax,%fs \n"
81 " mov %ax,%gs \n"
82 " mov %ax,%ss \n"
83 " lidt 1f-trampoline_start \n"
84 " ljmp $0xf000,$0xfff0 \n"
85 "1: .word 0x3ff,0,0 \n"
86 "trampoline_end: .code32 \n"
87 " \n"
88 "gdt_desr: \n"
89 " .word gdt_end - gdt - 1 \n"
90 " .long gdt \n"
91 " \n"
92 " .align 8 \n"
93 "gdt: \n"
94 " .quad 0x0000000000000000 \n"
95 " .quad 0x00009a000000ffff \n" /* Ring 0 code, base 0 limit 0xffff */
96 " .quad 0x000092000000ffff \n" /* Ring 0 data, base 0 limit 0xffff */
97 "gdt_end: \n"
98 " \n"
99 " .bss \n"
100 " .align 8 \n"
101 "stack: \n"
102 " .skip 0x4000 \n"
103 "stack_top: \n"
104 );
106 extern void create_mp_tables(void);
108 static int
109 cirrus_check(void)
110 {
111 outw(0x3C4, 0x9206);
112 return inb(0x3C5) == 0x12;
113 }
115 static int
116 check_amd(void)
117 {
118 char id[12];
120 __asm__ __volatile__ (
121 "cpuid"
122 : "=b" (*(int *)(&id[0])),
123 "=c" (*(int *)(&id[8])),
124 "=d" (*(int *)(&id[4]))
125 : "a" (0) );
126 return __builtin_memcmp(id, "AuthenticAMD", 12) == 0;
127 }
129 static void
130 wrmsr(uint32_t idx, uint64_t v)
131 {
132 __asm__ __volatile__ (
133 "wrmsr"
134 : : "c" (idx), "a" ((uint32_t)v), "d" ((uint32_t)(v>>32)) );
135 }
137 static void
138 init_hypercalls(void)
139 {
140 uint32_t eax, ebx, ecx, edx;
141 unsigned long i;
142 char signature[13];
143 xen_extraversion_t extraversion;
145 cpuid(0x40000000, &eax, &ebx, &ecx, &edx);
147 *(uint32_t *)(signature + 0) = ebx;
148 *(uint32_t *)(signature + 4) = ecx;
149 *(uint32_t *)(signature + 8) = edx;
150 signature[12] = '\0';
152 if ( strcmp("XenVMMXenVMM", signature) || (eax < 0x40000002) )
153 {
154 printf("FATAL: Xen hypervisor not detected\n");
155 __asm__ __volatile__( "ud2" );
156 }
158 /* Fill in hypercall transfer pages. */
159 cpuid(0x40000002, &eax, &ebx, &ecx, &edx);
160 for ( i = 0; i < eax; i++ )
161 wrmsr(ebx, HYPERCALL_PHYSICAL_ADDRESS + (i << 12) + i);
163 /* Print version information. */
164 cpuid(0x40000001, &eax, &ebx, &ecx, &edx);
165 hypercall_xen_version(XENVER_extraversion, extraversion);
166 printf("Detected Xen v%u.%u%s\n", eax >> 16, eax & 0xffff, extraversion);
167 }
169 static void apic_setup(void)
170 {
171 /* Set the IOAPIC ID to tha static value used in the MP/ACPI tables. */
172 ioapic_write(0x00, IOAPIC_ID);
174 /* Set up Virtual Wire mode. */
175 lapic_write(APIC_SPIV, APIC_SPIV_APIC_ENABLED | 0xFF);
176 lapic_write(APIC_LVT0, APIC_MODE_EXTINT << 8);
177 lapic_write(APIC_LVT1, APIC_MODE_NMI << 8);
178 }
180 static void pci_setup(void)
181 {
182 uint32_t devfn, bar_reg, bar_data, bar_sz, cmd;
183 uint32_t *base, io_base = 0xc000, mem_base = HVM_BELOW_4G_MMIO_START;
184 uint16_t class, vendor_id, device_id;
185 unsigned int bar, pin, link, isa_irq;
187 /* Program PCI-ISA bridge with appropriate link routes. */
188 link = 0;
189 for ( isa_irq = 0; isa_irq < 15; isa_irq++ )
190 {
191 if ( !(PCI_ISA_IRQ_MASK & (1U << isa_irq)) )
192 continue;
193 pci_writeb(PCI_ISA_DEVFN, 0x60 + link, isa_irq);
194 printf("PCI-ISA link %u routed to IRQ%u\n", link, isa_irq);
195 if ( link++ == 4 )
196 break;
197 }
199 /* Program ELCR to match PCI-wired IRQs. */
200 outb(0x4d0, (uint8_t)(PCI_ISA_IRQ_MASK >> 0));
201 outb(0x4d1, (uint8_t)(PCI_ISA_IRQ_MASK >> 8));
203 /* Scan the PCI bus and map resources. */
204 for ( devfn = 0; devfn < 128; devfn++ )
205 {
206 class = pci_readw(devfn, PCI_CLASS_DEVICE);
207 vendor_id = pci_readw(devfn, PCI_VENDOR_ID);
208 device_id = pci_readw(devfn, PCI_DEVICE_ID);
209 if ( (vendor_id == 0xffff) && (device_id == 0xffff) )
210 continue;
212 ASSERT((devfn != PCI_ISA_DEVFN) ||
213 ((vendor_id == 0x8086) && (device_id == 0x7000)));
215 switch ( class )
216 {
217 case 0x0680:
218 ASSERT((vendor_id == 0x8086) && (device_id == 0x7113));
219 /*
220 * PIIX4 ACPI PM. Special device with special PCI config space.
221 * No ordinary BARs.
222 */
223 pci_writew(devfn, 0x20, 0x0000); /* No smb bus IO enable */
224 pci_writew(devfn, 0x22, 0x0000);
225 pci_writew(devfn, 0x3c, 0x0009); /* Hardcoded IRQ9 */
226 pci_writew(devfn, 0x3d, 0x0001);
227 break;
228 case 0x0101:
229 /* PIIX3 IDE */
230 ASSERT((vendor_id == 0x8086) && (device_id == 0x7010));
231 pci_writew(devfn, 0x40, 0x8000); /* enable IDE0 */
232 pci_writew(devfn, 0x42, 0x8000); /* enable IDE1 */
233 /* fall through */
234 default:
235 /* Default memory mappings. */
236 for ( bar = 0; bar < 7; bar++ )
237 {
238 bar_reg = PCI_BASE_ADDRESS_0 + 4*bar;
239 if ( bar == 6 )
240 bar_reg = PCI_ROM_ADDRESS;
242 bar_data = pci_readl(devfn, bar_reg);
244 pci_writel(devfn, bar_reg, ~0);
245 bar_sz = pci_readl(devfn, bar_reg);
246 if ( bar_sz == 0 )
247 continue;
249 if ( (bar_data & PCI_BASE_ADDRESS_SPACE) ==
250 PCI_BASE_ADDRESS_SPACE_MEMORY )
251 {
252 base = &mem_base;
253 bar_sz &= PCI_BASE_ADDRESS_MEM_MASK;
254 bar_data &= ~PCI_BASE_ADDRESS_MEM_MASK;
255 }
256 else
257 {
258 base = &io_base;
259 bar_sz &= PCI_BASE_ADDRESS_IO_MASK & 0xffff;
260 bar_data &= ~PCI_BASE_ADDRESS_IO_MASK;
261 }
262 bar_sz &= ~(bar_sz - 1);
264 *base = (*base + bar_sz - 1) & ~(bar_sz - 1);
265 bar_data |= *base;
266 *base += bar_sz;
268 pci_writel(devfn, bar_reg, bar_data);
269 printf("pci dev %02x:%x bar %02x size %08x: %08x\n",
270 devfn>>3, devfn&7, bar_reg, bar_sz, bar_data);
272 /* Now enable the memory or I/O mapping. */
273 cmd = pci_readw(devfn, PCI_COMMAND);
274 if ( (bar_reg == PCI_ROM_ADDRESS) ||
275 ((bar_data & PCI_BASE_ADDRESS_SPACE) ==
276 PCI_BASE_ADDRESS_SPACE_MEMORY) )
277 cmd |= PCI_COMMAND_MEMORY;
278 else
279 cmd |= PCI_COMMAND_IO;
280 pci_writew(devfn, PCI_COMMAND, cmd);
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 }
297 }
299 /*
300 * If the network card is in the boot order, load the Etherboot option ROM.
301 * Read the boot order bytes from CMOS and check if any of them are 0x4.
302 */
303 static int must_load_nic(void)
304 {
305 uint8_t boot_order;
307 /* Read CMOS register 0x3d (boot choices 0 and 1). */
308 boot_order = cmos_inb(0x3d);
309 if ( ((boot_order & 0xf) == 0x4) || ((boot_order & 0xf0) == 0x40) )
310 return 1;
312 /* Read CMOS register 0x38 (boot choice 2 and FDD test flag). */
313 boot_order = cmos_inb(0x38);
314 return ((boot_order & 0xf0) == 0x40);
315 }
317 /* Replace possibly erroneous memory-size CMOS fields with correct values. */
318 static void cmos_write_memory_size(void)
319 {
320 struct e820entry *map = E820_MAP;
321 int i, nr = *E820_MAP_NR;
322 uint32_t base_mem = 640, ext_mem = 0, alt_mem = 0;
324 for ( i = 0; i < nr; i++ )
325 if ( (map[i].addr >= 0x100000) && (map[i].type == E820_RAM) )
326 break;
328 if ( i != nr )
329 {
330 alt_mem = ext_mem = map[i].addr + map[i].size;
331 ext_mem = (ext_mem > 0x0100000) ? (ext_mem - 0x0100000) >> 10 : 0;
332 if ( ext_mem > 0xffff )
333 ext_mem = 0xffff;
334 alt_mem = (alt_mem > 0x1000000) ? (alt_mem - 0x1000000) >> 16 : 0;
335 }
337 /* All BIOSes: conventional memory (640kB). */
338 cmos_outb(0x15, (uint8_t)(base_mem >> 0));
339 cmos_outb(0x16, (uint8_t)(base_mem >> 8));
341 /* All BIOSes: extended memory (1kB chunks above 1MB). */
342 cmos_outb(0x17, (uint8_t)( ext_mem >> 0));
343 cmos_outb(0x18, (uint8_t)( ext_mem >> 8));
344 cmos_outb(0x30, (uint8_t)( ext_mem >> 0));
345 cmos_outb(0x31, (uint8_t)( ext_mem >> 8));
347 /* Some BIOSes: alternative extended memory (64kB chunks above 16MB). */
348 cmos_outb(0x34, (uint8_t)( alt_mem >> 0));
349 cmos_outb(0x35, (uint8_t)( alt_mem >> 8));
350 }
352 int main(void)
353 {
354 int acpi_sz;
356 printf("HVM Loader\n");
358 init_hypercalls();
360 printf("Writing SMBIOS tables ...\n");
361 hvm_write_smbios_tables();
363 printf("Loading ROMBIOS ...\n");
364 memcpy((void *)ROMBIOS_PHYSICAL_ADDRESS, rombios, sizeof(rombios));
365 highbios_setup();
367 apic_setup();
368 pci_setup();
370 if ( (get_vcpu_nr() > 1) || get_apic_mode() )
371 create_mp_tables();
373 if ( cirrus_check() )
374 {
375 printf("Loading Cirrus VGABIOS ...\n");
376 memcpy((void *)VGABIOS_PHYSICAL_ADDRESS,
377 vgabios_cirrusvga, sizeof(vgabios_cirrusvga));
378 }
379 else
380 {
381 printf("Loading Standard VGABIOS ...\n");
382 memcpy((void *)VGABIOS_PHYSICAL_ADDRESS,
383 vgabios_stdvga, sizeof(vgabios_stdvga));
384 }
386 if ( must_load_nic() )
387 {
388 printf("Loading ETHERBOOT ...\n");
389 memcpy((void *)ETHERBOOT_PHYSICAL_ADDRESS,
390 etherboot, sizeof(etherboot));
391 }
393 if ( get_acpi_enabled() != 0 )
394 {
395 printf("Loading ACPI ...\n");
396 acpi_sz = acpi_build_tables((uint8_t *)ACPI_PHYSICAL_ADDRESS);
397 ASSERT((ACPI_PHYSICAL_ADDRESS + acpi_sz) <= 0xF0000);
398 }
400 cmos_write_memory_size();
402 if ( !check_amd() )
403 {
404 printf("Loading VMXAssist ...\n");
405 memcpy((void *)VMXASSIST_PHYSICAL_ADDRESS,
406 vmxassist, sizeof(vmxassist));
408 printf("VMX go ...\n");
409 __asm__ __volatile__(
410 "jmp *%%eax"
411 : : "a" (VMXASSIST_PHYSICAL_ADDRESS), "d" (0)
412 );
413 }
415 printf("Invoking ROMBIOS ...\n");
416 return 0;
417 }
419 /*
420 * Local variables:
421 * mode: C
422 * c-set-style: "BSD"
423 * c-basic-offset: 4
424 * tab-width: 4
425 * indent-tabs-mode: nil
426 * End:
427 */