test(ff-a): signaling per-vCPU notifications

Testing per-vCPU notifications, which are notifications whose vCPU is
specified by the sender. Each vCPU is targeted with a notification, as
such all CPUs are brought up, in each one of them TFTF sends a request
to the receiver Cactus SP to get its pending notifications.
Small changes to helper functions to include the flags argument to be
passed to notifications FF-A calls. Tests encompass SP to SP and NWd to
SP.

Signed-off-by: J-Alves <joao.alves@arm.com>
Change-Id: I86ba840b4c1d35cce95e2bc197293511db6783a1
diff --git a/tftf/tests/runtime_services/secure_service/test_ffa_notifications.c b/tftf/tests/runtime_services/secure_service/test_ffa_notifications.c
index a8856b5..25b1e0a 100644
--- a/tftf/tests/runtime_services/secure_service/test_ffa_notifications.c
+++ b/tftf/tests/runtime_services/secure_service/test_ffa_notifications.c
@@ -15,6 +15,23 @@
 #include <spm_common.h>
 #include <test_helpers.h>
 
+/**
+ * Defining variables to test the per-vCPU notifications.
+ * The conceived test follows the same logic, despite the sender receiver type
+ * of endpoint (VM or secure partition).
+ * Using global variables because these need to be accessed in the cpu on handler
+ * function 'request_notification_get_per_vcpu_on_handler'.
+ * In each specific test function, change 'per_vcpu_receiver' and
+ * 'per_vcpu_sender' have the logic work for:
+ * - NWd to SP;
+ * - SP to NWd;
+ * - SP to SP.
+ */
+static ffa_id_t per_vcpu_receiver;
+static ffa_id_t per_vcpu_sender;
+uint32_t per_vcpu_flags_get;
+static event_t per_vcpu_finished[PLATFORM_CORE_COUNT];
+
 static const struct ffa_uuid expected_sp_uuids[] = {
 		{PRIMARY_UUID}, {SECONDARY_UUID}, {TERTIARY_UUID}
 };
@@ -578,6 +595,7 @@
 	uint16_t ids[FFA_NOTIFICATIONS_INFO_GET_MAX_IDS] = {0};
 	uint32_t lists_count;
 	uint32_t lists_sizes[FFA_NOTIFICATIONS_INFO_GET_MAX_IDS] = {0};
+	const bool more_notif_pending = false;
 
 	CHECK_SPMC_TESTING_SETUP(1, 1, expected_sp_uuids);
 
@@ -593,7 +611,7 @@
 
 	if (!notifications_info_get(ids, lists_count, lists_sizes,
 				    FFA_NOTIFICATIONS_INFO_GET_MAX_IDS,
-				    false)) {
+				    more_notif_pending)) {
 		return TEST_RESULT_FAIL;
 	}
 
@@ -784,3 +802,177 @@
 
 	return TEST_RESULT_SUCCESS;
 }
