test(ff-a): FFA_MSG_WAIT multicore RX buffer test

Add a test case in which multiple SP vcpus potentially interact with the
SP's RX buffer.

The test follows the following steps:
 * Send an indirect message to the SP with a constant value.
 * Use the FFA_MSG_WAIT ABI with the retain RX buffer flag in one SP
   vcpu. RX buffer should be retained by the SP.
 * The other vcpu attempts to read the RX buffer after the FFA_MSG_WAIT
   call. It should succeed and see the same constant value that was
   sent.

Signed-off-by: Kathleen Capella <kathleen.capella@arm.com>
Change-Id: Ieb49ec9062c9e6e0bff85be55e27300ef6f7aed8
diff --git a/test/vmapi/primary_with_secondaries/rx_ownership.c b/test/vmapi/primary_with_secondaries/rx_ownership.c
index 7b5607e..f12ace1 100644
--- a/test/vmapi/primary_with_secondaries/rx_ownership.c
+++ b/test/vmapi/primary_with_secondaries/rx_ownership.c
@@ -12,9 +12,22 @@
 
 #include "primary_with_secondary.h"
 #include "test/hftest.h"
+#include "test/semaphore.h"
 #include "test/vmapi/ffa.h"
 
 /**
+ * Structure defined for usage in tests with multiple cores.
+ * Used to pass arguments from primary to secondary core.
+ */
+struct secondary_cpu_entry_args {
+	ffa_id_t receiver_id;
+	ffa_vcpu_index_t vcpu_id;
+	ffa_vcpu_count_t receiver_vcpu_count;
+	struct mailbox_buffers mb;
+	struct semaphore sync;
+};
+
+/**
  * Test to verify that when an SP calls FFA_MSG_WAIT, it relinquishes the
  * ownership of its RX buffer back to the SPMC. Test sequence is as follows:
  * 	1. PVM runs SP via FFA_RUN.
@@ -92,3 +105,83 @@
 	ret = ffa_run(service1_info->vm_id, 0);
 	EXPECT_EQ(ret.func, FFA_YIELD_32);
 }
+
+static void cpu_entry_mp(uintptr_t arg)
+{
+	struct secondary_cpu_entry_args *args =
+		// NOLINTNEXTLINE(performance-no-int-to-ptr)
+		(struct secondary_cpu_entry_args *)arg;
+	ffa_vcpu_index_t service_vcpu_id;
+	struct ffa_value ret;
+	uint64_t msg = 0x123;
+
+	ASSERT_TRUE(args != NULL);
+
+	HFTEST_LOG("Within secondary core... %u", args->vcpu_id);
+	service_vcpu_id = (args->receiver_vcpu_count > 1) ? args->vcpu_id : 0;
+
+	SERVICE_SELECT_MP(args->receiver_id, "call_ffa_msg_wait_retain_rx",
+			  args->mb.send, service_vcpu_id);
+
+	ret = send_indirect_message(hf_vm_get_id(), args->receiver_id,
+				    args->mb.send, &msg, sizeof(msg), 0);
+	ASSERT_EQ(ret.func, FFA_SUCCESS_32);
+
+	ret = ffa_run(args->receiver_id, service_vcpu_id);
+	EXPECT_EQ(ret.func, FFA_MSG_WAIT_32);
+
+	/* Signal to primary core that test is complete.*/
+	HFTEST_LOG("Done with secondary core...");
+	semaphore_signal(&args->sync);
+}
+
+/**
+ * Test to show that one vCPU calling FFA_MSG_WAIT with retain buffer flag will
+ * not prevent another vCPU from reading the RX buffer.
+ *
+ *	1. SPMC sends indirect message intended to be read by SP vCPU 1 (msg =
+ * 0x123). RX buffer ownership is transferred from Producer (SPMC) to consumer
+ * (SP).
+ *	2. SP vCPU 0 calls FFA_MSG_WAIT with retain RX buffer flag set to 1 so
+ * the RX buffer ownership still belongs to the SP.
+ *	3. SP vCPU 1 reads buffer after FFA_MSG_WAIT call.
+ */
+TEST_PRECONDITION(rx_ownership, ffa_msg_wait_race_success, service1_is_not_vm)
+{
+	struct mailbox_buffers mb_mp = set_up_mailbox();
+	struct ffa_partition_info *service1_info = service1(mb_mp.recv);
+	const ffa_vcpu_index_t vcpu_id = 1;
+	struct secondary_cpu_entry_args args = {
+		.receiver_id = service1_info->vm_id,
+		.receiver_vcpu_count = service1_info->vcpu_count,
+		.vcpu_id = vcpu_id,
+		.mb = mb_mp,
+	};
+	struct ffa_value ret;
+
+	SERVICE_SELECT_MP(service1_info->vm_id, "read_rx_buffer", mb_mp.send,
+			  0);
+
+	/*
+	 * Initialize semaphore for synchronization purposes between primary and
+	 * secondary core.
+	 */
+	semaphore_init(&args.sync);
+
+	/* Start service on SP vCPU 1 */
+	HFTEST_LOG("Starting secondary core...");
+
+	ASSERT_TRUE(hftest_cpu_start(hftest_get_cpu_id(vcpu_id),
+				     hftest_get_secondary_ec_stack(vcpu_id),
+				     cpu_entry_mp, (uintptr_t)&args));
+
+	/* Wait for secondary core to return before finishing the test. */
+	semaphore_wait(&args.sync);
+	HFTEST_LOG("Returned from secondary core");
+
+	/* vCPU 0 on service1 reads RX buffer. */
+	ret = ffa_run(service1_info->vm_id, 0);
+	EXPECT_EQ(ret.func, FFA_YIELD_32);
+
+	HFTEST_LOG("Finished the test...");
+}
diff --git a/test/vmapi/primary_with_secondaries/services/rx_ownership.c b/test/vmapi/primary_with_secondaries/services/rx_ownership.c
index 8e55118..8065da2 100644
--- a/test/vmapi/primary_with_secondaries/services/rx_ownership.c
+++ b/test/vmapi/primary_with_secondaries/services/rx_ownership.c
@@ -13,8 +13,12 @@
 
 #include "primary_with_secondary.h"
 #include "test/hftest.h"
