KVM: SVM: Support string IO operations for an SEV-ES guest

For an SEV-ES guest, string-based port IO is performed to a shared
(un-encrypted) page so that both the hypervisor and guest can read or
write to it and each see the contents.

For string-based port IO operations, invoke SEV-ES specific routines that
can complete the operation using common KVM port IO support.

Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
Message-Id: <9d61daf0ffda496703717218f415cdc8fd487100.1607620209.git.thomas.lendacky@amd.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
Tom Lendacky 2020-12-10 11:09:54 -06:00 committed by Paolo Bonzini
parent 8f423a80d2
commit 7ed9abfe8e
6 changed files with 83 additions and 5 deletions

View File

@ -614,6 +614,7 @@ struct kvm_vcpu_arch {
struct kvm_pio_request pio; struct kvm_pio_request pio;
void *pio_data; void *pio_data;
void *guest_ins_data;
u8 event_exit_inst_len; u8 event_exit_inst_len;

View File

@ -1406,9 +1406,14 @@ static int sev_es_validate_vmgexit(struct vcpu_svm *svm)
case SVM_EXIT_INVD: case SVM_EXIT_INVD:
break; break;
case SVM_EXIT_IOIO: case SVM_EXIT_IOIO:
if (ghcb_get_sw_exit_info_1(ghcb) & SVM_IOIO_STR_MASK) {
if (!ghcb_sw_scratch_is_valid(ghcb))
goto vmgexit_err;
} else {
if (!(ghcb_get_sw_exit_info_1(ghcb) & SVM_IOIO_TYPE_MASK)) if (!(ghcb_get_sw_exit_info_1(ghcb) & SVM_IOIO_TYPE_MASK))
if (!ghcb_rax_is_valid(ghcb)) if (!ghcb_rax_is_valid(ghcb))
goto vmgexit_err; goto vmgexit_err;
}
break; break;
case SVM_EXIT_MSR: case SVM_EXIT_MSR:
if (!ghcb_rcx_is_valid(ghcb)) if (!ghcb_rcx_is_valid(ghcb))
@ -1776,3 +1781,12 @@ int sev_handle_vmgexit(struct vcpu_svm *svm)
return ret; return ret;
} }
int sev_es_string_io(struct vcpu_svm *svm, int size, unsigned int port, int in)
{
if (!setup_vmgexit_scratch(svm, in, svm->vmcb->control.exit_info_2))
return -EINVAL;
return kvm_sev_es_string_io(&svm->vcpu, size, port,
svm->ghcb_sa, svm->ghcb_sa_len, in);
}

View File

@ -2038,11 +2038,16 @@ static int io_interception(struct vcpu_svm *svm)
++svm->vcpu.stat.io_exits; ++svm->vcpu.stat.io_exits;
string = (io_info & SVM_IOIO_STR_MASK) != 0; string = (io_info & SVM_IOIO_STR_MASK) != 0;
in = (io_info & SVM_IOIO_TYPE_MASK) != 0; in = (io_info & SVM_IOIO_TYPE_MASK) != 0;
if (string)
return kvm_emulate_instruction(vcpu, 0);
port = io_info >> 16; port = io_info >> 16;
size = (io_info & SVM_IOIO_SIZE_MASK) >> SVM_IOIO_SIZE_SHIFT; size = (io_info & SVM_IOIO_SIZE_MASK) >> SVM_IOIO_SIZE_SHIFT;
if (string) {
if (sev_es_guest(vcpu->kvm))
return sev_es_string_io(svm, size, port, in);
else
return kvm_emulate_instruction(vcpu, 0);
}
svm->next_rip = svm->vmcb->control.exit_info_2; svm->next_rip = svm->vmcb->control.exit_info_2;
return kvm_fast_pio(&svm->vcpu, size, port, in); return kvm_fast_pio(&svm->vcpu, size, port, in);

View File

