Merge "TRNG: clean up TRNG test"
diff --git a/include/runtime_services/cactus_message_loop.h b/include/runtime_services/cactus_message_loop.h
new file mode 100644
index 0000000..d69e77c
--- /dev/null
+++ b/include/runtime_services/cactus_message_loop.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2021, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <ffa_helpers.h>
+#include <spm_common.h>
+
+/**
+ * Pairs a command id with a function call, to handle the command ID.
+ */
+struct cactus_cmd_handler {
+	const uint64_t id;
+	smc_ret_values (*fn)(const smc_ret_values *args,
+			     struct mailbox_buffers *mb);
+};
+
+/**
+ * Helper to create the name of a handler function.
+ */
+#define CACTUS_HANDLER_FN_NAME(name) cactus_##name##_handler
+
+/**
+ * Define handler's function signature.
+ */
+#define CACTUS_HANDLER_FN(name)						\
+	static smc_ret_values CACTUS_HANDLER_FN_NAME(name)(		\
+		const smc_ret_values *args, struct mailbox_buffers *mb)
+
+/**
+ * Helper to define Cactus command handler, and pair it with a command ID.
+ * It also creates a table with this information, to be traversed by
+ * 'cactus_handle_cmd' function.
+ */
+#define CACTUS_CMD_HANDLER(name, ID)					\
+	CACTUS_HANDLER_FN(name);					\
+	struct cactus_cmd_handler name __section(".cactus_handler") = {	\
+		.id = ID, .fn = CACTUS_HANDLER_FN_NAME(name),		\
+	};								\
+	CACTUS_HANDLER_FN(name)
+
+bool cactus_handle_cmd(smc_ret_values *cmd_args, smc_ret_values *ret,
+		       struct mailbox_buffers *mb);
diff --git a/spm/cactus/cactus_test_cmds.h b/include/runtime_services/cactus_test_cmds.h
similarity index 75%
rename from spm/cactus/cactus_test_cmds.h
rename to include/runtime_services/cactus_test_cmds.h
index c662d27..246f4f9 100644
--- a/spm/cactus/cactus_test_cmds.h
+++ b/include/runtime_services/cactus_test_cmds.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2020-2021, Arm Limited. All rights reserved.
+ * Copyright (c) 2021, Arm Limited. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
@@ -7,14 +7,21 @@
 #ifndef CACTUS_TEST_CMDS
 #define CACTUS_TEST_CMDS
 
-#include <debug.h>
 #include <ffa_helpers.h>
 
 /**
  * Success and error return to be sent over a msg response.
  */
-#define CACTUS_SUCCESS	 U(0)
-#define CACTUS_ERROR	 U(-1)
+#define CACTUS_SUCCESS		U(0)
+#define CACTUS_ERROR		U(-1)
+
+/**
+ * Error codes.
+ */
+#define CACTUS_ERROR_INVALID		U(1)
+#define CACTUS_ERROR_TEST		U(2)
+#define CACTUS_ERROR_FFA_CALL		U(3)
+#define CACTUS_ERROR_UNHANDLED		U(4)
 
 /**
  * Get command from struct smc_ret_values.
@@ -32,14 +39,66 @@
 	ffa_vm_id_t source, ffa_vm_id_t dest, uint64_t cmd, uint64_t val0,
 	uint64_t val1, uint64_t val2, uint64_t val3)
 {
-	return 	ffa_msg_send_direct_req64_5args(source, dest, cmd, val0, val1,
-						val2, val3);
+	return 	ffa_msg_send_direct_req64(source, dest, cmd, val0, val1, val2,
+					  val3);
 }
 
-#define PRINT_CMD(smc_ret)						\
-	VERBOSE("cmd %lx; args: %lx, %lx, %lx, %lx\n",	 		\
-		smc_ret.ret3, smc_ret.ret4, smc_ret.ret5, 		\
-		smc_ret.ret6, smc_ret.ret7)
+/**
+ * Template for responses to Cactus commands.
+ * 'cactus_send_response' is the template for custom responses, in case there is
+ * a need to propagate more than one value in the response of a command.
+ */
+static inline smc_ret_values cactus_send_response(
+	ffa_vm_id_t source, ffa_vm_id_t dest, uint32_t resp, uint32_t val0,
+	uint64_t val1, uint64_t val2, uint64_t val3)
+{
+	return ffa_msg_send_direct_resp64(source, dest, resp, val0, val1,
+					  val2, val3);
+}
+
+/**
+ * For responses of one value only.
+ */
+static inline smc_ret_values cactus_response(
+	ffa_vm_id_t source, ffa_vm_id_t dest, uint32_t response)
+{
+	return ffa_msg_send_direct_resp64(source, dest, response, 0, 0, 0, 0);
+}
+
+static inline uint32_t cactus_get_response(smc_ret_values ret)
+{
+	return (uint32_t)ret.ret3;
+}
+
+/**
+ * In a successful test, in case the SP needs to propagate an extra value
+ * to conclude the test.
+ * If more arguments are needed, a custom response should be defined for the
+ * specific test.
+ */
+static inline smc_ret_values cactus_success_resp(
+		ffa_vm_id_t source, ffa_vm_id_t dest, uint64_t value)
+{
+	return cactus_send_response(source, dest, CACTUS_SUCCESS, value,
+				    0, 0, 0);
+}
+
+/**
+ * In case the test fails on the SP side, the 'error_code' should help specify
+ * the reason, which can be specific to the test, or general ones as defined
+ * in the error code list.
+ */
+static inline smc_ret_values cactus_error_resp(
+		ffa_vm_id_t source, ffa_vm_id_t dest, uint32_t error_code)
+{
+	return cactus_send_response(source, dest, CACTUS_ERROR, error_code,
+				    0, 0, 0);
+}
+
+static inline uint32_t cactus_error_code(smc_ret_values ret)
+{
+	return (uint32_t) ret.ret4;
+}
 
 /**
  * With this test command the sender transmits a 64-bit value that it then
@@ -186,33 +245,8 @@
 static inline smc_ret_values cactus_req_simd_fill_send_cmd(
 	ffa_vm_id_t source, ffa_vm_id_t dest)
 {
-	return cactus_send_cmd(source, dest, CACTUS_REQ_SIMD_FILL_CMD, 0, 0, 0, 0);
-}
-
-/**
- * Template for responses to CACTUS commands.
- */
-static inline smc_ret_values cactus_response(
-	ffa_vm_id_t source, ffa_vm_id_t dest, uint32_t response)
-{
-	return ffa_msg_send_direct_resp(source, dest, response);
-}
-
-static inline smc_ret_values cactus_success_resp(
-		ffa_vm_id_t source, ffa_vm_id_t dest)
-{
-	return cactus_response(source, dest, CACTUS_SUCCESS);
-}
-
-static inline smc_ret_values cactus_error_resp(
-		ffa_vm_id_t source, ffa_vm_id_t dest)
-{
-	return cactus_response(source, dest, CACTUS_ERROR);
-}
-
-static inline uint32_t cactus_get_response(smc_ret_values ret)
-{
-	return (uint32_t)ret.ret3;
+	return cactus_send_cmd(source, dest, CACTUS_REQ_SIMD_FILL_CMD, 0, 0, 0,
+			       0);
 }
 
 #endif
diff --git a/include/runtime_services/ffa_helpers.h b/include/runtime_services/ffa_helpers.h
index 02a956e..4af051b 100644
--- a/include/runtime_services/ffa_helpers.h
+++ b/include/runtime_services/ffa_helpers.h
@@ -364,16 +364,6 @@
 	enum ffa_memory_shareability shareability, uint32_t *total_length,
 	uint32_t *fragment_length);
 
-ffa_memory_handle_t ffa_memory_send(
-	struct ffa_memory_region *memory_region, uint32_t mem_func,
-	uint32_t fragment_length, uint32_t total_length);
-
-ffa_memory_handle_t ffa_memory_init_and_send(
-	struct ffa_memory_region *memory_region, size_t memory_region_max_size,
-	ffa_vm_id_t sender, ffa_vm_id_t receiver,
-	const struct ffa_memory_region_constituent* constituents,
-	uint32_t constituents_count, uint32_t mem_func);
-
 static inline ffa_vm_id_t ffa_dir_msg_dest(smc_ret_values val) {
 	return (ffa_vm_id_t)val.ret1 & U(0xFFFF);
 }
@@ -382,24 +372,35 @@
 	return (ffa_vm_id_t)(val.ret1 >> 16U);
 }
 
-smc_ret_values ffa_msg_send_direct_req(uint32_t source_id, uint32_t dest_id, uint32_t message);
-smc_ret_values ffa_msg_send_direct_req64_5args(uint32_t source_id, uint32_t dest_id,
-					   uint64_t arg0, uint64_t arg1,
-					   uint64_t arg2, uint64_t arg3,
-					   uint64_t arg4);
+smc_ret_values ffa_msg_send_direct_req64(ffa_vm_id_t source_id,
+					 ffa_vm_id_t dest_id, uint64_t arg0,
+					 uint64_t arg1, uint64_t arg2,
+					 uint64_t arg3, uint64_t arg4);
+
+smc_ret_values ffa_msg_send_direct_req32(ffa_vm_id_t source_id,
+					 ffa_vm_id_t dest_id, uint32_t arg0,
+					 uint32_t arg1, uint32_t arg2,
+					 uint32_t arg3, uint32_t arg4);
+
+smc_ret_values ffa_msg_send_direct_resp64(ffa_vm_id_t source_id,
+					  ffa_vm_id_t dest_id, uint64_t arg0,
+					  uint64_t arg1, uint64_t arg2,
+					  uint64_t arg3, uint64_t arg4);
+
+smc_ret_values ffa_msg_send_direct_resp32(ffa_vm_id_t source_id,
+					  ffa_vm_id_t dest_id, uint32_t arg0,
+					  uint32_t arg1, uint32_t arg2,
+					  uint32_t arg3, uint32_t arg4);
 
 smc_ret_values ffa_run(uint32_t dest_id, uint32_t vcpu_id);
 smc_ret_values ffa_version(uint32_t input_version);
 smc_ret_values ffa_id_get(void);
 smc_ret_values ffa_msg_wait(void);
-smc_ret_values ffa_msg_send_direct_resp(ffa_vm_id_t source_id,
-					ffa_vm_id_t dest_id, uint32_t message);
 smc_ret_values ffa_error(int32_t error_code);
 smc_ret_values ffa_features(uint32_t feature);
 smc_ret_values ffa_partition_info_get(const uint32_t uuid[4]);
 smc_ret_values ffa_rx_release(void);
 smc_ret_values ffa_rxtx_map(uintptr_t send, uintptr_t recv, uint32_t pages);
