feat(spmc/lsp): add logical partition framework

Introduce a framework to support running logical
partitions alongside the SPMC in EL3  as per the
v1.1 FF-A spec.

The DECLARE_LOGICAL_PARTITION macro has been added to
simplify the process to define a Logical Partition.
The partitions themselves are statically allocated
with the descriptors placed in RO memory.

It is assumed that the MAX_EL3_LP_DESCS_COUNT will
be defined by the platform.

Change-Id: I1c2523e0ad2d9c5d36aeeef6b8bcb1e80db7c443
Signed-off-by: Marc Bonnici <marc.bonnici@arm.com>
diff --git a/include/common/bl_common.h b/include/common/bl_common.h
index 8cb4990..3a06cfb 100644
--- a/include/common/bl_common.h
+++ b/include/common/bl_common.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013-2021, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2013-2022, ARM Limited and Contributors. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
@@ -81,6 +81,10 @@
 #define __RODATA_END__			Load$$__RODATA_EPILOGUE__$$Base
 #define __RT_SVC_DESCS_START__		Load$$__RT_SVC_DESCS__$$Base
 #define __RT_SVC_DESCS_END__		Load$$__RT_SVC_DESCS__$$Limit
+#if SPMC_AT_EL3
+#define __EL3_LP_DESCS_START__		Load$$__EL3_LP_DESCS__$$Base
+#define __EL3_LP_DESCS_END__		Load$$__EL3_LP_DESCS__$$Limit
+#endif
 #define __RW_START__			Load$$LR$$LR_RW_DATA$$Base
 #define __RW_END__			Load$$LR$$LR_END$$Base
 #define __SPM_SHIM_EXCEPTIONS_START__	Load$$__SPM_SHIM_EXCEPTIONS__$$Base
diff --git a/include/common/bl_common.ld.h b/include/common/bl_common.ld.h
index 9888a3c..080e331 100644
--- a/include/common/bl_common.ld.h
+++ b/include/common/bl_common.ld.h
@@ -39,6 +39,16 @@
 	KEEP(*(rt_svc_descs))				\
 	__RT_SVC_DESCS_END__ = .;
 
+#if SPMC_AT_EL3
+#define EL3_LP_DESCS					\
+	. = ALIGN(STRUCT_ALIGN);			\
+	__EL3_LP_DESCS_START__ = .;			\
+	KEEP(*(el3_lp_descs))				\
+	__EL3_LP_DESCS_END__ = .;
+#else
+#define EL3_LP_DESCS
+#endif
+
 #define PMF_SVC_DESCS					\
 	. = ALIGN(STRUCT_ALIGN);			\
 	__PMF_SVC_DESCS_START__ = .;			\
@@ -89,7 +99,8 @@
 	PARSER_LIB_DESCS				\
 	CPU_OPS						\
 	GOT						\
