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) {
 		/*