test(spm): lend/donate realm memory
Validate if memory gets delegated to a realm PAS, it can't be used
in FF-A memory lend/donate from NS to SWd.
Signed-off-by: J-Alves <joao.alves@arm.com>
Change-Id: I16c9b7193b4ae8f56b1182e3b28d13a742bfcd4e
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 cd1cfb1..87180bf 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,18 +1,22 @@
/*
- * Copyright (c) 2020-2023, Arm Limited. All rights reserved.
+ * Copyright (c) 2020-2024, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "arch_features.h"
+#include "arch_helpers.h"
+#include "ffa_helpers.h"
#include "ffa_svc.h"
#include "stdint.h"
+#include "utils_def.h"
#include <debug.h>
#include "ffa_helpers.h"
#include <sync.h>
#include <cactus_test_cmds.h>
#include <ffa_endpoints.h>
+#include <host_realm_rmi.h>
#include <spm_common.h>
#include <spm_test_helpers.h>
#include <test_helpers.h>
@@ -39,6 +43,7 @@
/* Memory section to be used for memory share operations */
static __aligned(PAGE_SIZE) uint8_t
share_page[PAGE_SIZE * FRAGMENTED_SHARE_PAGE_COUNT];
+static __aligned(PAGE_SIZE) uint8_t donate_page[PAGE_SIZE];
static __aligned(PAGE_SIZE) uint8_t consecutive_donate_page[PAGE_SIZE];
static __aligned(PAGE_SIZE) uint8_t four_share_pages[PAGE_SIZE * 4];
@@ -105,7 +110,8 @@
if (EC_BITS(esr_elx) == EC_DABORT_CUR_EL) {
/* Synchronous data abort triggered by Granule protection */
if ((ISS_BITS(esr_elx) & ISS_DFSC_MASK) == DFSC_GPF_DABORT) {
- VERBOSE("%s GPF Data Abort caught\n", __func__);
+ VERBOSE("%s GPF Data Abort caught to address: %llx\n",
+ __func__, (uint64_t)read_far_el2());
gpc_abort_triggered = true;
return true;
}
@@ -114,6 +120,15 @@
return false;
}
+static bool get_gpc_abort_triggered(void)
+{
+ bool ret = gpc_abort_triggered;
+
+ gpc_abort_triggered = false;
+
+ return ret;
+}
+
/**
* Test invocation to FF-A memory sharing interfaces that should return in an
* error.
@@ -253,7 +268,7 @@
if (check_gpc_fault) {
unregister_custom_sync_exception_handler();
- if (!gpc_abort_triggered) {
+ if (!get_gpc_abort_triggered()) {
ERROR("No exception due to GPC for lend/donate with RME.\n");
return TEST_RESULT_FAIL;
}
@@ -292,7 +307,7 @@
test_result_t test_mem_donate_sp(void)
{
struct ffa_memory_region_constituent constituents[] = {
- {(void *)share_page, 1, 0}
+ {(void *)donate_page, 1, 0}
};
const uint32_t constituents_count = sizeof(constituents) /
sizeof(struct ffa_memory_region_constituent);
@@ -963,3 +978,186 @@
{
return hypervisor_retrieve_request_test_helper(FFA_MEM_LEND_SMC32, false, true);
}
+
+/**
+ * Test helper that performs memory sharing operation, and alters the PAS
+ * of the memory, to validate that SPM intersects the operation in case the PAS
+ * is not coherent with its use. Relevant for the functioning of FFA_MEM_LEND
+ * and FFA_MEM_DONATE from NWd to an SP.
+ *
+ * In cases the memory is not in NS state, the SPMC should intersect memory
+ * management call with an appropriate FFA_ERROR.
+ */
+static test_result_t test_ffa_mem_send_realm_expect_fail(
+ uint32_t mem_func, ffa_id_t borrower,
+ struct ffa_memory_region_constituent *constituents,
+ size_t constituents_count, uint64_t delegate_addr)
+{
+ struct ffa_value ret;
+ uint32_t remaining_constituent_count;
+ uint32_t total_length;
+ uint32_t fragment_length;
+ struct mailbox_buffers mb;
+ u_register_t ret_rmm;
+ test_result_t result = TEST_RESULT_FAIL;
+ struct ffa_memory_access receiver =
+ ffa_memory_access_init_permissions_from_mem_func(borrower,
+ mem_func);
+
+ if (get_armv9_2_feat_rme_support() == 0U) {
+ return TEST_RESULT_SKIPPED;
+ }
+
+ /***********************************************************************
+ * Check if SPMC has ffa_version and expected FFA endpoints are deployed.
+ **********************************************************************/
+ CHECK_SPMC_TESTING_SETUP(1, 2, expected_sp_uuids);
+
+ GET_TFTF_MAILBOX(mb);
+
+ register_custom_sync_exception_handler(data_abort_handler);
+
+ /*
+ * Delegate page to a realm. This should make memory sharing operation
+ * fail.
+ */
+ ret_rmm = host_rmi_granule_delegate((u_register_t)delegate_addr);
+
+ if (ret_rmm != 0UL) {
+ INFO("Delegate operation returns 0x%lx for address %llx\n",
+ ret_rmm, delegate_addr);
+ return TEST_RESULT_FAIL;
+ }
+
+ remaining_constituent_count = ffa_memory_region_init(
+ (struct ffa_memory_region *)mb.send, MAILBOX_SIZE, SENDER,
+ &receiver, 1, constituents, constituents_count, 0,
+ FFA_MEMORY_REGION_FLAG_CLEAR,
+ FFA_MEMORY_NOT_SPECIFIED_MEM, 0, 0,
+ &total_length, &fragment_length);
+
+ if (remaining_constituent_count != 0) {
+ goto out;
+ }
+
+ switch (mem_func) {
+ case FFA_MEM_LEND_SMC32:
+ ret = ffa_mem_lend(total_length, fragment_length);
+ break;
+ case FFA_MEM_DONATE_SMC32:
+ ret = ffa_mem_donate(total_length, fragment_length);
+ break;
+ default:
+ ERROR("Not expected for func name: %x\n", mem_func);
+ return TEST_RESULT_FAIL;
+ }
+
+ if (!is_expected_ffa_error(ret, FFA_ERROR_DENIED)) {
+ goto out;
+ }
+
+ /* Undelegate to reestablish the same security state for PAS. */
+ ret_rmm = host_rmi_granule_undelegate((u_register_t)delegate_addr);
+
+ for (uint32_t i = 0; i < constituents_count; i++) {
+ uint32_t *ptr = (uint32_t *)constituents[i].address;
+
+ *ptr = 0xFFA;
+ }
+
+ if (get_gpc_abort_triggered()) {
+ ERROR("Exception due to GPC for lend/donate with RME. Not"
+ " expected for this case.\n");
+ result = TEST_RESULT_FAIL;
+ } else {
+ result = TEST_RESULT_SUCCESS;
+ }
+out:
+ unregister_custom_sync_exception_handler();
+
+ if (ret_rmm != 0UL) {
+ INFO("Undelegate operation returns 0x%lx for address %llx\n",
+ ret_rmm, (uint64_t)delegate_addr);
+ return TEST_RESULT_FAIL;
+ }
+
+ return result;
+}
+
+/**
+ * Memory to be shared between partitions is described in a composite, with
+ * various constituents. In an RME system, the memory must be in NS PAS in
+ * operations from NWd to an SP. In case the PAS is not following this
+ * expectation memory lend/donate should fail, and all constituents must
+ * remain in the NS PAS.
+ *
+ * This test validates that if one page in the middle of one of the constituents
+ * is not in the NS PAS the operation fails.
+ */
+test_result_t test_ffa_mem_send_sp_realm_memory(void)
+{
+ test_result_t ret;
+ uint32_t mem_func[] = {FFA_MEM_LEND_SMC32, FFA_MEM_DONATE_SMC32};
+ struct ffa_memory_region_constituent constituents[] = {
+ {(void *)four_share_pages, 4, 0},
+ {(void *)share_page, 1, 0}
+ };
+
+ const uint32_t constituents_count = sizeof(constituents) /
+ sizeof(struct ffa_memory_region_constituent);
+
+ for (unsigned j = 0; j < ARRAY_SIZE(mem_func); j++) {
+ for (unsigned int i = 0; i < 4; i++) {
+ /* Address to be delegated to Realm PAS. */
+ uint64_t realm_addr =
+ (uint64_t)&four_share_pages[i * PAGE_SIZE];
+
+ INFO("%s memory with realm addr: %llx\n",
+ mem_func[j] == FFA_MEM_LEND_SMC32
+ ? "Lend"
+ : "Donate",
+ realm_addr);
+
+ ret = test_ffa_mem_send_realm_expect_fail(
+ mem_func[j], SP_ID(1), constituents,
+ constituents_count, realm_addr);
+
+ if (ret != TEST_RESULT_SUCCESS) {
+ break;
+ }
+ }
+ }
+
+ return ret;
+}
+
+/**
+ * Memory to be shared between partitions is described in a composite, with
+ * various constituents. In an RME system, the memory must be in NS PAS in
+ * operations from NWd to an SP. In case the PAS is not following this
+ * expectation memory lend/donate should fail, and all constituents must
+ * remain in the NS PAS.
+ *
+ * This test validates the case in which the memory lend/donate fail in
+ * case one of the constituents in the composite is not in the NS PAS.
+ */
+test_result_t test_ffa_mem_lend_sp_realm_memory_separate_constituent(void)
+{
+ test_result_t ret;
+ struct ffa_memory_region_constituent constituents[] = {
+ {(void *)four_share_pages, 4, 0},
+ {(void *)share_page, 1, 0}
+ };
+ const uint32_t constituents_count = sizeof(constituents) /
+ sizeof(struct ffa_memory_region_constituent);
+ /* Address to be delegated to Realm PAS. */
+ uint64_t realm_addr = (uint64_t)&share_page[0];
+
+ INFO("Sharing memory with realm addr: %llx\n", realm_addr);
+
+ ret = test_ffa_mem_send_realm_expect_fail(
+ FFA_MEM_LEND_SMC32, SP_ID(1), constituents,
+ constituents_count, realm_addr);
+
+ return ret;
+}
diff --git a/tftf/tests/tests-memory-access.xml b/tftf/tests/tests-memory-access.xml
index 42f21cc..4318cc9 100644
--- a/tftf/tests/tests-memory-access.xml
+++ b/tftf/tests/tests-memory-access.xml
@@ -53,6 +53,10 @@
function="rt_memory_cannot_be_accessed_in_rl" />
<testcase name="Share memory to an SP from a Root region"
function="rt_memory_cannot_be_accessed_in_s" />
+ <testcase name="FF-A memory share fails if using realm memory"
+ function="test_ffa_mem_send_sp_realm_memory" />
+ <testcase name="FF-A memory share fail realm memory other constituent"
+ function="test_ffa_mem_lend_sp_realm_memory_separate_constituent" />
</testsuite>
</testsuites>