-
 smc_ret_values ffa_mem_donate(uint32_t descriptor_length,
 			      uint32_t fragment_length);
 smc_ret_values ffa_mem_lend(uint32_t descriptor_length,
diff --git a/include/runtime_services/ffa_svc.h b/include/runtime_services/ffa_svc.h
index f08e803..c970265 100644
--- a/include/runtime_services/ffa_svc.h
+++ b/include/runtime_services/ffa_svc.h
@@ -22,7 +22,7 @@
 
 /* The macros below are used to identify FFA calls from the SMC function ID */
 #define FFA_FNUM_MIN_VALUE	U(0x60)
-#define FFA_FNUM_MAX_VALUE	U(0x7f)
+#define FFA_FNUM_MAX_VALUE	U(0x84)
 #define is_ffa_fid(fid) __extension__ ({		\
 	__typeof__(fid) _fid = (fid);			\
 	((GET_SMC_NUM(_fid) >= FFA_FNUM_MIN_VALUE) &&	\
@@ -84,6 +84,7 @@
 #define FFA_FNUM_MEM_RETRIEVE_RESP	U(0x75)
 #define FFA_FNUM_MEM_RELINQUISH	U(0x76)
 #define FFA_FNUM_MEM_RECLAIM		U(0x77)
+#define FFA_FNUM_SECONDARY_EP_REGISTER	U(0x84)
 
 /* FFA SMC32 FIDs */
 #define FFA_ERROR		FFA_FID(SMC_32, FFA_FNUM_ERROR)
@@ -126,6 +127,8 @@
 #define FFA_MEM_SHARE_SMC64	FFA_FID(SMC_64, FFA_FNUM_MEM_SHARE)
 #define FFA_MEM_RETRIEVE_REQ_SMC64 \
 	FFA_FID(SMC_64, FFA_FNUM_MEM_RETRIEVE_REQ)
+#define FFA_SECONDARY_EP_REGISTER_SMC64 \
+	FFA_FID(SMC_64, FFA_FNUM_SECONDARY_EP_REGISTER)
 
 /*
  * Reserve a special value for traffic targeted to the Hypervisor or SPM.
diff --git a/include/runtime_services/spm_common.h b/include/runtime_services/spm_common.h
index b090002..dbb113b 100644
--- a/include/runtime_services/spm_common.h
+++ b/include/runtime_services/spm_common.h
@@ -53,11 +53,18 @@
 	CONFIGURE_MAILBOX(mb_name, buffers_size);				\
 	smc_ret = ffa_rxtx_map(							\
 				(uintptr_t)mb_name.send,			\
-				(uintptr_t)mb_name.recv, 			\
+				(uintptr_t)mb_name.recv,			\
 				buffers_size / PAGE_SIZE			\
 			);							\
 	} while (false)
 
+/**
+ * Helpers to evaluate returns of FF-A calls.
+ */
+bool is_ffa_call_error(smc_ret_values val);
+bool is_ffa_direct_response(smc_ret_values ret);
+bool is_expected_ffa_return(smc_ret_values ret, uint32_t func_id);
+
 /*
  * Vector length:
  * SIMD: 128 bits = 16 bytes
@@ -82,4 +89,30 @@
 
 unsigned int get_ffa_feature_test_target(const struct ffa_features_test **test_target);
 
+/**
+ * Helper to conduct a memory retrieve. This is to be called by the receiver
+ * of a memory share operation.
+ */
+bool memory_retrieve(struct mailbox_buffers *mb,
+		     struct ffa_memory_region **retrieved, uint64_t handle,
+		     ffa_vm_id_t sender, ffa_vm_id_t receiver,
+		     uint32_t mem_func);
+
+/**
+ * Helper to conduct a memory relinquish. The caller is usually the receiver,
+ * after it being done with the memory shared, identified by the 'handle'.
+ */
+bool memory_relinquish(struct ffa_mem_relinquish *m, uint64_t handle,
+		       ffa_vm_id_t id);
+
+ffa_memory_handle_t memory_send(
+	struct ffa_memory_region *memory_region, uint32_t mem_func,
+	uint32_t fragment_length, uint32_t total_length);
+
+ffa_memory_handle_t memory_init_and_send(
+	struct ffa_memory_region *memory_region, size_t memory_region_max_size,
+	ffa_vm_id_t sender, ffa_vm_id_t receiver,
+	const struct ffa_memory_region_constituent* constituents,
+	uint32_t constituents_count, uint32_t mem_func);
+
 #endif /* SPM_COMMON_H */
diff --git a/spm/cactus/aarch64/cactus_entrypoint.S b/spm/cactus/aarch64/cactus_entrypoint.S
index b0f89d4..7e63856 100644
--- a/spm/cactus/aarch64/cactus_entrypoint.S
+++ b/spm/cactus/aarch64/cactus_entrypoint.S
@@ -7,25 +7,35 @@
 #include <arch.h>
 #include <asm_macros.S>
 #include <cactus_def.h>
-#include <platform_def.h>
+#include <cactus_platform_def.h>
 
 	.globl	cactus_entrypoint
+	.globl	secondary_cold_entry
 
+/* Provision one stack per Execution Context (or vCPU) */
 .section .bss.stacks
 	.balign CACHE_WRITEBACK_GRANULE
-	.fill	CACTUS_STACKS_SIZE
+	.fill	CACTUS_STACKS_SIZE * PLAT_CACTUS_CORE_COUNT
 stacks_end:
 
 func cactus_entrypoint
+	/* Entry reason is primary EC cold boot */
+	mov	x19, #1
+secondary_cold_entry:
+	/* Entry reason is secondary EC cold boot */
+	mrs	x0, mpidr_el1
+	bl	platform_get_core_pos
 
 	/* Setup the stack pointer. */
-	adr	x0, stacks_end
-	mov	sp, x0
+	adr	x1, stacks_end
+	mov	x2, #CACTUS_STACKS_SIZE
+	mul	x2, x0, x2
+	sub	sp, x1, x2
 
 	/* Enable I-Cache */
-	mrs	x0, sctlr_el1
-	orr	x0, x0, #SCTLR_I_BIT
-	msr	sctlr_el1, x0
+	mrs	x1, sctlr_el1
+	orr	x1, x1, #SCTLR_I_BIT
+	msr	sctlr_el1, x1
 	isb
 
 	/*
@@ -38,6 +48,15 @@
 	msr	cpacr_el1, x0
 	isb
 
+	/* Set up exceptions vector table */
+	adrp	x1, tftf_vector
+	add	x1, x1, :lo12:tftf_vector
+	msr	vbar_el1, x1
+	isb
+
+	/* Skip to main if warm boot */
+	cbz	x19, 0f
+
 	/* Relocate symbols */
 pie_fixup:
 	ldr	x0, =pie_fixup
@@ -46,13 +65,7 @@
 	add	x1, x1, x0
 	bl	fixup_gdt_reloc
 
-	/* Set up exceptions vector table */
-	adrp	x0, tftf_vector
-	add	x0, x0, :lo12:tftf_vector
-	msr	vbar_el1, x0
-	isb
-
-	/* And jump to the C entrypoint. */
+	/* Jump to the C entrypoint (it does not return) */
+0:	mov	x0, x19
 	b	cactus_main
-
 endfunc cactus_entrypoint
diff --git a/spm/cactus/cactus.ld.S b/spm/cactus/cactus.ld.S
index 11b28ba..50fc576 100644
--- a/spm/cactus/cactus.ld.S
+++ b/spm/cactus/cactus.ld.S
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017-2020, Arm Limited. All rights reserved.
+ * Copyright (c) 2017-2021, Arm Limited. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
@@ -52,6 +52,10 @@
         . = ALIGN(PAGE_SIZE);
         __DATA_START__ = .;
         *(.data*)
+        . = ALIGN(PAGE_SIZE);
+        cactus_cmd_handler_begin = .;
+        KEEP(*(.cactus_handler))
+        cactus_cmd_handler_end = .;
         . = NEXT(PAGE_SIZE);
         __DATA_END__ = .;
     }
diff --git a/spm/cactus/cactus.mk b/spm/cactus/cactus.mk
index 4a9cfcc..08b824c 100644
--- a/spm/cactus/cactus.mk
+++ b/spm/cactus/cactus.mk
@@ -33,13 +33,19 @@
 	$(addprefix spm/cactus/,			\
 		aarch64/cactus_entrypoint.S		\
 		cactus_debug.c				\
-		cactus_ffa_tests.c 			\
 		cactus_main.c				\
 	)						\
 	$(addprefix spm/common/,			\
 		aarch64/sp_arch_helpers.S		\
 		sp_helpers.c				\
 	)						\
+	$(addprefix spm/cactus/cactus_tests/,		\
+		cactus_message_loop.c			\
+		cactus_test_cpu_features.c		\
+		cactus_test_direct_messaging.c		\
+		cactus_test_ffa.c 			\
+		cactus_test_memory_sharing.c		\
+	)
 
 # TODO: Remove dependency on TFTF files.
 CACTUS_SOURCES	+=					\
diff --git a/spm/cactus/cactus_ffa_tests.c b/spm/cactus/cactus_ffa_tests.c
deleted file mode 100644
index 032d2e2..0000000
--- a/spm/cactus/cactus_ffa_tests.c
+++ /dev/null
@@ -1,353 +0,0 @@
-/*
- * Copyright (c) 2018-2021, Arm Limited. All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-#include <assert.h>
-#include <debug.h>
-#include <errno.h>
-
-#include <cactus_def.h>
-#include <cactus_platform_def.h>
-#include <ffa_endpoints.h>
-#include <sp_helpers.h>
-#include <spm_common.h>
-
-#include <lib/libc/string.h>
-#include <lib/xlat_tables/xlat_tables_v2.h>
-
-/* FFA version test helpers */
-#define FFA_MAJOR 1U
-#define FFA_MINOR 0U
-
-static const uint32_t primary_uuid[4] = PRIMARY_UUID;
-static const uint32_t secondary_uuid[4] = SECONDARY_UUID;
-static const uint32_t tertiary_uuid[4] = TERTIARY_UUID;
-static const uint32_t null_uuid[4] = {0};
-
-/*
- * Fill SIMD vectors from secure world side with a unique value.
- * 0x22 is just a dummy value to be distinguished from the value
- * in the normal world.
- */
-void fill_simd_vectors(void)
-{
-	simd_vector_t simd_vectors[SIMD_NUM_VECTORS];
-
-	for (unsigned int num = 0U; num < SIMD_NUM_VECTORS; num++) {
-		memset(simd_vectors[num], 0x22 * num, sizeof(simd_vector_t));
-	}
-
-	fill_simd_vector_regs(simd_vectors);
-}
-
-/*
- * Test FFA_FEATURES interface.
- */
-static void ffa_features_test(void)
-{
-	const char *test_features = "FFA Features interface";
-	smc_ret_values ffa_ret;
-	const struct ffa_features_test *ffa_feature_test_target;
-	unsigned int i, test_target_size =
-		get_ffa_feature_test_target(&ffa_feature_test_target);
-
-
-	announce_test_section_start(test_features);
-
-	for (i = 0U; i < test_target_size; i++) {
-		announce_test_start(ffa_feature_test_target[i].test_name);
-
-		ffa_ret = ffa_features(ffa_feature_test_target[i].feature);
-		expect(ffa_func_id(ffa_ret), ffa_feature_test_target[i].expected_ret);
-		if (ffa_feature_test_target[i].expected_ret == FFA_ERROR) {
-			expect(ffa_error_code(ffa_ret), FFA_ERROR_NOT_SUPPORTED);
-		}
-
-		announce_test_end(ffa_feature_test_target[i].test_name);
-	}
-
-	announce_test_section_end(test_features);
-}
-
-static void ffa_partition_info_helper(struct mailbox_buffers *mb, const uint32_t uuid[4],
-			       const struct ffa_partition_info *expected,
-			       const uint16_t expected_size)
-{
-	smc_ret_values ret = ffa_partition_info_get(uuid);
-	unsigned int i;
-	expect(ffa_func_id(ret), FFA_SUCCESS_SMC32);
-
-	struct ffa_partition_info *info = (struct ffa_partition_info *)(mb->recv);
-	for (i = 0U; i < expected_size; i++) {
-		expect(info[i].id, expected[i].id);
-		expect(info[i].exec_context, expected[i].exec_context);
-		expect(info[i].properties, expected[i].properties);
-	}
-
-	ret = ffa_rx_release();
-	expect(ffa_func_id(ret), FFA_SUCCESS_SMC32);
-}
-
-static void ffa_partition_info_wrong_test(void)
-{
-	const char *test_wrong_uuid = "Request wrong UUID";
-	uint32_t uuid[4] = {1};
-
-	announce_test_start(test_wrong_uuid);
-
-	smc_ret_values ret = ffa_partition_info_get(uuid);
-	expect(ffa_func_id(ret), FFA_ERROR);
-	expect(ffa_error_code(ret), FFA_ERROR_INVALID_PARAMETER);
-
-	announce_test_end(test_wrong_uuid);
-}
-
-static void ffa_partition_info_get_test(struct mailbox_buffers *mb)
-{
-	const char *test_partition_info = "FFA Partition info interface";
-	const char *test_primary = "Get primary partition info";
-	const char *test_secondary = "Get secondary partition info";
-	const char *test_tertiary = "Get tertiary partition info";
-	const char *test_all = "Get all partitions info";
-
-	const struct ffa_partition_info expected_info[] = {
-		/* Primary partition info */
-		{
-			.id = SPM_VM_ID_FIRST,
-			.exec_context = CACTUS_PRIMARY_EC_COUNT,
-			/* Supports receipt of direct message requests. */
-			.properties = 1U
-		},
-		/* Secondary partition info */
-		{
-			.id = SPM_VM_ID_FIRST + 1U,
-			.exec_context = CACTUS_SECONDARY_EC_COUNT,
-			.properties = 1U
-		},
-		/* Tertiary partition info */
-		{
-			.id = SPM_VM_ID_FIRST + 2U,
-			.exec_context = CACTUS_TERTIARY_EC_COUNT,
-			.properties = 1U
-		}
-	};
-
-	announce_test_section_start(test_partition_info);
-
-	announce_test_start(test_tertiary);
-	ffa_partition_info_helper(mb, tertiary_uuid, &expected_info[2], 1);
-	announce_test_end(test_tertiary);
-
-	announce_test_start(test_secondary);
-	ffa_partition_info_helper(mb, secondary_uuid, &expected_info[1], 1);
-	announce_test_end(test_secondary);
-
-	announce_test_start(test_primary);
-	ffa_partition_info_helper(mb, primary_uuid, &expected_info[0], 1);
-	announce_test_end(test_primary);
-
-	announce_test_start(test_all);
-	ffa_partition_info_helper(mb, null_uuid, expected_info, 3);
-	announce_test_end(test_all);
-
-	ffa_partition_info_wrong_test();
-
-	announce_test_section_end(test_partition_info);
-}
-
-void ffa_version_test(void)
-{
-	const char *test_ffa_version = "FFA Version interface";
-
-	announce_test_start(test_ffa_version);
-
-	smc_ret_values ret = ffa_version(MAKE_FFA_VERSION(FFA_MAJOR, FFA_MINOR));
-	uint32_t spm_version = (uint32_t)ret.ret0;
-
-	bool ffa_version_compatible =
-		((spm_version >> FFA_VERSION_MAJOR_SHIFT) == FFA_MAJOR &&
-		 (spm_version & FFA_VERSION_MINOR_MASK) >= FFA_MINOR);
-
-	NOTICE("FFA_VERSION returned %u.%u; Compatible: %i\n",
-		spm_version >> FFA_VERSION_MAJOR_SHIFT,
-		spm_version & FFA_VERSION_MINOR_MASK,
-		(int)ffa_version_compatible);
-
-	expect((int)ffa_version_compatible, (int)true);
-
-	announce_test_end(test_ffa_version);
-}
-
-bool ffa_memory_retrieve_test(struct mailbox_buffers *mb,
-			 struct ffa_memory_region **retrieved,
-			 uint64_t handle, ffa_vm_id_t sender,
-			 ffa_vm_id_t receiver, uint32_t mem_func)
-{
-	smc_ret_values ret;
-	uint32_t fragment_size;
-	uint32_t total_size;
-	uint32_t descriptor_size;
-
-	if (retrieved == NULL || mb == NULL) {
-		ERROR("Invalid parameters!\n");
-		return false;
-	}
-
-	/*
-	 * TODO: Revise shareability attribute in function call
-	 * below.
-	 * https://lists.trustedfirmware.org/pipermail/hafnium/2020-June/000023.html
-	 */
-	descriptor_size = ffa_memory_retrieve_request_init(
-	    mb->send, handle, sender, receiver, 0, 0,
-	    FFA_DATA_ACCESS_RW,
-	    FFA_INSTRUCTION_ACCESS_NX,
-	    FFA_MEMORY_NORMAL_MEM,
-	    FFA_MEMORY_CACHE_WRITE_BACK,
-	    FFA_MEMORY_OUTER_SHAREABLE);
-
-	ret = ffa_mem_retrieve_req(descriptor_size, descriptor_size);
-
-	if (ffa_func_id(ret) != FFA_MEM_RETRIEVE_RESP) {
-		ERROR("Couldn't retrieve the memory page. Error: %lx\n",
-		      ret.ret2);
-		return false;
-	}
-
-	/*
-	 * Following total_size and fragment_size are useful to keep track
-	 * of the state of transaction. When the sum of all fragment_size of all
-	 * fragments is equal to total_size, the memory transaction has been
-	 * completed.
-	 * This is a simple test with only one segment. As such, upon
-	 * successful ffa_mem_retrieve_req, total_size must be equal to
-	 * fragment_size.
-	 */
-	total_size = ret.ret1;
-	fragment_size = ret.ret2;
-
-	if (total_size != fragment_size) {
-		ERROR("Only expect one memory segment to be sent!\n");
-		return false;
-	}
-
-	if (fragment_size > PAGE_SIZE) {
-		ERROR("Fragment should be smaller than RX buffer!\n");
-		return false;
-	}
-
-	*retrieved = (struct ffa_memory_region *)mb->recv;
-
-	if ((*retrieved)->receiver_count > MAX_MEM_SHARE_RECIPIENTS) {
-		VERBOSE("SPMC memory sharing operations support max of %u "
-			"receivers!\n", MAX_MEM_SHARE_RECIPIENTS);
-		return false;
-	}
-
-	VERBOSE("Memory Retrieved!\n");
-
-	return true;
-}
-
-bool ffa_memory_relinquish_test(struct ffa_mem_relinquish *m,
-			   uint64_t handle,
-			   ffa_vm_id_t id)
-{
-	smc_ret_values ret;
-
-	ffa_mem_relinquish_init(m, handle, 0, id);
-	ret = ffa_mem_relinquish();
-	if (ffa_func_id(ret) != FFA_SUCCESS_SMC32) {
-		ERROR("%s failed to relinquish memory! error: %x\n",
-		      __func__, ffa_error_code(ret));
-		return false;
-	}
-
-	VERBOSE("Memory Relinquished!\n");
-	return true;
-}
-
-void ffa_memory_management_test(struct mailbox_buffers *mb, ffa_vm_id_t vm_id,
-				ffa_vm_id_t sender, uint32_t mem_func,
-				uint64_t handle)
-{
-	const char *test_ffa = "Memory Management";
-	struct ffa_memory_region *m;
-	struct ffa_composite_memory_region *composite;
-	int ret;
-	unsigned int mem_attrs;
-	uint32_t *ptr;
-
-	announce_test_section_start(test_ffa);
-
-	expect(ffa_memory_retrieve_test(
-				mb, &m, handle, sender, vm_id, mem_func),
-		true);
-
-	composite = ffa_memory_region_get_composite(m, 0);
-
-	VERBOSE("Address: %p; page_count: %x %x\n",
-		composite->constituents[0].address,
-		composite->constituents[0].page_count, PAGE_SIZE);
-
-	/* This test is only concerned with RW permissions. */
-	expect(ffa_get_data_access_attr(
-			m->receivers[0].receiver_permissions.permissions),
-		FFA_DATA_ACCESS_RW);
-
-	mem_attrs = MT_RW_DATA | MT_EXECUTE_NEVER;
-
-	if (!IS_SP_ID(sender)) {
-		mem_attrs |= MT_NS;
-	}
-
-	ret = mmap_add_dynamic_region(
-			(uint64_t)composite->constituents[0].address,
-			(uint64_t)composite->constituents[0].address,
-			composite->constituents[0].page_count * PAGE_SIZE,
-			mem_attrs);
-	expect(ret, 0);
-
-	VERBOSE("Memory has been mapped\n");
-
-	ptr = (uint32_t *) composite->constituents[0].address;
-
-	/* Write mem_func to retrieved memory region for validation purposes. */
-	VERBOSE("Writing: %x\n", mem_func);
-	for (unsigned int i = 0U; i < 5U; i++)
-		ptr[i] = mem_func;
-
-	/*
-	 * A FFA_MEM_DONATE changes the ownership of the page, as such no
-	 * relinquish is needed.
-	 */
-	if (mem_func != FFA_MEM_DONATE_SMC32) {
-		ret = mmap_remove_dynamic_region(
-			(uint64_t)composite->constituents[0].address,
-			composite->constituents[0].page_count * PAGE_SIZE);
-		expect(ret, 0);
-
-		expect(ffa_memory_relinquish_test(
-			   (struct ffa_mem_relinquish *)mb->send,
-			   m->handle, vm_id),
-		       true);
-	}
-
-	expect(ffa_func_id(ffa_rx_release()), FFA_SUCCESS_SMC32);
-
-	announce_test_section_end(test_ffa);
-}
-
-void ffa_tests(struct mailbox_buffers *mb)
-{
-	const char *test_ffa = "FFA Interfaces";
-
-	announce_test_section_start(test_ffa);
-
-	ffa_features_test();
-	ffa_version_test();
-	ffa_partition_info_get_test(mb);
-
-	announce_test_section_end(test_ffa);
-}
diff --git a/spm/cactus/cactus_main.c b/spm/cactus/cactus_main.c
index 11d7b99..e54f3b0 100644
--- a/spm/cactus/cactus_main.c
+++ b/spm/cactus/cactus_main.c
@@ -8,11 +8,12 @@
 #include <errno.h>
 #include <debug.h>
 
+#include <cactus_message_loop.h>
 #include <cactus_platform_def.h>
-#include <cactus_test_cmds.h>
 #include <drivers/arm/pl011.h>
 #include <drivers/console.h>
 #include <lib/aarch64/arch_helpers.h>
+#include <lib/tftf_lib.h>
 #include <lib/xlat_tables/xlat_mmu_helpers.h>
 #include <lib/xlat_tables/xlat_tables_v2.h>
 #include <plat_arm.h>
@@ -29,8 +30,7 @@
 extern const char build_message[];
 extern const char version_string[];
 
-/* Memory section to be used for memory share operations */
-static __aligned(PAGE_SIZE) uint8_t share_page[PAGE_SIZE];
+extern void secondary_cold_entry(void);
 
 /*
  *
@@ -40,13 +40,11 @@
  * but rather through Hafnium print hypercall.
  *
  */
+
 static void __dead2 message_loop(ffa_vm_id_t vm_id, struct mailbox_buffers *mb)
 {
 	smc_ret_values ffa_ret;
-	uint32_t sp_response;
-	ffa_vm_id_t source;
 	ffa_vm_id_t destination;
-	uint64_t cactus_cmd;
 
 	/*
 	* This initial wait call is necessary to inform SPMD that
@@ -73,235 +71,13 @@
 
 		destination = ffa_dir_msg_dest(ffa_ret);
 
-		source = ffa_dir_msg_source(ffa_ret);
-
 		if (destination != vm_id) {
 			ERROR("%s(%u) invalid vm id 0x%x\n",
 				__func__, vm_id, destination);
 			break;
 		}
 
-		PRINT_CMD(ffa_ret);
-
-		cactus_cmd = cactus_get_cmd(ffa_ret);
-
-		switch (cactus_cmd) {
-		case CACTUS_MEM_SEND_CMD:
-			ffa_memory_management_test(
-					mb, vm_id, source,
-					cactus_req_mem_send_get_mem_func(
-						ffa_ret),
-					cactus_mem_send_get_handle(ffa_ret));
-
-			/*
-			 * If execution gets to this point means all operations
-			 * with memory retrieval went well, as such replying
-			 */
-			ffa_ret = cactus_success_resp(vm_id, source);
-			break;
-		case CACTUS_REQ_MEM_SEND_CMD:
-		{
-			uint32_t mem_func =
-				cactus_req_mem_send_get_mem_func(ffa_ret);
-			ffa_vm_id_t receiver =
-				cactus_req_mem_send_get_receiver(ffa_ret);
-			ffa_memory_handle_t handle;
-
-			VERBOSE("%x requested to send memory to %x (func: %x)\n",
-				source, receiver, mem_func);
-
-			const struct ffa_memory_region_constituent
-							constituents[] = {
-				{(void *)share_page, 1, 0}
-			};
-
-			const uint32_t constituents_count = (
-				sizeof(constituents) /
-				sizeof(constituents[0])
-			);
-
-			handle = ffa_memory_init_and_send(
-				(struct ffa_memory_region *)mb->send, PAGE_SIZE,
-				vm_id, receiver, constituents,
-				constituents_count, mem_func);
-
-			/*
-			 * If returned an invalid handle, we should break the
-			 * test.
-			 */
-			expect(handle != FFA_MEMORY_HANDLE_INVALID, true);
-
-			ffa_ret = cactus_mem_send_cmd(vm_id, receiver, mem_func,
-						      handle);
-
-			if (ffa_func_id(ffa_ret) !=
-					FFA_MSG_SEND_DIRECT_RESP_SMC32) {
-				ERROR("Failed to send message. error: %x\n",
-					ffa_error_code(ffa_ret));
-				ffa_ret = cactus_error_resp(vm_id, source);
-				break;
-			}
-
-			/* If anything went bad on the receiver's end. */
-			if (cactus_get_response(ffa_ret) == CACTUS_ERROR) {
-				ERROR("Received error from receiver!\n");
-				ffa_ret = cactus_error_resp(vm_id, source);
-				break;
-			}
-
-			if (mem_func != FFA_MEM_DONATE_SMC32) {
-				/*
-				 * Do a memory reclaim only if the mem_func
-				 * regards to memory share or lend operations,
-				 * as with a donate the owner is permanently
-				 * given up access to the memory region.
-				 */
-				if (ffa_mem_reclaim(handle, 0)
-							.ret0 == FFA_ERROR) {
-					ERROR("Failed to reclaim memory!\n");
-					ffa_ret = cactus_error_resp(vm_id,
-								    source);
-					break;
-				}
-
-				/**
-				 * Read Content that has been written to memory
-				 * to validate access to memory segment has been
-				 * reestablished, and receiver made use of
-				 * memory region.
-				 */
-				#if (LOG_LEVEL >= LOG_LEVEL_VERBOSE)
-					uint32_t *ptr =
-						(uint32_t *)constituents
-								->address;
-					VERBOSE("Memory contents after receiver"
-						" SP's use:\n");
-					for (unsigned int i = 0U; i < 5U; i++)
-						VERBOSE("      %u: %x\n", i,
-									ptr[i]);
-				#endif
-			}
-
-			ffa_ret = cactus_success_resp(vm_id, source);
-			break;
-		}
-		case CACTUS_ECHO_CMD:
-		{
-			uint64_t echo_val = cactus_echo_get_val(ffa_ret);
-
-			VERBOSE("Received echo at %x, value %llx from %x.\n",
-				destination, echo_val, source);
-			ffa_ret = cactus_response(destination, source, echo_val);
-			break;
-		}
-		case CACTUS_REQ_ECHO_CMD:
-		{
-			ffa_vm_id_t echo_dest =
-					cactus_req_echo_get_echo_dest(ffa_ret);
-			uint64_t echo_val = cactus_echo_get_val(ffa_ret);
-			bool success = true;
-
-			VERBOSE("%x requested to send echo to %x, value %llx\n",
-				source, echo_dest, echo_val);
-
-			ffa_ret = cactus_echo_send_cmd(vm_id, echo_dest,
-							echo_val);
-
-			if (ffa_func_id(ffa_ret) !=
-			    FFA_MSG_SEND_DIRECT_RESP_SMC32) {
-				ERROR("Failed to send message. error: %x\n",
-					ffa_error_code(ffa_ret));
-				success = false;
-			}
-
-			if (cactus_get_response(ffa_ret) != echo_val) {
-				ERROR("Echo Failed!\n");
-				success = false;
-			}
-
-			ffa_ret = success ? cactus_success_resp(vm_id, source) :
-					    cactus_error_resp(vm_id, source);
-			break;
-		}
-		case CACTUS_DEADLOCK_CMD:
-		case CACTUS_REQ_DEADLOCK_CMD:
-		{
-			ffa_vm_id_t deadlock_dest =
-				cactus_deadlock_get_next_dest(ffa_ret);
-			ffa_vm_id_t deadlock_next_dest = source;
-
-			if (cactus_cmd == CACTUS_DEADLOCK_CMD) {
-				VERBOSE("%x is creating deadlock. next: %x\n",
-					source, deadlock_dest);
-			} else if (cactus_cmd == CACTUS_REQ_DEADLOCK_CMD) {
-				VERBOSE(
-				"%x requested deadlock with %x and %x\n",
-				source, deadlock_dest, deadlock_next_dest);
-
-				deadlock_next_dest =
-					cactus_deadlock_get_next_dest2(ffa_ret);
-			}
-
-			ffa_ret = cactus_deadlock_send_cmd(vm_id, deadlock_dest,
-							   deadlock_next_dest);
-
-			/*
-			 * Should be true for the last partition to attempt
-			 * an FF-A direct message, to the first partition.
-			 */
-			bool is_deadlock_detected =
-				(ffa_func_id(ffa_ret) == FFA_ERROR) &&
-				(ffa_error_code(ffa_ret) == FFA_ERROR_BUSY);
-
-			/*
-			 * Should be true after the deadlock has been detected
-			 * and after the first response has been sent down the
-			 * request chain.
-			 */
-			bool is_returning_from_deadlock =
-				(ffa_func_id(ffa_ret) ==
-				 FFA_MSG_SEND_DIRECT_RESP_SMC32)
-				&&
-				(cactus_get_response(ffa_ret) == CACTUS_SUCCESS);
-
-			if (is_deadlock_detected) {
-				NOTICE("Attempting dealock but got error %x\n",
-					ffa_error_code(ffa_ret));
-			}
-
-			if (is_deadlock_detected ||
-			    is_returning_from_deadlock) {
-				/*
-				 * This is not the partition, that would have
-				 * created the deadlock. As such, reply back
-				 * to the partitions.
-				 */
-				ffa_ret = cactus_success_resp(vm_id, source);
-				break;
-			}
-
-			/* Shouldn't get to this point */
-			ERROR("Deadlock test went wrong!\n");
-			ffa_ret = cactus_error_resp(vm_id, source);
-			break;
-		}
-		case CACTUS_REQ_SIMD_FILL_CMD:
-			fill_simd_vectors();
-			ffa_ret = cactus_success_resp(vm_id, source);
-			break;
-		default:
-			/*
-			 * Currently direct message test is handled here.
-			 * TODO: create a case within the switch case
-			 * For the sake of testing, add the vm id to the
-			 * received message.
-			 */
-			sp_response = ffa_ret.ret3 | vm_id;
-			VERBOSE("Replying with direct message response: %x\n", sp_response);
-			ffa_ret = ffa_msg_send_direct_resp(vm_id,
-							   HYP_ID,
-							   sp_response);
-
+		if (!cactus_handle_cmd(&ffa_ret, &ffa_ret, mb)) {
 			break;
 		}
 	}
@@ -374,6 +150,16 @@
 	init_xlat_tables();
 }
 
