plat/fvp: Add support for dynamic description of secure interrupts

Using the fconf framework, the Group 0 and Group 1 secure interrupt
descriptors are moved to device tree and retrieved in runtime. This
feature is enabled by the build flag SEC_INT_DESC_IN_FCONF.

Change-Id: I360c63a83286c7ecc2426cd1ff1b4746d61e633c
Signed-off-by: Madhukar Pappireddy <madhukar.pappireddy@arm.com>
diff --git a/plat/arm/board/fvp/fvp_gicv3.c b/plat/arm/board/fvp/fvp_gicv3.c
index a3ee8ef..3e04d6b 100644
--- a/plat/arm/board/fvp/fvp_gicv3.c
+++ b/plat/arm/board/fvp/fvp_gicv3.c
@@ -12,6 +12,7 @@
 #include <fconf_hw_config_getter.h>
 #include <lib/utils.h>
 #include <plat/arm/common/plat_arm.h>
+#include <plat/arm/common/fconf_sec_intr_config.h>
 #include <plat/common/platform.h>
 
 /* The GICv3 driver only needs to be initialized in EL3 */
@@ -23,10 +24,13 @@
 /* List of zero terminated GICR frame addresses which CPUs will probe */
 static uint64_t *fvp_gicr_frames = fvp_gicr_base_addrs;
 
+#if  !(SEC_INT_DESC_IN_FCONF && ((!defined(__aarch64__) && defined(IMAGE_BL32)) || \
+	(defined(__aarch64__) && defined(IMAGE_BL31))))
 static const interrupt_prop_t fvp_interrupt_props[] = {
 	PLAT_ARM_G1S_IRQ_PROPS(INTR_GROUP1S),
 	PLAT_ARM_G0_IRQ_PROPS(INTR_GROUP0)
 };
+#endif
 
 /*
  * MPIDR hashing function for translating MPIDRs read from GICR_TYPER register
@@ -52,8 +56,6 @@
 
 
 static gicv3_driver_data_t fvp_gic_data = {
-	.interrupt_props = fvp_interrupt_props,
-	.interrupt_props_num = ARRAY_SIZE(fvp_interrupt_props),
 	.rdistif_num = PLATFORM_CORE_COUNT,
 	.rdistif_base_addrs = fvp_rdistif_base_addrs,
 	.mpidr_to_core_pos = fvp_gicv3_mpidr_hash
@@ -61,7 +63,10 @@
 
 void plat_arm_gic_driver_init(void)
 {
-	/* Get GICD and GICR base addressed through FCONF APIs */
+	/*
+	 * Get GICD and GICR base addressed through FCONF APIs.
+	 * FCONF is not supported in BL32 for FVP.
+	 */
 #if (!defined(__aarch64__) && defined(IMAGE_BL32)) || \
 	(defined(__aarch64__) && defined(IMAGE_BL31))
 	fvp_gic_data.gicd_base = (uintptr_t)FCONF_GET_PROPERTY(hw_config,
@@ -69,9 +74,20 @@
 							       gicd_base);
 	fvp_gicr_base_addrs[0] = FCONF_GET_PROPERTY(hw_config, gicv3_config,
 						    gicr_base);
+#if SEC_INT_DESC_IN_FCONF
+	fvp_gic_data.interrupt_props = FCONF_GET_PROPERTY(hw_config,
+					sec_intr_prop, descriptor);
+	fvp_gic_data.interrupt_props_num = FCONF_GET_PROPERTY(hw_config,
+					sec_intr_prop, count);
+#else
+	fvp_gic_data.interrupt_props = fvp_interrupt_props;
+	fvp_gic_data.interrupt_props_num = ARRAY_SIZE(fvp_interrupt_props);
+#endif
 #else
 	fvp_gic_data.gicd_base = PLAT_ARM_GICD_BASE;
 	fvp_gicr_base_addrs[0] = PLAT_ARM_GICR_BASE;
