test(ff-a): test secondary core boot

Secure partitions register their secondary entrypoint at
initialization.
Added test to validate secondary core bring up:
- The primary VM in the ffa_secure_partitions test setup, starts the
secondary cores.
- Each secondary EC  is resumed from the first partition, the respective
pinned core's stack is setup, the mmu and caches are enabled, and
execution gets to the message loop (function `test_main_sp`).
- In the primary VM's execution context, it issues a echo request at
every core, to validate they are up and running.

Change-Id: I3a7e9f5fd8eb54181ed0d1af2becaed4268fa0fb
Signed-off-by: Olivier Deprez <olivier.deprez@arm.com>
Signed-off-by: J-Alves <joao.alves@arm.com>
diff --git a/inc/vmapi/hf/call.h b/inc/vmapi/hf/call.h
index 44aaf39..5b6ac0d 100644
--- a/inc/vmapi/hf/call.h
+++ b/inc/vmapi/hf/call.h
@@ -63,6 +63,17 @@
 }
 
 /**
+ * DEN0077A FF-A v1.1 Beta0 section 18.3.2.1
+ * Registers vCPU secondary entry point for the caller VM.
+ * Called from secure virtual FF-A instance.
+ */
+static inline struct ffa_value ffa_secondary_ep_register(uintptr_t address)
+{
+	return ffa_call((struct ffa_value){.func = FFA_SECONDARY_EP_REGISTER_64,
+					   .arg1 = address});
+}
+
+/**
  * Returns the VM's own ID.
  */
 static inline ffa_vm_id_t hf_vm_get_id(void)
diff --git a/src/arch/aarch64/hftest/BUILD.gn b/src/arch/aarch64/hftest/BUILD.gn
index cd25e17..f569e63 100644
--- a/src/arch/aarch64/hftest/BUILD.gn
+++ b/src/arch/aarch64/hftest/BUILD.gn
@@ -13,6 +13,12 @@
   ]
 }
 
+source_set("sp_secondary_entry") {
+  sources = [
+    "sp_secondary_entry.S",
+  ]
+}
+
 # Implements image_entry for a simple EL0 partition.
 source_set("el0_entry") {
   sources = [
diff --git a/src/arch/aarch64/hftest/sp_secondary_entry.S b/src/arch/aarch64/hftest/sp_secondary_entry.S
new file mode 100644
index 0000000..b54feae
--- /dev/null
+++ b/src/arch/aarch64/hftest/sp_secondary_entry.S
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2021 The Hafnium Authors.
+ *
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file or at
+ * https://opensource.org/licenses/BSD-3-Clause.
+ */
+
+.section .init.image_entry, "ax"
+.global secondary_ep_entry
+secondary_ep_entry:
+	/*
+	 * Prepare the stack for core.
+	 * The mpdir_el1 register contains the linear ID.
+	 */
+	mrs x29, mpidr_el1
+	ubfx x29, x29, #0, #30
+	mov x27, #4096
+	mul x27, x29, x27
+	adrp x30, kstack
+	add sp, x30, x27
+
+	/* Enable MMU and cache. */
+	bl hftest_mm_vcpu_init
+
+	/* Branch to message loop hinting a secondary vCPU. */
+	mov x0, xzr
+	b test_main_sp
diff --git a/test/hftest/BUILD.gn b/test/hftest/BUILD.gn
index 15c214a..d9a342c 100644
--- a/test/hftest/BUILD.gn
+++ b/test/hftest/BUILD.gn
@@ -75,6 +75,7 @@
     "//src/arch/${plat_arch}:entry",
     "//src/arch/${plat_arch}/hftest:entry",
     "//src/arch/${plat_arch}/hftest:power_mgmt",
+    "//src/arch/${plat_arch}/hftest:sp_secondary_entry",
     "//vmlib/${plat_arch}:call",
   ]
 }
diff --git a/test/hftest/secure_service.c b/test/hftest/secure_service.c
index 3a7e246..cff2f00 100644
--- a/test/hftest/secure_service.c
+++ b/test/hftest/secure_service.c
@@ -18,12 +18,15 @@
 #include "test/abort.h"
 #include "test/hftest.h"
 
-alignas(4096) uint8_t kstack[4096];
+alignas(4096) uint8_t kstack[MAX_CPUS][4096];
 
-void test_main_sp(void);
+void test_main_sp(bool);
 
 noreturn void kmain(void)
 {
+	extern void secondary_ep_entry(void);
+	struct ffa_value res;
+
 	/*
 	 * Initialize the stage-1 MMU and identity-map the entire address space.
 	 */
@@ -33,7 +36,11 @@
 		abort();
 	}
 
-	test_main_sp();
+	/* Register entry point for secondary vCPUs. */
+	res = ffa_secondary_ep_register((uintptr_t)secondary_ep_entry);
+	EXPECT_EQ(res.func, FFA_SUCCESS_32);
+
+	test_main_sp(true);
 
 	/* Do not expect to get to this point, so abort. */
 	abort();