+#include "test/semaphore.h"
 #include "test/vmapi/ffa.h"
 
+/* Used to coordinate between multiple vCPUs in multicore test. */
+static struct semaphore ffa_msg_wait_called;
+
 TEST_SERVICE(test_ffa_msg_wait_release_buffer)
 {
 	struct ffa_value ret;
@@ -91,3 +95,31 @@
 
 	ffa_yield();
 }
+
+TEST_SERVICE(read_rx_buffer)
+{
+	void *recv_buf = SERVICE_RECV_BUFFER();
+	uint64_t msg;
+
+	/* Wait until FFA_MSG_WAIT has been called on other VCPU. */
+	semaphore_wait(&ffa_msg_wait_called);
+
+	/* Read RX buffer and verify expected message payload. */
+	receive_indirect_message((void *)&msg, sizeof(msg), recv_buf, NULL);
+	EXPECT_EQ(msg, 0x123);
+	ffa_yield();
+}
+
+TEST_SERVICE(call_ffa_msg_wait_retain_rx)
+{
+	struct ffa_value ret;
+	HFTEST_LOG("Call FFA_MSG_WAIT");
+
+	/* FFA_MSG_WAIT should retain buffer */
+	semaphore_init(&ffa_msg_wait_called);
+	semaphore_signal(&ffa_msg_wait_called);
+	ret = ffa_msg_wait_with_flags(FFA_MSG_WAIT_FLAG_RETAIN_RX);
+	EXPECT_EQ(ret.func, FFA_RUN_32);
+
+	ffa_yield();
+}