From: Pekka Enberg Date: Sat, 27 Mar 2010 07:34:00 +0000 (+0200) Subject: kvm: Load bzImage real-mode boot sector and setup code X-Git-Tag: next-20110824~3^2~528^2~236 X-Git-Url: https://git.karo-electronics.de/?a=commitdiff_plain;h=b866d32b4e82cb5bb50abf13476faf129583488c;p=karo-tx-linux.git kvm: Load bzImage real-mode boot sector and setup code 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 --- diff --git a/tools/kvm/include/kvm/kvm.h b/tools/kvm/include/kvm/kvm.h index 2f9ac2b23675..a51b38de2df9 100644 --- a/tools/kvm/include/kvm/kvm.h +++ b/tools/kvm/include/kvm/kvm.h @@ -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); diff --git a/tools/kvm/kvm.c b/tools/kvm/kvm.c index f47a1f802b1c..a0031c0d97bd 100644 --- a/tools/kvm/kvm.c +++ b/tools/kvm/kvm.c @@ -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, }; diff --git a/tools/kvm/main.c b/tools/kvm/main.c index ba0aefab07a9..329925b61cd1 100644 --- a/tools/kvm/main.c +++ b/tools/kvm/main.c @@ -1,5 +1,7 @@ #include "kvm/kvm.h" +#include "util.h" + #include #include #include @@ -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);