diff --git a/test/vmapi/ffa_secure_partitions/BUILD.gn b/test/vmapi/ffa_secure_partitions/BUILD.gn
index e040c17..d80a973 100644
--- a/test/vmapi/ffa_secure_partitions/BUILD.gn
+++ b/test/vmapi/ffa_secure_partitions/BUILD.gn
@@ -15,10 +15,12 @@
   sources = [
     "dir_msg.c",
     "notifications.c",
+    "power_mgt.c",
     "setup_and_discovery.c",
   ]
 
   deps = [
+    "//src/arch/${plat_arch}/hftest:power_mgmt",
     "//test/hftest:hftest_primary_vm",
     "//test/vmapi/common:common",
   ]
diff --git a/test/vmapi/ffa_secure_partitions/power_mgt.c b/test/vmapi/ffa_secure_partitions/power_mgt.c
new file mode 100644
index 0000000..35d5e4f
--- /dev/null
+++ b/test/vmapi/ffa_secure_partitions/power_mgt.c
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2021 The Hafnium Authors.
+ *
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file or at
+ * https://opensource.org/licenses/BSD-3-Clause.
+ */
+
+#include "hf/arch/vm/power_mgmt.h"
+
+#include "hf/dlog.h"
+#include "hf/ffa.h"
+#include "hf/spinlock.h"
+
+#include "vmapi/hf/call.h"
+
+#include "partition_services.h"
+#include "test/hftest.h"
+#include "test/vmapi/ffa.h"
+
+alignas(4096) static uint8_t secondary_ec_stack[MAX_CPUS - 1][4096];
+
+/**
+ * Releases the lock passed in.
+ */
+static void cpu_entry_echo(uintptr_t arg)
+{
+	ffa_vm_id_t own_id = hf_vm_get_id();
+	// NOLINTNEXTLINE(performance-no-int-to-ptr)
+	struct spinlock *lock = (struct spinlock *)arg;
+	const uint32_t msg[] = {SP_ECHO_CMD, 0x1, 0x2, 0x3, 0x4};
+	const ffa_vm_id_t receiver_id = HF_OTHER_WORLD_ID + 1;
+	struct ffa_value res;
+
+	res = sp_echo_cmd_send(own_id, receiver_id, msg[0], msg[1], msg[2],
+			       msg[3]);
+
+	EXPECT_EQ(res.func, FFA_MSG_SEND_DIRECT_RESP_32);
+	EXPECT_EQ(res.arg4, msg[0]);
+	EXPECT_EQ(res.arg5, msg[1]);
+	EXPECT_EQ(res.arg6, msg[2]);
+	EXPECT_EQ(res.arg7, msg[3]);
+
+	sl_unlock(lock);
+
+	arch_cpu_stop();
+}
+
+TEST(ffa_power_mgt, cpu_start)
+{
+	struct spinlock lock = SPINLOCK_INIT;
+
+	/* Start secondary EC while holding lock. */
+	sl_lock(&lock);
+
+	for (uint32_t i = 0; i < MAX_CPUS - 1; i++) {
+		dlog_verbose("Booting CPU %u\n", i + 1);
+
+		EXPECT_EQ(hftest_cpu_start(hftest_get_cpu_id(i + 1),
+					   secondary_ec_stack[i],
+					   sizeof(secondary_ec_stack[0]),
+					   cpu_entry_echo, (uintptr_t)&lock),
+			  true);
+
+		/* Wait for CPU to release the lock. */
+		sl_lock(&lock);
+
+		dlog_verbose("Done with CPU %u\n", i + 1);
+	}
+}
diff --git a/test/vmapi/ffa_secure_partitions/services/message_loop.c b/test/vmapi/ffa_secure_partitions/services/message_loop.c
index 8c50432..d7136d5 100644
--- a/test/vmapi/ffa_secure_partitions/services/message_loop.c
+++ b/test/vmapi/ffa_secure_partitions/services/message_loop.c
@@ -16,13 +16,18 @@
  * Message loop to add tests to be controlled by the control partition(depends
  * on the test set-up).
  */
-noreturn void test_main_sp(void)
+noreturn void test_main_sp(bool is_boot_vcpu)
 {
 	struct ffa_value res = ffa_msg_wait();
 
 	while (1) {
 		EXPECT_EQ(res.func, FFA_MSG_SEND_DIRECT_REQ_32);
 
+		if (is_boot_vcpu) {
+			/* TODO: can only print from boot vCPU. */
+			HFTEST_LOG("Received direct message request");
+		}
+
 		switch (res.arg3) {
 		case SP_ECHO_CMD:
 			res = sp_echo_cmd(ffa_sender(res), res.arg3, res.arg4,
@@ -50,7 +55,8 @@
 		default:
 			HFTEST_LOG_FAILURE();
 			HFTEST_LOG(HFTEST_LOG_INDENT
-				   "0x%x is not a valid command id\n");
+				   "0x%x is not a valid command id\n",
+				   res.arg3);
 			abort();
 		}
 	}