refactor(gic): defer IRQ handler management to the GIC driver

Interrupt groups are not generic between GIC versions. SGIs and eSPIs
disappear, while SPIs and LPIs subtly change function. So abstract all
of this away and hide it behind each individual GIC driver.

Change-Id: Iaa55014b2940969508b290736c43134688e8c422
Signed-off-by: Boyan Karatotev <boyan.karatotev@arm.com>
diff --git a/drivers/arm/gic/aarch64/gic_v5.c b/drivers/arm/gic/aarch64/gic_v5.c
index 0f8f5fa..74fc590 100644
--- a/drivers/arm/gic/aarch64/gic_v5.c
+++ b/drivers/arm/gic/aarch64/gic_v5.c
@@ -36,6 +36,10 @@
 /* the IST is a power of 2 since that's what goes in IRS_IST_CFGR.LPI_ID_BITS */
 struct l2_iste ist[next_power_of_2(PLATFORM_CORE_COUNT) * IRQ_NUM_SGIS];
 
+static ppi_desc ppi_desc_table[PLATFORM_CORE_COUNT][GICV5_MAX_PPI_ID];
+static spi_desc spi_desc_table[PLAT_MAX_SPI_OFFSET_ID];
+static spi_desc lpi_desc_table[PLATFORM_CORE_COUNT * IRQ_NUM_SGIS];
+
 static inline uint8_t log2(uint32_t num)
 {
 	return (31 - __builtin_clz(num));
@@ -410,7 +414,32 @@
 	return (core_pos * IRQ_NUM_SGIS + index) | INPLACE(INT_TYPE, INT_LPI);
 }
 
+irq_handler_t *gicv5_get_irq_handler(unsigned int irq_num)
+{
+	unsigned int linear_id;
+
+	if (EXTRACT(INT_TYPE, irq_num) == INT_PPI) {
+		linear_id = platform_get_core_pos(read_mpidr_el1());
+		return &ppi_desc_table[linear_id][EXTRACT(INT_ID, irq_num)].handler;
+	}
+
+	if (EXTRACT(INT_TYPE, irq_num) == INT_LPI) {
+		return &lpi_desc_table[EXTRACT(INT_ID, irq_num)].handler;
+	}
+
+	if (EXTRACT(INT_TYPE, irq_num) == INT_SPI) {
+		return &spi_desc_table[EXTRACT(INT_ID, irq_num)].handler;
+	}
+
+	/* Interrupt should have been handled */
+	panic();
+	return NULL;
+}
+
 void gicv5_init(uintptr_t irs_base_addr)
 {
 	irs_base = irs_base_addr;
+	memset(ppi_desc_table, 0, sizeof(ppi_desc_table));
+	memset(spi_desc_table, 0, sizeof(spi_desc_table));
+	memset(lpi_desc_table, 0, sizeof(lpi_desc_table));
 }
diff --git a/drivers/arm/gic/arm_gic.c b/drivers/arm/gic/arm_gic.c
index ff51420..2f05a48 100644
--- a/drivers/arm/gic/arm_gic.c
+++ b/drivers/arm/gic/arm_gic.c
@@ -293,3 +293,21 @@
 
 	return false;
 }
+
+irq_handler_t *arm_gic_get_irq_handler(unsigned int irq_num)
+{
+	if (gicv5_detected) {
+		return gicv5_get_irq_handler(irq_num);
+	} else {
+		return gicv2v3_get_irq_handler(irq_num);
+	}
+}
+
+bool arm_gic_is_irq_shared(unsigned int irq_num)
+{
+	if (gicv5_detected) {
+		return gicv5_is_irq_spi(irq_num);
+	} else {
+		return gicv2v3_is_irq_spi(irq_num);
+	}
+}
diff --git a/drivers/arm/gic/gic_v2.c b/drivers/arm/gic/gic_v2.c
index f5fdc44..4a93406 100644
--- a/drivers/arm/gic/gic_v2.c
+++ b/drivers/arm/gic/gic_v2.c
@@ -364,4 +364,6 @@
 	assert(!is_gicv3_mode());
 	gicc_base_addr = gicc_base;
 	gicd_base_addr = gicd_base;
+
+	gicv2v3_irq_setup();
 }
