[PATCH 2/6] arm64/mm: Override PARange for !LPA2 and use it consistently

Anshuman Khandual anshuman.khandual at arm.com
Thu Nov 14 22:05:00 PST 2024


On 11/11/24 14:05, Ard Biesheuvel wrote:
> From: Ard Biesheuvel <ardb at kernel.org>
> 
> When FEAT_LPA{,2} are not implemented, the ID_AA64MMFR0_EL1.PARange and
> TCR.IPS values corresponding with 52-bit physical addressing are
> reserved.
> 
> Setting the TCR.IPS field to 0b110 (52-bit physical addressing) has side
> effects, such as how the TTBRn_ELx.BADDR fields are interpreted, and so
> it is important that disabling FEAT_LPA2 (by overriding the
> ID_AA64MMFR0.TGran fields) also presents a PARange field consistent with
> that.
> 
> So limit the field to 48 bits unless LPA2 is enabled, and update
> existing references to use the override consistently.
> 
> Fixes: 352b0395b505 ("arm64: Enable 52-bit virtual addressing for 4k and 16k granule configs")
> Cc: <stable at vger.kernel.org>
> Signed-off-by: Ard Biesheuvel <ardb at kernel.org>
> ---
>  arch/arm64/include/asm/assembler.h    | 5 +++++
>  arch/arm64/kernel/cpufeature.c        | 2 +-
>  arch/arm64/kernel/pi/idreg-override.c | 9 +++++++++
>  arch/arm64/kernel/pi/map_kernel.c     | 6 ++++++
>  arch/arm64/mm/init.c                  | 2 +-
>  5 files changed, 22 insertions(+), 2 deletions(-)
> 
> diff --git a/arch/arm64/include/asm/assembler.h b/arch/arm64/include/asm/assembler.h
> index 3d8d534a7a77..ad63457a05c5 100644
> --- a/arch/arm64/include/asm/assembler.h
> +++ b/arch/arm64/include/asm/assembler.h
> @@ -343,6 +343,11 @@ alternative_cb_end
>  	// Narrow PARange to fit the PS field in TCR_ELx
>  	ubfx	\tmp0, \tmp0, #ID_AA64MMFR0_EL1_PARANGE_SHIFT, #3
>  	mov	\tmp1, #ID_AA64MMFR0_EL1_PARANGE_MAX
> +#ifdef CONFIG_ARM64_LPA2
> +alternative_if_not ARM64_HAS_VA52
> +	mov	\tmp1, #ID_AA64MMFR0_EL1_PARANGE_48
> +alternative_else_nop_endif
> +#endif

I guess this will only take effect after cpu features have been finalized
but will not be applicable for __cpu_setup() during primary and secondary
cpu bring up during boot.

>  	cmp	\tmp0, \tmp1
>  	csel	\tmp0, \tmp1, \tmp0, hi
>  	bfi	\tcr, \tmp0, \pos, #3
> diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
> index 37e4c02e0272..6f5137040ff6 100644
> --- a/arch/arm64/kernel/cpufeature.c
> +++ b/arch/arm64/kernel/cpufeature.c
> @@ -3399,7 +3399,7 @@ static void verify_hyp_capabilities(void)
>  		return;
>  
>  	safe_mmfr1 = read_sanitised_ftr_reg(SYS_ID_AA64MMFR1_EL1);
> -	mmfr0 = read_cpuid(ID_AA64MMFR0_EL1);
> +	mmfr0 = read_sanitised_ftr_reg(SYS_ID_AA64MMFR0_EL1);

Small nit, should be renamed as safe_mmfr0 to be consistent with safe_mmfr1 ?

>  	mmfr1 = read_cpuid(ID_AA64MMFR1_EL1);
>  
>  	/* Verify VMID bits */
> diff --git a/arch/arm64/kernel/pi/idreg-override.c b/arch/arm64/kernel/pi/idreg-override.c
> index 22159251eb3a..c6b185b885f7 100644
> --- a/arch/arm64/kernel/pi/idreg-override.c
> +++ b/arch/arm64/kernel/pi/idreg-override.c
> @@ -83,6 +83,15 @@ static bool __init mmfr2_varange_filter(u64 val)
>  		id_aa64mmfr0_override.val |=
>  			(ID_AA64MMFR0_EL1_TGRAN_LPA2 - 1) << ID_AA64MMFR0_EL1_TGRAN_SHIFT;
>  		id_aa64mmfr0_override.mask |= 0xfU << ID_AA64MMFR0_EL1_TGRAN_SHIFT;
> +
> +		/*
> +		 * Override PARange to 48 bits - the override will just be
> +		 * ignored if the actual PARange is smaller, but this is
> +		 * unlikely to be the case for LPA2 capable silicon.
> +		 */
> +		id_aa64mmfr0_override.val |=
> +			ID_AA64MMFR0_EL1_PARANGE_48 << ID_AA64MMFR0_EL1_PARANGE_SHIFT;
> +		id_aa64mmfr0_override.mask |= 0xfU << ID_AA64MMFR0_EL1_PARANGE_SHIFT;
Could these be used instead ? 

SYS_FIELD_PREP_ENUM(ID_AA64MMFR0_EL1, PARANGE, 48)
ID_AA64MMFR0_EL1_PARANGE_MASK ?


>  	}
>  #endif
>  	return true;
> diff --git a/arch/arm64/kernel/pi/map_kernel.c b/arch/arm64/kernel/pi/map_kernel.c
> index f374a3e5a5fe..e57b043f324b 100644
> --- a/arch/arm64/kernel/pi/map_kernel.c
> +++ b/arch/arm64/kernel/pi/map_kernel.c
> @@ -136,6 +136,12 @@ static void noinline __section(".idmap.text") set_ttbr0_for_lpa2(u64 ttbr)
>  {
>  	u64 sctlr = read_sysreg(sctlr_el1);
>  	u64 tcr = read_sysreg(tcr_el1) | TCR_DS;
> +	u64 mmfr0 = read_sysreg(id_aa64mmfr0_el1);
> +	u64 parange = cpuid_feature_extract_unsigned_field(mmfr0,
> +							   ID_AA64MMFR0_EL1_PARANGE_SHIFT);
> +
> +	tcr &= ~TCR_IPS_MASK;

Could there be a different IPS value in TCR ? OR is this just a normal
clean up instead.

> +	tcr |= parange << TCR_IPS_SHIFT;

Wondering if FIELD_PREP() could be used here.

>  
>  	asm("	msr	sctlr_el1, %0		;"
>  	    "	isb				;"
> diff --git a/arch/arm64/mm/init.c b/arch/arm64/mm/init.c
> index d21f67d67cf5..4db9887b2aef 100644
> --- a/arch/arm64/mm/init.c
> +++ b/arch/arm64/mm/init.c
> @@ -280,7 +280,7 @@ void __init arm64_memblock_init(void)
>  
>  	if (IS_ENABLED(CONFIG_RANDOMIZE_BASE)) {
>  		extern u16 memstart_offset_seed;
> -		u64 mmfr0 = read_cpuid(ID_AA64MMFR0_EL1);
> +		u64 mmfr0 = read_sanitised_ftr_reg(SYS_ID_AA64MMFR0_EL1);

Could this have a comment explaining the need for sanitized value ?

>  		int parange = cpuid_feature_extract_unsigned_field(
>  					mmfr0, ID_AA64MMFR0_EL1_PARANGE_SHIFT);
>  		s64 range = linear_region_size -

Otherwise LGTM.



More information about the linux-arm-kernel mailing list