From e34ecf9d5ef15590c4681c651d508097ebf2b026 Mon Sep 17 00:00:00 2001 From: Ilias Apalodimas Date: Thu, 20 Feb 2025 15:54:38 +0200 Subject: meminfo: add memory details for armv8 Upcoming patches are mapping memory with RO, RW^X etc permsissions. Fix the meminfo command to display them properly Acked-by: Jerome Forissier Reviewed-by: Caleb Connolly Signed-off-by: Ilias Apalodimas --- arch/arm/cpu/armv8/cache_v8.c | 26 +++++++++++++++++++++++--- arch/arm/include/asm/armv8/mmu.h | 2 ++ cmd/meminfo.c | 6 ++++++ 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/arch/arm/cpu/armv8/cache_v8.c b/arch/arm/cpu/armv8/cache_v8.c index 5d6953ffedd..c4b3da4a8da 100644 --- a/arch/arm/cpu/armv8/cache_v8.c +++ b/arch/arm/cpu/armv8/cache_v8.c @@ -421,7 +421,7 @@ static int count_ranges(void) return count; } -#define ALL_ATTRS (3 << 8 | PMD_ATTRINDX_MASK) +#define ALL_ATTRS (3 << 8 | PMD_ATTRMASK) #define PTE_IS_TABLE(pte, level) (pte_type(&(pte)) == PTE_TYPE_TABLE && (level) < 3) enum walker_state { @@ -568,6 +568,20 @@ static void pretty_print_table_attrs(u64 pte) static void pretty_print_block_attrs(u64 pte) { u64 attrs = pte & PMD_ATTRINDX_MASK; + u64 perm_attrs = pte & PMD_ATTRMASK; + char mem_attrs[16] = { 0 }; + int cnt = 0; + + if (perm_attrs & PTE_BLOCK_PXN) + cnt += snprintf(mem_attrs + cnt, sizeof(mem_attrs) - cnt, "PXN "); + if (perm_attrs & PTE_BLOCK_UXN) + cnt += snprintf(mem_attrs + cnt, sizeof(mem_attrs) - cnt, "UXN "); + if (perm_attrs & PTE_BLOCK_RO) + cnt += snprintf(mem_attrs + cnt, sizeof(mem_attrs) - cnt, "RO"); + if (!mem_attrs[0]) + snprintf(mem_attrs, sizeof(mem_attrs), "RWX "); + + printf(" | %-10s", mem_attrs); switch (attrs) { case PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE): @@ -613,6 +627,7 @@ static void print_pte(u64 pte, int level) { if (PTE_IS_TABLE(pte, level)) { printf(" %-5s", "Table"); + printf(" %-12s", "|"); pretty_print_table_attrs(pte); } else { pretty_print_pte_type(pte); @@ -642,9 +657,9 @@ static bool pagetable_print_entry(u64 start_attrs, u64 end, int va_bits, int lev printf("%*s", indent * 2, ""); if (PTE_IS_TABLE(start_attrs, level)) - printf("[%#011llx]%14s", _addr, ""); + printf("[%#016llx]%19s", _addr, ""); else - printf("[%#011llx - %#011llx]", _addr, end); + printf("[%#016llx - %#016llx]", _addr, end); printf("%*s | ", (3 - level) * 2, ""); print_pte(start_attrs, level); @@ -1112,3 +1127,8 @@ void __weak enable_caches(void) icache_enable(); dcache_enable(); } + +void arch_dump_mem_attrs(void) +{ + dump_pagetable(gd->arch.tlb_addr, get_tcr(NULL, NULL)); +} diff --git a/arch/arm/include/asm/armv8/mmu.h b/arch/arm/include/asm/armv8/mmu.h index 0ab681c893d..6af8cd111a4 100644 --- a/arch/arm/include/asm/armv8/mmu.h +++ b/arch/arm/include/asm/armv8/mmu.h @@ -66,6 +66,7 @@ #define PTE_BLOCK_NG (1 << 11) #define PTE_BLOCK_PXN (UL(1) << 53) #define PTE_BLOCK_UXN (UL(1) << 54) +#define PTE_BLOCK_RO (UL(1) << 7) /* * AttrIndx[2:0] @@ -75,6 +76,7 @@ #define PMD_ATTRMASK (PTE_BLOCK_PXN | \ PTE_BLOCK_UXN | \ PMD_ATTRINDX_MASK | \ + PTE_BLOCK_RO | \ PTE_TYPE_VALID) /* diff --git a/cmd/meminfo.c b/cmd/meminfo.c index 5e83d61c2dd..acdb38dcba0 100644 --- a/cmd/meminfo.c +++ b/cmd/meminfo.c @@ -15,6 +15,10 @@ DECLARE_GLOBAL_DATA_PTR; +void __weak arch_dump_mem_attrs(void) +{ +} + static void print_region(const char *name, ulong base, ulong size, ulong *uptop) { ulong end = base + size; @@ -58,6 +62,8 @@ static int do_meminfo(struct cmd_tbl *cmdtp, int flag, int argc, if (!IS_ENABLED(CONFIG_CMD_MEMINFO_MAP)) return 0; + arch_dump_mem_attrs(); + printf("\n%-12s %8s %8s %8s %8s\n", "Region", "Base", "Size", "End", "Gap"); printf("------------------------------------------------\n"); -- cgit v1.3.1 From 62b2d933cf7a486d34e379aa11076c5b62a7a239 Mon Sep 17 00:00:00 2001 From: Ilias Apalodimas Date: Thu, 20 Feb 2025 15:54:39 +0200 Subject: doc: update meminfo with arch specific information Since we added support in meminfo to dump live page tables, describe the only working architecture for now (aarch64) and add links to public documentation for further reading. Signed-off-by: Ilias Apalodimas --- doc/usage/cmd/meminfo.rst | 71 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 53 insertions(+), 18 deletions(-) diff --git a/doc/usage/cmd/meminfo.rst b/doc/usage/cmd/meminfo.rst index 6c94493cccc..e10bdc6832c 100644 --- a/doc/usage/cmd/meminfo.rst +++ b/doc/usage/cmd/meminfo.rst @@ -18,7 +18,8 @@ Description The meminfo command shows the amount of memory. If ``CONFIG_CMD_MEMINFO_MAP`` is enabled, then it also shows the layout of memory used by U-Boot and the region -which is free for use by images. +which is free for use by images. In architectures that support it, it also prints +the mapped pages and their permissions. The latter is architecture specific. The layout of memory is set up before relocation, within the init sequence in ``board_init_f()``, specifically the various ``reserve_...()`` functions. This @@ -26,8 +27,9 @@ The layout of memory is set up before relocation, within the init sequence in ending with the stack. This results in the maximum possible amount of memory being left free for image-loading. -The meminfo command writes the DRAM size, then the rest of its outputs in 5 -columns: +The meminfo command writes the DRAM size. If the architecture also supports it, +page table entries will be shown next. Finally the rest of the outputs are +printed in 5 columns: Region Name of the region @@ -99,28 +101,61 @@ free Free memory, which is available for loading images. The base address of this is ``gd->ram_base`` which is generally set by ``CFG_SYS_SDRAM_BASE``. +Aarch64 specific flags +---------------------- + +More information on the output can be found +Chapter D8 - The AArch64 Virtual Memory System Architecture at +https://developer.arm.com/documentation/ddi0487/latest/ + +In short, for a stage 1 translation regime the following apply: + +* RWX: Pages mapped with Read, Write and Execute permissions +* RO: Pages mapped with Read-Only permissions +* PXN: PXN (Privileged Execute Never) applies to execution at EL1 and above +* UXN: UXN (Unprivileged Execute Never) applies to EL0 + Example ------- This example shows output with both ``CONFIG_CMD_MEMINFO`` and -``CONFIG_CMD_MEMINFO_MAP`` enabled:: - - => meminfo - DRAM: 256 MiB +``CONFIG_CMD_MEMINFO_MAP`` enabled for aarch64 qemu:: + + DRAM: 8 GiB + Walking pagetable at 000000023ffe0000, va_bits: 40. Using 4 levels + [0x0000023ffe1000] | Table | | | + [0x0000023ffe2000] | Table | | | + [0x00000000000000 - 0x00000008000000] | Block | RWX | Normal | Inner-shareable + [0x00000008000000 - 0x00000040000000] | Block | PXN UXN | Device-nGnRnE | Non-shareable + [0x00000040000000 - 0x00000200000000] | Block | RWX | Normal | Inner-shareable + [0x0000023ffea000] | Table | | | + [0x00000200000000 - 0x0000023f600000] | Block | RWX | Normal | Inner-shareable + [0x0000023ffeb000] | Table | | | + [0x0000023f600000 - 0x0000023f68c000] | Pages | RWX | Normal | Inner-shareable + [0x0000023f68c000 - 0x0000023f74f000] | Pages | RO | Normal | Inner-shareable + [0x0000023f74f000 - 0x0000023f794000] | Pages | PXN UXN RO | Normal | Inner-shareable + [0x0000023f794000 - 0x0000023f79d000] | Pages | PXN UXN | Normal | Inner-shareable + [0x0000023f79d000 - 0x0000023f800000] | Pages | RWX | Normal | Inner-shareable + [0x0000023f800000 - 0x00000240000000] | Block | RWX | Normal | Inner-shareable + [0x00000240000000 - 0x00004000000000] | Block | RWX | Normal | Inner-shareable + [0x0000023ffe3000] | Table | | | + [0x00004010000000 - 0x00004020000000] | Block | PXN UXN | Device-nGnRnE | Non-shareable + [0x0000023ffe4000] | Table | | | + [0x00008000000000 - 0x00010000000000] | Block | PXN UXN | Device-nGnRnE | Non-shareable Region Base Size End Gap ------------------------------------------------ - video f000000 1000000 10000000 - code ec3a000 3c5d28 efffd28 2d8 - malloc 8c38000 6002000 ec3a000 0 - board_info 8c37f90 68 8c37ff8 8 - global_data 8c37d80 208 8c37f88 8 - devicetree 8c33000 4d7d 8c37d7d 3 - bootstage 8c32c20 3c8 8c32fe8 18 - bloblist 8c32000 400 8c32400 820 - stack 7c31ff0 1000000 8c31ff0 10 - free 0 7c31ff0 7c31ff0 0 - + video 23f7e0000 800000 23ffe0000 + code 23f68a000 156000 23f7e0000 0 + malloc 23e64a000 1040000 23f68a000 0 + board_info 23e649f80 78 23e649ff8 8 + global_data 23e649df0 188 23e649f78 8 + devicetree 23e549df0 100000 23e649df0 0 + bloblist 23e547000 2000 23e549000 df0 + stack 23d546ff0 1000000 23e546ff0 10 + lmb 23d546ff0 0 23d546ff0 0 + lmb 23d543000 3ff0 23d546ff0 0 + free 40000000 23d543000 27d543000 ffffffffc0000000 Return value ------------ -- cgit v1.3.1 From 1c7d0c411c033f7158dccc34c795951b667dd591 Mon Sep 17 00:00:00 2001 From: Ilias Apalodimas Date: Thu, 20 Feb 2025 15:54:40 +0200 Subject: arm: Prepare linker scripts for memory permissions Upcoming patches are switching the memory mappings to RW, RO, RX after the U-Boot binary and its data are relocated. Add annotations in the linker scripts to and mark text, data, rodata sections and align them to a page boundary. It's worth noting that .efi_runtime memory permissions are left untouched for now. There's two problems with EFI currently. The first problem is that we bundle data, rodata and text in a single .efi_runtime section which also must be close to .text for now. As a result we also dont change the permissions for anything contained in CPUDIR/start.o. In order to fix that we have to decoule .text_rest, .text and .efi_runtime and have the runtime services on their own section with proper memory permission annotations (efi_rodata etc). The efi runtime regions (.efi_runtime_rel) can be relocated by the OS when the latter is calling SetVirtualAddressMap. Which means we have to configure those pages as RX for U-Boot but convert them to RWX just before ExitBootServices. It also needs extra code in efi_tuntime relocation code since R_AARCH64_NONE are emitted as well if we page align the section. Due to the above ignore EFI for now and fix it later once we have the rest in place. Acked-by: Jerome Forissier Tested-by: Neil Armstrong # on AML-S905X-CC Reviewed-by: Richard Henderson Signed-off-by: Ilias Apalodimas --- arch/arm/cpu/armv8/u-boot.lds | 59 ++++++++++++++++++++++++++++-------------- include/asm-generic/sections.h | 2 ++ 2 files changed, 42 insertions(+), 19 deletions(-) diff --git a/arch/arm/cpu/armv8/u-boot.lds b/arch/arm/cpu/armv8/u-boot.lds index 857f44412e0..f4ce98c82c8 100644 --- a/arch/arm/cpu/armv8/u-boot.lds +++ b/arch/arm/cpu/armv8/u-boot.lds @@ -36,9 +36,18 @@ SECTIONS __efi_runtime_stop = .; } +#ifdef CONFIG_MMU_PGPROT + .text_rest ALIGN(CONSTANT(COMMONPAGESIZE)) : +#else .text_rest : +#endif { + __text_start = .; *(.text*) +#ifdef CONFIG_MMU_PGPROT + . = ALIGN(CONSTANT(COMMONPAGESIZE)); +#endif + __text_end = .; } #ifdef CONFIG_ARMV8_PSCI @@ -97,35 +106,43 @@ SECTIONS LONG(0x1d1071c); /* Must output something to reset LMA */ } #endif - - . = ALIGN(8); - .rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) } - - . = ALIGN(8); - .data : { - *(.data*) + .efi_runtime_rel : { + __efi_runtime_rel_start = .; + *(.rel*.efi_runtime) + *(.rel*.efi_runtime.*) + __efi_runtime_rel_stop = .; } - . = ALIGN(8); - - . = .; +#ifdef CONFIG_MMU_PGPROT + .rodata ALIGN(CONSTANT(COMMONPAGESIZE)): { +#else + .rodata ALIGN(8) : { +#endif + __start_rodata = .; + *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) + } - . = ALIGN(8); - __u_boot_list : { + __u_boot_list ALIGN(8) : { KEEP(*(SORT(__u_boot_list*))); +#ifdef CONFIG_MMU_PGPROT + . = ALIGN(CONSTANT(COMMONPAGESIZE)); +#endif + __end_rodata = .; } - .efi_runtime_rel : { - __efi_runtime_rel_start = .; - *(.rel*.efi_runtime) - *(.rel*.efi_runtime.*) - __efi_runtime_rel_stop = .; +#ifdef CONFIG_MMU_PGPROT + .data ALIGN(CONSTANT(COMMONPAGESIZE)) : { +#else + .data ALIGN(8) : { +#endif + __start_data = .; + *(.data*) } . = ALIGN(8); __image_copy_end = .; - .rela.dyn : { + .rela.dyn ALIGN(8) : { __rel_dyn_start = .; *(.rela*) __rel_dyn_end = .; @@ -136,11 +153,15 @@ SECTIONS /* * arch/arm/lib/crt0_64.S assumes __bss_start - __bss_end % 8 == 0 */ - .bss ALIGN(8) : { + .bss ADDR(.rela.dyn) (OVERLAY) : { __bss_start = .; *(.bss*) . = ALIGN(8); __bss_end = .; +#ifdef CONFIG_MMU_PGPROT + . = ALIGN(CONSTANT(COMMONPAGESIZE)); +#endif + __end_data = .; } /DISCARD/ : { *(.dynsym) } diff --git a/include/asm-generic/sections.h b/include/asm-generic/sections.h index 3fd5c772a1a..024b1adde27 100644 --- a/include/asm-generic/sections.h +++ b/include/asm-generic/sections.h @@ -23,6 +23,7 @@ extern char __kprobes_text_start[], __kprobes_text_end[]; extern char __entry_text_start[], __entry_text_end[]; extern char __initdata_begin[], __initdata_end[]; extern char __start_rodata[], __end_rodata[]; +extern char __start_data[], __end_data[]; extern char __efi_helloworld_begin[]; extern char __efi_helloworld_end[]; extern char __efi_var_file_begin[]; @@ -63,6 +64,7 @@ static inline int arch_is_kernel_data(unsigned long addr) /* Start of U-Boot text region */ extern char __text_start[]; +extern char __text_end[]; /* This marks the text region which must be relocated */ extern char __image_copy_start[], __image_copy_end[]; -- cgit v1.3.1 From ff0a979fc3591dcfb28585ae97ed4078a3ed5ef4 Mon Sep 17 00:00:00 2001 From: Ilias Apalodimas Date: Thu, 20 Feb 2025 15:54:41 +0200 Subject: arm64: mmu_change_region_attr() add an option not to break PTEs The ARM ARM (Rev L.a) on section 8.17.1 describes the cases where break-before-make is required when changing live page tables. Since we can use a function to tweak block and page permissions, where BBM is not required split the existing mmu_change_region_attr() into two functions and create one that doesn't require BBM. Subsequent patches will use the new function to map the U-Boot binary with proper page permissions. While at it add function descriptions in their header files. Signed-off-by: Ilias Apalodimas --- arch/arm/cpu/armv8/cache_v8.c | 48 +++++++++++++++++++++++++------------------ arch/arm/include/asm/system.h | 18 ++++++++++++++++ 2 files changed, 46 insertions(+), 20 deletions(-) diff --git a/arch/arm/cpu/armv8/cache_v8.c b/arch/arm/cpu/armv8/cache_v8.c index c4b3da4a8da..c9948e99e2e 100644 --- a/arch/arm/cpu/armv8/cache_v8.c +++ b/arch/arm/cpu/armv8/cache_v8.c @@ -967,61 +967,69 @@ void mmu_set_region_dcache_behaviour(phys_addr_t start, size_t size, flush_dcache_range(real_start, real_start + real_size); } -/* - * Modify MMU table for a region with updated PXN/UXN/Memory type/valid bits. - * The procecess is break-before-make. The target region will be marked as - * invalid during the process of changing. - */ -void mmu_change_region_attr(phys_addr_t addr, size_t siz, u64 attrs) +void mmu_change_region_attr_nobreak(phys_addr_t addr, size_t siz, u64 attrs) { int level; u64 r, size, start; - start = addr; - size = siz; /* * Loop through the address range until we find a page granule that fits - * our alignment constraints, then set it to "invalid". + * our alignment constraints and set the new permissions */ + start = addr; + size = siz; while (size > 0) { for (level = 1; level < 4; level++) { - /* Set PTE to fault */ - r = set_one_region(start, size, PTE_TYPE_FAULT, true, - level); + /* Set PTE to new attributes */ + r = set_one_region(start, size, attrs, true, level); if (r) { - /* PTE successfully invalidated */ + /* PTE successfully updated */ size -= r; start += r; break; } } } - flush_dcache_range(gd->arch.tlb_addr, gd->arch.tlb_addr + gd->arch.tlb_size); __asm_invalidate_tlb_all(); +} +/* + * Modify MMU table for a region with updated PXN/UXN/Memory type/valid bits. + * The procecess is break-before-make. The target region will be marked as + * invalid during the process of changing. + */ +void mmu_change_region_attr(phys_addr_t addr, size_t siz, u64 attrs) +{ + int level; + u64 r, size, start; + + start = addr; + size = siz; /* * Loop through the address range until we find a page granule that fits - * our alignment constraints, then set it to the new cache attributes + * our alignment constraints, then set it to "invalid". */ - start = addr; - size = siz; while (size > 0) { for (level = 1; level < 4; level++) { - /* Set PTE to new attributes */ - r = set_one_region(start, size, attrs, true, level); + /* Set PTE to fault */ + r = set_one_region(start, size, PTE_TYPE_FAULT, true, + level); if (r) { - /* PTE successfully updated */ + /* PTE successfully invalidated */ size -= r; start += r; break; } } } + flush_dcache_range(gd->arch.tlb_addr, gd->arch.tlb_addr + gd->arch.tlb_size); __asm_invalidate_tlb_all(); + + mmu_change_region_attr_nobreak(addr, siz, attrs); } #else /* !CONFIG_IS_ENABLED(SYS_DCACHE_OFF) */ diff --git a/arch/arm/include/asm/system.h b/arch/arm/include/asm/system.h index 091082281c7..849b3d0efb7 100644 --- a/arch/arm/include/asm/system.h +++ b/arch/arm/include/asm/system.h @@ -303,8 +303,26 @@ void flush_l3_cache(void); * @emerg: Also map the region in the emergency table */ void mmu_map_region(phys_addr_t start, u64 size, bool emerg); + +/** + * mmu_change_region_attr() - change a mapped region attributes + * + * @start: Start address of the region + * @size: Size of the region + * @aatrs: New attributes + */ void mmu_change_region_attr(phys_addr_t start, size_t size, u64 attrs); +/** + * mmu_change_region_attr_nobreak() - change a mapped region attributes without doing + * break-before-make + * + * @start: Start address of the region + * @size: Size of the region + * @aatrs: New attributes + */ +void mmu_change_region_attr_nobreak(phys_addr_t addr, size_t size, u64 attrs); + /* * smc_call() - issue a secure monitor call * -- cgit v1.3.1 From ec1c6cfb1cfce92909a248f10c36bd8b18894d7e Mon Sep 17 00:00:00 2001 From: Ilias Apalodimas Date: Thu, 20 Feb 2025 15:54:42 +0200 Subject: treewide: Add a function to change page permissions For armv8 we are adding proper page permissions for the relocated U-Boot binary. Add a weak function that can be used across architectures to change the page permissions Tested-by: Neil Armstrong # on AML-S905X-CC Signed-off-by: Ilias Apalodimas --- arch/arc/lib/cache.c | 6 ++++++ arch/arm/cpu/arm926ejs/cache.c | 6 ++++++ arch/arm/cpu/armv7/cache_v7.c | 6 ++++++ arch/arm/cpu/armv7m/cache.c | 6 ++++++ arch/arm/cpu/armv8/cache_v8.c | 25 +++++++++++++++++++++++++ arch/arm/lib/cache.c | 6 ++++++ arch/m68k/lib/cache.c | 6 ++++++ arch/nios2/lib/cache.c | 6 ++++++ arch/powerpc/lib/cache.c | 6 ++++++ arch/riscv/lib/cache.c | 6 ++++++ arch/sh/cpu/sh4/cache.c | 6 ++++++ arch/xtensa/lib/cache.c | 6 ++++++ include/cpu_func.h | 17 +++++++++++++++++ 13 files changed, 108 insertions(+) diff --git a/arch/arc/lib/cache.c b/arch/arc/lib/cache.c index 5169fc627fa..08f9e7dceac 100644 --- a/arch/arc/lib/cache.c +++ b/arch/arc/lib/cache.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -819,3 +820,8 @@ void sync_n_cleanup_cache_all(void) __ic_entire_invalidate(); } + +int __weak pgprot_set_attrs(phys_addr_t addr, size_t size, enum pgprot_attrs perm) +{ + return -ENOSYS; +} diff --git a/arch/arm/cpu/arm926ejs/cache.c b/arch/arm/cpu/arm926ejs/cache.c index 5b87a3af91b..71b8ad0f71d 100644 --- a/arch/arm/cpu/arm926ejs/cache.c +++ b/arch/arm/cpu/arm926ejs/cache.c @@ -5,6 +5,7 @@ */ #include #include +#include #include #if !CONFIG_IS_ENABLED(SYS_DCACHE_OFF) @@ -88,3 +89,8 @@ void enable_caches(void) dcache_enable(); #endif } + +int __weak pgprot_set_attrs(phys_addr_t addr, size_t size, enum pgprot_attrs perm) +{ + return -ENOSYS; +} diff --git a/arch/arm/cpu/armv7/cache_v7.c b/arch/arm/cpu/armv7/cache_v7.c index d11420d2fdd..371dc92cd46 100644 --- a/arch/arm/cpu/armv7/cache_v7.c +++ b/arch/arm/cpu/armv7/cache_v7.c @@ -6,6 +6,7 @@ */ #include #include +#include #include #include #include @@ -209,3 +210,8 @@ __weak void v7_outer_cache_flush_all(void) {} __weak void v7_outer_cache_inval_all(void) {} __weak void v7_outer_cache_flush_range(u32 start, u32 end) {} __weak void v7_outer_cache_inval_range(u32 start, u32 end) {} + +int __weak pgprot_set_attrs(phys_addr_t addr, size_t size, enum pgprot_attrs perm) +{ + return -ENOSYS; +} diff --git a/arch/arm/cpu/armv7m/cache.c b/arch/arm/cpu/armv7m/cache.c index b6d08b7aad7..8e7db734055 100644 --- a/arch/arm/cpu/armv7m/cache.c +++ b/arch/arm/cpu/armv7m/cache.c @@ -11,6 +11,7 @@ #include #include #include +#include /* Cache maintenance operation registers */ @@ -370,3 +371,8 @@ void enable_caches(void) dcache_enable(); #endif } + +int __weak pgprot_set_attrs(phys_addr_t addr, size_t size, enum pgprot_attrs perm) +{ + return -ENOSYS; +} diff --git a/arch/arm/cpu/armv8/cache_v8.c b/arch/arm/cpu/armv8/cache_v8.c index c9948e99e2e..12ae9bd0603 100644 --- a/arch/arm/cpu/armv8/cache_v8.c +++ b/arch/arm/cpu/armv8/cache_v8.c @@ -14,6 +14,7 @@ #include #include #include +#include DECLARE_GLOBAL_DATA_PTR; @@ -1032,6 +1033,30 @@ void mmu_change_region_attr(phys_addr_t addr, size_t siz, u64 attrs) mmu_change_region_attr_nobreak(addr, siz, attrs); } +int pgprot_set_attrs(phys_addr_t addr, size_t size, enum pgprot_attrs perm) +{ + u64 attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) | PTE_BLOCK_INNER_SHARE | PTE_TYPE_VALID; + + switch (perm) { + case MMU_ATTR_RO: + attrs |= PTE_BLOCK_PXN | PTE_BLOCK_UXN | PTE_BLOCK_RO; + break; + case MMU_ATTR_RX: + attrs |= PTE_BLOCK_RO; + break; + case MMU_ATTR_RW: + attrs |= PTE_BLOCK_PXN | PTE_BLOCK_UXN; + break; + default: + log_err("Unknown attribute %d\n", perm); + return -EINVAL; + } + + mmu_change_region_attr_nobreak(addr, size, attrs); + + return 0; +} + #else /* !CONFIG_IS_ENABLED(SYS_DCACHE_OFF) */ /* diff --git a/arch/arm/lib/cache.c b/arch/arm/lib/cache.c index 516754caeaf..dd19bd3e4fb 100644 --- a/arch/arm/lib/cache.c +++ b/arch/arm/lib/cache.c @@ -10,6 +10,7 @@ #include #include #include +#include DECLARE_GLOBAL_DATA_PTR; @@ -170,3 +171,8 @@ __weak int arm_reserve_mmu(void) return 0; } + +int __weak pgprot_set_attrs(phys_addr_t addr, size_t size, enum pgprot_attrs perm) +{ + return -ENOSYS; +} diff --git a/arch/m68k/lib/cache.c b/arch/m68k/lib/cache.c index 370ad40f142..a21fe327944 100644 --- a/arch/m68k/lib/cache.c +++ b/arch/m68k/lib/cache.c @@ -8,6 +8,7 @@ #include #include #include +#include volatile int *cf_icache_status = (int *)ICACHE_STATUS; volatile int *cf_dcache_status = (int *)DCACHE_STATUS; @@ -151,3 +152,8 @@ __weak void flush_dcache_range(unsigned long start, unsigned long stop) { /* An empty stub, real implementation should be in platform code */ } + +int __weak pgprot_set_attrs(phys_addr_t addr, size_t size, enum pgprot_attrs perm) +{ + return -ENOSYS; +} diff --git a/arch/nios2/lib/cache.c b/arch/nios2/lib/cache.c index 8f543f2a2f2..d7fd9ca8bd4 100644 --- a/arch/nios2/lib/cache.c +++ b/arch/nios2/lib/cache.c @@ -8,6 +8,7 @@ #include #include #include +#include DECLARE_GLOBAL_DATA_PTR; @@ -127,3 +128,8 @@ void dcache_disable(void) { flush_dcache_all(); } + +int __weak pgprot_set_attrs(phys_addr_t addr, size_t size, enum pgprot_attrs perm) +{ + return -ENOSYS; +} diff --git a/arch/powerpc/lib/cache.c b/arch/powerpc/lib/cache.c index a9cd7b8d30a..e4d9546039d 100644 --- a/arch/powerpc/lib/cache.c +++ b/arch/powerpc/lib/cache.c @@ -8,6 +8,7 @@ #include #include #include +#include static ulong maybe_watchdog_reset(ulong flushed) { @@ -58,3 +59,8 @@ void invalidate_icache_all(void) { puts("No arch specific invalidate_icache_all available!\n"); } + +int __weak pgprot_set_attrs(phys_addr_t addr, size_t size, enum pgprot_attrs perm) +{ + return -ENOSYS; +} diff --git a/arch/riscv/lib/cache.c b/arch/riscv/lib/cache.c index 71e4937ab54..31aa30bc7d7 100644 --- a/arch/riscv/lib/cache.c +++ b/arch/riscv/lib/cache.c @@ -8,6 +8,7 @@ #include #include #include +#include #define CBO_INVAL(base) \ INSN_I(OPCODE_MISC_MEM, FUNC3(2), __RD(0), \ @@ -151,3 +152,8 @@ __weak void enable_caches(void) if (!zicbom_block_size) log_debug("Zicbom not initialized.\n"); } + +int __weak pgprot_set_attrs(phys_addr_t addr, size_t size, enum pgprot_attrs perm) +{ + return -ENOSYS; +} diff --git a/arch/sh/cpu/sh4/cache.c b/arch/sh/cpu/sh4/cache.c index 99acc599965..56161ee72e4 100644 --- a/arch/sh/cpu/sh4/cache.c +++ b/arch/sh/cpu/sh4/cache.c @@ -11,6 +11,7 @@ #include #include #include +#include #define CACHE_VALID 1 #define CACHE_UPDATED 2 @@ -126,3 +127,8 @@ int dcache_status(void) { return 0; } + +int __weak pgprot_set_attrs(phys_addr_t addr, size_t size, enum pgprot_attrs perm) +{ + return -ENOSYS; +} diff --git a/arch/xtensa/lib/cache.c b/arch/xtensa/lib/cache.c index e6a7f6827fc..1229b407783 100644 --- a/arch/xtensa/lib/cache.c +++ b/arch/xtensa/lib/cache.c @@ -6,6 +6,7 @@ #include #include +#include /* * We currently run always with caches enabled when running from memory. @@ -57,3 +58,8 @@ void invalidate_icache_all(void) { __invalidate_icache_all(); } + +int __weak pgprot_set_attrs(phys_addr_t addr, size_t size, enum pgprot_attrs perm) +{ + return -ENOSYS; +} diff --git a/include/cpu_func.h b/include/cpu_func.h index 7e81c4364a7..70a41ead3f7 100644 --- a/include/cpu_func.h +++ b/include/cpu_func.h @@ -69,6 +69,23 @@ void flush_dcache_range(unsigned long start, unsigned long stop); void invalidate_dcache_range(unsigned long start, unsigned long stop); void invalidate_dcache_all(void); void invalidate_icache_all(void); + +enum pgprot_attrs { + MMU_ATTR_RO, + MMU_ATTR_RX, + MMU_ATTR_RW, +}; + +/** pgprot_set_attrs() - Set page table permissions + * + * @addr: Physical address start + * @size: size of memory to change + * @perm: New permissions + * + * Return: 0 on success, error otherwise. + **/ +int pgprot_set_attrs(phys_addr_t addr, size_t size, enum pgprot_attrs perm); + /** * noncached_init() - Initialize non-cached memory region * -- cgit v1.3.1 From fb553201b67aededdc794f3a0880e386771cbd58 Mon Sep 17 00:00:00 2001 From: Ilias Apalodimas Date: Thu, 20 Feb 2025 15:54:43 +0200 Subject: arm64: Enable RW, RX and RO mappings for the relocated binary Now that we have everything in place switch the page permissions for .rodata, .text and .data just after we relocate everything in top of the RAM. Unfortunately we can't enable this by default, since we have examples of U-Boot crashing due to invalid access. This usually happens because code defines const variables that it later writes. So hide it behind a Kconfig option until we sort it out. It's worth noting that EFI runtime services are not covered by this patch on purpose. Since the OS can call SetVirtualAddressMap which can relocate runtime services, we need to set them to RX initially but remap them as RWX right before ExitBootServices. Link: https://lore.kernel.org/u-boot/20250129-rockchip-pinctrl-const-v1-0-450ccdadfa7e@cherry.de/ Link: https://lore.kernel.org/u-boot/20250130133646.2177194-1-andre.przywara@arm.com/ Reviewed-by: Jerome Forissier Reviewed-by: Richard Henderson Signed-off-by: Ilias Apalodimas --- common/Kconfig | 13 +++++++++++++ common/board_r.c | 20 ++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/common/Kconfig b/common/Kconfig index a1f65559a1b..be517b80eb5 100644 --- a/common/Kconfig +++ b/common/Kconfig @@ -914,6 +914,19 @@ config STACKPROTECTOR Enable stack smash detection through compiler's stack-protector canary logic +config MMU_PGPROT + bool "Enable RO, RW and RX mappings" + help + U-Boot maps all pages as RWX. If selected pages will + be marked as RO(.rodata), RX(.text), RW(.data) right after + we relocate. Since code sections needs to be page aligned + the final binary size will increase. The mappings can be dumped + using the 'meminfo' command. + + Enabling this feature can expose bugs in U-Boot where we have + code that violates read-only permissions for example. Use this + feature with caution. + config SPL_STACKPROTECTOR bool "Stack Protector buffer overflow detection for SPL" depends on STACKPROTECTOR && SPL diff --git a/common/board_r.c b/common/board_r.c index db0c5cb8032..8d69db1875d 100644 --- a/common/board_r.c +++ b/common/board_r.c @@ -170,7 +170,27 @@ static int initr_reloc_global_data(void) efi_save_gd(); efi_runtime_relocate(gd->relocaddr, NULL); + #endif + /* + * We are done with all relocations change the permissions of the binary + * NOTE: __start_rodata etc are defined in arm64 linker scripts and + * sections.h. If you want to add support for your platform you need to + * add the symbols on your linker script, otherwise they will point to + * random addresses. + * + */ + if (IS_ENABLED(CONFIG_MMU_PGPROT)) { + pgprot_set_attrs((phys_addr_t)(uintptr_t)(__start_rodata), + (size_t)(uintptr_t)(__end_rodata - __start_rodata), + MMU_ATTR_RO); + pgprot_set_attrs((phys_addr_t)(uintptr_t)(__start_data), + (size_t)(uintptr_t)(__end_data - __start_data), + MMU_ATTR_RW); + pgprot_set_attrs((phys_addr_t)(uintptr_t)(__text_start), + (size_t)(uintptr_t)(__text_end - __text_start), + MMU_ATTR_RX); + } return 0; } -- cgit v1.3.1