lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <CA+EHjTwMy1crsZLqfy8_Y56NFPJZ7vGyN-egc433GhxB_n=7aA@mail.gmail.com>
Date: Tue, 13 Jan 2026 14:06:53 +0000
From: Fuad Tabba <tabba@...gle.com>
To: Mark Brown <broonie@...nel.org>
Cc: Marc Zyngier <maz@...nel.org>, Joey Gouly <joey.gouly@....com>, 
	Catalin Marinas <catalin.marinas@....com>, Suzuki K Poulose <suzuki.poulose@....com>, 
	Will Deacon <will@...nel.org>, Paolo Bonzini <pbonzini@...hat.com>, Jonathan Corbet <corbet@....net>, 
	Shuah Khan <shuah@...nel.org>, Oliver Upton <oupton@...nel.org>, Dave Martin <Dave.Martin@....com>, 
	Mark Rutland <mark.rutland@....com>, Ben Horgan <ben.horgan@....com>, 
	linux-arm-kernel@...ts.infradead.org, kvmarm@...ts.linux.dev, 
	linux-kernel@...r.kernel.org, kvm@...r.kernel.org, linux-doc@...r.kernel.org, 
	linux-kselftest@...r.kernel.org, Peter Maydell <peter.maydell@...aro.org>, 
	Eric Auger <eric.auger@...hat.com>
Subject: Re: [PATCH v9 22/30] KVM: arm64: Expose SME specific state to userspace

On Tue, 23 Dec 2025 at 01:23, Mark Brown <broonie@...nel.org> 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 <broonie@...nel.org>
> ---
>  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_feat(vcpu->kvm, ID_AA64PFR1_EL1, SME, SME2) ||
> +                   (reg->id & SVE_REG_SLICE_MASK) > 0 ||
> +                   reg_num > 0)
> +                       return -ENOENT;
> +
> +               /* ZT0 is stored after ZA */
> +               reqlen = KVM_REG_ARM64_SME_ZTREG_SIZE;
> +               maxlen = KVM_REG_ARM64_SME_ZTREG_SIZE;
> +       } else {
> +               return -EINVAL;
> +       }
> +
> +       sme_state_size = vcpu_sme_state_size(vcpu);

Is it worth caching this value and storing it in arch, since the state
size doesn't change after finalization?

> +       if (WARN_ON(!sme_state_size))
> +               return -EINVAL;
> +
> +       region->koffset = array_index_nospec(reqoffset, sme_state_size);
> +       region->klen = min(maxlen, reqlen);
> +       region->upad = reqlen - region->klen;
> +
> +       return 0;
> +}
> +
> +/*
> + * ZA is exposed as an array of horizontal vectors with the same
> + * format as SVE, mirroring the architecture's LDR ZA[Wv, offs], [Xn]
> + * instruction.
> + */
> +
>  static int get_sme_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
>  {
> +       int ret;
> +       struct vec_state_reg_region region;
> +       char __user *uptr = (char __user *)reg->addr;
> +
>         /* 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;
> +       /* Try to interpret reg ID as an architectural SME register... */
> +       ret = sme_reg_to_region(&region, vcpu, reg);
> +       if (ret)
> +               return ret;
> +
> +       if (!kvm_arm_vcpu_vec_finalized(vcpu))
> +               return -EPERM;
> +
> +       /*
> +        * None of the SME specific registers are accessible unless
> +        * PSTATE.ZA is set.
> +        */
> +       if (!vcpu_za_enabled(vcpu))
> +               return -EINVAL;

I think this should be something other than -EINVAL, since the issue
isn't that the register isn't valid, but that it's inaccessible, as
the comment says. -EACCESS or something?

> +
> +       if (copy_from_user(vcpu->arch.sme_state + region.koffset, uptr,
> +                          region.klen))
> +               return -EFAULT;

This should be copy_to_user()

> +
> +       return 0;
>  }
>
>  static int set_sme_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
>  {
> +       int ret;
> +       struct vec_state_reg_region region;
> +       char __user *uptr = (char __user *)reg->addr;
> +
>         /* 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;
> +       /* Try to interpret reg ID as an architectural SME register... */
> +       ret = sme_reg_to_region(&region, vcpu, reg);
> +       if (ret)
> +               return ret;
> +
> +       if (!kvm_arm_vcpu_vec_finalized(vcpu))
> +               return -EPERM;
> +
> +       /*
> +        * None of the SME specific registers are accessible unless
> +        * PSTATE.ZA is set.
> +        */
> +       if (!vcpu_za_enabled(vcpu))
> +               return -EINVAL;

Same as get_sme_reg().

Cheers,
/fuad



> +
> +       if (copy_from_user(vcpu->arch.sme_state + region.koffset, uptr,
> +                          region.klen))
> +               return -EFAULT;
> +
> +       return 0;
>  }
> +
>  int kvm_arch_vcpu_ioctl_get_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs)
>  {
>         return -EINVAL;
>
> --
> 2.47.3
>

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