test: disallow migrating a blocked SP to another CPU
This patch adds a test to make sure the vCPU of an UP SP cannot be
migrated from current CPU to a different physical CPU while the vCPU
is in BLOCKED state as part of an SP call chain.
This patch also adds a helper function to check if the receiver
endpoint is an UP SP. This helper is used as a precondition for the
newly introduced test.
Signed-off-by: Madhukar Pappireddy <madhukar.pappireddy@arm.com>
Change-Id: I018c152737dbd9abae7385e8858a8d231d5e82e8
diff --git a/src/arch/aarch64/inc/hf/arch/vm/delay.h b/src/arch/aarch64/inc/hf/arch/vm/delay.h
new file mode 100644
index 0000000..b45564f
--- /dev/null
+++ b/src/arch/aarch64/inc/hf/arch/vm/delay.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2023 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/barriers.h"
+
+#include "msr.h"
+
+static inline uint64_t syscounter_read(void)
+{
+ isb();
+ return read_msr(cntvct_el0);
+}
+
+static inline void waitus(uint64_t us)
+{
+ uint64_t start_count_val = syscounter_read();
+ uint64_t wait_cycles = (us * read_msr(cntfrq_el0)) / 1000000;
+
+ while ((syscounter_read() - start_count_val) < wait_cycles) {
+ /* Busy wait... */
+ }
+}
+
+static inline void waitms(uint64_t ms)
+{
+ while (ms > 0) {
+ waitus(1000);
+ ms--;
+ }
+}
diff --git a/test/vmapi/ffa_secure_partitions/dir_msg.c b/test/vmapi/ffa_secure_partitions/dir_msg.c
index b4325c8..a106c8e 100644
--- a/test/vmapi/ffa_secure_partitions/dir_msg.c
+++ b/test/vmapi/ffa_secure_partitions/dir_msg.c
@@ -6,15 +6,29 @@
* https://opensource.org/licenses/BSD-3-Clause.
*/
+#include "hf/arch/vm/delay.h"
+#include "hf/arch/vm/power_mgmt.h"
+
#include "hf/dlog.h"
#include "hf/ffa.h"
+#include "hf/mm.h"
#include "vmapi/hf/call.h"
+#include "ffa_secure_partitions.h"
#include "partition_services.h"
#include "test/hftest.h"
#include "test/vmapi/ffa.h"
+#define SP_SLEEP_LONG 2000U
+
+alignas(4096) static uint8_t secondary_ec_stack[MAX_CPUS - 1][PAGE_SIZE];
+
+struct secondary_cpu_entry_args {
+ ffa_vm_id_t receiver_id;
+ ffa_vcpu_count_t vcpu_count;
+};
+
/**
* Communicates with partition via direct messaging to validate functioning of
* direct request/response interfaces.
@@ -86,3 +100,84 @@
EXPECT_EQ(res.func, FFA_MSG_SEND_DIRECT_RESP_32);
}
+
+static void migrate_busy_up_sp(uintptr_t arg)
+{
+ ffa_vm_id_t own_id = hf_vm_get_id();
+ struct ffa_value res;
+ const uint32_t msg[] = {SP_ECHO_CMD, 0x1, 0x2, 0x3, 0x4};
+ struct secondary_cpu_entry_args *args =
+ // NOLINTNEXTLINE(performance-no-int-to-ptr)
+ (struct secondary_cpu_entry_args *)arg;
+
+ /*
+ * Wait till the primary VM on boot CPU has established the
+ * call chain with receiver SP.
+ */
+ waitms(200);
+
+ /*
+ * A direct request message cannot be serviced by the receiver SP
+ * since it is in a BLOCKED state on a boot CPU.
+ */
+ res = sp_echo_cmd_send(own_id, args->receiver_id, msg[0], msg[1],
+ msg[2], msg[3]);
+
+ EXPECT_EQ(ffa_func_id(res), FFA_ERROR_32);
+
+ /*
+ * An attempt to migrate the UP SP from boot CPU to current CPU using
+ * FFA_RUN interface should fail.
+ */
+ res = ffa_run(args->receiver_id, 0);
+
+ EXPECT_EQ(ffa_func_id(res), FFA_ERROR_32);
+
+ arch_cpu_stop();
+}
+
+/**
+ * Test to make sure the vCPU of an UP SP cannot be migrated from current CPU
+ * to a different physical CPU while the vCPU is in BLOCKED state as part of an
+ * SP call chain.
+ */
+TEST_PRECONDITION(ffa_call_chain, disallow_migration_blocked_sp,
+ service2_is_up_sp)
+{
+ struct ffa_value res;
+ ffa_vm_id_t own_id = hf_vm_get_id();
+ struct secondary_cpu_entry_args args;
+ struct mailbox_buffers mb = set_up_mailbox();
+ struct ffa_partition_info *receiver_info = service2(mb.recv);
+ struct ffa_partition_info *companion_info = service1(mb.recv);
+ ffa_vm_id_t receiver_id = receiver_info->vm_id;
+ ffa_vm_id_t companion_id = companion_info->vm_id;
+
+ args.receiver_id = receiver_id;
+ args.vcpu_count = receiver_info->vcpu_count;
+
+ for (size_t i = 1; i < MAX_CPUS; i++) {
+ uintptr_t id;
+
+ id = hftest_get_cpu_id(i);
+ HFTEST_LOG("Booting CPU %u - %x", i, id);
+
+ EXPECT_EQ(
+ hftest_cpu_start(id, secondary_ec_stack[i - 1],
+ sizeof(secondary_ec_stack[0]),
+ migrate_busy_up_sp, (uintptr_t)&args),
+ true);
+
+ HFTEST_LOG("Done with CPU %u", i);
+ }
+
+ /*
+ * Send command to receiver SP to send command to companion SP to sleep
+ * there by putting receiver SP in BLOCKED state.
+ */
+ res = sp_fwd_sleep_cmd_send(own_id, receiver_id, companion_id,
+ SP_SLEEP_LONG, false);
+
+ EXPECT_EQ(res.func, FFA_MSG_SEND_DIRECT_RESP_32);
+ EXPECT_EQ(sp_resp(res), SP_SUCCESS);
+}
diff --git a/test/vmapi/ffa_secure_partitions/ffa_secure_partitions.c b/test/vmapi/ffa_secure_partitions/ffa_secure_partitions.c
index 9236c7e..adec178 100644
--- a/test/vmapi/ffa_secure_partitions/ffa_secure_partitions.c
+++ b/test/vmapi/ffa_secure_partitions/ffa_secure_partitions.c
@@ -11,3 +11,29 @@
SERVICE_PARTITION_INFO_GET(service1, SERVICE1)
SERVICE_PARTITION_INFO_GET(service2, SERVICE2)
SERVICE_PARTITION_INFO_GET(service3, SERVICE3)
+
+/**
+ * Helper to setup mailbox for precondition functions.
+ */
+static struct mailbox_buffers get_precondition_mailbox(void)
+{
+ static struct mailbox_buffers mb = {.recv = NULL, .send = NULL};
+
+ if (mb.send == NULL && mb.recv == NULL) {
+ mb = set_up_mailbox();
+ }
+
+ return mb;
+}
+
+/*
+ * The following is a precondition function, for the current system set-up.
+ * Check that service1 partition is an UP SP.
+ */
+bool service2_is_up_sp(void)
+{
+ struct mailbox_buffers mb = get_precondition_mailbox();
+ struct ffa_partition_info *service2_info = service2(mb.recv);
+
+ return (service2_info->vcpu_count == 1);
+}
diff --git a/test/vmapi/ffa_secure_partitions/inc/ffa_secure_partitions.h b/test/vmapi/ffa_secure_partitions/inc/ffa_secure_partitions.h
index 1611332..35c0110 100644
--- a/test/vmapi/ffa_secure_partitions/inc/ffa_secure_partitions.h
+++ b/test/vmapi/ffa_secure_partitions/inc/ffa_secure_partitions.h
@@ -52,3 +52,9 @@
struct ffa_partition_info* service1(void* recv);
struct ffa_partition_info* service2(void* recv);
struct ffa_partition_info* service3(void* recv);
+
+/* Precondition functions for this test setup. */
+bool service2_is_up_sp(void);
+
+uint64_t syscounter_read(void);
+void waitms(uint64_t ms);
diff --git a/test/vmapi/ffa_secure_partitions/secure_interrupts.c b/test/vmapi/ffa_secure_partitions/secure_interrupts.c
index d869f5b..319a9c5 100644
--- a/test/vmapi/ffa_secure_partitions/secure_interrupts.c
+++ b/test/vmapi/ffa_secure_partitions/secure_interrupts.c
@@ -6,44 +6,19 @@
* https://opensource.org/licenses/BSD-3-Clause.
*/
-#include "hf/arch/barriers.h"
#include "hf/arch/irq.h"
+#include "hf/arch/vm/delay.h"
#include "hf/arch/vm/interrupts_gicv3.h"
#include "hf/arch/vm/timer.h"
#include "ffa_secure_partitions.h"
#include "gicv3.h"
-#include "msr.h"
#include "partition_services.h"
#include "sp_helpers.h"
#define SP_SLEEP_TIME 400U
#define NS_SLEEP_TIME 200U
-static inline uint64_t syscounter_read(void)
-{
- isb();
- return read_msr(cntvct_el0);
-}
-
-static void waitus(uint64_t us)
-{
- uint64_t start_count_val = syscounter_read();
- uint64_t wait_cycles = (us * read_msr(cntfrq_el0)) / 1000000;
-
- while ((syscounter_read() - start_count_val) < wait_cycles) {
- /* Busy wait... */;
- }
-}
-
-static void waitms(uint64_t ms)
-{
- while (ms > 0) {
- waitus(1000);
- ms--;
- }
-}
-
static void configure_trusted_wdog_interrupt(ffa_vm_id_t source,
ffa_vm_id_t dest, bool enable)
{