KVM: Do not communicate to userspace through cpu registers during PIO
authorAvi Kivity <avi@qumranet.com>
Thu, 22 Feb 2007 17:39:30 +0000 (19:39 +0200)
committerAvi Kivity <avi@qumranet.com>
Thu, 3 May 2007 07:52:23 +0000 (10:52 +0300)
Currently when passing the a PIO emulation request to userspace, we
rely on userspace updating %rax (on 'in' instructions) and %rsi/%rdi/%rcx
(on string instructions).  This (a) requires two extra ioctls for getting
and setting the registers and (b) is unfriendly to non-x86 archs, when
they get kvm ports.

So fix by doing the register fixups in the kernel and passing to userspace
only an abstract description of the PIO to be done.

Signed-off-by: Avi Kivity <avi@qumranet.com>
drivers/kvm/kvm.h
drivers/kvm/kvm_main.c
drivers/kvm/svm.c
drivers/kvm/vmx.c
include/linux/kvm.h

index 901b8d917b557728d800fb6b3d96dd7719f18ed0..59cbc5b1d905dfc5b6d1407f0206a4ea380e029f 100644 (file)
@@ -274,6 +274,7 @@ struct kvm_vcpu {
        int mmio_size;
        unsigned char mmio_data[8];
        gpa_t mmio_phys_addr;
+       int pio_pending;
 
        struct {
                int active;
index 42be8a8f299df471ab718c193d8b167e0d407458..ff8bcfee76e50fef7a83fef0501d93d8a91a94c8 100644 (file)
@@ -1504,6 +1504,44 @@ void save_msrs(struct vmx_msr_entry *e, int n)
 }
 EXPORT_SYMBOL_GPL(save_msrs);
 
+static void complete_pio(struct kvm_vcpu *vcpu)
+{
+       struct kvm_io *io = &vcpu->run->io;
+       long delta;
+
+       kvm_arch_ops->cache_regs(vcpu);
+
+       if (!io->string) {
+               if (io->direction == KVM_EXIT_IO_IN)
+                       memcpy(&vcpu->regs[VCPU_REGS_RAX], &io->value,
+                              io->size);
+       } else {
+               delta = 1;
+               if (io->rep) {
+                       delta *= io->count;
+                       /*
+                        * The size of the register should really depend on
+                        * current address size.
+                        */
+                       vcpu->regs[VCPU_REGS_RCX] -= delta;
+               }
+               if (io->string_down)
+                       delta = -delta;
+               delta *= io->size;
+               if (io->direction == KVM_EXIT_IO_IN)
+                       vcpu->regs[VCPU_REGS_RDI] += delta;
+               else
+                       vcpu->regs[VCPU_REGS_RSI] += delta;
+       }
+
+       vcpu->pio_pending = 0;
+       vcpu->run->io_completed = 0;
+
+       kvm_arch_ops->decache_regs(vcpu);
+
+       kvm_arch_ops->skip_emulated_instruction(vcpu);
+}
+
 static int kvm_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
 {
        int r;
@@ -1518,9 +1556,13 @@ static int kvm_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
                kvm_run->emulated = 0;
        }
 
-       if (kvm_run->mmio_completed) {
-               memcpy(vcpu->mmio_data, kvm_run->mmio.data, 8);
-               vcpu->mmio_read_completed = 1;
+       if (kvm_run->io_completed) {
+               if (vcpu->pio_pending)
+                       complete_pio(vcpu);
+               else {
+                       memcpy(vcpu->mmio_data, kvm_run->mmio.data, 8);
+                       vcpu->mmio_read_completed = 1;
+               }
        }
 
        vcpu->mmio_needed = 0;
index 6787f11738cfa8d376b909f96cefa007f3db8d2b..c35b8c83bf3fc5a2bc82371db6726a5842cde5e8 100644 (file)
@@ -1037,6 +1037,7 @@ static int io_interception(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
        kvm_run->io.size = ((io_info & SVM_IOIO_SIZE_MASK) >> SVM_IOIO_SIZE_SHIFT);
        kvm_run->io.string = (io_info & SVM_IOIO_STR_MASK) != 0;
        kvm_run->io.rep = (io_info & SVM_IOIO_REP_MASK) != 0;
+       kvm_run->io.count = 1;
 
        if (kvm_run->io.string) {
                unsigned addr_mask;
@@ -1056,6 +1057,7 @@ static int io_interception(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
                }
        } else
                kvm_run->io.value = vcpu->svm->vmcb->save.rax;
+       vcpu->pio_pending = 1;
        return 0;
 }
 
index a721b60f7385b87d16fb1de729c8954a03f0824b..4d5f40fcb6511d9747bfefcb1adc9e858111b481 100644 (file)
@@ -1459,12 +1459,14 @@ static int handle_io(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
                = (vmcs_readl(GUEST_RFLAGS) & X86_EFLAGS_DF) != 0;
        kvm_run->io.rep = (exit_qualification & 32) != 0;
        kvm_run->io.port = exit_qualification >> 16;
+       kvm_run->io.count = 1;
        if (kvm_run->io.string) {
                if (!get_io_count(vcpu, &kvm_run->io.count))
                        return 1;
                kvm_run->io.address = vmcs_readl(GUEST_LINEAR_ADDRESS);
        } else
                kvm_run->io.value = vcpu->regs[VCPU_REGS_RAX]; /* rax */
+       vcpu->pio_pending = 1;
        return 0;
 }
 
index d88e7508ee0a6dc43224926ffd6348e86143fe1b..19aeb338518814bb9409c4a439a49bdbfdd4858e 100644 (file)
@@ -11,7 +11,7 @@
 #include <asm/types.h>
 #include <linux/ioctl.h>
 
-#define KVM_API_VERSION 5
+#define KVM_API_VERSION 6
 
 /*
  * Architectural interrupt line count, and the size of the bitmap needed
@@ -53,7 +53,7 @@ enum kvm_exit_reason {
 struct kvm_run {
        /* in */
        __u32 emulated;  /* skip current instruction */
-       __u32 mmio_completed; /* mmio request completed */
+       __u32 io_completed; /* mmio/pio request completed */
        __u8 request_interrupt_window;
        __u8 padding1[7];
 
@@ -80,7 +80,7 @@ struct kvm_run {
                        __u32 error_code;
                } ex;
                /* KVM_EXIT_IO */
-               struct {
+               struct kvm_io {
 #define KVM_EXIT_IO_IN  0
 #define KVM_EXIT_IO_OUT 1
                        __u8 direction;