[PATCH v8 26/38] KVM: arm64: Handle SME host state when running guests

Marc Zyngier maz at kernel.org
Tue Jan 25 03:59:02 PST 2022


On Tue, 25 Jan 2022 00:11:02 +0000,
Mark Brown <broonie at kernel.org> wrote:
> 
> While we don't currently support SME in guests we do currently support it
> for the host system so we need to take care of SME's impact, including
> the floating point register state, when running guests. Simiarly to SVE
> we need to manage the traps in CPACR_RL1, what is new is the handling of
> streaming mode and ZA.
> 
> Normally we defer any handling of the floating point register state until
> the guest first uses it however if the system is in streaming mode FPSIMD
> and SVE operations may generate SME traps which we would need to distinguish
> from actual attempts by the guest to use SME. Rather than do this for the
> time being if we are in streaming mode when entering the guest we force
> the floating point state to be saved immediately and exit streaming mode,
> meaning that the guest won't generate SME traps for supported operations.
> 
> We could handle ZA in the access trap similarly to the FPSIMD/SVE state
> without the disruption caused by streaming mode but for simplicity
> handle it the same way as streaming mode for now.
> 
> This will be revisited when we support SME for guests (hopefully before SME
> hardware becomes available), for now it will only incur additional cost on
> systems with SME and even there only if streaming mode or ZA are enabled.
> 
> Signed-off-by: Mark Brown <broonie at kernel.org>
> ---
>  arch/arm64/include/asm/kvm_host.h |  1 +
>  arch/arm64/kvm/fpsimd.c           | 38 +++++++++++++++++++++++++++++++
>  2 files changed, 39 insertions(+)
> 
> diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> index 7dc85d5a6552..404b7358ba96 100644
> --- a/arch/arm64/include/asm/kvm_host.h
> +++ b/arch/arm64/include/asm/kvm_host.h
> @@ -438,6 +438,7 @@ struct kvm_vcpu_arch {
>  #define KVM_ARM64_DEBUG_STATE_SAVE_SPE	(1 << 12) /* Save SPE context if active  */
>  #define KVM_ARM64_DEBUG_STATE_SAVE_TRBE	(1 << 13) /* Save TRBE context if active  */
>  #define KVM_ARM64_FP_FOREIGN_FPSTATE	(1 << 14)
> +#define KVM_ARM64_HOST_SME_ENABLED	(1 << 15) /* SME enabled for EL0 */
>  
>  #define KVM_GUESTDBG_VALID_MASK (KVM_GUESTDBG_ENABLE | \
>  				 KVM_GUESTDBG_USE_SW_BP | \
> diff --git a/arch/arm64/kvm/fpsimd.c b/arch/arm64/kvm/fpsimd.c
> index 338733ac63f8..cecaddb644ce 100644
> --- a/arch/arm64/kvm/fpsimd.c
> +++ b/arch/arm64/kvm/fpsimd.c
> @@ -82,6 +82,26 @@ void kvm_arch_vcpu_load_fp(struct kvm_vcpu *vcpu)
>  
>  	if (read_sysreg(cpacr_el1) & CPACR_EL1_ZEN_EL0EN)
>  		vcpu->arch.flags |= KVM_ARM64_HOST_SVE_ENABLED;
> +
> +	/*
> +	 * We don't currently support SME guests but if we leave
> +	 * things in streaming mode then when the guest starts running
> +	 * FPSIMD or SVE code it may generate SME traps so as a
> +	 * special case if we are in streaming mode we force the host
> +	 * state to be saved now and exit streaming mode so that we
> +	 * don't have to handle any SME traps for valid guest
> +	 * operations. Do this for ZA as well for now for simplicity.
> +	 */
> +	if (system_supports_sme()) {
> +		if (read_sysreg(cpacr_el1) & CPACR_EL1_SMEN_EL0EN)
> +			vcpu->arch.flags |= KVM_ARM64_HOST_SME_ENABLED;
> +
> +		if (read_sysreg_s(SYS_SVCR_EL0) &
> +		    (SYS_SVCR_EL0_SM_MASK | SYS_SVCR_EL0_ZA_MASK)) {
> +			vcpu->arch.flags &= ~KVM_ARM64_FP_HOST;
> +			fpsimd_save_and_flush_cpu_state();
> +		}
> +	}
>  }
>  
>  void kvm_arch_vcpu_ctxflush_fp(struct kvm_vcpu *vcpu)
> @@ -129,6 +149,24 @@ void kvm_arch_vcpu_put_fp(struct kvm_vcpu *vcpu)
>  
>  	local_irq_save(flags);
>  
> +	/*
> +	 * If we have VHE then the Hyp code will reset CPACR_EL1 to
> +	 * CPACR_EL1_DEFAULT and we need to reenable SME.
> +	 */
> +	if (has_vhe()) {
> +		if (system_supports_sme()) {

nit:	if (has_vhe() && system_supports_sme()) {

saves you one level of indentation.

> +			/* Also restore EL0 state seen on entry */
> +			if (vcpu->arch.flags & KVM_ARM64_HOST_SME_ENABLED)
> +				sysreg_clear_set(CPACR_EL1, 0,
> +						 CPACR_EL1_SMEN_EL0EN |
> +						 CPACR_EL1_SMEN_EL1EN);
> +			else
> +				sysreg_clear_set(CPACR_EL1,
> +						 CPACR_EL1_SMEN_EL0EN,
> +						 CPACR_EL1_SMEN_EL1EN);

I find the use of CPACR_EL1_SMEN in some cases and its individual bits
in some others pretty confusing. I understand that you have modelled
it after the SVE code, but maybe this is a mistake we don't need to
repeat. I'd be in favour of directly exposing the individual bits in
all cases.

Thanks,

	M.

-- 
Without deviation from the norm, progress is not possible.



More information about the linux-arm-kernel mailing list