[PATCH] armv8: always use current exception level for TCR_ELx access
Mark Kettenis
mark.kettenis at xs4all.nl
Tue Jun 7 19:45:14 CEST 2022
> From: Andre Przywara <andre.przywara at arm.com>
> Date: Tue, 7 Jun 2022 15:08:34 +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 caller 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>
Review-by: Mark Kettenis <kettenis at openbsd.org>
Tested-by: Mark Kettenis <kettenis at openbsd.org>
One comment below though...
> ---
> arch/arm/cpu/armv8/cache_v8.c | 28 +++++++++++++++++++++----
> arch/arm/cpu/armv8/fsl-layerscape/cpu.c | 4 ++--
> arch/arm/include/asm/armv8/mmu.h | 2 +-
> 3 files changed, 27 insertions(+), 7 deletions(-)
>
> diff --git a/arch/arm/cpu/armv8/cache_v8.c b/arch/arm/cpu/armv8/cache_v8.c
> index 3de18c7675..101c521e61 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(34))
Ideally that would be a #define. But I can see why you ended up doing
this. The most logical place for the #define would be in
arch/arm/include/asm/system.h, but that header is used in assembly
code and the bitshifting stuff in there is problematic as we'd need
something like:
#define HCR_EL2_E2H (1UL << 34)
but that isn't legal in assembly code.
> + 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 253008a9c1..c989a43cbe 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/include/asm/armv8/mmu.h b/arch/arm/include/asm/armv8/mmu.h
> index c36b2cf5a5..98ce521ec5 100644
> --- a/arch/arm/include/asm/armv8/mmu.h
> +++ b/arch/arm/include/asm/armv8/mmu.h
> @@ -134,7 +134,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.25.1
>
>
More information about the U-Boot
mailing list