test(memory share): hypervisor retrieve request
Checks that the hypervisor retrieve request from section 17.4.3 of FF-A
v1.2 REL0 is supported.
Specifically, the tests:
1. Sends a FFA_MEM_SHARE/FFA_MEM_LEND/FFA_MEM_DONATE message from the
tftf target to the SPMC.
2. Sends a hypervisor retrieve request from the tftf target to the SPMC.
3. Verify the contents of the FFA_MEM_RETRIEVE_RESP message against the
previously sent message.
4. Invoke reclaim interface for SPMC to free memory owned by the handle.
Change-Id: If27d8d5b515c5e468977ed4f5f4ddeb86e44c5df
Signed-off-by: Karl Meakin <karl.meakin@arm.com>
diff --git a/include/runtime_services/ffa_helpers.h b/include/runtime_services/ffa_helpers.h
index 91a7a6e..90bb30f 100644
--- a/include/runtime_services/ffa_helpers.h
+++ b/include/runtime_services/ffa_helpers.h
@@ -647,6 +647,9 @@
enum ffa_memory_type type, enum ffa_memory_cacheability cacheability,
enum ffa_memory_shareability shareability);
+void ffa_hypervisor_retrieve_request_init(struct ffa_memory_region *region,
+ ffa_memory_handle_t handle);
+
uint32_t ffa_memory_region_init(
struct ffa_memory_region *memory_region, size_t memory_region_max_size,
ffa_id_t sender, ffa_id_t receiver,
diff --git a/include/runtime_services/spm_common.h b/include/runtime_services/spm_common.h
index fc7ddd7..e9c845d 100644
--- a/include/runtime_services/spm_common.h
+++ b/include/runtime_services/spm_common.h
@@ -112,6 +112,9 @@
ffa_id_t sender, ffa_id_t receiver,
ffa_memory_region_flags_t flags, uint32_t mem_func);
+bool hypervisor_retrieve_request(struct mailbox_buffers *mb, uint64_t handle,
+ void *out, uint32_t out_size);
+
/**
* Helper to conduct a memory relinquish. The caller is usually the receiver,
* after it being done with the memory shared, identified by the 'handle'.
diff --git a/tftf/tests/runtime_services/secure_service/ffa_helpers.c b/tftf/tests/runtime_services/secure_service/ffa_helpers.c
index 9447a23..e5f93f1 100644
--- a/tftf/tests/runtime_services/secure_service/ffa_helpers.c
+++ b/tftf/tests/runtime_services/secure_service/ffa_helpers.c
@@ -303,6 +303,17 @@
memory_region->receiver_count * sizeof(struct ffa_memory_access);
}
+/**
+ * Configure `region` for a hypervisor retrieve request - i.e. all fields except
+ * `handle` are initialized to 0.
+ */
+void ffa_hypervisor_retrieve_request_init(struct ffa_memory_region *region,
+ ffa_memory_handle_t handle)
+{
+ memset(region, 0, sizeof(struct ffa_memory_region));
+ region->handle = handle;
+}
+
/*
* FFA Version ABI helper.
* Version fields:
diff --git a/tftf/tests/runtime_services/secure_service/spm_common.c b/tftf/tests/runtime_services/secure_service/spm_common.c
index e76564a..c4323e0 100644
--- a/tftf/tests/runtime_services/secure_service/spm_common.c
+++ b/tftf/tests/runtime_services/secure_service/spm_common.c
@@ -4,6 +4,9 @@
* SPDX-License-Identifier: BSD-3-Clause
*/
+#include "assert.h"
+#include "stdint.h"
+
#include "ffa_helpers.h"
#include <cactus_test_cmds.h>
#include <debug.h>
@@ -277,6 +280,79 @@
return true;
}
+bool hypervisor_retrieve_request(struct mailbox_buffers *mb, uint64_t handle,
+ void *out, uint32_t out_size)
+{
+ struct ffa_value ret;
+ uint32_t fragment_size;
+ uint32_t total_size;
+ struct ffa_memory_region *region_out = out;
+
+ if (out == NULL || mb == NULL) {
+ ERROR("Invalid parameters!\n");
+ return false;
+ }
+
+ ffa_hypervisor_retrieve_request_init(mb->send, handle);
+ ret = ffa_mem_retrieve_req(sizeof(struct ffa_memory_region),
+ sizeof(struct ffa_memory_region));
+
+ if (ffa_func_id(ret) != FFA_MEM_RETRIEVE_RESP) {
+ ERROR("Couldn't retrieve the memory page. Error: %x\n",
+ ffa_error_code(ret));
+ return false;
+ }
+
+ /*
+ * Following total_size and fragment_size are useful to keep track
+ * of the state of transaction. When the sum of all fragment_size of all
+ * fragments is equal to total_size, the memory transaction has been
+ * completed.
+ * This is a simple test with only one segment. As such, upon
+ * successful ffa_mem_retrieve_req, total_size must be equal to
+ * fragment_size.
+ */
+ total_size = ret.arg1;
+ fragment_size = ret.arg2;
+
+ if (total_size != fragment_size) {
+ ERROR("Only expect one memory segment to be sent!\n");
+ return false;
+ }
+
+ if (fragment_size > PAGE_SIZE) {
+ ERROR("Fragment should be smaller than RX buffer!\n");
+ return false;
+ }
+ if (total_size > out_size) {
+ ERROR("output buffer is not large enough to store all "
+ "fragments (total_size=%d, max_size=%d)\n",
+ total_size, out_size);
+ return false;
+ }
+
+ /*
+ * Copy the received message to the out buffer. This is necessary
+ * because `mb->recv` will be overwritten if sending a fragmented
+ * message.
+ */
+ memcpy(out, mb->recv, total_size);
+
+ if (region_out->receiver_count == 0) {
+ VERBOSE("copied region has no recivers\n");
+ return false;
+ }
+
+ if (region_out->receiver_count > MAX_MEM_SHARE_RECIPIENTS) {
+ VERBOSE("SPMC memory sharing operations support max of %u "
+ "receivers!\n",
+ MAX_MEM_SHARE_RECIPIENTS);
+ return false;
+ }
+
+ return true;
+}
+
bool memory_relinquish(struct ffa_mem_relinquish *m, uint64_t handle,
ffa_id_t id)
{
diff --git a/tftf/tests/runtime_services/secure_service/test_ffa_memory_sharing.c b/tftf/tests/runtime_services/secure_service/test_ffa_memory_sharing.c
index d42492b..5b80825 100644
--- a/tftf/tests/runtime_services/secure_service/test_ffa_memory_sharing.c
+++ b/tftf/tests/runtime_services/secure_service/test_ffa_memory_sharing.c
@@ -1,10 +1,11 @@
/*
- * Copyright (c) 2020-2022, Arm Limited. All rights reserved.
+ * Copyright (c) 2020-2023, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <debug.h>
+#include "ffa_helpers.h"
#include <cactus_test_cmds.h>
#include <ffa_endpoints.h>
@@ -459,3 +460,290 @@
return TEST_RESULT_SUCCESS;
}
+
+/**
+ * Print `region` if LOG_LEVEL >= LOG_LEVEL_VERBOSE
+ */
+static void print_memory_region(struct ffa_memory_region *region)
+{
+ VERBOSE("region.sender = %d\n", region->sender);
+ VERBOSE("region.attributes.shareability = %d\n",
+ region->attributes.shareability);
+ VERBOSE("region.attributes.cacheability = %d\n",
+ region->attributes.cacheability);
+ VERBOSE("region.attributes.type = %d\n", region->attributes.type);
+ VERBOSE("region.attributes.security = %d\n",
+ region->attributes.security);
+ VERBOSE("region.flags = %d\n", region->flags);
+ VERBOSE("region.handle = %lld\n", region->handle);
+ VERBOSE("region.tag = %lld\n", region->tag);
+ VERBOSE("region.memory_access_desc_size = %d\n",
+ region->memory_access_desc_size);
+ VERBOSE("region.receiver_count = %d\n", region->receiver_count);
+ VERBOSE("region.receivers_offset = %d\n", region->receivers_offset);
+}
+
+/**
+ * Used by hypervisor retrieve request test: validate descriptors provided by
+ * SPMC.
+ */
+static bool verify_retrieve_response(const struct ffa_memory_region *region1,
+ const struct ffa_memory_region *region2)
+{
+ if (region1->sender != region2->sender) {
+ ERROR("region1.sender=%d, expected %d\n", region1->sender,
+ region2->sender);
+ return false;
+ }
+ if (region1->attributes.shareability != region2->attributes.shareability) {
+ ERROR("region1.attributes.shareability=%d, expected %d\n",
+ region1->attributes.shareability,
+ region2->attributes.shareability);
+ return false;
+ }
+ if (region1->attributes.cacheability != region2->attributes.cacheability) {
+ ERROR("region1.attributes.cacheability=%d, expected %d\n",
+ region1->attributes.cacheability,
+ region2->attributes.cacheability);
+ return false;
+ }
+ if (region1->attributes.type != region2->attributes.type) {
+ ERROR("region1.attributes.type=%d, expected %d\n",
+ region1->attributes.type, region2->attributes.type);
+ return false;
+ }
+ if (region1->attributes.security != region2->attributes.security) {
+ ERROR("region1.attributes.security=%d, expected %d\n",
+ region1->attributes.security, region2->attributes.security);
+ return false;
+ }
+ if (region1->flags != region2->flags) {
+ ERROR("region1->flags=%d, expected %d\n", region1->flags,
+ region2->flags);
+ return false;
+ }
+ if (region1->handle != region2->handle) {
+ ERROR("region1.handle=%lld, expected %lld\n", region1->handle,
+ region2->handle);
+ return false;
+ }
+ if (region1->tag != region2->tag) {
+ ERROR("region1.tag=%lld, expected %lld\n", region1->tag, region2->tag);
+ return false;
+ }
+ if (region1->memory_access_desc_size != region2->memory_access_desc_size) {
+ ERROR("region1.memory_access_desc_size=%d, expected %d\n",
+ region1->memory_access_desc_size,
+ region2->memory_access_desc_size);
+ return false;
+ }
+ if (region1->receiver_count != region2->receiver_count) {
+ ERROR("region1.receiver_count=%d, expected %d\n",
+ region1->receiver_count, region2->receiver_count);
+ return false;
+ }
+ if (region1->receivers_offset != region2->receivers_offset) {
+ ERROR("region1.receivers_offset=%d, expected %d\n",
+ region1->receivers_offset, region2->receivers_offset);
+ return false;
+ }
+ for (uint32_t i = 0; i < 3; i++) {
+ if (region1->reserved[i] != 0) {
+ ERROR("region.reserved[%d]=%d, expected 0\n", i,
+ region1->reserved[i]);
+ return false;
+ }
+ }
+ return true;
+}
+
+/**
+ * Used by hypervisor retrieve request test: validate descriptors provided by
+ * SPMC.
+ */
+static bool
+verify_constituent(struct ffa_memory_region_constituent *constituent,
+ void *address, uint32_t page_count)
+{
+ if (constituent->address != address) {
+ ERROR("constituent.address=%p, expected %p\n",
+ constituent->address, address);
+ return false;
+ }
+ if (constituent->page_count != page_count) {
+ ERROR("constituent.page_count=%d, expected %d\n",
+ constituent->page_count, page_count);
+ return false;
+ }
+ if (constituent->reserved != 0) {
+ ERROR("constituent.reserved=%d, expected 0\n",
+ constituent->reserved);
+ return false;
+ }
+ return true;
+}
+
+/**
+ * Used by hypervisor retrieve request test: validate descriptors provided by
+ * SPMC.
+ */
+static bool verify_composite(struct ffa_composite_memory_region *composite,
+ struct ffa_memory_region_constituent *constituent,
+ uint32_t page_count, uint32_t constituent_count)
+{
+ if (composite->page_count != page_count) {
+ ERROR("composite.page_count=%d, expected %d\n",
+ composite->page_count, page_count);
+ return false;
+ }
+ if (composite->constituent_count != constituent_count) {
+ ERROR("composite.constituent_count=%d, expected %d\n",
+ composite->constituent_count, constituent_count);
+ return false;
+ }
+ if (composite->reserved_0 != 0) {
+ ERROR("composite.reserved_0=%llu, expected 0\n",
+ composite->reserved_0);
+ return false;
+ }
+ for (uint32_t j = 0; j < composite->constituent_count; j++) {
+ if (!verify_constituent(constituent, share_page, 1)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+/**
+ * Helper for performing a hypervisor retrieve request test.
+ */
+static test_result_t hypervisor_retrieve_request_test_helper(uint32_t mem_func)
+{
+ struct ffa_memory_region_constituent sent_constituents[] = {{
+ .address = (void *)share_page,
+ .page_count = 1,
+ .reserved = 0,
+ }};
+ uint32_t sent_constituents_count = ARRAY_SIZE(sent_constituents);
+ __aligned(PAGE_SIZE) static uint8_t page[PAGE_SIZE * 2] = {0};
+ struct ffa_memory_region *hypervisor_retrieve_response =
+ (struct ffa_memory_region *)page;
+ struct ffa_memory_region expected_response;
+ struct mailbox_buffers mb;
+ ffa_memory_handle_t handle;
+ struct ffa_value ret;
+
+ uint32_t expected_flags = 0;
+
+ ffa_memory_attributes_t expected_attrs = {
+ .cacheability = FFA_MEMORY_CACHE_WRITE_BACK,
+ .shareability = FFA_MEMORY_INNER_SHAREABLE,
+ .security = FFA_MEMORY_SECURITY_NON_SECURE,
+ .type = (mem_func != FFA_MEM_SHARE_SMC32)
+ ? FFA_MEMORY_NOT_SPECIFIED_MEM
+ : FFA_MEMORY_NORMAL_MEM,
+ };
+
+ CHECK_SPMC_TESTING_SETUP(1, 2, expected_sp_uuids);
+ GET_TFTF_MAILBOX(mb);
+
+ switch (mem_func) {
+ case FFA_MEM_SHARE_SMC32:
+ expected_flags = FFA_MEMORY_REGION_TRANSACTION_TYPE_SHARE;
+ break;
+ case FFA_MEM_LEND_SMC32:
+ expected_flags = FFA_MEMORY_REGION_TRANSACTION_TYPE_LEND;
+ break;
+ case FFA_MEM_DONATE_SMC32:
+ expected_flags = FFA_MEMORY_REGION_TRANSACTION_TYPE_DONATE;
+ break;
+ default:
+ ERROR("Invalid mem_func: %d\n", mem_func);
+ panic();
+ }
+
+ handle = memory_init_and_send(mb.send, MAILBOX_SIZE, SENDER, RECEIVER,
+ sent_constituents,
+ sent_constituents_count, mem_func, &ret);
+ if (handle == FFA_MEMORY_HANDLE_INVALID) {
+ ERROR("Memory share failed: %d\n", ffa_error_code(ret));
+ return TEST_RESULT_FAIL;
+ }
+
+ /*
+ * Send Hypervisor Retrieve request according to section 17.4.3 of FFA
+ * v1.2-REL0 specification.
+ */
+ if (!hypervisor_retrieve_request(&mb, handle, page, sizeof(page))) {
+ return TEST_RESULT_FAIL;
+ }
+
+ print_memory_region(hypervisor_retrieve_response);
+
+ /*
+ * Verify the received `FFA_MEM_RETRIEVE_RESP` aligns with
+ * transaction description sent above.
+ */
+ expected_response = (struct ffa_memory_region){
+ .sender = SENDER,
+ .attributes = expected_attrs,
+ .flags = expected_flags,
+ .handle = handle,
+ .tag = 0,
+ .memory_access_desc_size = sizeof(struct ffa_memory_access),
+ .receiver_count = 1,
+ .receivers_offset =
+ offsetof(struct ffa_memory_region, receivers),
+ };
+ if (!verify_retrieve_response(hypervisor_retrieve_response, &expected_response)) {
+ return TEST_RESULT_FAIL;
+ }
+
+ {
+ uint32_t i = 0;
+ struct ffa_composite_memory_region *composite =
+ ffa_memory_region_get_composite(
+ hypervisor_retrieve_response, i);
+ if (composite == NULL) {
+ ERROR("composite %d is null\n", i);
+ return TEST_RESULT_FAIL;
+ }
+
+ if (!verify_composite(composite, &composite->constituents[i],
+ sent_constituents_count, sent_constituents_count)) {
+ return TEST_RESULT_FAIL;
+ }
+ }
+
+ /*
+ * Reclaim for the SPMC to deallocate any data related to the handle.
+ */
+ ret = ffa_mem_reclaim(handle, 0);
+ if (is_ffa_call_error(ret)) {
+ ERROR("Memory reclaim failed: %d\n", ffa_error_code(ret));
+ return TEST_RESULT_FAIL;
+ }
+
+ ret = ffa_rx_release();
+ if (is_ffa_call_error(ret)) {
+ ERROR("rx release failed: %d\n", ffa_error_code(ret));
+ return TEST_RESULT_FAIL;
+ }
+
+ return TEST_RESULT_SUCCESS;
+}
+
+test_result_t test_hypervisor_share_retrieve(void)
+{
+ return hypervisor_retrieve_request_test_helper(FFA_MEM_SHARE_SMC32);
+}
+
+test_result_t test_hypervisor_lend_retrieve(void)
+{
+ return hypervisor_retrieve_request_test_helper(FFA_MEM_LEND_SMC32);
+}
+
+test_result_t test_hypervisor_donate_retrieve(void)
+{
+ return hypervisor_retrieve_request_test_helper(FFA_MEM_DONATE_SMC32);
+}
diff --git a/tftf/tests/tests-spm.xml b/tftf/tests/tests-spm.xml
index e47039f..fbc1763 100644
--- a/tftf/tests/tests-spm.xml
+++ b/tftf/tests/tests-spm.xml
@@ -88,6 +88,12 @@
<testsuite name="FF-A Memory Sharing"
description="Test FF-A Memory Sharing ABIs" >
+ <testcase name="Hypervisor share + memory retrieve request"
+ function="test_hypervisor_share_retrieve" />
+ <testcase name="Hypervisor lend + memory retrieve request"
+ function="test_hypervisor_lend_retrieve" />
+ <testcase name="Hypervisor donate + memory retrieve request"
+ function="test_hypervisor_donate_retrieve" />
<testcase name="Lend Memory to Secure World"
function="test_mem_lend_sp" />
<testcase name="Lend memory, clear flag set"