-	BASE_XLAT_TABLE_RO
+	BASE_XLAT_TABLE_RO				\
+	EL3_LP_DESCS
 
 /*
  * .data must be placed at a lower address than the stacks if the stack
diff --git a/include/services/el3_spmc_logical_sp.h b/include/services/el3_spmc_logical_sp.h
new file mode 100644
index 0000000..7ec9958
--- /dev/null
+++ b/include/services/el3_spmc_logical_sp.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2022, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+#ifndef EL3_SP_H
+#define EL3_SP_H
+
+#include <common/bl_common.h>
+#include <lib/cassert.h>
+
+/*******************************************************************************
+ * Structure definition, typedefs & constants for the Logical SPs.
+ ******************************************************************************/
+
+typedef uint64_t (*direct_msg_handler)(uint32_t smc_fid, bool secure_origin,
+				       uint64_t x1, uint64_t x2, uint64_t x3,
+				       uint64_t x4, void *cookie, void *handle,
+				       uint64_t flags);
+
+/* Prototype for logical partition initializing function. */
+typedef int32_t (*ffa_partition_init_t)(void);
+
+/* Logical Partition Descriptor. */
+struct el3_lp_desc {
+	ffa_partition_init_t init;
+	uint16_t sp_id;
+	uint32_t properties;
+	uint32_t uuid[4];  /* Little Endian. */
+	direct_msg_handler direct_req;
+	const char *debug_name;
+};
+
+/* Convenience macro to declare a logical partition descriptor. */
+#define DECLARE_LOGICAL_PARTITION(_name, _init, _sp_id, _uuid, _properties, \
+				  _direct_req)				    \
+	static const struct el3_lp_desc __partition_desc_ ## _name	    \
+		__section("el3_lp_descs") __used = {			    \
+			.debug_name = #_name,				    \
+			.init = (_init),				    \
+			.sp_id = (_sp_id),				    \
+			.uuid = _uuid,					    \
+			.properties = (_properties),			    \
+			.direct_req = (_direct_req),			    \
+		}
+
+
+/*******************************************************************************
+ * Function & variable prototypes.
+ ******************************************************************************/
+int el3_sp_desc_validate(void);
+uintptr_t handle_el3_sp(uint32_t smc_fid, void *cookie, void *handle,
+						unsigned int flags);
+IMPORT_SYM(uintptr_t, __EL3_LP_DESCS_START__,	EL3_LP_DESCS_START);
+IMPORT_SYM(uintptr_t, __EL3_LP_DESCS_END__,	EL3_LP_DESCS_END);
+
+#define EL3_LP_DESCS_COUNT ((EL3_LP_DESCS_END - EL3_LP_DESCS_START) \
+			  / sizeof(struct el3_lp_desc))
+
+#endif /* EL3_SP_H */
diff --git a/include/services/ffa_svc.h b/include/services/ffa_svc.h
index d3fb012..2b4a377 100644
--- a/include/services/ffa_svc.h
+++ b/include/services/ffa_svc.h
@@ -164,6 +164,13 @@
 	FFA_FID(SMC_64, FFA_FNUM_NOTIFICATION_INFO_GET)
 
 /*
+ * FF-A partition properties values.
+ */
+#define FFA_PARTITION_DIRECT_REQ_RECV	U(1 << 0)
+#define FFA_PARTITION_DIRECT_REQ_SEND	U(1 << 1)
+#define FFA_PARTITION_INDIRECT_MSG	U(1 << 2)
+
+/*
  * Reserve a special value for traffic targeted to the Hypervisor or SPM.
  */
 #define FFA_TARGET_INFO_MBZ		U(0x0)
diff --git a/services/std_svc/spm/el3_spmc/logical_sp.c b/services/std_svc/spm/el3_spmc/logical_sp.c
new file mode 100644
index 0000000..e080832
--- /dev/null
+++ b/services/std_svc/spm/el3_spmc/logical_sp.c
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2022, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <string.h>
+
+#include <common/debug.h>
+#include <services/el3_spmc_logical_sp.h>
+#include <services/ffa_svc.h>
+#include "spmc.h"
+
+/*******************************************************************************
+ * Validate any logical partition descriptors before we initialise.
+ * Initialization of said partitions will be taken care of during SPMC boot.
+ ******************************************************************************/
+int el3_sp_desc_validate(void)
+{
+	struct el3_lp_desc *lp_array;
+
+	/*
+	 * Assert the number of descriptors is less than maximum allowed.
+	 * This constant should be define on a per platform basis.
+	 */
+	assert(EL3_LP_DESCS_COUNT <= MAX_EL3_LP_DESCS_COUNT);
+
+	/* Check the array bounds are valid. */
+	assert(EL3_LP_DESCS_END >= EL3_LP_DESCS_START);
+
+	/* If no logical partitions are implemented then simply bail out. */
+	if (EL3_LP_DESCS_COUNT == 0U) {
+		return 0;
+	}
+
+	lp_array = get_el3_lp_array();
+
+	for (unsigned int index = 0; index < EL3_LP_DESCS_COUNT; index++) {
+		struct el3_lp_desc *lp_desc = &lp_array[index];
+
+		/* Validate our logical partition descriptors. */
+		if (lp_desc == NULL) {
+			ERROR("Invalid Logical SP Descriptor\n");
+			return -EINVAL;
+		}
+
+		/*
+		 * Ensure the ID follows the convention to indidate it resides
+		 * in the secure world.
+		 */
+		if (!ffa_is_secure_world_id(lp_desc->sp_id)) {
+			ERROR("Invalid Logical SP ID (0x%x)\n",
+			      lp_desc->sp_id);
+			return -EINVAL;
+		}
+
+		/* Ensure we don't conflict with the SPMC partition ID. */
+		if (lp_desc->sp_id == FFA_SPMC_ID) {
+			ERROR("Logical SP ID clashes with SPMC ID(0x%x)\n",
+			      lp_desc->sp_id);
+			return -EINVAL;
+		}
+
+		/* Ensure the UUID is not the NULL UUID. */
+		if (lp_desc->uuid[0] == 0 && lp_desc->uuid[1] == 0 &&
+		    lp_desc->uuid[2] == 0 && lp_desc->uuid[3] == 0) {
+			ERROR("Invalid UUID for Logical SP (0x%x)\n",
+			      lp_desc->sp_id);
+			return -EINVAL;
+		}
+
+		/* Ensure init function callback is registered. */
+		if (lp_desc->init == NULL) {
+			ERROR("Missing init function for Logical SP(0x%x)\n",
+			      lp_desc->sp_id);
+			return -EINVAL;
+		}
+
+		/* Ensure that LP only supports receiving direct requests. */
+		if (lp_desc->properties &
+		    ~(FFA_PARTITION_DIRECT_REQ_RECV)) {
+			ERROR("Invalid partition properties (0x%x)\n",
+			      lp_desc->properties);
+			return -EINVAL;
+		}
+
+		/* Ensure direct request function callback is registered. */
+		if (lp_desc->direct_req == NULL) {
+			ERROR("No Direct Req handler for Logical SP (0x%x)\n",
+			      lp_desc->sp_id);
+			return -EINVAL;
+		}
+
+		/* Ensure that all partition IDs are unique. */
+		for (unsigned int inner_idx = index + 1;
+		     inner_idx < EL3_LP_DESCS_COUNT; inner_idx++) {
+			if (lp_desc->sp_id == lp_array[inner_idx].sp_id) {
+				ERROR("Duplicate SP ID Detected (0x%x)\n",
+				      lp_desc->sp_id);
+				return -EINVAL;
+			}
+		}
+	}
+	return 0;
+}
diff --git a/services/std_svc/spm/el3_spmc/spmc.h b/services/std_svc/spm/el3_spmc/spmc.h
index df0aa61..db29727 100644
--- a/services/std_svc/spm/el3_spmc/spmc.h
+++ b/services/std_svc/spm/el3_spmc/spmc.h
@@ -11,6 +11,7 @@
 
 #include <lib/psci/psci.h>
 #include <lib/spinlock.h>
