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 <ilias.apalodi...@linaro.org> --- arch/arm/cpu/armv8/cache_v8.c | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/arch/arm/cpu/armv8/cache_v8.c b/arch/arm/cpu/armv8/cache_v8.c index 12ae9bd06039..1c1e33bec24f 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); -- 2.47.2