On Tue, 23 Dec 2025 at 01:23, Mark Brown wrote:
>
> SME introduces two new registers, the ZA matrix register and the ZT0 LUT
> register. Both of these registers are only accessible when PSTATE.ZA is
> set and ZT0 is only present if SME2 is enabled for the guest. Provide
> support for configuring these from VMMs.
>
> The ZA matrix is a single SVL*SVL register which is available when
> PSTATE.ZA is set. We follow the pattern established by the architecture
> itself and expose this to userspace as a series of horizontal SVE vectors
> with the streaming mode vector length, using the format already established
> for the SVE vectors themselves.
>
> ZT0 is a single register with a refreshingly fixed size 512 bit register
> which is like ZA accessible only when PSTATE.ZA is set. Add support for it
> to the userspace API, as with ZA we allow the register to be read or written
> regardless of the state of PSTATE.ZA in order to simplify userspace usage.
> The value will be reset to 0 whenever PSTATE.ZA changes from 0 to 1,
> userspace can read stale values but these are not observable by the guest
> without manipulation of PSTATE.ZA by userspace.
>
> While there is currently only one ZT register the naming as ZT0 and the
> instruction encoding clearly leave room for future extensions adding more
> ZT registers. This encoding can readily support such an extension if one is
> introduced.
>
> Signed-off-by: Mark Brown
> ---
> arch/arm64/include/uapi/asm/kvm.h | 17 ++
> arch/arm64/kvm/guest.c| 114
> +-
> 2 files changed, 129 insertions(+), 2 deletions(-)
>
> diff --git a/arch/arm64/include/uapi/asm/kvm.h
> b/arch/arm64/include/uapi/asm/kvm.h
> index 498a49a61487..9a19cc58d227 100644
> --- a/arch/arm64/include/uapi/asm/kvm.h
> +++ b/arch/arm64/include/uapi/asm/kvm.h
> @@ -357,6 +357,23 @@ struct kvm_arm_counter_offset {
> /* SME registers */
> #define KVM_REG_ARM64_SME (0x17 << KVM_REG_ARM_COPROC_SHIFT)
>
> +#define KVM_ARM64_SME_VQ_MIN __SVE_VQ_MIN
> +#define KVM_ARM64_SME_VQ_MAX __SVE_VQ_MAX
> +
> +/* ZA and ZTn occupy blocks at the following offsets within this range: */
> +#define KVM_REG_ARM64_SME_ZA_BASE 0
> +#define KVM_REG_ARM64_SME_ZT_BASE 0x600
> +
> +#define KVM_ARM64_SME_MAX_ZAHREG (__SVE_VQ_BYTES *
> KVM_ARM64_SME_VQ_MAX)
> +
> +#define KVM_REG_ARM64_SME_ZAHREG(n, i) \
> + (KVM_REG_ARM64 | KVM_REG_ARM64_SME | KVM_REG_ARM64_SME_ZA_BASE | \
> +KVM_REG_SIZE_U2048 | \
> +(((n) & (KVM_ARM64_SME_MAX_ZAHREG - 1)) << 5) |\
> +((i) & (KVM_ARM64_SVE_MAX_SLICES - 1)))
> +
> +#define KVM_REG_ARM64_SME_ZTREG_SIZE (512 / 8)
> +
> /* Vector lengths pseudo-register: */
> #define KVM_REG_ARM64_SME_VLS (KVM_REG_ARM64 | KVM_REG_ARM64_SME | \
> KVM_REG_SIZE_U512 | 0xfffe)
> diff --git a/arch/arm64/kvm/guest.c b/arch/arm64/kvm/guest.c
> index 90dcacb35f01..d4e30eb57a9c 100644
> --- a/arch/arm64/kvm/guest.c
> +++ b/arch/arm64/kvm/guest.c
> @@ -594,23 +594,133 @@ static int set_sme_vls(struct kvm_vcpu *vcpu, const
> struct kvm_one_reg *reg)
> return set_vec_vls(ARM64_VEC_SME, vcpu, reg);
> }
>
> +/*
> + * Validate SVE register ID and get sanitised bounds for user/kernel SVE
> + * register copy
> + */
> +static int sme_reg_to_region(struct vec_state_reg_region *region,
> +struct kvm_vcpu *vcpu,
> +const struct kvm_one_reg *reg)
> +{
> + /* reg ID ranges for ZA.H[n] registers */
> + unsigned int vq = vcpu_sme_max_vq(vcpu) - 1;
> + const u64 za_h_max = vq * __SVE_VQ_BYTES;
> + const u64 zah_id_min = KVM_REG_ARM64_SME_ZAHREG(0, 0);
> + const u64 zah_id_max = KVM_REG_ARM64_SME_ZAHREG(za_h_max - 1,
> + SVE_NUM_SLICES - 1);
> + unsigned int reg_num;
> +
> + unsigned int reqoffset, reqlen; /* User-requested offset and length */
> + unsigned int maxlen; /* Maximum permitted length */
> +
> + size_t sme_state_size;
> +
> + reg_num = (reg->id & SVE_REG_ID_MASK) >> SVE_REG_ID_SHIFT;
You use array_index_nospec() below for koffset, but it might be worth
using it for intermediate values, such as this one.
> +
> + if (reg->id >= zah_id_min && reg->id <= zah_id_max) {
> + if (!vcpu_has_sme(vcpu) || (reg->id & SVE_REG_SLICE_MASK) > 0)
> + return -ENOENT;
> +
> + /* ZA is exposed as SVE vectors ZA.H[n] */
> + reqoffset = ZA_SIG_ZAV_OFFSET(vq, reg_num) -
> + ZA_SIG_REGS_OFFSET;
> + reqlen = KVM_SVE_ZREG_SIZE;
> + maxlen = SVE_SIG_ZREG_SIZE(vq);
> + } else if (reg->id == KVM_REG_ARM64_SME_ZT_BASE) {
> + /* ZA is exposed as SVE vectors ZA.H[n] */
> + if (!kvm_has