xref: /kvmtool/x86/bios.c (revision f7abc4cd29272144a44d77bf66b85c08b88c3575)
19292f776SCyrill Gorcunov #include "kvm/kvm.h"
2b3594ec7SCyrill Gorcunov #include "kvm/boot-protocol.h"
3b3594ec7SCyrill Gorcunov #include "kvm/e820.h"
49292f776SCyrill Gorcunov #include "kvm/interrupt.h"
59292f776SCyrill Gorcunov #include "kvm/util.h"
69292f776SCyrill Gorcunov 
79292f776SCyrill Gorcunov #include <string.h>
88cb0027cSSasha Levin #include <asm/e820.h>
99292f776SCyrill Gorcunov 
1028ce0d66SCyrill Gorcunov #include "bios/bios-rom.h"
1128ce0d66SCyrill Gorcunov 
12ca82361eSPekka Enberg struct irq_handler {
13ca82361eSPekka Enberg 	unsigned long		address;
14ca82361eSPekka Enberg 	unsigned int		irq;
15ca82361eSPekka Enberg 	void			*handler;
16ca82361eSPekka Enberg 	size_t			size;
179292f776SCyrill Gorcunov };
189292f776SCyrill Gorcunov 
1906e80d53SCyrill Gorcunov #define BIOS_IRQ_PA_ADDR(name)	(MB_BIOS_BEGIN + BIOS_OFFSET__##name)
2028ce0d66SCyrill Gorcunov #define BIOS_IRQ_FUNC(name)	((char *)&bios_rom[BIOS_OFFSET__##name])
2128ce0d66SCyrill Gorcunov #define BIOS_IRQ_SIZE(name)	(BIOS_ENTRY_SIZE(BIOS_OFFSET__##name))
2228ce0d66SCyrill Gorcunov 
23ca82361eSPekka Enberg #define DEFINE_BIOS_IRQ_HANDLER(_irq, _handler)			\
24ca82361eSPekka Enberg 	{							\
25ca82361eSPekka Enberg 		.irq		= _irq,				\
2606e80d53SCyrill Gorcunov 		.address	= BIOS_IRQ_PA_ADDR(_handler),	\
27ca82361eSPekka Enberg 		.handler	= BIOS_IRQ_FUNC(_handler),	\
28ca82361eSPekka Enberg 		.size		= BIOS_IRQ_SIZE(_handler),	\
29ca82361eSPekka Enberg 	}
30ca82361eSPekka Enberg 
31ca82361eSPekka Enberg static struct irq_handler bios_irq_handlers[] = {
32ca82361eSPekka Enberg 	DEFINE_BIOS_IRQ_HANDLER(0x10, bios_int10),
33ca82361eSPekka Enberg 	DEFINE_BIOS_IRQ_HANDLER(0x15, bios_int15),
34ca82361eSPekka Enberg };
35ca82361eSPekka Enberg 
36ca82361eSPekka Enberg static void setup_irq_handler(struct kvm *kvm, struct irq_handler *handler)
37ca82361eSPekka Enberg {
38ca82361eSPekka Enberg 	struct real_intr_desc intr_desc;
39ca82361eSPekka Enberg 	void *p;
40ca82361eSPekka Enberg 
41ca82361eSPekka Enberg 	p = guest_flat_to_host(kvm, handler->address);
42ca82361eSPekka Enberg 	memcpy(p, handler->handler, handler->size);
43ca82361eSPekka Enberg 
44ca82361eSPekka Enberg 	intr_desc = (struct real_intr_desc) {
4506e80d53SCyrill Gorcunov 		.segment	= REAL_SEGMENT(MB_BIOS_BEGIN),
4606e80d53SCyrill Gorcunov 		.offset		= handler->address - MB_BIOS_BEGIN,
47ca82361eSPekka Enberg 	};
48ca82361eSPekka Enberg 
493a60be06SSasha Levin 	DIE_IF((handler->address - MB_BIOS_BEGIN) > 0xffffUL);
5006e80d53SCyrill Gorcunov 
51ca82361eSPekka Enberg 	interrupt_table__set(&kvm->interrupt_table, &intr_desc, handler->irq);
52ca82361eSPekka Enberg }
53ca82361eSPekka Enberg 
54b3594ec7SCyrill Gorcunov /**
55b3594ec7SCyrill Gorcunov  * e820_setup - setup some simple E820 memory map
56b3594ec7SCyrill Gorcunov  * @kvm - guest system descriptor
57b3594ec7SCyrill Gorcunov  */
58b3594ec7SCyrill Gorcunov static void e820_setup(struct kvm *kvm)
59b3594ec7SCyrill Gorcunov {
608cb0027cSSasha Levin 	struct e820map *e820;
618cb0027cSSasha Levin 	struct e820entry *mem_map;
62b3594ec7SCyrill Gorcunov 	unsigned int i = 0;
63b3594ec7SCyrill Gorcunov 
648cb0027cSSasha Levin 	e820		= guest_flat_to_host(kvm, E820_MAP_START);
658cb0027cSSasha Levin 	mem_map		= e820->map;
66b3594ec7SCyrill Gorcunov 
678cb0027cSSasha Levin 	mem_map[i++]	= (struct e820entry) {
68b3594ec7SCyrill Gorcunov 		.addr		= REAL_MODE_IVT_BEGIN,
69b3594ec7SCyrill Gorcunov 		.size		= EBDA_START - REAL_MODE_IVT_BEGIN,
708cb0027cSSasha Levin 		.type		= E820_RAM,
71b3594ec7SCyrill Gorcunov 	};
728cb0027cSSasha Levin 	mem_map[i++]	= (struct e820entry) {
73b3594ec7SCyrill Gorcunov 		.addr		= EBDA_START,
74b3594ec7SCyrill Gorcunov 		.size		= VGA_RAM_BEGIN - EBDA_START,
758cb0027cSSasha Levin 		.type		= E820_RESERVED,
76b3594ec7SCyrill Gorcunov 	};
778cb0027cSSasha Levin 	mem_map[i++]	= (struct e820entry) {
78b3594ec7SCyrill Gorcunov 		.addr		= MB_BIOS_BEGIN,
79b3594ec7SCyrill Gorcunov 		.size		= MB_BIOS_END - MB_BIOS_BEGIN,
808cb0027cSSasha Levin 		.type		= E820_RESERVED,
81b3594ec7SCyrill Gorcunov 	};
82874467f8SSasha Levin 	if (kvm->ram_size < KVM_32BIT_GAP_START) {
838cb0027cSSasha Levin 		mem_map[i++]	= (struct e820entry) {
84b3594ec7SCyrill Gorcunov 			.addr		= BZ_KERNEL_START,
85b3594ec7SCyrill Gorcunov 			.size		= kvm->ram_size - BZ_KERNEL_START,
868cb0027cSSasha Levin 			.type		= E820_RAM,
87b3594ec7SCyrill Gorcunov 		};
88874467f8SSasha Levin 	} else {
898cb0027cSSasha Levin 		mem_map[i++]	= (struct e820entry) {
90874467f8SSasha Levin 			.addr		= BZ_KERNEL_START,
91874467f8SSasha Levin 			.size		= KVM_32BIT_GAP_START - BZ_KERNEL_START,
928cb0027cSSasha Levin 			.type		= E820_RAM,
93874467f8SSasha Levin 		};
948cb0027cSSasha Levin 		mem_map[i++]	= (struct e820entry) {
95*f7abc4cdSHongyong Zang 			.addr		= KVM_32BIT_MAX_MEM_SIZE,
96*f7abc4cdSHongyong Zang 			.size		= kvm->ram_size - KVM_32BIT_MAX_MEM_SIZE,
978cb0027cSSasha Levin 			.type		= E820_RAM,
98874467f8SSasha Levin 		};
99874467f8SSasha Levin 	}
100b3594ec7SCyrill Gorcunov 
101a2857479SCyrill Gorcunov 	BUG_ON(i > E820_X_MAX);
102874467f8SSasha Levin 
1038cb0027cSSasha Levin 	e820->nr_map = i;
104b3594ec7SCyrill Gorcunov }
105b3594ec7SCyrill Gorcunov 
10655290abcSCyrill Gorcunov static void setup_vga_rom(struct kvm *kvm)
10755290abcSCyrill Gorcunov {
10855290abcSCyrill Gorcunov 	u16 *mode;
10955290abcSCyrill Gorcunov 	void *p;
11055290abcSCyrill Gorcunov 
11155290abcSCyrill Gorcunov 	p = guest_flat_to_host(kvm, VGA_ROM_OEM_STRING);
11255290abcSCyrill Gorcunov 	memset(p, 0, VGA_ROM_OEM_STRING_SIZE);
11355290abcSCyrill Gorcunov 	strncpy(p, "KVM VESA", VGA_ROM_OEM_STRING_SIZE);
11455290abcSCyrill Gorcunov 
11555290abcSCyrill Gorcunov 	mode = guest_flat_to_host(kvm, VGA_ROM_MODES);
11655290abcSCyrill Gorcunov 	mode[0]	= 0x0112;
11755290abcSCyrill Gorcunov 	mode[1] = 0xffff;
11855290abcSCyrill Gorcunov }
11955290abcSCyrill Gorcunov 
120b3594ec7SCyrill Gorcunov /**
121b3594ec7SCyrill Gorcunov  * setup_bios - inject BIOS into guest memory
122b3594ec7SCyrill Gorcunov  * @kvm - guest system descriptor
123b3594ec7SCyrill Gorcunov  */
1249292f776SCyrill Gorcunov void setup_bios(struct kvm *kvm)
1259292f776SCyrill Gorcunov {
1269292f776SCyrill Gorcunov 	unsigned long address = MB_BIOS_BEGIN;
1279292f776SCyrill Gorcunov 	struct real_intr_desc intr_desc;
128ca82361eSPekka Enberg 	unsigned int i;
1299292f776SCyrill Gorcunov 	void *p;
1309292f776SCyrill Gorcunov 
13115c2b0a0SCyrill Gorcunov 	/*
13215c2b0a0SCyrill Gorcunov 	 * before anything else -- clean some known areas
13315c2b0a0SCyrill Gorcunov 	 * we definitely don't want any trash here
13415c2b0a0SCyrill Gorcunov 	 */
13515c2b0a0SCyrill Gorcunov 	p = guest_flat_to_host(kvm, BDA_START);
13615c2b0a0SCyrill Gorcunov 	memset(p, 0, BDA_END - BDA_START);
13715c2b0a0SCyrill Gorcunov 
13815c2b0a0SCyrill Gorcunov 	p = guest_flat_to_host(kvm, EBDA_START);
13915c2b0a0SCyrill Gorcunov 	memset(p, 0, EBDA_END - EBDA_START);
14015c2b0a0SCyrill Gorcunov 
14115c2b0a0SCyrill Gorcunov 	p = guest_flat_to_host(kvm, MB_BIOS_BEGIN);
14215c2b0a0SCyrill Gorcunov 	memset(p, 0, MB_BIOS_END - MB_BIOS_BEGIN);
14315c2b0a0SCyrill Gorcunov 
14415c2b0a0SCyrill Gorcunov 	p = guest_flat_to_host(kvm, VGA_ROM_BEGIN);
14515c2b0a0SCyrill Gorcunov 	memset(p, 0, VGA_ROM_END - VGA_ROM_BEGIN);
14615c2b0a0SCyrill Gorcunov 
14728ce0d66SCyrill Gorcunov 	/* just copy the bios rom into the place */
14828ce0d66SCyrill Gorcunov 	p = guest_flat_to_host(kvm, MB_BIOS_BEGIN);
14928ce0d66SCyrill Gorcunov 	memcpy(p, bios_rom, bios_rom_size);
15028ce0d66SCyrill Gorcunov 
151b3594ec7SCyrill Gorcunov 	/* E820 memory map must be present */
152b3594ec7SCyrill Gorcunov 	e820_setup(kvm);
153b3594ec7SCyrill Gorcunov 
15455290abcSCyrill Gorcunov 	/* VESA needs own tricks */
15555290abcSCyrill Gorcunov 	setup_vga_rom(kvm);
15655290abcSCyrill Gorcunov 
1579292f776SCyrill Gorcunov 	/*
1589292f776SCyrill Gorcunov 	 * Setup a *fake* real mode vector table, it has only
159e85eb87eSXiaochen Wang 	 * one real handler which does just iret
1609292f776SCyrill Gorcunov 	 */
16106e80d53SCyrill Gorcunov 	address = BIOS_IRQ_PA_ADDR(bios_intfake);
1629292f776SCyrill Gorcunov 	intr_desc = (struct real_intr_desc) {
16306e80d53SCyrill Gorcunov 		.segment	= REAL_SEGMENT(MB_BIOS_BEGIN),
16406e80d53SCyrill Gorcunov 		.offset		= address - MB_BIOS_BEGIN,
1659292f776SCyrill Gorcunov 	};
1669292f776SCyrill Gorcunov 	interrupt_table__setup(&kvm->interrupt_table, &intr_desc);
1679292f776SCyrill Gorcunov 
168ca82361eSPekka Enberg 	for (i = 0; i < ARRAY_SIZE(bios_irq_handlers); i++)
169ca82361eSPekka Enberg 		setup_irq_handler(kvm, &bios_irq_handlers[i]);
1709292f776SCyrill Gorcunov 
17128ce0d66SCyrill Gorcunov 	/* we almost done */
1729292f776SCyrill Gorcunov 	p = guest_flat_to_host(kvm, 0);
1739292f776SCyrill Gorcunov 	interrupt_table__copy(&kvm->interrupt_table, p, REAL_INTR_SIZE);
1749292f776SCyrill Gorcunov }
175