diff --git a/drivers/arm/gic/gic_v2v3_common.c b/drivers/arm/gic/gic_v2v3_common.c
index 4eafa59..a929b92 100644
--- a/drivers/arm/gic/gic_v2v3_common.c
+++ b/drivers/arm/gic/gic_v2v3_common.c
@@ -11,6 +11,7 @@
 #include <drivers/arm/gic_v2v3_common.h>
 #include <drivers/arm/gic_v3.h>
 #include <mmio.h>
+#include <platform.h>
 
 /*******************************************************************************
  * GIC Distributor interface accessors for reading entire registers
@@ -197,3 +198,46 @@
 	/* Check whether the system register interface is enabled */
 	return !!is_sre_enabled();
 }
+
+bool gicv2v3_is_irq_spi(unsigned int irq_num)
+{
+	return IS_PLAT_SPI(irq_num);
+}
+
+static spi_desc spi_desc_table[PLAT_MAX_SPI_OFFSET_ID + 1];
+static ppi_desc ppi_desc_table[PLATFORM_CORE_COUNT][
+				(MAX_PPI_ID + 1) - MIN_PPI_ID];
+static sgi_desc sgi_desc_table[PLATFORM_CORE_COUNT][MAX_SGI_ID + 1];
+static spurious_desc spurious_desc_handler;
+
+void gicv2v3_irq_setup(void)
+{
+	memset(spi_desc_table, 0, sizeof(spi_desc_table));
+	memset(ppi_desc_table, 0, sizeof(ppi_desc_table));
+	memset(sgi_desc_table, 0, sizeof(sgi_desc_table));
+	memset(&spurious_desc_handler, 0, sizeof(spurious_desc_handler));
+}
+
+irq_handler_t *gicv2v3_get_irq_handler(unsigned int irq_num)
+{
+	if (IS_PLAT_SPI(irq_num)) {
+		return &spi_desc_table[irq_num - MIN_SPI_ID].handler;
+	}
+
+	unsigned int linear_id = platform_get_core_pos(read_mpidr_el1());
+
+	if (IS_PPI(irq_num)) {
+		return &ppi_desc_table[linear_id][irq_num - MIN_PPI_ID].handler;
+	}
+
+	if (IS_SGI(irq_num)) {
+		return &sgi_desc_table[linear_id][irq_num - MIN_SGI_ID].handler;
+	}
+
+	/*
+	 * The only possibility is for it to be a spurious
+	 * interrupt.
+	 */
+	assert(irq_num == GIC_SPURIOUS_INTERRUPT);
+	return &spurious_desc_handler;
+}
diff --git a/drivers/arm/gic/gic_v3.c b/drivers/arm/gic/gic_v3.c
index 8bbea48..953ea38 100644
--- a/drivers/arm/gic/gic_v3.c
+++ b/drivers/arm/gic/gic_v3.c
@@ -351,6 +351,11 @@
 	assert(core_pos < PLATFORM_CORE_COUNT);
 	assert(mpidr_list[core_pos] != UINT64_MAX);
 
+	/* only relevant for SPIs */
+	if (!IS_PLAT_SPI(interrupt_id)) {
+		return;
+	}
+
 	/* Routing information can be set only for SPIs */
 	assert(IS_SPI(interrupt_id));
 	route_affinity = mpidr_list[core_pos];
@@ -501,6 +506,8 @@
 
 	gicr_base_addr = gicr_base;
 	gicd_base_addr = gicd_base;
+
+	gicv2v3_irq_setup();
 }
 
 unsigned int gicv3_get_gicd_typer(void)
diff --git a/include/drivers/arm/arm_gic.h b/include/drivers/arm/arm_gic.h
index 802e797..1cb45fe 100644
--- a/include/drivers/arm/arm_gic.h
+++ b/include/drivers/arm/arm_gic.h
@@ -10,33 +10,13 @@
 #include <stdbool.h>
 #include <stdint.h>
 
