KVM: arm64: nv: Handle ERETA[AB] instructions
Now that we have some emulation in place for ERETA[AB], we can plug it into the exception handling machinery. As for a bare ERET, an "easy" ERETAx instruction is processed as a fixup, while something that requires a translation regime transition or an exception delivery is left to the slow path. Reviewed-by: Joey Gouly <joey.gouly@arm.com> Reviewed-by: Oliver Upton <oliver.upton@linux.dev> Link: https://lore.kernel.org/r/20240419102935.1935571-14-maz@kernel.org Signed-off-by: Marc Zyngier <maz@kernel.org>
This commit is contained in:
parent
6ccc971ee2
commit
213b3d1ea1
3 changed files with 33 additions and 5 deletions
|
@ -2172,7 +2172,7 @@ static u64 kvm_check_illegal_exception_return(struct kvm_vcpu *vcpu, u64 spsr)
|
||||||
|
|
||||||
void kvm_emulate_nested_eret(struct kvm_vcpu *vcpu)
|
void kvm_emulate_nested_eret(struct kvm_vcpu *vcpu)
|
||||||
{
|
{
|
||||||
u64 spsr, elr;
|
u64 spsr, elr, esr;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Forward this trap to the virtual EL2 if the virtual
|
* Forward this trap to the virtual EL2 if the virtual
|
||||||
|
@ -2181,11 +2181,29 @@ void kvm_emulate_nested_eret(struct kvm_vcpu *vcpu)
|
||||||
if (forward_traps(vcpu, HCR_NV))
|
if (forward_traps(vcpu, HCR_NV))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
/* Check for an ERETAx */
|
||||||
|
esr = kvm_vcpu_get_esr(vcpu);
|
||||||
|
if (esr_iss_is_eretax(esr) && !kvm_auth_eretax(vcpu, &elr)) {
|
||||||
|
/*
|
||||||
|
* Oh no, ERETAx failed to authenticate. If we have
|
||||||
|
* FPACCOMBINE, deliver an exception right away. If we
|
||||||
|
* don't, then let the mangled ELR value trickle down the
|
||||||
|
* ERET handling, and the guest will have a little surprise.
|
||||||
|
*/
|
||||||
|
if (kvm_has_pauth(vcpu->kvm, FPACCOMBINE)) {
|
||||||
|
esr &= ESR_ELx_ERET_ISS_ERETA;
|
||||||
|
esr |= FIELD_PREP(ESR_ELx_EC_MASK, ESR_ELx_EC_FPAC);
|
||||||
|
kvm_inject_nested_sync(vcpu, esr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
preempt_disable();
|
preempt_disable();
|
||||||
kvm_arch_vcpu_put(vcpu);
|
kvm_arch_vcpu_put(vcpu);
|
||||||
|
|
||||||
spsr = __vcpu_sys_reg(vcpu, SPSR_EL2);
|
spsr = __vcpu_sys_reg(vcpu, SPSR_EL2);
|
||||||
spsr = kvm_check_illegal_exception_return(vcpu, spsr);
|
spsr = kvm_check_illegal_exception_return(vcpu, spsr);
|
||||||
|
if (!esr_iss_is_eretax(esr))
|
||||||
elr = __vcpu_sys_reg(vcpu, ELR_EL2);
|
elr = __vcpu_sys_reg(vcpu, ELR_EL2);
|
||||||
|
|
||||||
trace_kvm_nested_eret(vcpu, elr, spsr);
|
trace_kvm_nested_eret(vcpu, elr, spsr);
|
||||||
|
|
|
@ -248,7 +248,8 @@ static int kvm_handle_ptrauth(struct kvm_vcpu *vcpu)
|
||||||
|
|
||||||
static int kvm_handle_eret(struct kvm_vcpu *vcpu)
|
static int kvm_handle_eret(struct kvm_vcpu *vcpu)
|
||||||
{
|
{
|
||||||
if (esr_iss_is_eretax(kvm_vcpu_get_esr(vcpu)))
|
if (esr_iss_is_eretax(kvm_vcpu_get_esr(vcpu)) &&
|
||||||
|
!vcpu_has_ptrauth(vcpu))
|
||||||
return kvm_handle_ptrauth(vcpu);
|
return kvm_handle_ptrauth(vcpu);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -208,7 +208,8 @@ void kvm_vcpu_put_vhe(struct kvm_vcpu *vcpu)
|
||||||
|
|
||||||
static bool kvm_hyp_handle_eret(struct kvm_vcpu *vcpu, u64 *exit_code)
|
static bool kvm_hyp_handle_eret(struct kvm_vcpu *vcpu, u64 *exit_code)
|
||||||
{
|
{
|
||||||
u64 spsr, mode;
|
u64 esr = kvm_vcpu_get_esr(vcpu);
|
||||||
|
u64 spsr, elr, mode;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Going through the whole put/load motions is a waste of time
|
* Going through the whole put/load motions is a waste of time
|
||||||
|
@ -242,10 +243,18 @@ static bool kvm_hyp_handle_eret(struct kvm_vcpu *vcpu, u64 *exit_code)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* If ERETAx fails, take the slow path */
|
||||||
|
if (esr_iss_is_eretax(esr)) {
|
||||||
|
if (!(vcpu_has_ptrauth(vcpu) && kvm_auth_eretax(vcpu, &elr)))
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
elr = read_sysreg_el1(SYS_ELR);
|
||||||
|
}
|
||||||
|
|
||||||
spsr = (spsr & ~(PSR_MODE_MASK | PSR_MODE32_BIT)) | mode;
|
spsr = (spsr & ~(PSR_MODE_MASK | PSR_MODE32_BIT)) | mode;
|
||||||
|
|
||||||
write_sysreg_el2(spsr, SYS_SPSR);
|
write_sysreg_el2(spsr, SYS_SPSR);
|
||||||
write_sysreg_el2(read_sysreg_el1(SYS_ELR), SYS_ELR);
|
write_sysreg_el2(elr, SYS_ELR);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue