Skip to content

Commit dd006da

Browse files
Ard Biesheuvelwildea01
Ard Biesheuvel
authored andcommitted
arm64: mm: increase VA range of identity map
The page size and the number of translation levels, and hence the supported virtual address range, are build-time configurables on arm64 whose optimal values are use case dependent. However, in the current implementation, if the system's RAM is located at a very high offset, the virtual address range needs to reflect that merely because the identity mapping, which is only used to enable or disable the MMU, requires the extended virtual range to map the physical memory at an equal virtual offset. This patch relaxes that requirement, by increasing the number of translation levels for the identity mapping only, and only when actually needed, i.e., when system RAM's offset is found to be out of reach at runtime. Tested-by: Laura Abbott <lauraa@codeaurora.org> Reviewed-by: Catalin Marinas <catalin.marinas@arm.com> Tested-by: Marc Zyngier <marc.zyngier@arm.com> Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org> Signed-off-by: Will Deacon <will.deacon@arm.com>
1 parent 6232cfd commit dd006da

File tree

8 files changed

+110
-4
lines changed

8 files changed

+110
-4
lines changed

arch/arm64/include/asm/mmu_context.h

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,49 @@ static inline void cpu_set_reserved_ttbr0(void)
6464
: "r" (ttbr));
6565
}
6666

67+
/*
68+
* TCR.T0SZ value to use when the ID map is active. Usually equals
69+
* TCR_T0SZ(VA_BITS), unless system RAM is positioned very high in
70+
* physical memory, in which case it will be smaller.
71+
*/
72+
extern u64 idmap_t0sz;
73+
74+
static inline bool __cpu_uses_extended_idmap(void)
75+
{
76+
return (!IS_ENABLED(CONFIG_ARM64_VA_BITS_48) &&
77+
unlikely(idmap_t0sz != TCR_T0SZ(VA_BITS)));
78+
}
79+
80+
static inline void __cpu_set_tcr_t0sz(u64 t0sz)
81+
{
82+
unsigned long tcr;
83+
84+
if (__cpu_uses_extended_idmap())
85+
asm volatile (
86+
" mrs %0, tcr_el1 ;"
87+
" bfi %0, %1, %2, %3 ;"
88+
" msr tcr_el1, %0 ;"
89+
" isb"
90+
: "=&r" (tcr)
91+
: "r"(t0sz), "I"(TCR_T0SZ_OFFSET), "I"(TCR_TxSZ_WIDTH));
92+
}
93+
94+
/*
95+
* Set TCR.T0SZ to the value appropriate for activating the identity map.
96+
*/
97+
static inline void cpu_set_idmap_tcr_t0sz(void)
98+
{
99+
__cpu_set_tcr_t0sz(idmap_t0sz);
100+
}
101+
102+
/*
103+
* Set TCR.T0SZ to its default value (based on VA_BITS)
104+
*/
105+
static inline void cpu_set_default_tcr_t0sz(void)
106+
{
107+
__cpu_set_tcr_t0sz(TCR_T0SZ(VA_BITS));
108+
}
109+
67110
static inline void switch_new_context(struct mm_struct *mm)
68111
{
69112
unsigned long flags;

arch/arm64/include/asm/page.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,9 @@
3333
* image. Both require pgd, pud (4 levels only) and pmd tables to (section)
3434
* map the kernel. With the 64K page configuration, swapper and idmap need to
3535
* map to pte level. The swapper also maps the FDT (see __create_page_tables
36-
* for more information).
36+
* for more information). Note that the number of ID map translation levels
37+
* could be increased on the fly if system RAM is out of reach for the default
38+
* VA range, so 3 pages are reserved in all cases.
3739
*/
3840
#ifdef CONFIG_ARM64_64K_PAGES
3941
#define SWAPPER_PGTABLE_LEVELS (CONFIG_ARM64_PGTABLE_LEVELS)
@@ -42,7 +44,7 @@
4244
#endif
4345

4446
#define SWAPPER_DIR_SIZE (SWAPPER_PGTABLE_LEVELS * PAGE_SIZE)
45-
#define IDMAP_DIR_SIZE (SWAPPER_DIR_SIZE)
47+
#define IDMAP_DIR_SIZE (3 * PAGE_SIZE)
4648

4749
#ifndef __ASSEMBLY__
4850

arch/arm64/include/asm/pgtable-hwdef.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,12 @@
143143
/*
144144
* TCR flags.
145145
*/
146-
#define TCR_TxSZ(x) (((UL(64) - (x)) << 16) | ((UL(64) - (x)) << 0))
146+
#define TCR_T0SZ_OFFSET 0
147+
#define TCR_T1SZ_OFFSET 16
148+
#define TCR_T0SZ(x) ((UL(64) - (x)) << TCR_T0SZ_OFFSET)
149+
#define TCR_T1SZ(x) ((UL(64) - (x)) << TCR_T1SZ_OFFSET)
150+
#define TCR_TxSZ(x) (TCR_T0SZ(x) | TCR_T1SZ(x))
151+
#define TCR_TxSZ_WIDTH 6
147152
#define TCR_IRGN_NC ((UL(0) << 8) | (UL(0) << 24))
148153
#define TCR_IRGN_WBWA ((UL(1) << 8) | (UL(1) << 24))
149154
#define TCR_IRGN_WT ((UL(2) << 8) | (UL(2) << 24))

