test(interrupts): exercise ns interrupt being signaled to SP

This patch adds a tftf based test to exercise non-secure interrupt
handling being signaled to the Cactus SP by the SPMC while processing
a direct message.

A detailed description is provided in the comment leading the test
function.

Signed-off-by: Madhukar Pappireddy <madhukar.pappireddy@arm.com>
Change-Id: I2f8511135470d2ca8f567532a1cb7c4ae374af3f
diff --git a/include/runtime_services/cactus_test_cmds.h b/include/runtime_services/cactus_test_cmds.h
index 1e60e0e..d86aa61 100644
--- a/include/runtime_services/cactus_test_cmds.h
+++ b/include/runtime_services/cactus_test_cmds.h
@@ -24,6 +24,10 @@
 #define CACTUS_ERROR_FFA_CALL		U(3)
 #define CACTUS_ERROR_UNHANDLED		U(4)
 
+#define ECHO_VAL1 U(0xa0a0a0a0)
+#define ECHO_VAL2 U(0xb0b0b0b0)
+#define ECHO_VAL3 U(0xc0c0c0c0)
+
 /**
  * Get command from struct ffa_value.
  */
diff --git a/tftf/tests/runtime_services/secure_service/test_ffa_interrupts.c b/tftf/tests/runtime_services/secure_service/test_ffa_interrupts.c
index 1c17513..0759a7c 100644
--- a/tftf/tests/runtime_services/secure_service/test_ffa_interrupts.c
+++ b/tftf/tests/runtime_services/secure_service/test_ffa_interrupts.c
@@ -14,10 +14,12 @@
 
 #define SENDER		HYP_ID
 #define RECEIVER	SP_ID(1)
-#define SLEEP_TIME	200U
+#define RECEIVER_2	SP_ID(2)
+#define TIMER_DURATION	50U
+#define SLEEP_TIME	100U
 
 static const struct ffa_uuid expected_sp_uuids[] = {
-		{PRIMARY_UUID}
+		{PRIMARY_UUID}, {SECONDARY_UUID}
 	};
 
 /*
@@ -31,8 +33,27 @@
 	return 0;
 }
 
+static int program_timer(unsigned long milli_secs)
+{
+	/* Program timer. */
+	timer_irq_received = 0;
+	tftf_timer_register_handler(timer_handler);
+
+	return tftf_program_timer(milli_secs);
+}
+
+static int check_timer_interrupt(void)
+{
+	/* Check that the timer interrupt has been handled in NWd(TFTF). */
+	tftf_cancel_timer();
+	tftf_timer_unregister_handler();
+
+	return timer_irq_received;
+}
+
 /*
- * @Test_Aim@ Test non-secure interrupts while executing Secure Partition.
+ * @Test_Aim@ Test non-secure interrupts while a Secure Partition capable
+ * of managed exit is executing.
  *
  * 1. Enable managed exit interrupt by sending interrupt_enable command to
  *    Cactus.
@@ -40,7 +61,7 @@
  * 2. Register a handler for the non-secure timer interrupt. Program it to fire
  *    in a certain time.
  *
- * 3. Send a blocking request to Cactus to execute in busy loop.
+ * 3. Send a direct request request to Cactus SP to execute in busy loop.
  *
  * 4. While executing in busy loop, the non-secure timer should
  *    fire and trap into SPM running at S-EL2 as FIQ.
@@ -61,29 +82,25 @@
  *    sleeping time.
  *
  */
-test_result_t test_ffa_ns_interrupt(void)
+test_result_t test_ffa_ns_interrupt_managed_exit(void)
 {
 	int ret;
 	struct ffa_value ret_values;
 
-	CHECK_SPMC_TESTING_SETUP(1, 0, expected_sp_uuids);
+	CHECK_SPMC_TESTING_SETUP(1, 1, expected_sp_uuids);
 
 	/* Enable managed exit interrupt as FIQ in the secure side. */
 	if (!spm_set_managed_exit_int(RECEIVER, true)) {
 		return TEST_RESULT_FAIL;
 	}
 
-	/* Program timer */
-	timer_irq_received = 0;
-	tftf_timer_register_handler(timer_handler);
-
-	ret = tftf_program_timer(100);
+	ret = program_timer(TIMER_DURATION);
 	if (ret < 0) {
 		ERROR("Failed to program timer (%d)\n", ret);
 		return TEST_RESULT_FAIL;
 	}
 
-	/* Send request to primary Cactus to sleep for 200ms */
+	/* Send request to primary Cactus to sleep for 100ms. */
 	ret_values = cactus_sleep_cmd(SENDER, RECEIVER, SLEEP_TIME);
 
 	if (!is_ffa_direct_response(ret_values)) {
@@ -99,11 +116,7 @@
 		return TEST_RESULT_FAIL;
 	}
 
-	/* Check that the timer interrupt has been handled in NS-world (TFTF) */
-	tftf_cancel_timer();
-	tftf_timer_unregister_handler();
-
-	if (timer_irq_received == 0) {
+	if (check_timer_interrupt() == 0) {
 		ERROR("Timer interrupt hasn't actually been handled.\n");
 		return TEST_RESULT_FAIL;
 	}
@@ -113,22 +126,119 @@
 	 * This resumes Cactus in the sleep routine.
 	 */
 	ret_values = ffa_msg_send_direct_req64(SENDER, RECEIVER,
-		0, 0, 0, 0, 0);
+					       0, 0, 0, 0, 0);
 
 	if (!is_ffa_direct_response(ret_values)) {
 		return TEST_RESULT_FAIL;
 	}
 
-	/* Make sure elapsed time not less than sleep time */
+	if (cactus_get_response(ret_values) == CACTUS_ERROR) {
+		return TEST_RESULT_FAIL;
+	}
+
+	/* Make sure elapsed time not less than sleep time. */
 	if (cactus_get_response(ret_values) < SLEEP_TIME) {
 		ERROR("Lapsed time less than requested sleep time\n");
 		return TEST_RESULT_FAIL;
 	}
 
-	/* Disable Managed exit interrupt */
+	/* Disable Managed exit interrupt. */
 	if (!spm_set_managed_exit_int(RECEIVER, false)) {
 		return TEST_RESULT_FAIL;
 	}
 
 	return TEST_RESULT_SUCCESS;
 }
