From 82234dd28a74d8c4dd17a92034d1dcbba5cd5bf9 Mon Sep 17 00:00:00 2001 From: Avi Kivity Date: Sun, 6 May 2007 13:28:58 +0300 Subject: kvm: libkvm: add backward compatibility support for kvm abi 10 --- kvm/user/kvm-abi-10.h | 59 ++++++++++++++++ kvm/user/kvmctl.c | 189 +++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 247 insertions(+), 1 deletion(-) create mode 100644 kvm/user/kvm-abi-10.h diff --git a/kvm/user/kvm-abi-10.h b/kvm/user/kvm-abi-10.h new file mode 100644 index 000000000..c6edcd05a --- /dev/null +++ b/kvm/user/kvm-abi-10.h @@ -0,0 +1,59 @@ + +struct kvm_run_abi10 { + /* in */ + __u32 io_completed; /* mmio/pio request completed */ + __u8 request_interrupt_window; + __u8 padding1[3]; + + /* out */ + __u32 exit_reason; + __u32 instruction_length; + __u8 ready_for_interrupt_injection; + __u8 if_flag; + __u8 padding2[6]; + + /* in (pre_kvm_run), out (post_kvm_run) */ + __u64 cr8; + __u64 apic_base; + + union { + /* KVM_EXIT_UNKNOWN */ + struct { + __u64 hardware_exit_reason; + } hw; + /* KVM_EXIT_FAIL_ENTRY */ + struct { + __u64 hardware_entry_failure_reason; + } fail_entry; + /* KVM_EXIT_EXCEPTION */ + struct { + __u32 exception; + __u32 error_code; + } ex; + /* KVM_EXIT_IO */ + struct kvm_io_abi10 { + __u8 direction; + __u8 size; /* bytes */ + __u16 port; + __u32 count; + __u64 data_offset; /* relative to kvm_run start */ + } io; + struct { + } debug; + /* KVM_EXIT_MMIO */ + struct { + __u64 phys_addr; + __u8 data[8]; + __u32 len; + __u8 is_write; + } mmio; + /* KVM_EXIT_HYPERCALL */ + struct { + __u64 args[6]; + __u64 ret; + __u32 longmode; + __u32 pad; + } hypercall; + }; +}; + diff --git a/kvm/user/kvmctl.c b/kvm/user/kvmctl.c index 07b01fcdd..d4f0eb3ae 100644 --- a/kvm/user/kvmctl.c +++ b/kvm/user/kvmctl.c @@ -23,6 +23,7 @@ #include #include #include "kvmctl.h" +#include "kvm-abi-10.h" #define EXPECTED_KVM_API_VERSION 12 @@ -30,6 +31,8 @@ #error libkvm: userspace and kernel version mismatch #endif +static int kvm_abi = EXPECTED_KVM_API_VERSION; + #define PAGE_SIZE 4096ul /* FIXME: share this number with kvm */ @@ -193,7 +196,7 @@ kvm_context_t kvm_init(struct kvm_callbacks *callbacks, fprintf(stderr, "kvm kernel version too old\n"); goto out_close; } - if (r < EXPECTED_KVM_API_VERSION) { + if (r < EXPECTED_KVM_API_VERSION && r != 10) { fprintf(stderr, "kvm kernel version too old\n"); goto out_close; } @@ -201,6 +204,7 @@ kvm_context_t kvm_init(struct kvm_callbacks *callbacks, fprintf(stderr, "kvm userspace version too old\n"); goto out_close; } + kvm_abi = r; kvm = malloc(sizeof(*kvm)); kvm->fd = fd; kvm->vm_fd = -1; @@ -414,6 +418,63 @@ int kvm_get_mem_map(kvm_context_t kvm, int slot, void *buf) #endif /* KVM_GET_MEM_MAP */ } +static int handle_io_abi10(kvm_context_t kvm, struct kvm_run_abi10 *run, + int vcpu) +{ + uint16_t addr = run->io.port; + int r; + int i; + void *p = (void *)run + run->io.data_offset; + + for (i = 0; i < run->io.count; ++i) { + switch (run->io.direction) { + case KVM_EXIT_IO_IN: + switch (run->io.size) { + case 1: + r = kvm->callbacks->inb(kvm->opaque, addr, p); + break; + case 2: + r = kvm->callbacks->inw(kvm->opaque, addr, p); + break; + case 4: + r = kvm->callbacks->inl(kvm->opaque, addr, p); + break; + default: + fprintf(stderr, "bad I/O size %d\n", run->io.size); + return -EMSGSIZE; + } + break; + case KVM_EXIT_IO_OUT: + switch (run->io.size) { + case 1: + r = kvm->callbacks->outb(kvm->opaque, addr, + *(uint8_t *)p); + break; + case 2: + r = kvm->callbacks->outw(kvm->opaque, addr, + *(uint16_t *)p); + break; + case 4: + r = kvm->callbacks->outl(kvm->opaque, addr, + *(uint32_t *)p); + break; + default: + fprintf(stderr, "bad I/O size %d\n", run->io.size); + return -EMSGSIZE; + } + break; + default: + fprintf(stderr, "bad I/O direction %d\n", run->io.direction); + return -EPROTO; + } + + p += run->io.size; + } + run->io_completed = 1; + + return 0; +} + static int handle_io(kvm_context_t kvm, struct kvm_run *run, int vcpu) { uint16_t addr = run->io.port; @@ -630,6 +691,47 @@ void kvm_show_regs(kvm_context_t kvm, int vcpu) sregs.efer); } +static int handle_mmio_abi10(kvm_context_t kvm, struct kvm_run_abi10 *kvm_run) +{ + unsigned long addr = kvm_run->mmio.phys_addr; + void *data = kvm_run->mmio.data; + int r = -1; + + if (kvm_run->mmio.is_write) { + switch (kvm_run->mmio.len) { + case 1: + r = kvm->callbacks->writeb(kvm->opaque, addr, *(uint8_t *)data); + break; + case 2: + r = kvm->callbacks->writew(kvm->opaque, addr, *(uint16_t *)data); + break; + case 4: + r = kvm->callbacks->writel(kvm->opaque, addr, *(uint32_t *)data); + break; + case 8: + r = kvm->callbacks->writeq(kvm->opaque, addr, *(uint64_t *)data); + break; + } + } else { + switch (kvm_run->mmio.len) { + case 1: + r = kvm->callbacks->readb(kvm->opaque, addr, (uint8_t *)data); + break; + case 2: + r = kvm->callbacks->readw(kvm->opaque, addr, (uint16_t *)data); + break; + case 4: + r = kvm->callbacks->readl(kvm->opaque, addr, (uint32_t *)data); + break; + case 8: + r = kvm->callbacks->readq(kvm->opaque, addr, (uint64_t *)data); + break; + } + kvm_run->io_completed = 1; + } + return r; +} + static int handle_mmio(kvm_context_t kvm, struct kvm_run *kvm_run) { unsigned long addr = kvm_run->mmio.phys_addr; @@ -704,6 +806,8 @@ int kvm_get_interrupt_flag(kvm_context_t kvm, int vcpu) { struct kvm_run *run = kvm->run[vcpu]; + if (kvm_abi == 10) + return ((struct kvm_run_abi10 *)run)->if_flag; return run->if_flag; } @@ -711,6 +815,8 @@ uint64_t kvm_get_apic_base(kvm_context_t kvm, int vcpu) { struct kvm_run *run = kvm->run[vcpu]; + if (kvm_abi == 10) + return ((struct kvm_run_abi10 *)run)->apic_base; return run->apic_base; } @@ -718,6 +824,8 @@ int kvm_is_ready_for_interrupt_injection(kvm_context_t kvm, int vcpu) { struct kvm_run *run = kvm->run[vcpu]; + if (kvm_abi == 10) + return ((struct kvm_run_abi10 *)run)->ready_for_interrupt_injection; return run->ready_for_interrupt_injection; } @@ -725,15 +833,94 @@ void kvm_set_cr8(kvm_context_t kvm, int vcpu, uint64_t cr8) { struct kvm_run *run = kvm->run[vcpu]; + if (kvm_abi == 10) { + ((struct kvm_run_abi10 *)run)->cr8 = cr8; + return; + } run->cr8 = cr8; } +static int kvm_run_abi10(kvm_context_t kvm, int vcpu) +{ + int r; + int fd = kvm->vcpu_fd[vcpu]; + struct kvm_run_abi10 *run = (struct kvm_run_abi10 *)kvm->run[vcpu]; + +again: + run->request_interrupt_window = try_push_interrupts(kvm); + pre_kvm_run(kvm, vcpu); + r = ioctl(fd, KVM_RUN, 0); + post_kvm_run(kvm, vcpu); + + run->io_completed = 0; + if (r == -1 && errno != EINTR) { + r = -errno; + printf("kvm_run: %m\n"); + return r; + } + if (r == -1) { + r = handle_io_window(kvm); + goto more; + } + if (1) { + switch (run->exit_reason) { + case KVM_EXIT_UNKNOWN: + fprintf(stderr, "unhandled vm exit: 0x%x\n", + (unsigned)run->hw.hardware_exit_reason); + kvm_show_regs(kvm, vcpu); + abort(); + break; + case KVM_EXIT_FAIL_ENTRY: + fprintf(stderr, "kvm_run: failed entry, reason %u\n", + (unsigned)run->fail_entry.hardware_entry_failure_reason & 0xffff); + return -ENOEXEC; + break; + case KVM_EXIT_EXCEPTION: + fprintf(stderr, "exception %d (%x)\n", + run->ex.exception, + run->ex.error_code); + kvm_show_regs(kvm, vcpu); + abort(); + break; + case KVM_EXIT_IO: + r = handle_io_abi10(kvm, run, vcpu); + break; + case KVM_EXIT_DEBUG: + r = handle_debug(kvm, vcpu); + break; + case KVM_EXIT_MMIO: + r = handle_mmio_abi10(kvm, run); + break; + case KVM_EXIT_HLT: + r = handle_halt(kvm, vcpu); + break; + case KVM_EXIT_IRQ_WINDOW_OPEN: + break; + case KVM_EXIT_SHUTDOWN: + r = handle_shutdown(kvm, vcpu); + break; + default: + fprintf(stderr, "unhandled vm exit: 0x%x\n", run->exit_reason); + kvm_show_regs(kvm, vcpu); + abort(); + break; + } + } +more: + if (!r) + goto again; + return r; +} + int kvm_run(kvm_context_t kvm, int vcpu) { int r; int fd = kvm->vcpu_fd[vcpu]; struct kvm_run *run = kvm->run[vcpu]; + if (kvm_abi == 10) + return kvm_run_abi10(kvm, vcpu); + again: run->request_interrupt_window = try_push_interrupts(kvm); pre_kvm_run(kvm, vcpu); -- cgit v1.2.3