diff options
author | Tom Rini <trini@konsulko.com> | 2025-03-14 09:31:36 -0600 |
---|---|---|
committer | Tom Rini <trini@konsulko.com> | 2025-03-14 09:31:36 -0600 |
commit | 973c366ce68d9a99e16bcbce3941a1c52492c097 (patch) | |
tree | 4551686cc38c7e419c833ebfeaf6bd6843124691 /arch/arm/cpu/armv8/cache_v8.c | |
parent | bbfabe290138d76e78e0dac28fec93c32e3964eb (diff) | |
parent | fb553201b67aededdc794f3a0880e386771cbd58 (diff) |
Merge tag 'mmu-next-14032025' of https://source.denx.de/u-boot/custodians/u-boot-tpm into next
Up to now we configure the entire memory space for U-Boot as RWX.
For modern architectures and security requirements, it's better to
map the memory properly.
This pull request adds basics support for mapping the U-Boot binary with
proper (RO, RW, RW^X) memory permissions on aarch64 right after we
relocate U-Boot in the top of DRAM.
It's worrth noting that the linker script annotations are only added for
the aarch64 architecture. We can, in the future, try to unify the linker --
at least for the architectures that have enough in common and expand this
Diffstat (limited to 'arch/arm/cpu/armv8/cache_v8.c')
-rw-r--r-- | arch/arm/cpu/armv8/cache_v8.c | 99 |
1 files changed, 76 insertions, 23 deletions
diff --git a/arch/arm/cpu/armv8/cache_v8.c b/arch/arm/cpu/armv8/cache_v8.c index 5d6953ffedd..12ae9bd0603 100644 --- a/arch/arm/cpu/armv8/cache_v8.c +++ b/arch/arm/cpu/armv8/cache_v8.c @@ -14,6 +14,7 @@ #include <asm/global_data.h> #include <asm/system.h> #include <asm/armv8/mmu.h> +#include <linux/errno.h> DECLARE_GLOBAL_DATA_PTR; @@ -421,7 +422,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 +569,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 +628,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 +658,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); @@ -952,61 +968,93 @@ 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); +} + +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) */ @@ -1112,3 +1160,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)); +} |