From: Ilias Apalodimas Date: Wed, 19 Mar 2025 07:22:02 +0000 (+0200) Subject: arm64: Fix page permissions for platforms running at EL2 X-Git-Tag: v2025.07-rc1~119^2~7 X-Git-Url: http://git.openpandora.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a5ac47911abec63bcc5b3d2561ed9a5cb5a63841;p=pandora-u-boot.git arm64: Fix page permissions for platforms running at EL2 We currently set both and print both PXN and UXN bits when removing execution for pages. This happens even in the existing per platform definitions of 'struct mm_region'. That's not entirely correct though. For stage-1 translations, if a platform runs on a translation regime with a single privilege level or the the translation regime supports two privilege levels and we are not in EL1&0 with HCR_EL2.{NV, NV1} = {1, 1} only BIT54 (XN) is needed and BIT53(PXN) is reserved 0. Currently we support Non-Secure EL2, Non-secure EL2&0 and Non-secure EL1&0. We already have get_effective_el() which returns 1 if we are - Running in EL1 so we assume an EL1 translation regime but without checking HCR_EL2.{NV, NV1} != {1,1} - Running in EL2 with HCR_EL2.E2H = 1 The only problem with the above is that if we are in EL1&0 and HCR_EL2.{NV1, NV} == {1, 1}, then - Bit[54] holds the PXN instead of the UXN - The Effective value of UXN is 0 - Bit[53] is RES0 So let's re-use that function and set PXN only when we are in and EL[2|1]&0 translation regime. Signed-off-by: Ilias Apalodimas --- diff --git a/arch/arm/cpu/armv8/cache_v8.c b/arch/arm/cpu/armv8/cache_v8.c index 12ae9bd0603..1c1e33bec24 100644 --- a/arch/arm/cpu/armv8/cache_v8.c +++ b/arch/arm/cpu/armv8/cache_v8.c @@ -575,8 +575,12 @@ static void pretty_print_block_attrs(u64 pte) 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_UXN) { + if (get_effective_el() == 1) + cnt += snprintf(mem_attrs + cnt, sizeof(mem_attrs) - cnt, "UXN "); + else + cnt += snprintf(mem_attrs + cnt, sizeof(mem_attrs) - cnt, "XN "); + } if (perm_attrs & PTE_BLOCK_RO) cnt += snprintf(mem_attrs + cnt, sizeof(mem_attrs) - cnt, "RO"); if (!mem_attrs[0]) @@ -1039,13 +1043,29 @@ int pgprot_set_attrs(phys_addr_t addr, size_t size, enum pgprot_attrs perm) switch (perm) { case MMU_ATTR_RO: - attrs |= PTE_BLOCK_PXN | PTE_BLOCK_UXN | PTE_BLOCK_RO; + /* + * get_effective_el() will return 1 if + * - Running in EL1 so we assume an EL1 translation regime + * with HCR_EL2.{NV, NV1} != {1,1} + * - Running in EL2 with HCR_EL2.E2H = 1 so we assume an + * EL2&0 translation regime. Since we don't have accesses + * from EL0 we don't have to check HCR_EL2.TGE + * + * Both of these requires PXN to be set + */ + if (get_effective_el() == 1) + attrs |= PTE_BLOCK_PXN | PTE_BLOCK_UXN | PTE_BLOCK_RO; + else + attrs |= 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; + if (get_effective_el() == 1) + attrs |= PTE_BLOCK_PXN | PTE_BLOCK_UXN; + else + attrs |= PTE_BLOCK_UXN; break; default: log_err("Unknown attribute %d\n", perm);