diff --git a/include/lib/xlat_tables/xlat_tables_defs.h b/include/lib/xlat_tables/xlat_tables_defs.h index 032ce9257..e6f44bfa1 100644 --- a/include/lib/xlat_tables/xlat_tables_defs.h +++ b/include/lib/xlat_tables/xlat_tables_defs.h @@ -56,7 +56,10 @@ #define SECOND_LEVEL_DESC_N TWO_MB_SHIFT #define THIRD_LEVEL_DESC_N FOUR_KB_SHIFT +/* XN: Translation regimes that support one VA range (EL2 and EL3). */ #define XN (ULL(1) << 2) +/* UXN, PXN: Translation regimes that support two VA ranges (EL1&0). */ +#define UXN (ULL(1) << 2) #define PXN (ULL(1) << 1) #define CONT_HINT (ULL(1) << 0) #define UPPER_ATTRS(x) (((x) & ULL(0x7)) << 52) diff --git a/lib/xlat_tables/aarch32/xlat_tables.c b/lib/xlat_tables/aarch32/xlat_tables.c index 4fe5bf912..6033522a0 100644 --- a/lib/xlat_tables/aarch32/xlat_tables.c +++ b/lib/xlat_tables/aarch32/xlat_tables.c @@ -93,6 +93,20 @@ static unsigned long long get_max_supported_pa(void) } #endif /* ENABLE_ASSERTIONS */ +int xlat_arch_current_el(void) +{ + /* + * If EL3 is in AArch32 mode, all secure PL1 modes (Monitor, System, + * SVC, Abort, UND, IRQ and FIQ modes) execute at EL3. + */ + return 3; +} + +uint64_t xlat_arch_get_xn_desc(int el __unused) +{ + return UPPER_ATTRS(XN); +} + void init_xlat_tables(void) { unsigned long long max_pa; diff --git a/lib/xlat_tables/aarch64/xlat_tables.c b/lib/xlat_tables/aarch64/xlat_tables.c index 4f2379363..7be36ca33 100644 --- a/lib/xlat_tables/aarch64/xlat_tables.c +++ b/lib/xlat_tables/aarch64/xlat_tables.c @@ -146,6 +146,25 @@ static unsigned long long get_max_supported_pa(void) } #endif /* ENABLE_ASSERTIONS */ +int xlat_arch_current_el(void) +{ + int el = GET_EL(read_CurrentEl()); + + assert(el > 0); + + return el; +} + +uint64_t xlat_arch_get_xn_desc(int el) +{ + if (el == 3) { + return UPPER_ATTRS(XN); + } else { + assert(el == 1); + return UPPER_ATTRS(PXN); + } +} + void init_xlat_tables(void) { unsigned long long max_pa; diff --git a/lib/xlat_tables/xlat_tables_common.c b/lib/xlat_tables/xlat_tables_common.c index 23f3daa8c..1f21470ce 100644 --- a/lib/xlat_tables/xlat_tables_common.c +++ b/lib/xlat_tables/xlat_tables_common.c @@ -64,6 +64,8 @@ static unsigned next_xlat; static unsigned long long xlat_max_pa; static uintptr_t xlat_max_va; +static uint64_t execute_never_mask; + /* * Array of all memory regions stored in order of ascending base address. * The list is terminated by the first entry with size == 0. @@ -237,7 +239,8 @@ static uint64_t mmap_desc(mmap_attr_t attr, unsigned long long addr_pa, * fetch, which could be an issue if this memory region * corresponds to a read-sensitive peripheral. */ - desc |= UPPER_ATTRS(XN); + desc |= execute_never_mask; + } else { /* Normal memory */ /* * Always map read-write normal memory as execute-never. @@ -245,7 +248,7 @@ static uint64_t mmap_desc(mmap_attr_t attr, unsigned long long addr_pa, * R/W memory is reserved for data storage, which must not be * executable.) * Note that setting the XN bit here is for consistency only. - * The enable_mmu_elx() function sets the SCTLR_EL3.WXN bit, + * The function that enables the MMU sets the SCTLR_ELx.WXN bit, * which makes any writable memory region to be treated as * execute-never, regardless of the value of the XN bit in the * translation table. @@ -253,8 +256,9 @@ static uint64_t mmap_desc(mmap_attr_t attr, unsigned long long addr_pa, * For read-only memory, rely on the MT_EXECUTE/MT_EXECUTE_NEVER * attribute to figure out the value of the XN bit. */ - if ((attr & MT_RW) || (attr & MT_EXECUTE_NEVER)) - desc |= UPPER_ATTRS(XN); + if ((attr & MT_RW) || (attr & MT_EXECUTE_NEVER)) { + desc |= execute_never_mask; + } if (mem_type == MT_MEMORY) { desc |= LOWER_ATTRS(ATTR_IWBWA_OWBWA_NTR_INDEX | ISH); @@ -401,7 +405,7 @@ void init_xlation_table(uintptr_t base_va, uint64_t *table, int level, uintptr_t *max_va, unsigned long long *max_pa) { - + execute_never_mask = xlat_arch_get_xn_desc(xlat_arch_current_el()); init_xlation_table_inner(mmap, base_va, table, level); *max_va = xlat_max_va; *max_pa = xlat_max_pa; diff --git a/lib/xlat_tables/xlat_tables_private.h b/lib/xlat_tables/xlat_tables_private.h index 54ad909fe..ea6333736 100644 --- a/lib/xlat_tables/xlat_tables_private.h +++ b/lib/xlat_tables/xlat_tables_private.h @@ -89,6 +89,17 @@ CASSERT(IS_POWER_OF_TWO(PLAT_PHY_ADDR_SPACE_SIZE), #endif /* AARCH32 */ void print_mmap(void); + +/* Returns the current Exception Level. The returned EL must be 1 or higher. */ +int xlat_arch_current_el(void); + +/* + * Returns the bit mask that has to be ORed to the rest of a translation table + * descriptor so that execution of code is prohibited at the given Exception + * Level. + */ +uint64_t xlat_arch_get_xn_desc(int el); + void init_xlation_table(uintptr_t base_va, uint64_t *table, int level, uintptr_t *max_va, unsigned long long *max_pa); diff --git a/lib/xlat_tables_v2/aarch32/xlat_tables_arch.c b/lib/xlat_tables_v2/aarch32/xlat_tables_arch.c index cd7aad8fa..cb1f92db2 100644 --- a/lib/xlat_tables_v2/aarch32/xlat_tables_arch.c +++ b/lib/xlat_tables_v2/aarch32/xlat_tables_arch.c @@ -91,6 +91,20 @@ void xlat_arch_tlbi_va_sync(void) #endif /* PLAT_XLAT_TABLES_DYNAMIC */ +int xlat_arch_current_el(void) +{ + /* + * If EL3 is in AArch32 mode, all secure PL1 modes (Monitor, System, + * SVC, Abort, UND, IRQ and FIQ modes) execute at EL3. + */ + return 3; +} + +uint64_t xlat_arch_get_xn_desc(int el __unused) +{ + return UPPER_ATTRS(XN); +} + void init_xlat_tables_arch(unsigned long long max_pa) { assert((PLAT_PHY_ADDR_SPACE_SIZE - 1) <= diff --git a/lib/xlat_tables_v2/aarch64/xlat_tables_arch.c b/lib/xlat_tables_v2/aarch64/xlat_tables_arch.c index 24266b2d1..31e39cf49 100644 --- a/lib/xlat_tables_v2/aarch64/xlat_tables_arch.c +++ b/lib/xlat_tables_v2/aarch64/xlat_tables_arch.c @@ -151,6 +151,25 @@ void xlat_arch_tlbi_va_sync(void) #endif /* PLAT_XLAT_TABLES_DYNAMIC */ +int xlat_arch_current_el(void) +{ + int el = GET_EL(read_CurrentEl()); + + assert(el > 0); + + return el; +} + +uint64_t xlat_arch_get_xn_desc(int el) +{ + if (el == 3) { + return UPPER_ATTRS(XN); + } else { + assert(el == 1); + return UPPER_ATTRS(PXN); + } +} + void init_xlat_tables_arch(unsigned long long max_pa) { assert((PLAT_PHY_ADDR_SPACE_SIZE - 1) <= diff --git a/lib/xlat_tables_v2/xlat_tables_common.c b/lib/xlat_tables_v2/xlat_tables_common.c index 7ca81b9c8..9f84b22e6 100644 --- a/lib/xlat_tables_v2/xlat_tables_common.c +++ b/lib/xlat_tables_v2/xlat_tables_common.c @@ -137,6 +137,8 @@ void init_xlat_tables(void) assert(!is_mmu_enabled()); assert(!tf_xlat_ctx.initialized); print_mmap(tf_xlat_ctx.mmap); + tf_xlat_ctx.execute_never_mask = + xlat_arch_get_xn_desc(xlat_arch_current_el()); init_xlation_table(&tf_xlat_ctx); xlat_tables_print(&tf_xlat_ctx); diff --git a/lib/xlat_tables_v2/xlat_tables_internal.c b/lib/xlat_tables_v2/xlat_tables_internal.c index 581f77032..e7c67a46a 100644 --- a/lib/xlat_tables_v2/xlat_tables_internal.c +++ b/lib/xlat_tables_v2/xlat_tables_internal.c @@ -116,7 +116,7 @@ static uint64_t *xlat_table_get_empty(xlat_ctx_t *ctx) /* Returns a block/page table descriptor for the given level and attributes. */ static uint64_t xlat_desc(mmap_attr_t attr, unsigned long long addr_pa, - int level) + int level, uint64_t execute_never_mask) { uint64_t desc; int mem_type; @@ -158,7 +158,8 @@ static uint64_t xlat_desc(mmap_attr_t attr, unsigned long long addr_pa, * fetch, which could be an issue if this memory region * corresponds to a read-sensitive peripheral. */ - desc |= UPPER_ATTRS(XN); + desc |= execute_never_mask; + } else { /* Normal memory */ /* * Always map read-write normal memory as execute-never. @@ -166,7 +167,7 @@ static uint64_t xlat_desc(mmap_attr_t attr, unsigned long long addr_pa, * R/W memory is reserved for data storage, which must not be * executable.) * Note that setting the XN bit here is for consistency only. - * The enable_mmu_elx() function sets the SCTLR_EL3.WXN bit, + * The function that enables the MMU sets the SCTLR_ELx.WXN bit, * which makes any writable memory region to be treated as * execute-never, regardless of the value of the XN bit in the * translation table. @@ -174,8 +175,9 @@ static uint64_t xlat_desc(mmap_attr_t attr, unsigned long long addr_pa, * For read-only memory, rely on the MT_EXECUTE/MT_EXECUTE_NEVER * attribute to figure out the value of the XN bit. */ - if ((attr & MT_RW) || (attr & MT_EXECUTE_NEVER)) - desc |= UPPER_ATTRS(XN); + if ((attr & MT_RW) || (attr & MT_EXECUTE_NEVER)) { + desc |= execute_never_mask; + } if (mem_type == MT_MEMORY) { desc |= LOWER_ATTRS(ATTR_IWBWA_OWBWA_NTR_INDEX | ISH); @@ -535,7 +537,8 @@ static uintptr_t xlat_tables_map_region(xlat_ctx_t *ctx, mmap_region_t *mm, if (action == ACTION_WRITE_BLOCK_ENTRY) { table_base[table_idx] = - xlat_desc(mm->attr, table_idx_pa, level); + xlat_desc(mm->attr, table_idx_pa, level, + ctx->execute_never_mask); } else if (action == ACTION_CREATE_NEW_TABLE) { @@ -940,7 +943,7 @@ int mmap_remove_dynamic_region_ctx(xlat_ctx_t *ctx, uintptr_t base_va, #if LOG_LEVEL >= LOG_LEVEL_VERBOSE /* Print the attributes of the specified block descriptor. */ -static void xlat_desc_print(uint64_t desc) +static void xlat_desc_print(uint64_t desc, uint64_t execute_never_mask) { int mem_type_index = ATTR_INDEX_GET(desc); @@ -955,7 +958,7 @@ static void xlat_desc_print(uint64_t desc) tf_printf(LOWER_ATTRS(AP_RO) & desc ? "-RO" : "-RW"); tf_printf(LOWER_ATTRS(NS) & desc ? "-NS" : "-S"); - tf_printf(UPPER_ATTRS(XN) & desc ? "-XN" : "-EXEC"); + tf_printf(execute_never_mask & desc ? "-XN" : "-EXEC"); } static const char * const level_spacers[] = { @@ -974,7 +977,7 @@ static const char *invalid_descriptors_ommited = */ static void xlat_tables_print_internal(const uintptr_t table_base_va, uint64_t *const table_base, const int table_entries, - const int level) + const int level, const uint64_t execute_never_mask) { assert(level <= XLAT_TABLE_LEVEL_MAX); @@ -1035,14 +1038,15 @@ static void xlat_tables_print_internal(const uintptr_t table_base_va, xlat_tables_print_internal(table_idx_va, (uint64_t *)addr_inner, - XLAT_TABLE_ENTRIES, level+1); + XLAT_TABLE_ENTRIES, level+1, + execute_never_mask); } else { tf_printf("%sVA:%p PA:0x%llx size:0x%zx ", level_spacers[level], (void *)table_idx_va, (unsigned long long)(desc & TABLE_ADDR_MASK), level_size); - xlat_desc_print(desc); + xlat_desc_print(desc, execute_never_mask); tf_printf("\n"); } } @@ -1063,7 +1067,7 @@ void xlat_tables_print(xlat_ctx_t *ctx) { #if LOG_LEVEL >= LOG_LEVEL_VERBOSE xlat_tables_print_internal(0, ctx->base_table, ctx->base_table_entries, - ctx->base_level); + ctx->base_level, ctx->execute_never_mask); #endif /* LOG_LEVEL >= LOG_LEVEL_VERBOSE */ } diff --git a/lib/xlat_tables_v2/xlat_tables_private.h b/lib/xlat_tables_v2/xlat_tables_private.h index e79890e58..465f2a4fb 100644 --- a/lib/xlat_tables_v2/xlat_tables_private.h +++ b/lib/xlat_tables_v2/xlat_tables_private.h @@ -108,6 +108,13 @@ typedef struct { /* Set to 1 when the translation tables are initialized. */ int initialized; + /* + * Bit mask that has to be ORed to the rest of a translation table + * descriptor in order to prohibit execution of code at the exception + * level of this translation context. + */ + uint64_t execute_never_mask; + } xlat_ctx_t; #if PLAT_XLAT_TABLES_DYNAMIC @@ -178,6 +185,16 @@ void mmap_add_region_ctx(xlat_ctx_t *ctx, mmap_region_t *mm); * Architecture-specific initialization code. */ +/* Returns the current Exception Level. The returned EL must be 1 or higher. */ +int xlat_arch_current_el(void); + +/* + * Returns the bit mask that has to be ORed to the rest of a translation table + * descriptor so that execution of code is prohibited at the given Exception + * Level. + */ +uint64_t xlat_arch_get_xn_desc(int el); + /* Execute architecture-specific translation table initialization code. */ void init_xlat_tables_arch(unsigned long long max_pa);