KVM: SEV: Return appropriate error codes if SEV-ES scratch setup fails
Return appropriate error codes if setting up the GHCB scratch area for an
SEV-ES guest fails. In particular, returning -EINVAL instead of -ENOMEM
when allocating the kernel buffer could be confusing as userspace would
likely suspect a guest issue.
Fixes: 8f423a80d2
("KVM: SVM: Support MMIO for an SEV-ES guest")
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Signed-off-by: Sean Christopherson <seanjc@google.com>
Message-Id: <20211109222350.2266045-2-seanjc@google.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
parent
a955cad84c
commit
75236f5f22
@ -2530,7 +2530,7 @@ void pre_sev_run(struct vcpu_svm *svm, int cpu)
|
||||
}
|
||||
|
||||
#define GHCB_SCRATCH_AREA_LIMIT (16ULL * PAGE_SIZE)
|
||||
static bool setup_vmgexit_scratch(struct vcpu_svm *svm, bool sync, u64 len)
|
||||
static int setup_vmgexit_scratch(struct vcpu_svm *svm, bool sync, u64 len)
|
||||
{
|
||||
struct vmcb_control_area *control = &svm->vmcb->control;
|
||||
struct ghcb *ghcb = svm->sev_es.ghcb;
|
||||
@ -2541,14 +2541,14 @@ static bool setup_vmgexit_scratch(struct vcpu_svm *svm, bool sync, u64 len)
|
||||
scratch_gpa_beg = ghcb_get_sw_scratch(ghcb);
|
||||
if (!scratch_gpa_beg) {
|
||||
pr_err("vmgexit: scratch gpa not provided\n");
|
||||
return false;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
scratch_gpa_end = scratch_gpa_beg + len;
|
||||
if (scratch_gpa_end < scratch_gpa_beg) {
|
||||
pr_err("vmgexit: scratch length (%#llx) not valid for scratch address (%#llx)\n",
|
||||
len, scratch_gpa_beg);
|
||||
return false;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if ((scratch_gpa_beg & PAGE_MASK) == control->ghcb_gpa) {
|
||||
@ -2566,7 +2566,7 @@ static bool setup_vmgexit_scratch(struct vcpu_svm *svm, bool sync, u64 len)
|
||||
scratch_gpa_end > ghcb_scratch_end) {
|
||||
pr_err("vmgexit: scratch area is outside of GHCB shared buffer area (%#llx - %#llx)\n",
|
||||
scratch_gpa_beg, scratch_gpa_end);
|
||||
return false;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
scratch_va = (void *)svm->sev_es.ghcb;
|
||||
@ -2579,18 +2579,18 @@ static bool setup_vmgexit_scratch(struct vcpu_svm *svm, bool sync, u64 len)
|
||||
if (len > GHCB_SCRATCH_AREA_LIMIT) {
|
||||
pr_err("vmgexit: scratch area exceeds KVM limits (%#llx requested, %#llx limit)\n",
|
||||
len, GHCB_SCRATCH_AREA_LIMIT);
|
||||
return false;
|
||||
return -EINVAL;
|
||||
}
|
||||
scratch_va = kzalloc(len, GFP_KERNEL_ACCOUNT);
|
||||
if (!scratch_va)
|
||||
return false;
|
||||
return -ENOMEM;
|
||||
|
||||
if (kvm_read_guest(svm->vcpu.kvm, scratch_gpa_beg, scratch_va, len)) {
|
||||
/* Unable to copy scratch area from guest */
|
||||
pr_err("vmgexit: kvm_read_guest for scratch area failed\n");
|
||||
|
||||
kfree(scratch_va);
|
||||
return false;
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -2606,7 +2606,7 @@ static bool setup_vmgexit_scratch(struct vcpu_svm *svm, bool sync, u64 len)
|
||||
svm->sev_es.ghcb_sa = scratch_va;
|
||||
svm->sev_es.ghcb_sa_len = len;
|
||||
|
||||
return true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void set_ghcb_msr_bits(struct vcpu_svm *svm, u64 value, u64 mask,
|
||||
@ -2745,10 +2745,10 @@ int sev_handle_vmgexit(struct kvm_vcpu *vcpu)
|
||||
ghcb_set_sw_exit_info_1(ghcb, 0);
|
||||
ghcb_set_sw_exit_info_2(ghcb, 0);
|
||||
|
||||
ret = -EINVAL;
|
||||
switch (exit_code) {
|
||||
case SVM_VMGEXIT_MMIO_READ:
|
||||
if (!setup_vmgexit_scratch(svm, true, control->exit_info_2))
|
||||
ret = setup_vmgexit_scratch(svm, true, control->exit_info_2);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
ret = kvm_sev_es_mmio_read(vcpu,
|
||||
@ -2757,7 +2757,8 @@ int sev_handle_vmgexit(struct kvm_vcpu *vcpu)
|
||||
svm->sev_es.ghcb_sa);
|
||||
break;
|
||||
case SVM_VMGEXIT_MMIO_WRITE:
|
||||
if (!setup_vmgexit_scratch(svm, false, control->exit_info_2))
|
||||
ret = setup_vmgexit_scratch(svm, false, control->exit_info_2);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
ret = kvm_sev_es_mmio_write(vcpu,
|
||||
@ -2800,6 +2801,7 @@ int sev_handle_vmgexit(struct kvm_vcpu *vcpu)
|
||||
vcpu_unimpl(vcpu,
|
||||
"vmgexit: unsupported event - exit_info_1=%#llx, exit_info_2=%#llx\n",
|
||||
control->exit_info_1, control->exit_info_2);
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
default:
|
||||
ret = svm_invoke_exit_handler(vcpu, exit_code);
|
||||
@ -2812,6 +2814,7 @@ int sev_es_string_io(struct vcpu_svm *svm, int size, unsigned int port, int in)
|
||||
{
|
||||
int count;
|
||||
int bytes;
|
||||
int r;
|
||||
|
||||
if (svm->vmcb->control.exit_info_2 > INT_MAX)
|
||||
return -EINVAL;
|
||||
@ -2820,8 +2823,9 @@ int sev_es_string_io(struct vcpu_svm *svm, int size, unsigned int port, int in)
|
||||
if (unlikely(check_mul_overflow(count, size, &bytes)))
|
||||
return -EINVAL;
|
||||
|
||||
if (!setup_vmgexit_scratch(svm, in, bytes))
|
||||
return -EINVAL;
|
||||
r = setup_vmgexit_scratch(svm, in, bytes);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
return kvm_sev_es_string_io(&svm->vcpu, size, port, svm->sev_es.ghcb_sa,
|
||||
count, in);
|
||||
|
Loading…
Reference in New Issue
Block a user