+	fvp_gic_data.interrupt_props = fvp_interrupt_props;
+	fvp_gic_data.interrupt_props_num = ARRAY_SIZE(fvp_interrupt_props);
 #endif
 
 	/*
diff --git a/plat/arm/board/fvp/platform.mk b/plat/arm/board/fvp/platform.mk
index 024e682..e605608 100644
--- a/plat/arm/board/fvp/platform.mk
+++ b/plat/arm/board/fvp/platform.mk
@@ -221,6 +221,11 @@
 BL31_SOURCES		+=	common/fdt_wrappers.c				\
 				lib/fconf/fconf.c				\
 				plat/arm/board/fvp/fconf/fconf_hw_config_getter.c
+
+ifeq (${SEC_INT_DESC_IN_FCONF},1)
+BL31_SOURCES		+=	plat/arm/common/fconf/fconf_sec_intr_config.c
+endif
+
 endif
 
 ifeq (${FVP_USE_SP804_TIMER},1)
diff --git a/plat/arm/board/fvp/sp_min/sp_min-fvp.mk b/plat/arm/board/fvp/sp_min/sp_min-fvp.mk
index ba6ceec..64cb7ad 100644
--- a/plat/arm/board/fvp/sp_min/sp_min-fvp.mk
+++ b/plat/arm/board/fvp/sp_min/sp_min-fvp.mk
@@ -25,6 +25,11 @@
 BL32_SOURCES		+=	common/fdt_wrappers.c				\
 				lib/fconf/fconf.c				\
 				plat/arm/board/fvp/fconf/fconf_hw_config_getter.c
+
+ifeq (${SEC_INT_DESC_IN_FCONF},1)
+BL32_SOURCES		+=	plat/arm/common/fconf/fconf_sec_intr_config.c
+endif
+
 endif
 
 include plat/arm/common/sp_min/arm_sp_min.mk
diff --git a/plat/arm/common/fconf/fconf_sec_intr_config.c b/plat/arm/common/fconf/fconf_sec_intr_config.c
new file mode 100644
index 0000000..f28be24
--- /dev/null
+++ b/plat/arm/common/fconf/fconf_sec_intr_config.c
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2020, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+#include <assert.h>
+
+#include <common/debug.h>
+#include <common/fdt_wrappers.h>
+#include <libfdt.h>
+#include <plat/arm/common/fconf_sec_intr_config.h>
+
+#define G0_INTR_NUM(i)		g0_intr_prop[3U * (i)]
+#define G0_INTR_PRIORITY(i)	g0_intr_prop[3U * (i) + 1]
+#define G0_INTR_CONFIG(i)	g0_intr_prop[3U * (i) + 2]
+
+#define G1S_INTR_NUM(i)		g1s_intr_prop[3U * (i)]
+#define G1S_INTR_PRIORITY(i)	g1s_intr_prop[3U * (i) + 1]
+#define G1S_INTR_CONFIG(i)	g1s_intr_prop[3U * (i) + 2]
+
+struct sec_intr_prop_t sec_intr_prop;
+
+static void print_intr_prop(interrupt_prop_t prop)
+{
+	VERBOSE("FCONF: Secure Interrupt NUM: %d, PRI: %d, TYPE: %d\n",
+		prop.intr_num, prop.intr_pri, prop.intr_cfg);
+}
+
+int fconf_populate_sec_intr_config(uintptr_t config)
+{
+	int node, err;
+	uint32_t g0_intr_count, g1s_intr_count;
+	uint32_t g0_intr_prop[SEC_INT_COUNT_MAX * 3];
+	uint32_t g1s_intr_prop[SEC_INT_COUNT_MAX * 3];
+
+	/* Necessary to work with libfdt APIs */
+	const void *hw_config_dtb = (const void *)config;
+
+	node = fdt_node_offset_by_compatible(hw_config_dtb, -1,
+						"arm,secure_interrupt_desc");
+	if (node < 0) {
+		ERROR("FCONF: Unable to locate node with %s compatible property\n",
+						"arm,secure_interrupt_desc");
+		return node;
+	}
+
+	/* Read number of Group 0 interrupts specified by platform */
+	err = fdt_read_uint32(hw_config_dtb, node, "g0_intr_cnt", &g0_intr_count);
+	if (err < 0) {
+		ERROR("FCONF: Could not locate g0s_intr_cnt property\n");
+		return err;
+	}
+
+	/* At least 1 Group 0 interrupt description has to be provided*/
+	if (g0_intr_count < 1U) {
+		ERROR("FCONF: Invalid number of Group 0 interrupts count specified\n");
+		return -1;
+	}
+
+	/* Read number of Group 1 secure interrupts specified by platform */
+	err = fdt_read_uint32(hw_config_dtb, node, "g1s_intr_cnt",
+				&g1s_intr_count);
+	if (err < 0) {
+		ERROR("FCONF: Could not locate g1s_intr_cnt property\n");
+		return err;
+	}
+
+	/* At least one Group 1 interrupt description has to be provided*/
+	if (g1s_intr_count < 1U) {
+		ERROR("FCONF: Invalid number of Group 1 secure interrupts count specified\n");
+		return -1;
+	}
+
+	/*
+	 * Check if the total number of secure interrupts described are within
+	 * the limit defined statically by the platform.
+	 */
+	if ((g0_intr_count + g1s_intr_count) > SEC_INT_COUNT_MAX) {
+		ERROR("FCONF: Total number of secure interrupts exceed limit the of %d\n",
+				SEC_INT_COUNT_MAX);
+		return -1;
+	}
+
+	sec_intr_prop.count = g0_intr_count + g1s_intr_count;
+
+	/* Read the Group 0 interrupt descriptors */
+	err = fdt_read_uint32_array(hw_config_dtb, node, "g0_intr_desc",
+				g0_intr_count * 3, g0_intr_prop);
+	if (err < 0) {
+		ERROR("FCONF: Read cell failed for 'g0s_intr_desc': %d\n", err);
+		return err;
+	}
+
+	/* Read the Group 1 secure interrupt descriptors */
+	err = fdt_read_uint32_array(hw_config_dtb, node, "g1s_intr_desc",
+				g1s_intr_count * 3, g1s_intr_prop);
+	if (err < 0) {
+		ERROR("FCONF: Read cell failed for 'g1s_intr_desc': %d\n", err);
+		return err;
+	}
+
+	/* Populate Group 0 interrupt descriptors into fconf based C struct */
+	for (uint32_t i = 0; i < g0_intr_count; i++) {
+		interrupt_prop_t sec_intr_property;
+
+		/* Secure Interrupt Group: INTR_GROUP0 i.e., 0x1 */
+		sec_intr_property.intr_grp = 1;
+		sec_intr_property.intr_num = G0_INTR_NUM(i);
+		sec_intr_property.intr_pri = G0_INTR_PRIORITY(i);
+		sec_intr_property.intr_cfg = G0_INTR_CONFIG(i);
+		sec_intr_prop.descriptor[i] = sec_intr_property;
+		print_intr_prop(sec_intr_property);
+	}
+
+	/* Populate G1 secure interrupt descriptors into fconf based C struct */
+	for (uint32_t i = 0; i < g1s_intr_count; i++) {
+		interrupt_prop_t sec_intr_property;
+
+		/* Secure Interrupt Group: INTR_GROUP1S i.e., 0x0 */
+		sec_intr_property.intr_grp = 0;
+		sec_intr_property.intr_num = G1S_INTR_NUM(i);
+		sec_intr_property.intr_pri = G1S_INTR_PRIORITY(i);
+		sec_intr_property.intr_cfg = G1S_INTR_CONFIG(i);
+		sec_intr_prop.descriptor[i + g0_intr_count] = sec_intr_property;
+		print_intr_prop(sec_intr_property);
+	}
+
+	return 0;
+}
+
+FCONF_REGISTER_POPULATOR(HW_CONFIG, sec_intr_prop, fconf_populate_sec_intr_config);