feat(interrupts): support for registering irq handlers

This patch provides support for registering and unregistering
handler that is invoked by Cactus SP while processing virtual
irq interrupt. For this, we simply repurpose the existing framework
that was used to perform tail end of interrupt handling.

Also, this patch increases the count of virtual interrupts supported
by Cactus SP inline with the corresponding change in Hafnium[1].

[1] https://review.trustedfirmware.org/c/hafnium/hafnium/+/19232

Signed-off-by: Madhukar Pappireddy <madhukar.pappireddy@arm.com>
Change-Id: Ife41f0d3bb7eebb7c78657abb5b4c5ad41202bb9
diff --git a/spm/cactus/cactus_interrupt.c b/spm/cactus/cactus_interrupt.c
index b8eb058..0a9035f 100644
--- a/spm/cactus/cactus_interrupt.c
+++ b/spm/cactus/cactus_interrupt.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021-2022, Arm Limited. All rights reserved.
+ * Copyright (c) 2021-2023, Arm Limited. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
@@ -45,20 +45,6 @@
 	     managed_exit_interrupt_id);
 }
 
-static void post_interrupt_handler(uint32_t intid)
-{
-	unsigned int core_pos = get_current_core_id();
-
-	last_serviced_interrupt[core_pos] = intid;
-
-	/* Invoke the tail end handler registered by the SP. */
-	spin_lock(&sp_handler_lock[intid]);
-	if (sp_interrupt_tail_end_handler[intid]) {
-		sp_interrupt_tail_end_handler[intid]();
-	}
-	spin_unlock(&sp_handler_lock[intid]);
-}
-
 /*
  * Cactus SP does not implement application threads. Hence, once the Cactus SP
  * sends the managed exit response to the direct request originator, execution
@@ -74,7 +60,11 @@
 	struct ffa_value ffa_ret;
 	bool waiting_resume_after_managed_exit;
 
-	/* Send managed exit response. */
+	/*
+	 * 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.
+	 */
 	ffa_ret = cactus_response(g_ffa_id, g_dir_req_source_id,
 			MANAGED_EXIT_INTERRUPT_ID);
 	waiting_resume_after_managed_exit = true;
@@ -99,53 +89,40 @@
 	VERBOSE("Resuming the suspended command\n");
 }
 