+#include <services/el3_spmc_logical_sp.h>
 #include "spm_common.h"
 
 /*
@@ -184,4 +185,10 @@
  */
 bool is_ffa_secure_id_valid(uint16_t partition_id);
 
+/*
+ * Helper function to obtain the array storing the EL3
+ * Logical Partition descriptors.
+ */
+struct el3_lp_desc *get_el3_lp_array(void);
+
 #endif /* SPMC_H */
diff --git a/services/std_svc/spm/el3_spmc/spmc.mk b/services/std_svc/spm/el3_spmc/spmc.mk
index 2b154dd..1827536 100644
--- a/services/std_svc/spm/el3_spmc/spmc.mk
+++ b/services/std_svc/spm/el3_spmc/spmc.mk
@@ -10,8 +10,8 @@
 
 SPMC_SOURCES	:=	$(addprefix services/std_svc/spm/el3_spmc/,	\
 			spmc_main.c				\
-			spmc_setup.c)
-
+			spmc_setup.c				\
+			logical_sp.c)
 
 # Let the top-level Makefile know that we intend to include a BL32 image
 NEED_BL32		:=	yes
diff --git a/services/std_svc/spm/el3_spmc/spmc_main.c b/services/std_svc/spm/el3_spmc/spmc_main.c
index 3fd8c78..450dd8c 100644
--- a/services/std_svc/spm/el3_spmc/spmc_main.c
+++ b/services/std_svc/spm/el3_spmc/spmc_main.c
@@ -19,6 +19,7 @@
 #include <lib/xlat_tables/xlat_tables_v2.h>
 #include <libfdt.h>
 #include <plat/common/platform.h>
+#include <services/el3_spmc_logical_sp.h>
 #include <services/ffa_svc.h>
 #include <services/spmc_svc.h>
 #include <services/spmd_svc.h>