@ -573,5 +573,6 @@ void __init sev_hardware_setup(void);
void sev_hardware_teardown(void); void sev_hardware_teardown(void);
void sev_free_vcpu(struct kvm_vcpu *vcpu); void sev_free_vcpu(struct kvm_vcpu *vcpu);
int sev_handle_vmgexit(struct vcpu_svm *svm); int sev_handle_vmgexit(struct vcpu_svm *svm);
int sev_es_string_io(struct vcpu_svm *svm, int size, unsigned int port, int in);
#endif #endif

View File

@ -10783,6 +10783,10 @@ int kvm_arch_interrupt_allowed(struct kvm_vcpu *vcpu)
unsigned long kvm_get_linear_rip(struct kvm_vcpu *vcpu) unsigned long kvm_get_linear_rip(struct kvm_vcpu *vcpu)
{ {
/* Can't read the RIP when guest state is protected, just return 0 */
if (vcpu->arch.guest_state_protected)
return 0;
if (is_64_bit_mode(vcpu)) if (is_64_bit_mode(vcpu))
return kvm_rip_read(vcpu); return kvm_rip_read(vcpu);
return (u32)(get_segment_base(vcpu, VCPU_SREG_CS) + return (u32)(get_segment_base(vcpu, VCPU_SREG_CS) +
@ -11415,6 +11419,56 @@ 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 complete_sev_es_emulated_ins(struct kvm_vcpu *vcpu)
{
memcpy(vcpu->arch.guest_ins_data, vcpu->arch.pio_data,
vcpu->arch.pio.count * vcpu->arch.pio.size);
vcpu->arch.pio.count = 0;
return 1;
}
static int kvm_sev_es_outs(struct kvm_vcpu *vcpu, unsigned int size,
unsigned int port, void *data, unsigned int count)
{
int ret;
ret = emulator_pio_out_emulated(vcpu->arch.emulate_ctxt, size, port,
data, count);
if (ret)
return ret;
vcpu->arch.pio.count = 0;
return 0;
}
static int kvm_sev_es_ins(struct kvm_vcpu *vcpu, unsigned int size,
unsigned int port, void *data, unsigned int count)
{
int ret;
ret = emulator_pio_in_emulated(vcpu->arch.emulate_ctxt, size, port,
data, count);
if (ret) {
vcpu->arch.pio.count = 0;
} else {
vcpu->arch.guest_ins_data = data;
vcpu->arch.complete_userspace_io = complete_sev_es_emulated_ins;
}
return 0;
}
int kvm_sev_es_string_io(struct kvm_vcpu *vcpu, unsigned int size,
unsigned int port, void *data, unsigned int count,
int in)
{
return in ? kvm_sev_es_ins(vcpu, size, port, data, count)
: kvm_sev_es_outs(vcpu, size, port, data, count);
}
EXPORT_SYMBOL_GPL(kvm_sev_es_string_io);
EXPORT_TRACEPOINT_SYMBOL_GPL(kvm_exit); EXPORT_TRACEPOINT_SYMBOL_GPL(kvm_exit);
EXPORT_TRACEPOINT_SYMBOL_GPL(kvm_fast_mmio); EXPORT_TRACEPOINT_SYMBOL_GPL(kvm_fast_mmio);
EXPORT_TRACEPOINT_SYMBOL_GPL(kvm_inj_virq); EXPORT_TRACEPOINT_SYMBOL_GPL(kvm_inj_virq);

View File

@ -431,5 +431,8 @@ int kvm_sev_es_mmio_write(struct kvm_vcpu *vcpu, gpa_t src, unsigned int bytes,
void *dst); void *dst);
int kvm_sev_es_mmio_read(struct kvm_vcpu *vcpu, gpa_t src, unsigned int bytes, int kvm_sev_es_mmio_read(struct kvm_vcpu *vcpu, gpa_t src, unsigned int bytes,
void *dst); void *dst);
int kvm_sev_es_string_io(struct kvm_vcpu *vcpu, unsigned int size,
unsigned int port, void *data, unsigned int count,
int in);
#endif #endif