[PATCH 4/4] KVM: arm64: Workaround Cortex-A510's single-step and PAC trap errata
James Morse
james.morse at arm.com
Tue Jan 25 10:19:45 PST 2022
Hi Marc,
On 25/01/2022 16:51, Marc Zyngier wrote:
> On Tue, 25 Jan 2022 15:38:03 +0000,
> James Morse <james.morse at arm.com> wrote:
>>
>> Cortex-A510's erratum #2077057 causes SPSR_EL2 to be corrupted when
>> single-stepping authenticated ERET instructions. A single step is
>> expected, but a pointer authentication trap is taken instead. The
>> erratum causes SPSR_EL1 to be copied to SPSR_EL2, which could allow
>> EL1 to cause a return to EL2 with a guest controlled ELR_EL2.
>>
>> Because the conditions require an ERET into active-not-pending state,
>> this is only a problem for the EL2 when EL2 is stepping EL1. In this case
>> the previous SPSR_EL2 value is preserved in struct kvm_vcpu, and can be
>> restored.
> Urgh. That's pretty nasty :-(.
>> diff --git a/arch/arm64/kvm/hyp/include/hyp/switch.h b/arch/arm64/kvm/hyp/include/hyp/switch.h
>> index 331dd10821df..93bf140cc972 100644
>> --- a/arch/arm64/kvm/hyp/include/hyp/switch.h
>> +++ b/arch/arm64/kvm/hyp/include/hyp/switch.h
>> @@ -440,6 +442,22 @@ static inline bool fixup_guest_exit(struct kvm_vcpu *vcpu, u64 *exit_code)
>> write_sysreg_el2(read_sysreg_el2(SYS_ELR) - 4, SYS_ELR);
>> }
>>
>> + /*
>> + * Check for the conditions of Cortex-A510's #2077057. When these occur
>> + * SPSR_EL2 can't be trusted, but isn't needed either as it is
>> + * unchanged from the value in vcpu_gp_regs(vcpu)->pstate.
>> + * Did we just take a PAC exception when a step exception was expected?
>> + */
>> + if (IS_ENABLED(CONFIG_ARM64_ERRATUM_2077057) &&
>
> nit: we can drop this IS_ENABLED()...
Hmmm, I thought dead code elimination was a good thing!
Without the cpu_errata.c match, (which is also guarded by #ifdef), the cap will never be
true, even if an affected CPU showed up. This way the compiler knows it can remove all this.
>> + cpus_have_const_cap(ARM64_WORKAROUND_2077057) &&
>
> and make this a final cap. Being a ARM64_CPUCAP_LOCAL_CPU_ERRATUM, we
> won't accept late CPUs on a system that wasn't affected until then.
>
>> + ARM_EXCEPTION_CODE(*exit_code) != ARM_EXCEPTION_IRQ &&
>> + esr_ec == ESR_ELx_EC_PAC &&
>> + vcpu->guest_debug & KVM_GUESTDBG_SINGLESTEP) {
>> + /* Active-not-pending? */
>> + if (*vcpu_cpsr(vcpu) & DBG_SPSR_SS)
>> + write_sysreg_el2(*vcpu_cpsr(vcpu), SYS_SPSR);
>
> Err... Isn't this way too late? The function starts with:
>
> vcpu->arch.ctxt.regs.pstate = read_sysreg_el2(SYS_SPSR);
>
> which is just another way to write:
>
> *vcpu_cpsr(vcpu) = read_sysreg_el2(SYS_SPSR);
>
> By that time, the vcpu's PSTATE is terminally corrupted.
Yes - bother. Staring at it didn't let me spot that!
I can hit the conditions to test this, but due to lack of imagination the model doesn't
corrupt the SPSR.
> I think you need to hoist this workaround way up, before we call into
> early_exit_filter() as it will assume that the guest pstate is correct
> (this is used by both pKVM and the NV code).
>
> Something like this (untested):
>
> diff --git a/arch/arm64/kvm/hyp/include/hyp/switch.h b/arch/arm64/kvm/hyp/include/hyp/switch.h
> index 93bf140cc972..a8a1502db237 100644
> --- a/arch/arm64/kvm/hyp/include/hyp/switch.h
> +++ b/arch/arm64/kvm/hyp/include/hyp/switch.h
> @@ -402,6 +402,26 @@ static inline bool kvm_hyp_handle_exit(struct kvm_vcpu *vcpu, u64 *exit_code)
> return false;
> }
>
> +static inline void synchronize_vcpu_pstate(struct kvm_vcpu *vcpu,
> + u64 *exit_code)
> +{
> + /*
> + * Check for the conditions of Cortex-A510's #2077057. When these occur
> + * SPSR_EL2 can't be trusted, but isn't needed either as it is
> + * unchanged from the value in vcpu_gp_regs(vcpu)->pstate.
> + * Did we just take a PAC exception when a step exception (being in the
> + * Active-not-pending state) was expected?
> + */
> + if (cpus_have_final_cap(ARM64_WORKAROUND_2077057) &&
> + ARM_EXCEPTION_CODE(*exit_code) != ARM_EXCEPTION_IRQ &&
> + kvm_vcpu_trap_get_class(vcpu) == ESR_ELx_EC_PAC &&
The vcpu's esr_el2 isn't yet set:
| ESR_ELx_EC(read_sysreg_el2(SYS_ESR)) == ESR_ELx_EC_PAC
(and I'll shuffle the order so this is last as its an extra sysreg read)
> + vcpu->guest_debug & KVM_GUESTDBG_SINGLESTEP &&
> + *vcpu_cpsr(vcpu) & DBG_SPSR_SS)
> + write_sysreg_el2(*vcpu_cpsr(vcpu), SYS_SPSR);
> +
> + *vcpu_cpsr(vcpu) = read_sysreg_el2(SYS_SPSR);
> +}
> +
> /*
> * Return true when we were able to fixup the guest exit and should return to
> * the guest, false when we should restore the host state and return to the
> @@ -415,7 +435,7 @@ static inline bool fixup_guest_exit(struct kvm_vcpu *vcpu, u64 *exit_code)
> * Save PSTATE early so that we can evaluate the vcpu mode
> * early on.
> */
> - vcpu->arch.ctxt.regs.pstate = read_sysreg_el2(SYS_SPSR);
> + synchronize_vcpu_pstate(vcpu, exit_code);
Even better, that saves the noise from moving esr_ec around!
> Other than that, I'm happy to take the series as a whole ASAP, if only
> for the two pretty embarrassing bug fixes. If you can respin it
> shortly and address the comments above, I'd like it into -rc2.
Will do. Shout if you strongly care about the IS_ENABLED().
Thanks,
James
More information about the linux-arm-kernel
mailing list