feat(el3-spmc): add support to setup S-EL0 context
Add support to setup S-EL0 context by setting up the following
S-EL1 shim exception handlers: This is a trampoline between S-EL0 and
monitor running at EL3 and is used to
handle or forward exceptions from S-EL0.
Boot Info region: This region holds the boot protocol data that is
passed between SPMC and SP.
Setup system registers: Setup sctlr_el1, vbar_el1, cntkctl_el1,
ctx_cpacr_el1(enable fp and smid), spsr and
sp_el0
Signed-off-by: Achin Gupta <achin.gupta@arm.com>
Signed-off-by: Nishant Sharma <nishant.sharma@arm.com>
Change-Id: I82d21fcd95529f235bee8bf838d36a2ac519bb0a
diff --git a/services/std_svc/spm/el3_spmc/spmc.h b/services/std_svc/spm/el3_spmc/spmc.h
index 48644ac..e4120de 100644
--- a/services/std_svc/spm/el3_spmc/spmc.h
+++ b/services/std_svc/spm/el3_spmc/spmc.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2022, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2022-2023, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
@@ -168,6 +168,9 @@
/* Mailbox tracking. */
struct mailbox mailbox;
+ /* Pointer to translation table context of a S-EL0 SP. */
+ xlat_ctx_t *xlat_ctx_handle;
+
/* Secondary entrypoint. Only valid for a S-EL1 SP. */
uintptr_t secondary_ep;
@@ -224,6 +227,9 @@
entry_point_info_t *ep_info);
void spmc_sp_common_ep_commit(struct secure_partition_desc *sp,
entry_point_info_t *ep_info);
+void spmc_el0_sp_spsr_setup(entry_point_info_t *ep_info);
+void spmc_el0_sp_setup(struct secure_partition_desc *sp,
+ int32_t boot_info_reg);
/*
* Helper function to perform a synchronous entry into a SP.
diff --git a/services/std_svc/spm/el3_spmc/spmc_main.c b/services/std_svc/spm/el3_spmc/spmc_main.c
index ada6f45..c4d5835 100644
--- a/services/std_svc/spm/el3_spmc/spmc_main.c
+++ b/services/std_svc/spm/el3_spmc/spmc_main.c
@@ -1588,7 +1588,7 @@
* since this is currently a hardcoded value for S-EL1 partitions
* we don't need to save it here, just validate.
*/
- if (config_32 != PLATFORM_CORE_COUNT) {
+ if ((sp->runtime_el == S_EL1) && (config_32 != PLATFORM_CORE_COUNT)) {
ERROR("SP Execution Context Count (%u) must be %u.\n",
config_32, PLATFORM_CORE_COUNT);
return -EINVAL;
@@ -1704,7 +1704,8 @@
* the manifest as boot information later.
*/
next_image_ep_info->args.arg1 = fdt_totalsize(sp_manifest);
- INFO("Manifest size = %lu bytes.\n", next_image_ep_info->args.arg1);
+ INFO("Manifest adr = %lx , size = %lu bytes\n", manifest_base,
+ next_image_ep_info->args.arg1);
/*
* Select an SP descriptor for initialising the partition's execution
@@ -1712,6 +1713,11 @@
*/
sp = spmc_get_current_sp_ctx();
+#if SPMC_AT_EL3_SEL0_SP
+ /* Assign translation tables context. */
+ sp_desc->xlat_ctx_handle = spm_get_sp_xlat_context();
+
+#endif /* SPMC_AT_EL3_SEL0_SP */
/* Initialize entry point information for the SP */
SET_PARAM_HEAD(next_image_ep_info, PARAM_EP, VERSION_1,
SECURE | EP_ST_ENABLE);
@@ -1725,7 +1731,7 @@
}
/* Check that the runtime EL in the manifest was correct. */
- if (sp->runtime_el != S_EL1) {
+ if (sp->runtime_el != S_EL0 && sp->runtime_el != S_EL1) {
ERROR("Unexpected runtime EL: %d\n", sp->runtime_el);
return -EINVAL;
}
@@ -1734,11 +1740,29 @@
spmc_sp_common_setup(sp, next_image_ep_info, boot_info_reg);
/* Perform any initialisation specific to S-EL1 SPs. */
- spmc_el1_sp_setup(sp, next_image_ep_info);
+ if (sp->runtime_el == S_EL1) {
+ spmc_el1_sp_setup(sp, next_image_ep_info);
+ }
+
+#if SPMC_AT_EL3_SEL0_SP
+ /* Setup spsr in endpoint info for common context management routine. */
+ if (sp->runtime_el == S_EL0) {
+ spmc_el0_sp_spsr_setup(next_image_ep_info);
+ }
+#endif /* SPMC_AT_EL3_SEL0_SP */
/* Initialize the SP context with the required ep info. */
spmc_sp_common_ep_commit(sp, next_image_ep_info);
+#if SPMC_AT_EL3_SEL0_SP
+ /*
+ * Perform any initialisation specific to S-EL0 not set by common
+ * context management routine.
+ */
+ if (sp->runtime_el == S_EL0) {
+ spmc_el0_sp_setup(sp, boot_info_reg);
+ }
+#endif /* SPMC_AT_EL3_SEL0_SP */
return 0;
}
diff --git a/services/std_svc/spm/el3_spmc/spmc_setup.c b/services/std_svc/spm/el3_spmc/spmc_setup.c
index 6de25f6..4299ced 100644
--- a/services/std_svc/spm/el3_spmc/spmc_setup.c
+++ b/services/std_svc/spm/el3_spmc/spmc_setup.c
@@ -20,6 +20,7 @@
#include <plat/common/platform.h>
#include <services/ffa_svc.h>
#include "spm_common.h"
+#include "spm_shim_private.h"
#include "spmc.h"
#include <tools_share/firmware_image_package.h>
@@ -31,6 +32,13 @@
static uint8_t ffa_boot_info_mem[PAGE_SIZE] __aligned(PAGE_SIZE);
/*
+ * We need to choose one execution context from all those available for a S-EL0
+ * SP. This execution context will be used subsequently irrespective of which
+ * physical CPU the SP runs on.
+ */
+#define SEL0_SP_EC_INDEX 0
+
+/*
* This function creates a initialization descriptor in the memory reserved
* for passing boot information to an SP. It then copies the partition manifest
* into this region and ensures that its reference in the initialization
@@ -143,13 +151,116 @@
}
/*
- * We are assuming that the index of the execution
- * context used is the linear index of the current physical cpu.
+ * S-EL1 partitions can be assigned with multiple execution contexts, each
+ * pinned to the physical CPU. Each execution context index corresponds to the
+ * respective liner core position.
+ * S-EL0 partitions execute in a single execution context (index 0).
*/
unsigned int get_ec_index(struct secure_partition_desc *sp)
{
+ return (sp->runtime_el == S_EL0) ?
+ SEL0_SP_EC_INDEX : plat_my_core_pos();
+}
+
+#if SPMC_AT_EL3_SEL0_SP
+/* Setup spsr in entry point info for common context management code to use. */
+void spmc_el0_sp_spsr_setup(entry_point_info_t *ep_info)
+{
- return plat_my_core_pos();
+ /* Setup Secure Partition SPSR for S-EL0 SP. */
+ ep_info->spsr = SPSR_64(MODE_EL0, MODE_SP_EL0, DISABLE_ALL_EXCEPTIONS);
+}
+
+/* Setup context of an EL0 Secure Partition. */
+void spmc_el0_sp_setup(struct secure_partition_desc *sp,
+ int32_t boot_info_reg)
+{
+ mmap_region_t sel1_exception_vectors =
+ MAP_REGION_FLAT(SPM_SHIM_EXCEPTIONS_START,
+ SPM_SHIM_EXCEPTIONS_SIZE,
+ MT_CODE | MT_SECURE | MT_PRIVILEGED);
+ cpu_context_t *ctx;
+
+ ctx = &sp->ec[SEL0_SP_EC_INDEX].cpu_ctx;
+
+ sp->xlat_ctx_handle->xlat_regime = EL1_EL0_REGIME;
+
+ /* This region contains the exception vectors used at S-EL1. */
+ mmap_add_region_ctx(sp->xlat_ctx_handle,
+ &sel1_exception_vectors);
+
+ /*
+ * If the SP manifest specified the register to pass the address of the
+ * boot information, then map the memory region to pass boot
+ * information.
+ */
+ if (boot_info_reg >= 0) {
+ mmap_region_t ffa_boot_info_region = MAP_REGION_FLAT(
+ (uintptr_t) ffa_boot_info_mem,
+ PAGE_SIZE,
+ MT_RO_DATA | MT_SECURE | MT_USER);
+ mmap_add_region_ctx(sp->xlat_ctx_handle, &ffa_boot_info_region);
+ }
+
+ /* Setup SCTLR_EL1 */
+ u_register_t sctlr_el1 = read_ctx_reg(get_el1_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 |
+ /* Don't change PSTATE.PAN on taking an exception to EL1 */
+ SCTLR_SPAN_BIT |
+ /* Allow cacheable data and instr. accesses to normal memory. */
+ SCTLR_C_BIT | SCTLR_I_BIT |
+ /* Enable MMU. */
+ SCTLR_M_BIT
+ ;
+
+ sctlr_el1 &= ~(
+ /* Explicit data accesses at EL0 are little-endian. */
+ SCTLR_E0E_BIT |
+ /*
+ * Alignment fault checking disabled when at EL1 and EL0 as
+ * the UEFI spec permits unaligned accesses.
+ */
+ SCTLR_A_BIT |
+ /* Accesses to DAIF from EL0 are trapped to EL1. */
+ SCTLR_UMA_BIT
+ );
+
+ write_ctx_reg(get_el1_sysregs_ctx(ctx), CTX_SCTLR_EL1, sctlr_el1);
+
+ /* Setup other system registers. */
+
+ /* Shim Exception Vector Base Address */
+ write_ctx_reg(get_el1_sysregs_ctx(ctx), CTX_VBAR_EL1,
+ SPM_SHIM_EXCEPTIONS_PTR);
+#if NS_TIMER_SWITCH
+ write_ctx_reg(get_el1_sysregs_ctx(ctx), CTX_CNTKCTL_EL1,
+ EL0PTEN_BIT | EL0VTEN_BIT | EL0PCTEN_BIT | EL0VCTEN_BIT);
+#endif
+
+ /*
+ * 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_el1_sysregs_ctx(ctx), CTX_CPACR_EL1,
+ CPACR_EL1_FPEN(CPACR_EL1_FP_TRAP_NONE));
}
+#endif /* SPMC_AT_EL3_SEL0_SP */
/* S-EL1 partition specific initialisation. */
void spmc_el1_sp_setup(struct secure_partition_desc *sp,
@@ -211,12 +322,6 @@
sp->sp_id = sp_id;
}
- /*
- * We currently only support S-EL1 partitions so ensure this is the
- * case.
- */
- assert(sp->runtime_el == S_EL1);
-
/* Check if the SP wants to use the FF-A boot protocol. */
if (boot_info_reg >= 0) {
/*