Some parts of the SME state are optional, enabled by additional features
on top of the base FEAT_SME and controlled with enable bits in SMCR_ELx. We
unconditionally enable these for the host but for KVM we will allow the
feature set exposed to guests to be restricted by the VMM. These are the
FFR register (FEAT_SME_FA64) and ZT0 (FEAT_SME2).

We defer saving of guest floating point state for non-protected guests to
the host kernel. We also want to avoid having to reconfigure the guest
floating point state if nothing used the floating point state while running
the host. If the guest was running with the optional features disabled then
traps will be enabled for them so the host kernel will need to skip
accessing that state when saving state for the guest.

Support this by moving the decision about saving this state to the point
where we bind floating point state to the CPU, instead of only storing
the SME VL to use we store the SMCR value.  This includes all the enable
controls for the subfeatures along the vector length.

In order to keep the code paths for the vector extensions consistent
also adjust the SVE path to store a ZCR value instead of the VL, since
no fields other than LEN are currently defined for ZCR this is much less
of a meaningful change.

Signed-off-by: Mark Brown <[email protected]>
---
 arch/arm64/include/asm/fpsimd.h |  4 ++--
 arch/arm64/kernel/fpsimd.c      | 16 ++++++++--------
 arch/arm64/kvm/fpsimd.c         |  3 ++-
 3 files changed, 12 insertions(+), 11 deletions(-)

diff --git a/arch/arm64/include/asm/fpsimd.h b/arch/arm64/include/asm/fpsimd.h
index 8d2a3d63481b..0a3299142683 100644
--- a/arch/arm64/include/asm/fpsimd.h
+++ b/arch/arm64/include/asm/fpsimd.h
@@ -166,8 +166,8 @@ struct cpu_fp_state {
        struct arm64_sme_state *sme_state;
        u64 *svcr;
        u64 *fpmr;
-       unsigned int sve_vl;
-       unsigned int sme_vl;
+       u64 smcr;
+       u64 zcr;
        enum fp_type *fp_type;
        enum fp_type to_save;
 };
diff --git a/arch/arm64/kernel/fpsimd.c b/arch/arm64/kernel/fpsimd.c
index 8009213288b1..dc1ad10e39a2 100644
--- a/arch/arm64/kernel/fpsimd.c
+++ b/arch/arm64/kernel/fpsimd.c
@@ -470,7 +470,7 @@ static void fpsimd_save_user_state(void)
        /* set by fpsimd_bind_task_to_cpu() or fpsimd_bind_state_to_cpu() */
        bool save_sve_regs = false;
        bool save_ffr;
-       unsigned int vl;
+       unsigned int vq;
 
        WARN_ON(!system_supports_fpsimd());
        WARN_ON(preemptible());
@@ -494,7 +494,7 @@ static void fpsimd_save_user_state(void)
            last->to_save == FP_STATE_SVE) {
                save_sve_regs = true;
                save_ffr = true;
-               vl = last->sve_vl;
+               vq = SYS_FIELD_GET(ZCR_ELx, LEN, last->zcr) + 1;
        }
 
        if (system_supports_sme()) {
@@ -504,19 +504,19 @@ static void fpsimd_save_user_state(void)
 
                if (*svcr & SVCR_ZA_MASK)
                        sme_save_state(last->sme_state,
-                                      system_supports_sme2());
+                                      last->smcr & SMCR_ELx_EZT0);
 
                /* If we are in streaming mode override regular SVE. */
                if (*svcr & SVCR_SM_MASK) {
                        save_sve_regs = true;
-                       save_ffr = system_supports_fa64();
-                       vl = last->sme_vl;
+                       save_ffr = last->smcr & SMCR_ELx_FA64;
+                       vq = SYS_FIELD_GET(SMCR_ELx, LEN, last->smcr) + 1;
                }
        }
 
        if (IS_ENABLED(CONFIG_ARM64_SVE) && save_sve_regs) {
                /* Get the configured VL from RDVL, will account for SM */
-               if (WARN_ON(sve_get_vl() != vl)) {
+               if (WARN_ON(sve_get_vl() != sve_vl_from_vq(vq))) {
                        /*
                         * Can't save the user regs, so current would
                         * re-enter user with corrupt state.
@@ -1704,8 +1704,8 @@ static void fpsimd_bind_task_to_cpu(void)
        last->st = &current->thread.uw.fpsimd_state;
        last->sve_state = current->thread.sve_state;
        last->sme_state = current->thread.sme_state;
-       last->sve_vl = task_get_sve_vl(current);
-       last->sme_vl = task_get_sme_vl(current);
+       last->zcr = task_zcr(current);
+       last->smcr = task_smcr(current);
        last->svcr = &current->thread.svcr;
        last->fpmr = &current->thread.uw.fpmr;
        last->fp_type = &current->thread.fp_type;
diff --git a/arch/arm64/kvm/fpsimd.c b/arch/arm64/kvm/fpsimd.c
index 3f6b1e29cd6b..567dd43970c5 100644
--- a/arch/arm64/kvm/fpsimd.c
+++ b/arch/arm64/kvm/fpsimd.c
@@ -89,7 +89,8 @@ void kvm_arch_vcpu_ctxsync_fp(struct kvm_vcpu *vcpu)
                 */
                fp_state.st = &vcpu->arch.ctxt.fp_regs;
                fp_state.sve_state = vcpu->arch.sve_state;
-               fp_state.sve_vl = vcpu->arch.sve_max_vl;
+               fp_state.zcr = vcpu_sve_max_vq(vcpu) - 1;
+               fp_state.smcr = 0;
                fp_state.sme_state = NULL;
                fp_state.svcr = __ctxt_sys_reg(&vcpu->arch.ctxt, SVCR);
                fp_state.fpmr = __ctxt_sys_reg(&vcpu->arch.ctxt, FPMR);

-- 
2.47.3


Reply via email to