arm64: update/clarify/relax Image and FDT placement rules
Subsystems (6)
v3 cover arm64: update/clarify/relax Image and FDT placement rules
This series came about after Mark Rutland brought up the fact that the current FDT placement logic used by the EFI stub is flawed. But actually, it turned out that the documentation for both the Image and FDT placement was incorrect as well, or confusing at the very least.
Changes since v2:
This is a complete overhaul of the previous version. The FDT changes are mostly equivalent, but have been reimplemented in a way that does not rely on the linear mapping to have been initialized yet. This includes changes to the fixmap code itself to not rely on that either. Combined with the ID map reduction in patch #1, this paves the way for relaxing the Image placement requirements as well, i.e., the kernel Image can now be placed anywhere in memory without affecting the accessibility of memory below it, or causing the resulting mapping to be less efficient due to physical and virtual memory to not be relatively aligned.
Changes since v1:
- patch #1: split off reservation of the FDT binary itself from the memreserve processing, since the former assumes the FDT is accessed via the linear mapping, which we are about to change
- patch #2: mention the older, stricter FDT placement rules in booting.txt, get rid of early_print,
use correct format specifier for phys_addr_t,
use R/O mapping for FDT,
- patches #3 .. #5: add R-b, minor style and grammar tweaks
Ard Biesheuvel (11):
arm64: reduce ID map to a single page
arm64: drop sleep_idmap_phys and clean up cpu_resume() of/fdt: split off FDT self reservation from memreserve processing
arm64: use fixmap region for permanent FDT mapping arm64/efi: adapt to relaxed FDT placement requirements
arm64: implement our own early_init_dt_add_memory_arch()
arm64: fixmap: allow init before linear mapping is set up
arm64: mm: explicitly bootstrap the linear mapping
arm64: move kernel mapping out of linear region
arm64: allow kernel Image to be loaded anywhere in physical memory arm64/efi: adapt to relaxed kernel Image placement requirements
Documentation/arm64/booting.txt | 25 ++- arch/arm/mm/init.c | 1 + arch/arm64/include/asm/compiler.h | 2 + arch/arm64/include/asm/efi.h | 8 +- arch/arm64/include/asm/fixmap.h | 9 ++ arch/arm64/include/asm/memory.h | 15 +- arch/arm64/include/asm/mmu.h | 1 + arch/arm64/kernel/Makefile | 1 + arch/arm64/kernel/efi-stub.c | 4 +- arch/arm64/kernel/head.S | 60 ++----- arch/arm64/kernel/setup.c | 45 +++--- arch/arm64/kernel/sleep.S | 9 +- arch/arm64/kernel/suspend.c | 3 - arch/arm64/kernel/vmlinux.lds.S | 35 ++++- arch/arm64/mm/init.c | 35 +++++ arch/arm64/mm/mmu.c | 271 ++++++++++++++++++++++++-------- arch/arm64/mm/proc.S | 3 +- arch/powerpc/kernel/prom.c | 1 + drivers/firmware/efi/libstub/arm-stub.c | 5 +- drivers/firmware/efi/libstub/efistub.h | 1 - drivers/firmware/efi/libstub/fdt.c | 23 +-- drivers/of/fdt.c | 19 ++- include/linux/of_fdt.h | 2 + 23 files changed, 380 insertions(+), 198 deletions(-)
Signature · 1 line
1.8.3.2
PATCH v3 1/11 arm64: reduce ID map to a single page
Commit ea8c2e112445 ("arm64: Extend the idmap to the whole kernel image") changed the early page table code so that the entire kernel Image is covered by the identity map. This allows functions that need to enable or disable the MMU to reside anywhere in the kernel Image.
However, this change has the unfortunate side effect that the Image cannot cross a physical 512 MB alignment boundary anymore, since the early page table code cannot deal with the Image crossing a /virtual/ 512 MB alignment boundary.
So instead, reduce the ID map to a single page, that is populated by the contents of the .idmap.text section. Only three functions reside there at the moment: __enable_mmu(), cpu_resume_mmu() and cpu_reset(). If new code is introduced that needs to manipulate the MMU state, it should be added to this section as well.
--- arch/arm64/kernel/head.S | 13 +++++++------ arch/arm64/kernel/sleep.S | 2 ++ arch/arm64/kernel/vmlinux.lds.S | 11 ++++++++++- arch/arm64/mm/proc.S | 3 ++- 4 files changed, 21 insertions(+), 8 deletions(-)
arch/arm64/kernel/head.S +7 -6
@@ -382,7 +382,7 @@ __create_page_tables: * Create the identity mapping. */ mov x0, x25 // idmap_pg_dir - adrp x3, KERNEL_START // __pa(KERNEL_START) + adrp x3, __idmap_text_start // __pa(__idmap_text_start) #ifndef CONFIG_ARM64_VA_BITS_48 #define EXTRA_SHIFT (PGDIR_SHIFT + PAGE_SHIFT - 3)
@@ -405,11 +405,11 @@ __create_page_tables: /* * Calculate the maximum allowed value for TCR_EL1.T0SZ so that the - * entire kernel image can be ID mapped. As T0SZ == (64 - #bits used), + * entire ID map region can be mapped. As T0SZ == (64 - #bits used), * this number conveniently equals the number of leading zeroes in - * the physical address of KERNEL_END. + * the physical address of __idmap_text_end. */ - adrp x5, KERNEL_END + adrp x5, __idmap_text_end clz x5, x5 cmp x5, TCR_T0SZ(VA_BITS) // default T0SZ small enough? b.ge 1f // .. then skip additional level
@@ -424,8 +424,8 @@ __create_page_tables: #endif create_pgd_entry x0, x3, x5, x6 - mov x5, x3 // __pa(KERNEL_START) - adr_l x6, KERNEL_END // __pa(KERNEL_END) + mov x5, x3 // __pa(__idmap_text_start) + adr_l x6, __idmap_text_end // __pa(__idmap_text_end) create_block_map x0, x7, x3, x5, x6 /*
@@ -669,6 +669,7 @@ ENDPROC(__secondary_switched) * * other registers depend on the function called upon completion */ + .section ".idmap.text", #alloc, #execinstr __enable_mmu: ldr x5, =vectors msr vbar_el1, x5
arch/arm64/kernel/sleep.S +2 -0
@@ -130,12 +130,14 @@ ENDPROC(__cpu_suspend_enter) /* * x0 must contain the sctlr value retrieved from restored context */ + .pushsection ".idmap.text", #alloc, #execinstr ENTRY(cpu_resume_mmu) ldr x3, =cpu_resume_after_mmu msr sctlr_el1, x0 // restore sctlr_el1 isb br x3 // global jump to virtual address ENDPROC(cpu_resume_mmu) + .popsection cpu_resume_after_mmu: mov x0, #0 // return zero on success ldp x19, x20, [sp, #16]
arch/arm64/kernel/vmlinux.lds.S +10 -1
@@ -38,6 +38,12 @@ jiffies = jiffies_64; *(.hyp.text) \ VMLINUX_SYMBOL(__hyp_text_end) = .; +#define IDMAP_TEXT \ + . = ALIGN(SZ_4K); \ + VMLINUX_SYMBOL(__idmap_text_start) = .; \ + *(.idmap.text) \ + VMLINUX_SYMBOL(__idmap_text_end) = .; + /* * The size of the PE/COFF section that covers the kernel image, which * runs from stext to _edata, must be a round multiple of the PE/COFF
@@ -95,6 +101,7 @@ SECTIONS SCHED_TEXT LOCK_TEXT HYPERVISOR_TEXT + IDMAP_TEXT *(.fixup) *(.gnu.warning) . = ALIGN(16);
@@ -167,11 +174,13 @@ SECTIONS } /* - * The HYP init code can't be more than a page long, + * The HYP init code and ID map text can't be longer than a page each, * and should not cross a page boundary. */ ASSERT(__hyp_idmap_text_end - (__hyp_idmap_text_start & ~(SZ_4K - 1)) <= SZ_4K, "HYP init code too big or misaligned") +ASSERT(__idmap_text_end - (__idmap_text_start & ~(SZ_4K - 1)) <= SZ_4K, + "ID map text too big or misaligned") /* * If padding is applied before .head.text, virt<->phys conversions will fail.
arch/arm64/mm/proc.S +2 -1
@@ -67,7 +67,7 @@ ENDPROC(cpu_cache_off) * * - loc - location to jump to for soft reset */ - .align 5 + .pushsection ".idmap.text", #alloc, #execinstr ENTRY(cpu_reset) mrs x1, sctlr_el1 bic x1, x1, #1
@@ -75,6 +75,7 @@ ENTRY(cpu_reset) isb ret x0 ENDPROC(cpu_reset) + .popsection ENTRY(cpu_soft_restart) /* Save address of cpu_reset() and reset address */
Signature · 1 line
1.8.3.2
Mark Rutland
reviewerPATCH v3 1/11 arm64: reduce ID map to a single page
Hi Ard,
Quoted 6 lines · Ard Biesheuvel
Commit
ea8c2e112445("arm64: Extend the idmap to the whole kernel image") changed the early page table code so that the entire kernel Image is covered by the identity map. This allows functions that need to enable or disable the MMU to reside anywhere in the kernel Image.
Quoted 8 lines
However, this change has the unfortunate side effect that the Image cannot cross a physical 512 MB alignment boundary anymore, since the early page table code cannot deal with the Image crossing a /virtual/ 512 MB alignment boundary.
So instead, reduce the ID map to a single page, that is populated by the contents of the .idmap.text section. Only three functions reside there at the moment:
__enable_mmu(),cpu_resume_mmu()andcpu_reset().
It would be worth mentioning in the cover letter which branch this is based on (arm64 for-next/core?), given the __enable_mmu + __turn_mmu_on folding isn't in mainline yet.
Quoted 4 lines
If new code is introduced that needs to manipulate the MMU state, it should be added to this section as well.
Signed-off-by Ard Biesheuvel <[email protected]>
Other than the minor nit below this looks good to me, and I've given it a spin on Juno with 4K and 64K pages. Assuming you can fix that up:
arch/arm64/kernel/head.S ENDPROC(__secondary_switched) · line 672
Quoted 5 lines
arch/arm64/kernel/head.S +1
-0@@ -669,6 +669,7 @@ ENDPROC(__secondary_switched) * * other registers depend on the function called upon completion */ + .section ".idmap.text", #alloc, #execinstr
We should use "ax" rather than #alloc, #execinstr to keep things consistent, unless there's some difference that I'm missing?
I've tested the patch locally with all instances changed to "ax".
Thanks,
Mark.
PATCH v3 1/11 arm64: reduce ID map to a single page
Quoted 14 lines · Mark Rutland
Hi Ard,
Quoted 11 lines · Ard Biesheuvel
Commit
ea8c2e112445("arm64: Extend the idmap to the whole kernel image") changed the early page table code so that the entire kernel Image is covered by the identity map. This allows functions that need to enable or disable the MMU to reside anywhere in the kernel Image.However, this change has the unfortunate side effect that the Image cannot cross a physical 512 MB alignment boundary anymore, since the early page table code cannot deal with the Image crossing a /virtual/ 512 MB alignment boundary.
Quoted 8 lines
Quoted 3 lines
So instead, reduce the ID map to a single page, that is populated by the contents of the .idmap.text section. Only three functions reside there at the moment:
__enable_mmu(),cpu_resume_mmu()andcpu_reset().It would be worth mentioning in the cover letter which branch this is based on (arm64 for-next/core?), given the
__enable_mmu+__turn_mmu_onfolding isn't in mainline yet.
Ah yes. That just feels so long ago already :-)
Quoted 11 lines
Quoted 4 lines
If new code is introduced that needs to manipulate the MMU state, it should be added to this section as well.
Signed-off-by Ard Biesheuvel <[email protected]>Other than the minor nit below this looks good to me, and I've given it a spin on Juno with 4K and 64K pages. Assuming you can fix that up:
Reviewed-by Mark Rutland <[email protected]>Tested-by Mark Rutland <[email protected]>
Thanks!
Quoted 11 lines
arch/arm64/kernel/head.S ENDPROC(__secondary_switched) · line 672
Quoted 5 lines
arch/arm64/kernel/head.S +1
-0@@ -669,6 +669,7 @@ ENDPROC(__secondary_switched) * * other registers depend on the function called upon completion */ + .section ".idmap.text", #alloc, #execinstrWe should use "ax" rather than #alloc, #execinstr to keep things consistent, unless there's some difference that I'm missing?
I've tested the patch locally with all instances changed to "ax".
OK, I will change it. I have a personal preference for wordy when it comes to things like this (try googling for ax), but as far as I know, they are 100% equivalent.
Signature · 1 line
Ard.
PATCH v3 2/11 arm64: drop sleep_idmap_phys and clean up cpu_resume()
In reply to Ard BiesheuvelTwo cleanups of the asm function cpu_resume():
- The global variable sleep_idmap_phys always points to idmap_pg_dir, so we can just use that value directly in the CPU resume path.
- Unclutter the load of sleep_save_sp::save_ptr_stash_phys.
--- arch/arm64/kernel/sleep.S | 7 ++----- arch/arm64/kernel/suspend.c | 3 --- 2 files changed, 2 insertions(+), 8 deletions(-)
arch/arm64/kernel/sleep.S +2 -5
@@ -164,15 +164,12 @@ ENTRY(cpu_resume) #else mov x7, xzr #endif - adrp x0, sleep_save_sp - add x0, x0, #:lo12:sleep_save_sp - ldr x0, [x0, #SLEEP_SAVE_SP_PHYS] + ldr_l x0, sleep_save_sp + SLEEP_SAVE_SP_PHYS ldr x0, [x0, x7, lsl #3] /* load sp from context */ ldr x2, [x0, #CPU_CTX_SP] - adrp x1, sleep_idmap_phys /* load physical address of identity map page table in x1 */ - ldr x1, [x1, #:lo12:sleep_idmap_phys] + adrp x1, idmap_pg_dir mov sp, x2 /* * cpu_do_resume expects x0 to contain context physical address
arch/arm64/kernel/suspend.c +0 -3
@@ -118,7 +118,6 @@ int __cpu_suspend(unsigned long arg, int (*fn)(unsigned long)) }struct sleep_save_sp sleep_save_sp; -phys_addr_t sleep_idmap_phys;static int __init cpu_suspend_init (void ) {
@@ -132,9 +131,7 @@ static int __init cpu_suspend_init(void) sleep_save_sp.save_ptr_stash = ctx_ptr; sleep_save_sp.save_ptr_stash_phys =virt_to_phys (ctx_ptr); - sleep_idmap_phys =virt_to_phys (idmap_pg_dir);__flush_dcache_area (&sleep_save_sp,sizeof (struct sleep_save_sp )); -__flush_dcache_area (&sleep_idmap_phys,sizeof (sleep_idmap_phys));return 0 ; }
Signature · 1 line
1.8.3.2
PATCH v3 3/11 of/fdt: split off FDT self reservation from memreserve processing
In reply to Ard BiesheuvelThis splits off the reservation of the memory occupied by the FDT binary itself from the processing of the memory reservations it contains. This is necessary because the physical address of the FDT, which is needed to perform the reservation, may not be known to the FDT driver core, i.e., it may be mapped outside the linear direct mapping, in which case __pa() returns a bogus value.
--- arch/arm/mm/init.c | 1 + arch/arm64/mm/init.c | 1 + arch/powerpc/kernel/prom.c | 1 + drivers/of/fdt.c | 19 ++++++++++++++----- include/linux/of_fdt.h | 2 ++ 5 files changed, 19 insertions(+), 5 deletions(-)
arch/arm/mm/init.c +1 -0
@@ -317,6 +317,7 @@ void __init arm_memblock_init(const struct machine_desc *mdesc)if (mdesc->reserve) mdesc->reserve (); +early_init_fdt_reserve_self ();early_init_fdt_scan_reserved_mem ();/* reserve memory for DMA contiguous allocations */
arch/arm64/mm/init.c +1 -0
@@ -170,6 +170,7 @@ void __init arm64_memblock_init(void)memblock_reserve (__virt_to_phys (initrd_start), initrd_end - initrd_start);#endif +early_init_fdt_reserve_self ();early_init_fdt_scan_reserved_mem ();/* 4GB maximum for 32-bit only capable devices */
arch/powerpc/kernel/prom.c +1 -0
@@ -573,6 +573,7 @@ static void __init early_reserve_mem_dt(void)int len;const __be32 *prop; +early_init_fdt_reserve_self ();early_init_fdt_scan_reserved_mem (); dt_root =of_get_flat_dt_root ();
drivers/of/fdt.c +14 -5
@@ -561,11 +561,6 @@ void __init early_init_fdt_scan_reserved_mem(void)if (!initial_boot_params)return ; -/* Reserve the dtb region */ -early_init_dt_reserve_memory_arch (__pa (initial_boot_params), -fdt_totalsize (initial_boot_params), -0 ); -/* Process header /memreserve/ fields */ for (n =0 ; ; n++) {fdt_get_mem_rsv (initial_boot_params, n, &base, &size);
@@ -579,6 +574,20 @@ void __init early_init_fdt_scan_reserved_mem(void) }/** +* early_init_fdt_reserve_self() - reserve the memory used by the FDT blob +*/ +void __init early_init_fdt_reserve_self (void ) +{ +if (!initial_boot_params) +return ; + +/* Reserve the dtb region */ +early_init_dt_reserve_memory_arch (__pa (initial_boot_params), +fdt_totalsize (initial_boot_params), +0 ); +} + +/** * of_scan_flat_dt - scan flattened tree blob and call callback on each. * @it: callback function * @data: context data pointer
include/linux/of_fdt.h +2 -0
@@ -62,6 +62,7 @@ extern int early_init_dt_scan_chosen(unsigned long node, const char *uname,extern int early_init_dt_scan_memory (unsigned long node,const char *uname,int depth,void *data);extern void early_init_fdt_scan_reserved_mem (void ); +extern void early_init_fdt_reserve_self (void );extern void early_init_dt_add_memory_arch (u64 base,u64 size);extern int early_init_dt_reserve_memory_arch (phys_addr_t base,phys_addr_t size,bool no_map);
@@ -89,6 +90,7 @@ extern u64 fdt_translate_address(const void *blob, int node_offset);extern void of_fdt_limit_memory (int limit);#else /* CONFIG_OF_FLATTREE */ static inline void early_init_fdt_scan_reserved_mem (void ) {} +static inline void early_init_fdt_reserve_self (void ) {}static inline const char *of_flat_dt_get_machine_name (void ) {return NULL ; }static inline void unflatten_device_tree (void ) {}static inline void unflatten_and_copy_device_tree (void ) {}
Signature · 1 line
1.8.3.2
PATCH v3 4/11 arm64: use fixmap region for permanent FDT mapping
In reply to Ard BiesheuvelCurrently, the FDT blob needs to be in the same 512 MB region as the kernel, so that it can be mapped into the kernel virtual memory space very early on using a minimal set of statically allocated translation tables.
Now that we have early fixmap support, we can relax this restriction, by moving the permanent FDT mapping to the fixmap region instead. This way, the FDT blob may be anywhere in memory.
This also moves the vetting of the FDT to setup.c, since the early init code in head.S does not handle mapping of the FDT anymore. At the same time, fix up some comments in head.S that have gone stale.
--- Documentation/arm64/booting.txt | 8 +++---- arch/arm64/include/asm/fixmap.h | 9 ++++++++ arch/arm64/include/asm/mmu.h | 1 + arch/arm64/kernel/Makefile | 1 + arch/arm64/kernel/head.S | 39 +-------------------------------- arch/arm64/kernel/setup.c | 45 ++++++++++++++++++++------------------ arch/arm64/mm/init.c | 1 - arch/arm64/mm/mmu.c | 48 +++++++++++++++++++++++++++++++++++++++++ 8 files changed, 88 insertions(+), 64 deletions(-)
Documentation/arm64/booting.txt +4 -4
@@ -45,11 +45,11 @@ sees fit.) Requirement: MANDATORY -The device tree blob (dtb) must be placed on an 8-byte boundary within -the first 512 megabytes from the start of the kernel image and must not -cross a 2-megabyte boundary. This is to allow the kernel to map the -blob using a single section mapping in the initial page tables. +The device tree blob (dtb) must be placed on an 8-byte boundary and must +not exceed 2 megabytes in size. +NOTE: versions prior to v4.2 also require that the dtb be placed within +512 MB of the 2 MB aligned boundary right below the kernel Image. 3. Decompress the kernel image ------------------------------
arch/arm64/include/asm/fixmap.h +9 -0
@@ -32,6 +32,15 @@*/ enum fixed_addresses {FIX_HOLE , + +/* +* Reserve 4 MB of virtual space for the FDT at the top of the fixmap +* region. Keep this at the top so it remains 2 MB aligned. +*/ +#define FIX_FDT_SIZE SZ_4M + FIX_FDT_END, +FIX_FDT =FIX_FDT_END +FIX_FDT_SIZE /PAGE_SIZE -1 , +FIX_EARLYCON_MEM_BASE ,FIX_TEXT_POKE0 , __end_of_permanent_fixed_addresses,
arch/arm64/include/asm/mmu.h +1 -0
@@ -34,5 +34,6 @@ extern void init_mem_pgprot(void);extern void create_pgd_mapping (struct mm_struct *mm,phys_addr_t phys,unsigned long virt,phys_addr_t size,pgprot_t prot); +extern void *fixmap_remap_fdt (phys_addr_t dt_phys);#endif
arch/arm64/kernel/Makefile +1 -0
@@ -6,6 +6,7 @@ CPPFLAGS_vmlinux.lds := -DTEXT_OFFSET=$(TEXT_OFFSET) AFLAGS_head.o := -DTEXT_OFFSET=$(TEXT_OFFSET) CFLAGS_efi-stub.o := -DTEXT_OFFSET=$(TEXT_OFFSET) CFLAGS_armv8_deprecated.o := -I$(src) +CFLAGS_setup.o := -I$(srctree)/scripts/dtc/libfdt/ CFLAGS_REMOVE_ftrace.o = -pg CFLAGS_REMOVE_insn.o = -pg
arch/arm64/kernel/head.S +1 -38
@@ -237,8 +237,6 @@ ENTRY(stext) bl el2_setup // Drop to EL1, w20=cpu_boot_mode adrp x24, __PHYS_OFFSET bl set_cpu_boot_mode_flag - - bl __vet_fdt bl __create_page_tables // x25=TTBR0, x26=TTBR1 /* * The following calls CPU setup code, see arch/arm64/mm/proc.S for
@@ -270,24 +268,6 @@ preserve_boot_args: ENDPROC(preserve_boot_args) /* - * Determine validity of the x21 FDT pointer. - * The dtb must be 8-byte aligned and live in the first 512M of memory. - */ -__vet_fdt: - tst x21, #0x7 - b.ne 1f - cmp x21, x24 - b.lt 1f - mov x0, #(1 << 29) - add x0, x0, x24 - cmp x21, x0 - b.ge 1f - ret -1: - mov x21, #0 - ret -ENDPROC(__vet_fdt) -/* * Macro to create a table entry to the next page. * * tbl: page table address
@@ -348,8 +328,7 @@ ENDPROC(__vet_fdt) * required to get the kernel running. The following sections are required: * - identity mapping to enable the MMU (low address, TTBR0) * - first few MB of the kernel linear mapping to jump to once the MMU has - * been enabled, including the FDT blob (TTBR1) - * - pgd entry for fixed mappings (TTBR1) + * been enabled */ __create_page_tables: adrp x25, idmap_pg_dir
@@ -439,22 +418,6 @@ __create_page_tables: create_block_map x0, x7, x3, x5, x6 /* - * Map the FDT blob (maximum 2MB; must be within 512MB of - * PHYS_OFFSET). - */ - mov x3, x21 // FDT phys address - and x3, x3, #~((1 << 21) - 1) // 2MB aligned - mov x6, #PAGE_OFFSET - sub x5, x3, x24 // subtract PHYS_OFFSET - tst x5, #~((1 << 29) - 1) // within 512MB? - csel x21, xzr, x21, ne // zero the FDT pointer - b.ne 1f - add x5, x5, x6 // __va(FDT blob) - add x6, x5, #1 << 21 // 2MB for the FDT blob - sub x6, x6, #1 // inclusive range - create_block_map x0, x7, x3, x5, x6 -1: - /* * Since the page tables have been populated with non-cacheable * accesses (MMU disabled), invalidate the idmap and swapper page * tables again to remove any speculatively loaded cache lines.
arch/arm64/kernel/setup.c +24 -21
80 lines not on this page · raw message
arch/arm64/mm/init.c +0 -1
@@ -170,7 +170,6 @@ void __init arm64_memblock_init(void)memblock_reserve (__virt_to_phys (initrd_start), initrd_end - initrd_start);#endif -early_init_fdt_reserve_self ();early_init_fdt_scan_reserved_mem ();/* 4GB maximum for 32-bit only capable devices */
arch/arm64/mm/mmu.c +48 -0
51 lines not on this page · raw message
Signature · 1 line
1.8.3.2
Mark Rutland
reviewerPATCH v3 4/11 arm64: use fixmap region for permanent FDT mapping
Hi Ard,
Documentation/arm64/booting.txt sees fit.) · line 52
Quoted 9 lines
Documentation/arm64/booting.txt +4
-4-The device tree blob (dtb) must be placed on an 8-byte boundary within -the first 512 megabytes from the start of the kernel image and must not -cross a 2-megabyte boundary. This is to allow the kernel to map the -blob using a single section mapping in the initial page tables. +The device tree blob (dtb) must be placed on an 8-byte boundary and must +not exceed 2 megabytes in size. +NOTE: versions prior to v4.2 also require that the dtb be placed within +512 MB of the 2 MB aligned boundary right below the kernel Image.
This reads a little odd (it sounds like you could load the DTB 512M below the boundary too).
How about:
NOTE: versions prior to v4.2 also require that the DTB be placed within the 512 MB region starting at text_offset bytes below the kernel Image.
[...]
Quoted 14 lines
enum
fixed_addresses{arch/arm64/include/asm/fixmap.h +9
-0FIX_HOLE, + +/* +* Reserve 4 MB of virtual space for the FDT at the top of the fixmap +* region. Keep this at the top so it remains 2 MB aligned. +*/ +#define FIX_FDT_SIZE SZ_4M +FIX_FDT_END , +FIX_FDT =FIX_FDT_END +FIX_FDT_SIZE /PAGE_SIZE -1 , +FIX_EARLYCON_MEM_BASE ,FIX_TEXT_POKE0 , __end_of_permanent_fixed_addresses,
[...]
arch/arm64/kernel/setup.c static void __init setup_processor(void) · line 329
Quoted 9 lines
arch/arm64/kernel/setup.c +9
-0+/* +* Before passing the dt_virt pointer to early_init_dt_scan(), we have +* to ensure that the FDT size as reported in the FDT itself does not +* exceed the window we just mapped for it. +*/ +if (!dt_virt || +fdt_check_header (dt_virt) !=0 || + (dt_phys & (SZ_2M -1 )) +fdt_totalsize (dt_virt) >FIX_FDT_SIZE || + !early_init_dt_scan (dt_virt)) {
Doesn't this allow a DTB > 2M (violating the boot protocol's 2M restriction)?
Perhaps we want a MAX_FDT_SIZE, and have FIX_FDT_SIZE be MAX_FDT_SIZE + 2M. Then we can check that the DTB <= MAX_FDT_SIZE, and we know we will always be able to map it.
Otherwise we could get intermittent failures for DTBs larger than 2MB, depending on where they're loaded. Always failing those for now would give us a consistent early warning that we need to bump MAX_FDT_SIZE.
[...]
arch/arm64/mm/mmu.c void __set_fixmap(enum fixed_addresses idx, · line 650
Quoted 4 lines
arch/arm64/mm/mmu.c +4
-0+void *__init fixmap_remap_fdt (phys_addr_t dt_phys) +{ +const u64 dt_virt_base =__fix_to_virt (FIX_FDT ); +phys_addr_t dt_phys_base = dt_phys & ~(SZ_2M -1 );
Do we definitely retain the high bits here? I don't have the C integer promotion rules paged into my head, but the definition in include/linux/sizes.h makes me suspicious, given it should have an int type:
#define SZ_2M 0x00200000
Quoted 4 lines
arch/arm64/mm/mmu.c +4
-0+u64 pfn = dt_phys_base >>PAGE_SHIFT ; +u64 pfn_end = pfn + (FIX_FDT_SIZE >>PAGE_SHIFT ); +pgprot_t prot =PAGE_KERNEL |PTE_RDONLY ; +
arch/arm64/mm/mmu.c void __set_fixmap(enum fixed_addresses idx, · line 664
Quoted 10 lines
arch/arm64/mm/mmu.c +10
-0+/* +* Make sure that the FDT region can be mapped without the need to +* allocate additional translation table pages, or perform physical +* to virtual address translations via the linear mapping. +* +* On 4k pages, we'll use section mappings for the region so we +* only have to be in the same PUD as the rest of the fixmap. +* On 64k pages, we need to be in the same PMD as well, as the region +* will be mapped using PTEs. +*/
It would be nice to have the last two statements swapped (with appropriate wording fixups) to match the order of the if branches.
Otherwise this looks good to me.
Mark.
Quoted 32 lines
arch/arm64/mm/mmu.c +29
-0+BUILD_BUG_ON (dt_virt_base & (SZ_2M -1 )); + +if (IS_ENABLED (CONFIG_ARM64_64K_PAGES )) { +pte_t *pte; + +BUILD_BUG_ON (__fix_to_virt (FIX_FDT_END ) >>PMD_SHIFT != +__fix_to_virt (FIX_BTMAP_BEGIN ) >>PMD_SHIFT ); + +/* map the FDT using 64k pages */ + pte =fixmap_pte (dt_virt_base); +do { +set_pte (pte++,pfn_pte (pfn++, prot)); + }while (pfn < pfn_end); + }else { +pmd_t *pmd; + +BUILD_BUG_ON (__fix_to_virt (FIX_FDT_END ) >>PUD_SHIFT != +__fix_to_virt (FIX_BTMAP_BEGIN ) >>PUD_SHIFT ); + +/* map the FDT using 2 MB sections */ + pmd =fixmap_pmd (dt_virt_base); +do { +set_pmd (pmd++,pfn_pmd (pfn,mk_sect_prot (prot))); + pfn +=PMD_SIZE >>PAGE_SHIFT ; + }while (pfn < pfn_end); + } + +return (void *)(dt_virt_base + dt_phys - dt_phys_base); +}Signature · 1 line
1.8.3.2
PATCH v3 4/11 arm64: use fixmap region for permanent FDT mapping
Quoted 15 lines · Mark Rutland
Hi Ard,
Documentation/arm64/booting.txt sees fit.) · line 52
Quoted 9 lines
Documentation/arm64/booting.txt +4
-4-The device tree blob (dtb) must be placed on an 8-byte boundary within -the first 512 megabytes from the start of the kernel image and must not -cross a 2-megabyte boundary. This is to allow the kernel to map the -blob using a single section mapping in the initial page tables. +The device tree blob (dtb) must be placed on an 8-byte boundary and must +not exceed 2 megabytes in size. +NOTE: versions prior to v4.2 also require that the dtb be placed within +512 MB of the 2 MB aligned boundary right below the kernel Image.This reads a little odd (it sounds like you could load the DTB 512M below the boundary too).
Quoted 5 lines
How about:
NOTE: versions prior to v4.2 also require that the DTB be placed within the 512 MB region starting at
text_offsetbytes below the kernel Image.
OK
Quoted 29 lines
[...]
Quoted 14 lines
enum
fixed_addresses{
FIX_HOLE,arch/arm64/include/asm/fixmap.h +9
-0+ +/* +* Reserve 4 MB of virtual space for the FDT at the top of the fixmap +* region. Keep this at the top so it remains 2 MB aligned. +*/ +#define FIX_FDT_SIZE SZ_4M +FIX_FDT_END , +FIX_FDT =FIX_FDT_END +FIX_FDT_SIZE /PAGE_SIZE -1 , +FIX_EARLYCON_MEM_BASE ,FIX_TEXT_POKE0 , __end_of_permanent_fixed_addresses,[...]
arch/arm64/kernel/setup.c static void __init setup_processor(void) · line 329
Quoted 9 lines
arch/arm64/kernel/setup.c +9
-0+/* +* Before passing the dt_virt pointer to early_init_dt_scan(), we have +* to ensure that the FDT size as reported in the FDT itself does not +* exceed the window we just mapped for it. +*/ +if (!dt_virt || +fdt_check_header (dt_virt) !=0 || + (dt_phys & (SZ_2M -1 )) +fdt_totalsize (dt_virt) >FIX_FDT_SIZE || + !early_init_dt_scan (dt_virt)) {
Quoted 3 lines
Doesn't this allow a DTB > 2M (violating the boot protocol's 2M restriction)?
Yes it does, and I was wondering what to do about it. We could be pedantic, and fail here with no user visible feedback about what went wrong, or we could be lax here, and add a warning later (similar to the x1/x2/x3 check) that the boot protocol was violated if the FDT exceeds 2 MB but otherwise proceed normally.
Quoted 8 lines
Perhaps we want a
MAX_FDT_SIZE, and haveFIX_FDT_SIZEbeMAX_FDT_SIZE+ 2M. Then we can check that the DTB <=MAX_FDT_SIZE, and we know we will always be able to map it.Otherwise we could get intermittent failures for DTBs larger than 2MB, depending on where they're loaded. Always failing those for now would give us a consistent early warning that we need to bump
MAX_FDT_SIZE.
Consistent, early, but emitted before we even have a console ...
Quoted 12 lines
[...]
arch/arm64/mm/mmu.c void __set_fixmap(enum fixed_addresses idx, · line 650
Quoted 4 lines
arch/arm64/mm/mmu.c +4
-0+void *__init fixmap_remap_fdt (phys_addr_t dt_phys) +{ +const u64 dt_virt_base =__fix_to_virt (FIX_FDT ); +phys_addr_t dt_phys_base = dt_phys & ~(SZ_2M -1 );Do we definitely retain the high bits here? I don't have the C integer promotion rules paged into my head, but the definition in
include/linux/sizes.hmakes me suspicious, given it should have an int type:
Quoted 2 lines
#define
SZ_2M0x00200000
I will use round_down() instead
Quoted 15 lines
Quoted 4 lines
arch/arm64/mm/mmu.c +4
-0+u64 pfn = dt_phys_base >>PAGE_SHIFT ; +u64 pfn_end = pfn + (FIX_FDT_SIZE >>PAGE_SHIFT ); +pgprot_t prot =PAGE_KERNEL |PTE_RDONLY ; +arch/arm64/mm/mmu.c void __set_fixmap(enum fixed_addresses idx, · line 664
Quoted 10 lines
arch/arm64/mm/mmu.c +10
-0+/* +* Make sure that the FDT region can be mapped without the need to +* allocate additional translation table pages, or perform physical +* to virtual address translations via the linear mapping. +* +* On 4k pages, we'll use section mappings for the region so we +* only have to be in the same PUD as the rest of the fixmap. +* On 64k pages, we need to be in the same PMD as well, as the region +* will be mapped using PTEs. +*/
Quoted 3 lines
It would be nice to have the last two statements swapped (with appropriate wording fixups) to match the order of the if branches.
ok
Quoted 2 lines
Otherwise this looks good to me.
thanks.
Signature · 1 line
Ard.
Quoted 32 lines
Quoted 32 lines
arch/arm64/mm/mmu.c +29
-1+BUILD_BUG_ON (dt_virt_base & (SZ_2M -1 )); + +if (IS_ENABLED (CONFIG_ARM64_64K_PAGES )) { +pte_t *pte; + +BUILD_BUG_ON (__fix_to_virt (FIX_FDT_END ) >>PMD_SHIFT != +__fix_to_virt (FIX_BTMAP_BEGIN ) >>PMD_SHIFT ); + +/* map the FDT using 64k pages */ + pte =fixmap_pte (dt_virt_base); +do { +set_pte (pte++,pfn_pte (pfn++, prot)); + }while (pfn < pfn_end); + }else { +pmd_t *pmd; + +BUILD_BUG_ON (__fix_to_virt (FIX_FDT_END ) >>PUD_SHIFT != +__fix_to_virt (FIX_BTMAP_BEGIN ) >>PUD_SHIFT ); + +/* map the FDT using 2 MB sections */ + pmd =fixmap_pmd (dt_virt_base); +do { +set_pmd (pmd++,pfn_pmd (pfn,mk_sect_prot (prot))); + pfn +=PMD_SIZE >>PAGE_SHIFT ; + }while (pfn < pfn_end); + } + +return (void *)(dt_virt_base + dt_phys - dt_phys_base); +} --1.8.3.2
Mark Rutland
reviewerPATCH v3 4/11 arm64: use fixmap region for permanent FDT mapping
Quoted 24 lines
Quoted 10 lines
arch/arm64/kernel/setup.c static void __init setup_processor(void) · line 329
Quoted 9 lines
arch/arm64/kernel/setup.c +9
-0+/* +* Before passing the dt_virt pointer to early_init_dt_scan(), we have +* to ensure that the FDT size as reported in the FDT itself does not +* exceed the window we just mapped for it. +*/ +if (!dt_virt || +fdt_check_header (dt_virt) !=0 || + (dt_phys & (SZ_2M -1 )) +fdt_totalsize (dt_virt) >FIX_FDT_SIZE || + !early_init_dt_scan (dt_virt)) {Quoted 3 lines
Doesn't this allow a DTB > 2M (violating the boot protocol's 2M restriction)?
Yes it does, and I was wondering what to do about it. We could be pedantic, and fail here with no user visible feedback about what went wrong, or we could be lax here, and add a warning later (similar to the x1/x2/x3 check) that the boot protocol was violated if the FDT exceeds 2 MB but otherwise proceed normally.
Quoted 4 lines
Perhaps we want a
MAX_FDT_SIZE, and haveFIX_FDT_SIZEbeMAX_FDT_SIZE+ 2M. Then we can check that the DTB <=MAX_FDT_SIZE, and we know we will always be able to map it.
Quoted 6 lines
Quoted 4 lines
Otherwise we could get intermittent failures for DTBs larger than 2MB, depending on where they're loaded. Always failing those for now would give us a consistent early warning that we need to bump
MAX_FDT_SIZE.Consistent, early, but emitted before we even have a console ...
When I say warning, I mean that boot will consistently fail, rather than there being a pr_warn.
People seem to wait until their platform is hilariously broken before reporting bugs...
Quoted 10 lines
Quoted 10 lines
arch/arm64/mm/mmu.c void __set_fixmap(enum fixed_addresses idx, · line 650
Quoted 4 lines
arch/arm64/mm/mmu.c +4
-0+void *__init fixmap_remap_fdt (phys_addr_t dt_phys) +{ +const u64 dt_virt_base =__fix_to_virt (FIX_FDT ); +phys_addr_t dt_phys_base = dt_phys & ~(SZ_2M -1 );Do we definitely retain the high bits here? I don't have the C integer promotion rules paged into my head, but the definition in
include/linux/sizes.hmakes me suspicious, given it should have an int type:
Quoted 4 lines
Quoted 2 lines
#define
SZ_2M0x00200000I will use
round_down()instead
Ok.
Mark.
PATCH v3 4/11 arm64: use fixmap region for permanent FDT mapping
Quoted 29 lines · Mark Rutland
Quoted 29 lines
Quoted 10 lines
arch/arm64/kernel/setup.c static void __init setup_processor(void) · line 329
Quoted 9 lines
arch/arm64/kernel/setup.c +9
-0+/* +* Before passing the dt_virt pointer to early_init_dt_scan(), we have +* to ensure that the FDT size as reported in the FDT itself does not +* exceed the window we just mapped for it. +*/ +if (!dt_virt || +fdt_check_header (dt_virt) !=0 || + (dt_phys & (SZ_2M -1 )) +fdt_totalsize (dt_virt) >FIX_FDT_SIZE || + !early_init_dt_scan (dt_virt)) {Quoted 3 lines
Doesn't this allow a DTB > 2M (violating the boot protocol's 2M restriction)?
Yes it does, and I was wondering what to do about it. We could be pedantic, and fail here with no user visible feedback about what went wrong, or we could be lax here, and add a warning later (similar to the x1/x2/x3 check) that the boot protocol was violated if the FDT exceeds 2 MB but otherwise proceed normally.
Quoted 8 lines
Perhaps we want a
MAX_FDT_SIZE, and haveFIX_FDT_SIZEbeMAX_FDT_SIZE+ 2M. Then we can check that the DTB <=MAX_FDT_SIZE, and we know we will always be able to map it.Otherwise we could get intermittent failures for DTBs larger than 2MB, depending on where they're loaded. Always failing those for now would give us a consistent early warning that we need to bump
MAX_FDT_SIZE.
Quoted 8 lines
Quoted 1 line
Consistent, early, but emitted before we even have a console ...
When I say warning, I mean that boot will consistently fail, rather than there being a
pr_warn.People seem to wait until their platform is hilariously broken before reporting bugs...
Yeah, that is true. At this stage, there is still room to be pedantic.
I will take your MAX_FDT_SiZE/FIX_FDT_SIZE suggestion, and check fdt_totalsize() against the former instead.
I was wondering: this code now always maps the full 4 MB, also on 64k pages, even if in the majority of cases, the actual FDT is much smaller. Do you think there is a downside to that?
Signature · 1 line
Ard.
Quoted 18 lines
Quoted 10 lines
Quoted 10 lines
arch/arm64/mm/mmu.c void __set_fixmap(enum fixed_addresses idx, · line 650
Quoted 4 lines
arch/arm64/mm/mmu.c +4
-0+void *__init fixmap_remap_fdt (phys_addr_t dt_phys) +{ +const u64 dt_virt_base =__fix_to_virt (FIX_FDT ); +phys_addr_t dt_phys_base = dt_phys & ~(SZ_2M -1 );Do we definitely retain the high bits here? I don't have the C integer promotion rules paged into my head, but the definition in
include/linux/sizes.hmakes me suspicious, given it should have an int type:Quoted 4 lines
Quoted 2 lines
#define
SZ_2M0x00200000I will use
round_down()insteadOk.
Mark.
Mark Rutland
reviewerPATCH v3 4/11 arm64: use fixmap region for permanent FDT mapping
Quoted 3 lines
I was wondering: this code now always maps the full 4 MB, also on 64k pages, even if in the majority of cases, the actual FDT is much smaller. Do you think there is a downside to that?
The two cases I can think of as problematic are when the FDT is in the last 2M of RAM, or in a 2M region immediately before some carveout which won't use MT_NORMAL attributes.
We could solve those by only mapping however many 2M chunks are necessary, or we could keep the requirement that it fits within a naturally-aligned 2M region for now.
I don't see that this should yield significant problems otherwise.
Mark.
PATCH v3 4/11 arm64: use fixmap region for permanent FDT mapping
Quoted 4 lines · Mark Rutland
Quoted 3 lines
I was wondering: this code now always maps the full 4 MB, also on 64k pages, even if in the majority of cases, the actual FDT is much smaller. Do you think there is a downside to that?
Quoted 8 lines
The two cases I can think of as problematic are when the FDT is in the last 2M of RAM, or in a 2M region immediately before some carveout which won't use
MT_NORMALattributes.We could solve those by only mapping however many 2M chunks are necessary, or we could keep the requirement that it fits within a naturally-aligned 2M region for now.
I strongly prefer the former, since otherwise, you always need to verify the allocation explicitly, for an fdt of any size, and it turns out the logic is easily updated to move the vetting into fixmap_remap_fdt() and check the magic and size after mapping the first chunk, and set pfn_end accordingly
Quoted 3 lines
I don't see that this should yield significant problems otherwise.
Mark.
Mark Rutland
reviewerPATCH v3 4/11 arm64: use fixmap region for permanent FDT mapping
Quoted 16 lines · Ard Biesheuvel
Quoted 4 lines · Mark Rutland
Quoted 3 lines
I was wondering: this code now always maps the full 4 MB, also on 64k pages, even if in the majority of cases, the actual FDT is much smaller. Do you think there is a downside to that?
Quoted 8 lines
The two cases I can think of as problematic are when the FDT is in the last 2M of RAM, or in a 2M region immediately before some carveout which won't use
MT_NORMALattributes.We could solve those by only mapping however many 2M chunks are necessary, or we could keep the requirement that it fits within a naturally-aligned 2M region for now.
Quoted 5 lines
I strongly prefer the former, since otherwise, you always need to verify the allocation explicitly, for an fdt of any size, and it turns out the logic is easily updated to move the vetting into
fixmap_remap_fdt()and check the magic and size after mapping the first chunk, and setpfn_endaccordingly
I'm happy with that; it also decays to the old behaviour for anything that works currently.
We should probably make it clear in the booting document that the kernel should be expected to map the DTB in 2M chunks, so people know they shouldn't share any 2M chunk with a carveout.
Mark.
PATCH v3 5/11 arm64/efi: adapt to relaxed FDT placement requirements
In reply to Ard BiesheuvelWith the relaxed FDT placement requirements in place, we can change the allocation strategy used by the stub to put the FDT image higher up in memory. At the same time, reduce the minimal alignment to 8 bytes, and impose a 2 MB size limit, as per the new requirements.
--- arch/arm64/include/asm/efi.h | 8 +++----- drivers/firmware/efi/libstub/arm-stub.c | 5 ++--- drivers/firmware/efi/libstub/efistub.h | 1 - drivers/firmware/efi/libstub/fdt.c | 23 +++++++++++++---------- 4 files changed, 18 insertions(+), 19 deletions(-)
arch/arm64/include/asm/efi.h +3 -5
@@ -39,12 +39,10 @@ extern void efi_init(void);/* arch specific definitions used by the stub code */ /* -* AArch64 requires the DTB to be 8-byte aligned in the first 512MiB from -* start of kernel and may not cross a 2MiB boundary. We set alignment to -* 2MiB so we know it won't cross a 2MiB boundary. +* AArch64 requires the DTB to be 8-byte aligned and not exceed 2MiB in size. */ -#define EFI_FDT_ALIGN SZ_2M/* used by allocate_new_fdt_and_exit_boot() */ -#define MAX_FDT_OFFSET SZ_512M +#define EFI_FDT_ALIGN 8 +#define EFI_FDT_MAX_SIZE SZ_2M#define efi_call_early (f, ...) sys_table_arg->boottime->f(__VA_ARGS__)
drivers/firmware/efi/libstub/arm-stub.c +2 -3
@@ -269,9 +269,8 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table, new_fdt_addr = fdt_addr; status =allocate_new_fdt_and_exit_boot (sys_table, handle, - &new_fdt_addr, dram_base +MAX_FDT_OFFSET , - initrd_addr, initrd_size, cmdline_ptr, - fdt_addr, fdt_size); + &new_fdt_addr, initrd_addr, initrd_size, + cmdline_ptr, fdt_addr, fdt_size);/* * If all went well, we need to return the FDT address to the
drivers/firmware/efi/libstub/efistub.h +0 -1
@@ -35,7 +35,6 @@ efi_status_t update_fdt(efi_system_table_t *sys_table, void *orig_fdt, efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table, void *handle, unsigned long *new_fdt_addr, - unsigned long max_addr, u64 initrd_addr, u64 initrd_size, char *cmdline_ptr, unsigned long fdt_addr,
drivers/firmware/efi/libstub/fdt.c +13 -10
@@ -165,10 +165,6 @@ fdt_set_fail:return EFI_LOAD_ERROR ; } -#ifndef EFI_FDT_ALIGN -#define EFI_FDT_ALIGN EFI_PAGE_SIZE -#endif -/* * Allocate memory for a new FDT, then add EFI, commandline, and * initrd related fields to the FDT. This routine increases the
@@ -186,7 +182,6 @@ fdt_set_fail: efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table, void *handle, unsigned long *new_fdt_addr, - unsigned long max_addr, u64 initrd_addr, u64 initrd_size, char *cmdline_ptr, unsigned long fdt_addr,
@@ -197,6 +192,7 @@ efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table,unsigned long mmap_key;efi_memory_desc_t *memory_map, *runtime_map;unsigned long new_fdt_size; +void *fdt_alloc;efi_status_t status;int runtime_entry_count =0 ;
@@ -221,14 +217,21 @@ efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table,* will allocate a bigger buffer if this ends up being too * small, so a rough guess is OK here. */ - new_fdt_size = fdt_size +EFI_PAGE_SIZE ; + new_fdt_size = fdt_size +EFI_PAGE_SIZE +EFI_FDT_ALIGN ;while (1 ) { - status =efi_high_alloc (sys_table, new_fdt_size,EFI_FDT_ALIGN , - new_fdt_addr, max_addr); +if (new_fdt_size >EFI_FDT_MAX_SIZE ) { +pr_efi_err (sys_table,"FDT size exceeds EFI_FDT_MAX_SIZE.\n" ); + goto fail; + } + status = sys_table->boottime->allocate_pool (EFI_LOADER_DATA , + new_fdt_size, + &fdt_alloc);if (status !=EFI_SUCCESS ) {pr_efi_err (sys_table,"Unable to allocate memory for new device tree.\n" ); goto fail; } + *new_fdt_addr =round_up ((unsigned long )fdt_alloc, +EFI_FDT_ALIGN );/* * Now that we have done our final memory allocation (and free)
@@ -258,7 +261,7 @@ efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table,* to get new one that reflects the free/alloc we do * on the device tree buffer. */ -efi_free (sys_table, new_fdt_size, *new_fdt_addr); + sys_table->boottime->free_pool (&fdt_alloc); sys_table->boottime->free_pool (memory_map); new_fdt_size +=EFI_PAGE_SIZE ; }else {
@@ -316,7 +319,7 @@ fail_free_mmap: sys_table->boottime->free_pool (memory_map); fail_free_new_fdt: -efi_free (sys_table, new_fdt_size, *new_fdt_addr); + sys_table->boottime->free_pool (&fdt_alloc); fail: sys_table->boottime->free_pool (runtime_map);
Signature · 1 line
1.8.3.2
Mark Rutland
reviewerPATCH v3 5/11 arm64/efi: adapt to relaxed FDT placement requirements
Quoted 20 lines · Ard Biesheuvel
With the relaxed FDT placement requirements in place, we can change the allocation strategy used by the stub to put the FDT image higher up in memory. At the same time, reduce the minimal alignment to 8 bytes, and impose a 2 MB size limit, as per the new requirements.
Signed-off-by Ard Biesheuvel <[email protected]>--- arch/arm64/include/asm/efi.h | 8 +++----- drivers/firmware/efi/libstub/arm-stub.c | 5 ++--- drivers/firmware/efi/libstub/efistub.h | 1 - drivers/firmware/efi/libstub/fdt.c | 23 +++++++++++++---------- 4 files changed, 18 insertions(+), 19 deletions(-)arch/arm64/include/asm/efi.h +0
-0@@ -39,12 +39,10 @@ extern void efi_init(void);/* arch specific definitions used by the stub code */
arch/arm64/include/asm/efi.h extern void efi_init(void); · line 45
Quoted 10 lines
arch/arm64/include/asm/efi.h +3
-5/* -* AArch64 requires the DTB to be 8-byte aligned in the first 512MiB from -* start of kernel and may not cross a 2MiB boundary. We set alignment to -* 2MiB so we know it won't cross a 2MiB boundary. +* AArch64 requires the DTB to be 8-byte aligned and not exceed 2MiB in size. */ -#define EFI_FDT_ALIGN SZ_2M/* used by allocate_new_fdt_and_exit_boot() */ -#define MAX_FDT_OFFSET SZ_512M +#define EFI_FDT_ALIGN 8 +#define EFI_FDT_MAX_SIZE SZ_2M
Is there any way we can organise things so we can share the kernel's MAX_FDT_SIZE definition, so as to keep the stub and kernel in sync?
Otherwise, the patch looks good to me.
Mark.
Quoted 108 lines
arch/arm64/include/asm/efi.h +0
-0#define efi_call_early (f, ...) sys_table_arg->boottime->f(__VA_ARGS__)drivers/firmware/efi/libstub/arm-stub.c +2
-3@@ -269,9 +269,8 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table, new_fdt_addr = fdt_addr; status =allocate_new_fdt_and_exit_boot (sys_table, handle, - &new_fdt_addr, dram_base +MAX_FDT_OFFSET , - initrd_addr, initrd_size, cmdline_ptr, - fdt_addr, fdt_size); + &new_fdt_addr, initrd_addr, initrd_size, + cmdline_ptr, fdt_addr, fdt_size);/* * If all went well, we need to return the FDT address to the drivers/firmware/efi/libstub/efistub.h +0
-1@@ -35,7 +35,6 @@ efi_status_t update_fdt(efi_system_table_t *sys_table, void *orig_fdt, efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table, void *handle, unsigned long *new_fdt_addr, - unsigned long max_addr, u64 initrd_addr, u64 initrd_size, char *cmdline_ptr, unsigned long fdt_addr,drivers/firmware/efi/libstub/fdt.c +13
-10@@ -165,10 +165,6 @@ fdt_set_fail:return EFI_LOAD_ERROR ; } -#ifndef EFI_FDT_ALIGN -#define EFI_FDT_ALIGN EFI_PAGE_SIZE -#endif -/* * Allocate memory for a new FDT, then add EFI, commandline, and * initrd related fields to the FDT. This routine increases the @@ -186,7 +182,6 @@ fdt_set_fail: efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table, void *handle, unsigned long *new_fdt_addr, - unsigned long max_addr, u64 initrd_addr, u64 initrd_size, char *cmdline_ptr, unsigned long fdt_addr,@@ -197,6 +192,7 @@ efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table,unsigned long mmap_key;efi_memory_desc_t *memory_map, *runtime_map;unsigned long new_fdt_size; +void *fdt_alloc;efi_status_t status;int runtime_entry_count =0 ;@@ -221,14 +217,21 @@ efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table,* will allocate a bigger buffer if this ends up being too * small, so a rough guess is OK here. */ - new_fdt_size = fdt_size +EFI_PAGE_SIZE ; + new_fdt_size = fdt_size +EFI_PAGE_SIZE +EFI_FDT_ALIGN ;while (1 ) { - status =efi_high_alloc (sys_table, new_fdt_size,EFI_FDT_ALIGN , - new_fdt_addr, max_addr); +if (new_fdt_size >EFI_FDT_MAX_SIZE ) { +pr_efi_err (sys_table,"FDT size exceeds EFI_FDT_MAX_SIZE.\n" ); + goto fail; + } + status = sys_table->boottime->allocate_pool (EFI_LOADER_DATA , + new_fdt_size, + &fdt_alloc);if (status !=EFI_SUCCESS ) {pr_efi_err (sys_table,"Unable to allocate memory for new device tree.\n" ); goto fail; } + *new_fdt_addr =round_up ((unsigned long )fdt_alloc, +EFI_FDT_ALIGN );/* * Now that we have done our final memory allocation (and free) @@ -258,7 +261,7 @@ efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table,* to get new one that reflects the free/alloc we do * on the device tree buffer. */ -efi_free (sys_table, new_fdt_size, *new_fdt_addr); + sys_table->boottime->free_pool (&fdt_alloc); sys_table->boottime->free_pool (memory_map); new_fdt_size +=EFI_PAGE_SIZE ; }else {@@ -316,7 +319,7 @@ fail_free_mmap: sys_table->boottime->free_pool (memory_map); fail_free_new_fdt: -efi_free (sys_table, new_fdt_size, *new_fdt_addr); + sys_table->boottime->free_pool (&fdt_alloc); fail: sys_table->boottime->free_pool (runtime_map);Signature · 1 line
1.8.3.2
PATCH v3 5/11 arm64/efi: adapt to relaxed FDT placement requirements
Quoted 21 lines · Mark Rutland
Quoted 20 lines · Ard Biesheuvel
With the relaxed FDT placement requirements in place, we can change the allocation strategy used by the stub to put the FDT image higher up in memory. At the same time, reduce the minimal alignment to 8 bytes, and impose a 2 MB size limit, as per the new requirements.
Signed-off-by Ard Biesheuvel <[email protected]>--- arch/arm64/include/asm/efi.h | 8 +++----- drivers/firmware/efi/libstub/arm-stub.c | 5 ++--- drivers/firmware/efi/libstub/efistub.h | 1 - drivers/firmware/efi/libstub/fdt.c | 23 +++++++++++++---------- 4 files changed, 18 insertions(+), 19 deletions(-)arch/arm64/include/asm/efi.h +0
-0@@ -39,12 +39,10 @@ extern void efi_init(void);/* arch specific definitions used by the stub code */
Quoted 14 lines
arch/arm64/include/asm/efi.h extern void efi_init(void); · line 45
Quoted 10 lines
arch/arm64/include/asm/efi.h +3
-5/* -* AArch64 requires the DTB to be 8-byte aligned in the first 512MiB from -* start of kernel and may not cross a 2MiB boundary. We set alignment to -* 2MiB so we know it won't cross a 2MiB boundary. +* AArch64 requires the DTB to be 8-byte aligned and not exceed 2MiB in size. */ -#define EFI_FDT_ALIGN SZ_2M/* used by allocate_new_fdt_and_exit_boot() */ -#define MAX_FDT_OFFSET SZ_512M +#define EFI_FDT_ALIGN 8 +#define EFI_FDT_MAX_SIZE SZ_2MIs there any way we can organise things so we can share the kernel's
MAX_FDT_SIZEdefinition, so as to keep the stub and kernel in sync?
I'll put MAX_FDT_SIZE in asm/page.h (unless you can propose a better place) That way, both fixmap.h and efi.h can refer to it.
Quoted 112 lines
Otherwise, the patch looks good to me.
Mark.
Quoted 108 lines
arch/arm64/include/asm/efi.h +0
-0#define efi_call_early (f, ...) sys_table_arg->boottime->f(__VA_ARGS__)drivers/firmware/efi/libstub/arm-stub.c +2
-3@@ -269,9 +269,8 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table, new_fdt_addr = fdt_addr; status =allocate_new_fdt_and_exit_boot (sys_table, handle, - &new_fdt_addr, dram_base +MAX_FDT_OFFSET , - initrd_addr, initrd_size, cmdline_ptr, - fdt_addr, fdt_size); + &new_fdt_addr, initrd_addr, initrd_size, + cmdline_ptr, fdt_addr, fdt_size);/* * If all went well, we need to return the FDT address to the drivers/firmware/efi/libstub/efistub.h +0
-1@@ -35,7 +35,6 @@ efi_status_t update_fdt(efi_system_table_t *sys_table, void *orig_fdt, efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table, void *handle, unsigned long *new_fdt_addr, - unsigned long max_addr, u64 initrd_addr, u64 initrd_size, char *cmdline_ptr, unsigned long fdt_addr,drivers/firmware/efi/libstub/fdt.c +13
-11@@ -165,10 +165,6 @@ fdt_set_fail:return EFI_LOAD_ERROR ; } -#ifndef EFI_FDT_ALIGN -#define EFI_FDT_ALIGN EFI_PAGE_SIZE -#endif -/* * Allocate memory for a new FDT, then add EFI, commandline, and * initrd related fields to the FDT. This routine increases the @@ -186,7 +182,6 @@ fdt_set_fail: efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table, void *handle, unsigned long *new_fdt_addr, - unsigned long max_addr, u64 initrd_addr, u64 initrd_size, char *cmdline_ptr, unsigned long fdt_addr,@@ -197,6 +192,7 @@ efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table,unsigned long mmap_key;efi_memory_desc_t *memory_map, *runtime_map;unsigned long new_fdt_size; +void *fdt_alloc;efi_status_t status;int runtime_entry_count =0 ;@@ -221,14 +217,21 @@ efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table,* will allocate a bigger buffer if this ends up being too * small, so a rough guess is OK here. */ - new_fdt_size = fdt_size +EFI_PAGE_SIZE ; + new_fdt_size = fdt_size +EFI_PAGE_SIZE +EFI_FDT_ALIGN ;while (1 ) { - status =efi_high_alloc (sys_table, new_fdt_size,EFI_FDT_ALIGN , - new_fdt_addr, max_addr); +if (new_fdt_size >EFI_FDT_MAX_SIZE ) { +pr_efi_err (sys_table,"FDT size exceeds EFI_FDT_MAX_SIZE.\n" ); + goto fail; + } + status = sys_table->boottime->allocate_pool (EFI_LOADER_DATA , + new_fdt_size, + &fdt_alloc);if (status !=EFI_SUCCESS ) {pr_efi_err (sys_table,"Unable to allocate memory for new device tree.\n" ); goto fail; } + *new_fdt_addr =round_up ((unsigned long )fdt_alloc, +EFI_FDT_ALIGN );/* * Now that we have done our final memory allocation (and free) @@ -258,7 +261,7 @@ efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table,* to get new one that reflects the free/alloc we do * on the device tree buffer. */ -efi_free (sys_table, new_fdt_size, *new_fdt_addr); + sys_table->boottime->free_pool (&fdt_alloc); sys_table->boottime->free_pool (memory_map); new_fdt_size +=EFI_PAGE_SIZE ; }else {@@ -316,7 +319,7 @@ fail_free_mmap: sys_table->boottime->free_pool (memory_map); fail_free_new_fdt: -efi_free (sys_table, new_fdt_size, *new_fdt_addr); + sys_table->boottime->free_pool (&fdt_alloc); fail: sys_table->boottime->free_pool (runtime_map); --1.8.3.2
Mark Rutland
reviewerPATCH v3 5/11 arm64/efi: adapt to relaxed FDT placement requirements
Quoted 37 lines · Ard Biesheuvel
Quoted 21 lines · Mark Rutland
Quoted 20 lines · Ard Biesheuvel
With the relaxed FDT placement requirements in place, we can change the allocation strategy used by the stub to put the FDT image higher up in memory. At the same time, reduce the minimal alignment to 8 bytes, and impose a 2 MB size limit, as per the new requirements.
Signed-off-by Ard Biesheuvel <[email protected]>--- arch/arm64/include/asm/efi.h | 8 +++----- drivers/firmware/efi/libstub/arm-stub.c | 5 ++--- drivers/firmware/efi/libstub/efistub.h | 1 - drivers/firmware/efi/libstub/fdt.c | 23 +++++++++++++---------- 4 files changed, 18 insertions(+), 19 deletions(-)arch/arm64/include/asm/efi.h +0
-0@@ -39,12 +39,10 @@ extern void efi_init(void);/* arch specific definitions used by the stub code */ Quoted 14 lines
arch/arm64/include/asm/efi.h extern void efi_init(void); · line 45
Quoted 10 lines
arch/arm64/include/asm/efi.h +3
-5/* -* AArch64 requires the DTB to be 8-byte aligned in the first 512MiB from -* start of kernel and may not cross a 2MiB boundary. We set alignment to -* 2MiB so we know it won't cross a 2MiB boundary. +* AArch64 requires the DTB to be 8-byte aligned and not exceed 2MiB in size. */ -#define EFI_FDT_ALIGN SZ_2M/* used by allocate_new_fdt_and_exit_boot() */ -#define MAX_FDT_OFFSET SZ_512M +#define EFI_FDT_ALIGN 8 +#define EFI_FDT_MAX_SIZE SZ_2MIs there any way we can organise things so we can share the kernel's
MAX_FDT_SIZEdefinition, so as to keep the stub and kernel in sync?
Quoted 2 lines
I'll put
MAX_FDT_SIZEinasm/page.h(unless you can propose a better place) That way, bothfixmap.handefi.hcan refer to it.
Using page.h feels a little weird, but I can't see that a better header exists currently.
We could add asm/boot.h for things related to boot protocol that we want to have shareable with the stub, but I can't see anything else that we'd want to put in there at the moment. If there are other things we could deduplicate it would probably make sense, but it seems overkill for a single definition. :/
Mark.
PATCH v3 5/11 arm64/efi: adapt to relaxed FDT placement requirements
Quoted 41 lines · Mark Rutland
Quoted 37 lines · Ard Biesheuvel
Quoted 21 lines · Mark Rutland
Quoted 20 lines · Ard Biesheuvel
With the relaxed FDT placement requirements in place, we can change the allocation strategy used by the stub to put the FDT image higher up in memory. At the same time, reduce the minimal alignment to 8 bytes, and impose a 2 MB size limit, as per the new requirements.
Signed-off-by Ard Biesheuvel <[email protected]>--- arch/arm64/include/asm/efi.h | 8 +++----- drivers/firmware/efi/libstub/arm-stub.c | 5 ++--- drivers/firmware/efi/libstub/efistub.h | 1 - drivers/firmware/efi/libstub/fdt.c | 23 +++++++++++++---------- 4 files changed, 18 insertions(+), 19 deletions(-)arch/arm64/include/asm/efi.h +0
-0@@ -39,12 +39,10 @@ extern void efi_init(void);/* arch specific definitions used by the stub code */ Quoted 14 lines
arch/arm64/include/asm/efi.h extern void efi_init(void); · line 45
Quoted 10 lines
arch/arm64/include/asm/efi.h +3
-5/* -* AArch64 requires the DTB to be 8-byte aligned in the first 512MiB from -* start of kernel and may not cross a 2MiB boundary. We set alignment to -* 2MiB so we know it won't cross a 2MiB boundary. +* AArch64 requires the DTB to be 8-byte aligned and not exceed 2MiB in size. */ -#define EFI_FDT_ALIGN SZ_2M/* used by allocate_new_fdt_and_exit_boot() */ -#define MAX_FDT_OFFSET SZ_512M +#define EFI_FDT_ALIGN 8 +#define EFI_FDT_MAX_SIZE SZ_2MIs there any way we can organise things so we can share the kernel's
MAX_FDT_SIZEdefinition, so as to keep the stub and kernel in sync?Quoted 2 lines
I'll put
MAX_FDT_SIZEinasm/page.h(unless you can propose a better place) That way, bothfixmap.handefi.hcan refer to it.
Quoted 9 lines
Using
page.hfeels a little weird, but I can't see that a better header exists currently.We could add
asm/boot.hfor things related to boot protocol that we want to have shareable with the stub, but I can't see anything else that we'd want to put in there at the moment. If there are other things we could deduplicate it would probably make sense, but it seems overkill for a single definition. :/
I was going to add '#define MIN_FDT_ALIGN 8' to it as well, and refer to it from efi.h.
I think asm/boot.h is not so bad actually.
PATCH v3 6/11 arm64: implement our own early_init_dt_add_memory_arch()
In reply to Ard BiesheuvelOverride the __weak early_init_dt_add_memory_arch() with our own version. Initially, this version just ignores MAX_PHYS_ADDR, but later on, we will modify it so it can deal with PHYS_OFFSET not having been assigned yet.
--- arch/arm64/mm/init.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+)
arch/arm64/mm/init.c +29 -0
@@ -372,3 +372,32 @@ static int __init keepinitrd_setup(char *__unused)__setup ("keepinitrd" , keepinitrd_setup);#endif + +void __init early_init_dt_add_memory_arch (u64 base,u64 size) +{ +const u64 phys_offset =__pa (PAGE_OFFSET ); + +if (!PAGE_ALIGNED (base)) { +if (size <PAGE_SIZE - (base & ~PAGE_MASK )) { +pr_warn ("Ignoring memory block 0x%llx - 0x%llx\n" , + base, base + size); +return ; + } + size -=PAGE_SIZE - (base & ~PAGE_MASK ); + base =PAGE_ALIGN (base); + } + size &=PAGE_MASK ; + +if (base + size < phys_offset) { +pr_warning ("Ignoring memory block 0x%llx - 0x%llx\n" , + base, base + size); +return ; + } +if (base < phys_offset) { +pr_warning ("Ignoring memory range 0x%llx - 0x%llx\n" , + base, phys_offset); + size -= phys_offset - base; + base = phys_offset; + } +memblock_add (base, size); +}
Signature · 1 line
1.8.3.2
PATCH v3 7/11 arm64: fixmap: allow init before linear mapping is set up
In reply to Ard BiesheuvelThis reworks early_ioremap_init() so it populates the various levels of translation tables while taking the following into account:
- be prepared for any of the levels to have been populated already, as this may be the case once we move the kernel text mapping out of the linear mapping;
- don't rely on __va() to translate the physical address in a page table entry to a virtual address, since this produces linear mapping addresses; instead, use the fact that at any level, we know exactly which page in swapper_pg_dir an entry could be pointing to if it points anywhere.
This allows us to defer the initialization of the linear mapping until after we have figured out where our RAM resides in the physical address space.
--- arch/arm64/include/asm/compiler.h | 2 + arch/arm64/kernel/vmlinux.lds.S | 14 +++-- arch/arm64/mm/mmu.c | 117 +++++++++++++++++++++++++------------- 3 files changed, 90 insertions(+), 43 deletions(-)
arch/arm64/include/asm/compiler.h +2 -0
@@ -27,4 +27,6 @@*/ #define __asmeq (x, y) ".ifnc " x "," y " ; .err ; .endif\n\t" +#define __pgdir __attribute__((section(".pgdir"),aligned(PAGE_SIZE))) +#endif /* __ASM_COMPILER_H */
arch/arm64/kernel/vmlinux.lds.S +9 -5
@@ -160,11 +160,15 @@ SECTIONS BSS_SECTION(0, 0, 0) - . = ALIGN(PAGE_SIZE); - idmap_pg_dir = .; - . += IDMAP_DIR_SIZE; - swapper_pg_dir = .; - . += SWAPPER_DIR_SIZE; + .pgdir (NOLOAD) : { + . = ALIGN(PAGE_SIZE); + idmap_pg_dir = .; + . += IDMAP_DIR_SIZE; + swapper_pg_dir = .; + __swapper_bs_region = . + PAGE_SIZE; + . += SWAPPER_DIR_SIZE; + *(.pgdir) + } _end = .;
arch/arm64/mm/mmu.c +79 -38
143 lines not on this page · raw message
Signature · 1 line
1.8.3.2
Mark Rutland
reviewerPATCH v3 7/11 arm64: fixmap: allow init before linear mapping is set up
Hi Ard,
Quoted 9 lines · Ard Biesheuvel
This reworks
early_ioremap_init()so it populates the various levels of translation tables while taking the following into account:
- be prepared for any of the levels to have been populated already, as this may be the case once we move the kernel text mapping out of the linear mapping;
- don't rely on__va()to translate the physical address in a page table entry to a virtual address, since this produces linear mapping addresses; instead, use the fact that at any level, we know exactly which page inswapper_pg_diran entry could be pointing to if it points anywhere.
Can we not use Catalin's PHYS_OFFSET swizzling trick instead? i.e.
* Set PHYS_OFFSET so __va hits in the text mapping.
* Create the fixmap entries.
* Parse the DTB or UEFI memory map, figure out the real PHYS_OFFSET.
* Create linear mapping for the initial tables.
* Swap PHYS_OFFSET for the real version, and update init_mm->pgd to point at the linear map alias of the swapper pgd.
It seemed like that would require less open-coding of table manipulation code, as we could use __va() early.
Is there a limitation with that approach that I'm missing?
Otherwise, comments below.
Quoted 52 lines
This allows us to defer the initialization of the linear mapping until after we have figured out where our RAM resides in the physical address space.
Signed-off-by Ard Biesheuvel <[email protected]>--- arch/arm64/include/asm/compiler.h | 2 + arch/arm64/kernel/vmlinux.lds.S | 14 +++-- arch/arm64/mm/mmu.c | 117 +++++++++++++++++++++++++------------- 3 files changed, 90 insertions(+), 43 deletions(-)arch/arm64/include/asm/compiler.h +2
-0@@ -27,4 +27,6 @@*/ #define __asmeq (x, y) ".ifnc " x "," y " ; .err ; .endif\n\t" +#define __pgdir __attribute__((section(".pgdir"),aligned(PAGE_SIZE))) +#endif /* __ASM_COMPILER_H */ arch/arm64/kernel/vmlinux.lds.S +9
-5@@ -160,11 +160,15 @@ SECTIONS BSS_SECTION(0, 0, 0) - . = ALIGN(PAGE_SIZE); - idmap_pg_dir = .; - . += IDMAP_DIR_SIZE; - swapper_pg_dir = .; - . += SWAPPER_DIR_SIZE; + .pgdir (NOLOAD) : { + . = ALIGN(PAGE_SIZE); + idmap_pg_dir = .; + . += IDMAP_DIR_SIZE; + swapper_pg_dir = .; + __swapper_bs_region = . + PAGE_SIZE; + . += SWAPPER_DIR_SIZE; + *(.pgdir) + } _end = .;arch/arm64/mm/mmu.c +0
-0
arch/arm64/mm/mmu.c static void __init __map_memblock(phys_addr_t start, phys_addr_t end) · line 344
Quoted 4 lines
arch/arm64/mm/mmu.c +1
-0@@ -341,6 +341,70 @@ static void __init __map_memblock(phys_addr_t start, phys_addr_t end) }#endif +struct mem_bootstrap_region {
The "region" naming is a little confusing IMO. To me it sounds like something akin to a VMA rather than a set of tables.
Quoted 23 lines
arch/arm64/mm/mmu.c +23
-0+#if CONFIG_ARM64_PGTABLE_LEVELS >3 +pud_t pud[PTRS_PER_PUD ]; +#endif +#if CONFIG_ARM64_PGTABLE_LEVELS >2 +pmd_t pmd[PTRS_PER_PMD ]; +#endif +pte_t pte[PTRS_PER_PTE ]; +}; + +static void __init bootstrap_mem_region (unsigned long addr, +struct mem_bootstrap_region *reg, +pmd_t **ppmd,pte_t **ppte) +{ +/* +* Avoid using the linear phys-to-virt translation __va() so that we +* can use this code before the linear mapping is set up. Note that +* any populated entries at any level can only point into swapper_pg_dir +* since no other translation table pages have been allocated yet. +* So at each level, we either need to populate it, or it has already +* been populated by a swapper_pg_dir table at the same level, in which +* case we can figure out its virtual address without applying __va() +* on the contents of the entry, using the following struct. +*/
arch/arm64/mm/mmu.c static void __init __map_memblock(phys_addr_t start, phys_addr_t end) · line 377
Quoted 10 lines
arch/arm64/mm/mmu.c +10
-0+extern struct mem_bootstrap_region __swapper_bs_region; + +pgd_t *pgd =pgd_offset_k (addr); +pud_t *pud = (pud_t *)pgd; +pmd_t *pmd = (pmd_t *)pud; + +#if CONFIG_ARM64_PGTABLE_LEVELS >3 +if (pgd_none (*pgd)) { +clear_page (reg->pud); +pgd_populate (&init_mm, pgd, reg->pud);
What's PHYS_OFFSET expected to be at this point (for the purposes of __pa() in the *_populate*() macros)?
If we're relying on __pa() to convert text->phys, won't __va() convert phys->text at this point?
arch/arm64/mm/mmu.c static void __init __map_memblock(phys_addr_t start, phys_addr_t end) · line 383
Quoted 6 lines
arch/arm64/mm/mmu.c +6
-0+ pud = reg->pud; + }else { + pud = __swapper_bs_region.pud; + } + pud +=pud_index (addr); +#endif
Can we free the unused reg tables in the else cases? If __pa() works we should be able to hand them to memblock, no?
[...]
arch/arm64/mm/mmu.c void vmemmap_free(unsigned long start, unsigned long end) · line 649
Quoted 4 lines
arch/arm64/mm/mmu.c +1
-1/* * The boot-ioremap range spans multiple pmds, for which -* we are not preparted: +* we are not prepared:
This typo has been bugging me for ages. I'll be glad to see it go!
Mark.
PATCH v3 7/11 arm64: fixmap: allow init before linear mapping is set up
Quoted 23 lines · Mark Rutland
Hi Ard,
Quoted 9 lines · Ard Biesheuvel
This reworks
early_ioremap_init()so it populates the various levels of translation tables while taking the following into account:
- be prepared for any of the levels to have been populated already, as this may be the case once we move the kernel text mapping out of the linear mapping;
- don't rely on__va()to translate the physical address in a page table entry to a virtual address, since this produces linear mapping addresses; instead, use the fact that at any level, we know exactly which page inswapper_pg_diran entry could be pointing to if it points anywhere.Can we not use Catalin's
PHYS_OFFSETswizzling trick instead? i.e.* Set
PHYS_OFFSETso__vahits in the text mapping.* Create the fixmap entries.
* Parse the DTB or UEFI memory map, figure out the real
PHYS_OFFSET.* Create linear mapping for the initial tables.
Quoted 8 lines
* Swap
PHYS_OFFSETfor the real version, and updateinit_mm->pgdto point at the linear map alias of the swapper pgd.It seemed like that would require less open-coding of table manipulation code, as we could use
__va()early.Is there a limitation with that approach that I'm missing?
I didn't quite catch Catalin's suggestion to mean the above, but the way you put it seems viable to me. I'll have a go and see how far I get with it.
Quoted 57 lines
Otherwise, comments below.
Quoted 55 lines
This allows us to defer the initialization of the linear mapping until after we have figured out where our RAM resides in the physical address space.
Signed-off-by Ard Biesheuvel <[email protected]>--- arch/arm64/include/asm/compiler.h | 2 + arch/arm64/kernel/vmlinux.lds.S | 14 +++-- arch/arm64/mm/mmu.c | 117 +++++++++++++++++++++++++------------- 3 files changed, 90 insertions(+), 43 deletions(-)arch/arm64/include/asm/compiler.h +2
-0@@ -27,4 +27,6 @@*/ #define __asmeq (x, y) ".ifnc " x "," y " ; .err ; .endif\n\t" +#define __pgdir __attribute__((section(".pgdir"),aligned(PAGE_SIZE))) +#endif /* __ASM_COMPILER_H */ arch/arm64/kernel/vmlinux.lds.S +9
-5@@ -160,11 +160,15 @@ SECTIONS BSS_SECTION(0, 0, 0) - . = ALIGN(PAGE_SIZE); - idmap_pg_dir = .; - . += IDMAP_DIR_SIZE; - swapper_pg_dir = .; - . += SWAPPER_DIR_SIZE; + .pgdir (NOLOAD) : { + . = ALIGN(PAGE_SIZE); + idmap_pg_dir = .; + . += IDMAP_DIR_SIZE; + swapper_pg_dir = .; + __swapper_bs_region = . + PAGE_SIZE; + . += SWAPPER_DIR_SIZE; + *(.pgdir) + } _end = .;arch/arm64/mm/mmu.c +0
-0@@ -341,6 +341,70 @@ static void __init __map_memblock(phys_addr_t start, phys_addr_t end) }#endif
Quoted 5 lines
arch/arm64/mm/mmu.c static void __init __map_memblock(phys_addr_t start, phys_addr_t end) · line 344
Quoted 1 line
arch/arm64/mm/mmu.c +1
-0+struct mem_bootstrap_region {The "region" naming is a little confusing IMO. To me it sounds like something akin to a VMA rather than a set of tables.
ok
Quoted 37 lines
Quoted 23 lines
arch/arm64/mm/mmu.c +23
-0+#if CONFIG_ARM64_PGTABLE_LEVELS >3 +pud_t pud[PTRS_PER_PUD ]; +#endif +#if CONFIG_ARM64_PGTABLE_LEVELS >2 +pmd_t pmd[PTRS_PER_PMD ]; +#endif +pte_t pte[PTRS_PER_PTE ]; +}; + +static void __init bootstrap_mem_region (unsigned long addr, +struct mem_bootstrap_region *reg, +pmd_t **ppmd,pte_t **ppte) +{ +/* +* Avoid using the linear phys-to-virt translation __va() so that we +* can use this code before the linear mapping is set up. Note that +* any populated entries at any level can only point into swapper_pg_dir +* since no other translation table pages have been allocated yet. +* So at each level, we either need to populate it, or it has already +* been populated by a swapper_pg_dir table at the same level, in which +* case we can figure out its virtual address without applying __va() +* on the contents of the entry, using the following struct. +*/ arch/arm64/mm/mmu.c static void __init __map_memblock(phys_addr_t start, phys_addr_t end) · line 377
Quoted 10 lines
arch/arm64/mm/mmu.c +10
-0+extern struct mem_bootstrap_region __swapper_bs_region; + +pgd_t *pgd =pgd_offset_k (addr); +pud_t *pud = (pud_t *)pgd; +pmd_t *pmd = (pmd_t *)pud; + +#if CONFIG_ARM64_PGTABLE_LEVELS >3 +if (pgd_none (*pgd)) { +clear_page (reg->pud); +pgd_populate (&init_mm, pgd, reg->pud);What's
PHYS_OFFSETexpected to be at this point (for the purposes of__pa()in the *_populate*() macros)?
Quoted 3 lines
If we're relying on
__pa()to converttext->phys, won't__va()convertphys->textat this point?
At this points, yes. But later on, when the kernel text moves out of the linear region, __pa() takes into account whether the input VA is above or below PAGE_OFFSET, and adds the kernel image offset in the latter case.
Changing __va() so it implements the inverse would be a can of worms i'd rather keep closed.
Quoted 10 lines
arch/arm64/mm/mmu.c static void __init __map_memblock(phys_addr_t start, phys_addr_t end) · line 383
Quoted 6 lines
arch/arm64/mm/mmu.c +6
-0+ pud = reg->pud; + }else { + pud = __swapper_bs_region.pud; + } + pud +=pud_index (addr); +#endif Can we free the unused reg tables in the else cases? If
__pa()works we should be able to hand them to memblock, no?
Only if we put the memblock_reserve() of the kernel image before early_fixmap_init(), otherwise we are freeing only to reserve it again later.
Quoted 9 lines
[...]
arch/arm64/mm/mmu.c void vmemmap_free(unsigned long start, unsigned long end) · line 649
Quoted 4 lines
/*
* The boot-ioremap range spans multiple pmds, for whicharch/arm64/mm/mmu.c +1
-1-* we are not preparted: +* we are not prepared: This typo has been bugging me for ages. I'll be glad to see it go!
:-)
Mark Rutland
reviewerPATCH v3 7/11 arm64: fixmap: allow init before linear mapping is set up
Quoted 30 lines · Ard Biesheuvel
Quoted 29 lines · Mark Rutland
Hi Ard,
Quoted 9 lines · Ard Biesheuvel
This reworks
early_ioremap_init()so it populates the various levels of translation tables while taking the following into account:
- be prepared for any of the levels to have been populated already, as this may be the case once we move the kernel text mapping out of the linear mapping;
- don't rely on__va()to translate the physical address in a page table entry to a virtual address, since this produces linear mapping addresses; instead, use the fact that at any level, we know exactly which page inswapper_pg_diran entry could be pointing to if it points anywhere.Can we not use Catalin's
PHYS_OFFSETswizzling trick instead? i.e.* Set
PHYS_OFFSETso__vahits in the text mapping.* Create the fixmap entries.
* Parse the DTB or UEFI memory map, figure out the real
PHYS_OFFSET.* Create linear mapping for the initial tables.
* Swap
PHYS_OFFSETfor the real version, and updateinit_mm->pgdto point at the linear map alias of the swapper pgd.It seemed like that would require less open-coding of table manipulation code, as we could use
__va()early.
Quoted 6 lines
Quoted 2 lines
Is there a limitation with that approach that I'm missing?
I didn't quite catch Catalin's suggestion to mean the above, but the way you put it seems viable to me. I'll have a go and see how far I get with it.
We discussed it (and wrote it up) on the plane back from the FW summit, and it may have made more sense in person than it did on the list; I've only skimmed Catalin's responses. ;)
Quoted 37 lines
Quoted 37 lines
Quoted 23 lines
arch/arm64/mm/mmu.c +23
-0+#if CONFIG_ARM64_PGTABLE_LEVELS >3 +pud_t pud[PTRS_PER_PUD ]; +#endif +#if CONFIG_ARM64_PGTABLE_LEVELS >2 +pmd_t pmd[PTRS_PER_PMD ]; +#endif +pte_t pte[PTRS_PER_PTE ]; +}; + +static void __init bootstrap_mem_region (unsigned long addr, +struct mem_bootstrap_region *reg, +pmd_t **ppmd,pte_t **ppte) +{ +/* +* Avoid using the linear phys-to-virt translation __va() so that we +* can use this code before the linear mapping is set up. Note that +* any populated entries at any level can only point into swapper_pg_dir +* since no other translation table pages have been allocated yet. +* So at each level, we either need to populate it, or it has already +* been populated by a swapper_pg_dir table at the same level, in which +* case we can figure out its virtual address without applying __va() +* on the contents of the entry, using the following struct. +*/ arch/arm64/mm/mmu.c static void __init __map_memblock(phys_addr_t start, phys_addr_t end) · line 377
Quoted 10 lines
arch/arm64/mm/mmu.c +10
-0+extern struct mem_bootstrap_region __swapper_bs_region; + +pgd_t *pgd =pgd_offset_k (addr); +pud_t *pud = (pud_t *)pgd; +pmd_t *pmd = (pmd_t *)pud; + +#if CONFIG_ARM64_PGTABLE_LEVELS >3 +if (pgd_none (*pgd)) { +clear_page (reg->pud); +pgd_populate (&init_mm, pgd, reg->pud);What's
PHYS_OFFSETexpected to be at this point (for the purposes of__pa()in the *_populate*() macros)?
Quoted 8 lines
Quoted 3 lines
If we're relying on
__pa()to converttext->phys, won't__va()convertphys->textat this point?At this points, yes. But later on, when the kernel text moves out of the linear region,
__pa()takes into account whether the input VA is above or belowPAGE_OFFSET, and adds the kernel image offset in the latter case.
Ah, I see.
Quoted 2 lines
Changing
__va()so it implements the inverse would be a can of worms i'd rather keep closed.
I completely agree.
Quoted 7 lines
Quoted 7 lines
Quoted 6 lines
arch/arm64/mm/mmu.c +6
-0+ pud = reg->pud; + }else { + pud = __swapper_bs_region.pud; + } + pud +=pud_index (addr); +#endif
Quoted 7 lines
Quoted 3 lines
Can we free the unused reg tables in the else cases? If
__pa()works we should be able to hand them to memblock, no?Only if we put the
memblock_reserve()of the kernel image beforeearly_fixmap_init(), otherwise we are freeing only to reserve it again later.
Damn. That gets really painful with the memory limit (which does a memblock_remove), early_param, and so on. There's horrible ordering dependencies between those.
We could get around that with an early page allocator. Have each user (just fixmap and linear map init?) place an upper bound on the pages they need into .pgtbl.pool, have them allocate from there as needed, and immediately after the memblock_reserve of the kernel, unreserve (remove
+ add) any remaining pages.
Mark.
PATCH v3 8/11 arm64: mm: explicitly bootstrap the linear mapping
In reply to Ard BiesheuvelIn preparation of moving the kernel text out of the linear mapping, ensure that the part of the kernel Image that contains the statically allocated page tables is made accessible via the linear mapping before performing the actual mapping of all of memory. This is needed by the normal mapping routines, that rely on the linear mapping to walk the page tables while manipulating them.
--- arch/arm64/kernel/vmlinux.lds.S | 10 +++- arch/arm64/mm/mmu.c | 104 ++++++++++++++++++++++++++++------------ 2 files changed, 81 insertions(+), 33 deletions(-)
arch/arm64/kernel/vmlinux.lds.S +8 -2
@@ -160,8 +160,7 @@ SECTIONS BSS_SECTION(0, 0, 0) - .pgdir (NOLOAD) : { - . = ALIGN(PAGE_SIZE); + .pgdir (NOLOAD) : ALIGN(SZ_1M) { idmap_pg_dir = .; . += IDMAP_DIR_SIZE; swapper_pg_dir = .;
@@ -187,6 +186,13 @@ ASSERT(__idmap_text_end - (__idmap_text_start & ~(SZ_4K - 1)) <= SZ_4K, "ID map text too big or misaligned") /* + * The pgdir region needs to be mappable using a single PMD or PUD sized region, + * so it should not cross a 512 MB or 1 GB alignment boundary, respectively + * (depending on page size). So align to an upper bound of its size. + */ +ASSERT(SIZEOF(.pgdir) < ALIGNOF(.pgdir), ".pgdir size exceeds its alignment") + +/* * If padding is applied before .head.text, virt<->phys conversions will fail. */ ASSERT(_text == (PAGE_OFFSET + TEXT_OFFSET), "HEAD is misaligned")
arch/arm64/mm/mmu.c +73 -31
120 lines not on this page · raw message
Signature · 1 line
1.8.3.2
PATCH v3 9/11 arm64: move kernel mapping out of linear region
In reply to Ard BiesheuvelThis moves the primary mapping of the kernel Image out of the linear region. This is a preparatory step towards allowing the kernel Image to reside anywhere in physical memory without affecting the ability to map all of it efficiently.
--- arch/arm64/include/asm/memory.h | 15 ++++++++++++--- arch/arm64/kernel/head.S | 8 ++++---- arch/arm64/kernel/vmlinux.lds.S | 4 ++-- arch/arm64/mm/mmu.c | 2 ++ 4 files changed, 20 insertions(+), 9 deletions(-)
arch/arm64/include/asm/memory.h +12 -3
@@ -38,8 +38,10 @@*/ #define PCI_IO_SIZE SZ_16M +#define KIMAGE_OFFSET SZ_64M +/* -* PAGE_OFFSET - the virtual address of the start of the kernel image (top +* PAGE_OFFSET - the virtual address of the base of the linear mapping (top * (VA_BITS - 1)) * VA_BITS - the maximum number of bits for virtual addresses. * TASK_SIZE - the maximum size of a user space task.
@@ -49,7 +51,8 @@*/ #define VA_BITS (CONFIG_ARM64_VA_BITS)#define PAGE_OFFSET (UL(0xffffffffffffffff) << (VA_BITS - 1)) -#define MODULES_END (PAGE_OFFSET) +#define KIMAGE_VADDR (PAGE_OFFSET - KIMAGE_OFFSET) +#define MODULES_END KIMAGE_VADDR#define MODULES_VADDR (MODULES_END - SZ_64M)#define PCI_IO_END (MODULES_VADDR - SZ_2M)#define PCI_IO_START (PCI_IO_END - PCI_IO_SIZE)
@@ -77,7 +80,11 @@* private definitions which should NOT be used outside memory.h * files. Use virt_to_phys/phys_to_virt/__pa/__va instead. */ -#define __virt_to_phys (x) (((phys_addr_t)(x) - PAGE_OFFSET + PHYS_OFFSET)) +#define __virt_to_phys (x) ({ \ + long __x = (long)(x) - PAGE_OFFSET; \ + __x >= 0 ? (phys_addr_t)(__x + PHYS_OFFSET) : \ + (phys_addr_t)(__x + PHYS_OFFSET + image_offset); }) +#define __phys_to_virt (x) ((unsigned long)((x) - PHYS_OFFSET + PAGE_OFFSET))/*
@@ -113,6 +120,8 @@ extern phys_addr_t memstart_addr;/* PHYS_OFFSET - the physical address of the start of memory. */ #define PHYS_OFFSET ({ memstart_addr; }) +extern u64 image_offset; +/* * PFNs are used to describe any physical page; this means * PFN 0 == physical address 0.
arch/arm64/kernel/head.S +4 -4
@@ -36,8 +36,6 @@ #include <asm/page.h> #include <asm/virt.h> -#define __PHYS_OFFSET (KERNEL_START - TEXT_OFFSET) - #if (TEXT_OFFSET & 0xfff) != 0 #error TEXT_OFFSET must be at least 4KB aligned #elif (PAGE_OFFSET & 0x1fffff) != 0
@@ -58,6 +56,8 @@ #define KERNEL_START _text #define KERNEL_END _end +#define KERNEL_BASE (KERNEL_START - TEXT_OFFSET) + /* * Initial memory map attributes.
@@ -235,7 +235,7 @@ section_table: ENTRY(stext) bl preserve_boot_args bl el2_setup // Drop to EL1, w20=cpu_boot_mode - adrp x24, __PHYS_OFFSET + adrp x24, KERNEL_BASE bl set_cpu_boot_mode_flag bl __create_page_tables // x25=TTBR0, x26=TTBR1 /*
@@ -411,7 +411,7 @@ __create_page_tables: * Map the kernel image (starting with PHYS_OFFSET). */ mov x0, x26 // swapper_pg_dir - mov x5, #PAGE_OFFSET + ldr x5, =KERNEL_BASE create_pgd_entry x0, x5, x3, x6 ldr x6, =KERNEL_END // __va(KERNEL_END) mov x3, x24 // phys offset
arch/arm64/kernel/vmlinux.lds.S +2 -2
@@ -84,7 +84,7 @@ SECTIONS *(.discard.*) } - . = PAGE_OFFSET + TEXT_OFFSET; + . = KIMAGE_VADDR + TEXT_OFFSET; .head.text : { _text = .;
@@ -195,4 +195,4 @@ ASSERT(SIZEOF(.pgdir) < ALIGNOF(.pgdir), ".pgdir size exceeds its alignment") /* * If padding is applied before .head.text, virt<->phys conversions will fail. */ -ASSERT(_text == (PAGE_OFFSET + TEXT_OFFSET), "HEAD is misaligned") +ASSERT(_text == (KIMAGE_VADDR + TEXT_OFFSET), "HEAD is misaligned")
arch/arm64/mm/mmu.c +2 -0
@@ -49,6 +49,8 @@ u64 idmap_t0sz = TCR_T0SZ(VA_BITS);struct page *empty_zero_page;EXPORT_SYMBOL (empty_zero_page); +u64 image_offset__read_mostly =KIMAGE_OFFSET ; +pgprot_t phys_mem_access_prot (struct file *file,unsigned long pfn,unsigned long size,pgprot_t vma_prot) {
Signature · 1 line
1.8.3.2
PATCH v3 10/11 arm64: allow kernel Image to be loaded anywhere in physical memory
In reply to Ard BiesheuvelThis relaxes the kernel Image placement requirements, so that it may be placed at any 2 MB aligned offset in physical memory.
This is accomplished by ignoring PHYS_OFFSET when installing memblocks, and accounting for the apparent virtual offset of the kernel Image (in addition to the 64 MB that is is moved below PAGE_OFFSET). As a result, virtual address references below PAGE_OFFSET are correctly mapped onto physical references into the kernel Image regardless of where it sits in memory.
--- Documentation/arm64/booting.txt | 17 +++++++---------- arch/arm64/mm/init.c | 32 +++++++++++++++++++------------- 2 files changed, 26 insertions(+), 23 deletions(-)
Documentation/arm64/booting.txt +7 -10
@@ -110,16 +110,13 @@ Header notes: depending on selected features, and is effectively unbound. The Image must be placed text_offset bytes from a 2MB aligned base -address near the start of usable system RAM and called there. Memory -below that base address is currently unusable by Linux, and therefore it -is strongly recommended that this location is the start of system RAM. -At least image_size bytes from the start of the image must be free for -use by the kernel. - -Any memory described to the kernel (even that below the 2MB aligned base -address) which is not marked as reserved from the kernel e.g. with a -memreserve region in the device tree) will be considered as available to -the kernel. +address anywhere in usable system RAM and called there. At least +image_size bytes from the start of the image must be free for use +by the kernel. + +Any memory described to the kernel which is not marked as reserved from +the kernel e.g. with a memreserve region in the device tree) will be +considered as available to the kernel. Before jumping into the kernel, the following conditions must be met:
arch/arm64/mm/init.c +19 -13
@@ -375,8 +375,6 @@ __setup("keepinitrd", keepinitrd_setup);void __init early_init_dt_add_memory_arch (u64 base,u64 size) { -const u64 phys_offset =__pa (PAGE_OFFSET ); -if (!PAGE_ALIGNED (base)) {if (size <PAGE_SIZE - (base & ~PAGE_MASK )) { pr_warn("Ignoring memory block 0x%llx - 0x%llx\n",
@@ -388,16 +386,24 @@ void __init early_init_dt_add_memory_arch(u64 base, u64 size) } size &=PAGE_MASK ; -if (base + size < phys_offset) { -pr_warning ("Ignoring memory block 0x%llx - 0x%llx\n" , - base, base + size); -return ; - } -if (base < phys_offset) { -pr_warning ("Ignoring memory range 0x%llx - 0x%llx\n" , - base, phys_offset); - size -= phys_offset - base; - base = phys_offset; - }memblock_add (base, size); + +/* +* Set memstart_addr to the base of the lowest physical memory region, +* rounded down to PUD/PMD alignment so we can map it efficiently. +* Since this also affects the apparent offset of the kernel image in +* the virtual address space, increase image_offset by the same amount +* that we decrease memstart_addr. +*/ +if (!memstart_addr || memstart_addr > base) { +u64 new_memstart_addr; + +if (IS_ENABLED (CONFIG_ARM64_64K_PAGES )) + new_memstart_addr = base &PMD_MASK ; +else + new_memstart_addr = base &PUD_MASK ; + + image_offset += memstart_addr - new_memstart_addr; + memstart_addr = new_memstart_addr; + } }
Signature · 1 line
1.8.3.2
Mark Rutland
reviewerPATCH v3 10/11 arm64: allow kernel Image to be loaded anywhere in physical memory
Quoted 31 lines · Ard Biesheuvel
This relaxes the kernel Image placement requirements, so that it may be placed at any 2 MB aligned offset in physical memory.
This is accomplished by ignoring
PHYS_OFFSETwhen installing memblocks, and accounting for the apparent virtual offset of the kernel Image (in addition to the 64 MB that is is moved belowPAGE_OFFSET). As a result, virtual address references belowPAGE_OFFSETare correctly mapped onto physical references into the kernel Image regardless of where it sits in memory.Signed-off-by Ard Biesheuvel <[email protected]>--- Documentation/arm64/booting.txt | 17 +++++++---------- arch/arm64/mm/init.c | 32 +++++++++++++++++++------------- 2 files changed, 26 insertions(+), 23 deletions(-)Documentation/arm64/booting.txt +0
-7@@ -110,16 +110,13 @@ Header notes: depending on selected features, and is effectively unbound. The Image must be placed text_offset bytes from a 2MB aligned base -address near the start of usable system RAM and called there. Memory -below that base address is currently unusable by Linux, and therefore it -is strongly recommended that this location is the start of system RAM. -At least image_size bytes from the start of the image must be free for -use by the kernel. - -Any memory described to the kernel (even that below the 2MB aligned base
Documentation/arm64/booting.txt Header notes: · line 119
Quoted 10 lines
Documentation/arm64/booting.txt +7
-3-address) which is not marked as reserved from the kernel e.g. with a -memreserve region in the device tree) will be considered as available to -the kernel. +address anywhere in usable system RAM and called there. At least +image_size bytes from the start of the image must be free for use +by the kernel. + +Any memory described to the kernel which is not marked as reserved from +the kernel e.g. with a memreserve region in the device tree) will be +considered as available to the kernel.
As with the other docs changes we'll need a note w.r.t. the behaviour older kernels. This might also be worth a feature bitmap bit so loaders can do the best thing for the given kernel version.
Quoted 8 lines
Documentation/arm64/booting.txt +8
-0+ /* + * Set memstart_addr to the base of the lowest physical memory region, + * rounded down to PUD/PMD alignment so we can map it efficiently. + * Since this also affects the apparent offset of the kernel image in + * the virtual address space, increase image_offset by the same amount + * that we decrease memstart_addr. + */ + if (!memstart_addr || memstart_addr > base) {
Documentation/arm64/booting.txt Header notes:
Quoted 10 lines
Documentation/arm64/booting.txt +10
-0+ u64 new_memstart_addr; + + if (IS_ENABLED(CONFIG_ARM64_64K_PAGES)) + new_memstart_addr = base & PMD_MASK; + else + new_memstart_addr = base & PUD_MASK; + + image_offset += memstart_addr - new_memstart_addr; + memstart_addr = new_memstart_addr; + }
There's one slight snag with this. Given sufficient memory (e.g. more than 512GB) and a sufficiently small VA size (e.g 39 bit), if the kernel is loaded at the end of RAM we might not cover it in the linear mapping.
It would be nice if we could detect that and warn/stop if possible (earlycon should be up by this point), rather than blowing up in strange ways. The other option is to try to limit the memstart_addr such that we know that we can map the kernel text (removing the unusable memory).
Mark.
PATCH v3 11/11 arm64/efi: adapt to relaxed kernel Image placement requirements
In reply to Ard BiesheuvelThis adapts the EFI stub kernel placement to the new relaxed requirements, by placing the kernel Image at the highest available 2 MB offset in physical memory.
--- arch/arm64/kernel/efi-stub.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
arch/arm64/kernel/efi-stub.c +2 -2
@@ -28,8 +28,8 @@ efi_status_t __init handle_kernel_image(efi_system_table_t *sys_table, kernel_size = _edata - _text;if (*image_addr != (dram_base +TEXT_OFFSET )) { kernel_memsize = kernel_size + (_end - _edata); - status =efi_low_alloc (sys_table, kernel_memsize +TEXT_OFFSET , -SZ_2M , reserve_addr); + status =efi_high_alloc (sys_table, kernel_memsize +TEXT_OFFSET , +SZ_2M , reserve_addr,ULONG_MAX );if (status !=EFI_SUCCESS ) {pr_efi_err (sys_table,"Failed to relocate kernel\n" );return status;
Signature · 1 line
1.8.3.2