/* * 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 #include "hf/arch/vm/power_mgmt.h" #include "hf/arch/vmid_base.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)) /** * Structure defined for usage in tests with multiple cores. * Used to pass arguments from primary to secondary core. */ 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 ffa_value res; res = ffa_msg_send_direct_req(hf_vm_get_id(), target_id, msg[0], msg[1], msg[2], msg[3], msg[4]); EXPECT_EQ(res.func, FFA_MSG_SEND_DIRECT_RESP_32); EXPECT_EQ(res.arg3, msg[0]); EXPECT_EQ(res.arg4, msg[1]); EXPECT_EQ(res.arg5, msg[2]); EXPECT_EQ(res.arg6, msg[3]); 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_vm_get_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. */ TEST(direct_message, ffa_send_direct_message_req_yield_echo) { 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_yield_direct_message_resp_echo", mb.send); ffa_run(service1_info->vm_id, 0); res = ffa_msg_send_direct_req(hf_vm_get_id(), service1_info->vm_id, msg[0], msg[1], msg[2], msg[3], msg[4]); /* * Consider the scenario where VM1 allocated CPU cycles to SP1 through * a direct request message but SP1 yields execution back to VM1 * instead of busy waiting for an IO operation. */ EXPECT_EQ(res.func, FFA_YIELD_32); /* SP1 id/vCPU index are passed through arg1. */ EXPECT_EQ(res.arg1, ffa_vm_vcpu(service1_info->vm_id, 0)); /* * Additionally, SP1 can also specify timeout while yielding cycles * back to VM1. This is a hint to VM1 that it can be resumed upon * expiration of the timeout. * Check for 64-bit timeout specified by SP1 through arg2 and arg3. The * purpose of these checks is just to validate the timeout value but * not to leverage it upon expiration. */ EXPECT_EQ(res.arg2, 0x1); EXPECT_EQ(res.arg3, 0x23456789); /* Allocate CPU cycles to resume SP. */ res = ffa_run(service1_info->vm_id, 0); EXPECT_EQ(res.func, FFA_MSG_SEND_DIRECT_RESP_32); EXPECT_EQ(res.arg3, msg[0]); EXPECT_EQ(res.arg4, msg[1]); EXPECT_EQ(res.arg5, msg[2]); EXPECT_EQ(res.arg6, msg[3]); 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. */ TEST_PRECONDITION(direct_message, ffa_direct_message_services_echo, service1_and_service2_are_secure) { struct mailbox_buffers mb = set_up_mailbox(); struct ffa_partition_info *service1_info = service1(mb.recv); struct ffa_partition_info *service2_info = service2(mb.recv); ffa_id_t own_id = hf_vm_get_id(); struct ffa_value ret; /* Run service2 for it to wait for a request from service1. */ SERVICE_SELECT(service2_info->vm_id, "ffa_direct_message_resp_echo", mb.send); ffa_run(service2_info->vm_id, 0); /* Service1 requests echo from service2. */ SERVICE_SELECT(service1_info->vm_id, "ffa_direct_message_echo_services", mb.send); /* Send to service1 the uuid of the target for its message. */ ret = send_indirect_message(own_id, service1_info->vm_id, mb.send, &service2_info->vm_id, sizeof(service2_info->vm_id), 0); ASSERT_EQ(ret.func, FFA_SUCCESS_32); ffa_run(service1_info->vm_id, 0); } /** * Initiate direct message request between two Secure Partitions. Configure * the second SP in the call chain to yield cycles received from first SP * through direct message request. The first SP is equipped to reallocate * CPU cycles to resume the direct message processing. */ TEST_PRECONDITION(direct_message, ffa_direct_message_services_yield_echo, service1_and_service2_are_secure) { struct mailbox_buffers mb = set_up_mailbox(); struct ffa_partition_info *service1_info = service1(mb.recv); struct ffa_partition_info *service2_info = service2(mb.recv); ffa_id_t own_id = hf_vm_get_id(); struct ffa_value ret; /* Run service2 for it to wait for a request from service1. */ SERVICE_SELECT(service2_info->vm_id, "ffa_yield_direct_message_resp_echo", mb.send); ffa_run(service2_info->vm_id, 0); /* Service1 requests echo from service2. */ SERVICE_SELECT(service1_info->vm_id, "ffa_yield_direct_message_echo_services", mb.send); /* Send to service1 the FF-A ID of the target for its message. */ ret = send_indirect_message(own_id, service1_info->vm_id, mb.send, &service2_info->vm_id, sizeof(service2_info->vm_id), 0); ASSERT_EQ(ret.func, FFA_SUCCESS_32); ret = ffa_run(service1_info->vm_id, 0); EXPECT_EQ(ret.func, FFA_YIELD_32); } /** * If Hafnium is the hypervisor, and service1 is a VM: * - Service verifies disallowed SMC invocations while ffa_msg_send_direct_req * is being serviced. * If Hafnium as SPMC is deployed and service1 is an SP: * - Validate the state transitions permitted under RTM_FFA_DIR_REQ partition * runtime model */ TEST(direct_message, ffa_send_direct_message_req_disallowed_smc) { 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_msg_req_disallowed_smc", mb.send); ffa_run(service1_info->vm_id, 0); res = ffa_msg_send_direct_req(hf_vm_get_id(), service1_info->vm_id, msg[0], msg[1], msg[2], msg[3], msg[4]); EXPECT_EQ(res.func, FFA_MSG_SEND_DIRECT_RESP_32); } /** * Send direct message to invalid destination. */ TEST(direct_message, ffa_send_direct_message_req_invalid_dst) { const uint32_t msg[] = {0x00001111, 0x22223333, 0x44445555, 0x66667777, 0x88889999}; struct ffa_value res; res = ffa_msg_send_direct_req(hf_vm_get_id(), HF_PRIMARY_VM_ID, msg[0], msg[1], msg[2], msg[3], msg[4]); EXPECT_FFA_ERROR(res, FFA_INVALID_PARAMETERS); } /** * Verify that the primary VM can't send direct message responses. */ TEST(direct_message, ffa_send_direct_message_resp_invalid) { 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_resp(hf_vm_get_id(), service1_info->vm_id, 0, 0, 0, 0, 0); EXPECT_FFA_ERROR(res, FFA_INVALID_PARAMETERS); } /** * Test has two purposes. It runs the test service via ffa_run, and validates * that: * - If service is an SP, it can't send a direct message request to a VM in the * NWd. * - If service is a secondary VM, it can't invoke a direct message request to * the PVM (legacy behavior, for hafnium as an hypervisor). */ TEST(direct_message, ffa_secondary_direct_msg_req_invalid) { 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_disallowed_direct_msg_req", mb.send); ffa_run(service1_info->vm_id, 0); res = ffa_msg_send_direct_req(hf_vm_get_id(), service1_info->vm_id, 0, 0, 0, 0, 0); EXPECT_EQ(res.func, FFA_MSG_SEND_DIRECT_RESP_32); } /** * Run secondary VM without sending a direct message request beforehand. * Secondary VM must fail sending a direct message response. */ TEST(direct_message, ffa_secondary_direct_msg_resp_invalid) { 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_disallowed_direct_msg_resp", mb.send); ffa_run(service1_info->vm_id, 0); res = ffa_msg_send_direct_req(hf_vm_get_id(), service1_info->vm_id, 0, 0, 0, 0, 0); EXPECT_EQ(res.func, FFA_MSG_SEND_DIRECT_RESP_32); } /** * Run secondary VM and send a direct message request. Secondary VM attempts * altering the sender and receiver in its direct message responses, and must * fail to do so. */ TEST(direct_message, ffa_secondary_spoofed_response) { 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_msg_resp_invalid_sender_receiver", mb.send); ffa_run(service1_info->vm_id, 0); res = ffa_msg_send_direct_req(hf_vm_get_id(), service1_info->vm_id, 0, 0, 0, 0, 0); EXPECT_EQ(res.func, FFA_MSG_SEND_DIRECT_RESP_32); } /* * Validate that the creation of a cyclic dependency via direct_messaging * is not possible. * The test only makes sense in the scope of validating the SPMC, as the * hypervisor limits the direct message requests to be only invoked from * the primary VM. Thus, using precondition that checks both involved test * services are SPs. */ TEST_PRECONDITION(direct_message, fail_if_cyclic_dependency, service1_and_service2_are_secure) { struct mailbox_buffers mb = set_up_mailbox(); struct ffa_partition_info *service1_info = service1(mb.recv); struct ffa_partition_info *service2_info = service2(mb.recv); ffa_id_t own_id = hf_vm_get_id(); struct ffa_value ret; /* Run service2 for it to wait for a request from service1. */ SERVICE_SELECT(service2_info->vm_id, "ffa_direct_message_cycle_denied", mb.send); ffa_run(service2_info->vm_id, 0); /* Service1 requests echo from service2. */ SERVICE_SELECT(service1_info->vm_id, "ffa_direct_message_echo_services", mb.send); /* Send to service1 the uuid of the target for its message. */ ret = send_indirect_message(own_id, service1_info->vm_id, mb.send, &service2_info->vm_id, sizeof(service2_info->vm_id), 0); ASSERT_EQ(ret.func, FFA_SUCCESS_32); EXPECT_EQ(ffa_run(service1_info->vm_id, 0).func, FFA_YIELD_32); } /** * Send direct message via FFA_MSG_SEND_DIRECT_REQ2, verify that sent info is * echoed back. */ TEST(direct_message, ffa_send_direct_message_req2_echo) { struct mailbox_buffers mb = set_up_mailbox(); struct ffa_partition_info *service1_info = service1(mb.recv); 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); echo_test_req2(service1_info->vm_id, target_uuid); } /** * 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. */ TEST(direct_message, ffa_send_direct_message_req2_yield_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; SERVICE_SELECT(service1_info->vm_id, "ffa_yield_direct_message_resp2_echo", mb.send); ffa_run(service1_info->vm_id, 0); res = ffa_msg_send_direct_req2(hf_vm_get_id(), service1_info->vm_id, &uuid, (const uint64_t *)&msg, ARRAY_SIZE(msg)); /* * Consider the scenario where VM1 allocated CPU cycles to service1 * through a direct request message but service1 yields execution back * to VM1 instead of busy waiting for an IO operation. */ EXPECT_EQ(res.func, FFA_YIELD_32); /* Service1 id/vCPU index are passed through arg1. */ EXPECT_EQ(res.arg1, ffa_vm_vcpu(service1_info->vm_id, 0)); /* * Additionally, service1 can also specify timeout while yielding cycles * back to VM1. This is a hint to VM1 that it can be resumed upon * expiration of the timeout. * Check for 64-bit timeout specified by service1 through arg2 and arg3. * The purpose of these checks is just to validate the timeout value but * not to leverage it upon expiration. */ EXPECT_EQ(res.arg2, 0x1); EXPECT_EQ(res.arg3, 0x23456789); /* Allocate CPU cycles to resume service */ res = ffa_run(service1_info->vm_id, 0); 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]); } /** * Initiate direct message request between test SPs. * If test services are VMs, test should be skipped. */ TEST_PRECONDITION(direct_message, ffa_direct_message_req2_services_echo, service1_and_service2_are_secure) { struct mailbox_buffers mb = set_up_mailbox(); struct ffa_partition_info *service1_info = service1(mb.recv); struct ffa_partition_info *service2_info = service2(mb.recv); ffa_id_t own_id = hf_vm_get_id(); struct ffa_value ret; const struct ffa_uuid service2_uuid = SERVICE2; /* Run service2 for it to wait for a request from service1. */ SERVICE_SELECT(service2_info->vm_id, "ffa_direct_message_req2_resp_echo", mb.send); ffa_run(service2_info->vm_id, 0); /* Service1 requests echo from service2. */ SERVICE_SELECT(service1_info->vm_id, "ffa_direct_message_req2_echo_services", mb.send); /* Send to service1 the uuid of the target for its message. */ ret = send_indirect_message(own_id, service1_info->vm_id, mb.send, &service2_uuid, sizeof(service2_uuid), 0); ASSERT_EQ(ret.func, FFA_SUCCESS_32); ffa_run(service1_info->vm_id, 0); } /** * Initiate direct message request between two Secure Partitions. Configure * the second SP in the call chain to yield cycles received from first SP * through direct message request. The first SP is equipped to reallocate * CPU cycles to resume the direct message processing. */ TEST_PRECONDITION(direct_message, ffa_direct_message_req2_services_yield_echo, service1_and_service2_are_secure) { struct mailbox_buffers mb = set_up_mailbox(); struct ffa_partition_info *service1_info = service1(mb.recv); struct ffa_partition_info *service2_info = service2(mb.recv); ffa_id_t own_id = hf_vm_get_id(); struct ffa_value ret; const struct ffa_uuid service2_uuid = SERVICE2; /* Run service2 for it to wait for a request from service1. */ SERVICE_SELECT(service2_info->vm_id, "ffa_yield_direct_message_resp2_echo", mb.send); ffa_run(service2_info->vm_id, 0); /* Service1 requests echo from service2. */ SERVICE_SELECT(service1_info->vm_id, "ffa_yield_direct_message_v_1_2_echo_services", mb.send); /* Send to service1 the UUID of the target for its message. */ ret = send_indirect_message(own_id, service1_info->vm_id, mb.send, &service2_uuid, sizeof(service2_uuid), 0); ASSERT_EQ(ret.func, FFA_SUCCESS_32); ret = ffa_run(service1_info->vm_id, 0); EXPECT_EQ(ret.func, FFA_YIELD_32); } /** * If Hafnium is the hypervisor, and service1 is a VM: * - Service verifies disallowed SMC invocations while ffa_msg_send_direct_req * is being serviced. * * If Hafnium as SPMC is deployed and service1 is an SP: * - Validate the state transitions permitted under RTM_FFA_DIR_REQ partition * runtime model */ TEST(direct_message, ffa_send_direct_message_req2_disallowed_smc) { 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); const struct ffa_uuid service1_uuid = SERVICE1; SERVICE_SELECT(service1_info->vm_id, "ffa_direct_msg_req2_disallowed_smc", mb.send); ffa_run(service1_info->vm_id, 0); res = ffa_msg_send_direct_req2(hf_vm_get_id(), service1_info->vm_id, &service1_uuid, (const uint64_t *)&msg, ARRAY_SIZE(msg)); EXPECT_EQ(res.func, FFA_MSG_SEND_DIRECT_RESP2_64); } /** * Send direct message via FFA_MSG_SEND_DIRECT_REQ2 targeting an invalid UUID. */ TEST(direct_message, ffa_send_direct_message_req2_invalid_uuid) { 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; /* Non-existent UUID. */ ffa_uuid_init(1, 1, 1, 1, &uuid); res = ffa_msg_send_direct_req2(hf_vm_get_id(), service1_info->vm_id, &uuid, (const uint64_t *)&msg, ARRAY_SIZE(msg)); EXPECT_FFA_ERROR(res, FFA_INVALID_PARAMETERS); /* UUID for a different partition than given FF-A id. */ uuid = SERVICE2; res = ffa_msg_send_direct_req2(hf_vm_get_id(), service1_info->vm_id, &uuid, (const uint64_t *)&msg, ARRAY_SIZE(msg)); EXPECT_FFA_ERROR(res, FFA_INVALID_PARAMETERS); } /** * Verify that the primary VM can't send direct message responses * via FFA_MSG_SEND_DIRECT_RESP2_64. */ TEST(direct_message, ffa_send_direct_message_resp2_invalid) { struct mailbox_buffers mb = set_up_mailbox(); struct ffa_value res; struct ffa_partition_info *service1_info = service1(mb.recv); const uint64_t msg[] = {1, 2, 3, 4, 5}; 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_resp2(hf_vm_get_id(), service1_info->vm_id, (const uint64_t *)&msg, ARRAY_SIZE(msg)); EXPECT_FFA_ERROR(res, FFA_INVALID_PARAMETERS); } /** * Test runs the test service via ffa_run, and validates that: * - If service is an SP, it can't send a direct message request to a VM in the * NWd. * * Legacy case for secondary VM * - If service is a secondary VM, it can't invoke a direct message request to * the PVM (legacy behavior, for hafnium as an hypervisor). */ TEST(direct_message, ffa_secondary_direct_msg_req2_invalid) { struct mailbox_buffers mb = set_up_mailbox(); struct ffa_value res; struct ffa_partition_info *service1_info = service1(mb.recv); const struct ffa_uuid service1_uuid = SERVICE1; uint64_t msg[MAX_RESP_REGS] = {0}; struct ffa_uuid own_uuid = PVM; SERVICE_SELECT(service1_info->vm_id, "ffa_disallowed_direct_msg_req2", mb.send); res = send_indirect_message(hf_vm_get_id(), service1_info->vm_id, mb.send, &own_uuid, sizeof(own_uuid), 0); ASSERT_EQ(res.func, FFA_SUCCESS_32); ffa_run(service1_info->vm_id, 0); res = ffa_msg_send_direct_req2(hf_vm_get_id(), service1_info->vm_id, &service1_uuid, (const uint64_t *)&msg, ARRAY_SIZE(msg)); EXPECT_EQ(res.func, FFA_MSG_SEND_DIRECT_RESP2_64); } /** * Run secondary VM without sending a direct message request beforehand. * Secondary VM must fail sending a direct message response. */ TEST(direct_message, ffa_secondary_direct_msg_resp2_invalid) { struct mailbox_buffers mb = set_up_mailbox(); struct ffa_value res; struct ffa_partition_info *service1_info = service1(mb.recv); struct ffa_uuid service1_uuid = SERVICE1; uint64_t msg[MAX_RESP_REGS] = {0}; SERVICE_SELECT(service1_info->vm_id, "ffa_disallowed_direct_msg_resp2", mb.send); ffa_run(service1_info->vm_id, 0); res = ffa_msg_send_direct_req2(hf_vm_get_id(), service1_info->vm_id, &service1_uuid, (uint64_t *)msg, ARRAY_SIZE(msg)); EXPECT_EQ(res.func, FFA_MSG_SEND_DIRECT_RESP2_64); } /** * Run secondary VM and send a direct message request. Secondary VM attempts * altering the sender and receiver in its direct message responses, and must * fail to do so. */ TEST(direct_message, ffa_secondary_spoofed_response2) { struct mailbox_buffers mb = set_up_mailbox(); struct ffa_value res; struct ffa_partition_info *service1_info = service1(mb.recv); struct ffa_uuid service1_uuid = SERVICE1; uint64_t msg[MAX_RESP_REGS] = {0}; SERVICE_SELECT(service1_info->vm_id, "ffa_direct_msg_resp2_invalid_sender_receiver", mb.send); ffa_run(service1_info->vm_id, 0); res = ffa_msg_send_direct_req2(hf_vm_get_id(), service1_info->vm_id, &service1_uuid, (uint64_t *)msg, ARRAY_SIZE(msg)); EXPECT_EQ(res.func, FFA_MSG_SEND_DIRECT_RESP2_64); } /** * Validate that the creation of a cyclic dependency via direct messaging * interfaces introduced in FF-A v1.2 is not possible. The test only makes sense * in the scope of validating the SPMC, as the hypervisor limits the direct * message requests to be only invoked from the primary VM. Thus, using * precondition that checks both involved test services are SPs. */ TEST_PRECONDITION(direct_message, fail_if_cyclic_dependency_v1_2, service1_and_service2_are_secure) { struct mailbox_buffers mb = set_up_mailbox(); struct ffa_partition_info *service1_info = service1(mb.recv); struct ffa_partition_info *service2_info = service2(mb.recv); ffa_id_t own_id = hf_vm_get_id(); struct ffa_uuid service1_uuid = SERVICE1; struct ffa_uuid service2_uuid = SERVICE2; struct ffa_value ret; /* * Run service2 for it to wait for a request from service1 after * receiving indirect message containing uuid. */ SERVICE_SELECT(service2_info->vm_id, "ffa_direct_message_v_1_2_cycle_denied", mb.send); /* Send to service2 the uuid of service1 for its attempted message. */ ret = send_indirect_message(own_id, service2_info->vm_id, mb.send, &service1_uuid, sizeof(service1_uuid), 0); ASSERT_EQ(ret.func, FFA_SUCCESS_32); ffa_run(service2_info->vm_id, 0); /* Service1 requests echo from service2. */ SERVICE_SELECT(service1_info->vm_id, "ffa_direct_message_req2_echo_services", mb.send); /* Send to service1 the uuid of the target for its message. */ ret = send_indirect_message(own_id, service1_info->vm_id, mb.send, &service2_uuid, sizeof(service2_uuid), 0); ASSERT_EQ(ret.func, FFA_SUCCESS_32); EXPECT_EQ(ffa_run(service1_info->vm_id, 0).func, FFA_YIELD_32); } /** * Send a direct message request via FFA_MSG_SEND_DIR_REQ2 to each of the target * partition's UUIDs and verify that sent info is echoed back. */ // NOLINTNEXTLINE(readability-function-size) TEST_PRECONDITION(direct_message, ffa_send_direct_message_req2_multiple_uuids, service1_and_service2_are_secure) { 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 *service2_info = service2(mb.recv); struct ffa_uuid uuid = SERVICE2_UUID2; SERVICE_SELECT(service2_info->vm_id, "ffa_direct_message_req2_resp_loop", mb.send); ffa_run(service2_info->vm_id, 0); res = ffa_msg_send_direct_req2(hf_vm_get_id(), service2_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]); uuid = SERVICE2; res = ffa_msg_send_direct_req2(hf_vm_get_id(), service2_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]); } /** * Test that a request sent via: * - FFA_MSG_SEND_DIRECT_REQ2 cannot be completed by FFA_MSG_SEND_DIRECT_RESP * - FFA_MSG_SEND_DIRECT_REQ cannot be completed by FFA_MSG_SEND_DIRECT_RESP2 */ TEST(direct_message, ffa_direct_msg_check_abi_pairs_nwd_to_sp) { const uint64_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); struct ffa_partition_info *service2_info = service2(mb.recv); struct ffa_uuid uuid1 = SERVICE1; /* Setup Service1 to respond with FFA_MSG_SEND_DIRECT_RESP ABI. */ SERVICE_SELECT(service1_info->vm_id, "ffa_direct_msg_req2_resp_failure", mb.send); ffa_run(service1_info->vm_id, 0); /* Send a direct request with FFA_MSG_SEND_DIRECT_REQ2. */ res = ffa_msg_send_direct_req2(hf_vm_get_id(), service1_info->vm_id, &uuid1, (const uint64_t *)&msg, ARRAY_SIZE(msg)); EXPECT_EQ(res.func, FFA_MSG_SEND_DIRECT_RESP2_64); /* Set up Service2 to respond with FFA_MSG_SEND_DIRECT_RESP2 ABI. */ SERVICE_SELECT(service2_info->vm_id, "ffa_direct_msg_req_resp2_failure", mb.send); ffa_run(service2_info->vm_id, 0); /* * Send a direct request with FFA_MSG_SEND_DIRECT_REQ and expect * failure. */ res = ffa_msg_send_direct_req(hf_vm_get_id(), service2_info->vm_id, msg[0], msg[1], msg[2], msg[3], msg[4]); EXPECT_EQ(res.func, FFA_MSG_SEND_DIRECT_RESP_32); } /** * Ensure that an SP that enters the waiting state with FFA_MSG_SEND_DIRECT_RESP * can preserve extended registers when resumed by FFA_MSG_SEND_DIRECT_REQ2. * * Run twice to cover the reverse scenario - SP enters waiting state with * FFA_MSG_SEND_DIRECT_RESP2 and is resumed by FFA_MSG_SEND_DIRECT_REQ. */ // NOLINTNEXTLINE(readability-function-size) TEST(direct_message, ffa_direct_message_req2_after_req) { 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 uuid1 = SERVICE1; SERVICE_SELECT(service1_info->vm_id, "ffa_direct_msg_resp_ext_registers_preserved", mb.send); ffa_run(service1_info->vm_id, 0); for (uint32_t i = 0; i < 2; i++) { /* Send a direct request with FFA_MSG_SEND_DIRECT_REQ. */ res = ffa_msg_send_direct_req(hf_vm_get_id(), service1_info->vm_id, msg[0], msg[1], msg[2], msg[3], msg[4]); EXPECT_EQ(res.func, FFA_MSG_SEND_DIRECT_RESP_32); EXPECT_EQ(res.arg3, msg[0]); EXPECT_EQ(res.arg4, msg[1]); EXPECT_EQ(res.arg5, msg[2]); EXPECT_EQ(res.arg6, msg[3]); EXPECT_EQ(res.arg7, msg[4]); EXPECT_EQ(res.extended_val.arg8, 0); EXPECT_EQ(res.extended_val.arg9, 0); EXPECT_EQ(res.extended_val.arg10, 0); EXPECT_EQ(res.extended_val.arg11, 0); EXPECT_EQ(res.extended_val.arg12, 0); EXPECT_EQ(res.extended_val.arg13, 0); EXPECT_EQ(res.extended_val.arg14, 0); EXPECT_EQ(res.extended_val.arg15, 0); EXPECT_EQ(res.extended_val.arg16, 0); EXPECT_EQ(res.extended_val.arg17, 0); /* Send a direct request with FFA_MSG_SEND_DIRECT_REQ2. */ res = ffa_msg_send_direct_req2( hf_vm_get_id(), service1_info->vm_id, &uuid1, (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]); } } /** * Test showing that an FF-A v1.1 endpoint (service4) cannot send a direct * request via FFA_MSG_SEND_DIRECT_REQ2. */ TEST_PRECONDITION(direct_message, ffa_msg_send_direct_req2_send_v1_1_not_supported, service1_is_not_vm) { struct mailbox_buffers mb = set_up_mailbox(); struct ffa_partition_info *service4_info = service4(mb.recv); ffa_id_t own_id = hf_vm_get_id(); struct ffa_value ret; const struct ffa_uuid service1_uuid = SERVICE1; /* * Service4 requests echo from service1. * Request sent via FFA_MSG_SEND_DIRECT_REQ2 should fail as Service4 * FF-A version is < FF-A v1.2. */ SERVICE_SELECT(service4_info->vm_id, "version_does_not_support_req2", mb.send); /* Send to service4 the uuid of the target for its message. */ ret = send_indirect_message(own_id, service4_info->vm_id, mb.send, &service1_uuid, sizeof(service1_uuid), 0); ASSERT_EQ(ret.func, FFA_SUCCESS_32); ffa_run(service4_info->vm_id, 0); } /** * Test showing that an FF-A v1.1 endpoint (service3) cannot receive a direct * request via FFA_MSG_SEND_DIRECT_REQ2. * * Also show an FF-A v1.2 endpoint (service4) that does not specify receipt of * direct requsts via FFA_MSG_SEND_DIRECT_REQ2 in its manifest cannot receive a * direct request via this function id. */ TEST_PRECONDITION(direct_message, ffa_msg_send_direct_req2_recv_not_supported, service1_and_service2_are_secure) { 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_partition_info *service3_info = service3(mb.recv); struct ffa_partition_info *service4_info = service4(mb.recv); struct ffa_value res; const struct ffa_uuid service3_uuid = SERVICE3; const struct ffa_uuid service4_uuid = SERVICE4; /* Send a direct request with FFA_MSG_SEND_DIRECT_REQ2. */ res = ffa_msg_send_direct_req2(hf_vm_get_id(), service3_info->vm_id, &service3_uuid, (const uint64_t *)&msg, ARRAY_SIZE(msg)); EXPECT_FFA_ERROR(res, FFA_DENIED); SERVICE_SELECT(service4_info->vm_id, "ffa_direct_message_req2_resp_echo", mb.send); ffa_run(service4_info->vm_id, 0); /* Send a direct request with FFA_MSG_SEND_DIRECT_REQ2. */ res = ffa_msg_send_direct_req2(hf_vm_get_id(), service4_info->vm_id, &service4_uuid, (const uint64_t *)&msg, ARRAY_SIZE(msg)); EXPECT_FFA_ERROR(res, FFA_DENIED); } /** * Validate that the creation of a cyclic dependency via combined usage of * FFA_MSG_SEND_DIRECT_REQ and FFA_MSG_SEND_DIRECT_REQ2 is not possible. The * test only makes sense in the scope of validating the SPMC, as the hypervisor * limits the direct message requests to be only invoked from the primary VM. * Thus, using precondition that checks both involved test services are SPs. */ TEST_PRECONDITION(direct_message, fail_if_cyclic_dependency_req_req2, service1_and_service2_are_secure) { struct mailbox_buffers mb = set_up_mailbox(); struct ffa_partition_info *service1_info = service1(mb.recv); struct ffa_partition_info *service2_info = service2(mb.recv); ffa_id_t own_id = hf_vm_get_id(); struct ffa_uuid service1_uuid = SERVICE1; struct ffa_value ret; /* * Run service2 for it to wait for a request from service1 after * receiving indirect message containing uuid. */ SERVICE_SELECT(service2_info->vm_id, "ffa_direct_message_cycle_req_req2_denied", mb.send); /* Send to service2 the uuid of service1 for its attempted message. */ ret = send_indirect_message(own_id, service2_info->vm_id, mb.send, &service1_uuid, sizeof(service1_uuid), 0); ASSERT_EQ(ret.func, FFA_SUCCESS_32); ffa_run(service2_info->vm_id, 0); /* Service1 requests echo from service2. */ SERVICE_SELECT(service1_info->vm_id, "ffa_direct_message_echo_services", mb.send); /* Send to service1 the FF-A id of the target for its message. */ ret = send_indirect_message(own_id, service1_info->vm_id, mb.send, &service2_info->vm_id, sizeof(service2_info->vm_id), 0); 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", 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); } HFTEST_LOG("Done with secondary core..."); /* Signal to primary core that test is complete.*/ semaphore_signal(&args->sync); 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..."); ASSERT_TRUE(hftest_cpu_start(hftest_get_cpu_id(vcpu_id), hftest_get_secondary_ec_stack(vcpu_id), 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..."); } /** * 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..."); ASSERT_TRUE(hftest_cpu_start(hftest_get_cpu_id(vcpu_id), hftest_get_secondary_ec_stack(vcpu_id - 1), 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..."); } /** * 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, (enum ffa_framework_msg_func)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.arg2, FFA_FRAMEWORK_MSG_BIT | FFA_FRAMEWORK_MSG_VM_CREATION_RESP); EXPECT_EQ(res.arg3, 0); }