test(spm): tests for RXTX_MAP/RXTX_UNMAP
Tests to check that RXTX_MAP and RXTX_UNMAP properly unmap/map the RX
and TX pages from the NWd stage 2 page tables.
Specifically:
* RXTX_MAP should fail when using secure memory
* RXTX_MAP should fail when using non-secure memory outside the regions
specified in SPMC nodes
* Memory sharing functions (lend, share, donate) should fail when using
memory that has been mapped by RXTX_MAP
* RXTX_UNMAP should fail when using different VM IDs
* Forwarded RXTX_MAP should succeed when the region is not mapped
* Two consecutive forwarded RXTX_MAPs should succeed when the regions
don't overlap and the endpoint IDs are different
* Forwarded RXTX_MAP should fail when the region is already mapped
* Memory sharing functions (lend, share, donate) should fail when using
memory that has been mapped by forwarded RXTX_MAP
Change-Id: I006681ab54f5ff602e862ae09438d0d174c8d0b0
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 0f19827..3ae43d2 100644
--- a/include/runtime_services/ffa_helpers.h
+++ b/include/runtime_services/ffa_helpers.h
@@ -497,6 +497,34 @@
ffa_memory_receiver_flags_t flags;
};
+/**
+ * Endpoint RX/TX descriptor, as defined by Table 13.27 in FF-A v1.1 EAC0.
+ * It's used by the Hypervisor to describe the RX/TX buffers mapped by a VM
+ * to the SPMC, in order to allow indirect messaging.
+ */
+struct ffa_endpoint_rxtx_descriptor {
+ ffa_id_t endpoint_id;
+ uint16_t reserved;
+
+ /*
+ * 8-byte aligned offset from the base address of this descriptor to the
+ * `ffa_composite_memory_region` describing the RX buffer.
+ */
+ uint32_t rx_offset;
+
+ /*
+ * 8-byte aligned offset from the base address of this descriptor to the
+ * `ffa_composite_memory_region` describing the TX buffer.
+ */
+ uint32_t tx_offset;
+
+ /* Pad to align on 16-byte boundary. */
+ uint32_t pad;
+};
+
+_Static_assert(sizeof(struct ffa_endpoint_rxtx_descriptor) == 16,
+ "ffa_endpoint_rx_tx_descriptor must be 16 bytes wide");
+
/** Flags to control the behaviour of a memory sharing transaction. */
typedef uint32_t ffa_memory_region_flags_t;
@@ -759,6 +787,7 @@
struct ffa_value ffa_partition_info_get(const struct ffa_uuid uuid);
struct ffa_value ffa_rx_release(void);
struct ffa_value ffa_rxtx_map(uintptr_t send, uintptr_t recv, uint32_t pages);
+struct ffa_value ffa_rxtx_unmap_with_id(uint32_t id);
struct ffa_value ffa_rxtx_unmap(void);
struct ffa_value ffa_mem_donate(uint32_t descriptor_length,
uint32_t fragment_length);
@@ -800,6 +829,14 @@
ffa_memory_receiver_flags_t flags,
struct ffa_memory_access_impdef *impdef);
+void ffa_endpoint_rxtx_descriptor_init(
+ struct ffa_endpoint_rxtx_descriptor *desc, ffa_id_t endpoint_id,
+ void *rx_address, void *tx_address);
+
+struct ffa_value ffa_rxtx_map_forward(
+ struct ffa_endpoint_rxtx_descriptor *desc, ffa_id_t endpoint_id,
+ void *rx_address, void *tx_address);
+
#endif /* __ASSEMBLY__ */
#endif /* FFA_HELPERS_H */
diff --git a/tftf/tests/runtime_services/secure_service/ffa_helpers.c b/tftf/tests/runtime_services/secure_service/ffa_helpers.c
index 8b53bb0..6db193c 100644
--- a/tftf/tests/runtime_services/secure_service/ffa_helpers.c
+++ b/tftf/tests/runtime_services/secure_service/ffa_helpers.c
@@ -495,15 +495,20 @@
/* Unmap the RXTX buffer allocated by the given FF-A component */
struct ffa_value ffa_rxtx_unmap(void)
{
+ return ffa_rxtx_unmap_with_id(0);
+}
+
+struct ffa_value ffa_rxtx_unmap_with_id(uint32_t id)
+{
struct ffa_value args = {
.fid = FFA_RXTX_UNMAP,
- .arg1 = FFA_PARAM_MBZ,
+ .arg1 = id << 16,
.arg2 = FFA_PARAM_MBZ,
.arg3 = FFA_PARAM_MBZ,
.arg4 = FFA_PARAM_MBZ,
.arg5 = FFA_PARAM_MBZ,
.arg6 = FFA_PARAM_MBZ,
- .arg7 = FFA_PARAM_MBZ
+ .arg7 = FFA_PARAM_MBZ,
};
return ffa_service_call(&args);
@@ -806,3 +811,76 @@
return access;
}
+
+/**
+ * Initialises the given `ffa_composite_memory_region` to be used for an
+ * `FFA_RXTX_MAP` forwarding in the case when Hypervisor needs the SPMC to map a
+ * VM's RXTX pair.
+ */
+static void
+ffa_composite_memory_region_init(struct ffa_composite_memory_region *composite,
+ void *address, uint32_t page_count)
+{
+ composite->page_count = page_count;
+ composite->constituent_count = 1;
+ composite->reserved_0 = 0;
+
+ composite->constituents[0].page_count = page_count;
+ composite->constituents[0].address = (void *)address;
+ composite->constituents[0].reserved = 0;
+}
+
+/**
+ * Initialises the given `ffa_endpoint_rx_tx_descriptor` to be used for an
+ * `FFA_RXTX_MAP` forwarding in the case when Hypervisor needs the SPMC to map a
+ * VM's RXTX pair.
+ *
+ * Each buffer is described by an `ffa_composite_memory_region` containing
+ * one `ffa_memory_region_constituent`.
+ */
+void ffa_endpoint_rxtx_descriptor_init(
+ struct ffa_endpoint_rxtx_descriptor *desc, ffa_id_t endpoint_id,
+ void *rx_address, void *tx_address)
+{
+ desc->endpoint_id = endpoint_id;
+ desc->reserved = 0;
+ desc->pad = 0;
+
+ /*
+ * RX's composite descriptor is allocated after the enpoint descriptor.
+ * `sizeof(struct ffa_endpoint_rx_tx_descriptor)` is guaranteed to be
+ * 16-byte aligned.
+ */
+ desc->rx_offset = sizeof(struct ffa_endpoint_rxtx_descriptor);
+
+ ffa_composite_memory_region_init(
+ (struct ffa_composite_memory_region *)((uintptr_t)desc +
+ desc->rx_offset),
+ rx_address, 1);
+
+ /*
+ * TX's composite descriptor is allocated after the RX descriptor.
+ * `sizeof(struct ffa_composite_memory_region)` and
+ * `sizeof(struct ffa_memory_region_constituent)` are guaranteed to be
+ * 16-byte aligned in ffa_memory.c.
+ */
+ desc->tx_offset = desc->rx_offset +
+ sizeof(struct ffa_composite_memory_region) +
+ sizeof(struct ffa_memory_region_constituent);
+
+ ffa_composite_memory_region_init(
+ (struct ffa_composite_memory_region *)((uintptr_t)desc +
+ desc->tx_offset),
+ tx_address, 1);
+}
+
+/**
+ * Mimics a forwarded FFA_RXTX_MAP call from a hypervisor.
+ */
+struct ffa_value ffa_rxtx_map_forward(struct ffa_endpoint_rxtx_descriptor *desc,
+ ffa_id_t endpoint_id,
+ void *rx_address, void *tx_address)
+{
+ ffa_endpoint_rxtx_descriptor_init(desc, endpoint_id, rx_address, tx_address);
+ return ffa_rxtx_map(0, 0, 0);
+}
diff --git a/tftf/tests/runtime_services/secure_service/test_ffa_setup_and_discovery.c b/tftf/tests/runtime_services/secure_service/test_ffa_setup_and_discovery.c
index 567ac48..3c03d5f 100644
--- a/tftf/tests/runtime_services/secure_service/test_ffa_setup_and_discovery.c
+++ b/tftf/tests/runtime_services/secure_service/test_ffa_setup_and_discovery.c
@@ -17,6 +17,20 @@
static bool should_skip_version_test;
+/*
+ * Used as the RX/TX buffers belonging to VM 1 in the forwarding FFA_RXTX_MAP
+ * tests.
+ */
+static __aligned(PAGE_SIZE) uint8_t vm1_rx_buffer[PAGE_SIZE];
+static __aligned(PAGE_SIZE) uint8_t vm1_tx_buffer[PAGE_SIZE];
+
+/*
+ * Used as the RX/TX buffers belonging to VM 2 in the forwarding FFA_RXTX_MAP
+ * tests.
+ */
+static __aligned(PAGE_SIZE) uint8_t vm2_rx_buffer[PAGE_SIZE];
+static __aligned(PAGE_SIZE) uint8_t vm2_tx_buffer[PAGE_SIZE];
+
static struct mailbox_buffers mb;
static const struct ffa_uuid sp_uuids[] = {
@@ -125,17 +139,15 @@
static test_result_t test_ffa_version(uint32_t input_version,
uint32_t expected_return)
{
- if (should_skip_version_test) {
+ if (should_skip_version_test)
return TEST_RESULT_SKIPPED;
- }
struct ffa_value ret_values = ffa_version(input_version);
uint32_t spm_version = (uint32_t)(0xFFFFFFFF & ret_values.fid);
- if (spm_version == expected_return) {
+ if (spm_version == expected_return)
return TEST_RESULT_SUCCESS;
- }
tftf_testcase_printf("Input Version: 0x%x\n"
"Return: 0x%x\nExpected: 0x%x\n",
@@ -251,6 +263,281 @@
return test_ffa_rxtx_map(FFA_ERROR);
}
+/**
+ * Test to verify that call to FFA_RXTX_MAP should fail when using
+ * secure memory.
+ */
+test_result_t test_ffa_rxtx_map_secure_memory_fail(void)
+{
+ uintptr_t send = 0x7200000;
+ uintptr_t recv = send + PAGE_SIZE;
+ struct ffa_value ret;
+
+ SKIP_TEST_IF_FFA_VERSION_LESS_THAN(1, 2);
+
+ /* Unmap mailbox to remove state from previous tests. */
+ reset_tftf_mailbox();
+
+ ret = ffa_rxtx_map(send, recv, 1);
+ if (!is_expected_ffa_error(ret, FFA_ERROR_DENIED))
+ return TEST_RESULT_FAIL;
+
+ return TEST_RESULT_SUCCESS;
+}
+
+/**
+ * Test to verify that call to FFA_RXTX_MAP should fail when using non-secure
+ * memory outside the non-secure regions specified in the SPMC manifest nodes.
+ */
+test_result_t test_ffa_rxtx_map_nonsecure_memory_fail(void)
+{
+ uintptr_t send = 0x0000880080001000;
+ uintptr_t recv = send + PAGE_SIZE;
+ struct ffa_value ret;
+
+ SKIP_TEST_IF_FFA_VERSION_LESS_THAN(1, 2);
+
+ /* Unmap mailbox to remove state from previous tests. */
+ reset_tftf_mailbox();
+
+ ret = ffa_rxtx_map(send, recv, 1);
+ if (!is_expected_ffa_error(ret, FFA_ERROR_DENIED))
+ return TEST_RESULT_FAIL;
+
+ return TEST_RESULT_SUCCESS;
+}
+
+/**
+ * Test to verify that calls to memory sharing functions should fail when the
+ * ranges have been mapped by FFA_RXTX_MAP.
+ */
+test_result_t test_ffa_rxtx_map_memory_share_fail(void)
+{
+
+ struct ffa_memory_region_constituent constituent = {
+ .page_count = 1,
+ .reserved = 0,
+ };
+ uint32_t mem_funcs[] = {
+ FFA_MEM_LEND_SMC32,
+ FFA_MEM_SHARE_SMC32,
+ FFA_MEM_DONATE_SMC32,
+ };
+ struct ffa_value ret;
+
+ SKIP_TEST_IF_FFA_VERSION_LESS_THAN(1, 2);
+ CONFIGURE_AND_MAP_MAILBOX(mb, PAGE_SIZE, ret);
+ if (is_ffa_call_error(ret))
+ return TEST_RESULT_FAIL;
+
+ constituent.address = mb.send;
+
+ for (uint32_t i = 0; i < ARRAY_SIZE(mem_funcs); i++) {
+ uint32_t mem_func = mem_funcs[i];
+ struct ffa_memory_access receiver =
+ ffa_memory_access_init_permissions_from_mem_func(
+ SP_ID(1), mem_func);
+ memory_init_and_send(mb.send, PAGE_SIZE, HYP_ID, &receiver, 1,
+ &constituent, 1, mem_func, &ret);
+ if (!is_expected_ffa_error(ret, FFA_ERROR_DENIED))
+ return TEST_RESULT_FAIL;
+ }
+
+ return TEST_RESULT_SUCCESS;
+}
+
+/**
+ * Test to verify that call to FFA_RXTX_UNMAP should fail when using a
+ * non-existent VM ID.
+ */
+test_result_t test_ffa_rxtx_unmap_nonexistent_vm_id_fail(void)
+{
+ struct ffa_value ret;
+
+ SKIP_TEST_IF_FFA_VERSION_LESS_THAN(1, 2);
+ reset_tftf_mailbox();
+
+ ret = ffa_rxtx_map((uintptr_t)vm1_tx_buffer, (uintptr_t)vm1_rx_buffer,
+ 1);
+ if (is_ffa_call_error(ret))
+ return TEST_RESULT_FAIL;
+
+ ret = ffa_rxtx_unmap_with_id(HYP_ID + 1);
+ if (!is_expected_ffa_error(ret, FFA_ERROR_INVALID_PARAMETER))
+ return TEST_RESULT_FAIL;
+
+ return TEST_RESULT_SUCCESS;
+}
+
+/**
+ * Test to verify that a forwarded FFA_RXTX_MAP call succeeds when the RX/TX
+ * regions have not already been mapped.
+ */
+test_result_t test_ffa_rxtx_map_forward_success(void)
+{
+ struct ffa_value ret;
+
+ SKIP_TEST_IF_FFA_VERSION_LESS_THAN(1, 2);
+ CONFIGURE_AND_MAP_MAILBOX(mb, PAGE_SIZE, ret);
+ if (is_ffa_call_error(ret))
+ return TEST_RESULT_FAIL;
+
+ ret = ffa_rxtx_map_forward(mb.send, VM_ID(1), vm1_rx_buffer,
+ vm1_tx_buffer);
+ if (!is_expected_ffa_return(ret, FFA_SUCCESS_SMC32))
+ return TEST_RESULT_FAIL;
+
+ ret = ffa_rxtx_unmap_with_id(VM_ID(1));
+ if (!is_expected_ffa_return(ret, FFA_SUCCESS_SMC32))
+ return TEST_RESULT_FAIL;
+
+ ret = ffa_rxtx_unmap();
+ if (!is_expected_ffa_return(ret, FFA_SUCCESS_SMC32))
+ return TEST_RESULT_FAIL;
+
+ return TEST_RESULT_SUCCESS;
+}
+
+/**
+ * Test to verify that consecutive forwarding of the FFA_RXTX_MAP call succeeds
+ * if using different VM IDs and different addresses.
+ */
+test_result_t test_ffa_rxtx_map_forward_consecutive_success(void)
+{
+ struct ffa_value ret;
+
+ SKIP_TEST_IF_FFA_VERSION_LESS_THAN(1, 2);
+ CONFIGURE_AND_MAP_MAILBOX(mb, PAGE_SIZE, ret);
+ if (!is_expected_ffa_return(ret, FFA_SUCCESS_SMC32))
+ return TEST_RESULT_FAIL;
+
+ ret = ffa_rxtx_map_forward(mb.send, VM_ID(1), vm1_rx_buffer,
+ vm1_tx_buffer);
+ if (!is_expected_ffa_return(ret, FFA_SUCCESS_SMC32))
+ return TEST_RESULT_FAIL;
+
+ ret = ffa_rxtx_map_forward(mb.send, VM_ID(2), vm2_rx_buffer,
+ vm2_tx_buffer);
+ if (!is_expected_ffa_return(ret, FFA_SUCCESS_SMC32))
+ return TEST_RESULT_FAIL;
+
+ ret = ffa_rxtx_unmap_with_id(VM_ID(1));
+ if (!is_expected_ffa_return(ret, FFA_SUCCESS_SMC32))
+ return TEST_RESULT_FAIL;
+
+ ret = ffa_rxtx_unmap_with_id(VM_ID(2));
+ if (!is_expected_ffa_return(ret, FFA_SUCCESS_SMC32))
+ return TEST_RESULT_FAIL;
+
+ ret = ffa_rxtx_unmap();
+ if (!is_expected_ffa_return(ret, FFA_SUCCESS_SMC32))
+ return TEST_RESULT_FAIL;
+
+ return TEST_RESULT_SUCCESS;
+}
+
+/**
+ * Test to verify that forwarding of the FFA_RXTX_MAP call with the VM's buffers
+ * fails if the hypervisor's RXTX buffers are not mapped.
+ */
+test_result_t test_ffa_rxtx_map_forward_unmapped_buffers_fail(void)
+{
+ struct ffa_value ret;
+
+ SKIP_TEST_IF_FFA_VERSION_LESS_THAN(1, 2);
+ /*
+ * Unmap mailbox to recreate case where hypervisor's buffers aren't
+ * mapped.
+ */
+ reset_tftf_mailbox();
+
+ ret = ffa_rxtx_map_forward(mb.send, VM_ID(1), vm1_rx_buffer,
+ vm1_tx_buffer);
+ if (!is_expected_ffa_error(ret, FFA_ERROR_INVALID_PARAMETER))
+ return TEST_RESULT_FAIL;
+
+ return TEST_RESULT_SUCCESS;
+}
+
+/**
+ * Test to verify that FFA_RXTX_MAP forwarding fails if trying to forward
+ * buffers that have already been forwarded.
+ */
+test_result_t test_ffa_rxtx_map_forward_different_ids_fail(void)
+{
+ struct ffa_value ret;
+
+ SKIP_TEST_IF_FFA_VERSION_LESS_THAN(1, 2);
+
+ CONFIGURE_AND_MAP_MAILBOX(mb, PAGE_SIZE, ret);
+ if (is_ffa_call_error(ret))
+ return TEST_RESULT_FAIL;
+
+ ret = ffa_rxtx_map_forward(mb.send, VM_ID(1), vm1_rx_buffer,
+ vm1_tx_buffer);
+ if (!is_expected_ffa_return(ret, FFA_SUCCESS_SMC32))
+ return TEST_RESULT_FAIL;
+
+ ret = ffa_rxtx_map_forward(mb.send, VM_ID(2), vm1_rx_buffer,
+ vm1_tx_buffer);
+ if (!is_expected_ffa_error(ret, FFA_ERROR_DENIED))
+ return TEST_RESULT_FAIL;
+
+ ret = ffa_rxtx_unmap_with_id(VM_ID(1));
+ if (!is_expected_ffa_return(ret, FFA_SUCCESS_SMC32))
+ return TEST_RESULT_FAIL;
+
+ return TEST_RESULT_SUCCESS;
+}
+
+/**
+ * Test to verify that calls to memory sharing functions should fail when the
+ * ranges have been mapped by a forwarded FFA_RXTX_MAP.
+ */
+test_result_t test_ffa_rxtx_map_forward_memory_share_fail(void)
+{
+
+ struct ffa_memory_region_constituent constituent = {
+ .page_count = 1,
+ .reserved = 0,
+ .address = vm1_tx_buffer,
+ };
+ uint32_t mem_funcs[] = {
+ FFA_MEM_LEND_SMC32,
+ FFA_MEM_SHARE_SMC32,
+ FFA_MEM_DONATE_SMC32,
+ };
+ struct ffa_value ret;
+
+ SKIP_TEST_IF_FFA_VERSION_LESS_THAN(1, 2);
+
+ ret = ffa_rxtx_map_forward(mb.send, VM_ID(1), vm1_rx_buffer,
+ vm1_tx_buffer);
+ if (!is_expected_ffa_return(ret, FFA_SUCCESS_SMC32))
+ return TEST_RESULT_FAIL;
+
+ for (uint32_t i = 0; i < ARRAY_SIZE(mem_funcs); i++) {
+ uint32_t mem_func = mem_funcs[i];
+ struct ffa_memory_access receiver =
+ ffa_memory_access_init_permissions_from_mem_func(
+ SP_ID(1), mem_func);
+ memory_init_and_send(mb.send, PAGE_SIZE, HYP_ID, &receiver, 1,
+ &constituent, 1, mem_func, &ret);
+ if (!is_expected_ffa_error(ret, FFA_ERROR_DENIED))
+ return TEST_RESULT_FAIL;
+ }
+
+ ret = ffa_rxtx_unmap_with_id(VM_ID(1));
+ if (!is_expected_ffa_return(ret, FFA_SUCCESS_SMC32))
+ return TEST_RESULT_FAIL;
+
+ ret = ffa_rxtx_unmap();
+ if (!is_expected_ffa_return(ret, FFA_SUCCESS_SMC32))
+ return TEST_RESULT_FAIL;
+
+ return TEST_RESULT_SUCCESS;
+}
+
static test_result_t test_ffa_rxtx_unmap(uint32_t expected_return)
{
struct ffa_value ret;
@@ -319,23 +606,11 @@
test_result_t test_ffa_rxtx_unmap_fail_if_sp(void)
{
struct ffa_value ret;
- struct ffa_value args;
CHECK_SPMC_TESTING_SETUP(1, 1, sp_uuids);
/* Invoked FFA_RXTX_UNMAP, providing the ID of an SP in w1. */
- args = (struct ffa_value) {
- .fid = FFA_RXTX_UNMAP,
- .arg1 = SP_ID(1) << 16,
- .arg2 = FFA_PARAM_MBZ,
- .arg3 = FFA_PARAM_MBZ,
- .arg4 = FFA_PARAM_MBZ,
- .arg5 = FFA_PARAM_MBZ,
- .arg6 = FFA_PARAM_MBZ,
- .arg7 = FFA_PARAM_MBZ
- };
-
- ret = ffa_service_call(&args);
+ ret = ffa_rxtx_unmap_with_id(SP_ID(1));
if (!is_expected_ffa_error(ret, FFA_ERROR_INVALID_PARAMETER)) {
return TEST_RESULT_FAIL;
diff --git a/tftf/tests/tests-spm.xml b/tftf/tests/tests-spm.xml
index 09e0fd7..69b656e 100644
--- a/tftf/tests/tests-spm.xml
+++ b/tftf/tests/tests-spm.xml
@@ -25,12 +25,30 @@
function="test_ffa_rxtx_map_success" />
<testcase name="FF-A RXTX Map API consecutive"
function="test_ffa_rxtx_map_fail" />
+ <testcase name="FF-A RXTX Map API secure memory"
+ function="test_ffa_rxtx_map_secure_memory_fail"/>
+ <testcase name="FF-A RXTX Map API non-secure memory"
+ function="test_ffa_rxtx_map_nonsecure_memory_fail"/>
+ <testcase name="FF-A RXTX Map API memory sharing"
+ function="test_ffa_rxtx_map_memory_share_fail"/>
+ <testcase name="FF-A RXTX Unmap API ID nonexistent"
+ function="test_ffa_rxtx_unmap_nonexistent_vm_id_fail"/>
<testcase name="FF-A RXTX Unmap API success"
function="test_ffa_rxtx_unmap_success" />
<testcase name="FF-A RXTX Unmap API consecutive"
function="test_ffa_rxtx_unmap_fail" />
<testcase name="FF-A RXTX remap unmapped region success"
function="test_ffa_rxtx_map_unmapped_success" />
+ <testcase name="FF-A RXTX map forward success"
+ function="test_ffa_rxtx_map_forward_success" />
+ <testcase name="FF-A RXTX map forward consecutive success"
+ function="test_ffa_rxtx_map_forward_consecutive_success" />
+ <testcase name="FF-A RXTX map forward with unmapped buffers fail"
+ function="test_ffa_rxtx_map_forward_unmapped_buffers_fail" />
+ <testcase name="FF-A RXTX map forward with different IDs fail"
+ function="test_ffa_rxtx_map_forward_different_ids_fail" />
+ <testcase name="FF-A RXTX map forward memory share fail"
+ function="test_ffa_rxtx_map_forward_memory_share_fail" />
<testcase name="FF-A RXTX unmap SP rxtx buffer"
function="test_ffa_rxtx_unmap_fail_if_sp" />
<testcase name="Test FFA_SPM_ID_GET"