[PATCH v2] armv8: always use current exception level for TCR_ELx access

Mark Kettenis mark.kettenis at xs4all.nl
Tue Jun 14 22:10:40 CEST 2022


> From: Andre Przywara <andre.przywara at arm.com>
> Date: Tue, 14 Jun 2022 00:11:10 +0100
> 
> Currently get_tcr() takes an "el" parameter, to select the proper
> version of the TCR_ELx system register.
> This is problematic in case of the Apple M1, since it runs with
> HCR_EL2.E2H fixed to 1, so TCR_EL2 is actually using the TCR_EL1 layout,
> and we get the wrong version.
> 
> For U-Boot's purposes the only sensible choice here is the current
> exception level, and indeed most callers treat it like that, so let's
> remove that parameter and read the current EL inside the function.
> This allows us to check for the E2H bit, and pretend it's EL1 in this
> case.
> 
> There are two callers which don't care about the EL, and they pass 0,
> which looks wrong, but is irrelevant in these two cases, since we don't
> use the return value there. So the change cannot affect those two.
> 
> Signed-off-by: Andre Przywara <andre.przywara at arm.com>

Reviewed-by: Mark Kettenis <kettenis at openbsd.org>
Tested-by: Mark Kettenis <kettenis at openbsd.org>

> ---
> Changelog v1 ... v2:
> - Give bit 34 a name, and also use that in start.S
> 
> Mark, can you please test this? The A53s I have here for a quick test
> are igorant of this patch ...
> 
>  arch/arm/cpu/armv8/cache_v8.c           | 28 +++++++++++++++++++++----
>  arch/arm/cpu/armv8/fsl-layerscape/cpu.c |  4 ++--
>  arch/arm/cpu/armv8/start.S              |  2 +-
>  arch/arm/include/asm/armv8/mmu.h        |  4 +++-
>  4 files changed, 30 insertions(+), 8 deletions(-)
> 
> diff --git a/arch/arm/cpu/armv8/cache_v8.c b/arch/arm/cpu/armv8/cache_v8.c
> index 3de18c7675b..e4736e56436 100644
> --- a/arch/arm/cpu/armv8/cache_v8.c
> +++ b/arch/arm/cpu/armv8/cache_v8.c
> @@ -39,8 +39,28 @@ DECLARE_GLOBAL_DATA_PTR;
>   *    off:          FFF
>   */
>  
> -u64 get_tcr(int el, u64 *pips, u64 *pva_bits)
> +static int get_effective_el(void)
>  {
> +	int el = current_el();
> +
> +	if (el == 2) {
> +		u64 hcr_el2;
> +
> +		/*
> +		 * If we are using the EL2&0 translation regime, the TCR_EL2
> +		 * looks like the EL1 version, even though we are in EL2.
> +		 */
> +		__asm__ ("mrs %0, HCR_EL2\n" : "=r" (hcr_el2));
> +		if (hcr_el2 & BIT(HCR_EL2_E2H_BIT))
> +			return 1;
> +	}
> +
> +	return el;
> +}
> +
> +u64 get_tcr(u64 *pips, u64 *pva_bits)
> +{
> +	int el = get_effective_el();
>  	u64 max_addr = 0;
>  	u64 ips, va_bits;
>  	u64 tcr;
> @@ -115,7 +135,7 @@ static u64 *find_pte(u64 addr, int level)
>  
>  	debug("addr=%llx level=%d\n", addr, level);
>  
> -	get_tcr(0, NULL, &va_bits);
> +	get_tcr(NULL, &va_bits);
>  	if (va_bits < 39)
>  		start_level = 1;
>  
> @@ -343,7 +363,7 @@ __weak u64 get_page_table_size(void)
>  	u64 va_bits;
>  	int start_level = 0;
>  
> -	get_tcr(0, NULL, &va_bits);
> +	get_tcr(NULL, &va_bits);
>  	if (va_bits < 39)
>  		start_level = 1;
>  
> @@ -415,7 +435,7 @@ __weak void mmu_setup(void)
>  		setup_all_pgtables();
>  
>  	el = current_el();
> -	set_ttbr_tcr_mair(el, gd->arch.tlb_addr, get_tcr(el, NULL, NULL),
> +	set_ttbr_tcr_mair(el, gd->arch.tlb_addr, get_tcr(NULL, NULL),
>  			  MEMORY_ATTRIBUTES);
>  
>  	/* enable the mmu */
> diff --git a/arch/arm/cpu/armv8/fsl-layerscape/cpu.c b/arch/arm/cpu/armv8/fsl-layerscape/cpu.c
> index 253008a9c13..c989a43cbeb 100644
> --- a/arch/arm/cpu/armv8/fsl-layerscape/cpu.c
> +++ b/arch/arm/cpu/armv8/fsl-layerscape/cpu.c
> @@ -454,7 +454,7 @@ static inline void early_mmu_setup(void)
>  
>  	/* point TTBR to the new table */
>  	set_ttbr_tcr_mair(el, gd->arch.tlb_addr,
> -			  get_tcr(el, NULL, NULL) &
> +			  get_tcr(NULL, NULL) &
>  			  ~(TCR_ORGN_MASK | TCR_IRGN_MASK),
>  			  MEMORY_ATTRIBUTES);
>  
> @@ -609,7 +609,7 @@ static inline void final_mmu_setup(void)
>  	invalidate_icache_all();
>  
>  	/* point TTBR to the new table */
> -	set_ttbr_tcr_mair(el, gd->arch.tlb_addr, get_tcr(el, NULL, NULL),
> +	set_ttbr_tcr_mair(el, gd->arch.tlb_addr, get_tcr(NULL, NULL),
>  			  MEMORY_ATTRIBUTES);
>  
>  	set_sctlr(get_sctlr() | CR_M);
> diff --git a/arch/arm/cpu/armv8/start.S b/arch/arm/cpu/armv8/start.S
> index d328e8c08a1..28f0df13f0d 100644
> --- a/arch/arm/cpu/armv8/start.S
> +++ b/arch/arm/cpu/armv8/start.S
> @@ -125,7 +125,7 @@ pie_fixup_done:
>  	msr	cptr_el3, xzr			/* Enable FP/SIMD */
>  	b	0f
>  2:	mrs	x1, hcr_el2
> -	tbnz	x1, #34, 1f			/* HCR_EL2.E2H */
> +	tbnz	x1, #HCR_EL2_E2H_BIT, 1f	/* HCR_EL2.E2H */
>  	orr	x1, x1, #HCR_EL2_AMO_EL2	/* Route SErrors to EL2 */
>  	msr	hcr_el2, x1
>  	set_vbar vbar_el2, x0
> diff --git a/arch/arm/include/asm/armv8/mmu.h b/arch/arm/include/asm/armv8/mmu.h
> index c36b2cf5a58..9f58cedb650 100644
> --- a/arch/arm/include/asm/armv8/mmu.h
> +++ b/arch/arm/include/asm/armv8/mmu.h
> @@ -103,6 +103,8 @@
>  #define TCR_EL2_RSVD		(1U << 31 | 1 << 23)
>  #define TCR_EL3_RSVD		(1U << 31 | 1 << 23)
>  
> +#define HCR_EL2_E2H_BIT		34
> +
>  #ifndef __ASSEMBLY__
>  static inline void set_ttbr_tcr_mair(int el, u64 table, u64 tcr, u64 attr)
>  {
> @@ -134,7 +136,7 @@ struct mm_region {
>  
>  extern struct mm_region *mem_map;
>  void setup_pgtables(void);
> -u64 get_tcr(int el, u64 *pips, u64 *pva_bits);
> +u64 get_tcr(u64 *pips, u64 *pva_bits);
>  #endif
>  
>  #endif /* _ASM_ARMV8_MMU_H_ */
> -- 
> 2.35.3
> 
> 


More information about the U-Boot mailing list