feat: add support for secure interrupt handling and completion

As per FF-A v1.1 spec, the conduit used by SPM to signal an interrupt
to S-EL1 Partition is vIRQ and/or ERET.

Depending on the execution state of the vCPU of the target SP,
FFA_INTERRUPT ABI and/or Interrupt ID can be used as parameters during
eret to the SP.

This patch adds support for secure interrupt handling in Cactus SP as
well as adds support for FFA_INTERRUPT ABI in the message loop.

Signed-off-by: Madhukar Pappireddy <madhukar.pappireddy@arm.com>
Change-Id: I173b9e0af11b6fd2605145391da68dfda04a14f2
diff --git a/spm/cactus/aarch64/cactus_exceptions.S b/spm/cactus/aarch64/cactus_exceptions.S
index 31cdbf9..6aec16d 100644
--- a/spm/cactus/aarch64/cactus_exceptions.S
+++ b/spm/cactus/aarch64/cactus_exceptions.S
@@ -101,7 +101,7 @@
 func irq_vector_entry
 	sub	sp, sp, #0x100
 	save_gp_regs
-	bl	cactus_irq_handler
+	bl	cactus_interrupt_handler
 	restore_gp_regs
 	add	sp, sp, #0x100
 	eret
@@ -110,7 +110,7 @@
 func fiq_vector_entry
 	sub	sp, sp, #0x100
 	save_gp_regs
-	bl	cactus_fiq_handler
+	bl	cactus_interrupt_handler
 	restore_gp_regs
 	add	sp, sp, #0x100
 	eret
diff --git a/spm/cactus/cactus_interrupt.c b/spm/cactus/cactus_interrupt.c
index f61df94..31c6a69 100644
--- a/spm/cactus/cactus_interrupt.c
+++ b/spm/cactus/cactus_interrupt.c
@@ -6,46 +6,50 @@
 
 #include <debug.h>
 
+#include "cactus_message_loop.h"
+#include "cactus_test_cmds.h"
+#include <drivers/arm/sp805.h>
 #include <ffa_helpers.h>
 #include <sp_helpers.h>
+#include "spm_common.h"
 #include <spm_helpers.h>
 
-#include "cactus_test_cmds.h"
-#include "spm_common.h"
+#include <platform_def.h>
 
 extern ffa_id_t g_ffa_id;
 
-static void managed_exit_handler(void)
+void cactus_interrupt_handler(void)
 {
-	/*
-	 * Real SP will save its context here.
-	 * Send interrupt ID for acknowledgement
-	 */
-	cactus_response(g_ffa_id, HYP_ID, MANAGED_EXIT_INTERRUPT_ID);
-}
+	uint32_t intid = spm_interrupt_get();
 
-int cactus_irq_handler(void)
-{
-	uint32_t irq_num;
+	switch (intid) {
+	case MANAGED_EXIT_INTERRUPT_ID:
+		/*
+		 * A secure partition performs its housekeeping and sends a
+		 * direct response to signal interrupt completion.
+		 * This is a pure virtual interrupt, no need for deactivation.
+		 */
+		cactus_response(g_ffa_id, HYP_ID, MANAGED_EXIT_INTERRUPT_ID);
+		break;
+	case IRQ_TWDOG_INTID:
+		/*
+		 * Interrupt triggered due to Trusted watchdog timer expiry.
+		 * Clear the interrupt and stop the timer.
+		 */
+		NOTICE("Trusted WatchDog timer stopped\n");
+		sp805_twdog_stop();
 
-	irq_num = spm_interrupt_get();
+		/* Perform secure interrupt de-activation. */
+		spm_interrupt_deactivate(intid);
 
-	ERROR("%s: Interrupt ID %u not handled!\n", __func__, irq_num);
-
-	return 0;
-}
-
-int cactus_fiq_handler(void)
-{
-	uint32_t fiq_num;
-
-	fiq_num = spm_interrupt_get();
-
-	if (fiq_num == MANAGED_EXIT_INTERRUPT_ID) {
-		managed_exit_handler();
-	} else {
-		ERROR("%s: Interrupt ID %u not handled!\n", __func__, fiq_num);
+		break;
+	default:
+		/*
+		 * Currently the only source of secure interrupt is Trusted
+		 * Watchdog timer.
+		 */
+		ERROR("%s: Interrupt ID %x not handled!\n", __func__,
+			 intid);
+		panic();
 	}
-
-	return 0;
 }
diff --git a/spm/cactus/cactus_main.c b/spm/cactus/cactus_main.c
index 78bb8fc..c80abd9 100644
--- a/spm/cactus/cactus_main.c
+++ b/spm/cactus/cactus_main.c
@@ -69,14 +69,26 @@
 		}
 
 		if (ffa_func_id(ffa_ret) != FFA_MSG_SEND_DIRECT_REQ_SMC32 &&
-		    ffa_func_id(ffa_ret) != FFA_MSG_SEND_DIRECT_REQ_SMC64) {
+		    ffa_func_id(ffa_ret) != FFA_MSG_SEND_DIRECT_REQ_SMC64 &&
+		    ffa_func_id(ffa_ret) != FFA_INTERRUPT) {
 			ERROR("%s(%u) unknown func id 0x%x\n",
 				__func__, vm_id, ffa_func_id(ffa_ret));
 			break;
 		}
 