-/***************************************************************************
- * Defines and prototypes for ARM GIC driver.
- **************************************************************************/
-#define MAX_SGIS		16
-#define MIN_SGI_ID		0
-#define MAX_SGI_ID		15
-#define MIN_PPI_ID		16
-#define MAX_PPI_ID		31
-#define MIN_SPI_ID		32
-#define MAX_SPI_ID		1019
-
-#define IS_SGI(irq_num)							\
-	(((irq_num) >= MIN_SGI_ID) && ((irq_num) <= MAX_SGI_ID))
-
-#define IS_PPI(irq_num)							\
-	(((irq_num) >= MIN_PPI_ID) && ((irq_num) <= MAX_PPI_ID))
-
-#define IS_SPI(irq_num)							\
-	(((irq_num) >= MIN_SPI_ID) && ((irq_num) <= MAX_SPI_ID))
-
-#define IS_VALID_INTR_ID(irq_num)					\
-	(((irq_num) >= MIN_SGI_ID) && ((irq_num) <= MAX_SPI_ID))
-
 #define GIC_HIGHEST_NS_PRIORITY	0
 #define GIC_LOWEST_NS_PRIORITY	254 /* 255 would disable an interrupt */
 #define GIC_SPURIOUS_INTERRUPT	1023
 
+/* Prototype of a handler function for an IRQ */
+typedef int (*irq_handler_t)(void *data);
+
 /* return the GIC version detected */
 int arm_gic_get_version(void);
 
@@ -170,4 +150,15 @@
  *****************************************************************************/
 bool arm_gic_is_espi_supported(void);
 
+/******************************************************************************
+ * Gets the handler for an interrupt
+ *****************************************************************************/
+irq_handler_t *arm_gic_get_irq_handler(unsigned int irq_num);
+
+/******************************************************************************
+ * Returns true if the IRQ number is shared between cores (as opposed to
+ * individual or banked for each).
+ *****************************************************************************/
+bool arm_gic_is_irq_shared(unsigned int irq_num);
+
 #endif /* __ARM_GIC_H__ */
diff --git a/include/drivers/arm/gic_v2v3_common.h b/include/drivers/arm/gic_v2v3_common.h
index 8182d5d..761ae01 100644
--- a/include/drivers/arm/gic_v2v3_common.h
+++ b/include/drivers/arm/gic_v2v3_common.h
@@ -9,6 +9,34 @@
 
 #include <stdbool.h>
 
+#include <drivers/arm/arm_gic.h>
+
+/***************************************************************************
+ * Defines and prototypes for ARM GIC driver.
+ **************************************************************************/
+#define MIN_SGI_ID		0
+#define MAX_SGI_ID		15
+#define MIN_PPI_ID		16
+#define MAX_PPI_ID		31
+#define MIN_SPI_ID		32
+#define MAX_SPI_ID		1019
+
+#define IS_SGI(irq_num)							\
+	(((irq_num) >= MIN_SGI_ID) && ((irq_num) <= MAX_SGI_ID))
+
+#define IS_PPI(irq_num)							\
+	(((irq_num) >= MIN_PPI_ID) && ((irq_num) <= MAX_PPI_ID))
+
+#define IS_SPI(irq_num)							\
+	(((irq_num) >= MIN_SPI_ID) && ((irq_num) <= MAX_SPI_ID))
+
+#define IS_PLAT_SPI(irq_num)						\
+	(((irq_num) >= MIN_SPI_ID) &&					\
+	 ((irq_num) <= MIN_SPI_ID + PLAT_MAX_SPI_OFFSET_ID))
+
+#define IS_VALID_INTR_ID(irq_num)					\
+	(((irq_num) >= MIN_SGI_ID) && ((irq_num) <= MAX_SPI_ID))
+
 /***************************************************************************
  * Defines and prototypes common to GIC v2 and v3 drivers.
  **************************************************************************/
@@ -90,6 +118,9 @@
 void gicd_set_icactiver(uintptr_t base, unsigned int interrupt_id);
 void gicd_set_ipriorityr(uintptr_t base, unsigned int interrupt_id,
 					unsigned int priority);
+bool gicv2v3_is_irq_spi(unsigned int irq_num);
+void gicv2v3_irq_setup(void);
+irq_handler_t *gicv2v3_get_irq_handler(unsigned int irq_num);
 
 static inline unsigned int gicv2v3_get_sgi_num(unsigned int irq_num,
 						unsigned int core_pos)
diff --git a/include/drivers/arm/gic_v5.h b/include/drivers/arm/gic_v5.h
index 9c0b503..e8f3734 100644
--- a/include/drivers/arm/gic_v5.h
+++ b/include/drivers/arm/gic_v5.h
@@ -98,6 +98,7 @@
 void gicv5_end_of_interrupt(unsigned int raw_iar);
 void gicv5_setup(void);
 uint32_t gicv5_get_sgi_num(uint32_t index, unsigned int core_pos);