+
+/**
+ * CPU_ON handler for testing per-vCPU notifications to SPs (either from VMs
+ * or from SPs). It requests the SP to retrieve its pending notifications
+ * within its current Execution Context. The SP shall obtain all per-vCPU
+ * targeted to the running vCPU.
+ */
+static test_result_t request_notification_get_per_vcpu_on_handler(void)
+{
+	unsigned int mpid = read_mpidr_el1() & MPID_MASK;
+	unsigned int core_pos = platform_get_core_pos(mpid);
+	smc_ret_values ret;
+	test_result_t result = TEST_RESULT_FAIL;
+
+	uint64_t exp_from_vm = 0;
+	uint64_t exp_from_sp = 0;
+
+	if (IS_SP_ID(per_vcpu_sender)) {
+		exp_from_sp = FFA_NOTIFICATION(core_pos);
+	} else {
+		exp_from_vm = FFA_NOTIFICATION(core_pos);
+	}
+
+	VERBOSE("Request get per-vCPU notification to %x, core: %u.\n",
+		 per_vcpu_receiver, core_pos);
+
+	/*
+	 * Secure Partitions secondary ECs need one round of ffa_run to reach
+	 * the message loop.
+	 */
+	if (per_vcpu_receiver != SP_ID(1)) {
+		ret = ffa_run(per_vcpu_receiver, core_pos);
+
+		if (ffa_func_id(ret) != FFA_MSG_WAIT) {
+			ERROR("Failed to run SP%x on core %u\n",
+			      per_vcpu_receiver, core_pos);
+			goto out;
+		}
+	}
+
+	/* Request to get notifications sent to the respective vCPU. */
+	if (!notification_get_and_validate(
+		per_vcpu_receiver, exp_from_sp, exp_from_vm, core_pos,
+		per_vcpu_flags_get)) {
+		goto out;
+	}
+
+	result = TEST_RESULT_SUCCESS;
+
+out:
+	/* Tell the lead CPU that the calling CPU has completed the test. */
+	tftf_send_event(&per_vcpu_finished[core_pos]);
+
+	return result;
+}
+
+/**
+ * Base function to test signaling of per-vCPU notifications.
+ * Test whole flow between two FF-A endpoints: binding, getting notification
+ * info, and getting pending notifications.
+ * Each vCPU will receive a notification whose ID is the same as the core
+ * position.
+ */
+static test_result_t base_test_per_vcpu_notifications(ffa_id_t sender,
+						      ffa_id_t receiver)
+{
+	CHECK_SPMC_TESTING_SETUP(1, 1, expected_sp_uuids);
+
+	/*
+	 * Manually set variables to validate what should be the return of to
+	 * FFA_NOTIFICATION_INFO_GET.
+	 */
+	uint16_t exp_ids[FFA_NOTIFICATIONS_INFO_GET_MAX_IDS] = {
+		receiver, 0, 1, 2,
+		receiver, 3, 4, 5,
+		receiver, 6, 7, 0,
+		0, 0, 0, 0,
+		0, 0, 0, 0,
+	};
+	uint32_t exp_lists_count = 3;
+	uint32_t exp_lists_sizes[FFA_NOTIFICATIONS_INFO_GET_MAX_IDS] = {
+		3, 3, 2, 0, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	};
+
+	const bool exp_more_notif_pending = false;
+	test_result_t result = TEST_RESULT_SUCCESS;
+	uint64_t notifications_to_unbind = 0;
+
+	per_vcpu_flags_get = IS_SP_ID(sender)
+				? FFA_NOTIFICATIONS_FLAG_BITMAP_SP
+				: FFA_NOTIFICATIONS_FLAG_BITMAP_VM;
+
+	/*
+	 * Prepare notifications bitmap to request Cactus to bind them as
+	 * per-vCPU.
+	 */
+	for (unsigned int i = 0; i < PLATFORM_CORE_COUNT; i++) {
+		notifications_to_unbind |= FFA_NOTIFICATION(i);
+
+		uint32_t flags = FFA_NOTIFICATIONS_FLAG_PER_VCPU |
+				 FFA_NOTIFICATIONS_FLAGS_VCPU_ID((uint16_t)i);
+
+		if (!notification_bind_and_set(sender,
+					       receiver,
+					       FFA_NOTIFICATION(i),
+					       flags)) {
+			return TEST_RESULT_FAIL;
+		}
+	}
+
+	/* Call FFA_NOTIFICATION_INFO_GET and validate return. */
+	if (!notifications_info_get(exp_ids, exp_lists_count, exp_lists_sizes,
+				    FFA_NOTIFICATIONS_INFO_GET_MAX_IDS,
+				    exp_more_notif_pending)) {
+		ERROR("Info Get Failed....\n");
+		result = TEST_RESULT_FAIL;
+		goto out;
+	}
+
+	/*
+	 * Request SP to get notifications in core 0, as this is not iterated
+	 * at the CPU ON handler.
+	 */
+	if (!notification_get_and_validate(
+		receiver,
+		IS_SP_ID(sender) ? FFA_NOTIFICATION(0) : 0,
+		!IS_SP_ID(sender) ? FFA_NOTIFICATION(0) : 0,
+		0,
+		per_vcpu_flags_get)) {
+		result = TEST_RESULT_FAIL;
+	}
+
+	/* Setting global variables to be accessed by the cpu_on handler. */
+	per_vcpu_receiver = receiver;
+	per_vcpu_sender = sender;
+
+	/*
+	 * Bring up all the cores, and request the receiver to get notifications
+	 * in each one of them.
+	 */
+	if (spm_run_multi_core_test(
+		(uintptr_t)request_notification_get_per_vcpu_on_handler,
+		per_vcpu_finished) != TEST_RESULT_SUCCESS) {
+		result = TEST_RESULT_FAIL;
+	}
+
+out:
+	/* As a clean-up, unbind notifications. */
+	if (!request_notification_unbind(receiver, receiver,
+					 sender,
+					 notifications_to_unbind,
+					 CACTUS_SUCCESS, 0)) {
+		result = TEST_RESULT_FAIL;
+	}
+
+	return result;
+}
+
+/**
+ * Test to validate a VM can signal a per-vCPU notification to an SP.
+ */
+test_result_t test_ffa_notifications_vm_signals_sp_per_vcpu(void)
+{
+	return base_test_per_vcpu_notifications(0, SP_ID(1));
+}
+
+/**
+ * Test to validate an SP can signal a per-vCPU notification to an SP.
+ */
+test_result_t test_ffa_notifications_sp_signals_sp_per_vcpu(void)
+{
+	return base_test_per_vcpu_notifications(SP_ID(1), SP_ID(2));
+}
diff --git a/tftf/tests/tests-spm.xml b/tftf/tests/tests-spm.xml
index 968bd16..1a0134d 100644
--- a/tftf/tests/tests-spm.xml
+++ b/tftf/tests/tests-spm.xml
@@ -130,6 +130,10 @@
                function="test_ffa_notifications_unbind_pending" />
      <testcase name="Notifications info get no data"
                function="test_ffa_notifications_info_get_none" />
+     <testcase name="Notifications VM signals SP per-vCPU"
+               function="test_ffa_notifications_vm_signals_sp_per_vcpu" />
+     <testcase name="Notifications SP signals SP per-vCPU"
+               function="test_ffa_notifications_sp_signals_sp_per_vcpu" />
   </testsuite>
 
 </testsuites>