test(cactus): prevent realm region access from swd

This change adds TFTF and cactus tests to check a realm region cannot
be accessed from secure world.
A non-secure buffer is delegated to realm PAS and shared to a secure
partition through FF-A memory sharing operations.
The SP retrieves the region from the SPM, maps it and attempts a write
access. The PE is expected to trigger a GPF data abort caught by a
custom exception handler.
Exception is trapped at S-EL1 within the secure partition because
Hafnium configures HCR_EL2.GPF=0 (and SCR_EL3.GPF=0).

Signed-off-by: Olivier Deprez <olivier.deprez@arm.com>
Change-Id: I8f855f394d0490b3584e60ceba4f3d2a20197495
diff --git a/include/runtime_services/spm_common.h b/include/runtime_services/spm_common.h
index 398d01a..f187ed7 100644
--- a/include/runtime_services/spm_common.h
+++ b/include/runtime_services/spm_common.h
@@ -129,7 +129,7 @@
 bool memory_retrieve(struct mailbox_buffers *mb,
 		     struct ffa_memory_region **retrieved, uint64_t handle,
 		     ffa_id_t sender, ffa_id_t receiver,
-		     uint32_t mem_func, ffa_memory_region_flags_t flags);
+		     ffa_memory_region_flags_t flags);
 
 /**
  * Helper to conduct a memory relinquish. The caller is usually the receiver,
diff --git a/spm/cactus/aarch64/cactus_exceptions.S b/spm/cactus/aarch64/cactus_exceptions.S
index 6aec16d..9b024f8 100644
--- a/spm/cactus/aarch64/cactus_exceptions.S
+++ b/spm/cactus/aarch64/cactus_exceptions.S
@@ -31,14 +31,16 @@
 	/*
 	 * Current EL with SPx : 0x200 - 0x400.
 	 */
-unhandled_exception sync_spx
+vector_entry sync_spx
+	b	sync_exception_vector_entry
+end_vector_entry sync_spx
 
 vector_entry irq_spx
-	b	irq_vector_entry
+	b	interrupt_vector_entry
 end_vector_entry irq_spx
 
 vector_entry fiq_spx
-	b	fiq_vector_entry
+	b	interrupt_vector_entry
 end_vector_entry fiq_spx
 
 unhandled_exception serr_spx