+void register_maintenance_interrupt_handlers(void)
+{
+	sp_register_interrupt_handler(send_managed_exit_response,
+		managed_exit_interrupt_id);
+	sp_register_interrupt_handler(notification_pending_interrupt_handler,
+		NOTIFICATION_PENDING_INTERRUPT_INTID);
+}
+
 void cactus_interrupt_handler_irq(void)
 {
 	uint32_t intid = spm_interrupt_get();
+	unsigned int core_pos = get_current_core_id();
 
-	if (intid == 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.
-		 */
-		VERBOSE("vIRQ: Sending ME response to %x\n",
-			g_dir_req_source_id);
-		send_managed_exit_response();
+	last_serviced_interrupt[core_pos] = intid;
+
+	/* Invoke the handler registered by the SP. */
+	spin_lock(&sp_handler_lock[intid]);
+	if (sp_interrupt_handler[intid]) {
+		sp_interrupt_handler[intid]();
 	} else {
-		switch (intid) {
-		case IRQ_TWDOG_INTID:
-			/*
-			 * Interrupt triggered due to Trusted watchdog timer expiry.
-			 * Clear the interrupt and stop the timer.
-			 */
-			VERBOSE("Trusted WatchDog timer stopped\n");
-			sp805_twdog_stop();
-
-			/* Perform secure interrupt de-activation. */
-			spm_interrupt_deactivate(intid);
-
-			break;
-		case NOTIFICATION_PENDING_INTERRUPT_INTID:
-			notification_pending_interrupt_handler();
-			break;
-		default:
-			ERROR("%s: Interrupt ID %x not handled!\n", __func__,
-				 intid);
-			panic();
-			break;
-		}
+		ERROR("%s: Interrupt ID %x not handled!\n", __func__, intid);
+		panic();
 	}
-	post_interrupt_handler(intid);
+	spin_unlock(&sp_handler_lock[intid]);
 }
 
 void cactus_interrupt_handler_fiq(void)
 {
 	uint32_t intid = spm_interrupt_get();
+	unsigned int core_pos = get_current_core_id();
 
-	switch (intid) {
-	case MANAGED_EXIT_INTERRUPT_ID:
+	last_serviced_interrupt[core_pos] = intid;
+
+	if (intid == MANAGED_EXIT_INTERRUPT_ID) {
 		/*
 		 * A secure partition performs its housekeeping and sends a
 		 * direct response to signal interrupt completion.
@@ -154,13 +131,10 @@
 		VERBOSE("vFIQ: Sending ME response to %x\n",
 			g_dir_req_source_id);
 		send_managed_exit_response();
-		break;
-	default:
+	} else {
 		/*
 		 * Currently only managed exit interrupt is supported by vFIQ.
 		 */
 		panic();
-		break;
 	}
-	post_interrupt_handler(intid);
 }
diff --git a/spm/cactus/cactus_main.c b/spm/cactus/cactus_main.c
index 8cd4cf1..0518f1f 100644
--- a/spm/cactus/cactus_main.c
+++ b/spm/cactus/cactus_main.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018-2022, Arm Limited. All rights reserved.
+ * Copyright (c) 2018-2023, Arm Limited. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
@@ -317,6 +317,7 @@
 
 	register_secondary_entrypoint();
 	discover_managed_exit_interrupt_id();
+	register_maintenance_interrupt_handlers();
 
 	/* Invoking Tests */
 	ffa_tests(&mb);
diff --git a/spm/cactus/cactus_tests/cactus_test_interrupts.c b/spm/cactus/cactus_tests/cactus_test_interrupts.c
index 7f3de0c..efe2260 100644
--- a/spm/cactus/cactus_tests/cactus_test_interrupts.c
+++ b/spm/cactus/cactus_tests/cactus_test_interrupts.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021-2022, Arm Limited. All rights reserved.
+ * Copyright (c) 2021-2023, Arm Limited. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
@@ -18,8 +18,22 @@
 extern uint32_t last_serviced_interrupt[PLATFORM_CORE_COUNT];
 static int flag_set;
 
-static void sec_wdog_interrupt_handled(void)
+static void handle_sec_wdog_interrupt(void)
 {
+	/*
+	 * Interrupt triggered due to Trusted watchdog timer expiry.
+	 * Clear the interrupt and stop the timer.
+	 */
+	VERBOSE("Trusted WatchDog timer stopped\n");
+	sp805_twdog_stop();
+
+	/* Perform secure interrupt de-activation. */
+	spm_interrupt_deactivate(IRQ_TWDOG_INTID);
+}
+
+static void check_sec_wdog_interrupt_triggered(void)
+{
+	handle_sec_wdog_interrupt();
 	expect(flag_set, 0);
 	flag_set = 1;
 }
@@ -137,6 +151,9 @@
 
 	uint64_t time_ms = cactus_get_wdog_duration(*args);
 
+	sp_register_interrupt_handler(handle_sec_wdog_interrupt,
+				      IRQ_TWDOG_INTID);
+
 	VERBOSE("Starting TWDOG: %llums\n", time_ms);
 	sp805_twdog_refresh();
 	sp805_twdog_start((time_ms * ARM_SP805_TWDG_CLK_HZ) / 1000);
@@ -146,8 +163,8 @@
 
 bool handle_twdog_interrupt_sp_sleep(uint32_t sleep_time, uint64_t *time_lapsed)
 {
-	sp_register_interrupt_tail_end_handler(sec_wdog_interrupt_handled,
-						IRQ_TWDOG_INTID);
+	sp_register_interrupt_handler(check_sec_wdog_interrupt_triggered,
+				      IRQ_TWDOG_INTID);
 	*time_lapsed += sp_sleep_elapsed_time(sleep_time);
 
 	if (flag_set == 0) {
@@ -156,7 +173,7 @@
 
 	/* Reset the flag and unregister the handler. */
 	flag_set = 0;
-	sp_unregister_interrupt_tail_end_handler(IRQ_TWDOG_INTID);
+	sp_unregister_interrupt_handler(IRQ_TWDOG_INTID);
 
 	return true;
 }
diff --git a/spm/common/sp_helpers.c b/spm/common/sp_helpers.c
index 448084f..eeb0d19 100644
--- a/spm/common/sp_helpers.c
+++ b/spm/common/sp_helpers.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018-2022, Arm Limited. All rights reserved.
+ * Copyright (c) 2018-2023, Arm Limited. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
@@ -15,7 +15,7 @@
 
 spinlock_t sp_handler_lock[NUM_VINT_ID];
 
-void (*sp_interrupt_tail_end_handler[NUM_VINT_ID])(void);
+void (*sp_interrupt_handler[NUM_VINT_ID])(void);
 
 uintptr_t bound_rand(uintptr_t min, uintptr_t max)
 {
@@ -92,7 +92,7 @@
 	}
 }
 
-void sp_register_interrupt_tail_end_handler(void (*handler)(void),
+void sp_register_interrupt_handler(void (*handler)(void),
 			uint32_t interrupt_id)
 {
 	if (interrupt_id >= NUM_VINT_ID) {
@@ -101,11 +101,11 @@
 	}
 
 	spin_lock(&sp_handler_lock[interrupt_id]);
-	sp_interrupt_tail_end_handler[interrupt_id] = handler;
+	sp_interrupt_handler[interrupt_id] = handler;
 	spin_unlock(&sp_handler_lock[interrupt_id]);
 }
 
-void sp_unregister_interrupt_tail_end_handler(uint32_t interrupt_id)
+void sp_unregister_interrupt_handler(uint32_t interrupt_id)
 {
 	if (interrupt_id >= NUM_VINT_ID) {
 		ERROR("Cannot unregister handler for interrupt %u\n", interrupt_id);
@@ -113,6 +113,6 @@
 	}
 
 	spin_lock(&sp_handler_lock[interrupt_id]);
-	sp_interrupt_tail_end_handler[interrupt_id] = NULL;
+	sp_interrupt_handler[interrupt_id] = NULL;
 	spin_unlock(&sp_handler_lock[interrupt_id]);
 }
diff --git a/spm/common/sp_helpers.h b/spm/common/sp_helpers.h
index 98af484..629801e 100644
--- a/spm/common/sp_helpers.h
+++ b/spm/common/sp_helpers.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018-2022, Arm Limited. All rights reserved.
+ * Copyright (c) 2018-2023, Arm Limited. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
@@ -12,8 +12,8 @@
 #include <spm_common.h>
 #include <spinlock.h>
 
-/* Currently, Hafnium/SPM supports only 64 virtual interrupt IDs. */
-#define NUM_VINT_ID	64
+/* Currently, Hafnium/SPM supports 1024 virtual interrupt IDs. */
+#define NUM_VINT_ID	1024
 
 typedef struct {
 	u_register_t fid;
@@ -68,16 +68,18 @@
 
 void sp_handler_spin_lock_init(void);
 
-/* Handler invoked at the tail end of interrupt processing by SP. */
-extern void (*sp_interrupt_tail_end_handler[NUM_VINT_ID])(void);
+/* Handler invoked by SP while processing interrupt. */
+extern void (*sp_interrupt_handler[NUM_VINT_ID])(void);
 
 /* Register the handler. */
-void sp_register_interrupt_tail_end_handler(void (*handler)(void),
+void sp_register_interrupt_handler(void (*handler)(void),
 						uint32_t interrupt_id);
 
 /* Un-register the handler. */
-void sp_unregister_interrupt_tail_end_handler(uint32_t interrupt_id);
+void sp_unregister_interrupt_handler(uint32_t interrupt_id);
 
 void discover_managed_exit_interrupt_id(void);
 
+void register_maintenance_interrupt_handlers(void);
+
 #endif /* SP_HELPERS_H */