+irq_handler_t *gicv5_get_irq_handler(unsigned int interrupt_id);
 void gicv5_init(uintptr_t irs_base_addr);
 #else
 static inline bool is_gicv5_mode(void) { return false; }
@@ -118,6 +119,7 @@
 static inline void gicv5_end_of_interrupt(unsigned int raw_iar) {}
 static inline void gicv5_setup(void) {}
 static inline uint32_t gicv5_get_sgi_num(uint32_t index, unsigned int core_pos) { return 0; }
+static inline irq_handler_t *gicv5_get_irq_handler(unsigned int interrupt_id) {return NULL; }
 static inline void gicv5_init(uintptr_t irs_base_addr) {}
 #endif
 
diff --git a/include/lib/irq.h b/include/lib/irq.h
index 49fab13..cdd34df 100644
--- a/include/lib/irq.h
+++ b/include/lib/irq.h
@@ -34,9 +34,6 @@
 
 #ifndef __ASSEMBLY__
 
-/* Prototype of a handler function for an IRQ */
-typedef int (*irq_handler_t)(void *data);
-
 /* Keep track of the IRQ handler registered for a given SPI */
 typedef struct {
 	irq_handler_t handler;
diff --git a/lib/exceptions/irq.c b/lib/exceptions/irq.c
index 5128df7..289a17a 100644
--- a/lib/exceptions/irq.c
+++ b/lib/exceptions/irq.c
@@ -18,16 +18,6 @@
 #include <tftf.h>
 #include <tftf_lib.h>
 
-#define IS_PLAT_SPI(irq_num)						\
-	(((irq_num) >= MIN_SPI_ID) &&					\
-	 ((irq_num) <= MIN_SPI_ID + PLAT_MAX_SPI_OFFSET_ID))
-
-static spi_desc spi_desc_table[PLAT_MAX_SPI_OFFSET_ID + 1];
-static ppi_desc ppi_desc_table[PLATFORM_CORE_COUNT][
-				(MAX_PPI_ID + 1) - MIN_PPI_ID];
-static sgi_desc sgi_desc_table[PLATFORM_CORE_COUNT][MAX_SGI_ID + 1];
-static spurious_desc spurious_desc_handler;
-
 /*
  * For a given SPI, the associated IRQ handler is common to all CPUs.
  * Therefore, we need a lock to prevent simultaneous updates.
@@ -37,29 +27,8 @@
  * saves memory. Updating an SPI handler shouldn't occur that often anyway so we
  * shouldn't suffer from this restriction too much.
  */
-static spinlock_t spi_lock;
+static spinlock_t shared_irq_lock;
 
-static irq_handler_t *get_irq_handler(unsigned int irq_num)
-{
-	if (IS_PLAT_SPI(irq_num))
-		return &spi_desc_table[irq_num - MIN_SPI_ID].handler;
-
-	unsigned int mpid = read_mpidr_el1();
-	unsigned int linear_id = platform_get_core_pos(mpid);
-
-	if (IS_PPI(irq_num))
-		return &ppi_desc_table[linear_id][irq_num - MIN_PPI_ID].handler;
-
-	if (IS_SGI(irq_num))
-		return &sgi_desc_table[linear_id][irq_num - MIN_SGI_ID].handler;
-
-	/*
-	 * The only possibility is for it to be a spurious
-	 * interrupt.
-	 */
-	assert(irq_num == GIC_SPURIOUS_INTERRUPT);
-	return &spurious_desc_handler;
-}
 
 unsigned int tftf_irq_get_my_sgi_num(unsigned int seq_id)
 {
@@ -69,8 +38,6 @@
 
 void tftf_send_sgi(unsigned int sgi_id, unsigned int core_pos)
 {
-	assert(IS_SGI(sgi_id));
-
 	/*
 	 * Ensure that all memory accesses prior to sending the SGI are
 	 * completed.
@@ -92,14 +59,7 @@
 
 void tftf_irq_enable(unsigned int irq_num, uint8_t irq_priority)
 {
-	if (IS_PLAT_SPI(irq_num)) {
-		/*
-		 * Instruct the GIC Distributor to forward the interrupt to
-		 * the calling core
-		 */
-		arm_gic_set_intr_target(irq_num, platform_get_core_pos(read_mpidr_el1()));
-	}
-
+	arm_gic_set_intr_target(irq_num, platform_get_core_pos(read_mpidr_el1()));
 	arm_gic_set_intr_priority(irq_num, irq_priority);
 	arm_gic_intr_enable(irq_num);
 
@@ -138,9 +98,9 @@
 	irq_handler_t *cur_handler;
 	int ret = -1;
 
-	cur_handler = get_irq_handler(irq_num);
-	if (IS_PLAT_SPI(irq_num))
-		spin_lock(&spi_lock);
+	cur_handler = arm_gic_get_irq_handler(irq_num);
+	if (arm_gic_is_irq_shared(irq_num))
+		spin_lock(&shared_irq_lock);
 
 	/*
 	 * Update the IRQ handler, if the current handler is in the expected
@@ -152,8 +112,8 @@
 		ret = 0;
 	}
 
-	if (IS_PLAT_SPI(irq_num))
-		spin_unlock(&spi_lock);
+	if (arm_gic_is_irq_shared(irq_num))
+		spin_unlock(&shared_irq_lock);
 
 	return ret;
 }
@@ -204,7 +164,7 @@
 	/* Acknowledge the interrupt */
 	irq_num = arm_gic_intr_ack(&raw_iar);
 
-	handler = get_irq_handler(irq_num);
+	handler = arm_gic_get_irq_handler(irq_num);
 	irq_data = &irq_num;
 
 	if (*handler != NULL)
@@ -219,9 +179,5 @@
 
 void tftf_irq_setup(void)
 {
-	memset(spi_desc_table, 0, sizeof(spi_desc_table));
-	memset(ppi_desc_table, 0, sizeof(ppi_desc_table));
-	memset(sgi_desc_table, 0, sizeof(sgi_desc_table));
-	memset(&spurious_desc_handler, 0, sizeof(spurious_desc_handler));
-	init_spinlock(&spi_lock);
+	init_spinlock(&shared_irq_lock);
 }
diff --git a/tftf/framework/timer/timer_framework.c b/tftf/framework/timer/timer_framework.c
index 5830b5a..0972ceb 100644
--- a/tftf/framework/timer/timer_framework.c
+++ b/tftf/framework/timer/timer_framework.c
@@ -97,7 +97,7 @@
 
 void tftf_initialise_timer_secondary_core(void)
 {
-	if (!IS_SPI(TIMER_IRQ)) {
+	if (!arm_gic_is_irq_shared(TIMER_IRQ)) {
 		arm_gic_set_intr_priority(TIMER_IRQ, GIC_HIGHEST_NS_PRIORITY);
 		arm_gic_intr_enable(TIMER_IRQ);
 	}
@@ -186,7 +186,7 @@
 	if ((!get_current_prog_time()) || (interrupt_req_time[core_pos] <
 				(get_current_prog_time() - TIMER_STEP_VALUE))) {
 
-		if (IS_SPI(TIMER_IRQ)) {
+		if (arm_gic_is_irq_shared(TIMER_IRQ)) {
 			arm_gic_set_intr_target(TIMER_IRQ, core_pos);
 		}
 
diff --git a/tftf/tests/framework_validation_tests/test_timer_framework.c b/tftf/tests/framework_validation_tests/test_timer_framework.c
index 59a22b1..51b6f9b 100644
--- a/tftf/tests/framework_validation_tests/test_timer_framework.c
+++ b/tftf/tests/framework_validation_tests/test_timer_framework.c
@@ -7,6 +7,7 @@
 #include <arch.h>
 #include <arch_helpers.h>
 #include <debug.h>
+#include <drivers/arm/arm_gic.h>
 #include <events.h>
 #include <irq.h>
 #include <mmio.h>
diff --git a/tftf/tests/runtime_services/standard_service/sdei/system_tests/test_sdei_bind_failure.c b/tftf/tests/runtime_services/standard_service/sdei/system_tests/test_sdei_bind_failure.c
index a553adf..6a8c0e0 100644
--- a/tftf/tests/runtime_services/standard_service/sdei/system_tests/test_sdei_bind_failure.c
+++ b/tftf/tests/runtime_services/standard_service/sdei/system_tests/test_sdei_bind_failure.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2024, Arm Limited. All rights reserved.
+ * Copyright (c) 2024-2025, Arm Limited. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
@@ -8,6 +8,7 @@
 #include <tftf_lib.h>
 #include <sdei.h>
 #include <drivers/arm/arm_gic.h>
+#include <drivers/arm/gic_v2v3_common.h>
 
 /*
  * Only this many events can be bound in the PPI range. If you attempt to bind