diff --git a/kvm.c b/kvm.c index 060fa67..b4ce2c0 100644 --- a/kvm.c +++ b/kvm.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -189,20 +190,31 @@ uint32_t kvm__load_kernel(struct kvm *kvm, const char *kernel_filename) return ret; } +static inline uint64_t ip_flat_to_real(struct kvm *self, uint64_t ip) +{ + uint64_t cs = self->sregs.cs.selector; + + 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) { - self->regs = (struct kvm_regs) { - .rip = rip, - .rflags = 0x0000000000000002ULL, - }; - - if (ioctl(self->vcpu_fd, KVM_SET_REGS, &self->regs) < 0) - die_perror("KVM_SET_REGS failed"); + /* + * 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);