test(dir_msg): demo MP test with direct message
Validate direct messaging between secondary cores on sender and receiver
VMs. Add a test for FFA_MSG_SEND_DIRECT_REQ/RESP and for
FFA_MSG_SEND_DIRECT_REQ2/RESP2.
Add semaphore semantics to hftest to coordinate synchronizing between
test VM cores.
Change-Id: Ia2c1466fdcfc1704c6134d9caad7b694f137fee9
Signed-off-by: J-Alves <joao.alves@arm.com>
Signed-off-by: Kathleen Capella <kathleen.capella@arm.com>
diff --git a/test/hftest/power_mgmt.c b/test/hftest/power_mgmt.c
index 4ca3c52..afd1744 100644
--- a/test/hftest/power_mgmt.c
+++ b/test/hftest/power_mgmt.c
@@ -10,6 +10,7 @@
#include "hf/arch/mm.h"
+#include "hf/mm.h"
#include "hf/spinlock.h"
#include "test/hftest.h"
@@ -20,6 +21,12 @@
struct spinlock lock;
};
+/*
+ * Stack for secondary execution contexts.
+ * Used in tests for MP partitions where multicore functionality is tested.
+ */
+alignas(PAGE_SIZE) uint8_t secondary_ec_stack[MAX_CPUS - 1][PAGE_SIZE];
+
static noreturn void cpu_entry(uintptr_t arg)
{
/*
diff --git a/test/inc/test/semaphore.h b/test/inc/test/semaphore.h
new file mode 100644
index 0000000..accece8
--- /dev/null
+++ b/test/inc/test/semaphore.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2024 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.
+ */
+
+#pragma once
+
+/*
+ * Includes the arch-specific definition of 'struct spinlock' and
+ * implementations of:
+ * - SPINLOCK_INIT
+ * - sl_lock()
+ * - sl_unlock()
+ */
+#include "hf/arch/spinlock.h"
+
+/* TODO: For now, semaphore is a wrapper to spinlock.
+ * In the future, introduce counter within semaphore struct.
+ */
+struct semaphore {
+ struct spinlock lock;
+};
+
+static inline void semaphore_init(struct semaphore *a)
+{
+ sl_init(&a->lock);
+ sl_lock(&a->lock);
+}
+
+static inline void semaphore_wait(struct semaphore *a)
+{
+ sl_lock(&a->lock);
+}
+
+static inline void semaphore_signal(struct semaphore *a)
+{
+ sl_unlock(&a->lock);
+}
diff --git a/test/vmapi/primary_with_secondaries/dir_msg.c b/test/vmapi/primary_with_secondaries/dir_msg.c
index 92a0a9f..3a9d2e0 100644
--- a/test/vmapi/primary_with_secondaries/dir_msg.c
+++ b/test/vmapi/primary_with_secondaries/dir_msg.c
@@ -8,33 +8,42 @@
#include <stdint.h>
+#include "hf/arch/vm/power_mgmt.h"
+
#include "hf/ffa.h"
#include "vmapi/hf/call.h"
#include "primary_with_secondary.h"
#include "test/hftest.h"
+#include "test/hftest_impl.h"
+#include "test/semaphore.h"
#include "test/vmapi/ffa.h"
#define MAX_RESP_REGS (MAX_MSG_SIZE / sizeof(uint64_t))
/**
- * Send direct message, verify that sent info is echoed back.
+ * Structure defined for usage in tests with multiple cores.
+ * Used to pass arguments from primary to secondary core.
*/
-TEST(direct_message, ffa_send_direct_message_req_echo)
+struct echo_test_secondary_cpu_entry_args {
+ uint32_t req_func;
+ ffa_id_t receiver_id;
+ struct ffa_uuid receiver_uuid;
+ ffa_vcpu_count_t receiver_vcpu_count;
+ ffa_vcpu_index_t vcpu_id;
+ struct mailbox_buffers mb;
+ struct semaphore sync;
+};
+
+static void echo_test(ffa_id_t target_id)
{
const uint32_t msg[] = {0x00001111, 0x22223333, 0x44445555, 0x66667777,
0x88889999};
- struct mailbox_buffers mb = set_up_mailbox();
struct ffa_value res;
- struct ffa_partition_info *service1_info = service1(mb.recv);
- SERVICE_SELECT(service1_info->vm_id, "ffa_direct_message_resp_echo",
- mb.send);
- ffa_run(service1_info->vm_id, 0);
-
- res = ffa_msg_send_direct_req(HF_PRIMARY_VM_ID, service1_info->vm_id,
- msg[0], msg[1], msg[2], msg[3], msg[4]);
+ res = ffa_msg_send_direct_req(HF_PRIMARY_VM_ID, target_id, msg[0],
+ msg[1], msg[2], msg[3], msg[4]);
EXPECT_EQ(res.func, FFA_MSG_SEND_DIRECT_RESP_32);
@@ -45,6 +54,36 @@
EXPECT_EQ(res.arg7, msg[4]);
}
+static void echo_test_req2(ffa_id_t target_id, struct ffa_uuid target_uuid)
+{
+ const uint64_t msg[] = {0x00001111, 0x22223333, 0x44445555, 0x66667777,
+ 0x88889999, 0x01010101, 0x23232323, 0x45454545,
+ 0x67676767, 0x89898989, 0x11001100, 0x22332233,
+ 0x44554455, 0x66776677};
+
+ struct ffa_value res;
+ res = ffa_msg_send_direct_req2(HF_PRIMARY_VM_ID, target_id,
+ &target_uuid, (const uint64_t *)&msg,
+ ARRAY_SIZE(msg));
+
+ EXPECT_EQ(res.func, FFA_MSG_SEND_DIRECT_RESP2_64);
+
+ EXPECT_EQ(res.arg4, msg[0]);
+ EXPECT_EQ(res.arg5, msg[1]);
+ EXPECT_EQ(res.arg6, msg[2]);
+ EXPECT_EQ(res.arg7, msg[3]);
+ EXPECT_EQ(res.extended_val.arg8, msg[4]);
+ EXPECT_EQ(res.extended_val.arg9, msg[5]);
+ EXPECT_EQ(res.extended_val.arg10, msg[6]);
+ EXPECT_EQ(res.extended_val.arg11, msg[7]);
+ EXPECT_EQ(res.extended_val.arg12, msg[8]);
+ EXPECT_EQ(res.extended_val.arg13, msg[9]);
+ EXPECT_EQ(res.extended_val.arg14, msg[10]);
+ EXPECT_EQ(res.extended_val.arg15, msg[11]);
+ EXPECT_EQ(res.extended_val.arg16, msg[12]);
+ EXPECT_EQ(res.extended_val.arg17, msg[13]);
+}
+
/**
* Send direct message to an VM/SP. Expect it to yield its CPU cycles. Allocate
* cycles through FFA_RUN and verify that sent info is echoed back.
@@ -97,6 +136,22 @@
EXPECT_EQ(res.arg7, msg[4]);
}
+/*
+ * Send direct message, verify that sent info is echoed back.
+ */
+TEST(direct_message, ffa_send_direct_message_req_echo)
+{
+ struct mailbox_buffers mb = set_up_mailbox();
+ struct ffa_partition_info *service1_info = service1(mb.recv);
+
+ SERVICE_SELECT(service1_info->vm_id, "ffa_direct_message_resp_echo",
+ mb.send);
+
+ ffa_run(service1_info->vm_id, 0);
+
+ echo_test(service1_info->vm_id);
+}
+
/**
* Initiate direct message request between test SPs.
* If test services are VMs, test should be skipped.
@@ -323,39 +378,15 @@
*/
TEST(direct_message, ffa_send_direct_message_req2_echo)
{
- const uint64_t msg[] = {0x00001111, 0x22223333, 0x44445555, 0x66667777,
- 0x88889999, 0x01010101, 0x23232323, 0x45454545,
- 0x67676767, 0x89898989, 0x11001100, 0x22332233,
- 0x44554455, 0x66776677};
struct mailbox_buffers mb = set_up_mailbox();
- struct ffa_value res;
struct ffa_partition_info *service1_info = service1(mb.recv);
- struct ffa_uuid uuid = SERVICE1;
+ struct ffa_uuid target_uuid = SERVICE1;
SERVICE_SELECT(service1_info->vm_id,
"ffa_direct_message_req2_resp_echo", mb.send);
ffa_run(service1_info->vm_id, 0);
- res = ffa_msg_send_direct_req2(HF_PRIMARY_VM_ID, service1_info->vm_id,
- &uuid, (const uint64_t *)&msg,
- ARRAY_SIZE(msg));
-
- EXPECT_EQ(res.func, FFA_MSG_SEND_DIRECT_RESP2_64);
-
- EXPECT_EQ(res.arg4, msg[0]);
- EXPECT_EQ(res.arg5, msg[1]);
- EXPECT_EQ(res.arg6, msg[2]);
- EXPECT_EQ(res.arg7, msg[3]);
- EXPECT_EQ(res.extended_val.arg8, msg[4]);
- EXPECT_EQ(res.extended_val.arg9, msg[5]);
- EXPECT_EQ(res.extended_val.arg10, msg[6]);
- EXPECT_EQ(res.extended_val.arg11, msg[7]);
- EXPECT_EQ(res.extended_val.arg12, msg[8]);
- EXPECT_EQ(res.extended_val.arg13, msg[9]);
- EXPECT_EQ(res.extended_val.arg14, msg[10]);
- EXPECT_EQ(res.extended_val.arg15, msg[11]);
- EXPECT_EQ(res.extended_val.arg16, msg[12]);
- EXPECT_EQ(res.extended_val.arg17, msg[13]);
+ echo_test_req2(service1_info->vm_id, target_uuid);
}
/**
@@ -973,3 +1004,110 @@
ASSERT_EQ(ret.func, FFA_SUCCESS_32);
EXPECT_EQ(ffa_run(service1_info->vm_id, 0).func, FFA_YIELD_32);
}
+
+static void cpu_entry_echo_mp(uintptr_t arg)
+{
+ struct echo_test_secondary_cpu_entry_args *args =
+ // NOLINTNEXTLINE(performance-no-int-to-ptr)
+ (struct echo_test_secondary_cpu_entry_args *)arg;
+ ffa_vcpu_index_t service_vcpu_id;
+
+ ASSERT_TRUE(args != NULL);
+
+ HFTEST_LOG("Within secondary core... %u\n", args->vcpu_id);
+
+ service_vcpu_id = (args->receiver_vcpu_count > 1) ? args->vcpu_id : 0;
+
+ if (args->req_func == FFA_MSG_SEND_DIRECT_REQ_32) {
+ SERVICE_SELECT_MP(args->receiver_id,
+ "ffa_direct_message_resp_echo", args->mb.send,
+ service_vcpu_id);
+ ffa_run(args->receiver_id, service_vcpu_id);
+ echo_test(args->receiver_id);
+ } else {
+ SERVICE_SELECT_MP(args->receiver_id,
+ "ffa_direct_message_req2_resp_echo",
+ args->mb.send, service_vcpu_id);
+ ffa_run(args->receiver_id, service_vcpu_id);
+ echo_test_req2(args->receiver_id, args->receiver_uuid);
+ }
+
+ /* Signal to primary core that test is complete.*/
+ semaphore_signal(&args->sync);
+
+ HFTEST_LOG("Done with secondary core...\n");
+
+ arch_cpu_stop();
+}
+
+/**
+ * Test validating direct messaging via FFA_MSG_SEND_DIRECT_REQ/RESP
+ * between secondary cores.
+ */
+TEST_PRECONDITION(direct_message, echo_mp, 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 echo_test_secondary_cpu_entry_args args = {
+ .req_func = FFA_MSG_SEND_DIRECT_REQ_32,
+ .receiver_id = service1_info->vm_id,
+ .receiver_uuid = SERVICE1,
+ .receiver_vcpu_count = service1_info->vcpu_count,
+ .vcpu_id = vcpu_id,
+ .mb = mb_mp};
+
+ /*
+ * Initialize semaphore for synchronization purposes between primary and
+ * secondary core.
+ */
+ semaphore_init(&args.sync);
+
+ HFTEST_LOG("Starting secondary core...\n");
+
+ ASSERT_TRUE(hftest_cpu_start(hftest_get_cpu_id(vcpu_id),
+ secondary_ec_stack,
+ sizeof(secondary_ec_stack),
+ cpu_entry_echo_mp, (uintptr_t)&args));
+
+ /* Wait for secondary core to return before finishing the test. */
+ semaphore_wait(&args.sync);
+
+ HFTEST_LOG("Finished the test...\n");
+}
+
+/**
+ * Test validating direct messaging via FFA_MSG_SEND_DIRECT_REQ2/RESP2
+ * between secondary cores.
+ */
+TEST_PRECONDITION(direct_message, echo_mp_req2, 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 echo_test_secondary_cpu_entry_args args = {
+ .req_func = FFA_MSG_SEND_DIRECT_REQ2_64,
+ .receiver_id = service1_info->vm_id,
+ .receiver_uuid = SERVICE1,
+ .receiver_vcpu_count = service1_info->vcpu_count,
+ .vcpu_id = vcpu_id,
+ .mb = mb_mp};
+
+ /*
+ * Initialize semaphore for synchronization purposes between primary and
+ * secondary core.
+ */
+ semaphore_init(&args.sync);
+
+ HFTEST_LOG("Starting secondary core...\n");
+
+ ASSERT_TRUE(hftest_cpu_start(hftest_get_cpu_id(vcpu_id),
+ secondary_ec_stack,
+ sizeof(secondary_ec_stack),
+ cpu_entry_echo_mp, (uintptr_t)&args));
+
+ /* Wait for secondary core to return before finishing the test. */
+ semaphore_wait(&args.sync);
+
+ HFTEST_LOG("Finished the test...\n");
+}
diff --git a/test/vmapi/primary_with_secondaries/inc/primary_with_secondary.h b/test/vmapi/primary_with_secondaries/inc/primary_with_secondary.h
index c2fce04..563ebb2 100644
--- a/test/vmapi/primary_with_secondaries/inc/primary_with_secondary.h
+++ b/test/vmapi/primary_with_secondaries/inc/primary_with_secondary.h
@@ -8,6 +8,8 @@
#pragma once
+#include "hf/mm.h"
+
#include "test/vmapi/ffa.h"
/*
@@ -94,3 +96,5 @@
/* Helpers common to the setup. */
bool exception_received(struct ffa_value* run_res, const void* recv_buf);
+
+extern uint8_t secondary_ec_stack[MAX_CPUS - 1][PAGE_SIZE];