arch/arm64/kernel/head.S

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,43 @@ __create_page_tables:
383383
*/
384384
mov x0, x25 // idmap_pg_dir
385385
adrp x3, KERNEL_START // __pa(KERNEL_START)
386+
387+
#ifndef CONFIG_ARM64_VA_BITS_48
388+
#define EXTRA_SHIFT (PGDIR_SHIFT + PAGE_SHIFT - 3)
389+
#define EXTRA_PTRS (1 << (48 - EXTRA_SHIFT))
390+
391+
/*
392+
* If VA_BITS < 48, it may be too small to allow for an ID mapping to be
393+
* created that covers system RAM if that is located sufficiently high
394+
* in the physical address space. So for the ID map, use an extended
395+
* virtual range in that case, by configuring an additional translation
396+
* level.
397+
* First, we have to verify our assumption that the current value of
398+
* VA_BITS was chosen such that all translation levels are fully
399+
* utilised, and that lowering T0SZ will always result in an additional
400+
* translation level to be configured.
401+
*/
402+
#if VA_BITS != EXTRA_SHIFT
403+
#error "Mismatch between VA_BITS and page size/number of translation levels"
404+
#endif
405+
406+
/*
407+
* Calculate the maximum allowed value for TCR_EL1.T0SZ so that the
408+
* entire kernel image can be ID mapped. As T0SZ == (64 - #bits used),
409+
* this number conveniently equals the number of leading zeroes in
410+
* the physical address of KERNEL_END.
411+
*/
412+
adrp x5, KERNEL_END
413+
clz x5, x5
414+
cmp x5, TCR_T0SZ(VA_BITS) // default T0SZ small enough?
415+
b.ge 1f // .. then skip additional level
416+
417+
str_l x5, idmap_t0sz, x6
418+
419+
create_table_entry x0, x3, EXTRA_SHIFT, EXTRA_PTRS, x5, x6
420+
1:
421+
#endif
422+
386423
create_pgd_entry x0, x3, x5, x6
387424
mov x5, x3 // __pa(KERNEL_START)
388425
adr_l x6, KERNEL_END // __pa(KERNEL_END)

arch/arm64/kernel/smp.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ asmlinkage void secondary_start_kernel(void)
151151
*/
152152
cpu_set_reserved_ttbr0();
153153
flush_tlb_all();
154+
cpu_set_default_tcr_t0sz();
154155

155156
preempt_disable();
156157
trace_hardirqs_off();

arch/arm64/mm/mmu.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@
4040

4141
#include "mm.h"
4242

43+
u64 idmap_t0sz = TCR_T0SZ(VA_BITS);
44+
4345
/*
4446
* Empty_zero_page is a special page that is used for zero-initialized data
4547
* and COW.
@@ -454,15 +456,18 @@ void __init paging_init(void)
454456
*/
455457
cpu_set_reserved_ttbr0();
456458
flush_tlb_all();
459+
cpu_set_default_tcr_t0sz();
457460
}
458461

459462
/*
460463
* Enable the identity mapping to allow the MMU disabling.
461464
*/
462465
void setup_mm_for_reboot(void)
463466
{
464-
cpu_switch_mm(idmap_pg_dir, &init_mm);
467+
cpu_set_reserved_ttbr0();
465468
flush_tlb_all();
469+
cpu_set_idmap_tcr_t0sz();
470+
cpu_switch_mm(idmap_pg_dir, &init_mm);
466471
}
467472

468473
/*

arch/arm64/mm/proc-macros.S

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,3 +52,13 @@
5252
mov \reg, #4 // bytes per word
5353
lsl \reg, \reg, \tmp // actual cache line size
5454
.endm
55+
56+
/*
57+
* tcr_set_idmap_t0sz - update TCR.T0SZ so that we can load the ID map
58+
*/
59+
.macro tcr_set_idmap_t0sz, valreg, tmpreg
60+
#ifndef CONFIG_ARM64_VA_BITS_48
61+
ldr_l \tmpreg, idmap_t0sz
62+
bfi \valreg, \tmpreg, #TCR_T0SZ_OFFSET, #TCR_TxSZ_WIDTH
63+
#endif
64+
.endm

arch/arm64/mm/proc.S

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ ENTRY(cpu_do_resume)
156156
msr cpacr_el1, x6
157157
msr ttbr0_el1, x1
158158
msr ttbr1_el1, x7
159+
tcr_set_idmap_t0sz x8, x7
159160
msr tcr_el1, x8
160161
msr vbar_el1, x9
161162
msr mdscr_el1, x10
@@ -233,6 +234,8 @@ ENTRY(__cpu_setup)
233234
*/
234235
ldr x10, =TCR_TxSZ(VA_BITS) | TCR_CACHE_FLAGS | TCR_SMP_FLAGS | \
235236
TCR_TG_FLAGS | TCR_ASID16 | TCR_TBI0
237+
tcr_set_idmap_t0sz x10, x9
238+
236239
/*
237240
* Read the PARange bits from ID_AA64MMFR0_EL1 and set the IPS bits in
238241
* TCR_EL1.

0 commit comments

Comments
 (0)