test(SPM): GPF during FFA_MEM_FRAG_TX
Test that the SPMC recovers from a GPF when handling
FFA_MEM_FRAG_TX interface.
Change-Id: I5b98419b32cdfd26431b461aede96e88d238b78b
Signed-off-by: J-Alves <joao.alves@arm.com>
diff --git a/tftf/tests/runtime_services/secure_service/spm_common.c b/tftf/tests/runtime_services/secure_service/spm_common.c
index 4fac119..39c13b1 100644
--- a/tftf/tests/runtime_services/secure_service/spm_common.c
+++ b/tftf/tests/runtime_services/secure_service/spm_common.c
@@ -493,9 +493,8 @@
const struct ffa_memory_region_constituent constituents[],
uint32_t constituent_count, uint32_t remaining_constituent_count,
uint32_t sent_length, uint32_t total_length, bool allocator_is_spmc,
- struct ffa_value ret)
+ struct ffa_value *ret)
{
-
uint64_t handle;
uint64_t handle_mask;
uint64_t expected_handle_mask =
@@ -508,25 +507,24 @@
while (remaining_constituent_count != 0) {
VERBOSE("%s: %d constituents left to send.\n", __func__,
remaining_constituent_count);
- if (ret.fid != FFA_MEM_FRAG_RX) {
+ if (ret->fid != FFA_MEM_FRAG_RX) {
ERROR("ffa_mem_frax_tx() failed: %d\n",
- ffa_error_code(ret));
+ ffa_error_code(*ret));
return false;
}
if (fragment_handle == FFA_MEMORY_HANDLE_INVALID) {
- fragment_handle = ffa_frag_handle(ret);
- } else if (ffa_frag_handle(ret) != fragment_handle) {
- ERROR("%s: fragment handle mismatch: expected %llu, "
- "got %llu\n",
- __func__, fragment_handle, ffa_frag_handle(ret));
+ fragment_handle = ffa_frag_handle(*ret);
+ } else if (ffa_frag_handle(*ret) != fragment_handle) {
+ ERROR("%s: fragment handle mismatch: expected %llu, got %llu\n",
+ __func__, fragment_handle, ffa_frag_handle(*ret));
return false;
}
- if (ret.arg3 != sent_length) {
+ if (ret->arg3 != sent_length) {
ERROR("%s: fragment length mismatch: expected %u, got "
"%lu\n",
- __func__, sent_length, ret.arg3);
+ __func__, sent_length, ret->arg3);
return false;
}
@@ -536,7 +534,7 @@
remaining_constituent_count,
remaining_constituent_count, &fragment_length);
- ret = ffa_mem_frag_tx(fragment_handle, fragment_length);
+ *ret = ffa_mem_frag_tx(fragment_handle, fragment_length);
sent_length += fragment_length;
}
@@ -546,13 +544,13 @@
return false;
}
- if (ret.fid != FFA_SUCCESS_SMC32) {
+ if (ret->fid != FFA_SUCCESS_SMC32) {
ERROR("%s: ffa_mem_frax_tx() failed: %d\n", __func__,
- ffa_error_code(ret));
+ ffa_error_code(*ret));
return false;
}
- handle = ffa_mem_success_handle(ret);
+ handle = ffa_mem_success_handle(*ret);
handle_mask = (handle >> FFA_MEMORY_HANDLE_ALLOCATOR_SHIFT) &
FFA_MEMORY_HANDLE_ALLOCATOR_MASK;
@@ -612,7 +610,7 @@
if (!send_fragmented_memory_region(
send_buffer, constituents, constituent_count,
remaining_constituent_count, fragment_length, total_length,
- true, *ret)) {
+ true, ret)) {
return FFA_MEMORY_HANDLE_INVALID;
}
@@ -968,7 +966,6 @@
return false;
}
-
if (sender != NULL) {
*sender = source_vm_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 ad4040a..d9656db 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
@@ -1580,6 +1580,8 @@
return TEST_RESULT_FAIL;
}
+ ffa_rx_release();
+
return TEST_RESULT_SUCCESS;
}
@@ -1606,3 +1608,132 @@
{
return base_ffa_memory_retrieve_request_fail_buffer_realm(true, true);
}
+
+/**
+ * Do a memory sharing operation over two fragments.
+ * Before the 2nd fragment the TX buffer is set in the realm PAS.
+ * The SPMC should fault, recover from it and return ffa_error(FFA_ERROR_ABORTED).
+ */
+test_result_t test_ffa_memory_share_fragmented_tx_realm(void)
+{
+ struct mailbox_buffers mb;
+ uint32_t remaining_constituent_count = 0;
+ uint32_t total_length;
+ uint32_t fragment_length;
+ struct ffa_memory_access receiver = ffa_memory_access_init_permissions_from_mem_func(
+ SP_ID(1), FFA_MEM_SHARE_SMC32);
+ struct ffa_memory_region_constituent constituents[] = {
+ {(void *)four_share_pages, 4, 0},
+ {(void *)share_page, 1, 0}
+ };
+ struct ffa_value ffa_ret;
+ u_register_t ret_rmm;
+ test_result_t ret;
+ uint64_t handle;
+
+ if (get_armv9_2_feat_rme_support() == 0U) {
+ return TEST_RESULT_SKIPPED;
+ }
+
+ CHECK_SPMC_TESTING_SETUP(1, 2, expected_sp_uuids);
+
+ GET_TFTF_MAILBOX(mb);
+
+ register_custom_sync_exception_handler(data_abort_handler);
+
+ /* Only send one constituent to start with. */
+ remaining_constituent_count = ffa_memory_region_init(
+ (struct ffa_memory_region *)mb.send, MAILBOX_SIZE, SENDER,
+ &receiver, 1, constituents, ARRAY_SIZE(constituents), 0,
+ 0, FFA_MEMORY_NOT_SPECIFIED_MEM,
+ FFA_MEMORY_CACHE_WRITE_BACK,
+ FFA_MEMORY_INNER_SHAREABLE,
+ &total_length, &fragment_length);
+
+ /* It should have copied them all. */
+ if (remaining_constituent_count > 0) {
+ ERROR("Transaction descriptor initialization failed!\n");
+ ret = TEST_RESULT_FAIL;
+ goto exit;
+ }
+
+ /*
+ * Take the size of a constituent from the fragment to force the
+ * operation to be fragmented.
+ */
+ fragment_length -= sizeof(struct ffa_memory_region_constituent);
+
+ ffa_ret = ffa_mem_share(total_length, fragment_length);
+
+ if (!is_expected_ffa_return(ffa_ret, FFA_MEM_FRAG_RX)) {
+ ERROR("Expected %s after the memory share.\n",
+ ffa_func_name(FFA_MEM_FRAG_RX));
+ ret = TEST_RESULT_FAIL;
+ goto exit;
+ }
+
+ handle = ffa_frag_handle(ffa_ret);
+
+ if (handle == FFA_MEMORY_HANDLE_INVALID) {
+ ERROR("SPMC returned an invalid handle for the operation.\n");
+ ret = TEST_RESULT_FAIL;
+ goto exit;
+ }
+
+ /* Prepare the next fragment for the operation. */
+ remaining_constituent_count = ffa_memory_fragment_init(
+ mb.send, PAGE_SIZE, &constituents[1], 1, &fragment_length);
+
+ /*
+ * Delegate send/tx buffer to a realm. This should make memory sharing operation
+ * fail.
+ */
+ ret_rmm = host_rmi_granule_delegate((u_register_t)mb.send);
+
+ if (ret_rmm != 0UL) {
+ INFO("Delegate operation returns 0x%lx for address %p\n",
+ ret_rmm, mb.send);
+ ret = TEST_RESULT_FAIL;
+ goto exit;
+ }
+
+ ffa_ret = ffa_mem_frag_tx(handle, fragment_length);
+
+ if (!is_expected_ffa_error(ffa_ret, FFA_ERROR_ABORTED)) {
+ ret = TEST_RESULT_FAIL;
+ goto exit;
+ }
+
+ /* Undelegate to reestablish the same security state for PAS. */
+ ret_rmm = host_rmi_granule_undelegate((u_register_t)mb.send);
+ if (ret_rmm != 0UL) {
+ ERROR("Undelegate operation returns 0x%lx for address %llx\n",
+ ret_rmm, (uint64_t)mb.send);
+ ret = TEST_RESULT_FAIL;
+ goto exit;
+ }
+
+ /* This time test should pass. */
+ ffa_ret = ffa_mem_frag_tx(handle, fragment_length);
+
+ if (is_ffa_call_error(ffa_ret)) {
+ ret = TEST_RESULT_FAIL;
+ goto exit;
+ }
+
+ /* Reclaim memory to be able to reuse it. */
+ ffa_ret = ffa_mem_reclaim(handle, 0);
+
+ if (is_ffa_call_error(ffa_ret)) {
+ ERROR("Failed to reclaim memory to be used in next test\n");
+ ret = TEST_RESULT_FAIL;
+ goto exit;
+ }
+
+ ret = TEST_RESULT_SUCCESS;
+
+exit:
+ unregister_custom_sync_exception_handler();
+
+ return ret;
+}
diff --git a/tftf/tests/tests-memory-access.xml b/tftf/tests/tests-memory-access.xml
index 200c5dd..2de25da 100644
--- a/tftf/tests/tests-memory-access.xml
+++ b/tftf/tests/tests-memory-access.xml
@@ -83,6 +83,8 @@
function="test_ffa_hypervisor_retrieve_request_fail_tx_realm" />
<testcase name="FF-A Memory Relinquish, NWd TX buffer is in realm PAS"
function="test_ffa_memory_relinquish_fail_tx_realm" />
+ <testcase name="FF-A Memory Frag Tx, NWd TX buffer is in realm PAS"
+ function="test_ffa_memory_share_fragmented_tx_realm" />
</testsuite>
</testsuites>