+
+/*
+ * @Test_Aim@ Test the scenario where a non-secure interrupt triggers while a
+ * Secure Partition,that specified action for NS interrupt as SIGNALABLE, is
+ * executing.
+ *
+ * 1. Register a handler for the non-secure timer interrupt. Program it to fire
+ *    in a certain time.
+ *
+ * 2. Send a direct request to Cactus SP to execute in busy loop.
+ *
+ * 3. While executing in busy loop, the non-secure timer should fire. Cactus SP
+ *    should be preempted by non-secure interrupt.
+ *
+ * 4. Execution traps to SPMC running at S-EL2 as FIQ. SPMC returns control to
+ *    the normal world through FFA_INTERRUPT ABI for it to handle the non-secure
+ *    interrupt.
+ *
+ * 5. Check whether the pending non-secure timer interrupt successfully got
+ *    handled in the normal world by TFTF.
+ *
+ * 6. Resume the Cactus SP using FFA_RUN ABI for it to complete the sleep
+ *    routine.
+ *
+ * 7. Ensure the Cactus SP sends the DIRECT RESPONSE message.
+ *
+ * 8. Check if time lapsed is greater than sleep time.
+ *
+ */
+test_result_t test_ffa_ns_interrupt_signaled(void)
+{
+	int ret;
+	struct ffa_value ret_values;
+	unsigned int core_pos = get_current_core_id();
+
+	CHECK_SPMC_TESTING_SETUP(1, 1, expected_sp_uuids);
+
+	ret = program_timer(TIMER_DURATION);
+	if (ret < 0) {
+		ERROR("Failed to program timer (%d)\n", ret);
+		return TEST_RESULT_FAIL;
+	}
+
+	/* Send request to secondary Cactus to sleep for 100ms. */
+	ret_values = cactus_sleep_cmd(SENDER, RECEIVER_2, SLEEP_TIME);
+
+	if (check_timer_interrupt() == 0) {
+		ERROR("Timer interrupt hasn't actually been handled.\n");
+		return TEST_RESULT_FAIL;
+	}
+
+	/*
+	 * Cactus SP should be preempted by non-secure interrupt. SPMC
+	 * returns control to the normal world through FFA_INTERRUPT ABI
+	 * for it to handle the non-secure interrupt.
+	 */
+	if (ffa_func_id(ret_values) != FFA_INTERRUPT) {
+		ERROR("Expected FFA_INTERRUPT as return status!\n");
+		return TEST_RESULT_FAIL;
+	}
+
+	/*
+	 * Ensure SPMC returns FFA_ERROR with BUSY error code when a direct
+	 * request message is sent to the preempted SP.
+	 */
+	ret_values = cactus_echo_send_cmd(SENDER, RECEIVER_2, ECHO_VAL1);
+
+	if ((ffa_func_id(ret_values) != FFA_ERROR) ||
+	    (ffa_error_code(ret_values) != FFA_ERROR_BUSY)) {
+		ERROR("Expected FFA_ERROR(BUSY)! Got %x(%x)\n",
+		      ffa_func_id(ret_values), ffa_error_code(ret_values));
+		return TEST_RESULT_FAIL;
+	}
+
+	/*
+	 * Resume the Cactus SP using FFA_RUN ABI for it to complete the
+	 * sleep routine and send the direct response message.
+	 */
+	VERBOSE("Resuming %x\n", RECEIVER_2);
+	ret_values = ffa_run(RECEIVER_2, core_pos);
+
+	if (!is_ffa_direct_response(ret_values)) {
+		return TEST_RESULT_FAIL;
+	}
+
+	/* Make sure elapsed time not less than sleep time. */
+	if (cactus_get_response(ret_values) < SLEEP_TIME) {
+		ERROR("Lapsed time less than requested sleep time\n");
+		return TEST_RESULT_FAIL;
+	}
+
+	return TEST_RESULT_SUCCESS;
+}
diff --git a/tftf/tests/tests-spm.xml b/tftf/tests/tests-spm.xml
index 19033b7..95451c3 100644
--- a/tftf/tests/tests-spm.xml
+++ b/tftf/tests/tests-spm.xml
@@ -108,8 +108,10 @@
 
    <testsuite name="FF-A Interrupt"
              description="Test non-secure Interrupts" >
-     <testcase name="Test NS interrupts"
-               function="test_ffa_ns_interrupt" />
+     <testcase name="Test Managed Exit"
+               function="test_ffa_ns_interrupt_managed_exit" />
+     <testcase name="Test NS interrupt Signalable"
+               function="test_ffa_ns_interrupt_signaled" />
      <testcase name="Test Secure interrupt handling while SP running"
                function="test_ffa_sec_interrupt_sp_running" />
      <testcase name="Test Secure interrupt handling while SP waiting"