On Tue, 23 Dec 2025 at 01:23, Mark Brown <[email protected]> wrote:
>
> SME introduces a mode called streaming mode where the Z, P and optionally
> FFR registers can be accessed using the SVE instructions but with the SME
> vector length. Reflect this in the ABI for accessing the guest registers by
> making the vector length for the vcpu reflect the vector length that would
> be seen by the guest were it running, using the SME vector length when the
> guest is configured for streaming mode.
>
> Since SME may be present without SVE we also update the existing checks for
> access to the Z, P and V registers to check for either SVE or streaming
> mode. When not in streaming mode the guest floating point state may be
> accessed via the V registers.
>
> Any VMM that supports SME must be aware of the need to configure streaming
> mode prior to writing the floating point registers that this creates.
>
> Signed-off-by: Mark Brown <[email protected]>
> ---
>  arch/arm64/kvm/guest.c | 38 ++++++++++++++++++++++++++++++++++----
>  1 file changed, 34 insertions(+), 4 deletions(-)
>
> diff --git a/arch/arm64/kvm/guest.c b/arch/arm64/kvm/guest.c
> index 2a1fdcb0ec49..90dcacb35f01 100644
> --- a/arch/arm64/kvm/guest.c
> +++ b/arch/arm64/kvm/guest.c
> @@ -73,6 +73,11 @@ static u64 core_reg_offset_from_id(u64 id)
>         return id & ~(KVM_REG_ARCH_MASK | KVM_REG_SIZE_MASK | 
> KVM_REG_ARM_CORE);
>  }
>
> +static bool vcpu_has_sve_regs(const struct kvm_vcpu *vcpu)
> +{
> +       return vcpu_has_sve(vcpu) || vcpu_in_streaming_mode(vcpu);
> +}
> +
>  static int core_reg_size_from_offset(const struct kvm_vcpu *vcpu, u64 off)
>  {
>         int size;
> @@ -110,9 +115,10 @@ static int core_reg_size_from_offset(const struct 
> kvm_vcpu *vcpu, u64 off)
>         /*
>          * The KVM_REG_ARM64_SVE regs must be used instead of
>          * KVM_REG_ARM_CORE for accessing the FPSIMD V-registers on
> -        * SVE-enabled vcpus:
> +        * SVE-enabled vcpus or when a SME enabled vcpu is in
> +        * streaming mode:
>          */
> -       if (vcpu_has_sve(vcpu) && core_reg_offset_is_vreg(off))
> +       if (vcpu_has_sve_regs(vcpu) && core_reg_offset_is_vreg(off))
>                 return -EINVAL;
>
>         return size;
> @@ -426,6 +432,24 @@ struct vec_state_reg_region {
>         unsigned int upad;      /* extra trailing padding in user memory */
>  };
>
> +/*
> + * We represent the Z and P registers to userspace using either the
> + * SVE or SME vector length, depending on which features the guest has
> + * and if the guest is in streaming mode.
> + */
> +static unsigned int vcpu_sve_cur_vq(struct kvm_vcpu *vcpu)
> +{
> +       unsigned int vq = 0;
> +
> +       if (vcpu_has_sve(vcpu))
> +               vq = vcpu_sve_max_vq(vcpu);
> +
> +       if (vcpu_in_streaming_mode(vcpu))
> +               vq = vcpu_sme_max_vq(vcpu);
> +
> +       return vq;
> +}
> +
>  /*
>   * Validate SVE register ID and get sanitised bounds for user/kernel SVE
>   * register copy
> @@ -466,7 +490,7 @@ static int sve_reg_to_region(struct vec_state_reg_region 
> *region,
>                 if (!vcpu_has_sve(vcpu) || (reg->id & SVE_REG_SLICE_MASK) > 0)

Should this be vcpu_has_sve_regs()? (zreg range)

>                         return -ENOENT;
>
> -               vq = vcpu_sve_max_vq(vcpu);
> +               vq = vcpu_sve_cur_vq(vcpu);
>
>                 reqoffset = SVE_SIG_ZREG_OFFSET(vq, reg_num) -
>                                 SVE_SIG_REGS_OFFSET;
> @@ -476,7 +500,7 @@ static int sve_reg_to_region(struct vec_state_reg_region 
> *region,
>                 if (!vcpu_has_sve(vcpu) || (reg->id & SVE_REG_SLICE_MASK) > 0)

And should this also be vcpu_has_sve_regs()? (preg range)

Should you also handle FFR here, considering that in streaming mode
it's optional?

Cheers,
/fuad



>                         return -ENOENT;
>
> -               vq = vcpu_sve_max_vq(vcpu);
> +               vq = vcpu_sve_cur_vq(vcpu);
>
>                 reqoffset = SVE_SIG_PREG_OFFSET(vq, reg_num) -
>                                 SVE_SIG_REGS_OFFSET;
> @@ -515,6 +539,9 @@ static int get_sve_reg(struct kvm_vcpu *vcpu, const 
> struct kvm_one_reg *reg)
>         if (!kvm_arm_vcpu_vec_finalized(vcpu))
>                 return -EPERM;
>
> +       if (!vcpu_has_sve_regs(vcpu))
> +               return -EBUSY;
> +
>         if (copy_to_user(uptr, vcpu->arch.sve_state + region.koffset,
>                          region.klen) ||
>             clear_user(uptr + region.klen, region.upad))
> @@ -541,6 +568,9 @@ static int set_sve_reg(struct kvm_vcpu *vcpu, const 
> struct kvm_one_reg *reg)
>         if (!kvm_arm_vcpu_vec_finalized(vcpu))
>                 return -EPERM;
>
> +       if (!vcpu_has_sve_regs(vcpu))
> +               return -EBUSY;
> +
>         if (copy_from_user(vcpu->arch.sve_state + region.koffset, uptr,
>                            region.klen))
>                 return -EFAULT;
>
> --
> 2.47.3
>

Reply via email to