]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
Fix up 'ip' in kvm__reset_vcpu() to take real mode into account
authorPekka Enberg <penberg@cs.helsinki.fi>
Fri, 26 Mar 2010 15:24:39 +0000 (17:24 +0200)
committerPekka Enberg <penberg@cs.helsinki.fi>
Fri, 26 Mar 2010 15:24:39 +0000 (17:24 +0200)
We can't just set 'rip' to 1 MB mark because in real mode, 'ip' is just 16 bits
wide.

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

index 060fa6743bda00fc9ecd4cf0c770dcf574bd7d93..b4ce2c0f492198f9dd8292cf9d9055c07e71c1df 100644 (file)
@@ -8,6 +8,7 @@
 #include <inttypes.h>
 #include <sys/mman.h>
 #include <stdbool.h>
+#include <limits.h>
 #include <stdarg.h>
 #include <stdlib.h>
 #include <string.h>
@@ -189,20 +190,31 @@ uint32_t kvm__load_kernel(struct kvm *kvm, const char *kernel_filename)
        return ret;
 }
 
-void kvm__reset_vcpu(struct kvm *self, uint64_t rip)
+static inline uint64_t ip_flat_to_real(struct kvm *self, uint64_t ip)
 {
-       self->regs = (struct kvm_regs) {
-               .rip            = rip,
-               .rflags         = 0x0000000000000002ULL,
-       };
+       uint64_t cs = self->sregs.cs.selector;
 
-       if (ioctl(self->vcpu_fd, KVM_SET_REGS, &self->regs) < 0)
-               die_perror("KVM_SET_REGS failed");
+       return ip - (cs << 4);
+}
+
+static inline uint64_t ip_real_to_flat(struct kvm *self, uint64_t ip)
+{
+       uint64_t cs = self->sregs.cs.selector;
+
+       return ip + (cs << 4);
+}
+
+void kvm__reset_vcpu(struct kvm *self, uint64_t rip)
+{
+       /*
+        * 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       = 0xfff0UL,
                        .base           = 0xffff0000UL,
                        .limit          = 0xffffU,
                        .type           = 0x0bU,
@@ -265,6 +277,19 @@ void kvm__reset_vcpu(struct kvm *self, uint64_t rip)
 
        if (ioctl(self->vcpu_fd, KVM_SET_SREGS, &self->sregs) < 0)
                die_perror("KVM_SET_SREGS failed");
+
+       self->regs = (struct kvm_regs) {
+               /* We start the guest in 16-bit real mode  */
+               .rip            = ip_flat_to_real(self, rip),
+               .rflags         = 0x0000000000000002ULL,
+       };
+
+       if (self->regs.rip > USHRT_MAX)
+               die("ip 0x%" PRIx64 " is too high for real mode", (uint64_t) self->regs.rip);
+
+       if (ioctl(self->vcpu_fd, KVM_SET_REGS, &self->regs) < 0)
+               die_perror("KVM_SET_REGS failed");
+
 }
 
 void kvm__run(struct kvm *self)
@@ -368,14 +393,14 @@ void kvm__show_code(struct kvm *self)
        unsigned int i;
        uint8_t *ip;
 
-       ip = guest_addr_to_host(self, self->regs.rip - code_prologue);
+       ip = guest_addr_to_host(self, ip_real_to_flat(self, self->regs.rip) - code_prologue);
 
        printf("Code: ");
 
        for (i = 0; i < code_len; i++, ip++) {
                c = *ip;
 
-               if (ip == guest_addr_to_host(self, self->regs.rip))
+               if (ip == guest_addr_to_host(self, ip_real_to_flat(self, self->regs.rip)))
                        printf("<%02x> ", c);
                else
                        printf("%02x ", c);