+static void register_secondary_entrypoint(void)
+{
+	smc_args args;
+
+	args.fid = FFA_SECONDARY_EP_REGISTER_SMC64;
+	args.arg1 = (u_register_t)&secondary_cold_entry;
+
+	tftf_smc(&args);
+}
+
 int tftf_irq_handler_dispatcher(void)
 {
 	ERROR("%s\n", __func__);
@@ -381,31 +167,39 @@
 	return 0;
 }
 
-void __dead2 cactus_main(void)
+void __dead2 cactus_main(bool primary_cold_boot)
 {
 	assert(IS_IN_EL1() != 0);
 
 	struct mailbox_buffers mb;
 
-	/* Clear BSS */
-	memset((void *)CACTUS_BSS_START,
-	       0, CACTUS_BSS_END - CACTUS_BSS_START);
-
 	/* Get current FFA id */
 	smc_ret_values ffa_id_ret = ffa_id_get();
 	if (ffa_func_id(ffa_id_ret) != FFA_SUCCESS_SMC32) {
 		ERROR("FFA_ID_GET failed.\n");
 		panic();
 	}
-
 	ffa_vm_id_t ffa_id = ffa_id_ret.ret2 & 0xffff;
-	mb.send = (void *) get_sp_tx_start(ffa_id);
-	mb.recv = (void *) get_sp_rx_start(ffa_id);
 
-	/* Configure and enable Stage-1 MMU, enable D-Cache */
-	cactus_plat_configure_mmu(ffa_id);
+	if (primary_cold_boot == true) {
+		/* Clear BSS */
+		memset((void *)CACTUS_BSS_START,
+		       0, CACTUS_BSS_END - CACTUS_BSS_START);
+
+
+		mb.send = (void *) get_sp_tx_start(ffa_id);
+		mb.recv = (void *) get_sp_rx_start(ffa_id);
+
+		/* Configure and enable Stage-1 MMU, enable D-Cache */
+		cactus_plat_configure_mmu(ffa_id);
+	}
+
 	enable_mmu_el1(0);
 
+	if (primary_cold_boot == false) {
+		goto msg_loop;
+	}
+
 	if (ffa_id == SPM_VM_ID_FIRST) {
 		console_init(CACTUS_PL011_UART_BASE,
 			     CACTUS_PL011_UART_CLK_IN_HZ,
@@ -437,9 +231,12 @@
 	INFO("FF-A id: %x\n", ffa_id);
 	cactus_print_memory_layout(ffa_id);
 
+	register_secondary_entrypoint();
+
 	/* Invoking Tests */
 	ffa_tests(&mb);
 
+msg_loop:
 	/* End up to message loop */
 	message_loop(ffa_id, &mb);
 
diff --git a/spm/cactus/cactus_tests.h b/spm/cactus/cactus_tests.h
index c0c235e..1039ba5 100644
--- a/spm/cactus/cactus_tests.h
+++ b/spm/cactus/cactus_tests.h
@@ -13,19 +13,6 @@
  * Test functions
  */
 
-/*
- * Alter SIMD vectors to check saving of the context while switching between
- * the normal world and the secure world.
- */
-void fill_simd_vectors(void);
-
-/*
- * Test to FFA interfaces.
- */
-void ffa_memory_management_test(struct mailbox_buffers *mb, ffa_vm_id_t vm_id,
-				ffa_vm_id_t sender, uint32_t mem_func,
-				uint64_t handle);
-
 void ffa_tests(struct mailbox_buffers *mb);
 
 /*
diff --git a/spm/cactus/cactus_tests/cactus_message_loop.c b/spm/cactus/cactus_tests/cactus_message_loop.c
new file mode 100644
index 0000000..11207dc
--- /dev/null
+++ b/spm/cactus/cactus_tests/cactus_message_loop.c
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2021, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include "cactus_message_loop.h"
+#include "cactus_test_cmds.h"
+#include <ffa_helpers.h>
+#include <debug.h>
+
+
+/**
+ * Begin and end of command handler table, respectively. Both symbols defined by
+ * the linker.
+ */
+extern struct cactus_cmd_handler cactus_cmd_handler_begin[];
+extern struct cactus_cmd_handler cactus_cmd_handler_end[];
+
+#define PRINT_CMD(smc_ret)						\
+	VERBOSE("cmd %lx; args: %lx, %lx, %lx, %lx\n",	 		\
+		smc_ret.ret3, smc_ret.ret4, smc_ret.ret5, 		\
+		smc_ret.ret6, smc_ret.ret7)
+
+/**
+ * Traverses command table from section ".cactus_handler", searches for a
+ * registered command and invokes the respective handler.
+ */
+bool cactus_handle_cmd(smc_ret_values *cmd_args, smc_ret_values *ret,
+		       struct mailbox_buffers *mb)
+{
+	uint64_t in_cmd;
+
+	if (cmd_args == NULL || ret == NULL) {
+		ERROR("Invalid argumentos passed to %s!\n", __func__);
+		return false;
+	}
+
+	PRINT_CMD((*cmd_args));
+
+	in_cmd = cactus_get_cmd(*cmd_args);
+
+	for (struct cactus_cmd_handler *it_cmd = cactus_cmd_handler_begin;
+	     it_cmd < cactus_cmd_handler_end;
+	     it_cmd++) {
+		if (it_cmd->id == in_cmd) {
+			*ret = it_cmd->fn(cmd_args, mb);
+			return true;
+		}
+	}
+
+	ERROR("Unhandled test command!\n");
+	*ret = cactus_error_resp(ffa_dir_msg_dest(*cmd_args),
+				 ffa_dir_msg_source(*cmd_args),
+				 CACTUS_ERROR_UNHANDLED);
+	return true;
+}
diff --git a/spm/cactus/cactus_tests/cactus_test_cpu_features.c b/spm/cactus/cactus_tests/cactus_test_cpu_features.c
new file mode 100644
index 0000000..7bf6e83
--- /dev/null
+++ b/spm/cactus/cactus_tests/cactus_test_cpu_features.c
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2021, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include "cactus_message_loop.h"
+#include "cactus_test_cmds.h"
+#include "spm_common.h"
+
+/*
+ * Fill SIMD vectors from secure world side with a unique value.
+ * 0x22 is just a dummy value to be distinguished from the value
+ * in the normal world.
+ */
+CACTUS_CMD_HANDLER(req_simd_fill, CACTUS_REQ_SIMD_FILL_CMD)
+{
+	simd_vector_t simd_vectors[SIMD_NUM_VECTORS];
+
+	for (unsigned int num = 0U; num < SIMD_NUM_VECTORS; num++) {
+		memset(simd_vectors[num], 0x22 * num, sizeof(simd_vector_t));
+	}
+
+	fill_simd_vector_regs(simd_vectors);
+
+	return cactus_response(ffa_dir_msg_dest(*args),
+			       ffa_dir_msg_source(*args),
+			       CACTUS_SUCCESS);
+}
diff --git a/spm/cactus/cactus_tests/cactus_test_direct_messaging.c b/spm/cactus/cactus_tests/cactus_test_direct_messaging.c
new file mode 100644
index 0000000..a59cfa2
--- /dev/null
+++ b/spm/cactus/cactus_tests/cactus_test_direct_messaging.c
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2021, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include "cactus_message_loop.h"
+#include "cactus_test_cmds.h"
+#include <debug.h>
+#include <ffa_helpers.h>
+
+CACTUS_CMD_HANDLER(echo_cmd, CACTUS_ECHO_CMD)
+{
+	uint64_t echo_val = cactus_echo_get_val(*args);
+
+	VERBOSE("Received echo at %x, value %llx.\n", ffa_dir_msg_dest(*args),
+						      echo_val);
+
+	return cactus_success_resp(ffa_dir_msg_dest(*args),
+				   ffa_dir_msg_source(*args),
+				   echo_val);
+}
+
+CACTUS_CMD_HANDLER(req_echo_cmd, CACTUS_REQ_ECHO_CMD)
+{
+	smc_ret_values ffa_ret;
+	ffa_vm_id_t vm_id = ffa_dir_msg_dest(*args);
+	ffa_vm_id_t echo_dest = cactus_req_echo_get_echo_dest(*args);
+	uint64_t echo_val = cactus_echo_get_val(*args);
+
+	VERBOSE("%x requested to send echo to %x, value %llx\n",
+		ffa_dir_msg_source(*args), echo_dest, echo_val);
+
+	ffa_ret = cactus_echo_send_cmd(vm_id, echo_dest, echo_val);
+
+	if (!is_ffa_direct_response(ffa_ret)) {
+		return cactus_error_resp(vm_id, ffa_dir_msg_source(*args),
+					 CACTUS_ERROR_FFA_CALL);
+	}
+
+	if (cactus_get_response(ffa_ret) != CACTUS_SUCCESS ||
+	    cactus_echo_get_val(ffa_ret) != echo_val) {
+		ERROR("Echo Failed!\n");
+		return cactus_error_resp(vm_id, ffa_dir_msg_source(*args),
+					 CACTUS_ERROR_TEST);
+	}
+
+	return cactus_success_resp(vm_id, ffa_dir_msg_source(*args), 0);
+}
+
+static smc_ret_values base_deadlock_handler(ffa_vm_id_t vm_id,
+					    ffa_vm_id_t source,
+					    ffa_vm_id_t deadlock_dest,
+					    ffa_vm_id_t deadlock_next_dest)
+{
+	smc_ret_values ffa_ret;
+
+	ffa_ret = cactus_deadlock_send_cmd(vm_id, deadlock_dest,
+					   deadlock_next_dest);
+
+	/*
+	 * Should be true for the last partition to attempt
+	 * an FF-A direct message, to the first partition.
+	 */
+	bool is_deadlock_detected = (ffa_func_id(ffa_ret) == FFA_ERROR) &&
+				    (ffa_error_code(ffa_ret) == FFA_ERROR_BUSY);
+
+	/*
+	 * Should be true after the deadlock has been detected and after the
+	 * first response has been sent down the request chain.
+	 */
+	bool is_returning_from_deadlock =
+		(is_ffa_direct_response(ffa_ret)) &&
+		(cactus_get_response(ffa_ret) == CACTUS_SUCCESS);
+
+	if (is_deadlock_detected) {
+		VERBOSE("Attempt to create deadlock failed\n");
+	}
+
+	if (is_deadlock_detected || is_returning_from_deadlock) {
+		/*
+		 * This is not the partition, that would have created the
+		 * deadlock. As such, reply back to the partitions.
+		 */
+		return cactus_success_resp(vm_id, source, 0);
+	}
+
+	/* Shouldn't get to this point */
+	ERROR("Deadlock test went wrong!\n");
+	return cactus_error_resp(vm_id, source, CACTUS_ERROR_TEST);
+}
+
+CACTUS_CMD_HANDLER(deadlock_cmd, CACTUS_DEADLOCK_CMD)
+{
+	ffa_vm_id_t source = ffa_dir_msg_source(*args);
+	ffa_vm_id_t deadlock_dest = cactus_deadlock_get_next_dest(*args);
+	ffa_vm_id_t deadlock_next_dest = source;
+
+	VERBOSE("%x is creating deadlock. next: %x\n", source, deadlock_dest);
+
+	return base_deadlock_handler(ffa_dir_msg_dest(*args), source,
+				      deadlock_dest, deadlock_next_dest);
+}
+
+CACTUS_CMD_HANDLER(req_deadlock_cmd, CACTUS_REQ_DEADLOCK_CMD)
+{
+	ffa_vm_id_t vm_id = ffa_dir_msg_dest(*args);
+	ffa_vm_id_t source = ffa_dir_msg_source(*args);
+	ffa_vm_id_t deadlock_dest = cactus_deadlock_get_next_dest(*args);
+	ffa_vm_id_t deadlock_next_dest = cactus_deadlock_get_next_dest2(*args);
+
+	VERBOSE("%x requested deadlock with %x and %x\n",
+		ffa_dir_msg_source(*args), deadlock_dest, deadlock_next_dest);
+
+	return base_deadlock_handler(vm_id, source, deadlock_dest, deadlock_next_dest);
+}
diff --git a/spm/cactus/cactus_tests/cactus_test_ffa.c b/spm/cactus/cactus_tests/cactus_test_ffa.c
new file mode 100644
index 0000000..c1ba783
--- /dev/null
+++ b/spm/cactus/cactus_tests/cactus_test_ffa.c
@@ -0,0 +1,176 @@
+/*
+ * Copyright (c) 2018-2021, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+#include <assert.h>
+#include <debug.h>
+#include <errno.h>
+
+#include <cactus_def.h>
+#include <cactus_platform_def.h>
+#include <ffa_endpoints.h>
+#include <sp_helpers.h>
+#include <spm_common.h>
+
+#include <lib/libc/string.h>
+
+/* FFA version test helpers */
+#define FFA_MAJOR 1U
+#define FFA_MINOR 0U
+
+static const uint32_t primary_uuid[4] = PRIMARY_UUID;
+static const uint32_t secondary_uuid[4] = SECONDARY_UUID;
+static const uint32_t tertiary_uuid[4] = TERTIARY_UUID;
+static const uint32_t null_uuid[4] = {0};
+
+/*
+ * Test FFA_FEATURES interface.
+ */
+static void ffa_features_test(void)
+{
+	const char *test_features = "FFA Features interface";
+	smc_ret_values ffa_ret;
+	const struct ffa_features_test *ffa_feature_test_target;
+	unsigned int i, test_target_size =
+		get_ffa_feature_test_target(&ffa_feature_test_target);
+
+
+	announce_test_section_start(test_features);
+
+	for (i = 0U; i < test_target_size; i++) {
+		announce_test_start(ffa_feature_test_target[i].test_name);
+
+		ffa_ret = ffa_features(ffa_feature_test_target[i].feature);
+		expect(ffa_func_id(ffa_ret), ffa_feature_test_target[i].expected_ret);
+		if (ffa_feature_test_target[i].expected_ret == FFA_ERROR) {
+			expect(ffa_error_code(ffa_ret), FFA_ERROR_NOT_SUPPORTED);
+		}
+
+		announce_test_end(ffa_feature_test_target[i].test_name);
+	}
+
+	announce_test_section_end(test_features);
+}
+
+static void ffa_partition_info_helper(struct mailbox_buffers *mb, const uint32_t uuid[4],
+			       const struct ffa_partition_info *expected,
+			       const uint16_t expected_size)
+{
+	smc_ret_values ret = ffa_partition_info_get(uuid);
+	unsigned int i;
+	expect(ffa_func_id(ret), FFA_SUCCESS_SMC32);
+
+	struct ffa_partition_info *info = (struct ffa_partition_info *)(mb->recv);
+	for (i = 0U; i < expected_size; i++) {
+		expect(info[i].id, expected[i].id);
+		expect(info[i].exec_context, expected[i].exec_context);
+		expect(info[i].properties, expected[i].properties);
+	}
+
+	ret = ffa_rx_release();
+	expect(ffa_func_id(ret), FFA_SUCCESS_SMC32);
+}
+
+static void ffa_partition_info_wrong_test(void)
+{
+	const char *test_wrong_uuid = "Request wrong UUID";
+	uint32_t uuid[4] = {1};
+
+	announce_test_start(test_wrong_uuid);
+
+	smc_ret_values ret = ffa_partition_info_get(uuid);
+	expect(ffa_func_id(ret), FFA_ERROR);
+	expect(ffa_error_code(ret), FFA_ERROR_INVALID_PARAMETER);
+
+	announce_test_end(test_wrong_uuid);
+}
+
+static void ffa_partition_info_get_test(struct mailbox_buffers *mb)
+{
+	const char *test_partition_info = "FFA Partition info interface";
+	const char *test_primary = "Get primary partition info";
+	const char *test_secondary = "Get secondary partition info";
+	const char *test_tertiary = "Get tertiary partition info";
+	const char *test_all = "Get all partitions info";
+
+	const struct ffa_partition_info expected_info[] = {
+		/* Primary partition info */
+		{
+			.id = SPM_VM_ID_FIRST,
+			.exec_context = CACTUS_PRIMARY_EC_COUNT,
+			/* Supports receipt of direct message requests. */
+			.properties = 1U
+		},
+		/* Secondary partition info */
+		{
+			.id = SPM_VM_ID_FIRST + 1U,
+			.exec_context = CACTUS_SECONDARY_EC_COUNT,
+			.properties = 1U
+		},
+		/* Tertiary partition info */
+		{
+			.id = SPM_VM_ID_FIRST + 2U,
+			.exec_context = CACTUS_TERTIARY_EC_COUNT,
+			.properties = 1U
+		}
+	};
+
+	announce_test_section_start(test_partition_info);
+
+	announce_test_start(test_tertiary);
+	ffa_partition_info_helper(mb, tertiary_uuid, &expected_info[2], 1);
+	announce_test_end(test_tertiary);
+
+	announce_test_start(test_secondary);
+	ffa_partition_info_helper(mb, secondary_uuid, &expected_info[1], 1);
+	announce_test_end(test_secondary);
+
+	announce_test_start(test_primary);
+	ffa_partition_info_helper(mb, primary_uuid, &expected_info[0], 1);
+	announce_test_end(test_primary);
+
+	announce_test_start(test_all);
+	ffa_partition_info_helper(mb, null_uuid, expected_info, 3);
+	announce_test_end(test_all);
+
+	ffa_partition_info_wrong_test();
+
+	announce_test_section_end(test_partition_info);
+}
+
+void ffa_version_test(void)
+{
+	const char *test_ffa_version = "FFA Version interface";
+
+	announce_test_start(test_ffa_version);
+
+	smc_ret_values ret = ffa_version(MAKE_FFA_VERSION(FFA_MAJOR, FFA_MINOR));
+	uint32_t spm_version = (uint32_t)ret.ret0;
+
+	bool ffa_version_compatible =
+		((spm_version >> FFA_VERSION_MAJOR_SHIFT) == FFA_MAJOR &&
+		 (spm_version & FFA_VERSION_MINOR_MASK) >= FFA_MINOR);
+
+	NOTICE("FFA_VERSION returned %u.%u; Compatible: %i\n",
+		spm_version >> FFA_VERSION_MAJOR_SHIFT,
+		spm_version & FFA_VERSION_MINOR_MASK,
+		(int)ffa_version_compatible);
+
+	expect((int)ffa_version_compatible, (int)true);
+
+	announce_test_end(test_ffa_version);
+}
+
+void ffa_tests(struct mailbox_buffers *mb)
+{
+	const char *test_ffa = "FFA Interfaces";
+
+	announce_test_section_start(test_ffa);
+
+	ffa_features_test();
+	ffa_version_test();
+	ffa_partition_info_get_test(mb);
+
+	announce_test_section_end(test_ffa);
+}
diff --git a/spm/cactus/cactus_tests/cactus_test_memory_sharing.c b/spm/cactus/cactus_tests/cactus_test_memory_sharing.c
new file mode 100644
index 0000000..e7bce50
--- /dev/null
+++ b/spm/cactus/cactus_tests/cactus_test_memory_sharing.c
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) 2021, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <cactus_def.h>
+#include "cactus_message_loop.h"
+#include "cactus_test_cmds.h"
+#include "cactus_tests.h"
+#include <debug.h>
+#include <ffa_helpers.h>
+#include <sp_helpers.h>
+#include <xlat_tables_defs.h>
+#include <lib/xlat_tables/xlat_tables_v2.h>
+
+/* Memory section to be used for memory share operations */
+static __aligned(PAGE_SIZE) uint8_t share_page[PAGE_SIZE];
+
+CACTUS_CMD_HANDLER(mem_send_cmd, CACTUS_MEM_SEND_CMD)
+{
+	struct ffa_memory_region *m;
+	struct ffa_composite_memory_region *composite;
+	int ret;
+	unsigned int mem_attrs;
+	uint32_t *ptr;
+	ffa_vm_id_t source = ffa_dir_msg_source(*args);
+	ffa_vm_id_t vm_id = ffa_dir_msg_dest(*args);
+	uint32_t mem_func = cactus_req_mem_send_get_mem_func(*args);
+	uint64_t handle = cactus_mem_send_get_handle(*args);
+
+	expect(memory_retrieve(mb, &m, handle, source, vm_id, mem_func), true);
+
+	composite = ffa_memory_region_get_composite(m, 0);
+
+	VERBOSE("Address: %p; page_count: %x %x\n",
+		composite->constituents[0].address,
+		composite->constituents[0].page_count, PAGE_SIZE);
+
+	/* This test is only concerned with RW permissions. */
+	if (ffa_get_data_access_attr(
+			m->receivers[0].receiver_permissions.permissions) !=
+		FFA_DATA_ACCESS_RW) {
+		ERROR("Permissions not expected!\n");
+		return cactus_error_resp(vm_id, source, CACTUS_ERROR_TEST);
+	}
+
+	mem_attrs = MT_RW_DATA | MT_EXECUTE_NEVER;
+
+	if (!IS_SP_ID(source)) {
+		mem_attrs |= MT_NS;
+	}
+
+	ret = mmap_add_dynamic_region(
+			(uint64_t)composite->constituents[0].address,
+			(uint64_t)composite->constituents[0].address,
+			composite->constituents[0].page_count * PAGE_SIZE,
+			mem_attrs);
+
+	if (ret != 0) {
+		ERROR("Failed first mmap_add_dynamic_region!\n");
+		return cactus_error_resp(vm_id, source, CACTUS_ERROR_TEST);
+	}
+
+	VERBOSE("Memory has been mapped\n");
+
+	ptr = (uint32_t *) composite->constituents[0].address;
+
+	/* Write mem_func to retrieved memory region for validation purposes. */
+	VERBOSE("Writing: %x\n", mem_func);
+	for (unsigned int i = 0U; i < 5U; i++)
+		ptr[i] = mem_func;
+
+	/*
+	 * A FFA_MEM_DONATE changes the ownership of the page, as such no
+	 * relinquish is needed.
+	 */
+	if (mem_func != FFA_MEM_DONATE_SMC32) {
+		ret = mmap_remove_dynamic_region(
+			(uint64_t)composite->constituents[0].address,
+			composite->constituents[0].page_count * PAGE_SIZE);
+
+		if (ret != 0) {
+			ERROR("Failed first mmap_add_dynamic_region!\n");
+			return cactus_error_resp(vm_id, source,
+						 CACTUS_ERROR_TEST);
+		}
+
+		if (!memory_relinquish((struct ffa_mem_relinquish *)mb->send,
+					m->handle, vm_id)) {
+			return cactus_error_resp(vm_id, source,
+						 CACTUS_ERROR_TEST);
+		}
+	}
+
+	if (ffa_func_id(ffa_rx_release()) != FFA_SUCCESS_SMC32) {
+		ERROR("Failed to release buffer!\n");
+		return cactus_error_resp(vm_id, source,
+					 CACTUS_ERROR_FFA_CALL);
+	}
+
+	return cactus_success_resp(vm_id,
+				   source, 0);
+}
+
+CACTUS_CMD_HANDLER(req_mem_send_cmd, CACTUS_REQ_MEM_SEND_CMD)
+{
+	smc_ret_values ffa_ret;
+	uint32_t mem_func = cactus_req_mem_send_get_mem_func(*args);
+	ffa_vm_id_t receiver = cactus_req_mem_send_get_receiver(*args);
+	ffa_memory_handle_t handle;
+	ffa_vm_id_t vm_id = ffa_dir_msg_dest(*args);
+	ffa_vm_id_t source = ffa_dir_msg_source(*args);
+
+	VERBOSE("%x requested to send memory to %x (func: %x)\n",
+		source, receiver, mem_func);
+
+	const struct ffa_memory_region_constituent constituents[] = {
+		{(void *)share_page, 1, 0}
+	};
+
+	const uint32_t constituents_count = (sizeof(constituents) /
+					     sizeof(constituents[0]));
+
+	handle = memory_init_and_send(
+		(struct ffa_memory_region *)mb->send, PAGE_SIZE,
+		vm_id, receiver, constituents,
+		constituents_count, mem_func);
+
+	/*
+	 * If returned an invalid handle, we should break the test.
+	 */
+	if (handle == FFA_MEMORY_HANDLE_INVALID) {
+		ERROR("Received an invalid FF-A memory Handle!\n");
+		return cactus_error_resp(vm_id, source,
+					 CACTUS_ERROR_TEST);
+	}
+
+	ffa_ret = cactus_mem_send_cmd(vm_id, receiver, mem_func, handle);
+
+	if (!is_ffa_direct_response(ffa_ret)) {
+		return cactus_error_resp(vm_id, source, CACTUS_ERROR_FFA_CALL);
+	}
+
+	/* If anything went bad on the receiver's end. */
+	if (cactus_get_response(ffa_ret) == CACTUS_ERROR) {
+		ERROR("Received error from receiver!\n");
+		return cactus_error_resp(vm_id, source, CACTUS_ERROR_TEST);
+	}
+
+	if (mem_func != FFA_MEM_DONATE_SMC32) {
+		/*
+		 * Do a memory reclaim only if the mem_func regards to memory
+		 * share or lend operations, as with a donate the owner is
+		 * permanently given up access to the memory region.
+		 */
+		ffa_ret = ffa_mem_reclaim(handle, 0);
+		if (is_ffa_call_error(ffa_ret)) {
+			return cactus_error_resp(vm_id, source,
+						 CACTUS_ERROR_TEST);
+		}
+
+		/**
+		 * Read Content that has been written to memory to validate
+		 * access to memory segment has been reestablished, and receiver
+		 * made use of memory region.
+		 */
+		#if (LOG_LEVEL >= LOG_LEVEL_VERBOSE)
+			uint32_t *ptr = (uint32_t *)constituents->address;
+
+			VERBOSE("Memory contents after receiver SP's use:\n");
+			for (unsigned int i = 0U; i < 5U; i++) {
+				VERBOSE("      %u: %x\n", i, ptr[i]);
+			}
+		#endif
+	}
+
+	return cactus_success_resp(vm_id, source, 0);
+}
diff --git a/spm/cactus/plat/arm/fvp/include/cactus_platform_def.h b/spm/cactus/plat/arm/fvp/include/cactus_platform_def.h
index e879002..b4c57ef 100644
--- a/spm/cactus/plat/arm/fvp/include/cactus_platform_def.h
+++ b/spm/cactus/plat/arm/fvp/include/cactus_platform_def.h
@@ -16,9 +16,10 @@
 #define CACTUS_PL011_UART_CLK_IN_HZ	PL011_UART2_CLK_IN_HZ
 
 #define PLAT_CACTUS_RX_BASE		ULL(0x7300000)
+#define PLAT_CACTUS_CORE_COUNT		(8U)
 
 #define CACTUS_PRIMARY_EC_COUNT		(8U)
 #define CACTUS_SECONDARY_EC_COUNT	(8U)
-#define CACTUS_TERTIARY_EC_COUNT	(8U)
+#define CACTUS_TERTIARY_EC_COUNT	(1U)
 
 #endif /* CACTUS_PLATFORM_DEF_H */
diff --git a/spm/cactus/plat/arm/tc0/fdts/cactus-tertiary.dts b/spm/cactus/plat/arm/tc0/fdts/cactus-tertiary.dts
index 724d595..3b50530 100644
--- a/spm/cactus/plat/arm/tc0/fdts/cactus-tertiary.dts
+++ b/spm/cactus/plat/arm/tc0/fdts/cactus-tertiary.dts
@@ -18,7 +18,7 @@
 	ffa-version = <0x00010000>; /* 31:16 - Major, 15:0 - Minor */
 	uuid = <0x79b55c73 0x1d8c44b9 0x859361e1 0x770ad8d2>;
 	id = <3>;
-	execution-ctx-count = <8>;
+	execution-ctx-count = <1>;
 	exception-level = <2>; /* S-EL1 */
 	execution-state = <0>; /* AARCH64 */
 	load-address = <0xfe200000>;
diff --git a/spm/cactus/plat/arm/tc0/include/cactus_platform_def.h b/spm/cactus/plat/arm/tc0/include/cactus_platform_def.h
index d551314..42dd291 100644
--- a/spm/cactus/plat/arm/tc0/include/cactus_platform_def.h
+++ b/spm/cactus/plat/arm/tc0/include/cactus_platform_def.h
@@ -19,6 +19,6 @@
 
 #define CACTUS_PRIMARY_EC_COUNT		(8U)
 #define CACTUS_SECONDARY_EC_COUNT	(8U)
-#define CACTUS_TERTIARY_EC_COUNT	(8U)
+#define CACTUS_TERTIARY_EC_COUNT	(1U)
 
 #endif /* CACTUS_PLATFORM_DEF_H */
diff --git a/tftf/tests/runtime_services/secure_service/ffa_helpers.c b/tftf/tests/runtime_services/secure_service/ffa_helpers.c
index 923ee2c..8e7b58c 100644
--- a/tftf/tests/runtime_services/secure_service/ffa_helpers.c
+++ b/tftf/tests/runtime_services/secure_service/ffa_helpers.c
@@ -3,8 +3,6 @@
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
-
-#include <debug.h>
 #include <smccc.h>
 
 #include <ffa_endpoints.h>
@@ -57,58 +55,83 @@
  *     -BUSY: Message target is busy
  *     -ABORTED: Message target ran into an unexpected error and has aborted
  */
-static smc_ret_values __ffa_msg_send_direct_req32_5(uint32_t source_id,
-						     uint32_t dest_id,
-						     uint32_t arg0,
-						     uint32_t arg1,
-						     uint32_t arg2,
-						     uint32_t arg3,
-						     uint32_t arg4)
+smc_ret_values ffa_msg_send_direct_req64(ffa_vm_id_t source_id,
+					 ffa_vm_id_t dest_id, uint64_t arg0,
+					 uint64_t arg1, uint64_t arg2,
+					 uint64_t arg3, uint64_t arg4)
 {
 	smc_args args = {
-		FFA_MSG_SEND_DIRECT_REQ_SMC32,
-		(source_id << 16) | dest_id,
-		0,
-		arg0, arg1, arg2, arg3, arg4
+		.fid = FFA_MSG_SEND_DIRECT_REQ_SMC64,
+		.arg1 = ((uint32_t)(source_id << 16)) | (dest_id),
+		.arg2 = 0,
+		.arg3 = arg0,
+		.arg4 = arg1,
+		.arg5 = arg2,
+		.arg6 = arg3,
+		.arg7 = arg4,
 	};
 
 	return tftf_smc(&args);
 }
 
-/* Direct message send helper accepting a single 32b message argument */
-smc_ret_values ffa_msg_send_direct_req(uint32_t source_id, uint32_t dest_id,
-					uint32_t message)
-{
-	return __ffa_msg_send_direct_req32_5(source_id, dest_id,
-					      message, 0, 0, 0, 0);
-}
-
-smc_ret_values ffa_msg_send_direct_req64_5args(uint32_t source_id,
-						     uint32_t dest_id,
-						     uint64_t arg0,
-						     uint64_t arg1,
-						     uint64_t arg2,
-						     uint64_t arg3,
-						     uint64_t arg4)
+smc_ret_values ffa_msg_send_direct_req32(ffa_vm_id_t source_id,
+					 ffa_vm_id_t dest_id, uint32_t arg0,
+					 uint32_t arg1, uint32_t arg2,
+					 uint32_t arg3, uint32_t arg4)
 {
 	smc_args args = {
-		FFA_MSG_SEND_DIRECT_REQ_SMC64,
-		(source_id << 16) | dest_id,
-		0,
-		arg0, arg1, arg2, arg3, arg4
+		.fid = FFA_MSG_SEND_DIRECT_REQ_SMC32,
+		.arg1 = ((uint32_t)(source_id << 16)) | (dest_id),
+		.arg2 = 0,
+		.arg3 = arg0,
+		.arg4 = arg1,
+		.arg5 = arg2,
+		.arg6 = arg3,
+		.arg7 = arg4,
 	};
 
 	return tftf_smc(&args);
 }
 
-/* Direct message send helper accepting a single 64b message argument */
-smc_ret_values ffa_msg_send_direct_req64(uint32_t source_id, uint32_t dest_id,
-					uint64_t message)
+smc_ret_values ffa_msg_send_direct_resp64(ffa_vm_id_t source_id,
+					  ffa_vm_id_t dest_id, uint64_t arg0,
+					  uint64_t arg1, uint64_t arg2,
+					  uint64_t arg3, uint64_t arg4)
 {
-	return ffa_msg_send_direct_req64_5args(source_id, dest_id,
-					      message, 0, 0, 0, 0);
+	smc_args args = {
+		.fid = FFA_MSG_SEND_DIRECT_RESP_SMC64,
+		.arg1 = ((uint32_t)(source_id << 16)) | (dest_id),
+		.arg2 = 0,
+		.arg3 = arg0,
+		.arg4 = arg1,
+		.arg5 = arg2,
+		.arg6 = arg3,
+		.arg7 = arg4,
+	};
+
+	return tftf_smc(&args);
 }
 
+smc_ret_values ffa_msg_send_direct_resp32(ffa_vm_id_t source_id,
+					  ffa_vm_id_t dest_id, uint32_t arg0,
+					  uint32_t arg1, uint32_t arg2,
+					  uint32_t arg3, uint32_t arg4)
+{
+	smc_args args = {
+		.fid = FFA_MSG_SEND_DIRECT_RESP_SMC32,
+		.arg1 = ((uint32_t)(source_id << 16)) | (dest_id),
+		.arg2 = 0,
+		.arg3 = arg0,
+		.arg4 = arg1,
+		.arg5 = arg2,
+		.arg6 = arg3,
+		.arg7 = arg4,
+	};
+
+	return tftf_smc(&args);
+}
+
+
 /**
  * Initialises the header of the given `ffa_memory_region`, not including the
  * composite memory region offset.
@@ -268,91 +291,6 @@
 	       memory_region->receiver_count * sizeof(struct ffa_memory_access);
 }
 
-/**
- * Helper to call memory send function whose func id is passed as a parameter.
- * Returns a valid handle in case of successful operation or
- * FFA_MEMORY_HANDLE_INVALID if something goes wrong.
- *
- * TODO: Do memory send with 'ffa_memory_region' taking multiple segments
- */
-ffa_memory_handle_t ffa_memory_send(
-	struct ffa_memory_region *memory_region, uint32_t mem_func,
-	uint32_t fragment_length, uint32_t total_length)
-{
-	smc_ret_values ret;
-	ffa_vm_id_t receiver =
-		memory_region->receivers[0].receiver_permissions.receiver;
-
-	if (fragment_length != total_length) {
-		ERROR("For now, fragment_length and total_length need to be"
-		      " equal");
-		return FFA_MEMORY_HANDLE_INVALID;
-	}
-
-	switch (mem_func) {
-	case FFA_MEM_SHARE_SMC32:
-		ret = ffa_mem_share(total_length, fragment_length);
-		break;
-	case FFA_MEM_LEND_SMC32:
-		ret = ffa_mem_lend(total_length, fragment_length);
-		break;
-	case FFA_MEM_DONATE_SMC32:
-		ret = ffa_mem_donate(total_length, fragment_length);
-		break;
-	default:
-		ERROR("TFTF - Invalid func id %x!\n", mem_func);
-		return FFA_MEMORY_HANDLE_INVALID;
-	}
-
-	if (ffa_func_id(ret) != FFA_SUCCESS_SMC32) {
-		ERROR("Failed to send memory to %x, error: %x.\n",
-				      receiver, ffa_error_code(ret));
-		return FFA_MEMORY_HANDLE_INVALID;
-	}
-
-	return ffa_mem_success_handle(ret);
-;
-}
-
-/**
- * Helper that initializes and sends a memory region. The memory region's
- * configuration is statically defined and is implementation specific. However,
- * doing it in this file for simplicity and for testing purposes.
- */
-ffa_memory_handle_t ffa_memory_init_and_send(
-	struct ffa_memory_region *memory_region, size_t memory_region_max_size,
-	ffa_vm_id_t sender, ffa_vm_id_t receiver,
-	const struct ffa_memory_region_constituent *constituents,
-	uint32_t constituents_count, uint32_t mem_func)
-{
-	uint32_t remaining_constituent_count;
-	uint32_t total_length;
-	uint32_t fragment_length;
-
-	enum ffa_data_access data_access = (mem_func == FFA_MEM_DONATE_SMC32) ?
-						FFA_DATA_ACCESS_NOT_SPECIFIED :
-						FFA_DATA_ACCESS_RW;
-
-	remaining_constituent_count = ffa_memory_region_init(
-		memory_region, memory_region_max_size, sender, receiver, constituents,
-		constituents_count, 0, 0, data_access,
-		FFA_INSTRUCTION_ACCESS_NOT_SPECIFIED,
-		FFA_MEMORY_NORMAL_MEM, FFA_MEMORY_CACHE_WRITE_BACK,
-		FFA_MEMORY_INNER_SHAREABLE, &total_length, &fragment_length
-	);
-
-	/*
-	 * For simplicity of the test, and at least for the time being,
-	 * the following condition needs to be true.
-	 */
-	if (remaining_constituent_count != 0U) {
-		ERROR("Remaining constituent should be 0\n");
-		return FFA_MEMORY_HANDLE_INVALID;
-	}
-
-	return ffa_memory_send(memory_region, mem_func, fragment_length,
-			       total_length);
-}
 
 /*
  * FFA Version ABI helper.
@@ -388,19 +326,6 @@
 	return tftf_smc(&args);
 }
 
-smc_ret_values ffa_msg_send_direct_resp(ffa_vm_id_t source_id,
-						ffa_vm_id_t dest_id,
-						uint32_t message)
-{
-	smc_args args = {
-		.fid = FFA_MSG_SEND_DIRECT_RESP_SMC32,
-		.arg1 = ((uint32_t)source_id << 16) | dest_id,
-		.arg3 = message
-	};
-
-	return tftf_smc(&args);
-}
-
 smc_ret_values ffa_error(int32_t error_code)
 {
 	smc_args args = {
diff --git a/tftf/tests/runtime_services/secure_service/spm_common.c b/tftf/tests/runtime_services/secure_service/spm_common.c
index cc3ed5d..12b70a9 100644
--- a/tftf/tests/runtime_services/secure_service/spm_common.c
+++ b/tftf/tests/runtime_services/secure_service/spm_common.c
@@ -4,13 +4,61 @@
  * SPDX-License-Identifier: BSD-3-Clause
  */
 
+#include <debug.h>
 #include <ffa_endpoints.h>
 #include <spm_common.h>
+#include <xlat_tables_v2.h>
 
 #define __STR(x) #x
 #define STR(x) __STR(x)
 #define SIMD_TWO_VECTORS_BYTES_STR	(2 * SIMD_VECTOR_LEN_BYTES)
 
+/**
+ * Helper to log errors after FF-A calls.
+ */
+bool is_ffa_call_error(smc_ret_values ret)
+{
+	if (ffa_func_id(ret) == FFA_ERROR) {
+		ERROR("FF-A call returned error (%x): %d\n",
+		      ffa_func_id(ret), ffa_error_code(ret));
+		return true;
+	}
+	return false;
+}
+
+/**
+ * Helper to verify return of FF-A call is an FFA_MSG_SEND_DIRECT_RESP.
+ * Should be used after FFA_MSG_SEND_DIRECT_REQ, or after sending a test command
+ * to an SP.
+ */
+bool is_ffa_direct_response(smc_ret_values ret)
+{
+	if ((ffa_func_id(ret) == FFA_MSG_SEND_DIRECT_RESP_SMC32) ||
+	    (ffa_func_id(ret) == FFA_MSG_SEND_DIRECT_RESP_SMC64)) {
+		return true;
+	}
+
+	ERROR("%x is not FF-A response.\n", ffa_func_id(ret));
+	/* To log error in case it is FFA_ERROR*/
+	is_ffa_call_error(ret);
+
+	return false;
+}
+
+/**
+ * Helper to check the return value of FF-A call is as expected.
+ */
+bool is_expected_ffa_return(smc_ret_values ret, uint32_t func_id)
+{
+	if (ffa_func_id(ret) == func_id) {
+		return true;
+	}
+
+	ERROR("Expecting %x, FF-A return was %x\n", func_id, ffa_func_id(ret));
+
+	return false;
+}
+
 void fill_simd_vector_regs(const simd_vector_t v[SIMD_NUM_VECTORS])
 {
 #ifdef __aarch64__
@@ -83,8 +131,9 @@
 	 * Send a first OP-TEE-defined protocol message through
 	 * FFA direct message.
 	 */
-	ret_values = ffa_msg_send_direct_req(HYP_ID, SP_ID(1),
-						OPTEE_FFA_GET_API_VERSION);
+	ret_values = ffa_msg_send_direct_req32(HYP_ID, SP_ID(1),
+					       OPTEE_FFA_GET_API_VERSION, 0,
+					       0, 0, 0);
 	if ((ret_values.ret3 == FFA_VERSION_MAJOR) &&
 	    (ret_values.ret4 == FFA_VERSION_MINOR)) {
 		is_optee_spmc_criteria++;
@@ -94,8 +143,9 @@
 	 * Send a second OP-TEE-defined protocol message through
 	 * FFA direct message.
 	 */
-	ret_values = ffa_msg_send_direct_req(HYP_ID, SP_ID(1),
-						OPTEE_FFA_GET_OS_VERSION);
+	ret_values = ffa_msg_send_direct_req32(HYP_ID, SP_ID(1),
+					       OPTEE_FFA_GET_OS_VERSION,
+					       0, 0, 0, 0);
 	if ((ret_values.ret3 == OPTEE_FFA_GET_OS_VERSION_MAJOR) &&
 	    (ret_values.ret4 == OPTEE_FFA_GET_OS_VERSION_MINOR)) {
 		is_optee_spmc_criteria++;
@@ -146,3 +196,175 @@
 	return sizeof(ffa_feature_test_target) /
 	       sizeof(struct ffa_features_test);
 }
+
+bool memory_retrieve(struct mailbox_buffers *mb,
+		     struct ffa_memory_region **retrieved, uint64_t handle,
+		     ffa_vm_id_t sender, ffa_vm_id_t receiver,
+		     uint32_t mem_func)
+{
+	smc_ret_values ret;
+	uint32_t fragment_size;
+	uint32_t total_size;
+	uint32_t descriptor_size;
+
+	if (retrieved == NULL || mb == NULL) {
+		ERROR("Invalid parameters!\n");
+		return false;
+	}
+
+	/*
+	 * TODO: Revise shareability attribute in function call
+	 * below.
+	 * https://lists.trustedfirmware.org/pipermail/hafnium/2020-June/000023.html
+	 */
+	descriptor_size = ffa_memory_retrieve_request_init(
+	    mb->send, handle, sender, receiver, 0, 0,
+	    FFA_DATA_ACCESS_RW,
+	    FFA_INSTRUCTION_ACCESS_NX,
+	    FFA_MEMORY_NORMAL_MEM,
+	    FFA_MEMORY_CACHE_WRITE_BACK,
+	    FFA_MEMORY_OUTER_SHAREABLE);
+
+	ret = ffa_mem_retrieve_req(descriptor_size, descriptor_size);
+
+	if (ffa_func_id(ret) != FFA_MEM_RETRIEVE_RESP) {
+		ERROR("Couldn't retrieve the memory page. Error: %x\n",
+		      ffa_error_code(ret));
+		return false;
+	}
+
+	/*
+	 * Following total_size and fragment_size are useful to keep track
+	 * of the state of transaction. When the sum of all fragment_size of all
+	 * fragments is equal to total_size, the memory transaction has been
+	 * completed.
+	 * This is a simple test with only one segment. As such, upon
+	 * successful ffa_mem_retrieve_req, total_size must be equal to
+	 * fragment_size.
+	 */
+	total_size = ret.ret1;
+	fragment_size = ret.ret2;
+
+	if (total_size != fragment_size) {
+		ERROR("Only expect one memory segment to be sent!\n");
+		return false;
+	}
+
+	if (fragment_size > PAGE_SIZE) {
+		ERROR("Fragment should be smaller than RX buffer!\n");
+		return false;
+	}
+
+	*retrieved = (struct ffa_memory_region *)mb->recv;
+
+	if ((*retrieved)->receiver_count > MAX_MEM_SHARE_RECIPIENTS) {
+		VERBOSE("SPMC memory sharing operations support max of %u "
+			"receivers!\n", MAX_MEM_SHARE_RECIPIENTS);
+		return false;
+	}
+
+	VERBOSE("Memory Retrieved!\n");
+
+	return true;
+}
+
+bool memory_relinquish(struct ffa_mem_relinquish *m, uint64_t handle,
+		       ffa_vm_id_t id)
+{
+	smc_ret_values ret;
+
+	ffa_mem_relinquish_init(m, handle, 0, id);
+	ret = ffa_mem_relinquish();
+	if (ffa_func_id(ret) != FFA_SUCCESS_SMC32) {
+		ERROR("%s failed to relinquish memory! error: %x\n",
+		      __func__, ffa_error_code(ret));
+		return false;
+	}
+
+	VERBOSE("Memory Relinquished!\n");
+	return true;
+}
+
+/**
+ * Helper to call memory send function whose func id is passed as a parameter.
+ * Returns a valid handle in case of successful operation or
+ * FFA_MEMORY_HANDLE_INVALID if something goes wrong.
+ *
+ * TODO: Do memory send with 'ffa_memory_region' taking multiple segments
+ */
+ffa_memory_handle_t memory_send(
+	struct ffa_memory_region *memory_region, uint32_t mem_func,
+	uint32_t fragment_length, uint32_t total_length)
+{
+	smc_ret_values ret;
+	ffa_vm_id_t receiver =
+		memory_region->receivers[0].receiver_permissions.receiver;
+
+	if (fragment_length != total_length) {
+		ERROR("For now, fragment_length and total_length need to be"
+		      " equal");
+		return FFA_MEMORY_HANDLE_INVALID;
+	}
+
+	switch (mem_func) {
+	case FFA_MEM_SHARE_SMC32:
+		ret = ffa_mem_share(total_length, fragment_length);
+		break;
+	case FFA_MEM_LEND_SMC32:
+		ret = ffa_mem_lend(total_length, fragment_length);
+		break;
+	case FFA_MEM_DONATE_SMC32:
+		ret = ffa_mem_donate(total_length, fragment_length);
+		break;
+	default:
+		ERROR("TFTF - Invalid func id %x!\n", mem_func);
+		return FFA_MEMORY_HANDLE_INVALID;
+	}
+
+	if (is_ffa_call_error(ret)) {
+		ERROR("Failed to send message to: %x\n", receiver);
+		return FFA_MEMORY_HANDLE_INVALID;
+	}
+
+	return ffa_mem_success_handle(ret);
+}
+
+/**
+ * Helper that initializes and sends a memory region. The memory region's
+ * configuration is statically defined and is implementation specific. However,
+ * doing it in this file for simplicity and for testing purposes.
+ */
+ffa_memory_handle_t memory_init_and_send(
+	struct ffa_memory_region *memory_region, size_t memory_region_max_size,
+	ffa_vm_id_t sender, ffa_vm_id_t receiver,
+	const struct ffa_memory_region_constituent *constituents,
+	uint32_t constituents_count, uint32_t mem_func)
+{
+	uint32_t remaining_constituent_count;
+	uint32_t total_length;
+	uint32_t fragment_length;
+
+	enum ffa_data_access data_access = (mem_func == FFA_MEM_DONATE_SMC32) ?
+						FFA_DATA_ACCESS_NOT_SPECIFIED :
+						FFA_DATA_ACCESS_RW;
+
+	remaining_constituent_count = ffa_memory_region_init(
+		memory_region, memory_region_max_size, sender, receiver, constituents,
+		constituents_count, 0, 0, data_access,
+		FFA_INSTRUCTION_ACCESS_NOT_SPECIFIED,
+		FFA_MEMORY_NORMAL_MEM, FFA_MEMORY_CACHE_WRITE_BACK,
+		FFA_MEMORY_INNER_SHAREABLE, &total_length, &fragment_length
+	);
+
+	/*
+	 * For simplicity of the test, and at least for the time being,
+	 * the following condition needs to be true.
+	 */
+	if (remaining_constituent_count != 0U) {
+		ERROR("Remaining constituent should be 0\n");
+		return FFA_MEMORY_HANDLE_INVALID;
+	}
+
+	return memory_send(memory_region, mem_func, fragment_length,
+			       total_length);
+}
diff --git a/tftf/tests/runtime_services/secure_service/test_ffa_direct_messaging.c b/tftf/tests/runtime_services/secure_service/test_ffa_direct_messaging.c
index 1295442..0a722e4 100644
--- a/tftf/tests/runtime_services/secure_service/test_ffa_direct_messaging.c
+++ b/tftf/tests/runtime_services/secure_service/test_ffa_direct_messaging.c
@@ -11,6 +11,8 @@
 #include <cactus_test_cmds.h>
 #include <ffa_endpoints.h>
 #include <ffa_svc.h>
+#include <lib/events.h>
+#include <lib/power_management.h>
 #include <platform.h>
 #include <test_helpers.h>
 
@@ -18,37 +20,31 @@
 #define ECHO_VAL2 U(0xb0b0b0b0)
 #define ECHO_VAL3 U(0xc0c0c0c0)
 
-#define DIRECT_MSG_TEST_PATTERN1	(0xaaaa0000)
-#define DIRECT_MSG_TEST_PATTERN2	(0xbbbb0000)
-#define DIRECT_MSG_TEST_PATTERN3	(0xcccc0000)
-
 static const struct ffa_uuid expected_sp_uuids[] = {
 		{PRIMARY_UUID}, {SECONDARY_UUID}, {TERTIARY_UUID}
 	};
 
-static test_result_t send_receive_direct_msg(unsigned int sp_id,
-					     unsigned int test_pattern)
-{
-	smc_ret_values ret_values;
 
-	/* Send a message to SP through direct messaging */
-	ret_values = ffa_msg_send_direct_req(HYP_ID, sp_id, test_pattern);
+static event_t cpu_booted[PLATFORM_CORE_COUNT];
+
+static test_result_t send_cactus_echo_cmd(ffa_vm_id_t sender,
+					  ffa_vm_id_t dest,
+					  uint64_t value)
+{
+	smc_ret_values ret;
+	ret = cactus_echo_send_cmd(sender, dest, value);
 
 	/*
 	 * Return responses may be FFA_MSG_SEND_DIRECT_RESP or FFA_INTERRUPT,
 	 * but only expect the former. Expect SMC32 convention from SP.
 	 */
-	if (ffa_func_id(ret_values) != FFA_MSG_SEND_DIRECT_RESP_SMC32) {
-		tftf_testcase_printf("ffa_msg_send_direct_req returned %x\n",
-				     ffa_func_id(ret_values));
+	if (!is_ffa_direct_response(ret)) {
 		return TEST_RESULT_FAIL;
 	}
 
-	/*
-	 * Message loop in SP returns initial message with the running VM id
-	 * into the lower 16 bits of initial message.
-	 */
-	if (ret_values.ret3 != (test_pattern | sp_id)) {
+	if (cactus_get_response(ret) != CACTUS_SUCCESS ||
+	    cactus_echo_get_val(ret) != value) {
+		ERROR("Echo Failed!\n");
 		return TEST_RESULT_FAIL;
 	}
 
@@ -67,7 +63,7 @@
 	/**********************************************************************
 	 * Send a message to SP1 through direct messaging
 	 **********************************************************************/
-	result = send_receive_direct_msg(SP_ID(1), DIRECT_MSG_TEST_PATTERN1);
+	result = send_cactus_echo_cmd(HYP_ID, SP_ID(1), ECHO_VAL1);
 	if (result != TEST_RESULT_SUCCESS) {
 		return result;
 	}
@@ -75,7 +71,7 @@
 	/**********************************************************************
 	 * Send a message to SP2 through direct messaging
 	 **********************************************************************/
-	result = send_receive_direct_msg(SP_ID(2), DIRECT_MSG_TEST_PATTERN2);
+	result = send_cactus_echo_cmd(HYP_ID, SP_ID(2), ECHO_VAL2);
 	if (result != TEST_RESULT_SUCCESS) {
 		return result;
 	}
@@ -83,7 +79,7 @@
 	/**********************************************************************
 	 * Send a message to SP1 through direct messaging
 	 **********************************************************************/
-	result = send_receive_direct_msg(SP_ID(1), DIRECT_MSG_TEST_PATTERN3);
+	result = send_cactus_echo_cmd(HYP_ID, SP_ID(1), ECHO_VAL3);
 
 	return result;
 }
@@ -105,9 +101,7 @@
 
 	ret = cactus_req_echo_send_cmd(sender, dest, echo_dest, value);
 
-	if (ffa_func_id(ret) != FFA_MSG_SEND_DIRECT_RESP_SMC32) {
-		ERROR("Failed to send message. error: %x\n",
-		      ffa_error_code(ret));
+	if (!is_ffa_direct_response(ret)) {
 		return TEST_RESULT_FAIL;
 	}
 
@@ -157,9 +151,7 @@
 
 	ret = cactus_req_deadlock_send_cmd(HYP_ID, SP_ID(1), SP_ID(2), SP_ID(3));
 
-	if (ffa_func_id(ret) != FFA_MSG_SEND_DIRECT_RESP_SMC32) {
-		ERROR("Failed to send message. error: %x\n",
-		      ffa_error_code(ret));
+	if (is_ffa_direct_response(ret) == false) {
 		return TEST_RESULT_FAIL;
 	}
 
@@ -169,3 +161,142 @@
 
 	return TEST_RESULT_SUCCESS;
 }
+
+/**
+ * Handler that is passed during tftf_cpu_on to individual CPU cores.
+ * Runs a specific core and send a direct message request.
+ * Expects core_pos | SP_ID as a response.
+ */
+static test_result_t cpu_on_handler(void)
+{
+	unsigned int mpid = read_mpidr_el1() & MPID_MASK;
+	unsigned int core_pos = platform_get_core_pos(mpid);
+	test_result_t ret = TEST_RESULT_SUCCESS;
+	smc_ret_values ffa_ret;
+
+	/*
+	 * Send a direct message request to SP1 (MP SP) from current physical
+	 * CPU. Notice SP1 ECs are already woken as a result of the PSCI_CPU_ON
+	 * invocation so they already reached the message loop.
+	 * The SPMC uses the MP pinned context corresponding to the physical
+	 * CPU emitting the request.
+	 */
+	ret = send_cactus_echo_cmd(HYP_ID, SP_ID(1), ECHO_VAL1);
+	if (ret != TEST_RESULT_SUCCESS) {
+		goto out;
+	}
+
+	/*
+	 * Secure Partitions beyond the first SP only have their first
+	 * EC (or vCPU0) woken up at boot time by the SPMC.
+	 * Other ECs need one round of ffa_run to reach the message loop.
+	 */
+	ffa_ret = ffa_run(SP_ID(2), core_pos);
+	if (ffa_func_id(ffa_ret) != FFA_MSG_WAIT) {
+		ERROR("Failed to run SP%x on core %u\n", SP_ID(2),
+				core_pos);
+		ret = TEST_RESULT_FAIL;
+		goto out;
+	}
+
+	/*
+	 * Send a direct message request to SP2 (MP SP) from current physical
+	 * CPU. The SPMC uses the MP pinned context corresponding to the
+	 * physical CPU emitting the request.
+	 */
+	ret = send_cactus_echo_cmd(HYP_ID, SP_ID(2), ECHO_VAL2);
+	if (ret != TEST_RESULT_SUCCESS) {
+		goto out;
+	}
+
+	/*
+	 * Send a direct message request to SP3 (UP SP) from current physical CPU.
+	 * The SPMC uses the single vCPU migrated to the new physical core.
+	 * The single SP vCPU may receive requests from multiple physical CPUs.
+	 * Thus it is possible one message is being processed on one core while
+	 * another (or multiple) cores attempt sending a new direct message
+	 * request. In such case the cores attempting the new request receive
+	 * a busy response from the SPMC. To handle this case a retry loop is
+	 * implemented permitting some fairness.
+	 */
+	uint32_t trial_loop = 5U;
+	while (trial_loop--) {
+		ffa_ret = cactus_echo_send_cmd(HYP_ID, SP_ID(3), ECHO_VAL3);
+		if ((ffa_func_id(ffa_ret) == FFA_ERROR) &&
+		    (ffa_error_code(ffa_ret) == FFA_ERROR_BUSY)) {
+			VERBOSE("%s(%u) trial %u\n", __func__, core_pos, trial_loop);
+			waitms(1);
+			continue;
+		}
+
+		if (is_ffa_direct_response(ffa_ret) == true) {
+			if (cactus_get_response(ffa_ret) != CACTUS_SUCCESS ||
+				cactus_echo_get_val(ffa_ret) != ECHO_VAL3) {
+				ERROR("Echo Failed!\n");
+				ret = TEST_RESULT_FAIL;
+			}
+
+			goto out;
+		}
+	}
+
+	ret = TEST_RESULT_FAIL;
+
+out:
+	/* Tell the lead CPU that the calling CPU has completed the test */
+	tftf_send_event(&cpu_booted[core_pos]);
+
+	return ret;
+}
+
+/**
+ * Test direct messaging in multicore setup. Runs SPs on all the cores and sends
+ * direct messages to SPs.
+ */
+test_result_t test_ffa_secondary_core_direct_msg(void)
+{
+	unsigned int lead_mpid = read_mpidr_el1() & MPID_MASK;
+	unsigned int core_pos, cpu_node, mpidr;
+	int32_t ret;
+
+	/**********************************************************************
+	 * Check SPMC has ffa_version and expected FFA endpoints are deployed.
+	 **********************************************************************/
+	CHECK_SPMC_TESTING_SETUP(1, 0, expected_sp_uuids);
+
+	for (unsigned int i = 0U; i < PLATFORM_CORE_COUNT; i++) {
+		tftf_init_event(&cpu_booted[i]);
+	}
+
+	for_each_cpu(cpu_node) {
+		mpidr = tftf_get_mpidr_from_node(cpu_node);
+		if (mpidr == lead_mpid) {
+			continue;
+		}
+
+		ret = tftf_cpu_on(mpidr, (uintptr_t)cpu_on_handler, 0U);
+		if (ret != 0) {
+			ERROR("tftf_cpu_on mpidr 0x%x returns %d\n", mpidr, ret);
+		}
+	}
+
+	VERBOSE("Waiting secondary CPUs to turn off ...\n");
+
+	for_each_cpu(cpu_node) {
+		mpidr = tftf_get_mpidr_from_node(cpu_node);
+		if (mpidr == lead_mpid) {
+			continue;
+		}
+
+		core_pos = platform_get_core_pos(mpidr);
+		tftf_wait_for_event(&cpu_booted[core_pos]);
+	}
+
+	VERBOSE("Done exiting.\n");
+
+	/**********************************************************************
+	 * All tests passed.
+	 **********************************************************************/
+
+	return TEST_RESULT_SUCCESS;
+}
diff --git a/tftf/tests/runtime_services/secure_service/test_ffa_memory_sharing.c b/tftf/tests/runtime_services/secure_service/test_ffa_memory_sharing.c
index 05a8b59..f126c57 100644
--- a/tftf/tests/runtime_services/secure_service/test_ffa_memory_sharing.c
+++ b/tftf/tests/runtime_services/secure_service/test_ffa_memory_sharing.c
@@ -10,6 +10,7 @@
 #include <ffa_endpoints.h>
 #include <test_helpers.h>
 #include <tftf_lib.h>
+#include <spm_common.h>
 #include <xlat_tables_defs.h>
 
 #define MAILBOX_SIZE PAGE_SIZE
@@ -63,7 +64,7 @@
 	const uint32_t constituents_count = sizeof(constituents) /
 			sizeof(struct ffa_memory_region_constituent);
 
-	handle = ffa_memory_init_and_send((struct ffa_memory_region *)mb.send,
+	handle = memory_init_and_send((struct ffa_memory_region *)mb.send,
 					MAILBOX_SIZE, SENDER, RECEIVER,
 					constituents, constituents_count,
 					mem_func);
@@ -79,9 +80,7 @@
 
 	ret = cactus_mem_send_cmd(SENDER, RECEIVER, mem_func, handle);
 
-	if (ffa_func_id(ret) != FFA_MSG_SEND_DIRECT_RESP_SMC32) {
-		ERROR("Failed to send message. error: %x\n",
-		      ffa_error_code(ret));
+	if (!is_ffa_direct_response(ret)) {
 		return TEST_RESULT_FAIL;
 	}
 
@@ -103,7 +102,7 @@
 		(void)ptr;
 
 	if (mem_func != FFA_MEM_DONATE_SMC32 &&
-	    ffa_mem_reclaim(handle, 0).ret0 == FFA_ERROR) {
+	    is_ffa_call_error(ffa_mem_reclaim(handle, 0))) {
 			tftf_testcase_printf("Couldn't reclaim memory\n");
 			return TEST_RESULT_FAIL;
 	}
@@ -144,9 +143,7 @@
 	ret = cactus_req_mem_send_send_cmd(HYP_ID, sender_sp, mem_func,
 					   receiver_sp);
 
-	if (ffa_func_id(ret) != FFA_MSG_SEND_DIRECT_RESP_SMC32) {
-		ERROR("Failed to send message. error: %x\n",
-		      ffa_error_code(ret));
+	if (!is_ffa_direct_response(ret)) {
 		return TEST_RESULT_FAIL;
 	}
 
diff --git a/tftf/tests/runtime_services/secure_service/test_spm_cpu_features.c b/tftf/tests/runtime_services/secure_service/test_spm_cpu_features.c
index c6470c1..f57fa24 100644
--- a/tftf/tests/runtime_services/secure_service/test_spm_cpu_features.c
+++ b/tftf/tests/runtime_services/secure_service/test_spm_cpu_features.c
@@ -55,9 +55,7 @@
 
 	smc_ret_values ret = cactus_req_simd_fill_send_cmd(SENDER, RECEIVER);
 
-	if (ffa_func_id(ret) != FFA_MSG_SEND_DIRECT_RESP_SMC32) {
-		ERROR("Failed to send message. error: %x\n",
-		      ffa_error_code(ret));
+	if (!is_ffa_direct_response(ret)) {
 		return TEST_RESULT_FAIL;
 	}
 
diff --git a/tftf/tests/tests-spm.xml b/tftf/tests/tests-spm.xml
index 5e80988..ee34292 100644
--- a/tftf/tests/tests-spm.xml
+++ b/tftf/tests/tests-spm.xml
@@ -44,6 +44,12 @@
 
   </testsuite>
 
+ <testsuite name="FF-A Power management"
+             description="Test FF-A power management" >
+    <testcase name="FF-A SP hotplug"
+              function="test_ffa_secondary_core_direct_msg" />
+  </testsuite>
+
   <testsuite name="FF-A Memory Sharing"
              description="Test FF-A Memory Sharing ABIs" >
      <testcase name="Lend Memory to Secure World"