]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
kvm: Load bzImage real-mode boot sector and setup code
authorPekka Enberg <penberg@cs.helsinki.fi>
Sat, 27 Mar 2010 07:34:00 +0000 (09:34 +0200)
committerPekka Enberg <penberg@cs.helsinki.fi>
Sat, 27 Mar 2010 07:34:00 +0000 (09:34 +0200)
We start KVM in 16-bit mode so we should set cs:ip to point to the real-mode
boot sector and setup code, not the protected mode kernel that starts at 1M.

Signed-off-by: Pekka Enberg <penberg@cs.helsinki.fi>
tools/kvm/include/kvm/kvm.h
tools/kvm/kvm.c
tools/kvm/main.c

index 2f9ac2b23675a751c79460580c4d2db3e22782b2..a51b38de2df95a65b82adb969fc45c14d69c6a1c 100644 (file)
@@ -15,14 +15,17 @@ struct kvm {
        uint64_t                ram_size;
        void                    *ram_start;
 
+       uint16_t                boot_cs;        /* 16-bit code segment selector */
+       uint16_t                boot_ip;        /* 16-bit code offset */
+
        struct kvm_regs         regs;
        struct kvm_sregs        sregs;
 };
 
 struct kvm *kvm__init(void);
 void kvm__enable_singlestep(struct kvm *self);
-uint32_t kvm__load_kernel(struct kvm *kvm, const char *kernel_filename);
-void kvm__reset_vcpu(struct kvm *self, uint64_t rip);
+bool kvm__load_kernel(struct kvm *kvm, const char *kernel_filename);
+void kvm__reset_vcpu(struct kvm *self);
 void kvm__run(struct kvm *self);
 void kvm__emulate_io(struct kvm *self, uint16_t port, void *data, int direction, int size, uint32_t count);
 
index f47a1f802b1c90217a97e6c8d2538d49e9a4b484..a0031c0d97bd45678669175c64d6fa6441a4e75d 100644 (file)
@@ -153,9 +153,10 @@ static inline uint32_t segment_to_flat(uint16_t selector, uint16_t offset)
        return ((uint32_t)selector << 4) + (uint32_t) offset;
 }
 
-#define KERNEL_BIN_START_ADDR  segment_to_flat(0xf000, 0xfff0)
+#define BIN_BOOT_LOADER_CS     0xf000
+#define BIN_BOOT_LOADER_IP     0xfff0
 
-static uint32_t load_flat_binary(struct kvm *kvm, int fd)
+static int load_flat_binary(struct kvm *kvm, int fd)
 {
        void *p;
        int nr;
@@ -163,22 +164,40 @@ static uint32_t load_flat_binary(struct kvm *kvm, int fd)
        if (lseek(fd, 0, SEEK_SET) < 0)
                die_perror("lseek");
 
-       p = guest_addr_to_host(kvm, KERNEL_BIN_START_ADDR);
+       p = guest_addr_to_host(kvm, segment_to_flat(BIN_BOOT_LOADER_CS, BIN_BOOT_LOADER_IP));
 
        while ((nr = read(fd, p, 65536)) > 0)
                p += nr;
 
-       return KERNEL_BIN_START_ADDR;
+       kvm->boot_cs    = BIN_BOOT_LOADER_CS;
+       kvm->boot_ip    = BIN_BOOT_LOADER_IP;
+
+       return true;
 }
 
-/* bzImages are loaded at 1 MB by default.  */
-#define KERNEL_BZ_START_ADDR   (1024ULL * 1024ULL)
+/*
+ * See Documentation/x86/boot.txt for details no how a bzImage is laid out in
+ * memory.
+ */
+
+/*
+ * As we emulate the boot loader, we can load kernel boot sector and setup code
+ * at the address which usually has the boot loader.
+ */
+#define BZ_BOOT_LOADER_START           0x007c00UL
+
+/*
+ * The protected mode kernel part of a modern bzImage is loaded at 1 MB by
+ * default.
+ */
+#define BZ_KERNEL_START                        0x100000UL
 
 static const char *BZIMAGE_MAGIC       = "HdrS";
 
