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