@@ -98,23 +100,30 @@
 	ldp	x0, x1, [sp, #0x0]
 .endm
 
-func irq_vector_entry
+func sync_exception_vector_entry
 	sub	sp, sp, #0x100
 	save_gp_regs
-	bl	cactus_interrupt_handler
-	restore_gp_regs
+	mov	x19, sp
+	bl	tftf_sync_exception_handler
+	cbnz	x0, 0f
+	mov x0, x19
+	/* Save original stack pointer value on the stack */
+	add	x1, x0, #0x100
+	str	x1, [x0, #0xf8]
+	b print_exception
+0:	restore_gp_regs
 	add	sp, sp, #0x100
 	eret
-endfunc irq_vector_entry
+endfunc sync_exception_vector_entry
 
-func fiq_vector_entry
+func interrupt_vector_entry
 	sub	sp, sp, #0x100
 	save_gp_regs
 	bl	cactus_interrupt_handler
 	restore_gp_regs
 	add	sp, sp, #0x100
 	eret
-endfunc fiq_vector_entry
+endfunc interrupt_vector_entry
 
 func crash_dump
 	/* Save general-purpose registers on the stack. */
diff --git a/spm/cactus/cactus.mk b/spm/cactus/cactus.mk
index 4109579..8970b29 100644
--- a/spm/cactus/cactus.mk
+++ b/spm/cactus/cactus.mk
@@ -68,6 +68,7 @@
 			lib/smc/${ARCH}/asm_smc.S			\
 			lib/smc/${ARCH}/smc.c				\
 			lib/smc/${ARCH}/hvc.c				\
+			lib/exceptions/${ARCH}/sync.c			\
 			lib/locks/${ARCH}/spinlock.S			\
 			lib/utils/mp_printf.c				\
 			${XLAT_TABLES_LIB_SRCS}
diff --git a/spm/cactus/cactus_main.c b/spm/cactus/cactus_main.c
index c80abd9..58186d1 100644
--- a/spm/cactus/cactus_main.c
+++ b/spm/cactus/cactus_main.c
@@ -181,13 +181,6 @@
 	tftf_smc(&args);
 }
 
-int tftf_irq_handler_dispatcher(void)
-{
-	ERROR("%s\n", __func__);
-
-	return 0;
-}
-
 void __dead2 cactus_main(bool primary_cold_boot)
 {
 	assert(IS_IN_EL1() != 0);
diff --git a/spm/cactus/cactus_tests/cactus_test_memory_sharing.c b/spm/cactus/cactus_tests/cactus_test_memory_sharing.c
index 69d62dd..051208e 100644
--- a/spm/cactus/cactus_tests/cactus_test_memory_sharing.c
+++ b/spm/cactus/cactus_tests/cactus_test_memory_sharing.c
@@ -14,6 +14,27 @@
 #include <sp_helpers.h>
 #include <xlat_tables_defs.h>
 #include <lib/xlat_tables/xlat_tables_v2.h>
+#include <sync.h>
+
+static volatile uint32_t data_abort_gpf_triggered;
+
+static bool data_abort_gpf_handler(void)
+{
+	uint64_t esr_el1 = read_esr_el1();
+
+	VERBOSE("%s count %u esr_el1 %llx elr_el1 %llx\n",
+		__func__, data_abort_gpf_triggered, esr_el1,
+		read_elr_el1());
+
+	/* Expect a data abort because of a GPF. */
+	if ((EC_BITS(esr_el1) == EC_DABORT_CUR_EL) &&
+	    ((ISS_BITS(esr_el1) & ISS_DFSC_MASK) == DFSC_GPF_DABORT)) {
+		data_abort_gpf_triggered++;
+		return true;
+	}
+
+	return false;
+}
 
 /**
  * Each Cactus SP has a memory region dedicated to memory sharing tests
@@ -51,7 +72,7 @@
 					 cactus_mem_send_get_retrv_flags(*args);
 	uint32_t words_to_write = cactus_mem_send_words_to_write(*args);
 
-	expect(memory_retrieve(mb, &m, handle, source, vm_id, mem_func,
+	expect(memory_retrieve(mb, &m, handle, source, vm_id,
 			       retrv_flags), true);
 
 	composite = ffa_memory_region_get_composite(m, 0);
@@ -104,12 +125,17 @@
 		}
 	}
 
+	data_abort_gpf_triggered = 0;
+	register_custom_sync_exception_handler(data_abort_gpf_handler);
+
 	/* Write mem_func to retrieved memory region for validation purposes. */
 	VERBOSE("Writing: %x\n", mem_func);
 	for (unsigned int i = 0U; i < words_to_write; i++) {
 		ptr[i] = mem_func;
 	}
 
+	unregister_custom_sync_exception_handler();
+
 	/*
 	 * A FFA_MEM_DONATE changes the ownership of the page, as such no
 	 * relinquish is needed.
@@ -120,7 +146,7 @@
 			composite->constituents[0].page_count * PAGE_SIZE);
 
 		if (ret != 0) {
-			ERROR("Failed first mmap_add_dynamic_region!\n");
+			ERROR("Failed to unmap received memory region(%d)!\n", ret);
 			return cactus_error_resp(vm_id, source,
 						 CACTUS_ERROR_TEST);
 		}
@@ -139,7 +165,7 @@
 	}
 
 	return cactus_success_resp(vm_id,
-				   source, 0);
+				   source, data_abort_gpf_triggered);
 }
 
 CACTUS_CMD_HANDLER(req_mem_send_cmd, CACTUS_REQ_MEM_SEND_CMD)
diff --git a/tftf/tests/runtime_services/secure_service/spm_common.c b/tftf/tests/runtime_services/secure_service/spm_common.c
index aa1d2ed..fa4d1bc 100644
--- a/tftf/tests/runtime_services/secure_service/spm_common.c
+++ b/tftf/tests/runtime_services/secure_service/spm_common.c
@@ -351,7 +351,7 @@
 bool memory_retrieve(struct mailbox_buffers *mb,
 		     struct ffa_memory_region **retrieved, uint64_t handle,
 		     ffa_id_t sender, ffa_id_t receiver,
-		     uint32_t mem_func, ffa_memory_region_flags_t flags)
+		     ffa_memory_region_flags_t flags)
 {
 	smc_ret_values ret;
 	uint32_t fragment_size;
diff --git a/tftf/tests/runtime_services/secure_service/test_ffa_exceptions.c b/tftf/tests/runtime_services/secure_service/test_ffa_exceptions.c
new file mode 100644
index 0000000..f68a527
--- /dev/null
+++ b/tftf/tests/runtime_services/secure_service/test_ffa_exceptions.c
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2022, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <arch_helpers.h>
+#include <cactus_test_cmds.h>
+#include <debug.h>
+#include <ffa_endpoints.h>
+#include <ffa_svc.h>
+#include <irq.h>
+#include <platform.h>
+#include <runtime_services/realm_payload/realm_payload_test.h>
+#include <smccc.h>
+#include <spm_common.h>
+#include <test_helpers.h>
+
+#define SENDER HYP_ID
+#define RECEIVER SP_ID(1)
+
+static __aligned(PAGE_SIZE) uint64_t share_page[PAGE_SIZE / sizeof(uint64_t)];
+
+static const struct ffa_uuid expected_sp_uuids[] = {
+		{PRIMARY_UUID}, {SECONDARY_UUID}, {TERTIARY_UUID}
+};
+
+/**
+ * @Test_Aim@ Check a realm region cannot be accessed from a secure partition.
+ *
+ * This test shares a TFTF allocated buffer with a secure partition through
+ * FF-A memory sharing operation. The buffer is initially marked NS in the GPT
+ * and transitioned to realm after sharing. Then, the SP is invoked to retrieve
+ * the region (map it to its S2 translation regime), and maps it to its secure
+ * S1 translation regime. It then attempts a read access which results in the
+ * PE triggering a GPF caught by a custom synchronous abort handler.
+ *
+ */
+test_result_t rl_memory_cannot_be_accessed_in_s(void)
+{
+	struct ffa_memory_region_constituent constituents[] = {
+		{
+			(void *)share_page, 1, 0
+		}
+	};
+	const uint32_t constituents_count = sizeof(constituents) /
+		sizeof(struct ffa_memory_region_constituent);
+	ffa_memory_handle_t handle;
+	struct mailbox_buffers mb;
+	smc_ret_values ret;
+	u_register_t retmm;
+
+	if (get_armv9_2_feat_rme_support() == 0U) {
+		return TEST_RESULT_SKIPPED;
+	}
+
+	CHECK_SPMC_TESTING_SETUP(1, 1, expected_sp_uuids);
+
+	GET_TFTF_MAILBOX(mb);
+
+	handle = memory_init_and_send((struct ffa_memory_region *)mb.send,
+					PAGE_SIZE, SENDER, RECEIVER,
+					constituents, constituents_count,
+					FFA_MEM_SHARE_SMC32, &ret);
+
+	if (handle == FFA_MEMORY_HANDLE_INVALID) {
+		return TEST_RESULT_FAIL;
+	}
+
+	VERBOSE("TFTF - Handle: %llx Address: %p\n",
+		handle, constituents[0].address);
+
+	/* Delegate the shared page to Realm. */
+	retmm = realm_granule_delegate((u_register_t)&share_page);
+	if (retmm != 0UL) {
+		ERROR("Granule delegate failed!\n");
+		return TEST_RESULT_FAIL;
+	}
+
+	/* Retrieve the shared page and attempt accessing it. */
+	ret = cactus_mem_send_cmd(SENDER, RECEIVER, FFA_MEM_SHARE_SMC32,
+				  handle, 0, 1);
+
+	/* Undelegate the shared page. */
+	retmm = realm_granule_undelegate((u_register_t)&share_page);
+	if (retmm != 0UL) {
+		ERROR("Granule undelegate failed!\n");
+		return TEST_RESULT_FAIL;
+	}
+
+	if (is_ffa_call_error(ffa_mem_reclaim(handle, 0))) {
+		ERROR("Memory reclaim failed!\n");
+		return TEST_RESULT_FAIL;
+	}
+
+	/*
+	 * Expect success response with value 1 hinting an exception
+	 * triggered while the SP accessed the region.
+	 */
+	if (!(cactus_get_response(ret) == CACTUS_SUCCESS &&
+	      cactus_error_code(ret) == 1)) {
+		ERROR("Exceptions test failed!\n");
+		return TEST_RESULT_FAIL;
+	}
+
+	return TEST_RESULT_SUCCESS;
+}
diff --git a/tftf/tests/tests-spm.mk b/tftf/tests/tests-spm.mk
index 79cfb61..941758c 100644
--- a/tftf/tests/tests-spm.mk
+++ b/tftf/tests/tests-spm.mk
@@ -16,4 +16,10 @@
 		test_ffa_notifications.c				\
 		test_spm_cpu_features.c					\
 		test_spm_smmu.c						\
+		test_ffa_exceptions.c					\
+	)
+
+TESTS_SOURCES	+=							\
+	$(addprefix tftf/tests/runtime_services/realm_payload/,		\
+		realm_payload_test_helpers.c				\
 	)
diff --git a/tftf/tests/tests-spm.xml b/tftf/tests/tests-spm.xml
index cc79ea0..9a78c8a 100644
--- a/tftf/tests/tests-spm.xml
+++ b/tftf/tests/tests-spm.xml
@@ -44,6 +44,14 @@
                function="test_ffa_partition_info" />
   </testsuite>
 
+  <testsuite name="SP exceptions"
+             description="SP exceptions" >
+
+     <testcase name="Access from a SP to a Realm region"
+               function="rl_memory_cannot_be_accessed_in_s" />
+
+  </testsuite>
+
   <testsuite name="FF-A Direct messaging"
              description="Test FF-A Direct messaging" >