@@ -41,6 +42,15 @@
 static struct ns_endpoint_desc ns_ep_desc[NS_PARTITION_COUNT];
 
 /*
+ * Helper function to obtain the array storing the EL3
+ * Logical Partition descriptors.
+ */
+struct el3_lp_desc *get_el3_lp_array(void)
+{
+	return (struct el3_lp_desc *) EL3_LP_DESCS_START;
+}
+
+/*
  * Helper function to obtain the descriptor of the last SP to whom control was
  * handed to on this physical cpu. Currently, we assume there is only one SP.
  * TODO: Expand to track multiple partitions when required.
@@ -105,6 +115,8 @@
  ******************************************************************************/
 bool is_ffa_secure_id_valid(uint16_t partition_id)
 {
+	struct el3_lp_desc *el3_lp_descs = get_el3_lp_array();
+
 	/* Ensure the ID is not the invalid partition ID. */
 	if (partition_id == INV_SP_ID) {
 		return false;
@@ -133,12 +145,22 @@
 		return false;
 	}
 
+	/* Ensure we don't clash with any Logical SP's. */
+	for (unsigned int i = 0U; i < EL3_LP_DESCS_COUNT; i++) {
+		if (el3_lp_descs[i].sp_id == partition_id) {
+			return false;
+		}
+	}
+
 	return true;
 }
 
 /*******************************************************************************
  * This function either forwards the request to the other world or returns
  * with an ERET depending on the source of the call.
+ * We can assume that the destination is for an entity at a lower exception
+ * level as any messages destined for a logical SP resident in EL3 will have
+ * already been taken care of by the SPMC before entering this function.
  ******************************************************************************/
 static uint64_t spmc_smc_return(uint32_t smc_fid,
 				bool secure_origin,
@@ -210,6 +232,7 @@
 				       uint64_t flags)
 {
 	uint16_t dst_id = ffa_endpoint_destination(x1);
+	struct el3_lp_desc *el3_lp_descs;
 	struct secure_partition_desc *sp;
 	unsigned int idx;
 
@@ -219,11 +242,22 @@
 					     FFA_ERROR_INVALID_PARAMETER);
 	}
 
+	el3_lp_descs = get_el3_lp_array();
+
+	/* Check if the request is destined for a Logical Partition. */
+	for (unsigned int i = 0U; i < MAX_EL3_LP_DESCS_COUNT; i++) {
+		if (el3_lp_descs[i].sp_id == dst_id) {
+			return el3_lp_descs[i].direct_req(
+					smc_fid, secure_origin, x1, x2, x3, x4,
+					cookie, handle, flags);
+		}
+	}
+
 	/*
-	 * If called by the secure world it is an invalid call since a
-	 * SP cannot call into the Normal world and there is no other SP to call
-	 * into. If there are other SPs in future then the partition runtime
-	 * model would need to be validated as well.
+	 * If the request was not targeted to a LSP and from the secure world
+	 * then it is invalid since a SP cannot call into the Normal world and
+	 * there is no other SP to call into. If there are other SPs in future
+	 * then the partition runtime model would need to be validated as well.
 	 */
 	if (secure_origin) {
 		VERBOSE("Direct request not supported to the Normal World.\n");
@@ -621,6 +655,37 @@
  * This function takes an SP context pointer and performs a synchronous entry
  * into it.
  ******************************************************************************/
+static int32_t logical_sp_init(void)
+{
+	int32_t rc = 0;
+	struct el3_lp_desc *el3_lp_descs;
+
+	/* Perform initial validation of the Logical Partitions. */
+	rc = el3_sp_desc_validate();
+	if (rc != 0) {
+		ERROR("Logical Partition validation failed!\n");
+		return rc;
+	}
+
+	el3_lp_descs = get_el3_lp_array();
+
+	INFO("Logical Secure Partition init start.\n");
+	for (unsigned int i = 0U; i < EL3_LP_DESCS_COUNT; i++) {
+		rc = el3_lp_descs[i].init();
+		if (rc != 0) {
+			ERROR("Logical SP (0x%x) Failed to Initialize\n",
+			      el3_lp_descs[i].sp_id);
+			return rc;
+		}
+		VERBOSE("Logical SP (0x%x) Initialized\n",
+			      el3_lp_descs[i].sp_id);
+	}
+
+	INFO("Logical Secure Partition init completed.\n");
+
+	return rc;
+}
+
 uint64_t spmc_sp_synchronous_entry(struct sp_exec_ctx *ec)
 {
 	uint64_t rc;
@@ -725,6 +790,13 @@
 	initalize_sp_descs();
 	initalize_ns_ep_descs();
 
+	/* Setup logical SPs. */
+	ret = logical_sp_init();
+	if (ret != 0) {
+		ERROR("Failed to initialize Logical Partitions.\n");
+		return ret;
+	}
+
 	/* Perform physical SP setup. */
 
 	/* Disable MMU at EL1 (initialized by BL2) */