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();
+}