Re: [PATCH] arm64/fpsimd: Only provide the length to cpufeature for xCR registers

From: Will Deacon
Date: Wed Aug 02 2023 - 07:21:37 EST


On Thu, Jul 27, 2023 at 10:31:44PM +0100, Mark Brown wrote:
> For both SVE and SME we abuse the generic register field comparison
> support in the cpufeature code as part of our detection of unsupported
> variations in the vector lengths available to PEs, reporting the maximum
> vector lengths via ZCR_EL1.LEN and SMCR_EL1.LEN. Since these are
> configuration registers rather than identification registers the
> assumptions the cpufeature code makes about how unknown bitfields behave
> are invalid, leading to warnings when SME features like FA64 are enabled
> and we hotplug a CPU:
>
> CPU features: SANITY CHECK: Unexpected variation in SYS_SMCR_EL1. Boot CPU: 0x0000000000000f, CPU3: 0x0000008000000f
> CPU features: Unsupported CPU feature variation detected.
>
> SVE has no controls other than the vector length so is not yet impacted
> but the same issue will apply there if any are defined.
>
> Since the only field we are interested in having the cpufeature code
> handle is the length field and we use a custom read function to obtain
> the value we can avoid these warnings by filtering out all other bits
> when we return the register value.
>
> Fixes: 2e0f2478ea37eb ("arm64/sve: Probe SVE capabilities and usable vector lengths")
> FixeS: b42990d3bf77cc ("arm64/sme: Identify supported SME vector lengths at boot")
> Signed-off-by: Mark Brown <broonie@xxxxxxxxxx>
> ---
> arch/arm64/kernel/fpsimd.c | 6 +++---
> 1 file changed, 3 insertions(+), 3 deletions(-)
>
> diff --git a/arch/arm64/kernel/fpsimd.c b/arch/arm64/kernel/fpsimd.c
> index 89d54a5242d1..c7fdeebd050c 100644
> --- a/arch/arm64/kernel/fpsimd.c
> +++ b/arch/arm64/kernel/fpsimd.c
> @@ -1189,11 +1189,11 @@ u64 read_zcr_features(void)
> write_sysreg_s(ZCR_ELx_LEN_MASK, SYS_ZCR_EL1);
>
> zcr = read_sysreg_s(SYS_ZCR_EL1);
> - zcr &= ~(u64)ZCR_ELx_LEN_MASK; /* find sticky 1s outside LEN field */
> + zcr &= ~(u64)ZCR_ELx_LEN_MASK;
> vq_max = sve_vq_from_vl(sve_get_vl());
> zcr |= vq_max - 1; /* set LEN field to maximum effective value */
>
> - return zcr;
> + return SYS_FIELD_GET(ZCR_ELx, LEN, zcr);

Hmm, now this function looks like a mixture of code which relies on the
LEN field living at the bottom of the register and code which is agnostic
to that.

Can we update the 'zcr |= vq_max - 1' part to use something like
FIELD_PREP() instead?

> }
>
> void __init sve_setup(void)
> @@ -1364,7 +1364,7 @@ u64 read_smcr_features(void)
> vq_max = sve_vq_from_vl(sme_get_vl());
> smcr |= vq_max - 1; /* set LEN field to maximum effective value */
>
> - return smcr;
> + return SYS_FIELD_GET(SMCR_ELx, LEN, smcr);

It looks like there's a similar thing here.

Will