diff --git a/Makefile b/Makefile index 547b584..5e86541 100644 --- a/Makefile +++ b/Makefile @@ -621,6 +621,12 @@ endif endif +ifeq (${ARM_XLAT_TABLES_LIB_V1}, 1) + ifeq (${ALLOW_RO_XLAT_TABLES}, 1) + $(error "ALLOW_RO_XLAT_TABLES requires translation tables library v2") + endif +endif + ################################################################################ # Process platform overrideable behaviour ################################################################################ @@ -747,6 +753,7 @@ # Build options checks ################################################################################ +$(eval $(call assert_boolean,ALLOW_RO_XLAT_TABLES)) $(eval $(call assert_boolean,COLD_BOOT_SINGLE_CPU)) $(eval $(call assert_boolean,CREATE_KEYS)) $(eval $(call assert_boolean,CTX_INCLUDE_AARCH32_REGS)) @@ -814,6 +821,7 @@ # platform to overwrite the default options ################################################################################ +$(eval $(call add_define,ALLOW_RO_XLAT_TABLES)) $(eval $(call add_define,ARM_ARCH_MAJOR)) $(eval $(call add_define,ARM_ARCH_MINOR)) $(eval $(call add_define,COLD_BOOT_SINGLE_CPU)) diff --git a/include/lib/xlat_tables/xlat_tables_v2.h b/include/lib/xlat_tables/xlat_tables_v2.h index 0e09998..a80fab0 100644 --- a/include/lib/xlat_tables/xlat_tables_v2.h +++ b/include/lib/xlat_tables/xlat_tables_v2.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2017-2020, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -345,6 +345,16 @@ size_t size, uint32_t attr); int xlat_change_mem_attributes(uintptr_t base_va, size_t size, uint32_t attr); +#if PLAT_RO_XLAT_TABLES +/* + * Change the memory attributes of the memory region encompassing the higher + * level translation tables to secure read-only data. + * + * Return 0 on success, a negative error code on error. + */ +int xlat_make_tables_readonly(void); +#endif + /* * Query the memory attributes of a memory page in a set of translation tables. * diff --git a/include/lib/xlat_tables/xlat_tables_v2_helpers.h b/include/lib/xlat_tables/xlat_tables_v2_helpers.h index b17b71a..c88fa4d 100644 --- a/include/lib/xlat_tables/xlat_tables_v2_helpers.h +++ b/include/lib/xlat_tables/xlat_tables_v2_helpers.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2017-2020, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -70,6 +70,9 @@ */ uint64_t (*tables)[XLAT_TABLE_ENTRIES]; int tables_num; +#if PLAT_RO_XLAT_TABLES + bool readonly_tables; +#endif /* * Keep track of how many regions are mapped in each table. The base * table can't be unmapped so it isn't needed to keep track of it. @@ -122,6 +125,14 @@ /* do nothing */ #endif /* PLAT_XLAT_TABLES_DYNAMIC */ +#if PLAT_RO_XLAT_TABLES +#define XLAT_CTX_INIT_TABLE_ATTR() \ + .readonly_tables = false, +#else +#define XLAT_CTX_INIT_TABLE_ATTR() + /* do nothing */ +#endif + #define REGISTER_XLAT_CONTEXT_FULL_SPEC(_ctx_name, _mmap_count, \ _xlat_tables_count, _virt_addr_space_size, \ _phy_addr_space_size, _xlat_regime, _section_name)\ @@ -142,22 +153,63 @@ XLAT_ALLOC_DYNMAP_STRUCT(_ctx_name, _xlat_tables_count) \ \ static xlat_ctx_t _ctx_name##_xlat_ctx = { \ - .va_max_address = (_virt_addr_space_size) - 1UL, \ .pa_max_address = (_phy_addr_space_size) - 1ULL, \ + .va_max_address = (_virt_addr_space_size) - 1UL, \ .mmap = _ctx_name##_mmap, \ .mmap_num = (_mmap_count), \ - .base_level = GET_XLAT_TABLE_LEVEL_BASE(_virt_addr_space_size),\ + .tables = _ctx_name##_xlat_tables, \ + .tables_num = _xlat_tables_count, \ + XLAT_CTX_INIT_TABLE_ATTR() \ + XLAT_REGISTER_DYNMAP_STRUCT(_ctx_name) \ + .next_table = 0, \ .base_table = _ctx_name##_base_xlat_table, \ .base_table_entries = \ GET_NUM_BASE_LEVEL_ENTRIES(_virt_addr_space_size),\ - .tables = _ctx_name##_xlat_tables, \ - .tables_num = _xlat_tables_count, \ - XLAT_REGISTER_DYNMAP_STRUCT(_ctx_name) \ - .xlat_regime = (_xlat_regime), \ .max_pa = 0U, \ .max_va = 0U, \ - .next_table = 0, \ + .base_level = GET_XLAT_TABLE_LEVEL_BASE(_virt_addr_space_size),\ .initialized = false, \ + .xlat_regime = (_xlat_regime) \ + } + +#define REGISTER_XLAT_CONTEXT_RO_BASE_TABLE(_ctx_name, _mmap_count, \ + _xlat_tables_count, _virt_addr_space_size, \ + _phy_addr_space_size, _xlat_regime, _section_name)\ + CASSERT(CHECK_PHY_ADDR_SPACE_SIZE(_phy_addr_space_size), \ + assert_invalid_physical_addr_space_sizefor_##_ctx_name);\ + \ + static mmap_region_t _ctx_name##_mmap[_mmap_count + 1]; \ + \ + static uint64_t _ctx_name##_xlat_tables[_xlat_tables_count] \ + [XLAT_TABLE_ENTRIES] \ + __aligned(XLAT_TABLE_SIZE) __section(_section_name); \ + \ + static uint64_t _ctx_name##_base_xlat_table \ + [GET_NUM_BASE_LEVEL_ENTRIES(_virt_addr_space_size)] \ + __aligned(GET_NUM_BASE_LEVEL_ENTRIES(_virt_addr_space_size)\ + * sizeof(uint64_t)) \ + __section(".rodata"); \ + \ + XLAT_ALLOC_DYNMAP_STRUCT(_ctx_name, _xlat_tables_count) \ + \ + static xlat_ctx_t _ctx_name##_xlat_ctx = { \ + .pa_max_address = (_phy_addr_space_size) - 1ULL, \ + .va_max_address = (_virt_addr_space_size) - 1UL, \ + .mmap = _ctx_name##_mmap, \ + .mmap_num = (_mmap_count), \ + .tables = _ctx_name##_xlat_tables, \ + .tables_num = _xlat_tables_count, \ + XLAT_CTX_INIT_TABLE_ATTR() \ + XLAT_REGISTER_DYNMAP_STRUCT(_ctx_name) \ + .next_table = 0, \ + .base_table = _ctx_name##_base_xlat_table, \ + .base_table_entries = \ + GET_NUM_BASE_LEVEL_ENTRIES(_virt_addr_space_size),\ + .max_pa = 0U, \ + .max_va = 0U, \ + .base_level = GET_XLAT_TABLE_LEVEL_BASE(_virt_addr_space_size),\ + .initialized = false, \ + .xlat_regime = (_xlat_regime) \ } #endif /*__ASSEMBLER__*/ diff --git a/include/plat/arm/common/plat_arm.h b/include/plat/arm/common/plat_arm.h index 025a64f..862e73a 100644 --- a/include/plat/arm/common/plat_arm.h +++ b/include/plat/arm/common/plat_arm.h @@ -237,6 +237,11 @@ void arm_free_init_memory(void); /* + * Make the higher level translation tables read-only + */ +void arm_xlat_make_tables_readonly(void); + +/* * Mandatory functions required in ARM standard platforms */ unsigned int plat_arm_get_cluster_core_count(u_register_t mpidr); diff --git a/lib/aarch64/cache_helpers.S b/lib/aarch64/cache_helpers.S index 9ef8ca7..de9c8e4 100644 --- a/lib/aarch64/cache_helpers.S +++ b/lib/aarch64/cache_helpers.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2019, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2013-2020, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -30,7 +30,7 @@ dc \op, x0 add x0, x0, x2 cmp x0, x1 - b.lo loop_\op + b.lo loop_\op dsb sy exit_loop_\op: ret @@ -140,7 +140,7 @@ level_done: add x10, x10, #2 // increment cache number cmp x3, x10 - b.hi loop1 + b.hi loop1 msr csselr_el1, xzr // select cache level 0 in csselr dsb sy // barrier to complete final cache operation isb diff --git a/lib/xlat_tables_v2/ro_xlat_tables.mk b/lib/xlat_tables_v2/ro_xlat_tables.mk new file mode 100644 index 0000000..7991e1a --- /dev/null +++ b/lib/xlat_tables_v2/ro_xlat_tables.mk @@ -0,0 +1,37 @@ +# +# Copyright (c) 2020, ARM Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +ifeq (${USE_DEBUGFS}, 1) + $(error "Debugfs requires functionality from the dynamic translation \ + library and is incompatible with ALLOW_RO_XLAT_TABLES.") +endif + +ifeq (${ARCH},aarch32) + ifeq (${RESET_TO_SP_MIN},1) + $(error "RESET_TO_SP_MIN requires functionality from the dynamic \ + translation library and is incompatible with \ + ALLOW_RO_XLAT_TABLES.") + endif +else # if AArch64 + ifeq (${PLAT},tegra) + $(error "Tegra requires functionality from the dynamic translation \ + library and is incompatible with ALLOW_RO_XLAT_TABLES.") + endif + ifeq (${RESET_TO_BL31},1) + $(error "RESET_TO_BL31 requires functionality from the dynamic \ + translation library and is incompatible with \ + ALLOW_RO_XLAT_TABLES.") + endif + ifeq (${SPD},trusty) + $(error "Trusty requires functionality from the dynamic translation \ + library and is incompatible with ALLOW_RO_XLAT_TABLES.") + endif + ifeq (${SPM_MM},1) + $(error "SPM_MM requires functionality to change memory region \ + attributes, which is not possible once the translation tables \ + have been made read-only.") + endif +endif diff --git a/lib/xlat_tables_v2/xlat_tables.mk b/lib/xlat_tables_v2/xlat_tables.mk index c946315..bcc3e68 100644 --- a/lib/xlat_tables_v2/xlat_tables.mk +++ b/lib/xlat_tables_v2/xlat_tables.mk @@ -1,5 +1,5 @@ # -# Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved. +# Copyright (c) 2017-2020, ARM Limited and Contributors. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -13,3 +13,7 @@ XLAT_TABLES_LIB_V2 := 1 $(eval $(call add_define,XLAT_TABLES_LIB_V2)) + +ifeq (${ALLOW_RO_XLAT_TABLES}, 1) + include lib/xlat_tables_v2/ro_xlat_tables.mk +endif diff --git a/lib/xlat_tables_v2/xlat_tables_context.c b/lib/xlat_tables_v2/xlat_tables_context.c index f4b64b3..adca578 100644 --- a/lib/xlat_tables_v2/xlat_tables_context.c +++ b/lib/xlat_tables_v2/xlat_tables_context.c @@ -1,9 +1,10 @@ /* - * Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2017-2020, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ +#include #include #include @@ -24,8 +25,14 @@ * Allocate and initialise the default translation context for the BL image * currently executing. */ +#if PLAT_RO_XLAT_TABLES +REGISTER_XLAT_CONTEXT_RO_BASE_TABLE(tf, MAX_MMAP_REGIONS, MAX_XLAT_TABLES, + PLAT_VIRT_ADDR_SPACE_SIZE, PLAT_PHY_ADDR_SPACE_SIZE, + EL_REGIME_INVALID, "xlat_table"); +#else REGISTER_XLAT_CONTEXT(tf, MAX_MMAP_REGIONS, MAX_XLAT_TABLES, PLAT_VIRT_ADDR_SPACE_SIZE, PLAT_PHY_ADDR_SPACE_SIZE); +#endif void mmap_add_region(unsigned long long base_pa, uintptr_t base_va, size_t size, unsigned int attr) @@ -119,6 +126,75 @@ return xlat_change_mem_attributes_ctx(&tf_xlat_ctx, base_va, size, attr); } +#if PLAT_RO_XLAT_TABLES +/* Change the memory attributes of the descriptors which resolve the address + * range that belongs to the translation tables themselves, which are by default + * mapped as part of read-write data in the BL image's memory. + * + * Since the translation tables map themselves via these level 3 (page) + * descriptors, any change applied to them with the MMU on would introduce a + * chicken and egg problem because of the break-before-make sequence. + * Eventually, it would reach the descriptor that resolves the very table it + * belongs to and the invalidation (break step) would cause the subsequent write + * (make step) to it to generate an MMU fault. Therefore, the MMU is disabled + * before making the change. + * + * No assumption is made about what data this function needs, therefore all the + * caches are flushed in order to ensure coherency. A future optimization would + * be to only flush the required data to main memory. + */ +int xlat_make_tables_readonly(void) +{ + assert(tf_xlat_ctx.initialized == true); +#ifdef __aarch64__ + if (tf_xlat_ctx.xlat_regime == EL1_EL0_REGIME) { + disable_mmu_el1(); + } else if (tf_xlat_ctx.xlat_regime == EL3_REGIME) { + disable_mmu_el3(); + } else { + assert(tf_xlat_ctx.xlat_regime == EL2_REGIME); + return -1; + } + + /* Flush all caches. */ + dcsw_op_all(DCCISW); +#else /* !__aarch64__ */ + assert(tf_xlat_ctx.xlat_regime == EL1_EL0_REGIME); + /* On AArch32, we flush the caches before disabling the MMU. The reason + * for this is that the dcsw_op_all AArch32 function pushes some + * registers onto the stack under the assumption that it is writing to + * cache, which is not true with the MMU off. This would result in the + * stack becoming corrupted and a wrong/junk value for the LR being + * restored at the end of the routine. + */ + dcsw_op_all(DC_OP_CISW); + disable_mmu_secure(); +#endif + + int rc = xlat_change_mem_attributes_ctx(&tf_xlat_ctx, + (uintptr_t)tf_xlat_ctx.tables, + tf_xlat_ctx.tables_num * XLAT_TABLE_SIZE, + MT_RO_DATA | MT_SECURE); + +#ifdef __aarch64__ + if (tf_xlat_ctx.xlat_regime == EL1_EL0_REGIME) { + enable_mmu_el1(0U); + } else { + assert(tf_xlat_ctx.xlat_regime == EL3_REGIME); + enable_mmu_el3(0U); + } +#else /* !__aarch64__ */ + enable_mmu_svc_mon(0U); +#endif + + if (rc == 0) { + tf_xlat_ctx.readonly_tables = true; + } + + return rc; +} +#endif /* PLAT_RO_XLAT_TABLES */ + /* * If dynamic allocation of new regions is disabled then by the time we call the * function enabling the MMU, we'll have registered all the memory regions to diff --git a/make_helpers/defaults.mk b/make_helpers/defaults.mk index e8e990d..60958a1 100644 --- a/make_helpers/defaults.mk +++ b/make_helpers/defaults.mk @@ -207,6 +207,13 @@ # Build option to choose whether Trusted Firmware uses library at ROM USE_ROMLIB := 0 +# Build option to choose whether the xlat tables of BL images can be read-only. +# Note that this only serves as a higher level option to PLAT_RO_XLAT_TABLES, +# which is the per BL-image option that actually enables the read-only tables +# API. The reason for having this additional option is to have a common high +# level makefile where we can check for incompatible features/build options. +ALLOW_RO_XLAT_TABLES := 0 + # Chain of trust. COT := tbbr diff --git a/plat/arm/board/fvp/platform.mk b/plat/arm/board/fvp/platform.mk index 4176968..05c11ce 100644 --- a/plat/arm/board/fvp/platform.mk +++ b/plat/arm/board/fvp/platform.mk @@ -292,7 +292,7 @@ ifeq (${RESET_TO_SP_MIN},1) BL32_CFLAGS += -DPLAT_XLAT_TABLES_DYNAMIC=1 endif -else # if AArch64 +else # AArch64 ifeq (${RESET_TO_BL31},1) BL31_CFLAGS += -DPLAT_XLAT_TABLES_DYNAMIC=1 endif @@ -301,6 +301,17 @@ endif endif +ifeq (${ALLOW_RO_XLAT_TABLES}, 1) + ifeq (${ARCH},aarch32) + BL32_CFLAGS += -DPLAT_RO_XLAT_TABLES=1 + else # AArch64 + BL31_CFLAGS += -DPLAT_RO_XLAT_TABLES=1 + ifeq (${SPD},tspd) + BL32_CFLAGS += -DPLAT_RO_XLAT_TABLES=1 + endif + endif +endif + ifeq (${USE_DEBUGFS},1) BL31_CFLAGS += -DPLAT_XLAT_TABLES_DYNAMIC=1 endif diff --git a/plat/arm/board/juno/platform.mk b/plat/arm/board/juno/platform.mk index 27650d2..f07c1b1 100644 --- a/plat/arm/board/juno/platform.mk +++ b/plat/arm/board/juno/platform.mk @@ -155,6 +155,14 @@ endif endif +ifeq (${ALLOW_RO_XLAT_TABLES}, 1) + ifeq (${JUNO_AARCH32_EL3_RUNTIME}, 1) + BL32_CFLAGS += -DPLAT_RO_XLAT_TABLES=1 + else + BL31_CFLAGS += -DPLAT_RO_XLAT_TABLES=1 + endif +endif + # Add the FDT_SOURCES and options for Dynamic Config FDT_SOURCES += plat/arm/board/juno/fdts/${PLAT}_fw_config.dts TB_FW_CONFIG := ${BUILD_PLAT}/fdts/${PLAT}_fw_config.dtb diff --git a/plat/arm/common/arm_bl31_setup.c b/plat/arm/common/arm_bl31_setup.c index c135d7f..85535c1 100644 --- a/plat/arm/common/arm_bl31_setup.c +++ b/plat/arm/common/arm_bl31_setup.c @@ -256,9 +256,14 @@ /* Initialize the runtime console */ arm_console_runtime_init(); + #if RECLAIM_INIT_CODE arm_free_init_memory(); #endif + +#if PLAT_RO_XLAT_TABLES + arm_xlat_make_tables_readonly(); +#endif } #if RECLAIM_INIT_CODE diff --git a/plat/arm/common/arm_common.c b/plat/arm/common/arm_common.c index d1e9620..d1eee08 100644 --- a/plat/arm/common/arm_common.c +++ b/plat/arm/common/arm_common.c @@ -25,6 +25,26 @@ * conflicts with the definition in plat/common. */ #pragma weak plat_get_syscnt_freq2 +/******************************************************************************* + * Changes the memory attributes for the region of mapped memory where the BL + * image's translation tables are located such that the tables will have + * read-only permissions. + ******************************************************************************/ +#if PLAT_RO_XLAT_TABLES +void arm_xlat_make_tables_readonly(void) +{ + int rc = xlat_make_tables_readonly(); + + if (rc != 0) { + ERROR("Failed to make translation tables read-only at EL%u.\n", + get_current_el()); + panic(); + } + + INFO("Translation tables are now read-only at EL%u.\n", + get_current_el()); +} +#endif void arm_setup_romlib(void) { diff --git a/plat/arm/common/sp_min/arm_sp_min_setup.c b/plat/arm/common/sp_min/arm_sp_min_setup.c index 0cc746b..cbbdfa2 100644 --- a/plat/arm/common/sp_min/arm_sp_min_setup.c +++ b/plat/arm/common/sp_min/arm_sp_min_setup.c @@ -167,6 +167,10 @@ { /* Initialize the runtime console */ arm_console_runtime_init(); + +#if PLAT_RO_XLAT_TABLES + arm_xlat_make_tables_readonly(); +#endif } /******************************************************************************* diff --git a/plat/arm/common/tsp/arm_tsp_setup.c b/plat/arm/common/tsp/arm_tsp_setup.c index aefdf89..5dd964d 100644 --- a/plat/arm/common/tsp/arm_tsp_setup.c +++ b/plat/arm/common/tsp/arm_tsp_setup.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2019, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2020, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -79,4 +79,8 @@ setup_page_tables(bl_regions, plat_arm_get_mmap()); enable_mmu_el1(0); + +#if PLAT_RO_XLAT_TABLES + arm_xlat_make_tables_readonly(); +#endif }