test: VM availability messaging tests
Add tests for sending VM availability messages from PVM to SP:
* If the SP is subscribed to the messages, the tracked state for the VM
should be updated according to table 18.9
* If the SP is not subscribed to the messages, the message should not be
forwarded by the SPMC.
* If the SP responds to a framework message with a non-framework
message, it should not be forwarded by the SPMC.
Change-Id: I062db22bf8862821de677f1b003d3d8f4c540bff
Signed-off-by: Karl Meakin <karl.meakin@arm.com>
diff --git a/inc/vmapi/hf/ffa.h b/inc/vmapi/hf/ffa.h
index 55941ea..f08ff36 100644
--- a/inc/vmapi/hf/ffa.h
+++ b/inc/vmapi/hf/ffa.h
@@ -808,6 +808,15 @@
((ffa_framework_msg_flags(args) & FFA_FRAMEWORK_MSG_BIT) != 0);
}
+/**
+ * Get the ID of the VM that has been created/destroyed from VM availability
+ * message
+ */
+static inline ffa_id_t ffa_vm_availability_message_vm_id(struct ffa_value args)
+{
+ return args.arg5 & 0xFFFF;
+}
+
/** Get the function ID from a framework message */
static inline uint32_t ffa_framework_msg_func(struct ffa_value args)
{
diff --git a/test/vmapi/ffa_secure_partitions/services/arch/aarch64/secure/el0/partition_manifest_service_sp_second.dts b/test/vmapi/ffa_secure_partitions/services/arch/aarch64/secure/el0/partition_manifest_service_sp_second.dts
index bd035a8..59eb777 100644
--- a/test/vmapi/ffa_secure_partitions/services/arch/aarch64/secure/el0/partition_manifest_service_sp_second.dts
+++ b/test/vmapi/ffa_secure_partitions/services/arch/aarch64/secure/el0/partition_manifest_service_sp_second.dts
@@ -22,6 +22,7 @@
entrypoint-offset = <0x2000>;
xlat-granule = <0>; /* 4KiB */
messaging-method = <0x607>; /* Support direct (both APIs) and indirect messaging. */
+ vm-availability-messages = <0x03>; /* Supports VM created and VM destroyed messages. */
ns-interrupts-action = <2>; /* NS interrupts are signaled */
boot-order = <4>;
notification-support; /* Receipt of notifications. */
diff --git a/test/vmapi/ffa_secure_partitions/services/arch/aarch64/secure/partition_manifest_service_sp_first.dts b/test/vmapi/ffa_secure_partitions/services/arch/aarch64/secure/partition_manifest_service_sp_first.dts
index a7a8735..ee98dc2 100644
--- a/test/vmapi/ffa_secure_partitions/services/arch/aarch64/secure/partition_manifest_service_sp_first.dts
+++ b/test/vmapi/ffa_secure_partitions/services/arch/aarch64/secure/partition_manifest_service_sp_first.dts
@@ -29,6 +29,7 @@
entrypoint-offset = <0x2000>;
xlat-granule = <0>; /* 4KiB */
messaging-method = <0x7>; /* Supports direct and indirect requests. */
+ vm-availability-messages = <0x03>; /* Supports VM created and VM destroyed messages. */
ns-interrupts-action = <1>; /* Managed exit is supported. */
managed-exit-virq;
boot-order = <1>;
diff --git a/test/vmapi/ffa_secure_partitions/services/arch/aarch64/secure/partition_manifest_service_sp_second.dts b/test/vmapi/ffa_secure_partitions/services/arch/aarch64/secure/partition_manifest_service_sp_second.dts
index 3af9c9b..cd42395 100644
--- a/test/vmapi/ffa_secure_partitions/services/arch/aarch64/secure/partition_manifest_service_sp_second.dts
+++ b/test/vmapi/ffa_secure_partitions/services/arch/aarch64/secure/partition_manifest_service_sp_second.dts
@@ -22,6 +22,7 @@
entrypoint-offset = <0x2000>;
xlat-granule = <0>; /* 4KiB */
messaging-method = <0x607>; /* Supports direct (both APIs) and indirect messages. */
+ vm-availability-messages = <0x03>; /* Supports VM created and VM destroyed messages. */
ns-interrupts-action = <2>; /* Non secure interrupts are signaled. */
boot-order = <2>;
notification-support; /* Receipt of notifications. */
diff --git a/test/vmapi/ffa_secure_partitions/services/arch/aarch64/secure/partition_manifest_service_sp_second_up.dts b/test/vmapi/ffa_secure_partitions/services/arch/aarch64/secure/partition_manifest_service_sp_second_up.dts
index e9915f2..6476644 100644
--- a/test/vmapi/ffa_secure_partitions/services/arch/aarch64/secure/partition_manifest_service_sp_second_up.dts
+++ b/test/vmapi/ffa_secure_partitions/services/arch/aarch64/secure/partition_manifest_service_sp_second_up.dts
@@ -22,6 +22,7 @@
entrypoint-offset = <0x2000>;
xlat-granule = <0>; /* 4KiB */
messaging-method = <0x607>; /* Supports direct (both APIs) and indirect messages. */
+ vm-availability-messages = <0x03>; /* Subscribed to VM created and VM destroyed messages. */
ns-interrupts-action = <2>; /* Non secure interrupts are signaled. */
boot-order = <2>;
notification-support; /* Receipt of notifications. */
diff --git a/test/vmapi/ffa_secure_partitions/setup_and_discovery.c b/test/vmapi/ffa_secure_partitions/setup_and_discovery.c
index 73d778d..e68079a 100644
--- a/test/vmapi/ffa_secure_partitions/setup_and_discovery.c
+++ b/test/vmapi/ffa_secure_partitions/setup_and_discovery.c
@@ -44,7 +44,9 @@
ffa_uuid_init(0x9458bb2d, 0x353b4ee2, 0xaa25710c, 0x99b73ddc, &uuid);
EXPECT_TRUE(ffa_uuid_equal(&partitions[1].uuid, &uuid));
EXPECT_EQ(partitions[1].properties,
- FFA_PARTITION_DIRECT_REQ_RECV | FFA_PARTITION_AARCH64_EXEC);
+ FFA_PARTITION_AARCH64_EXEC | FFA_PARTITION_DIRECT_REQ_RECV |
+ FFA_PARTITION_VM_CREATED |
+ FFA_PARTITION_VM_DESTROYED);
/* Expect a secondary SP as third partition. */
EXPECT_EQ(partitions[2].vm_id, SP_ID(2));
@@ -56,6 +58,8 @@
FFA_PARTITION_DIRECT_REQ_RECV | FFA_PARTITION_INDIRECT_MSG |
FFA_PARTITION_NOTIFICATION |
FFA_PARTITION_AARCH64_EXEC |
+ FFA_PARTITION_VM_CREATED |
+ FFA_PARTITION_VM_DESTROYED |
FFA_PARTITION_DIRECT_REQ2_RECV);
/* Expect a tertiary SP as fourth partition. */
@@ -234,6 +238,8 @@
FFA_PARTITION_DIRECT_REQ_RECV | FFA_PARTITION_INDIRECT_MSG |
FFA_PARTITION_NOTIFICATION |
FFA_PARTITION_AARCH64_EXEC |
+ FFA_PARTITION_VM_CREATED |
+ FFA_PARTITION_VM_DESTROYED |
FFA_PARTITION_DIRECT_REQ2_RECV);
/*
diff --git a/test/vmapi/primary_with_secondaries/dir_msg.c b/test/vmapi/primary_with_secondaries/dir_msg.c
index 9ef7db8..b0bbe56 100644
--- a/test/vmapi/primary_with_secondaries/dir_msg.c
+++ b/test/vmapi/primary_with_secondaries/dir_msg.c
@@ -9,6 +9,7 @@
#include <stdint.h>
#include "hf/arch/vm/power_mgmt.h"
+#include "hf/arch/vmid_base.h"
#include "hf/ffa.h"
@@ -1110,3 +1111,329 @@
HFTEST_LOG("Finished the test...");
}
+
+/**
+ * Helper for sending a VM availability message and asserting on the response.
+ *
+ * NOTE: This is intended for hypervisor messages according to the spec, but we
+ * are using it from the primary VM because it is more convenient and we care
+ * about testing the SPMC component. Hypervisor implementation was changed to
+ * forward these requests from the PVM.
+ */
+void assert_vm_availability_message(
+ ffa_id_t sender_id, ffa_id_t receiver_id, ffa_id_t vm_id,
+ enum ffa_framework_msg_func framework_func, uint32_t response_ffa_func,
+ ffa_id_t response_sender_id, ffa_id_t response_receiver_id,
+ enum ffa_framework_msg_func response_framework_func,
+ enum ffa_error response_status)
+{
+ struct ffa_value res;
+
+ res = ffa_framework_msg_send_direct_req(sender_id, receiver_id,
+ framework_func, vm_id);
+
+ EXPECT_EQ(res.func, response_ffa_func);
+
+ /* sender and receiver endpoint IDs */
+ EXPECT_EQ(ffa_sender(res), response_receiver_id);
+ EXPECT_EQ(ffa_receiver(res), response_sender_id);
+
+ /* message flags */
+ EXPECT_EQ(res.arg2, FFA_FRAMEWORK_MSG_BIT | response_framework_func);
+
+ /* status code */
+ EXPECT_EQ((enum ffa_error)res.arg3, response_status);
+}
+
+/** Assert that a VM availability message is successful. */
+void assert_vm_availability_message_success(
+ ffa_id_t sender_id, ffa_id_t receiver_id, ffa_id_t vm_id,
+ enum ffa_framework_msg_func framework_func)
+{
+ assert_vm_availability_message(sender_id, receiver_id, vm_id,
+ framework_func,
+ FFA_MSG_SEND_DIRECT_RESP_32, sender_id,
+ receiver_id, framework_func + 1, 0);
+}
+
+/**
+ * Assert that a VM availability message is successfully delivered to the SP,
+ * but the SP reponds with `FFA_INVALID_PARAMETERS` because of an invalid state
+ * transition.
+ */
+void assert_vm_availability_message_invalid_transition(
+ ffa_id_t sender_id, ffa_id_t receiver_id, ffa_id_t vm_id,
+ enum ffa_framework_msg_func framework_func)
+{
+ assert_vm_availability_message(
+ sender_id, receiver_id, vm_id, framework_func,
+ FFA_MSG_SEND_DIRECT_RESP_32, sender_id, receiver_id,
+ framework_func + 1, FFA_INVALID_PARAMETERS);
+}
+
+/**
+ * Assert that a VM availability message is not delivered to the SP.
+ */
+void assert_vm_availability_message_not_delivered(
+ ffa_id_t sender_id, ffa_id_t receiver_id, ffa_id_t vm_id,
+ enum ffa_framework_msg_func framework_func)
+{
+ assert_vm_availability_message(sender_id, receiver_id, vm_id,
+ framework_func, FFA_ERROR_32, 0, 0,
+ FFA_INVALID_PARAMETERS, 0);
+}
+
+/**
+ * VM state: Unvailable
+ * Message: VM created
+ * New state: Available
+ */
+TEST_PRECONDITION(vm_availability_messaging, vm_unavailable_created,
+ service1_is_secure)
+{
+ struct mailbox_buffers mb = set_up_mailbox();
+ ffa_id_t sender_id = hf_vm_get_id();
+ ffa_id_t receiver_id = service1(mb.recv)->vm_id;
+ ffa_id_t vm_id = VM_ID(1);
+
+ SERVICE_SELECT(receiver_id, "vm_availability_messaging", mb.send);
+ ffa_run(receiver_id, 0);
+
+ assert_vm_availability_message_success(
+ sender_id, receiver_id, vm_id,
+ FFA_FRAMEWORK_MSG_VM_CREATION_REQ);
+}
+
+/**
+ * VM state: Available
+ * Message: VM created
+ * New state: Error
+ */
+TEST_PRECONDITION(vm_availability_messaging, vm_available_created,
+ service1_is_secure)
+{
+ struct mailbox_buffers mb = set_up_mailbox();
+ ffa_id_t sender_id = hf_vm_get_id();
+ ffa_id_t receiver_id = service1(mb.recv)->vm_id;
+ ffa_id_t vm_id = VM_ID(1);
+
+ SERVICE_SELECT(receiver_id, "vm_availability_messaging", mb.send);
+ ffa_run(receiver_id, 0);
+
+ assert_vm_availability_message_success(
+ sender_id, receiver_id, vm_id,
+ FFA_FRAMEWORK_MSG_VM_CREATION_REQ);
+
+ assert_vm_availability_message_invalid_transition(
+ sender_id, receiver_id, vm_id,
+ FFA_FRAMEWORK_MSG_VM_CREATION_REQ);
+}
+
+/**
+ * VM state: Unavailable
+ * Message: VM destroyed
+ * New state: Error
+ */
+TEST_PRECONDITION(vm_availability_messaging, vm_unavailable_destroyed,
+ service1_is_secure)
+{
+ struct mailbox_buffers mb = set_up_mailbox();
+ ffa_id_t sender_id = hf_vm_get_id();
+ ffa_id_t receiver_id = service1(mb.recv)->vm_id;
+ ffa_id_t vm_id = VM_ID(1);
+
+ SERVICE_SELECT(receiver_id, "vm_availability_messaging", mb.send);
+ ffa_run(receiver_id, 0);
+
+ assert_vm_availability_message_invalid_transition(
+ sender_id, receiver_id, vm_id,
+ FFA_FRAMEWORK_MSG_VM_DESTRUCTION_REQ);
+}
+
+/**
+ * VM state: Available
+ * Message: VM destroyed
+ * New state: Unavailable
+ */
+TEST_PRECONDITION(vm_availability_messaging, vm_available_destroyed,
+ service1_is_secure)
+{
+ struct mailbox_buffers mb = set_up_mailbox();
+ ffa_id_t sender_id = hf_vm_get_id();
+ ffa_id_t receiver_id = service1(mb.recv)->vm_id;
+ ffa_id_t vm_id = VM_ID(1);
+
+ SERVICE_SELECT(receiver_id, "vm_availability_messaging", mb.send);
+ ffa_run(receiver_id, 0);
+
+ assert_vm_availability_message_success(
+ sender_id, receiver_id, vm_id,
+ FFA_FRAMEWORK_MSG_VM_CREATION_REQ);
+
+ assert_vm_availability_message_success(
+ sender_id, receiver_id, vm_id,
+ FFA_FRAMEWORK_MSG_VM_DESTRUCTION_REQ);
+}
+
+/**
+ * VM state: Error
+ * Message: VM created
+ * New state: Error
+ */
+TEST_PRECONDITION(vm_availability_messaging, vm_error_created,
+ service1_is_secure)
+{
+ struct mailbox_buffers mb = set_up_mailbox();
+ ffa_id_t sender_id = hf_vm_get_id();
+ ffa_id_t receiver_id = service1(mb.recv)->vm_id;
+ ffa_id_t vm_id = VM_ID(1);
+
+ SERVICE_SELECT(receiver_id, "vm_availability_messaging", mb.send);
+ ffa_run(receiver_id, 0);
+
+ assert_vm_availability_message_invalid_transition(
+ sender_id, receiver_id, vm_id,
+ FFA_FRAMEWORK_MSG_VM_DESTRUCTION_REQ);
+
+ assert_vm_availability_message_invalid_transition(
+ sender_id, receiver_id, vm_id,
+ FFA_FRAMEWORK_MSG_VM_CREATION_REQ);
+}
+
+/**
+ * VM state: Error
+ * Message: VM destroyed
+ * New state: Error
+ */
+TEST_PRECONDITION(vm_availability_messaging, vm_error_destroyed,
+ service1_is_secure)
+{
+ struct mailbox_buffers mb = set_up_mailbox();
+ ffa_id_t sender_id = hf_vm_get_id();
+ ffa_id_t receiver_id = service1(mb.recv)->vm_id;
+ ffa_id_t vm_id = VM_ID(1);
+
+ SERVICE_SELECT(receiver_id, "vm_availability_messaging", mb.send);
+ ffa_run(receiver_id, 0);
+
+ assert_vm_availability_message_invalid_transition(
+ sender_id, receiver_id, vm_id,
+ FFA_FRAMEWORK_MSG_VM_DESTRUCTION_REQ);
+
+ assert_vm_availability_message_invalid_transition(
+ sender_id, receiver_id, vm_id,
+ FFA_FRAMEWORK_MSG_VM_DESTRUCTION_REQ);
+}
+
+/**
+ * Multiple SPs can receieve VM availability messages, and each SP has their own
+ * set of VM states.
+ */
+TEST_PRECONDITION(vm_availability_messaging, multiple_sps,
+ service1_and_service2_are_secure)
+{
+ struct mailbox_buffers mb = set_up_mailbox();
+ ffa_id_t sender_id = hf_vm_get_id();
+ ffa_id_t sp1 = service1(mb.recv)->vm_id;
+ ffa_id_t sp2 = service2(mb.recv)->vm_id;
+ ffa_id_t vm_id = VM_ID(1);
+
+ SERVICE_SELECT(sp1, "vm_availability_messaging", mb.send);
+ ffa_run(sp1, 0);
+
+ SERVICE_SELECT(sp2, "vm_availability_messaging", mb.send);
+ ffa_run(sp2, 0);
+
+ assert_vm_availability_message_success(
+ sender_id, sp1, vm_id, FFA_FRAMEWORK_MSG_VM_CREATION_REQ);
+
+ assert_vm_availability_message_success(
+ sender_id, sp2, vm_id, FFA_FRAMEWORK_MSG_VM_CREATION_REQ);
+}
+
+/**
+ * If the SP is not subscribed, any VM availability message should not be
+ * delivered.
+ */
+TEST_PRECONDITION(vm_availability_messaging, sp_not_subscribed,
+ service1_and_service2_are_secure)
+{
+ struct mailbox_buffers mb = set_up_mailbox();
+ ffa_id_t sender_id = hf_vm_get_id();
+ struct ffa_partition_info *sp3_info = service3(mb.recv);
+ ffa_id_t receiver_id = sp3_info->vm_id;
+ ffa_id_t vm_id = VM_ID(1);
+
+ EXPECT_EQ(sp3_info->properties & FFA_PARTITION_VM_CREATED, 0);
+ EXPECT_EQ(sp3_info->properties & FFA_PARTITION_VM_DESTROYED, 0);
+
+ SERVICE_SELECT(receiver_id, "vm_availability_messaging", mb.send);
+ ffa_run(receiver_id, 0);
+
+ assert_vm_availability_message_not_delivered(
+ sender_id, receiver_id, vm_id,
+ FFA_FRAMEWORK_MSG_VM_CREATION_REQ);
+
+ assert_vm_availability_message_not_delivered(
+ sender_id, receiver_id, vm_id,
+ FFA_FRAMEWORK_MSG_VM_DESTRUCTION_REQ);
+}
+
+/*
+ * Check that SPs cannot send VM availability messages.
+ */
+TEST_PRECONDITION(vm_availability_messaging, sp_cannot_send_messages,
+ service1_is_secure)
+{
+ struct mailbox_buffers mb = set_up_mailbox();
+ ffa_id_t sender_id = hf_vm_get_id();
+ ffa_id_t receiver_id = service1(mb.recv)->vm_id;
+ ffa_id_t vm_id = VM_ID(1);
+
+ struct ffa_value res;
+
+ SERVICE_SELECT(receiver_id, "vm_availability_messaging_send_from_sp",
+ mb.send);
+ ffa_run(receiver_id, 0);
+
+ res = ffa_msg_send_direct_req(sender_id, receiver_id,
+ FFA_FRAMEWORK_MSG_VM_CREATION_REQ, vm_id,
+ 0, 0, 0);
+ EXPECT_EQ(res.func, FFA_MSG_SEND_DIRECT_RESP_32);
+ EXPECT_EQ(res.arg3, FFA_ERROR_32);
+ EXPECT_EQ((enum ffa_error)res.arg5, FFA_INVALID_PARAMETERS);
+
+ res = ffa_msg_send_direct_req(sender_id, receiver_id,
+ FFA_FRAMEWORK_MSG_VM_DESTRUCTION_REQ,
+ vm_id, 0, 0, 0);
+ EXPECT_EQ(res.func, FFA_MSG_SEND_DIRECT_RESP_32);
+ EXPECT_EQ(res.arg3, FFA_ERROR_32);
+ EXPECT_EQ((enum ffa_error)res.arg5, FFA_INVALID_PARAMETERS);
+}
+
+/*
+ * Check that SPs cannot send non-framework messages in response to a VM
+ * availability message.
+ */
+TEST_PRECONDITION(vm_availability_messaging,
+ sp_cannot_send_non_framework_messages, service1_is_secure)
+{
+ struct mailbox_buffers mb = set_up_mailbox();
+ ffa_id_t sender_id = hf_vm_get_id();
+ ffa_id_t receiver_id = service1(mb.recv)->vm_id;
+ ffa_id_t vm_id = VM_ID(1);
+
+ struct ffa_value res;
+
+ SERVICE_SELECT(receiver_id,
+ "vm_availability_messaging_send_non_framework_from_sp",
+ mb.send);
+ ffa_run(receiver_id, 0);
+
+ res = ffa_framework_msg_send_direct_req(
+ sender_id, receiver_id, FFA_FRAMEWORK_MSG_VM_CREATION_REQ,
+ vm_id);
+
+ EXPECT_EQ(res.func, FFA_MSG_SEND_DIRECT_RESP_32);
+ EXPECT_EQ(res.arg3, 0);
+}
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 a0b4dee..103a6a1 100644
--- a/test/vmapi/primary_with_secondaries/inc/primary_with_secondary.h
+++ b/test/vmapi/primary_with_secondaries/inc/primary_with_secondary.h
@@ -81,6 +81,7 @@
/* Precondition functions for this test setup. */
bool service1_is_vm(void);
bool service1_is_not_vm(void);
+bool service1_is_secure(void);
bool service1_and_service2_are_secure(void);
bool service1_service2_and_service3_are_secure(void);
bool service1_is_mp_sp(void);
diff --git a/test/vmapi/primary_with_secondaries/primary_with_secondaries.c b/test/vmapi/primary_with_secondaries/primary_with_secondaries.c
index 78f9a98..bffc349 100644
--- a/test/vmapi/primary_with_secondaries/primary_with_secondaries.c
+++ b/test/vmapi/primary_with_secondaries/primary_with_secondaries.c
@@ -33,6 +33,14 @@
return mb;
}
+bool service1_is_secure(void)
+{
+ struct mailbox_buffers mb = get_precondition_mailbox();
+ struct ffa_partition_info *service1_info = service1(mb.recv);
+
+ return !ffa_is_vm_id(service1_info->vm_id);
+}
+
bool service1_and_service2_are_secure(void)
{
struct mailbox_buffers mb = get_precondition_mailbox();
diff --git a/test/vmapi/primary_with_secondaries/services/arch/aarch64/secure/el0/partition_manifest_service_sp1.dts b/test/vmapi/primary_with_secondaries/services/arch/aarch64/secure/el0/partition_manifest_service_sp1.dts
index 3e2cc98..8b83114 100644
--- a/test/vmapi/primary_with_secondaries/services/arch/aarch64/secure/el0/partition_manifest_service_sp1.dts
+++ b/test/vmapi/primary_with_secondaries/services/arch/aarch64/secure/el0/partition_manifest_service_sp1.dts
@@ -22,6 +22,7 @@
entrypoint-offset = <0x2000>;
xlat-granule = <0>; /* 4KiB */
messaging-method = <0x607>; /* Supports direct (both APIs) and indirect requests. */
+ vm-availability-messages = <0x03>; /* Supports VM created and VM destroyed messages. */
boot-order = <1>;
notification-support; /* Receipt of notifications. */
gp-register-num = <0>;
diff --git a/test/vmapi/primary_with_secondaries/services/arch/aarch64/secure/el0/partition_manifest_service_sp2.dts b/test/vmapi/primary_with_secondaries/services/arch/aarch64/secure/el0/partition_manifest_service_sp2.dts
index 9931bf3..77f6046 100644
--- a/test/vmapi/primary_with_secondaries/services/arch/aarch64/secure/el0/partition_manifest_service_sp2.dts
+++ b/test/vmapi/primary_with_secondaries/services/arch/aarch64/secure/el0/partition_manifest_service_sp2.dts
@@ -23,6 +23,7 @@
entrypoint-offset = <0x2000>;
xlat-granule = <0>; /* 4KiB */
messaging-method = <0x607>; /* Supports direct (both APIs) and indirect requests. */
+ vm-availability-messages = <0x03>; /* Supports VM created and VM destroyed messages. */
boot-order = <2>;
notification-support; /* Receipt of notifications. */
gp-register-num = <0>;
diff --git a/test/vmapi/primary_with_secondaries/services/arch/aarch64/secure/partition_manifest_service_sp1.dts b/test/vmapi/primary_with_secondaries/services/arch/aarch64/secure/partition_manifest_service_sp1.dts
index 1e09280..a76ec66 100644
--- a/test/vmapi/primary_with_secondaries/services/arch/aarch64/secure/partition_manifest_service_sp1.dts
+++ b/test/vmapi/primary_with_secondaries/services/arch/aarch64/secure/partition_manifest_service_sp1.dts
@@ -22,6 +22,7 @@
entrypoint-offset = <0x2000>;
xlat-granule = <0>; /* 4KiB */
messaging-method = <0x607>; /* Supports direct (both APIs) and indirect requests. */
+ vm-availability-messages = <0x03>; /* Supports VM created and VM destroyed messages. */
boot-order = <1>;
notification-support; /* Receipt of notifications. */
gp-register-num = <0>;
diff --git a/test/vmapi/primary_with_secondaries/services/arch/aarch64/secure/partition_manifest_service_sp2.dts b/test/vmapi/primary_with_secondaries/services/arch/aarch64/secure/partition_manifest_service_sp2.dts
index 7dd82a1..96b8217 100644
--- a/test/vmapi/primary_with_secondaries/services/arch/aarch64/secure/partition_manifest_service_sp2.dts
+++ b/test/vmapi/primary_with_secondaries/services/arch/aarch64/secure/partition_manifest_service_sp2.dts
@@ -23,6 +23,7 @@
entrypoint-offset = <0x2000>;
xlat-granule = <0>; /* 4KiB */
messaging-method = <0x607>; /* Supports direct (both APIs) and indirect requests. */
+ vm-availability-messages = <0x03>; /* Supports VM created and VM destroyed messages. */
boot-order = <2>;
notification-support; /* Receipt of notifications. */
gp-register-num = <0>;
diff --git a/test/vmapi/primary_with_secondaries/services/dir_msg.c b/test/vmapi/primary_with_secondaries/services/dir_msg.c
index b3b56e6..3ff4af7 100644
--- a/test/vmapi/primary_with_secondaries/services/dir_msg.c
+++ b/test/vmapi/primary_with_secondaries/services/dir_msg.c
@@ -6,10 +6,11 @@
* https://opensource.org/licenses/BSD-3-Clause.
*/
+#include "hf/check.h"
#include "hf/ffa.h"
-#include "hf/std.h"
#include "vmapi/hf/call.h"
+#include "vmapi/hf/ffa.h"
#include "primary_with_secondary.h"
#include "test/hftest.h"
@@ -804,3 +805,148 @@
(const uint64_t *)msg, MAX_RESP_REGS);
}
}
+
+/**
+ * Identifies the VM availiability message.
+ * See section 18.3 of v1.2 FF-A specification.
+ */
+enum ffa_vm_availability_message {
+ VM_MSG_CREATED,
+ VM_MSG_DESTROYED,
+};
+
+/**
+ * The state of a VM.
+ * See section 18.3.2.3 of v1.2 FF-A specification.
+ */
+enum ffa_vm_availability_state {
+ VM_STATE_UNAVAILABLE,
+ VM_STATE_AVAILABLE,
+ VM_STATE_ERROR,
+};
+
+/**
+ * In the scope of the VM availability messages, the test SP is maintaining the
+ * state of 4 VMs.
+ */
+static enum ffa_vm_availability_state vm_states[4] = {0};
+
+static enum ffa_vm_availability_state next_vm_availability_state(
+ enum ffa_vm_availability_state current_state,
+ enum ffa_vm_availability_message msg)
+{
+ /* clang-format off */
+ static const enum ffa_vm_availability_state table[3][2] = {
+ /* State VM created VM destoyed */
+ /* VM unavailable */ { VM_STATE_AVAILABLE, VM_STATE_ERROR },
+ /* VM available */ { VM_STATE_ERROR, VM_STATE_UNAVAILABLE },
+ /* Error */ { VM_STATE_ERROR, VM_STATE_ERROR },
+ };
+ /* clang-format on */
+
+ return table[current_state][msg];
+}
+
+/**
+ * Receive VM availability messages and update VM's state accordingly.
+ */
+TEST_SERVICE(vm_availability_messaging)
+{
+ struct ffa_value args = ffa_msg_wait();
+
+ for (;;) {
+ enum ffa_framework_msg_func func = ffa_framework_msg_func(args);
+ ffa_id_t sender_id = ffa_sender(args);
+ ffa_id_t receiver_id = ffa_receiver(args);
+ ffa_id_t vm_id = ffa_vm_availability_message_vm_id(args);
+ enum ffa_vm_availability_state current_state;
+ enum ffa_vm_availability_state new_state;
+ enum ffa_vm_availability_message msg;
+
+ switch (func) {
+ case FFA_FRAMEWORK_MSG_VM_CREATION_REQ:
+ msg = VM_MSG_CREATED;
+ break;
+ case FFA_FRAMEWORK_MSG_VM_DESTRUCTION_REQ:
+ msg = VM_MSG_DESTROYED;
+ break;
+ default:
+ dlog_verbose("Unknown framework message func: %#x\n",
+ func);
+ return;
+ }
+
+ current_state = vm_states[vm_id];
+ new_state = next_vm_availability_state(current_state, msg);
+ vm_states[vm_id] = new_state;
+
+ args = ffa_framework_message_send_direct_resp(
+ receiver_id, sender_id,
+ ((msg == VM_MSG_CREATED)
+ ? FFA_FRAMEWORK_MSG_VM_CREATION_RESP
+ : FFA_FRAMEWORK_MSG_VM_DESTRUCTION_RESP),
+ (new_state != VM_STATE_ERROR) ? 0
+ : FFA_INVALID_PARAMETERS);
+ }
+}
+
+/**
+ * Forward a framework message from primary VM, to demonstrate that SP cannot
+ * send framework messages.
+ */
+TEST_SERVICE(vm_availability_messaging_send_from_sp)
+{
+ struct ffa_value args = ffa_msg_wait();
+
+ for (;;) {
+ enum ffa_framework_msg_func func = ffa_framework_msg_func(args);
+ ffa_id_t sp_id = ffa_receiver(args);
+ ffa_id_t pvm_id = ffa_sender(args);
+ ffa_id_t vm_id = ffa_vm_availability_message_vm_id(args);
+
+ struct ffa_value ret = ffa_framework_msg_send_direct_req(
+ sp_id, pvm_id, func, vm_id);
+
+ /* Verify that the framework message request failed. */
+ ASSERT_EQ(ret.func, FFA_ERROR_32);
+ ASSERT_EQ(ret.arg3, 0);
+
+ /*
+ * Send a valid response, so that the PVM is not blocked
+ * forever.
+ */
+ args = ffa_msg_send_direct_resp(sp_id, pvm_id, ret.func,
+ ret.arg1, ret.arg2, ret.arg3,
+ ret.arg4);
+ }
+}
+
+/**
+ * Forward a framework message from primary VM, to demonstrate that SP cannot
+ * send non-framework messages in response to framework messages.
+ */
+TEST_SERVICE(vm_availability_messaging_send_non_framework_from_sp)
+{
+ struct ffa_value args = ffa_msg_wait();
+ struct ffa_value ret;
+ uint32_t ffa_func = args.func;
+ ffa_id_t sp_id = ffa_receiver(args);
+ ffa_id_t pvm_id = ffa_sender(args);
+
+ ret = ffa_msg_send_direct_resp(sp_id, pvm_id, args.arg3, args.arg4,
+ args.arg5, args.arg6, args.arg7);
+
+ /* Verify that the direct message response failed. */
+ ASSERT_EQ(ret.func, FFA_ERROR_32);
+ ASSERT_EQ((enum ffa_error)ret.arg2, FFA_DENIED);
+
+ /* Send a valid response, so that the VM is not blocked forever. */
+ args = ffa_framework_message_send_direct_resp(sp_id, pvm_id, ffa_func,
+ 0);
+ ASSERT_EQ(args.func, FFA_MSG_SEND_DIRECT_REQ_32);
+ EXPECT_EQ(ffa_sender(args), pvm_id);
+ EXPECT_EQ(ffa_receiver(args), sp_id);
+ EXPECT_EQ(args.arg2,
+ FFA_FRAMEWORK_MSG_BIT | FFA_FRAMEWORK_MSG_VM_DESTRUCTION_REQ);
+ ASSERT_EQ(args.arg3, 0);
+}