Skip to content

Commit

Permalink
KVM: Do not communicate to userspace through cpu registers during PIO
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Avi Kivity committed May 3, 2007
1 parent 9a2bb7f commit 46fc147
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 6 deletions.
1 change: 1 addition & 0 deletions drivers/kvm/kvm.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
48 changes: 45 additions & 3 deletions drivers/kvm/kvm_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down
2 changes: 2 additions & 0 deletions drivers/kvm/svm.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
}

Expand Down
2 changes: 2 additions & 0 deletions drivers/kvm/vmx.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
6 changes: 3 additions & 3 deletions include/linux/kvm.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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];

Expand All @@ -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;
Expand Down

0 comments on commit 46fc147

Please sign in to comment.