KVM: SEV-ES: go over the sev_pio_data buffer in multiple passes if needed
The PIO scratch buffer is larger than a single page, and therefore
it is not possible to copy it in a single step to vcpu->arch/pio_data.
Bound each call to emulator_pio_in/out to a single page; keep
track of how many I/O operations are left in vcpu->arch.sev_pio_count,
so that the operation can be restarted in the complete_userspace_io
callback.
For OUT, this means that the previous kvm_sev_es_outs implementation
becomes an iterator of the loop, and we can consume the sev_pio_data
buffer before leaving to userspace.
For IN, instead, consuming the buffer and decreasing sev_pio_count
is always done in the complete_userspace_io callback, because that
is when the memcpy is done into sev_pio_data.
Cc: stable@vger.kernel.org
Fixes: 7ed9abfe8e
("KVM: SVM: Support string IO operations for an SEV-ES guest")
Reported-by: Felix Wilhelm <fwilhelm@google.com>
Reviewed-by: Maxim Levitsky <mlevitsk@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
parent
4fa4b38dae
commit
95e16b4792
@ -703,6 +703,7 @@ struct kvm_vcpu_arch {
|
|||||||
struct kvm_pio_request pio;
|
struct kvm_pio_request pio;
|
||||||
void *pio_data;
|
void *pio_data;
|
||||||
void *sev_pio_data;
|
void *sev_pio_data;
|
||||||
|
unsigned sev_pio_count;
|
||||||
|
|
||||||
u8 event_exit_inst_len;
|
u8 event_exit_inst_len;
|
||||||
|
|
||||||
|
@ -12386,38 +12386,77 @@ int kvm_sev_es_mmio_read(struct kvm_vcpu *vcpu, gpa_t gpa, unsigned int bytes,
|
|||||||
EXPORT_SYMBOL_GPL(kvm_sev_es_mmio_read);
|
EXPORT_SYMBOL_GPL(kvm_sev_es_mmio_read);
|
||||||
|
|
||||||
static int kvm_sev_es_outs(struct kvm_vcpu *vcpu, unsigned int size,
|
static int kvm_sev_es_outs(struct kvm_vcpu *vcpu, unsigned int size,
|
||||||
unsigned int port, unsigned int count)
|
unsigned int port);
|
||||||
{
|
|
||||||
int ret = emulator_pio_out(vcpu, size, port,
|
|
||||||
vcpu->arch.sev_pio_data, count);
|
|
||||||
|
|
||||||
if (ret) {
|
static int complete_sev_es_emulated_outs(struct kvm_vcpu *vcpu)
|
||||||
/* Emulation done by the kernel. */
|
{
|
||||||
return ret;
|
int size = vcpu->arch.pio.size;
|
||||||
}
|
int port = vcpu->arch.pio.port;
|
||||||
|
|
||||||
vcpu->arch.pio.count = 0;
|
vcpu->arch.pio.count = 0;
|
||||||
|
if (vcpu->arch.sev_pio_count)
|
||||||
|
return kvm_sev_es_outs(vcpu, size, port);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int kvm_sev_es_outs(struct kvm_vcpu *vcpu, unsigned int size,
|
||||||
|
unsigned int port)
|
||||||
|
{
|
||||||
|
for (;;) {
|
||||||
|
unsigned int count =
|
||||||
|
min_t(unsigned int, PAGE_SIZE / size, vcpu->arch.sev_pio_count);
|
||||||
|
int ret = emulator_pio_out(vcpu, size, port, vcpu->arch.sev_pio_data, count);
|
||||||
|
|
||||||
|
/* memcpy done already by emulator_pio_out. */
|
||||||
|
vcpu->arch.sev_pio_count -= count;
|
||||||
|
vcpu->arch.sev_pio_data += count * vcpu->arch.pio.size;
|
||||||
|
if (!ret)
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* Emulation done by the kernel. */
|
||||||
|
if (!vcpu->arch.sev_pio_count)
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
vcpu->arch.complete_userspace_io = complete_sev_es_emulated_outs;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int kvm_sev_es_ins(struct kvm_vcpu *vcpu, unsigned int size,
|
||||||
|
unsigned int port);
|
||||||
|
|
||||||
|
static void advance_sev_es_emulated_ins(struct kvm_vcpu *vcpu)
|
||||||
|
{
|
||||||
|
unsigned count = vcpu->arch.pio.count;
|
||||||
|
complete_emulator_pio_in(vcpu, vcpu->arch.sev_pio_data);
|
||||||
|
vcpu->arch.sev_pio_count -= count;
|
||||||
|
vcpu->arch.sev_pio_data += count * vcpu->arch.pio.size;
|
||||||
|
}
|
||||||
|
|
||||||
static int complete_sev_es_emulated_ins(struct kvm_vcpu *vcpu)
|
static int complete_sev_es_emulated_ins(struct kvm_vcpu *vcpu)
|
||||||
{
|
{
|
||||||
memcpy(vcpu->arch.sev_pio_data, vcpu->arch.pio_data,
|
int size = vcpu->arch.pio.size;
|
||||||
vcpu->arch.pio.count * vcpu->arch.pio.size);
|
int port = vcpu->arch.pio.port;
|
||||||
vcpu->arch.pio.count = 0;
|
|
||||||
|
|
||||||
|
advance_sev_es_emulated_ins(vcpu);
|
||||||
|
if (vcpu->arch.sev_pio_count)
|
||||||
|
return kvm_sev_es_ins(vcpu, size, port);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int kvm_sev_es_ins(struct kvm_vcpu *vcpu, unsigned int size,
|
static int kvm_sev_es_ins(struct kvm_vcpu *vcpu, unsigned int size,
|
||||||
unsigned int port, unsigned int count)
|
unsigned int port)
|
||||||
{
|
{
|
||||||
int ret = emulator_pio_in(vcpu, size, port,
|
for (;;) {
|
||||||
vcpu->arch.sev_pio_data, count);
|
unsigned int count =
|
||||||
|
min_t(unsigned int, PAGE_SIZE / size, vcpu->arch.sev_pio_count);
|
||||||
|
if (!__emulator_pio_in(vcpu, size, port, count))
|
||||||
|
break;
|
||||||
|
|
||||||
if (ret) {
|
|
||||||
/* Emulation done by the kernel. */
|
/* Emulation done by the kernel. */
|
||||||
return ret;
|
advance_sev_es_emulated_ins(vcpu);
|
||||||
|
if (!vcpu->arch.sev_pio_count)
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
vcpu->arch.complete_userspace_io = complete_sev_es_emulated_ins;
|
vcpu->arch.complete_userspace_io = complete_sev_es_emulated_ins;
|
||||||
@ -12429,8 +12468,9 @@ int kvm_sev_es_string_io(struct kvm_vcpu *vcpu, unsigned int size,
|
|||||||
int in)
|
int in)
|
||||||
{
|
{
|
||||||
vcpu->arch.sev_pio_data = data;
|
vcpu->arch.sev_pio_data = data;
|
||||||
return in ? kvm_sev_es_ins(vcpu, size, port, count)
|
vcpu->arch.sev_pio_count = count;
|
||||||
: kvm_sev_es_outs(vcpu, size, port, count);
|
return in ? kvm_sev_es_ins(vcpu, size, port)
|
||||||
|
: kvm_sev_es_outs(vcpu, size, port);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(kvm_sev_es_string_io);
|
EXPORT_SYMBOL_GPL(kvm_sev_es_string_io);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user