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
>