diff --git a/docs/secure-partition-manager-design.rst b/docs/secure-partition-manager-design.rst index 3dd1e0c..fec7c00 100644 --- a/docs/secure-partition-manager-design.rst +++ b/docs/secure-partition-manager-design.rst @@ -126,8 +126,7 @@ To enable SPM support in TF-A, the source code must be compiled with the build flag ``ENABLE_SPM=1``. On Arm platforms the build option ``ARM_BL31_IN_DRAM`` -can be used to select the location of BL31, both SRAM and DRAM are supported. -Also, the location of the binary that contains the BL32 image +must be set to 1. Also, the location of the binary that contains the BL32 image (``BL32=path/to/image.bin``) must be specified. First, build the Standalone MM Secure Partition. To build it, refer to the @@ -139,7 +138,7 @@ :: BL32=path/to/standalone/mm/sp BL33=path/to/bl33.bin \ - make PLAT=fvp ENABLE_SPM=1 fip all + make PLAT=fvp ENABLE_SPM=1 ARM_BL31_IN_DRAM=1 fip all Describing Secure Partition resources ------------------------------------- diff --git a/include/lib/el3_runtime/context_mgmt.h b/include/lib/el3_runtime/context_mgmt.h index eb7a953..e3f7726 100644 --- a/include/lib/el3_runtime/context_mgmt.h +++ b/include/lib/el3_runtime/context_mgmt.h @@ -7,11 +7,11 @@ #ifndef __CM_H__ #define __CM_H__ -#ifndef AARCH32 #include #include +#include +#include #include -#endif /******************************************************************************* * Forward declarations @@ -32,6 +32,7 @@ void cm_init_my_context(const struct entry_point_info *ep); void cm_init_context_by_index(unsigned int cpu_idx, const struct entry_point_info *ep); +void cm_setup_context(cpu_context_t *ctx, const entry_point_info_t *ep); void cm_prepare_el3_exit(uint32_t security_state); #ifndef AARCH32 diff --git a/include/plat/arm/board/common/board_arm_def.h b/include/plat/arm/board/common/board_arm_def.h index ad6b4f8..21ceae3 100644 --- a/include/plat/arm/board/common/board_arm_def.h +++ b/include/plat/arm/board/common/board_arm_def.h @@ -30,7 +30,9 @@ #elif defined(IMAGE_BL2U) # define PLATFORM_STACK_SIZE 0x200 #elif defined(IMAGE_BL31) -#ifdef PLAT_XLAT_TABLES_DYNAMIC +#if ENABLE_SPM +# define PLATFORM_STACK_SIZE 0x500 +#elif PLAT_XLAT_TABLES_DYNAMIC # define PLATFORM_STACK_SIZE 0x800 #else # define PLATFORM_STACK_SIZE 0x400 @@ -94,7 +96,11 @@ * PLAT_ARM_MAX_BL31_SIZE is calculated using the current BL31 debug size plus a * little space for growth. */ -#define PLAT_ARM_MAX_BL31_SIZE 0x20000 +#if ENABLE_SPM +# define PLAT_ARM_MAX_BL31_SIZE 0x40000 +#else +# define PLAT_ARM_MAX_BL31_SIZE 0x20000 +#endif #ifdef AARCH32 /* diff --git a/include/plat/arm/common/arm_spm_def.h b/include/plat/arm/common/arm_spm_def.h index 3f5c958..83277a6 100644 --- a/include/plat/arm/common/arm_spm_def.h +++ b/include/plat/arm/common/arm_spm_def.h @@ -98,12 +98,6 @@ /* Total number of memory regions with distinct properties */ #define ARM_SP_IMAGE_NUM_MEM_REGIONS 6 -/* - * Name of the section to put the translation tables used by the S-EL1/S-EL0 - * context of a Secure Partition. - */ -#define PLAT_SP_IMAGE_XLAT_SECTION_NAME "arm_el3_tzc_dram" - /* Cookies passed to the Secure Partition at boot. Not used by ARM platforms. */ #define PLAT_SPM_COOKIE_0 ULL(0) #define PLAT_SPM_COOKIE_1 ULL(0) diff --git a/include/services/secure_partition.h b/include/services/secure_partition.h index f68f711..f1fdb73 100644 --- a/include/services/secure_partition.h +++ b/include/services/secure_partition.h @@ -55,8 +55,4 @@ secure_partition_mp_info_t *mp_info; } secure_partition_boot_info_t; -/* Setup function for secure partitions context. */ - -void secure_partition_setup(void); - #endif /* __SECURE_PARTITION_H__ */ diff --git a/lib/el3_runtime/aarch32/context_mgmt.c b/lib/el3_runtime/aarch32/context_mgmt.c index c784c22..11ef6e5 100644 --- a/lib/el3_runtime/aarch32/context_mgmt.c +++ b/lib/el3_runtime/aarch32/context_mgmt.c @@ -41,8 +41,7 @@ * entry_point_info structure. * * The security state to initialize is determined by the SECURE attribute - * of the entry_point_info. The function returns a pointer to the initialized - * context and sets this as the next context to return to. + * of the entry_point_info. * * The EE and ST attributes are used to configure the endianness and secure * timer availability for the new execution context. @@ -51,7 +50,7 @@ * el3_exit(). For Secure-EL1 cm_prepare_el3_exit() is equivalent to * cm_e1_sysreg_context_restore(). ******************************************************************************/ -static void cm_init_context_common(cpu_context_t *ctx, const entry_point_info_t *ep) +void cm_setup_context(cpu_context_t *ctx, const entry_point_info_t *ep) { unsigned int security_state; uint32_t scr, sctlr; @@ -149,7 +148,7 @@ { cpu_context_t *ctx; ctx = cm_get_context_by_index(cpu_idx, GET_SECURITY_STATE(ep->h.attr)); - cm_init_context_common(ctx, ep); + cm_setup_context(ctx, ep); } /******************************************************************************* @@ -161,7 +160,7 @@ { cpu_context_t *ctx; ctx = cm_get_context(GET_SECURITY_STATE(ep->h.attr)); - cm_init_context_common(ctx, ep); + cm_setup_context(ctx, ep); } /******************************************************************************* diff --git a/lib/el3_runtime/aarch64/context_mgmt.c b/lib/el3_runtime/aarch64/context_mgmt.c index d8267e2..f389368 100644 --- a/lib/el3_runtime/aarch64/context_mgmt.c +++ b/lib/el3_runtime/aarch64/context_mgmt.c @@ -49,8 +49,7 @@ * entry_point_info structure. * * The security state to initialize is determined by the SECURE attribute - * of the entry_point_info. The function returns a pointer to the initialized - * context and sets this as the next context to return to. + * of the entry_point_info. * * The EE and ST attributes are used to configure the endianess and secure * timer availability for the new execution context. @@ -59,7 +58,7 @@ * el3_exit(). For Secure-EL1 cm_prepare_el3_exit() is equivalent to * cm_e1_sysreg_context_restore(). ******************************************************************************/ -static void cm_init_context_common(cpu_context_t *ctx, const entry_point_info_t *ep) +void cm_setup_context(cpu_context_t *ctx, const entry_point_info_t *ep) { unsigned int security_state; uint32_t scr_el3, pmcr_el0; @@ -258,7 +257,7 @@ { cpu_context_t *ctx; ctx = cm_get_context_by_index(cpu_idx, GET_SECURITY_STATE(ep->h.attr)); - cm_init_context_common(ctx, ep); + cm_setup_context(ctx, ep); } /******************************************************************************* @@ -270,7 +269,7 @@ { cpu_context_t *ctx; ctx = cm_get_context(GET_SECURITY_STATE(ep->h.attr)); - cm_init_context_common(ctx, ep); + cm_setup_context(ctx, ep); } /******************************************************************************* diff --git a/plat/arm/board/fvp/platform.mk b/plat/arm/board/fvp/platform.mk index f807dc6..c02831a 100644 --- a/plat/arm/board/fvp/platform.mk +++ b/plat/arm/board/fvp/platform.mk @@ -225,6 +225,12 @@ override BL1_SOURCES = endif +ifeq (${ENABLE_SPM},1) +ifneq (${ARM_BL31_IN_DRAM},1) + $(error "Error: SPM needs BL31 to be located in DRAM.") +endif +endif + include plat/arm/board/common/board_common.mk include plat/arm/common/arm_common.mk diff --git a/services/std_svc/spm/secure_partition_setup.c b/services/std_svc/spm/secure_partition_setup.c deleted file mode 100644 index 9b3f622..0000000 --- a/services/std_svc/spm/secure_partition_setup.c +++ /dev/null @@ -1,317 +0,0 @@ -/* - * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "spm_private.h" -#include "spm_shim_private.h" - -/* Place translation tables by default along with the ones used by BL31. */ -#ifndef PLAT_SP_IMAGE_XLAT_SECTION_NAME -#define PLAT_SP_IMAGE_XLAT_SECTION_NAME "xlat_table" -#endif - -/* Allocate and initialise the translation context for the secure partition. */ -REGISTER_XLAT_CONTEXT2(secure_partition, - PLAT_SP_IMAGE_MMAP_REGIONS, - PLAT_SP_IMAGE_MAX_XLAT_TABLES, - PLAT_VIRT_ADDR_SPACE_SIZE, PLAT_PHY_ADDR_SPACE_SIZE, - EL1_EL0_REGIME, PLAT_SP_IMAGE_XLAT_SECTION_NAME); - -/* Export a handle on the secure partition translation context */ -xlat_ctx_t *secure_partition_xlat_ctx_handle = &secure_partition_xlat_ctx; - -/* Setup context of the Secure Partition */ -void secure_partition_setup(void) -{ - VERBOSE("S-EL1/S-EL0 context setup start...\n"); - - cpu_context_t *ctx = cm_get_context(SECURE); - - /* Make sure that we got a Secure context. */ - assert(ctx != NULL); - - /* Assert we are in Secure state. */ - assert((read_scr_el3() & SCR_NS_BIT) == 0); - - /* Disable MMU at EL1. */ - disable_mmu_icache_el1(); - - /* Invalidate TLBs at EL1. */ - tlbivmalle1(); - dsbish(); - - /* - * General-Purpose registers - * ------------------------- - */ - - /* - * X0: Virtual address of a buffer shared between EL3 and Secure EL0. - * The buffer will be mapped in the Secure EL1 translation regime - * with Normal IS WBWA attributes and RO data and Execute Never - * instruction access permissions. - * - * X1: Size of the buffer in bytes - * - * X2: cookie value (Implementation Defined) - * - * X3: cookie value (Implementation Defined) - * - * X4 to X30 = 0 (already done by cm_init_my_context()) - */ - write_ctx_reg(get_gpregs_ctx(ctx), CTX_GPREG_X0, PLAT_SPM_BUF_BASE); - write_ctx_reg(get_gpregs_ctx(ctx), CTX_GPREG_X1, PLAT_SPM_BUF_SIZE); - write_ctx_reg(get_gpregs_ctx(ctx), CTX_GPREG_X2, PLAT_SPM_COOKIE_0); - write_ctx_reg(get_gpregs_ctx(ctx), CTX_GPREG_X3, PLAT_SPM_COOKIE_1); - - /* - * SP_EL0: A non-zero value will indicate to the SP that the SPM has - * initialized the stack pointer for the current CPU through - * implementation defined means. The value will be 0 otherwise. - */ - write_ctx_reg(get_gpregs_ctx(ctx), CTX_GPREG_SP_EL0, - PLAT_SP_IMAGE_STACK_BASE + PLAT_SP_IMAGE_STACK_PCPU_SIZE); - - /* - * Setup translation tables - * ------------------------ - */ - -#if ENABLE_ASSERTIONS - - /* Get max granularity supported by the platform. */ - - u_register_t id_aa64mmfr0_el1 = read_id_aa64mmfr0_el1(); - - int tgran64_supported = - ((id_aa64mmfr0_el1 >> ID_AA64MMFR0_EL1_TGRAN64_SHIFT) & - ID_AA64MMFR0_EL1_TGRAN64_MASK) == - ID_AA64MMFR0_EL1_TGRAN64_SUPPORTED; - - int tgran16_supported = - ((id_aa64mmfr0_el1 >> ID_AA64MMFR0_EL1_TGRAN16_SHIFT) & - ID_AA64MMFR0_EL1_TGRAN16_MASK) == - ID_AA64MMFR0_EL1_TGRAN16_SUPPORTED; - - int tgran4_supported = - ((id_aa64mmfr0_el1 >> ID_AA64MMFR0_EL1_TGRAN4_SHIFT) & - ID_AA64MMFR0_EL1_TGRAN4_MASK) == - ID_AA64MMFR0_EL1_TGRAN4_SUPPORTED; - - uintptr_t max_granule_size; - - if (tgran64_supported) { - max_granule_size = 64 * 1024; - } else if (tgran16_supported) { - max_granule_size = 16 * 1024; - } else { - assert(tgran4_supported); - max_granule_size = 4 * 1024; - } - - VERBOSE("Max translation granule supported: %lu KiB\n", - max_granule_size / 1024); - - uintptr_t max_granule_size_mask = max_granule_size - 1; - - /* Base must be aligned to the max granularity */ - assert((ARM_SP_IMAGE_NS_BUF_BASE & max_granule_size_mask) == 0); - - /* Size must be a multiple of the max granularity */ - assert((ARM_SP_IMAGE_NS_BUF_SIZE & max_granule_size_mask) == 0); - -#endif /* ENABLE_ASSERTIONS */ - - /* This region contains the exception vectors used at S-EL1. */ - const mmap_region_t sel1_exception_vectors = - MAP_REGION_FLAT(SPM_SHIM_EXCEPTIONS_START, - SPM_SHIM_EXCEPTIONS_SIZE, - MT_CODE | MT_SECURE | MT_PRIVILEGED); - mmap_add_region_ctx(&secure_partition_xlat_ctx, - &sel1_exception_vectors); - - mmap_add_ctx(&secure_partition_xlat_ctx, - plat_get_secure_partition_mmap(NULL)); - - init_xlat_tables_ctx(&secure_partition_xlat_ctx); - - /* - * MMU-related registers - * --------------------- - */ - - /* Set attributes in the right indices of the MAIR */ - u_register_t mair_el1 = - MAIR_ATTR_SET(ATTR_DEVICE, ATTR_DEVICE_INDEX) | - MAIR_ATTR_SET(ATTR_IWBWA_OWBWA_NTR, ATTR_IWBWA_OWBWA_NTR_INDEX) | - MAIR_ATTR_SET(ATTR_NON_CACHEABLE, ATTR_NON_CACHEABLE_INDEX); - - write_ctx_reg(get_sysregs_ctx(ctx), CTX_MAIR_EL1, mair_el1); - - /* Setup TCR_EL1. */ - u_register_t tcr_ps_bits = tcr_physical_addr_size_bits(PLAT_PHY_ADDR_SPACE_SIZE); - - u_register_t tcr_el1 = - /* Size of region addressed by TTBR0_EL1 = 2^(64-T0SZ) bytes. */ - (64 - __builtin_ctzl(PLAT_VIRT_ADDR_SPACE_SIZE)) | - /* Inner and outer WBWA, shareable. */ - TCR_SH_INNER_SHAREABLE | TCR_RGN_OUTER_WBA | TCR_RGN_INNER_WBA | - /* Set the granularity to 4KB. */ - TCR_TG0_4K | - /* Limit Intermediate Physical Address Size. */ - tcr_ps_bits << TCR_EL1_IPS_SHIFT | - /* Disable translations using TBBR1_EL1. */ - TCR_EPD1_BIT - /* The remaining fields related to TBBR1_EL1 are left as zero. */ - ; - - tcr_el1 &= ~( - /* Enable translations using TBBR0_EL1 */ - TCR_EPD0_BIT - ); - - write_ctx_reg(get_sysregs_ctx(ctx), CTX_TCR_EL1, tcr_el1); - - /* Setup SCTLR_EL1 */ - u_register_t sctlr_el1 = read_ctx_reg(get_sysregs_ctx(ctx), CTX_SCTLR_EL1); - - sctlr_el1 |= - /*SCTLR_EL1_RES1 |*/ - /* Don't trap DC CVAU, DC CIVAC, DC CVAC, DC CVAP, or IC IVAU */ - SCTLR_UCI_BIT | - /* RW regions at xlat regime EL1&0 are forced to be XN. */ - SCTLR_WXN_BIT | - /* Don't trap to EL1 execution of WFI or WFE at EL0. */ - SCTLR_NTWI_BIT | SCTLR_NTWE_BIT | - /* Don't trap to EL1 accesses to CTR_EL0 from EL0. */ - SCTLR_UCT_BIT | - /* Don't trap to EL1 execution of DZ ZVA at EL0. */ - SCTLR_DZE_BIT | - /* Enable SP Alignment check for EL0 */ - SCTLR_SA0_BIT | - /* Allow cacheable data and instr. accesses to normal memory. */ - SCTLR_C_BIT | SCTLR_I_BIT | - /* Alignment fault checking enabled when at EL1 and EL0. */ - SCTLR_A_BIT | - /* Enable MMU. */ - SCTLR_M_BIT - ; - - sctlr_el1 &= ~( - /* Explicit data accesses at EL0 are little-endian. */ - SCTLR_E0E_BIT | - /* Accesses to DAIF from EL0 are trapped to EL1. */ - SCTLR_UMA_BIT - ); - - write_ctx_reg(get_sysregs_ctx(ctx), CTX_SCTLR_EL1, sctlr_el1); - - /* Point TTBR0_EL1 at the tables of the context created for the SP. */ - write_ctx_reg(get_sysregs_ctx(ctx), CTX_TTBR0_EL1, - (u_register_t)secure_partition_base_xlat_table); - - /* - * Setup other system registers - * ---------------------------- - */ - - /* Shim Exception Vector Base Address */ - write_ctx_reg(get_sysregs_ctx(ctx), CTX_VBAR_EL1, - SPM_SHIM_EXCEPTIONS_PTR); - - /* - * FPEN: Allow the Secure Partition to access FP/SIMD registers. - * Note that SPM will not do any saving/restoring of these registers on - * behalf of the SP. This falls under the SP's responsibility. - * TTA: Enable access to trace registers. - * ZEN (v8.2): Trap SVE instructions and access to SVE registers. - */ - write_ctx_reg(get_sysregs_ctx(ctx), CTX_CPACR_EL1, - CPACR_EL1_FPEN(CPACR_EL1_FP_TRAP_NONE)); - - /* - * Prepare information in buffer shared between EL3 and S-EL0 - * ---------------------------------------------------------- - */ - - void *shared_buf_ptr = (void *) PLAT_SPM_BUF_BASE; - - /* Copy the boot information into the shared buffer with the SP. */ - assert((uintptr_t)shared_buf_ptr + sizeof(secure_partition_boot_info_t) - <= (PLAT_SPM_BUF_BASE + PLAT_SPM_BUF_SIZE)); - - assert(PLAT_SPM_BUF_BASE <= (UINTPTR_MAX - PLAT_SPM_BUF_SIZE + 1)); - - const secure_partition_boot_info_t *sp_boot_info = - plat_get_secure_partition_boot_info(NULL); - - assert(sp_boot_info != NULL); - - memcpy((void *) shared_buf_ptr, (const void *) sp_boot_info, - sizeof(secure_partition_boot_info_t)); - - /* Pointer to the MP information from the platform port. */ - secure_partition_mp_info_t *sp_mp_info = - ((secure_partition_boot_info_t *) shared_buf_ptr)->mp_info; - - assert(sp_mp_info != NULL); - - /* - * Point the shared buffer MP information pointer to where the info will - * be populated, just after the boot info. - */ - ((secure_partition_boot_info_t *) shared_buf_ptr)->mp_info = - (secure_partition_mp_info_t *) ((uintptr_t)shared_buf_ptr - + sizeof(secure_partition_boot_info_t)); - - /* - * Update the shared buffer pointer to where the MP information for the - * payload will be populated - */ - shared_buf_ptr = ((secure_partition_boot_info_t *) shared_buf_ptr)->mp_info; - - /* - * Copy the cpu information into the shared buffer area after the boot - * information. - */ - assert(sp_boot_info->num_cpus <= PLATFORM_CORE_COUNT); - - assert((uintptr_t)shared_buf_ptr - <= (PLAT_SPM_BUF_BASE + PLAT_SPM_BUF_SIZE - - (sp_boot_info->num_cpus * sizeof(*sp_mp_info)))); - - memcpy(shared_buf_ptr, (const void *) sp_mp_info, - sp_boot_info->num_cpus * sizeof(*sp_mp_info)); - - /* - * Calculate the linear indices of cores in boot information for the - * secure partition and flag the primary CPU - */ - sp_mp_info = (secure_partition_mp_info_t *) shared_buf_ptr; - - for (unsigned int index = 0; index < sp_boot_info->num_cpus; index++) { - u_register_t mpidr = sp_mp_info[index].mpidr; - - sp_mp_info[index].linear_id = plat_core_pos_by_mpidr(mpidr); - if (plat_my_core_pos() == sp_mp_info[index].linear_id) - sp_mp_info[index].flags |= MP_INFO_FLAG_PRIMARY_CPU; - } - - VERBOSE("S-EL1/S-EL0 context setup end.\n"); -} diff --git a/services/std_svc/spm/sp_setup.c b/services/std_svc/spm/sp_setup.c new file mode 100644 index 0000000..de27e3e --- /dev/null +++ b/services/std_svc/spm/sp_setup.c @@ -0,0 +1,300 @@ +/* + * Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "spm_private.h" +#include "spm_shim_private.h" + +/* Setup context of the Secure Partition */ +void spm_sp_setup(sp_context_t *sp_ctx) +{ + cpu_context_t *ctx = &(sp_ctx->cpu_ctx); + + /* + * Initialize CPU context + * ---------------------- + */ + + entry_point_info_t ep_info = {0}; + + SET_PARAM_HEAD(&ep_info, PARAM_EP, VERSION_1, SECURE | EP_ST_ENABLE); + ep_info.pc = BL32_BASE; + ep_info.spsr = SPSR_64(MODE_EL0, MODE_SP_EL0, DISABLE_ALL_EXCEPTIONS); + + cm_setup_context(ctx, &ep_info); + + /* + * General-Purpose registers + * ------------------------- + */ + + /* + * X0: Virtual address of a buffer shared between EL3 and Secure EL0. + * The buffer will be mapped in the Secure EL1 translation regime + * with Normal IS WBWA attributes and RO data and Execute Never + * instruction access permissions. + * + * X1: Size of the buffer in bytes + * + * X2: cookie value (Implementation Defined) + * + * X3: cookie value (Implementation Defined) + * + * X4 to X30 = 0 (already done by cm_init_my_context()) + */ + write_ctx_reg(get_gpregs_ctx(ctx), CTX_GPREG_X0, PLAT_SPM_BUF_BASE); + write_ctx_reg(get_gpregs_ctx(ctx), CTX_GPREG_X1, PLAT_SPM_BUF_SIZE); + write_ctx_reg(get_gpregs_ctx(ctx), CTX_GPREG_X2, PLAT_SPM_COOKIE_0); + write_ctx_reg(get_gpregs_ctx(ctx), CTX_GPREG_X3, PLAT_SPM_COOKIE_1); + + /* + * SP_EL0: A non-zero value will indicate to the SP that the SPM has + * initialized the stack pointer for the current CPU through + * implementation defined means. The value will be 0 otherwise. + */ + write_ctx_reg(get_gpregs_ctx(ctx), CTX_GPREG_SP_EL0, + PLAT_SP_IMAGE_STACK_BASE + PLAT_SP_IMAGE_STACK_PCPU_SIZE); + + /* + * Setup translation tables + * ------------------------ + */ + +#if ENABLE_ASSERTIONS + + /* Get max granularity supported by the platform. */ + + u_register_t id_aa64mmfr0_el1 = read_id_aa64mmfr0_el1(); + + int tgran64_supported = + ((id_aa64mmfr0_el1 >> ID_AA64MMFR0_EL1_TGRAN64_SHIFT) & + ID_AA64MMFR0_EL1_TGRAN64_MASK) == + ID_AA64MMFR0_EL1_TGRAN64_SUPPORTED; + + int tgran16_supported = + ((id_aa64mmfr0_el1 >> ID_AA64MMFR0_EL1_TGRAN16_SHIFT) & + ID_AA64MMFR0_EL1_TGRAN16_MASK) == + ID_AA64MMFR0_EL1_TGRAN16_SUPPORTED; + + int tgran4_supported = + ((id_aa64mmfr0_el1 >> ID_AA64MMFR0_EL1_TGRAN4_SHIFT) & + ID_AA64MMFR0_EL1_TGRAN4_MASK) == + ID_AA64MMFR0_EL1_TGRAN4_SUPPORTED; + + uintptr_t max_granule_size; + + if (tgran64_supported) { + max_granule_size = 64 * 1024; + } else if (tgran16_supported) { + max_granule_size = 16 * 1024; + } else { + assert(tgran4_supported); + max_granule_size = 4 * 1024; + } + + VERBOSE("Max translation granule supported: %lu KiB\n", + max_granule_size / 1024); + + uintptr_t max_granule_size_mask = max_granule_size - 1; + + /* Base must be aligned to the max granularity */ + assert((ARM_SP_IMAGE_NS_BUF_BASE & max_granule_size_mask) == 0); + + /* Size must be a multiple of the max granularity */ + assert((ARM_SP_IMAGE_NS_BUF_SIZE & max_granule_size_mask) == 0); + +#endif /* ENABLE_ASSERTIONS */ + + /* This region contains the exception vectors used at S-EL1. */ + const mmap_region_t sel1_exception_vectors = + MAP_REGION_FLAT(SPM_SHIM_EXCEPTIONS_START, + SPM_SHIM_EXCEPTIONS_SIZE, + MT_CODE | MT_SECURE | MT_PRIVILEGED); + mmap_add_region_ctx(sp_ctx->xlat_ctx_handle, + &sel1_exception_vectors); + + mmap_add_ctx(sp_ctx->xlat_ctx_handle, + plat_get_secure_partition_mmap(NULL)); + + init_xlat_tables_ctx(sp_ctx->xlat_ctx_handle); + + /* + * MMU-related registers + * --------------------- + */ + + /* Set attributes in the right indices of the MAIR */ + u_register_t mair_el1 = + MAIR_ATTR_SET(ATTR_DEVICE, ATTR_DEVICE_INDEX) | + MAIR_ATTR_SET(ATTR_IWBWA_OWBWA_NTR, ATTR_IWBWA_OWBWA_NTR_INDEX) | + MAIR_ATTR_SET(ATTR_NON_CACHEABLE, ATTR_NON_CACHEABLE_INDEX); + + write_ctx_reg(get_sysregs_ctx(ctx), CTX_MAIR_EL1, mair_el1); + + /* Setup TCR_EL1. */ + u_register_t tcr_ps_bits = tcr_physical_addr_size_bits(PLAT_PHY_ADDR_SPACE_SIZE); + + u_register_t tcr_el1 = + /* Size of region addressed by TTBR0_EL1 = 2^(64-T0SZ) bytes. */ + (64 - __builtin_ctzl(PLAT_VIRT_ADDR_SPACE_SIZE)) | + /* Inner and outer WBWA, shareable. */ + TCR_SH_INNER_SHAREABLE | TCR_RGN_OUTER_WBA | TCR_RGN_INNER_WBA | + /* Set the granularity to 4KB. */ + TCR_TG0_4K | + /* Limit Intermediate Physical Address Size. */ + tcr_ps_bits << TCR_EL1_IPS_SHIFT | + /* Disable translations using TBBR1_EL1. */ + TCR_EPD1_BIT + /* The remaining fields related to TBBR1_EL1 are left as zero. */ + ; + + tcr_el1 &= ~( + /* Enable translations using TBBR0_EL1 */ + TCR_EPD0_BIT + ); + + write_ctx_reg(get_sysregs_ctx(ctx), CTX_TCR_EL1, tcr_el1); + + /* Setup SCTLR_EL1 */ + u_register_t sctlr_el1 = read_ctx_reg(get_sysregs_ctx(ctx), CTX_SCTLR_EL1); + + sctlr_el1 |= + /*SCTLR_EL1_RES1 |*/ + /* Don't trap DC CVAU, DC CIVAC, DC CVAC, DC CVAP, or IC IVAU */ + SCTLR_UCI_BIT | + /* RW regions at xlat regime EL1&0 are forced to be XN. */ + SCTLR_WXN_BIT | + /* Don't trap to EL1 execution of WFI or WFE at EL0. */ + SCTLR_NTWI_BIT | SCTLR_NTWE_BIT | + /* Don't trap to EL1 accesses to CTR_EL0 from EL0. */ + SCTLR_UCT_BIT | + /* Don't trap to EL1 execution of DZ ZVA at EL0. */ + SCTLR_DZE_BIT | + /* Enable SP Alignment check for EL0 */ + SCTLR_SA0_BIT | + /* Allow cacheable data and instr. accesses to normal memory. */ + SCTLR_C_BIT | SCTLR_I_BIT | + /* Alignment fault checking enabled when at EL1 and EL0. */ + SCTLR_A_BIT | + /* Enable MMU. */ + SCTLR_M_BIT + ; + + sctlr_el1 &= ~( + /* Explicit data accesses at EL0 are little-endian. */ + SCTLR_E0E_BIT | + /* Accesses to DAIF from EL0 are trapped to EL1. */ + SCTLR_UMA_BIT + ); + + write_ctx_reg(get_sysregs_ctx(ctx), CTX_SCTLR_EL1, sctlr_el1); + + uint64_t *xlat_base = + ((xlat_ctx_t *)sp_ctx->xlat_ctx_handle)->base_table; + + /* Point TTBR0_EL1 at the tables of the context created for the SP. */ + write_ctx_reg(get_sysregs_ctx(ctx), CTX_TTBR0_EL1, + (u_register_t)xlat_base); + + /* + * Setup other system registers + * ---------------------------- + */ + + /* Shim Exception Vector Base Address */ + write_ctx_reg(get_sysregs_ctx(ctx), CTX_VBAR_EL1, + SPM_SHIM_EXCEPTIONS_PTR); + + /* + * FPEN: Allow the Secure Partition to access FP/SIMD registers. + * Note that SPM will not do any saving/restoring of these registers on + * behalf of the SP. This falls under the SP's responsibility. + * TTA: Enable access to trace registers. + * ZEN (v8.2): Trap SVE instructions and access to SVE registers. + */ + write_ctx_reg(get_sysregs_ctx(ctx), CTX_CPACR_EL1, + CPACR_EL1_FPEN(CPACR_EL1_FP_TRAP_NONE)); + + /* + * Prepare information in buffer shared between EL3 and S-EL0 + * ---------------------------------------------------------- + */ + + void *shared_buf_ptr = (void *) PLAT_SPM_BUF_BASE; + + /* Copy the boot information into the shared buffer with the SP. */ + assert((uintptr_t)shared_buf_ptr + sizeof(secure_partition_boot_info_t) + <= (PLAT_SPM_BUF_BASE + PLAT_SPM_BUF_SIZE)); + + assert(PLAT_SPM_BUF_BASE <= (UINTPTR_MAX - PLAT_SPM_BUF_SIZE + 1)); + + const secure_partition_boot_info_t *sp_boot_info = + plat_get_secure_partition_boot_info(NULL); + + assert(sp_boot_info != NULL); + + memcpy((void *) shared_buf_ptr, (const void *) sp_boot_info, + sizeof(secure_partition_boot_info_t)); + + /* Pointer to the MP information from the platform port. */ + secure_partition_mp_info_t *sp_mp_info = + ((secure_partition_boot_info_t *) shared_buf_ptr)->mp_info; + + assert(sp_mp_info != NULL); + + /* + * Point the shared buffer MP information pointer to where the info will + * be populated, just after the boot info. + */ + ((secure_partition_boot_info_t *) shared_buf_ptr)->mp_info = + (secure_partition_mp_info_t *) ((uintptr_t)shared_buf_ptr + + sizeof(secure_partition_boot_info_t)); + + /* + * Update the shared buffer pointer to where the MP information for the + * payload will be populated + */ + shared_buf_ptr = ((secure_partition_boot_info_t *) shared_buf_ptr)->mp_info; + + /* + * Copy the cpu information into the shared buffer area after the boot + * information. + */ + assert(sp_boot_info->num_cpus <= PLATFORM_CORE_COUNT); + + assert((uintptr_t)shared_buf_ptr + <= (PLAT_SPM_BUF_BASE + PLAT_SPM_BUF_SIZE - + (sp_boot_info->num_cpus * sizeof(*sp_mp_info)))); + + memcpy(shared_buf_ptr, (const void *) sp_mp_info, + sp_boot_info->num_cpus * sizeof(*sp_mp_info)); + + /* + * Calculate the linear indices of cores in boot information for the + * secure partition and flag the primary CPU + */ + sp_mp_info = (secure_partition_mp_info_t *) shared_buf_ptr; + + for (unsigned int index = 0; index < sp_boot_info->num_cpus; index++) { + u_register_t mpidr = sp_mp_info[index].mpidr; + + sp_mp_info[index].linear_id = plat_core_pos_by_mpidr(mpidr); + if (plat_my_core_pos() == sp_mp_info[index].linear_id) + sp_mp_info[index].flags |= MP_INFO_FLAG_PRIMARY_CPU; + } +} diff --git a/services/std_svc/spm/sp_xlat.c b/services/std_svc/spm/sp_xlat.c new file mode 100644 index 0000000..2aa2fa1 --- /dev/null +++ b/services/std_svc/spm/sp_xlat.c @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2018, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "spm_private.h" +#include "spm_shim_private.h" + +/* Place translation tables by default along with the ones used by BL31. */ +#ifndef PLAT_SP_IMAGE_XLAT_SECTION_NAME +#define PLAT_SP_IMAGE_XLAT_SECTION_NAME "xlat_table" +#endif + +/* Allocate and initialise the translation context for the secure partitions. */ +REGISTER_XLAT_CONTEXT2(sp, + PLAT_SP_IMAGE_MMAP_REGIONS, + PLAT_SP_IMAGE_MAX_XLAT_TABLES, + PLAT_VIRT_ADDR_SPACE_SIZE, PLAT_PHY_ADDR_SPACE_SIZE, + EL1_EL0_REGIME, PLAT_SP_IMAGE_XLAT_SECTION_NAME); + +/* Lock used for SP_MEMORY_ATTRIBUTES_GET and SP_MEMORY_ATTRIBUTES_SET */ +static spinlock_t mem_attr_smc_lock; + +/* Get handle of Secure Partition translation context */ +xlat_ctx_t *spm_get_sp_xlat_context(void) +{ + return &sp_xlat_ctx; +}; + +/* + * Attributes are encoded using a different format in the SMC interface than in + * the Trusted Firmware, where the mmap_attr_t enum type is used. This function + * converts an attributes value from the SMC format to the mmap_attr_t format by + * setting MT_RW/MT_RO, MT_USER/MT_PRIVILEGED and MT_EXECUTE/MT_EXECUTE_NEVER. + * The other fields are left as 0 because they are ignored by the function + * change_mem_attributes(). + */ +static unsigned int smc_attr_to_mmap_attr(unsigned int attributes) +{ + unsigned int tf_attr = 0U; + + unsigned int access = (attributes & SP_MEMORY_ATTRIBUTES_ACCESS_MASK) + >> SP_MEMORY_ATTRIBUTES_ACCESS_SHIFT; + + if (access == SP_MEMORY_ATTRIBUTES_ACCESS_RW) { + tf_attr |= MT_RW | MT_USER; + } else if (access == SP_MEMORY_ATTRIBUTES_ACCESS_RO) { + tf_attr |= MT_RO | MT_USER; + } else { + /* Other values are reserved. */ + assert(access == SP_MEMORY_ATTRIBUTES_ACCESS_NOACCESS); + /* The only requirement is that there's no access from EL0 */ + tf_attr |= MT_RO | MT_PRIVILEGED; + } + + if ((attributes & SP_MEMORY_ATTRIBUTES_NON_EXEC) == 0) { + tf_attr |= MT_EXECUTE; + } else { + tf_attr |= MT_EXECUTE_NEVER; + } + + return tf_attr; +} + +/* + * This function converts attributes from the Trusted Firmware format into the + * SMC interface format. + */ +static unsigned int smc_mmap_to_smc_attr(unsigned int attr) +{ + unsigned int smc_attr = 0U; + + unsigned int data_access; + + if ((attr & MT_USER) == 0) { + /* No access from EL0. */ + data_access = SP_MEMORY_ATTRIBUTES_ACCESS_NOACCESS; + } else { + if ((attr & MT_RW) != 0) { + assert(MT_TYPE(attr) != MT_DEVICE); + data_access = SP_MEMORY_ATTRIBUTES_ACCESS_RW; + } else { + data_access = SP_MEMORY_ATTRIBUTES_ACCESS_RO; + } + } + + smc_attr |= (data_access & SP_MEMORY_ATTRIBUTES_ACCESS_MASK) + << SP_MEMORY_ATTRIBUTES_ACCESS_SHIFT; + + if ((attr & MT_EXECUTE_NEVER) != 0U) { + smc_attr |= SP_MEMORY_ATTRIBUTES_NON_EXEC; + } + + return smc_attr; +} + +int32_t spm_memory_attributes_get_smc_handler(sp_context_t *sp_ctx, + uintptr_t base_va) +{ + uint32_t attributes; + + spin_lock(&mem_attr_smc_lock); + + int rc = get_mem_attributes(sp_ctx->xlat_ctx_handle, + base_va, &attributes); + + spin_unlock(&mem_attr_smc_lock); + + /* Convert error codes of get_mem_attributes() into SPM ones. */ + assert((rc == 0) || (rc == -EINVAL)); + + if (rc == 0) { + return (int32_t) smc_mmap_to_smc_attr(attributes); + } else { + return SPM_INVALID_PARAMETER; + } +} + +int spm_memory_attributes_set_smc_handler(sp_context_t *sp_ctx, + u_register_t page_address, + u_register_t pages_count, + u_register_t smc_attributes) +{ + uintptr_t base_va = (uintptr_t) page_address; + size_t size = (size_t) (pages_count * PAGE_SIZE); + uint32_t attributes = (uint32_t) smc_attributes; + + INFO(" Start address : 0x%lx\n", base_va); + INFO(" Number of pages: %i (%zi bytes)\n", (int) pages_count, size); + INFO(" Attributes : 0x%x\n", attributes); + + spin_lock(&mem_attr_smc_lock); + + int ret = change_mem_attributes(sp_ctx->xlat_ctx_handle, + base_va, size, + smc_attr_to_mmap_attr(attributes)); + + spin_unlock(&mem_attr_smc_lock); + + /* Convert error codes of change_mem_attributes() into SPM ones. */ + assert((ret == 0) || (ret == -EINVAL)); + + return (ret == 0) ? SPM_SUCCESS : SPM_INVALID_PARAMETER; +} diff --git a/services/std_svc/spm/spm.mk b/services/std_svc/spm/spm.mk index 562eaee..0e77086 100644 --- a/services/std_svc/spm/spm.mk +++ b/services/std_svc/spm/spm.mk @@ -1,5 +1,5 @@ # -# Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. +# Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -11,14 +11,12 @@ $(error "Error: SPM is only supported on aarch64.") endif -# SPM sources - - SPM_SOURCES := $(addprefix services/std_svc/spm/, \ - spm_main.c \ ${ARCH}/spm_helpers.S \ - secure_partition_setup.c \ - ${ARCH}/spm_shim_exceptions.S) + ${ARCH}/spm_shim_exceptions.S \ + spm_main.c \ + sp_setup.c \ + sp_xlat.c) # Let the top-level Makefile know that we intend to include a BL32 image diff --git a/services/std_svc/spm/spm_main.c b/services/std_svc/spm/spm_main.c index e0fe494..be76dc2 100644 --- a/services/std_svc/spm/spm_main.c +++ b/services/std_svc/spm/spm_main.c @@ -23,320 +23,229 @@ #include "spm_private.h" -/* Lock used for SP_MEMORY_ATTRIBUTES_GET and SP_MEMORY_ATTRIBUTES_SET */ -static spinlock_t mem_attr_smc_lock; - /******************************************************************************* * Secure Partition context information. ******************************************************************************/ -static secure_partition_context_t sp_ctx; +static sp_context_t sp_ctx; /******************************************************************************* - * Replace the S-EL1 re-entry information with S-EL0 re-entry - * information + * Set state of a Secure Partition context. ******************************************************************************/ -static void spm_setup_next_eret_into_sel0(const cpu_context_t *secure_context) +void sp_state_set(sp_context_t *sp_ptr, sp_state_t state) { - assert(secure_context == cm_get_context(SECURE)); - - cm_set_elr_spsr_el3(SECURE, read_elr_el1(), read_spsr_el1()); + spin_lock(&(sp_ptr->state_lock)); + sp_ptr->state = state; + spin_unlock(&(sp_ptr->state_lock)); } /******************************************************************************* - * This function takes an SP context pointer and: - * 1. Applies the S-EL1 system register context from sp_ctx->cpu_ctx. - * 2. Saves the current C runtime state (callee-saved registers) on the stack - * frame and saves a reference to this state. - * 3. Calls el3_exit() so that the EL3 system and general purpose registers - * from the sp_ctx->cpu_ctx are used to enter the secure partition image. + * Wait until the state of a Secure Partition is the specified one and change it + * to the desired state. ******************************************************************************/ -static uint64_t spm_synchronous_sp_entry(secure_partition_context_t *sp_ctx_ptr) +void sp_state_wait_switch(sp_context_t *sp_ptr, sp_state_t from, sp_state_t to) { - uint64_t rc; + int success = 0; - assert(sp_ctx_ptr != NULL); - assert(sp_ctx_ptr->c_rt_ctx == 0); - assert(cm_get_context(SECURE) == &sp_ctx_ptr->cpu_ctx); + while (success == 0) { + spin_lock(&(sp_ptr->state_lock)); - /* Apply the Secure EL1 system register context and switch to it */ + if (sp_ptr->state == from) { + sp_ptr->state = to; + + success = 1; + } + + spin_unlock(&(sp_ptr->state_lock)); + } +} + +/******************************************************************************* + * Check if the state of a Secure Partition is the specified one and, if so, + * change it to the desired state. Returns 0 on success, -1 on error. + ******************************************************************************/ +int sp_state_try_switch(sp_context_t *sp_ptr, sp_state_t from, sp_state_t to) +{ + int ret = -1; + + spin_lock(&(sp_ptr->state_lock)); + + if (sp_ptr->state == from) { + sp_ptr->state = to; + + ret = 0; + } + + spin_unlock(&(sp_ptr->state_lock)); + + return ret; +} + +/******************************************************************************* + * This function takes an SP context pointer and prepares the CPU to enter. + ******************************************************************************/ +static void spm_sp_prepare_enter(sp_context_t *sp_ctx) +{ + assert(sp_ctx != NULL); + + /* Assign the context of the SP to this CPU */ + cm_set_context(&(sp_ctx->cpu_ctx), SECURE); + + /* Restore the context assigned above */ cm_el1_sysregs_context_restore(SECURE); cm_set_next_eret_context(SECURE); - VERBOSE("%s: We're about to enter the Secure partition...\n", __func__); - - rc = spm_secure_partition_enter(&sp_ctx_ptr->c_rt_ctx); -#if ENABLE_ASSERTIONS - sp_ctx_ptr->c_rt_ctx = 0; -#endif - - return rc; + /* Invalidate TLBs at EL1. */ + tlbivmalle1(); + dsbish(); } - /******************************************************************************* - * This function takes a Secure partition context pointer and: - * 1. Saves the S-EL1 system register context to sp_ctx->cpu_ctx. - * 2. Restores the current C runtime state (callee saved registers) from the - * stack frame using the reference to this state saved in - * spm_secure_partition_enter(). - * 3. It does not need to save any general purpose or EL3 system register state - * as the generic smc entry routine should have saved those. + * Enter SP after preparing it with spm_sp_prepare_enter(). ******************************************************************************/ -static void __dead2 spm_synchronous_sp_exit( - const secure_partition_context_t *sp_ctx_ptr, uint64_t ret) +static uint64_t spm_sp_enter(sp_context_t *sp_ctx) { - assert(sp_ctx_ptr != NULL); - /* Save the Secure EL1 system register context */ - assert(cm_get_context(SECURE) == &sp_ctx_ptr->cpu_ctx); - cm_el1_sysregs_context_save(SECURE); - - assert(sp_ctx_ptr->c_rt_ctx != 0U); - spm_secure_partition_exit(sp_ctx_ptr->c_rt_ctx, ret); - - /* Should never reach here */ - assert(0); + /* Enter Secure Partition */ + return spm_secure_partition_enter(&sp_ctx->c_rt_ctx); } /******************************************************************************* - * This function passes control to the Secure Partition image (BL32) for the - * first time on the primary cpu after a cold boot. It assumes that a valid - * secure context has already been created by spm_setup() which can be directly - * used. This function performs a synchronous entry into the Secure partition. - * The SP passes control back to this routine through a SMC. + * Jump to each Secure Partition for the first time. ******************************************************************************/ static int32_t spm_init(void) { - entry_point_info_t *secure_partition_ep_info; - uint64_t rc; + uint64_t rc = 0; + sp_context_t *ctx; - VERBOSE("%s entry\n", __func__); + INFO("Secure Partition init...\n"); - /* - * Get information about the Secure Partition (BL32) image. Its - * absence is a critical failure. - */ - secure_partition_ep_info = bl31_plat_get_next_image_ep_info(SECURE); - assert(secure_partition_ep_info != NULL); + ctx = &sp_ctx; - /* - * Initialise the common context and then overlay the S-EL0 specific - * context on top of it. - */ - cm_init_my_context(secure_partition_ep_info); - secure_partition_setup(); + ctx->state = SP_STATE_RESET; - /* - * Make all CPUs use the same secure context. - */ - for (unsigned int i = 0; i < PLATFORM_CORE_COUNT; i++) { - cm_set_context_by_index(i, &sp_ctx.cpu_ctx, SECURE); - } - - /* - * Arrange for an entry into the secure partition. - */ - sp_ctx.sp_init_in_progress = 1; - rc = spm_synchronous_sp_entry(&sp_ctx); + spm_sp_prepare_enter(ctx); + rc |= spm_sp_enter(ctx); assert(rc == 0); - sp_ctx.sp_init_in_progress = 0; - VERBOSE("SP_MEMORY_ATTRIBUTES_SET_AARCH64 availability has been revoked\n"); + + ctx->state = SP_STATE_IDLE; + + INFO("Secure Partition initialized.\n"); return rc; } /******************************************************************************* - * Given a secure partition entrypoint info pointer, entry point PC & pointer to - * a context data structure, this function will initialize the SPM context and - * entry point info for the secure partition. - ******************************************************************************/ -void spm_init_sp_ep_state(struct entry_point_info *sp_ep_info, - uint64_t pc, - secure_partition_context_t *sp_ctx_ptr) -{ - uint32_t ep_attr; - - assert(sp_ep_info != NULL); - assert(pc != 0U); - assert(sp_ctx_ptr != NULL); - - cm_set_context(&sp_ctx_ptr->cpu_ctx, SECURE); - - /* initialise an entrypoint to set up the CPU context */ - ep_attr = SECURE | EP_ST_ENABLE; - if ((read_sctlr_el3() & SCTLR_EE_BIT) != 0U) - ep_attr |= EP_EE_BIG; - SET_PARAM_HEAD(sp_ep_info, PARAM_EP, VERSION_1, ep_attr); - - sp_ep_info->pc = pc; - /* The secure partition runs in S-EL0. */ - sp_ep_info->spsr = SPSR_64(MODE_EL0, - MODE_SP_EL0, - DISABLE_ALL_EXCEPTIONS); - - zeromem(&sp_ep_info->args, sizeof(sp_ep_info->args)); -} - -/******************************************************************************* - * Secure Partition Manager setup. The SPM finds out the SP entrypoint if not - * already known and initialises the context for entry into the SP for its - * initialisation. + * Initialize contexts of all Secure Partitions. ******************************************************************************/ int32_t spm_setup(void) { - entry_point_info_t *secure_partition_ep_info; + sp_context_t *ctx; - VERBOSE("%s entry\n", __func__); + /* Disable MMU at EL1 (initialized by BL2) */ + disable_mmu_icache_el1(); - /* - * Get information about the Secure Partition (BL32) image. Its - * absence is a critical failure. - */ - secure_partition_ep_info = bl31_plat_get_next_image_ep_info(SECURE); - if (secure_partition_ep_info == NULL) { - WARN("No SPM provided by BL2 boot loader, Booting device" - " without SPM initialization. SMCs destined for SPM" - " will return SMC_UNK\n"); - return 1; - } + /* Initialize context of the SP */ + INFO("Secure Partition context setup start...\n"); - /* - * If there's no valid entry point for SP, we return a non-zero value - * signalling failure initializing the service. We bail out without - * registering any handlers - */ - if (secure_partition_ep_info->pc == 0U) { - return 1; - } + ctx = &sp_ctx; - spm_init_sp_ep_state(secure_partition_ep_info, - secure_partition_ep_info->pc, - &sp_ctx); + /* Assign translation tables context. */ + ctx->xlat_ctx_handle = spm_get_sp_xlat_context(); - /* - * All SPM initialization done. Now register our init function with - * BL31 for deferred invocation - */ + spm_sp_setup(ctx); + + /* Register init function for deferred init. */ bl31_register_bl32_init(&spm_init); - VERBOSE("%s exit\n", __func__); + INFO("Secure Partition setup done.\n"); return 0; } -/* - * Attributes are encoded using a different format in the SMC interface than in - * the Trusted Firmware, where the mmap_attr_t enum type is used. This function - * converts an attributes value from the SMC format to the mmap_attr_t format by - * setting MT_RW/MT_RO, MT_USER/MT_PRIVILEGED and MT_EXECUTE/MT_EXECUTE_NEVER. - * The other fields are left as 0 because they are ignored by the function - * change_mem_attributes(). - */ -static unsigned int smc_attr_to_mmap_attr(unsigned int attributes) +/******************************************************************************* + * MM_COMMUNICATE handler + ******************************************************************************/ +static uint64_t mm_communicate(uint32_t smc_fid, uint64_t mm_cookie, + uint64_t comm_buffer_address, + uint64_t comm_size_address, void *handle) { - unsigned int tf_attr = 0U; + sp_context_t *ctx = &sp_ctx; - unsigned int access = (attributes & SP_MEMORY_ATTRIBUTES_ACCESS_MASK) - >> SP_MEMORY_ATTRIBUTES_ACCESS_SHIFT; - - if (access == SP_MEMORY_ATTRIBUTES_ACCESS_RW) { - tf_attr |= MT_RW | MT_USER; - } else if (access == SP_MEMORY_ATTRIBUTES_ACCESS_RO) { - tf_attr |= MT_RO | MT_USER; - } else { - /* Other values are reserved. */ - assert(access == SP_MEMORY_ATTRIBUTES_ACCESS_NOACCESS); - /* The only requirement is that there's no access from EL0 */ - tf_attr |= MT_RO | MT_PRIVILEGED; + /* Cookie. Reserved for future use. It must be zero. */ + if (mm_cookie != 0U) { + ERROR("MM_COMMUNICATE: cookie is not zero\n"); + SMC_RET1(handle, SPM_INVALID_PARAMETER); } - if ((attributes & SP_MEMORY_ATTRIBUTES_NON_EXEC) == 0) { - tf_attr |= MT_EXECUTE; - } else { - tf_attr |= MT_EXECUTE_NEVER; + if (comm_buffer_address == 0U) { + ERROR("MM_COMMUNICATE: comm_buffer_address is zero\n"); + SMC_RET1(handle, SPM_INVALID_PARAMETER); } - return tf_attr; + if (comm_size_address != 0U) { + VERBOSE("MM_COMMUNICATE: comm_size_address is not 0 as recommended.\n"); + } + + /* Save the Normal world context */ + cm_el1_sysregs_context_save(NON_SECURE); + + /* Wait until the Secure Partition is IDLE and set it to BUSY. */ + sp_state_wait_switch(ctx, SP_STATE_IDLE, SP_STATE_BUSY); + + /* Jump to the Secure Partition. */ + spm_sp_prepare_enter(ctx); + + SMC_RET4(&(ctx->cpu_ctx), smc_fid, comm_buffer_address, + comm_size_address, plat_my_core_pos()); } -/* - * This function converts attributes from the Trusted Firmware format into the - * SMC interface format. - */ -static unsigned int smc_mmap_to_smc_attr(unsigned int attr) +/******************************************************************************* + * SP_EVENT_COMPLETE_AARCH64 handler + ******************************************************************************/ +static uint64_t sp_event_complete(uint64_t x1) { - unsigned int smc_attr = 0U; + sp_context_t *ctx = &sp_ctx; - unsigned int data_access; + /* Save secure state */ + cm_el1_sysregs_context_save(SECURE); - if ((attr & MT_USER) == 0) { - /* No access from EL0. */ - data_access = SP_MEMORY_ATTRIBUTES_ACCESS_NOACCESS; - } else { - if ((attr & MT_RW) != 0) { - assert(MT_TYPE(attr) != MT_DEVICE); - data_access = SP_MEMORY_ATTRIBUTES_ACCESS_RW; - } else { - data_access = SP_MEMORY_ATTRIBUTES_ACCESS_RO; - } + if (ctx->state == SP_STATE_RESET) { + /* + * SPM reports completion. The SPM must have initiated the + * original request through a synchronous entry into the secure + * partition. Jump back to the original C runtime context. + */ + spm_secure_partition_exit(ctx->c_rt_ctx, x1); + + /* spm_secure_partition_exit doesn't return */ } - smc_attr |= (data_access & SP_MEMORY_ATTRIBUTES_ACCESS_MASK) - << SP_MEMORY_ATTRIBUTES_ACCESS_SHIFT; + /* + * This is the result from the Secure partition of an earlier request. + * Copy the result into the non-secure context and return to the + * non-secure state. + */ - if ((attr & MT_EXECUTE_NEVER) != 0U) { - smc_attr |= SP_MEMORY_ATTRIBUTES_NON_EXEC; - } + /* Mark Secure Partition as idle */ + assert(ctx->state == SP_STATE_BUSY); - return smc_attr; + sp_state_set(ctx, SP_STATE_IDLE); + + /* Get a reference to the non-secure context */ + cpu_context_t *ns_cpu_context = cm_get_context(NON_SECURE); + + assert(ns_cpu_context != NULL); + + /* Restore non-secure state */ + cm_el1_sysregs_context_restore(NON_SECURE); + cm_set_next_eret_context(NON_SECURE); + + /* Return to non-secure world */ + SMC_RET1(ns_cpu_context, x1); } -static int32_t spm_memory_attributes_get_smc_handler(uintptr_t base_va) -{ - uint32_t attributes; - - spin_lock(&mem_attr_smc_lock); - - int rc = get_mem_attributes(secure_partition_xlat_ctx_handle, - base_va, &attributes); - - spin_unlock(&mem_attr_smc_lock); - - /* Convert error codes of get_mem_attributes() into SPM ones. */ - assert((rc == 0) || (rc == -EINVAL)); - - if (rc == 0) { - return (int32_t) smc_mmap_to_smc_attr(attributes); - } else { - return SPM_INVALID_PARAMETER; - } -} - -static int spm_memory_attributes_set_smc_handler(u_register_t page_address, - u_register_t pages_count, - u_register_t smc_attributes) -{ - uintptr_t base_va = (uintptr_t) page_address; - size_t size = (size_t) (pages_count * PAGE_SIZE); - uint32_t attributes = (uint32_t) smc_attributes; - - INFO(" Start address : 0x%lx\n", base_va); - INFO(" Number of pages: %i (%zi bytes)\n", (int) pages_count, size); - INFO(" Attributes : 0x%x\n", attributes); - - spin_lock(&mem_attr_smc_lock); - - int ret = change_mem_attributes(secure_partition_xlat_ctx_handle, - base_va, size, smc_attr_to_mmap_attr(attributes)); - - spin_unlock(&mem_attr_smc_lock); - - /* Convert error codes of change_mem_attributes() into SPM ones. */ - assert((ret == 0) || (ret == -EINVAL)); - - return (ret == 0) ? SPM_SUCCESS : SPM_INVALID_PARAMETER; -} - - +/******************************************************************************* + * Secure Partition Manager SMC handler. + ******************************************************************************/ uint64_t spm_smc_handler(uint32_t smc_fid, uint64_t x1, uint64_t x2, @@ -346,7 +255,6 @@ void *handle, uint64_t flags) { - cpu_context_t *ns_cpu_context; unsigned int ns; /* Determine which security state this SMC originated from */ @@ -356,66 +264,40 @@ /* Handle SMCs from Secure world. */ + assert(handle == cm_get_context(SECURE)); + + /* Make next ERET jump to S-EL0 instead of S-EL1. */ + cm_set_elr_spsr_el3(SECURE, read_elr_el1(), read_spsr_el1()); + switch (smc_fid) { case SPM_VERSION_AARCH32: SMC_RET1(handle, SPM_VERSION_COMPILED); case SP_EVENT_COMPLETE_AARCH64: - assert(handle == cm_get_context(SECURE)); - cm_el1_sysregs_context_save(SECURE); - spm_setup_next_eret_into_sel0(handle); - - if (sp_ctx.sp_init_in_progress) { - /* - * SPM reports completion. The SPM must have - * initiated the original request through a - * synchronous entry into the secure - * partition. Jump back to the original C - * runtime context. - */ - spm_synchronous_sp_exit(&sp_ctx, x1); - assert(0); - } - - /* Release the Secure Partition context */ - spin_unlock(&sp_ctx.lock); - - /* - * This is the result from the Secure partition of an - * earlier request. Copy the result into the non-secure - * context, save the secure state and return to the - * non-secure state. - */ - - /* Get a reference to the non-secure context */ - ns_cpu_context = cm_get_context(NON_SECURE); - assert(ns_cpu_context != NULL); - - /* Restore non-secure state */ - cm_el1_sysregs_context_restore(NON_SECURE); - cm_set_next_eret_context(NON_SECURE); - - /* Return to normal world */ - SMC_RET1(ns_cpu_context, x1); + return sp_event_complete(x1); case SP_MEMORY_ATTRIBUTES_GET_AARCH64: INFO("Received SP_MEMORY_ATTRIBUTES_GET_AARCH64 SMC\n"); - if (!sp_ctx.sp_init_in_progress) { + if (sp_ctx.state != SP_STATE_RESET) { WARN("SP_MEMORY_ATTRIBUTES_GET_AARCH64 is available at boot time only\n"); SMC_RET1(handle, SPM_NOT_SUPPORTED); } - SMC_RET1(handle, spm_memory_attributes_get_smc_handler(x1)); + SMC_RET1(handle, + spm_memory_attributes_get_smc_handler( + &sp_ctx, x1)); case SP_MEMORY_ATTRIBUTES_SET_AARCH64: INFO("Received SP_MEMORY_ATTRIBUTES_SET_AARCH64 SMC\n"); - if (!sp_ctx.sp_init_in_progress) { + if (sp_ctx.state != SP_STATE_RESET) { WARN("SP_MEMORY_ATTRIBUTES_SET_AARCH64 is available at boot time only\n"); SMC_RET1(handle, SPM_NOT_SUPPORTED); } - SMC_RET1(handle, spm_memory_attributes_set_smc_handler(x1, x2, x3)); + SMC_RET1(handle, + spm_memory_attributes_set_smc_handler( + &sp_ctx, x1, x2, x3)); default: break; } @@ -430,43 +312,7 @@ case MM_COMMUNICATE_AARCH32: case MM_COMMUNICATE_AARCH64: - { - uint64_t mm_cookie = x1; - uint64_t comm_buffer_address = x2; - uint64_t comm_size_address = x3; - - /* Cookie. Reserved for future use. It must be zero. */ - if (mm_cookie != 0U) { - ERROR("MM_COMMUNICATE: cookie is not zero\n"); - SMC_RET1(handle, SPM_INVALID_PARAMETER); - } - - if (comm_buffer_address == 0U) { - ERROR("MM_COMMUNICATE: comm_buffer_address is zero\n"); - SMC_RET1(handle, SPM_INVALID_PARAMETER); - } - - if (comm_size_address != 0U) { - VERBOSE("MM_COMMUNICATE: comm_size_address is not 0 as recommended.\n"); - } - - /* Save the Normal world context */ - cm_el1_sysregs_context_save(NON_SECURE); - - /* Lock the Secure Partition context. */ - spin_lock(&sp_ctx.lock); - - /* - * Restore the secure world context and prepare for - * entry in S-EL0 - */ - assert(&sp_ctx.cpu_ctx == cm_get_context(SECURE)); - cm_el1_sysregs_context_restore(SECURE); - cm_set_next_eret_context(SECURE); - - SMC_RET4(&sp_ctx.cpu_ctx, smc_fid, comm_buffer_address, - comm_size_address, plat_my_core_pos()); - } + return mm_communicate(smc_fid, x1, x2, x3, handle); case SP_MEMORY_ATTRIBUTES_GET_AARCH64: case SP_MEMORY_ATTRIBUTES_SET_AARCH64: diff --git a/services/std_svc/spm/spm_private.h b/services/std_svc/spm/spm_private.h index 1d16b45..64d8cf8 100644 --- a/services/std_svc/spm/spm_private.h +++ b/services/std_svc/spm/spm_private.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -29,30 +29,42 @@ #define SP_C_RT_CTX_SIZE 0x60 #define SP_C_RT_CTX_ENTRIES (SP_C_RT_CTX_SIZE >> DWORD_SHIFT) - #ifndef __ASSEMBLY__ #include #include #include -/* Handle on the Secure partition translation context */ -extern xlat_ctx_t *secure_partition_xlat_ctx_handle; +typedef enum secure_partition_state { + SP_STATE_RESET = 0, + SP_STATE_IDLE, + SP_STATE_BUSY +} sp_state_t; -struct entry_point_info; - -typedef struct secure_partition_context { +typedef struct sp_context { uint64_t c_rt_ctx; cpu_context_t cpu_ctx; - unsigned int sp_init_in_progress; - spinlock_t lock; -} secure_partition_context_t; + xlat_ctx_t *xlat_ctx_handle; + sp_state_t state; + spinlock_t state_lock; +} sp_context_t; + +/* Assembly helpers */ uint64_t spm_secure_partition_enter(uint64_t *c_rt_ctx); void __dead2 spm_secure_partition_exit(uint64_t c_rt_ctx, uint64_t ret); -void spm_init_sp_ep_state(struct entry_point_info *sp_ep_info, - uint64_t pc, - secure_partition_context_t *sp_ctx_ptr); + +void spm_sp_setup(sp_context_t *sp_ctx); + +xlat_ctx_t *spm_get_sp_xlat_context(void); + +int32_t spm_memory_attributes_get_smc_handler(sp_context_t *sp_ctx, + uintptr_t base_va); +int spm_memory_attributes_set_smc_handler(sp_context_t *sp_ctx, + u_register_t page_address, + u_register_t pages_count, + u_register_t smc_attributes); + #endif /* __ASSEMBLY__ */ #endif /* __SPM_PRIVATE_H__ */