aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAvi Kivity <avi@qumranet.com>2007-05-06 13:28:58 +0300
committerAvi Kivity <avi@qumranet.com>2007-05-06 13:28:58 +0300
commit82234dd28a74d8c4dd17a92034d1dcbba5cd5bf9 (patch)
tree7e529b5d7b6257eff57f9eac00cf7fe3b11cde43
parent3a175b54b13933d7f6b1aa4b83f6e69ee55aa74e (diff)
kvm: libkvm: add backward compatibility support for kvm abi 10kvm-22
-rw-r--r--kvm/user/kvm-abi-10.h59
-rw-r--r--kvm/user/kvmctl.c189
2 files changed, 247 insertions, 1 deletions
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 <errno.h>
#include <sys/ioctl.h>
#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);