-static uint32_t load_bzimage(struct kvm *kvm, int fd)
+static bool load_bzimage(struct kvm *kvm, int fd)
 {
        struct boot_params boot;
+       ssize_t setup_size;
        void *p;
        int nr;
 
@@ -188,21 +207,28 @@ static uint32_t load_bzimage(struct kvm *kvm, int fd)
        read(fd, &boot, sizeof(boot));
 
         if (memcmp(&boot.hdr.header, BZIMAGE_MAGIC, strlen(BZIMAGE_MAGIC)) != 0)
-               return 0;
+               return false;
 
-       lseek(fd, (boot.hdr.setup_sects+1) * 512, SEEK_SET);
+       setup_size = (boot.hdr.setup_sects + 1) * 512;
+       p = guest_addr_to_host(kvm, BZ_BOOT_LOADER_START);
 
-       p = guest_addr_to_host(kvm, KERNEL_BZ_START_ADDR);
+       if (read(fd, p, setup_size) != setup_size)
+               die_perror("read");
+
+       p = guest_addr_to_host(kvm, BZ_KERNEL_START);
 
        while ((nr = read(fd, p, 65536)) > 0)
                p += nr;
 
-       return boot.hdr.code32_start;
+       kvm->boot_cs    = 0;
+       kvm->boot_ip    = BZ_BOOT_LOADER_START;
+
+       return true;
 }
 
-uint32_t kvm__load_kernel(struct kvm *kvm, const char *kernel_filename)
+bool kvm__load_kernel(struct kvm *kvm, const char *kernel_filename)
 {
-       uint32_t ret;
+       bool ret;
        int fd;
 
        fd = open(kernel_filename, O_RDONLY);
@@ -237,17 +263,12 @@ static inline uint64_t ip_real_to_flat(struct kvm *self, uint64_t ip)
        return ip + (cs << 4);
 }
 
-void kvm__reset_vcpu(struct kvm *self, uint64_t rip)
+void kvm__reset_vcpu(struct kvm *self)
 {
-       /*
-        * First set up segmentation because we need to translate the flat
-        * 'rip' to segmented 16-bit cs:ip address.
-        */
-
        self->sregs = (struct kvm_sregs) {
                .cr0            = 0x60000010ULL,
                .cs             = (struct kvm_segment) {
-                       .selector       = 0xf000UL,
+                       .selector       = self->boot_cs,
                        .base           = 0xffff0000UL,
                        .limit          = 0xffffU,
                        .type           = 0x0bU,
@@ -312,8 +333,8 @@ void kvm__reset_vcpu(struct kvm *self, uint64_t rip)
                die_perror("KVM_SET_SREGS failed");
 
        self->regs = (struct kvm_regs) {
+               .rip            = self->boot_ip,
                /* We start the guest in 16-bit real mode  */
-               .rip            = ip_flat_to_real(self, rip),
                .rflags         = 0x0000000000000002ULL,
        };
 
index ba0aefab07a9fb71983870fa8a2c5c6a07fe0bd3..329925b61cd13f16f6840c4a6fddc441d54da733 100644 (file)
@@ -1,5 +1,7 @@
 #include "kvm/kvm.h"
 
+#include "util.h"
+
 #include <inttypes.h>
 #include <stdint.h>
 #include <stdlib.h>
@@ -14,7 +16,6 @@ static void usage(char *argv[])
 int main(int argc, char *argv[])
 {
        const char *kernel_filename;
-       uint64_t kernel_start;
        struct kvm *kvm;
 
        if (argc < 2)
@@ -24,9 +25,10 @@ int main(int argc, char *argv[])
 
        kvm = kvm__init();
 
-       kernel_start = kvm__load_kernel(kvm, kernel_filename);
+       if (!kvm__load_kernel(kvm, kernel_filename))
+               die("unable to load kernel %s", kernel_filename);
 
-       kvm__reset_vcpu(kvm, kernel_start);
+       kvm__reset_vcpu(kvm);
 
        kvm__enable_singlestep(kvm);