On Tue, 23 Dec 2025 at 01:22, Mark Brown <[email protected]> wrote:
>
> SME implements a vector length which architecturally looks very similar
> to that for SVE, configured in a very similar manner.  This controls the
> vector length used for the ZA matrix register, and for the SVE vector
> and predicate registers when in streaming mode.  The only substantial
> difference is that unlike SVE the architecture does not guarantee that
> any particular vector length will be implemented.
>
> Configuration for SME vector lengths is done using a virtual register as
> for SVE, hook up the implementation for the virtual register.  Since we
> do not yet have support for any of the new SME registers stub register
> access functions are provided that only allow VL configuration.  These
> will be extended as the SME specific registers, as for SVE.
>
> Since vq_available() is currently only defined for CONFIG_SVE add a stub
> for builds where that is disabled.
>
> Signed-off-by: Mark Brown <[email protected]>
> ---
>  arch/arm64/include/asm/fpsimd.h   |  1 +
>  arch/arm64/include/asm/kvm_host.h | 24 ++++++++++--
>  arch/arm64/include/uapi/asm/kvm.h |  9 +++++
>  arch/arm64/kvm/guest.c            | 82 
> +++++++++++++++++++++++++++++++--------
>  4 files changed, 96 insertions(+), 20 deletions(-)
>
> diff --git a/arch/arm64/include/asm/fpsimd.h b/arch/arm64/include/asm/fpsimd.h
> index 146c1af55e22..8b0840bd7e14 100644
> --- a/arch/arm64/include/asm/fpsimd.h
> +++ b/arch/arm64/include/asm/fpsimd.h
> @@ -340,6 +340,7 @@ static inline int sve_max_vl(void)
>         return -EINVAL;
>  }
>
> +static inline bool vq_available(enum vec_type type, unsigned int vq) { 
> return false; }
>  static inline bool sve_vq_available(unsigned int vq) { return false; }
>
>  static inline void sve_user_disable(void) { BUILD_BUG(); }
> diff --git a/arch/arm64/include/asm/kvm_host.h 
> b/arch/arm64/include/asm/kvm_host.h
> index 3a3330b2a6a9..b41700df3ce9 100644
> --- a/arch/arm64/include/asm/kvm_host.h
> +++ b/arch/arm64/include/asm/kvm_host.h
> @@ -810,8 +810,15 @@ struct kvm_vcpu_arch {
>          * low 128 bits of the SVE Z registers.  When the core
>          * floating point code saves the register state of a task it
>          * records which view it saved in fp_type.
> +        *
> +        * If SME support is also present then it provides an
> +        * alternative view of the SVE registers accessed as for the Z
> +        * registers when PSTATE.SM is 1, plus an additional set of
> +        * SME specific state in the matrix register ZA and LUT
> +        * register ZT0.
>          */
>         void *sve_state;
> +       void *sme_state;
>         enum fp_type fp_type;
>         unsigned int max_vl[ARM64_VEC_MAX];
>
> @@ -1098,14 +1105,23 @@ struct kvm_vcpu_arch {
>
>  #define vcpu_gp_regs(v)                (&(v)->arch.ctxt.regs)
>
> -/* Pointer to the vcpu's SVE FFR for sve_{save,load}_state() */
> -#define vcpu_sve_pffr(vcpu) (kern_hyp_va((vcpu)->arch.sve_state) +     \
> -                            
> sve_ffr_offset((vcpu)->arch.max_vl[ARM64_VEC_SVE]))
> -
>  #define vcpu_vec_max_vq(vcpu, type) sve_vq_from_vl((vcpu)->arch.max_vl[type])
>
>  #define vcpu_sve_max_vq(vcpu)  vcpu_vec_max_vq(vcpu, ARM64_VEC_SVE)
> +#define vcpu_sme_max_vq(vcpu)  vcpu_vec_max_vq(vcpu, ARM64_VEC_SME)
> +
> +#define vcpu_sve_max_vl(vcpu)  ((vcpu)->arch.max_vl[ARM64_VEC_SVE])
> +#define vcpu_sme_max_vl(vcpu)  ((vcpu)->arch.max_vl[ARM64_VEC_SME])
>
> +#define vcpu_max_vl(vcpu) max(vcpu_sve_max_vl(vcpu), vcpu_sme_max_vl(vcpu))
> +#define vcpu_max_vq(vcpu) sve_vq_from_vl(vcpu_max_vl(vcpu))
> +
> +#define vcpu_cur_sve_vl(vcpu) (vcpu_in_streaming_mode(vcpu) ? \
> +                              vcpu_sme_max_vl(vcpu) : vcpu_sve_max_vl(vcpu))

