This patch adds BIOS E820 memory map emulation to KVM.
Signed-off-by: Pekka Enberg <penberg@cs.helsinki.fi>
$(Q) $(CC) $(CFLAGS) -c bios/int10.S -o bios/int10.o
bios/int15.o: bios/int10.S bios/int15-real.S
+ $(E) " CC " $@
+ $(Q) $(CC) -include code16gcc.h $(CFLAGS) $(BIOS_CFLAGS) -c -s bios/e820.c -o bios/e820.o
$(E) " CC " $@
$(Q) $(CC) $(CFLAGS) $(BIOS_CFLAGS) -c -s bios/int15-real.S -o bios/int15-real.o
$(E) " LD " $@
- $(Q) ld -T bios/bios-strip.ld.S -o bios/int15-real.bin.elf bios/int15-real.o
+ $(Q) ld -T bios/bios-strip.ld.S -o bios/int15-real.bin.elf bios/int15-real.o bios/e820.o
$(E) " OBJCOPY " $@
$(Q) objcopy -O binary -j .text bios/int15-real.bin.elf bios/int15-real.bin
$(Q) $(CC) $(CFLAGS) -c bios/int15.S -o bios/int15.o
--- /dev/null
+#include "kvm/e820.h"
+
+#include "kvm/bios.h"
+#include "kvm/util.h"
+
+static inline void outb(uint8_t v, uint16_t port)
+{
+ asm volatile("outb %0,%1" : : "a" (v), "dN" (port));
+}
+
+static inline uint8_t rdfs8(unsigned long addr)
+{
+ uint8_t v;
+ asm volatile("movb %%fs:%1,%0" : "=q" (v) : "m" (*(uint8_t *)addr));
+ return v;
+}
+
+void e820_query_map(struct e820_query *query)
+{
+ uint8_t map_size;
+ uint32_t ndx;
+
+ ndx = query->ebx;
+
+ map_size = rdfs8(E820_MAP_SIZE);
+
+ if (ndx < map_size) {
+ unsigned long start;
+ unsigned int i;
+ uint8_t *p;
+
+ start = E820_MAP_START + sizeof(struct e820_entry) * ndx;
+
+ p = (void *) query->edi;
+
+ for (i = 0; i < sizeof(struct e820_entry); i++)
+ *p++ = rdfs8(start + i);
+ }
+
+ query->eax = SMAP;
+ query->ecx = 20;
+ query->ebx = ++ndx;
+
+ if (ndx >= map_size)
+ query->ebx = 0; /* end of map */
+}
#include "macro.S"
+#define EFLAGS_CF (1 << 0)
+
ENTRY(___int15)
- jmp out
+ cmp $0xE820, %eax
+ jne out
+
+ pushw %fs
+
+ pushl %edx
+ pushl %ecx
+ pushl %edi
+ pushl %ebx
+ pushl %eax
+
+ xor %ax, %ax
+ mov %ax, %fs
-# movl %cs, %eax
-# movl %eax, %es
-# movl $0x534D4150, %eax # 'SMAP'
-# movl $0, %ebx # end of map
-# movl $20, %ecx
-# mov e820entry_start, %di
-# mov $(e820entry_end - e820entry_start), %ecx
+ movl %esp, %eax
+ call e820_query_map
+ popl %eax
+ popl %ebx
+ popl %edi
+ popl %ecx
+ popl %edx
+
+ popw %fs
+
+ /* Clear CF */
+ andl $~EFLAGS_CF, 0x4(%esp)
out:
sti
IRET
-/*
- * private IRQ data
- */
-GLOBAL(e820entry_start)
- e820entry_addr: .quad 0
- e820entry_size: .quad 0
- e820entry_type: .long 0
-GLOBAL(e820entry_end)
/*
* must be last in this file
--- /dev/null
+/*
+ * code16gcc.h
+ *
+ * This file is -include'd when compiling 16-bit C code.
+ * Note: this asm() needs to be emitted before gcc emits any code.
+ * Depending on gcc version, this requires -fno-unit-at-a-time or
+ * -fno-toplevel-reorder.
+ *
+ * Hopefully gcc will eventually have a real -m16 option so we can
+ * drop this hack long term.
+ */
+
+#ifndef __ASSEMBLY__
+asm(".code16gcc");
+#endif
#define REAL_MODE_IVT_BEGIN 0x00000000
#define REAL_MODE_IVT_END 0x000003ff
+#define BDA_START 0x00000400UL
+#define BDA_END 0x000004FFUL
+
+#define EBDA_START 0x0009FC00UL
+#define EBDA_END 0x0009FFFFUL
+
+#define E820_MAP_SIZE EBDA_START
+#define E820_MAP_START (EBDA_START + 0x01)
+
#define MB_BIOS_BEGIN 0x000f0000
#define MB_BIOS_END 0x000fffff
#define MB_BIOS_SS 0xFFF7
#define MB_BIOS_SP 0x40
-#define E820_RAM 1
-#define E820_RESERVED 2
-#define E820_ACPI 3
-#define E820_NVS 4
-#define E820_UNUSABLE 5
-
-#define E820_MAP_OFFSET 64
-
#define ALIGN(x, a) \
(((x) + ((a) - 1)) & ~((a) - 1))
--- /dev/null
+#ifndef KVM_E820_H
+#define KVM_E820_H
+
+#include <stdint.h>
+
+#define SMAP 0x534d4150 /* ASCII "SMAP" */
+
+#define E820_MEM_USABLE 1
+#define E820_MEM_RESERVED 2
+
+struct e820_entry {
+ uint64_t addr; /* start of memory segment */
+ uint64_t size; /* size of memory segment */
+ uint32_t type; /* type of memory segment */
+} __attribute__((packed));
+
+struct e820_query {
+ uint32_t eax;
+ uint32_t ebx;
+ uint32_t edi;
+ uint32_t ecx;
+ uint32_t edx;
+};
+
+void e820_query_map(struct e820_query *query);
+
+#endif /* KVM_E820_H */
void kvm__enable_singlestep(struct kvm *self);
bool kvm__load_kernel(struct kvm *kvm, const char *kernel_filename, const char *kernel_cmdline);
void kvm__reset_vcpu(struct kvm *self);
+void kvm__setup_mem(struct kvm *self);
void kvm__run(struct kvm *self);
bool kvm__emulate_io(struct kvm *self, uint16_t port, void *data, int direction, int size, uint32_t count);
bool kvm__emulate_mmio(struct kvm *self, uint64_t phys_addr, uint8_t *data, uint32_t len, uint8_t is_write);
#include "kvm/interrupt.h"
#include "kvm/cpufeature.h"
+#include "kvm/e820.h"
#include "kvm/util.h"
#include <linux/kvm.h>
kvm__setup_msrs(self);
}
+void kvm__setup_mem(struct kvm *self)
+{
+ struct e820_entry *mem_map;
+ unsigned char *size;
+
+ size = guest_flat_to_host(self, E820_MAP_SIZE);
+ mem_map = guest_flat_to_host(self, E820_MAP_START);
+
+ *size = 4;
+
+ mem_map[0] = (struct e820_entry) {
+ .addr = REAL_MODE_IVT_BEGIN,
+ .size = BDA_END - REAL_MODE_IVT_BEGIN,
+ .type = E820_MEM_RESERVED,
+ };
+ mem_map[1] = (struct e820_entry) {
+ .addr = BDA_END,
+ .size = EBDA_END - BDA_END,
+ .type = E820_MEM_USABLE,
+ };
+ mem_map[2] = (struct e820_entry) {
+ .addr = EBDA_END,
+ .size = BZ_KERNEL_START - EBDA_END,
+ .type = E820_MEM_RESERVED,
+ };
+ mem_map[3] = (struct e820_entry) {
+ .addr = BZ_KERNEL_START,
+ .size = self->ram_size - BZ_KERNEL_START,
+ .type = E820_MEM_USABLE,
+ };
+}
+
void kvm__run(struct kvm *self)
{
if (ioctl(self->vcpu_fd, KVM_RUN, 0) < 0)
kvm__reset_vcpu(kvm);
+ kvm__setup_mem(kvm);
+
if (single_step)
kvm__enable_singlestep(kvm);