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>
89292f776SCyrill Gorcunov
928ce0d66SCyrill Gorcunov #include "bios/bios-rom.h"
1028ce0d66SCyrill Gorcunov
11ca82361eSPekka Enberg struct irq_handler {
12ca82361eSPekka Enberg unsigned long address;
13ca82361eSPekka Enberg unsigned int irq;
14ca82361eSPekka Enberg void *handler;
15ca82361eSPekka Enberg size_t size;
169292f776SCyrill Gorcunov };
179292f776SCyrill Gorcunov
1806e80d53SCyrill Gorcunov #define BIOS_IRQ_PA_ADDR(name) (MB_BIOS_BEGIN + BIOS_OFFSET__##name)
1928ce0d66SCyrill Gorcunov #define BIOS_IRQ_FUNC(name) ((char *)&bios_rom[BIOS_OFFSET__##name])
2028ce0d66SCyrill Gorcunov #define BIOS_IRQ_SIZE(name) (BIOS_ENTRY_SIZE(BIOS_OFFSET__##name))
2128ce0d66SCyrill Gorcunov
22ca82361eSPekka Enberg #define DEFINE_BIOS_IRQ_HANDLER(_irq, _handler) \
23ca82361eSPekka Enberg { \
24ca82361eSPekka Enberg .irq = _irq, \
2506e80d53SCyrill Gorcunov .address = BIOS_IRQ_PA_ADDR(_handler), \
26ca82361eSPekka Enberg .handler = BIOS_IRQ_FUNC(_handler), \
27ca82361eSPekka Enberg .size = BIOS_IRQ_SIZE(_handler), \
28ca82361eSPekka Enberg }
29ca82361eSPekka Enberg
30ca82361eSPekka Enberg static struct irq_handler bios_irq_handlers[] = {
31ca82361eSPekka Enberg DEFINE_BIOS_IRQ_HANDLER(0x10, bios_int10),
32ca82361eSPekka Enberg DEFINE_BIOS_IRQ_HANDLER(0x15, bios_int15),
33ca82361eSPekka Enberg };
34ca82361eSPekka Enberg
setup_irq_handler(struct kvm * kvm,struct irq_handler * handler)35ca82361eSPekka Enberg static void setup_irq_handler(struct kvm *kvm, struct irq_handler *handler)
36ca82361eSPekka Enberg {
37ca82361eSPekka Enberg struct real_intr_desc intr_desc;
38ca82361eSPekka Enberg void *p;
39ca82361eSPekka Enberg
40ca82361eSPekka Enberg p = guest_flat_to_host(kvm, handler->address);
41ca82361eSPekka Enberg memcpy(p, handler->handler, handler->size);
42ca82361eSPekka Enberg
43ca82361eSPekka Enberg intr_desc = (struct real_intr_desc) {
4406e80d53SCyrill Gorcunov .segment = REAL_SEGMENT(MB_BIOS_BEGIN),
4506e80d53SCyrill Gorcunov .offset = handler->address - MB_BIOS_BEGIN,
46ca82361eSPekka Enberg };
47ca82361eSPekka Enberg
48*da4cfc3eSSicheng Liu DIE_IF((handler->address - MB_BIOS_BEGIN + 1) > MB_BIOS_SIZE);
4906e80d53SCyrill Gorcunov
5042ac24f9SSasha Levin interrupt_table__set(&kvm->arch.interrupt_table, &intr_desc, handler->irq);
51ca82361eSPekka Enberg }
52ca82361eSPekka Enberg
53b3594ec7SCyrill Gorcunov /**
54b3594ec7SCyrill Gorcunov * e820_setup - setup some simple E820 memory map
55b3594ec7SCyrill Gorcunov * @kvm - guest system descriptor
56b3594ec7SCyrill Gorcunov */
e820_setup(struct kvm * kvm)57b3594ec7SCyrill Gorcunov static void e820_setup(struct kvm *kvm)
58b3594ec7SCyrill Gorcunov {
598cb0027cSSasha Levin struct e820map *e820;
608cb0027cSSasha Levin struct e820entry *mem_map;
61b3594ec7SCyrill Gorcunov unsigned int i = 0;
62b3594ec7SCyrill Gorcunov
638cb0027cSSasha Levin e820 = guest_flat_to_host(kvm, E820_MAP_START);
648cb0027cSSasha Levin mem_map = e820->map;
65b3594ec7SCyrill Gorcunov
668cb0027cSSasha Levin mem_map[i++] = (struct e820entry) {
67b3594ec7SCyrill Gorcunov .addr = REAL_MODE_IVT_BEGIN,
68b3594ec7SCyrill Gorcunov .size = EBDA_START - REAL_MODE_IVT_BEGIN,
698cb0027cSSasha Levin .type = E820_RAM,
70b3594ec7SCyrill Gorcunov };
718cb0027cSSasha Levin mem_map[i++] = (struct e820entry) {
72b3594ec7SCyrill Gorcunov .addr = EBDA_START,
73b3594ec7SCyrill Gorcunov .size = VGA_RAM_BEGIN - EBDA_START,
748cb0027cSSasha Levin .type = E820_RESERVED,
75b3594ec7SCyrill Gorcunov };
768cb0027cSSasha Levin mem_map[i++] = (struct e820entry) {
77b3594ec7SCyrill Gorcunov .addr = MB_BIOS_BEGIN,
78*da4cfc3eSSicheng Liu .size = MB_BIOS_SIZE,
798cb0027cSSasha Levin .type = E820_RESERVED,
80b3594ec7SCyrill Gorcunov };
81874467f8SSasha Levin if (kvm->ram_size < KVM_32BIT_GAP_START) {
828cb0027cSSasha Levin mem_map[i++] = (struct e820entry) {
83b3594ec7SCyrill Gorcunov .addr = BZ_KERNEL_START,
84b3594ec7SCyrill Gorcunov .size = kvm->ram_size - BZ_KERNEL_START,
858cb0027cSSasha Levin .type = E820_RAM,
86b3594ec7SCyrill Gorcunov };
87874467f8SSasha Levin } else {
888cb0027cSSasha Levin mem_map[i++] = (struct e820entry) {
89874467f8SSasha Levin .addr = BZ_KERNEL_START,
90874467f8SSasha Levin .size = KVM_32BIT_GAP_START - BZ_KERNEL_START,
918cb0027cSSasha Levin .type = E820_RAM,
92874467f8SSasha Levin };
938cb0027cSSasha Levin mem_map[i++] = (struct e820entry) {
94f7abc4cdSHongyong Zang .addr = KVM_32BIT_MAX_MEM_SIZE,
95f7abc4cdSHongyong Zang .size = kvm->ram_size - KVM_32BIT_MAX_MEM_SIZE,
968cb0027cSSasha Levin .type = E820_RAM,
97874467f8SSasha Levin };
98874467f8SSasha Levin }
99b3594ec7SCyrill Gorcunov
100a2857479SCyrill Gorcunov BUG_ON(i > E820_X_MAX);
101874467f8SSasha Levin
1028cb0027cSSasha Levin e820->nr_map = i;
103b3594ec7SCyrill Gorcunov }
104b3594ec7SCyrill Gorcunov
setup_vga_rom(struct kvm * kvm)10555290abcSCyrill Gorcunov static void setup_vga_rom(struct kvm *kvm)
10655290abcSCyrill Gorcunov {
10755290abcSCyrill Gorcunov u16 *mode;
10855290abcSCyrill Gorcunov void *p;
10955290abcSCyrill Gorcunov
11055290abcSCyrill Gorcunov p = guest_flat_to_host(kvm, VGA_ROM_OEM_STRING);
11155290abcSCyrill Gorcunov memset(p, 0, VGA_ROM_OEM_STRING_SIZE);
11255290abcSCyrill Gorcunov strncpy(p, "KVM VESA", VGA_ROM_OEM_STRING_SIZE);
11355290abcSCyrill Gorcunov
11455290abcSCyrill Gorcunov mode = guest_flat_to_host(kvm, VGA_ROM_MODES);
11555290abcSCyrill Gorcunov mode[0] = 0x0112;
11655290abcSCyrill Gorcunov mode[1] = 0xffff;
11755290abcSCyrill Gorcunov }
11855290abcSCyrill Gorcunov
119b3594ec7SCyrill Gorcunov /**
120b3594ec7SCyrill Gorcunov * setup_bios - inject BIOS into guest memory
121b3594ec7SCyrill Gorcunov * @kvm - guest system descriptor
122b3594ec7SCyrill Gorcunov */
setup_bios(struct kvm * kvm)1239292f776SCyrill Gorcunov void setup_bios(struct kvm *kvm)
1249292f776SCyrill Gorcunov {
1259292f776SCyrill Gorcunov unsigned long address = MB_BIOS_BEGIN;
1269292f776SCyrill Gorcunov struct real_intr_desc intr_desc;
127ca82361eSPekka Enberg unsigned int i;
1289292f776SCyrill Gorcunov void *p;
1299292f776SCyrill Gorcunov
13015c2b0a0SCyrill Gorcunov /*
13115c2b0a0SCyrill Gorcunov * before anything else -- clean some known areas
13215c2b0a0SCyrill Gorcunov * we definitely don't want any trash here
13315c2b0a0SCyrill Gorcunov */
13415c2b0a0SCyrill Gorcunov p = guest_flat_to_host(kvm, BDA_START);
135*da4cfc3eSSicheng Liu memset(p, 0, BDA_SIZE);
13615c2b0a0SCyrill Gorcunov
13715c2b0a0SCyrill Gorcunov p = guest_flat_to_host(kvm, EBDA_START);
138*da4cfc3eSSicheng Liu memset(p, 0, EBDA_SIZE);
13915c2b0a0SCyrill Gorcunov
14015c2b0a0SCyrill Gorcunov p = guest_flat_to_host(kvm, MB_BIOS_BEGIN);
141*da4cfc3eSSicheng Liu memset(p, 0, MB_BIOS_SIZE);
14215c2b0a0SCyrill Gorcunov
14315c2b0a0SCyrill Gorcunov p = guest_flat_to_host(kvm, VGA_ROM_BEGIN);
144*da4cfc3eSSicheng Liu memset(p, 0, VGA_ROM_SIZE);
14515c2b0a0SCyrill Gorcunov
14628ce0d66SCyrill Gorcunov /* just copy the bios rom into the place */
14728ce0d66SCyrill Gorcunov p = guest_flat_to_host(kvm, MB_BIOS_BEGIN);
14828ce0d66SCyrill Gorcunov memcpy(p, bios_rom, bios_rom_size);
14928ce0d66SCyrill Gorcunov
150b3594ec7SCyrill Gorcunov /* E820 memory map must be present */
151b3594ec7SCyrill Gorcunov e820_setup(kvm);
152b3594ec7SCyrill Gorcunov
15355290abcSCyrill Gorcunov /* VESA needs own tricks */
15455290abcSCyrill Gorcunov setup_vga_rom(kvm);
15555290abcSCyrill Gorcunov
1569292f776SCyrill Gorcunov /*
1579292f776SCyrill Gorcunov * Setup a *fake* real mode vector table, it has only
158e85eb87eSXiaochen Wang * one real handler which does just iret
1599292f776SCyrill Gorcunov */
16006e80d53SCyrill Gorcunov address = BIOS_IRQ_PA_ADDR(bios_intfake);
1619292f776SCyrill Gorcunov intr_desc = (struct real_intr_desc) {
16206e80d53SCyrill Gorcunov .segment = REAL_SEGMENT(MB_BIOS_BEGIN),
16306e80d53SCyrill Gorcunov .offset = address - MB_BIOS_BEGIN,
1649292f776SCyrill Gorcunov };
16542ac24f9SSasha Levin interrupt_table__setup(&kvm->arch.interrupt_table, &intr_desc);
1669292f776SCyrill Gorcunov
167ca82361eSPekka Enberg for (i = 0; i < ARRAY_SIZE(bios_irq_handlers); i++)
168ca82361eSPekka Enberg setup_irq_handler(kvm, &bios_irq_handlers[i]);
1699292f776SCyrill Gorcunov
17028ce0d66SCyrill Gorcunov /* we almost done */
1719292f776SCyrill Gorcunov p = guest_flat_to_host(kvm, 0);
17242ac24f9SSasha Levin interrupt_table__copy(&kvm->arch.interrupt_table, p, REAL_INTR_SIZE);
1739292f776SCyrill Gorcunov }
174