aboutsummaryrefslogtreecommitdiff
path: root/lib/xlat_tables_v2/xlat_tables_internal.c
diff options
context:
space:
mode:
authorAntonio Nino Diaz <antonio.ninodiaz@arm.com>2017-04-27 13:30:22 +0100
committerAntonio Nino Diaz <antonio.ninodiaz@arm.com>2017-05-02 15:34:01 +0100
commita56402521f80cf1b17e3936abcc6b1772aa91e66 (patch)
treeca2de540bfb5a40e8be469e3a0d10d51687dc404 /lib/xlat_tables_v2/xlat_tables_internal.c
parent062dd378280a6a1b9ace59d0be9c466a05a2de95 (diff)
Fix execute-never permissions in xlat tables libs
Translation regimes that only support one virtual address space (such as the ones for EL2 and EL3) can flag memory regions as execute-never by setting to 1 the XN bit in the Upper Attributes field in the translation tables descriptors. Translation regimes that support two different virtual address spaces (such as the one shared by EL1 and EL0) use bits PXN and UXN instead. The Trusted Firmware runs at EL3 and EL1, it has to handle translation tables of both translation regimes, but the previous code handled both regimes the same way, as if both had only 1 VA range. When trying to set a descriptor as execute-never it would set the XN bit correctly in EL3, but it would set the XN bit in EL1 as well. XN is at the same bit position as UXN, which means that EL0 was being prevented from executing code at this region, not EL1 as the code intended. Therefore, the PXN bit was unset to 0 all the time. The result is that, in AArch64 mode, read-only data sections of BL2 weren't protected from being executed. This patch adds support of translation regimes with two virtual address spaces to both versions of the translation tables library, fixing the execute-never permissions for translation tables in EL1. The library currently does not support initializing translation tables for EL0 software, therefore it does not set/unset the UXN bit. If EL1 software needs to initialize translation tables for EL0 software, it should use a different library instead. Change-Id: If27588f9820ff42988851d90dc92801c8ecbe0c9 Signed-off-by: Antonio Nino Diaz <antonio.ninodiaz@arm.com>
Diffstat (limited to 'lib/xlat_tables_v2/xlat_tables_internal.c')
-rw-r--r--lib/xlat_tables_v2/xlat_tables_internal.c28
1 files changed, 16 insertions, 12 deletions
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 */
}