test(tftf): test Secure interrupt can preempt Realm EL1

- Send a direct message request command to first Cactus SP to start the
  trusted watchdog timer.
- Create and execute a busy loop to sleep the PE in the realm world for
  REALM_TIME_SLEEP ms.
- Trusted watchdog timer expires during this time which leads to secure
  interrupt being triggered while cpu is executing in realm world.
- Realm EL1 exits to host, but because the FIQ is still pending,
  the Host will be pre-empted to EL3.
- Once the SP handles the interrupt, it returns execution back to normal
  world
- TFTF parses REC's exit reason(FIQ in this case)
- TFTF sends direct message request command to first Cactus SP to query
  last serviced interrupt and verifies it is Trusted watchdog interrupt.
- TFTF disables watchdog and destroys Realm payload on exit.

Signed-off-by: Nabil Kahlouche <nabil.kahlouche@arm.com>
Change-Id: I6f4cfd334777000d33924bb1239b77182a3dcea6
diff --git a/include/runtime_services/host_realm_managment/host_realm_helper.h b/include/runtime_services/host_realm_managment/host_realm_helper.h
index 6269efd..8e6d602 100644
--- a/include/runtime_services/host_realm_managment/host_realm_helper.h
+++ b/include/runtime_services/host_realm_managment/host_realm_helper.h
@@ -19,7 +19,7 @@
 		u_register_t ns_shared_mem_adr,
 		u_register_t ns_shared_mem_size);
 bool host_destroy_realm(void);
-bool host_enter_realm_execute(uint8_t cmd, struct realm **realm_ptr);
+bool host_enter_realm_execute(uint8_t cmd, struct realm **realm_ptr, int test_exit_reason);
 test_result_t host_cmp_result(void);
 
 #endif /* HOST_REALM_HELPER_H */
diff --git a/tftf/framework/framework.mk b/tftf/framework/framework.mk
index aff0fef..6f4022d 100644
--- a/tftf/framework/framework.mk
+++ b/tftf/framework/framework.mk
@@ -25,6 +25,7 @@
 	-Iinclude/runtime_services			\
 	-Iinclude/runtime_services/secure_el0_payloads	\
 	-Iinclude/runtime_services/secure_el1_payloads	\
+	-Iinclude/runtime_services/host_realm_managment	\
 	-Ispm/cactus					\
 	-Ispm/ivy					\
 	-Irealm						\
diff --git a/tftf/tests/runtime_services/host_realm_managment/host_realm_helper.c b/tftf/tests/runtime_services/host_realm_managment/host_realm_helper.c
index 4787da4..e529c29 100644
--- a/tftf/tests/runtime_services/host_realm_managment/host_realm_helper.c
+++ b/tftf/tests/runtime_services/host_realm_managment/host_realm_helper.c
@@ -25,8 +25,6 @@
 static bool realm_payload_created;
 static bool shared_mem_created;
 static bool realm_payload_mmaped;
