As per DDI0487 R_TYTWB GCS adds an additional case where an illegal exception return can be generated. If all of:
- PSTATE.EXLOCK is 0. - The EL is not being changed by the ERET. - GCSCR_ELx.EXLOCKEN is 1.
are true then the return is illegal. Emulate this behaviour when emulating ERET for nested guests.
Signed-off-by: Mark Brown broonie@kernel.org --- arch/arm64/kvm/emulate-nested.c | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-)
diff --git a/arch/arm64/kvm/emulate-nested.c b/arch/arm64/kvm/emulate-nested.c index 90cb4b7ae0ff..9b02b85eda64 100644 --- a/arch/arm64/kvm/emulate-nested.c +++ b/arch/arm64/kvm/emulate-nested.c @@ -2632,6 +2632,41 @@ bool forward_debug_exception(struct kvm_vcpu *vcpu) return forward_mdcr_traps(vcpu, MDCR_EL2_TDE); }
+/* + * A subset of the pseudocode ELFromSPSR(), validity checks are + * assumed to have been done in code that is not GCS specific. + */ +static inline int exlock_el_from_spsr(u64 spsr) +{ + return FIELD_GET(GENMASK(3, 2), spsr); +} + +/* See IllegalExceptionReturn() pseudocode */ +static bool kvm_check_illegal_exlock_return(struct kvm_vcpu *vcpu, u64 spsr) +{ + u64 cur_el, target_el; + u64 gcscr; + + if (!kvm_has_gcs(vcpu->kvm)) + return false; + + if (spsr & PSR_EXLOCK_BIT) + return false; + + cur_el = exlock_el_from_spsr(vcpu->arch.ctxt.regs.pstate); + target_el = exlock_el_from_spsr(spsr); + + if (cur_el != target_el) + return false; + + if (vcpu_is_el2(vcpu)) + gcscr = __vcpu_sys_reg(vcpu, GCSCR_EL2); + else + gcscr = __vcpu_sys_reg(vcpu, GCSCR_EL1); + + return gcscr & GCSCR_ELx_EXLOCKEN; +} + static u64 kvm_check_illegal_exception_return(struct kvm_vcpu *vcpu, u64 spsr) { u64 mode = spsr & PSR_MODE_MASK; @@ -2642,12 +2677,15 @@ static u64 kvm_check_illegal_exception_return(struct kvm_vcpu *vcpu, u64 spsr) * - trying to return to an illegal M value * - trying to return to a 32bit EL * - trying to return to EL1 with HCR_EL2.TGE set + * - GCSCR_ELx.EXLOCKEN is 1 and PSTATE.EXLOCK is 0 when attempting + * to return from ELx the same EL. */ if (mode == PSR_MODE_EL3t || mode == PSR_MODE_EL3h || mode == 0b00001 || (mode & BIT(1)) || (spsr & PSR_MODE32_BIT) || (vcpu_el2_tge_is_set(vcpu) && (mode == PSR_MODE_EL1t || - mode == PSR_MODE_EL1h))) { + mode == PSR_MODE_EL1h)) || + kvm_check_illegal_exlock_return(vcpu, spsr)) { /* * The guest is playing with our nerves. Preserve EL, SP, * masks, flags from the existing PSTATE, and set IL.