nit: This isn't really the current VL, but the current max VL. That
said, I don't think 'cur_max` is a better name. Maybe a comment or
something?

> +/* Pointer to the vcpu's SVE FFR for sve_{save,load}_state() */
> +#define vcpu_sve_pffr(vcpu) (kern_hyp_va((vcpu)->arch.sve_state) +     \
> +                            sve_ffr_offset(vcpu_cur_sve_vl(vcpu)))
>
>  #define vcpu_sve_zcr_elx(vcpu)                                         \
>         (unlikely(is_hyp_ctxt(vcpu)) ? ZCR_EL2 : ZCR_EL1)
> diff --git a/arch/arm64/include/uapi/asm/kvm.h 
> b/arch/arm64/include/uapi/asm/kvm.h
> index c67564f02981..498a49a61487 100644
> --- a/arch/arm64/include/uapi/asm/kvm.h
> +++ b/arch/arm64/include/uapi/asm/kvm.h
> @@ -354,6 +354,15 @@ struct kvm_arm_counter_offset {
>  #define KVM_ARM64_SVE_VLS_WORDS        \
>         ((KVM_ARM64_SVE_VQ_MAX - KVM_ARM64_SVE_VQ_MIN) / 64 + 1)
>
> +/* SME registers */
> +#define KVM_REG_ARM64_SME              (0x17 << KVM_REG_ARM_COPROC_SHIFT)
> +
> +/* Vector lengths pseudo-register: */
> +#define KVM_REG_ARM64_SME_VLS          (KVM_REG_ARM64 | KVM_REG_ARM64_SME | \
> +                                        KVM_REG_SIZE_U512 | 0xfffe)
> +#define KVM_ARM64_SME_VLS_WORDS        \
> +       ((KVM_ARM64_SVE_VQ_MAX - KVM_ARM64_SVE_VQ_MIN) / 64 + 1)
> +
>  /* Bitmap feature firmware registers */
>  #define KVM_REG_ARM_FW_FEAT_BMAP               (0x0016 << 
> KVM_REG_ARM_COPROC_SHIFT)
>  #define KVM_REG_ARM_FW_FEAT_BMAP_REG(r)                (KVM_REG_ARM64 | 
> KVM_REG_SIZE_U64 | \
> diff --git a/arch/arm64/kvm/guest.c b/arch/arm64/kvm/guest.c
> index 456ef61b6ed5..2a1fdcb0ec49 100644
> --- a/arch/arm64/kvm/guest.c
> +++ b/arch/arm64/kvm/guest.c
> @@ -310,22 +310,20 @@ static int set_core_reg(struct kvm_vcpu *vcpu, const 
> struct kvm_one_reg *reg)
>  #define vq_mask(vq) ((u64)1 << ((vq) - SVE_VQ_MIN) % 64)
>  #define vq_present(vqs, vq) (!!((vqs)[vq_word(vq)] & vq_mask(vq)))
>
> -static int get_sve_vls(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
> +static int get_vec_vls(enum vec_type vec_type, struct kvm_vcpu *vcpu,
> +                     const struct kvm_one_reg *reg)
>  {
>         unsigned int max_vq, vq;
>         u64 vqs[KVM_ARM64_SVE_VLS_WORDS];
>
> -       if (!vcpu_has_sve(vcpu))
> -               return -ENOENT;
> -
> -       if (WARN_ON(!sve_vl_valid(vcpu->arch.max_vl[ARM64_VEC_SVE])))
> +       if (WARN_ON(!sve_vl_valid(vcpu->arch.max_vl[vec_type])))
>                 return -EINVAL;
>
>         memset(vqs, 0, sizeof(vqs));
>
> -       max_vq = vcpu_sve_max_vq(vcpu);
> +       max_vq = vcpu_vec_max_vq(vcpu, vec_type);
>         for (vq = SVE_VQ_MIN; vq <= max_vq; ++vq)
> -               if (sve_vq_available(vq))
> +               if (vq_available(vec_type, vq))
>                         vqs[vq_word(vq)] |= vq_mask(vq);
>
>         if (copy_to_user((void __user *)reg->addr, vqs, sizeof(vqs)))
> @@ -334,40 +332,41 @@ static int get_sve_vls(struct kvm_vcpu *vcpu, const 
> struct kvm_one_reg *reg)
>         return 0;
>  }
>
> -static int set_sve_vls(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
> +static int set_vec_vls(enum vec_type vec_type, struct kvm_vcpu *vcpu,
> +                      const struct kvm_one_reg *reg)
>  {
>         unsigned int max_vq, vq;
>         u64 vqs[KVM_ARM64_SVE_VLS_WORDS];
>
> -       if (!vcpu_has_sve(vcpu))
> -               return -ENOENT;
> -
>         if (kvm_arm_vcpu_vec_finalized(vcpu))
>                 return -EPERM; /* too late! */
>
> -       if (WARN_ON(vcpu->arch.sve_state))
> +       if (WARN_ON(!sve_vl_valid(vcpu->arch.max_vl[vec_type])))
>                 return -EINVAL;
>
>         if (copy_from_user(vqs, (const void __user *)reg->addr, sizeof(vqs)))
>                 return -EFAULT;
>
> +       if (WARN_ON(vcpu->arch.sve_state || vcpu->arch.sme_state))
> +               return -EINVAL;
> +

Can this ever happen? kvm_arm_vcpu_vec_finalized() is checked above,
and vcpu->arch.{sve,sme}_state are only assigned in
kvm_vcpu_finalize_vec() immediately before setting the finalized flag.

Cheers,
/fuad



>         max_vq = 0;
>         for (vq = SVE_VQ_MIN; vq <= SVE_VQ_MAX; ++vq)
>                 if (vq_present(vqs, vq))
>                         max_vq = vq;
>
> -       if (max_vq > sve_vq_from_vl(kvm_max_vl[ARM64_VEC_SVE]))
> +       if (max_vq > sve_vq_from_vl(kvm_max_vl[vec_type]))
>                 return -EINVAL;
>
>         /*
>          * Vector lengths supported by the host can't currently be
>          * hidden from the guest individually: instead we can only set a
> -        * maximum via ZCR_EL2.LEN.  So, make sure the available vector
> +        * maximum via xCR_EL2.LEN.  So, make sure the available vector
>          * lengths match the set requested exactly up to the requested
>          * maximum:
>          */
>         for (vq = SVE_VQ_MIN; vq <= max_vq; ++vq)
> -               if (vq_present(vqs, vq) != sve_vq_available(vq))
> +               if (vq_present(vqs, vq) != vq_available(vec_type, vq))
>                         return -EINVAL;
>
>         /* Can't run with no vector lengths at all: */
> @@ -375,11 +374,27 @@ static int set_sve_vls(struct kvm_vcpu *vcpu, const 
> struct kvm_one_reg *reg)
>                 return -EINVAL;
>
>         /* vcpu->arch.sve_state will be alloc'd by kvm_vcpu_finalize_sve() */
> -       vcpu->arch.max_vl[ARM64_VEC_SVE] = sve_vl_from_vq(max_vq);
> +       vcpu->arch.max_vl[vec_type] = sve_vl_from_vq(max_vq);
>
>         return 0;
>  }
>
> +static int get_sve_vls(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
> +{
> +       if (!vcpu_has_sve(vcpu))
> +               return -ENOENT;
> +
> +       return get_vec_vls(ARM64_VEC_SVE, vcpu, reg);
> +}
> +
> +static int set_sve_vls(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
> +{
> +       if (!vcpu_has_sve(vcpu))
> +               return -ENOENT;
> +
> +       return set_vec_vls(ARM64_VEC_SVE, vcpu, reg);
> +}
> +
>  #define SVE_REG_SLICE_SHIFT    0
>  #define SVE_REG_SLICE_BITS     5
>  #define SVE_REG_ID_SHIFT       (SVE_REG_SLICE_SHIFT + SVE_REG_SLICE_BITS)
> @@ -533,6 +548,39 @@ static int set_sve_reg(struct kvm_vcpu *vcpu, const 
> struct kvm_one_reg *reg)
>         return 0;
>  }
>
> +static int get_sme_vls(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
> +{
> +       if (!vcpu_has_sme(vcpu))
> +               return -ENOENT;
> +
> +       return get_vec_vls(ARM64_VEC_SME, vcpu, reg);
> +}
> +
> +static int set_sme_vls(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
> +{
> +       if (!vcpu_has_sme(vcpu))
> +               return -ENOENT;
> +
> +       return set_vec_vls(ARM64_VEC_SME, vcpu, reg);
> +}
> +
> +static int get_sme_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
> +{
> +       /* Handle the KVM_REG_ARM64_SME_VLS pseudo-reg as a special case: */
> +       if (reg->id == KVM_REG_ARM64_SME_VLS)
> +               return get_sme_vls(vcpu, reg);
> +
> +       return -EINVAL;
> +}
> +
> +static int set_sme_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
> +{
> +       /* Handle the KVM_REG_ARM64_SME_VLS pseudo-reg as a special case: */
> +       if (reg->id == KVM_REG_ARM64_SME_VLS)
> +               return set_sme_vls(vcpu, reg);
> +
> +       return -EINVAL;
> +}
>  int kvm_arch_vcpu_ioctl_get_regs(struct kvm_vcpu *vcpu, struct kvm_regs 
> *regs)
>  {
>         return -EINVAL;
> @@ -711,6 +759,7 @@ int kvm_arm_get_reg(struct kvm_vcpu *vcpu, const struct 
> kvm_one_reg *reg)
>         case KVM_REG_ARM_FW_FEAT_BMAP:
>                 return kvm_arm_get_fw_reg(vcpu, reg);
>         case KVM_REG_ARM64_SVE: return get_sve_reg(vcpu, reg);
> +       case KVM_REG_ARM64_SME: return get_sme_reg(vcpu, reg);
>         }
>
>         return kvm_arm_sys_reg_get_reg(vcpu, reg);
> @@ -728,6 +777,7 @@ int kvm_arm_set_reg(struct kvm_vcpu *vcpu, const struct 
> kvm_one_reg *reg)
>         case KVM_REG_ARM_FW_FEAT_BMAP:
>                 return kvm_arm_set_fw_reg(vcpu, reg);
>         case KVM_REG_ARM64_SVE: return set_sve_reg(vcpu, reg);
> +       case KVM_REG_ARM64_SME: return set_sme_reg(vcpu, reg);
>         }
>
>         return kvm_arm_sys_reg_set_reg(vcpu, reg);
>
> --
> 2.47.3
>

Reply via email to