-static u_register_t exit_reason = RMI_EXIT_INVALID;
-static unsigned int host_call_result = TEST_RESULT_FAIL;
 static volatile bool timer_enabled;
 
 /* From the TFTF_BASE offset, memory used by TFTF + Shared + Realm + POOL should
@@ -310,10 +308,10 @@
 	return true;
 }
 
-bool host_enter_realm_execute(uint8_t cmd, struct realm **realm_ptr)
+bool host_enter_realm_execute(uint8_t cmd, struct realm **realm_ptr, int test_exit_reason)
 {
-	exit_reason = RMI_EXIT_INVALID;
-	host_call_result = TEST_RESULT_FAIL;
+	u_register_t exit_reason = RMI_EXIT_INVALID;
+	unsigned int host_call_result = TEST_RESULT_FAIL;
 
 	realm_shared_data_set_realm_cmd(cmd);
 	if (!host_enter_realm(&exit_reason, &host_call_result)) {
@@ -324,13 +322,14 @@
 		*realm_ptr = &realm;
 	}
 
-	if (((exit_reason == RMI_EXIT_IRQ) &&
-	     (cmd == REALM_PMU_INTERRUPT)) ||
-	    ((exit_reason == RMI_EXIT_HOST_CALL) &&
-	     (host_call_result == TEST_RESULT_SUCCESS))) {
+	if ((exit_reason == RMI_EXIT_HOST_CALL) && (host_call_result == TEST_RESULT_SUCCESS)) {
 		return true;
 	}
 
+	if (test_exit_reason == exit_reason) {
+		 return true;
+	}
+
 	if (exit_reason <= RMI_EXIT_SERROR) {
 		ERROR("%s(%u) RMI_EXIT_%s host_call_result=%u\n",
 		__func__, cmd, rmi_exit[exit_reason], host_call_result);
diff --git a/tftf/tests/runtime_services/realm_payload/host_realm_payload_tests.c b/tftf/tests/runtime_services/realm_payload/host_realm_payload_tests.c
index 42fab1d..55a333a 100644
--- a/tftf/tests/runtime_services/realm_payload/host_realm_payload_tests.c
+++ b/tftf/tests/runtime_services/realm_payload/host_realm_payload_tests.c
@@ -63,7 +63,7 @@
 	}
 
 	realm_shared_data_set_host_val(HOST_SLEEP_INDEX, SLEEP_TIME_MS);
-	ret1 = host_enter_realm_execute(REALM_SLEEP_CMD, NULL);
+	ret1 = host_enter_realm_execute(REALM_SLEEP_CMD, NULL, RMI_EXIT_HOST_CALL);
 	ret2 = host_destroy_realm();
 
 	if (!ret1 || !ret2) {
@@ -172,7 +172,7 @@
 		return TEST_RESULT_FAIL;
 	}
 
-	ret1 = host_enter_realm_execute(cmd, &realm_ptr);
+	ret1 = host_enter_realm_execute(cmd, &realm_ptr, RMI_EXIT_IRQ);
 	if (!ret1 || (cmd != REALM_PMU_INTERRUPT)) {
 		goto test_exit;
 	}
diff --git a/tftf/tests/runtime_services/realm_payload/host_realm_spm.c b/tftf/tests/runtime_services/realm_payload/host_realm_spm.c
new file mode 100644
index 0000000..de5413f
--- /dev/null
+++ b/tftf/tests/runtime_services/realm_payload/host_realm_spm.c
@@ -0,0 +1,199 @@
+/*
+ * Copyright (c) 2022, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <stdlib.h>
+
+#include <cactus_test_cmds.h>
+#include <ffa_endpoints.h>
+#include <ffa_helpers.h>
+#include <host_realm_helper.h>
+#include <host_realm_mem_layout.h>
+#include <host_shared_data.h>
+#include <test_helpers.h>
+
+#define REALM_TIME_SLEEP	300U
+#define SENDER HYP_ID
+#define RECEIVER SP_ID(1)
+static const struct ffa_uuid expected_sp_uuids[] = { {PRIMARY_UUID} };
+static struct mailbox_buffers mb;
+static bool secure_mailbox_initialised;
+
+/*
+ * This function helps to Initialise secure_mailbox, creates realm payload and
+ * shared memory to be used between Host and Realm.
+ * Skip test if RME is not supported or not the right RMM version is begin used
+ */
+static test_result_t init_test(void)
+{
+	u_register_t retrmm;
+
+
+	/* Verify that FFA is there and that it has the correct version. */
+	SKIP_TEST_IF_AARCH32();
+	SKIP_TEST_IF_FFA_VERSION_LESS_THAN(1, 1);
+
+	if (!secure_mailbox_initialised) {
+		GET_TFTF_MAILBOX(mb);
+		CHECK_SPMC_TESTING_SETUP(1, 1, expected_sp_uuids);
+		secure_mailbox_initialised = true;
+	}
+
+	if (get_armv9_2_feat_rme_support() == 0U) {
+		return TEST_RESULT_SKIPPED;
+	}
+
+	retrmm = host_rmi_version();
+	VERBOSE("RMM version is: %lu.%lu\n",
+			RMI_ABI_VERSION_GET_MAJOR(retrmm),
+			RMI_ABI_VERSION_GET_MINOR(retrmm));
+	/*
+	 * Skip the test if RMM is TRP, TRP version is always null.
+	 */
+	if (retrmm == 0UL) {
+		return TEST_RESULT_SKIPPED;
+	}
+
+	/*
+	 * Initialise Realm payload
+	 */
+	if (!host_create_realm_payload((u_register_t)REALM_IMAGE_BASE,
+			(u_register_t)PAGE_POOL_BASE,
+			(u_register_t)(PAGE_POOL_MAX_SIZE +
+			NS_REALM_SHARED_MEM_SIZE),
+			(u_register_t)PAGE_POOL_MAX_SIZE, 0UL)) {
+		return TEST_RESULT_FAIL;
+	}
+
+	/*
+	 * Create shared memory between Host and Realm
+	 */
+	if (!host_create_shared_mem(NS_REALM_SHARED_MEM_BASE,
+			NS_REALM_SHARED_MEM_SIZE)) {
+		return TEST_RESULT_FAIL;
+	}
+
+	return TEST_RESULT_SUCCESS;
+}
+
+static bool host_realm_handle_fiq_exit(struct realm *realm_ptr)
+{
+	struct rmi_rec_run *run = (struct rmi_rec_run *)realm_ptr->run;
+	if (run->exit.exit_reason == RMI_EXIT_FIQ) {
+		return true;
+	}
+	return false;
+}
+
+/*
+ * @Test_Aim@ Test secure interrupt handling while Secure Partition is in waiting
+ * state and Realm world runs a busy loop at R-EL1.
+ *
+ * 1. Send a direct message request command to first Cactus SP to start the
+ *    trusted watchdog timer.
+ *
+ * 2. Once the SP returns with a direct response message, it moves to WAITING
+ *    state.
+ *
+ * 3. Create and execute a busy loop to sleep the PE in the realm world for
+ *    REALM_TIME_SLEEP ms.
+ *
+ * 4. Trusted watchdog timer expires during this time which leads to secure
+ *    interrupt being triggered while cpu is executing in realm world.
+ *
+ * 5. Realm EL1 exits to host, but because the FIQ is still pending,
+ *    the Host will be pre-empted to EL3.
+ *
+ * 6. The interrupt is trapped to BL31/SPMD as FIQ and later synchronously
+ *    delivered to SPM.
+ *
+ * 7. SPM injects a virtual IRQ to first Cactus Secure Partition.
+ *
+ * 8. Once the SP has handled the interrupt, it returns execution back to normal
+ *    world using FFA_MSG_WAIT call.
+ *
+ * 9. TFTF parses REC's exit reason (FIQ in this case).
+ *
+ * 10. TFTF sends a direct request message to SP to query the ID of last serviced
+ *     secure virtual interrupt.
+ *
+ * 121. Further, TFTF expects SP to return the ID of Trusted Watchdog timer
+ *     interrupt through a direct response message.
+ *
+ * 13. Test finishes successfully once the TFTF disables the trusted watchdog
+ *     interrupt through a direct message request command.
+ *
+ * 14. TFTF then proceed to destroy the Realm.
+ *
+ */
+test_result_t host_realm_sec_interrupt_can_preempt_rl(void)
+{
+	struct realm *realm_ptr;
+	struct ffa_value ret_values;
+	test_result_t res;
+
+	res = init_test();
+	if (res != TEST_RESULT_SUCCESS) {
+		return res;
+	}
+
+	/* Enable trusted watchdog interrupt as IRQ in the secure side. */
+	if (!enable_trusted_wdog_interrupt(SENDER, RECEIVER)) {
+		return TEST_RESULT_FAIL;
+	}
+
+	/*
+	 * Send a message to SP1 through direct messaging.
+	 */
+	ret_values = cactus_send_twdog_cmd(SENDER, RECEIVER, (REALM_TIME_SLEEP/2));
+
+	if (!is_ffa_direct_response(ret_values)) {
+		ERROR("Expected a direct response for starting TWDOG timer\n");
+		return TEST_RESULT_FAIL;
+	}
+
+	/*
+	 * Spin Realm payload for REALM_TIME_SLEEP ms, This ensures secure wdog
+	 * timer triggers during this time.
+	 */
+	realm_shared_data_set_host_val(HOST_SLEEP_INDEX, REALM_TIME_SLEEP);
+	host_enter_realm_execute(REALM_SLEEP_CMD, &realm_ptr, RMI_EXIT_FIQ);
+
+	/*
+	 * Check if Realm exit reason is FIQ.
+	 */
+	if (!host_realm_handle_fiq_exit(realm_ptr)) {
+		ERROR("Trusted watchdog timer interrupt not fired\n");
+		host_destroy_realm();
+		return TEST_RESULT_FAIL;
+	}
+
+	/* Check for the last serviced secure virtual interrupt. */
+	ret_values = cactus_get_last_interrupt_cmd(SENDER, RECEIVER);
+
+	if (!is_ffa_direct_response(ret_values)) {
+		ERROR("Expected a direct response for last serviced interrupt"
+			" command\n");
+		return TEST_RESULT_FAIL;
+	}
+
+	/* Make sure Trusted Watchdog timer interrupt was serviced*/
+	if (cactus_get_response(ret_values) != IRQ_TWDOG_INTID) {
+		ERROR("Trusted watchdog timer interrupt not serviced by SP\n");
+		return TEST_RESULT_FAIL;
+	}
+
+	/* Disable Trusted Watchdog interrupt. */
+	if (!disable_trusted_wdog_interrupt(SENDER, RECEIVER)) {
+		return TEST_RESULT_FAIL;
+	}
+
+	if (!host_destroy_realm()) {
+		ERROR("host_destroy_realm error\n");
+		return TEST_RESULT_FAIL;
+	}
+
+	return TEST_RESULT_SUCCESS;
+}
diff --git a/tftf/tests/tests-realm-payload.mk b/tftf/tests/tests-realm-payload.mk
index 764a51e..b6f45ed 100644
--- a/tftf/tests/tests-realm-payload.mk
+++ b/tftf/tests/tests-realm-payload.mk
@@ -10,6 +10,7 @@
 TESTS_SOURCES	+=							\
 	$(addprefix tftf/tests/runtime_services/realm_payload/,		\
 		host_realm_payload_tests.c				\
+		host_realm_spm.c					\
 	)
 
 TESTS_SOURCES	+=							\
@@ -22,6 +23,13 @@
 	)
 
 TESTS_SOURCES	+=							\
+	$(addprefix tftf/tests/runtime_services/secure_service/,	\
+		${ARCH}/ffa_arch_helpers.S				\
+		ffa_helpers.c						\
+		spm_common.c						\
+	)
+
+TESTS_SOURCES	+=							\
 	$(addprefix lib/heap/,						\
 		page_alloc.c						\
 	)
diff --git a/tftf/tests/tests-realm-payload.xml b/tftf/tests/tests-realm-payload.xml
index e84929a..b3d86ad 100644
--- a/tftf/tests/tests-realm-payload.xml
+++ b/tftf/tests/tests-realm-payload.xml
@@ -28,5 +28,7 @@
 	  function="host_realm_pmuv3_rmm_preserves" />
 	  <testcase name="PMUv3 overflow interrupt"
 	  function="host_realm_pmuv3_overflow_interrupt" />
+	  <testcase name="Test Secure interrupt can preempt Realm EL1"
+	  function="host_realm_sec_interrupt_can_preempt_rl" />
   </testsuite>
 </testsuites>