-		destination = ffa_dir_msg_dest(ffa_ret);
+		if (ffa_func_id(ffa_ret) == FFA_INTERRUPT) {
+			/*
+			 * Received FFA_INTERRUPT in waiting state.
+			 * The interrupt id is passed although this is just
+			 * informational as we're running with virtual
+			 * interrupts unmasked and the interrupt is processed
+			 * by the interrupt handler.
+			 */
+			ffa_ret = ffa_msg_wait();
+			continue;
+		}
 
+		destination = ffa_dir_msg_dest(ffa_ret);
 		if (destination != vm_id) {
 			ERROR("%s(%u) invalid vm id 0x%x\n",
 				__func__, vm_id, destination);
diff --git a/spm/cactus/cactus_tests/cactus_message_loop.c b/spm/cactus/cactus_tests/cactus_message_loop.c
index fde7074..c24f6fc 100644
--- a/spm/cactus/cactus_tests/cactus_message_loop.c
+++ b/spm/cactus/cactus_tests/cactus_message_loop.c
@@ -32,7 +32,7 @@
 	uint64_t in_cmd;
 
 	if (cmd_args == NULL || ret == NULL) {
-		ERROR("Invalid argumentos passed to %s!\n", __func__);
+		ERROR("Invalid arguments passed to %s!\n", __func__);
 		return false;
 	}
 
diff --git a/spm/cactus/cactus_tests/cactus_test_interrupts.c b/spm/cactus/cactus_tests/cactus_test_interrupts.c
index 5cd53d1..ced5dca 100644
--- a/spm/cactus/cactus_tests/cactus_test_interrupts.c
+++ b/spm/cactus/cactus_tests/cactus_test_interrupts.c
@@ -12,6 +12,8 @@
 #include "cactus_message_loop.h"
 #include "cactus_test_cmds.h"
 
+#include <platform.h>
+
 CACTUS_CMD_HANDLER(sleep_cmd, CACTUS_SLEEP_CMD)
 {
 	uint64_t time_lapsed;
@@ -37,12 +39,19 @@
 	ffa_id_t fwd_dest = cactus_get_fwd_sleep_dest(*args);
 	uint32_t sleep_ms = cactus_get_sleep_time(*args);
 
-
 	VERBOSE("VM%x requested %x to sleep for value %u\n",
 		ffa_dir_msg_source(*args), fwd_dest, sleep_ms);
 
 	ffa_ret = cactus_sleep_cmd(vm_id, fwd_dest, sleep_ms);
 
+	while (ffa_ret.ret0 == FFA_INTERRUPT) {
+		/* Received FFA_INTERRUPT in blocked state. */
+		VERBOSE("Processing FFA_INTERRUPT while blocked on direct response\n");
+		unsigned int my_core_pos = platform_get_core_pos(read_mpidr_el1());
+
+		ffa_ret = ffa_run(fwd_dest, my_core_pos);
+	}
+
 	if (!is_ffa_direct_response(ffa_ret)) {
 		ERROR("Encountered error in CACTUS_FWD_SLEEP_CMD response\n");
 		return cactus_error_resp(vm_id, ffa_dir_msg_source(*args),