Map larger kernels at early init
v4 cover Map larger kernels at early init
The early pagetable creation code assumes that a single pgd, pud, pmd and pte are sufficient to map the kernel text for MMU bringup. For 16KB granules this is, unfortunately, rarely the case. Some kernels may be too big even for a 64KB granule employing this scheme.
This patch series addresses the problem in three steps: 1) re-order the reserved_ttbr0 to allow its address computation to be independent of swapper_pg_dir size, 2) re-order the trampoline in a similar manner, and 3) re-write the early pgtable code to allow for multiple page table entries at each level.
Changes in v4: Reviewed-by's/Tested-by's added, count logic simplified in last patch.
Changes in v3: Series rebased on top of arm64/for-next/core branch. This necessitated changes to accommodate kpti (mainly moving the trampiline page table before the swapper, in patch #2); as well as 52-bit PA (some assembler rebasing).
Changes in v2: Ack added to patch #1, KASLR space calculation redone in patch #2.
Steve Capper (3):
arm64: Re-order reserved_ttbr0 in linker script
arm64: entry: Move the trampoline to be before PAN
arm64: Extend early page table code to allow for larger kernels
arch/arm64/include/asm/asm-uaccess.h | 8 +- arch/arm64/include/asm/kernel-pgtable.h | 47 ++++++++++- arch/arm64/include/asm/pgtable.h | 1 + arch/arm64/include/asm/uaccess.h | 4 +- arch/arm64/kernel/entry.S | 4 +- arch/arm64/kernel/head.S | 144 +++++++++++++++++++++++--------- arch/arm64/kernel/vmlinux.lds.S | 15 ++-- arch/arm64/mm/mmu.c | 3 +- 8 files changed, 171 insertions(+), 55 deletions(-)
Signature · 1 line
2.11.0
PATCH v4 1/3 arm64: Re-order reserved_ttbr0 in linker script
Currently one resolves the location of the reserved_ttbr0 for PAN by taking a positive offset from swapper_pg_dir. In a future patch we wish to extend the swapper s.t. its size is determined at link time rather than comile time, rendering SWAPPER_DIR_SIZE unsuitable for such a low level calculation.
In this patch we re-arrange the order of the linker script s.t. instead one computes reserved_ttbr0 by subtracting RESERVED_TTBR0_SIZE from swapper_pg_dir.
---
Changed in V4: added Ard's tags
--- arch/arm64/include/asm/asm-uaccess.h | 8 ++++---- arch/arm64/include/asm/uaccess.h | 4 ++-- arch/arm64/kernel/vmlinux.lds.S | 5 ++--- 3 files changed, 8 insertions(+), 9 deletions(-)
arch/arm64/include/asm/asm-uaccess.h +4 -4
@@ -13,11 +13,11 @@*/ #ifdef CONFIG_ARM64_SW_TTBR0_PAN .macro __uaccess_ttbr0_disable, tmp1 - mrs \tmp1, ttbr1_el1 // swapper_pg_dir - add \tmp1, \tmp1, #SWAPPER_DIR_SIZE // reserved_ttbr0 at the end of swapper_pg_dir - msr ttbr0_el1, \tmp1 // set reserved TTBR0_EL1 + mrs \tmp1, ttbr1_el1 // swapper_pg_dir + sub \tmp1, \tmp1, #RESERVED_TTBR0_SIZE // reserved_ttbr0 just before swapper_pg_dir + msr ttbr0_el1, \tmp1 // set reserved TTBR0_EL1 isb - sub \tmp1, \tmp1, #SWAPPER_DIR_SIZE + add \tmp1, \tmp1, #RESERVED_TTBR0_SIZE bic \tmp1, \tmp1, #TTBR_ASID_MASK msr ttbr1_el1, \tmp1 // set reserved ASID isb
arch/arm64/include/asm/uaccess.h +2 -2
@@ -108,8 +108,8 @@ static inline void __uaccess_ttbr0_disable(void)unsigned long ttbr; ttbr =read_sysreg (ttbr1_el1); -/* reserved_ttbr0 placed at the end of swapper_pg_dir */ -write_sysreg (ttbr +SWAPPER_DIR_SIZE , ttbr0_el1); +/* reserved_ttbr0 placed before swapper_pg_dir */ +write_sysreg (ttbr -RESERVED_TTBR0_SIZE , ttbr0_el1);isb ();/* Set reserved ASID */ ttbr &= ~TTBR_ASID_MASK ;
arch/arm64/kernel/vmlinux.lds.S +2 -3
@@ -218,13 +218,12 @@ SECTIONS . = ALIGN(PAGE_SIZE); idmap_pg_dir = .; . += IDMAP_DIR_SIZE; - swapper_pg_dir = .; - . += SWAPPER_DIR_SIZE; - #ifdef CONFIG_ARM64_SW_TTBR0_PAN reserved_ttbr0 = .; . += RESERVED_TTBR0_SIZE; #endif + swapper_pg_dir = .; + . += SWAPPER_DIR_SIZE; #ifdef CONFIG_UNMAP_KERNEL_AT_EL0 tramp_pg_dir = .;
Signature · 1 line
2.11.0
PATCH v4 2/3 arm64: entry: Move the trampoline to be before PAN
In reply to Steve CapperThe trampoline page tables are positioned after the early page tables in the kernel linker script.
As we are about to change the early page table logic to resolve the swapper size at link time as opposed to compile time, the SWAPPER_DIR_SIZE variable (currently used to locate the trampline) will be rendered unsuitable for low level assembler.
This patch solves this issue by moving the trampoline before the PAN page tables. The offset to the trampoline from ttbr1 can then be expressed by: PAGE_SIZE + RESERVED_TTBR0_SIZE, which is available to the entry assembler.
---
Changed in V4: added Ard's tags
--- arch/arm64/kernel/entry.S | 4 ++-- arch/arm64/kernel/vmlinux.lds.S | 11 ++++++----- 2 files changed, 8 insertions(+), 7 deletions(-)
arch/arm64/kernel/entry.S +2 -2
@@ -987,7 +987,7 @@ __ni_sys_trace: .macro tramp_map_kernel, tmp mrs \tmp, ttbr1_el1 - sub \tmp, \tmp, #(SWAPPER_DIR_SIZE + RESERVED_TTBR0_SIZE) + add \tmp, \tmp, #(PAGE_SIZE + RESERVED_TTBR0_SIZE) bic \tmp, \tmp, #USER_ASID_FLAG msr ttbr1_el1, \tmp #ifdef CONFIG_QCOM_FALKOR_ERRATUM_1003
@@ -1006,7 +1006,7 @@ alternative_else_nop_endif .macro tramp_unmap_kernel, tmp mrs \tmp, ttbr1_el1 - add \tmp, \tmp, #(SWAPPER_DIR_SIZE + RESERVED_TTBR0_SIZE) + sub \tmp, \tmp, #(PAGE_SIZE + RESERVED_TTBR0_SIZE) orr \tmp, \tmp, #USER_ASID_FLAG msr ttbr1_el1, \tmp /*
arch/arm64/kernel/vmlinux.lds.S +6 -5
@@ -218,6 +218,12 @@ SECTIONS . = ALIGN(PAGE_SIZE); idmap_pg_dir = .; . += IDMAP_DIR_SIZE; + +#ifdef CONFIG_UNMAP_KERNEL_AT_EL0 + tramp_pg_dir = .; + . += PAGE_SIZE; +#endif + #ifdef CONFIG_ARM64_SW_TTBR0_PAN reserved_ttbr0 = .; . += RESERVED_TTBR0_SIZE;
@@ -225,11 +231,6 @@ SECTIONS swapper_pg_dir = .; . += SWAPPER_DIR_SIZE; -#ifdef CONFIG_UNMAP_KERNEL_AT_EL0 - tramp_pg_dir = .; - . += PAGE_SIZE; -#endif - __pecoff_data_size = ABSOLUTE(. - __initdata_begin); _end = .;
Signature · 1 line
2.11.0
PATCH v4 3/3 arm64: Extend early page table code to allow for larger kernels
In reply to Steve CapperCurrently the early assembler page table code assumes that precisely 1xpgd, 1xpud, 1xpmd are sufficient to represent the early kernel text mappings.
Unfortunately this is rarely the case when running with a 16KB granule, and we also run into limits with 4KB granule when building much larger kernels.
This patch re-writes the early page table logic to compute indices of mappings for each level of page table, and if multiple indices are required, the next-level page table is scaled up accordingly.
Also the required size of the swapper_pg_dir is computed at link time to cover the mapping [KIMAGE_ADDR + VOFFSET, _end]. When KASLR is enabled, an extra page is set aside for each level that may require extra entries at runtime.
---
Changed in V4:
local loop variable now unique to macro instatiation, count logic simplified
--- arch/arm64/include/asm/kernel-pgtable.h | 47 ++++++++++- arch/arm64/include/asm/pgtable.h | 1 + arch/arm64/kernel/head.S | 144 +++++++++++++++++++++++--------- arch/arm64/kernel/vmlinux.lds.S | 1 + arch/arm64/mm/mmu.c | 3 +- 5 files changed, 156 insertions(+), 40 deletions(-)
arch/arm64/include/asm/kernel-pgtable.h +46 -1
@@ -52,7 +52,52 @@#define IDMAP_PGTABLE_LEVELS (ARM64_HW_PGTABLE_LEVELS(PHYS_MASK_SHIFT))#endif -#define SWAPPER_DIR_SIZE (SWAPPER_PGTABLE_LEVELS * PAGE_SIZE) + +/* +* If KASLR is enabled, then an offset K is added to the kernel address +* space. The bottom 21 bits of this offset are zero to guarantee 2MB +* alignment for PA and VA. +* +* For each pagetable level of the swapper, we know that the shift will +* be larger than 21 (for the 4KB granule case we use section maps thus +* the smallest shift is actually 30) thus there is the possibility that +* KASLR can increase the number of pagetable entries by 1, so we make +* room for this extra entry. +* +* Note KASLR cannot increase the number of required entries for a level +* by more than one because it increments both the virtual start and end +* addresses equally (the extra entry comes from the case where the end +* address is just pushed over a boundary and the start address isn't). +*/ + +#ifdef CONFIG_RANDOMIZE_BASE +#define EARLY_KASLR (1) +#else +#define EARLY_KASLR (0) +#endif + +#define EARLY_ENTRIES (vstart, vend, shift) (((vend) >> (shift)) \ + - ((vstart) >> (shift)) + 1 + EARLY_KASLR) + +#define EARLY_PGDS (vstart, vend) (EARLY_ENTRIES(vstart, vend, PGDIR_SHIFT)) + +#if SWAPPER_PGTABLE_LEVELS >3 +#define EARLY_PUDS (vstart, vend) (EARLY_ENTRIES(vstart, vend, PUD_SHIFT)) +#else +#define EARLY_PUDS (vstart, vend) (0) +#endif + +#if SWAPPER_PGTABLE_LEVELS >2 +#define EARLY_PMDS (vstart, vend) (EARLY_ENTRIES(vstart, vend, SWAPPER_TABLE_SHIFT)) +#else +#define EARLY_PMDS (vstart, vend) (0) +#endif + +#define EARLY_PAGES (vstart, vend) ( 1/* PGDIR page */ \ + + EARLY_PGDS((vstart), (vend)) /* each PGDIR needs a next level page table */ \ + + EARLY_PUDS((vstart), (vend)) /* each PUD needs a next level page table */ \ + + EARLY_PMDS((vstart), (vend))) /* each PMD needs a next level page table */ +#define SWAPPER_DIR_SIZE (PAGE_SIZE * EARLY_PAGES(KIMAGE_VADDR + TEXT_OFFSET, _end))#define IDMAP_DIR_SIZE (IDMAP_PGTABLE_LEVELS * PAGE_SIZE)#ifdef CONFIG_ARM64_SW_TTBR0_PAN
arch/arm64/include/asm/pgtable.h +1 -0
@@ -706,6 +706,7 @@ static inline void pmdp_set_wrprotect(struct mm_struct *mm,#endif extern pgd_t swapper_pg_dir[PTRS_PER_PGD ]; +extern pgd_t swapper_pg_end[];extern pgd_t idmap_pg_dir[PTRS_PER_PGD ];extern pgd_t tramp_pg_dir[PTRS_PER_PGD ];
arch/arm64/kernel/head.S +106 -38
193 lines not on this page · raw message
arch/arm64/kernel/vmlinux.lds.S +1 -0
@@ -230,6 +230,7 @@ SECTIONS #endif swapper_pg_dir = .; . += SWAPPER_DIR_SIZE; + swapper_pg_end = .; __pecoff_data_size = ABSOLUTE(. - __initdata_begin); _end = .;
arch/arm64/mm/mmu.c +2 -1
@@ -644,7 +644,8 @@ void __init paging_init(void)* allocated with it. */ memblock_free (__pa_symbol (swapper_pg_dir) +PAGE_SIZE , -SWAPPER_DIR_SIZE -PAGE_SIZE ); +__pa_symbol (swapper_pg_end) -__pa_symbol (swapper_pg_dir) + -PAGE_SIZE ); }/*
Signature · 1 line
2.11.0
Catalin Marinas
maintainerv4 cover Map larger kernels at early init
In reply to Steve CapperQuoted 4 lines · Steve Capper
Steve Capper (3):
arm64: Re-orderreserved_ttbr0in linker script
arm64: entry: Move the trampoline to be before PAN
arm64: Extend early page table code to allow for larger kernels
I queued the patches for 4.16. I'll push them out once my tests complete.
Thanks.
Signature · 1 line
Catalin