aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--docs/platform-interrupt-controller-API.rst74
-rw-r--r--docs/user-guide.rst13
-rw-r--r--drivers/arm/gic/v2/gicv2_main.c33
-rw-r--r--drivers/arm/gic/v3/gicv3_main.c68
-rw-r--r--include/drivers/arm/gicv2.h6
-rw-r--r--include/drivers/arm/gicv3.h2
-rw-r--r--include/plat/common/platform.h2
-rw-r--r--make_helpers/defaults.mk4
-rw-r--r--plat/common/plat_gicv2.c53
-rw-r--r--plat/common/plat_gicv3.c13
11 files changed, 268 insertions, 2 deletions
diff --git a/Makefile b/Makefile
index a7d3a87892..b32b417b6f 100644
--- a/Makefile
+++ b/Makefile
@@ -449,6 +449,7 @@ $(eval $(call assert_boolean,ENABLE_RUNTIME_INSTRUMENTATION))
$(eval $(call assert_boolean,ENABLE_SPE_FOR_LOWER_ELS))
$(eval $(call assert_boolean,ERROR_DEPRECATED))
$(eval $(call assert_boolean,GENERATE_COT))
+$(eval $(call assert_boolean,GICV2_G0_FOR_EL3))
$(eval $(call assert_boolean,HW_ASSISTED_COHERENCY))
$(eval $(call assert_boolean,LOAD_IMAGE_V2))
$(eval $(call assert_boolean,NS_TIMER_SWITCH))
@@ -486,6 +487,7 @@ $(eval $(call add_define,ENABLE_PSCI_STAT))
$(eval $(call add_define,ENABLE_RUNTIME_INSTRUMENTATION))
$(eval $(call add_define,ENABLE_SPE_FOR_LOWER_ELS))
$(eval $(call add_define,ERROR_DEPRECATED))
+$(eval $(call add_define,GICV2_G0_FOR_EL3))
$(eval $(call add_define,HW_ASSISTED_COHERENCY))
$(eval $(call add_define,LOAD_IMAGE_V2))
$(eval $(call add_define,LOG_LEVEL))
diff --git a/docs/platform-interrupt-controller-API.rst b/docs/platform-interrupt-controller-API.rst
index 3161c20a27..3d46cf3441 100644
--- a/docs/platform-interrupt-controller-API.rst
+++ b/docs/platform-interrupt-controller-API.rst
@@ -126,6 +126,80 @@ This API should set the priority of the interrupt specified by first parameter
In case of ARM standard platforms using GIC, the implementation of the API
writes to GIC *Priority Register* set interrupt priority.
+Function: int plat_ic_has_interrupt_type(unsigned int type); [optional]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+::
+
+ Argument : unsigned int
+ Return : int
+
+This API should return whether the platform supports a given interrupt type. The
+parameter ``type`` shall be one of ``INTR_TYPE_EL3``, ``INTR_TYPE_S_EL1``, or
+``INTR_TYPE_NS``.
+
+In case of ARM standard platforms using GICv3, the implementation of the API
+returns ``1`` for all interrupt types.
+
+In case of ARM standard platforms using GICv2, the API always return ``1`` for
+``INTR_TYPE_NS``. Return value for other types depends on the value of build
+option ``GICV2_G0_FOR_EL3``:
+
+- For interrupt type ``INTR_TYPE_EL3``:
+
+ - When ``GICV2_G0_FOR_EL3`` is ``0``, it returns ``0``, indicating no support
+ for EL3 interrupts.
+
+ - When ``GICV2_G0_FOR_EL3`` is ``1``, it returns ``1``, indicating support for
+ EL3 interrupts.
+
+- For interrupt type ``INTR_TYPE_S_EL1``:
+
+ - When ``GICV2_G0_FOR_EL3`` is ``0``, it returns ``1``, indicating support for
+ Secure EL1 interrupts.
+
+ - When ``GICV2_G0_FOR_EL3`` is ``1``, it returns ``0``, indicating no support
+ for Secure EL1 interrupts.
+
+Function: void plat_ic_set_interrupt_type(unsigned int id, unsigned int type); [optional]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+::
+
+ Argument : unsigned int
+ Argument : unsigned int
+ Return : void
+
+This API should set the interrupt specified by first parameter ``id`` to the
+type specified by second parameter ``type``. The ``type`` parameter can be
+one of:
+
+- ``INTR_TYPE_NS``: interrupt is meant to be consumed by the Non-secure world.
+
+- ``INTR_TYPE_S_EL1``: interrupt is meant to be consumed by Secure EL1.
+
+- ``INTR_TYPE_EL3``: interrupt is meant to be consumed by EL3.
+
+In case of ARM standard platforms using GIC, the implementation of the API
+writes to the GIC *Group Register* and *Group Modifier Register* (only GICv3) to
+assign the interrupt to the right group.
+
+For GICv3:
+
+- ``INTR_TYPE_NS`` maps to Group 1 interrupt.
+
+- ``INTR_TYPE_S_EL1`` maps to Secure Group 1 interrupt.
+
+- ``INTR_TYPE_EL3`` maps to Secure Group 0 interrupt.
+
+For GICv2:
+
+- ``INTR_TYPE_NS`` maps to Group 1 interrupt.
+
+- When the build option ``GICV2_G0_FOR_EL3`` is set to ``0`` (the default),
+ ``INTR_TYPE_S_EL1`` maps to Group 0. Otherwise, ``INTR_TYPE_EL3`` maps to
+ Group 0 interrupt.
+
----
*Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.*
diff --git a/docs/user-guide.rst b/docs/user-guide.rst
index 67af42562c..80273be2b5 100644
--- a/docs/user-guide.rst
+++ b/docs/user-guide.rst
@@ -386,6 +386,19 @@ Common build options
images will include support for Trusted Board Boot, but the FIP and FWU\_FIP
will not include the corresponding certificates, causing a boot failure.
+- ``GICV2_G0_FOR_EL3``: Unlike GICv3, the GICv2 architecture doesn't have
+ inherent support for specific EL3 type interrupts. Setting this build option
+ to ``1`` assumes GICv2 *Group 0* interrupts are expected to target EL3, both
+ by `platform abstraction layer`__ and `Interrupt Management Framework`__.
+ This allows GICv2 platforms to enable features requiring EL3 interrupt type.
+ This also means that all GICv2 Group 0 interrupts are delivered to EL3, and
+ the Secure Payload interrupts needs to be synchronously handed over to Secure
+ EL1 for handling. The default value of this option is ``0``, which means the
+ Group 0 interrupts are assumed to be handled by Secure EL1.
+
+ .. __: `platform-interrupt-controller-API.rst`
+ .. __: `interrupt-framework-design.rst`
+
- ``HANDLE_EA_EL3_FIRST``: When defined External Aborts and SError Interrupts
will be always trapped in EL3 i.e. in BL31 at runtime.
diff --git a/drivers/arm/gic/v2/gicv2_main.c b/drivers/arm/gic/v2/gicv2_main.c
index 4861e02df9..8048c6a7c8 100644
--- a/drivers/arm/gic/v2/gicv2_main.c
+++ b/drivers/arm/gic/v2/gicv2_main.c
@@ -10,11 +10,19 @@
#include <debug.h>
#include <gic_common.h>
#include <gicv2.h>
+#include <spinlock.h>
#include "../common/gic_common_private.h"
#include "gicv2_private.h"
static const gicv2_driver_data_t *driver_data;
+/*
+ * Spinlock to guard registers needing read-modify-write. APIs protected by this
+ * spinlock are used either at boot time (when only a single CPU is active), or
+ * when the system is fully coherent.
+ */
+spinlock_t gic_lock;
+
/*******************************************************************************
* Enable secure interrupts and use FIQs to route them. Disable legacy bypass
* and set the priority mask register to allow all interrupts to trickle in.
@@ -335,3 +343,28 @@ void gicv2_set_interrupt_priority(unsigned int id, unsigned int priority)
gicd_set_ipriorityr(driver_data->gicd_base, id, priority);
}
+
+/*******************************************************************************
+ * This function assigns group for the interrupt identified by id. The group can
+ * be any of GICV2_INTR_GROUP*
+ ******************************************************************************/
+void gicv2_set_interrupt_type(unsigned int id, unsigned int type)
+{
+ assert(driver_data);
+ assert(driver_data->gicd_base);
+ assert(id <= MAX_SPI_ID);
+
+ /* Serialize read-modify-write to Distributor registers */
+ spin_lock(&gic_lock);
+ switch (type) {
+ case GICV2_INTR_GROUP1:
+ gicd_set_igroupr(driver_data->gicd_base, id);
+ break;
+ case GICV2_INTR_GROUP0:
+ gicd_clr_igroupr(driver_data->gicd_base, id);
+ break;
+ default:
+ assert(0);
+ }
+ spin_unlock(&gic_lock);
+}
diff --git a/drivers/arm/gic/v3/gicv3_main.c b/drivers/arm/gic/v3/gicv3_main.c
index d0ecab6158..04b471290b 100644
--- a/drivers/arm/gic/v3/gicv3_main.c
+++ b/drivers/arm/gic/v3/gicv3_main.c
@@ -9,12 +9,20 @@
#include <assert.h>
#include <debug.h>
#include <gicv3.h>
+#include <spinlock.h>
#include "gicv3_private.h"
const gicv3_driver_data_t *gicv3_driver_data;
static unsigned int gicv2_compat;
/*
+ * Spinlock to guard registers needing read-modify-write. APIs protected by this
+ * spinlock are used either at boot time (when only a single CPU is active), or
+ * when the system is fully coherent.
+ */
+spinlock_t gic_lock;
+
+/*
* Redistributor power operations are weakly bound so that they can be
* overridden
*/
@@ -892,3 +900,63 @@ void gicv3_set_interrupt_priority(unsigned int id, unsigned int proc_num,
gicd_set_ipriorityr(gicv3_driver_data->gicd_base, id, priority);
}
}
+
+/*******************************************************************************
+ * This function assigns group for the interrupt identified by id. The proc_num
+ * is used if the interrupt is SGI or PPI, and programs the corresponding
+ * Redistributor interface. The group can be any of GICV3_INTR_GROUP*
+ ******************************************************************************/
+void gicv3_set_interrupt_type(unsigned int id, unsigned int proc_num,
+ unsigned int type)
+{
+ unsigned int igroup = 0, grpmod = 0;
+ uintptr_t gicr_base;
+
+ assert(gicv3_driver_data);
+ assert(gicv3_driver_data->gicd_base);
+ assert(proc_num < gicv3_driver_data->rdistif_num);
+ assert(gicv3_driver_data->rdistif_base_addrs);
+
+ switch (type) {
+ case INTR_GROUP1S:
+ igroup = 0;
+ grpmod = 1;
+ break;
+ case INTR_GROUP0:
+ igroup = 0;
+ grpmod = 0;
+ break;
+ case INTR_GROUP1NS:
+ igroup = 1;
+ grpmod = 0;
+ break;
+ default:
+ assert(0);
+ }
+
+ if (id < MIN_SPI_ID) {
+ gicr_base = gicv3_driver_data->rdistif_base_addrs[proc_num];
+ if (igroup)
+ gicr_set_igroupr0(gicr_base, id);
+ else
+ gicr_clr_igroupr0(gicr_base, id);
+
+ if (grpmod)
+ gicr_set_igrpmodr0(gicr_base, id);
+ else
+ gicr_clr_igrpmodr0(gicr_base, id);
+ } else {
+ /* Serialize read-modify-write to Distributor registers */
+ spin_lock(&gic_lock);
+ if (igroup)
+ gicd_set_igroupr(gicv3_driver_data->gicd_base, id);
+ else
+ gicd_clr_igroupr(gicv3_driver_data->gicd_base, id);
+
+ if (grpmod)
+ gicd_set_igrpmodr(gicv3_driver_data->gicd_base, id);
+ else
+ gicd_clr_igrpmodr(gicv3_driver_data->gicd_base, id);
+ spin_unlock(&gic_lock);
+ }
+}
diff --git a/include/drivers/arm/gicv2.h b/include/drivers/arm/gicv2.h
index 91674fd36a..229355ccf8 100644
--- a/include/drivers/arm/gicv2.h
+++ b/include/drivers/arm/gicv2.h
@@ -10,6 +10,11 @@
/*******************************************************************************
* GICv2 miscellaneous definitions
******************************************************************************/
+
+/* Interrupt group definitions */
+#define GICV2_INTR_GROUP0 0
+#define GICV2_INTR_GROUP1 1
+
/* Interrupt IDs reported by the HPPIR and IAR registers */
#define PENDING_G1_INTID 1022
@@ -151,6 +156,7 @@ unsigned int gicv2_get_interrupt_active(unsigned int id);
void gicv2_enable_interrupt(unsigned int id);
void gicv2_disable_interrupt(unsigned int id);
void gicv2_set_interrupt_priority(unsigned int id, unsigned int priority);
+void gicv2_set_interrupt_type(unsigned int id, unsigned int type);
#endif /* __ASSEMBLY__ */
#endif /* __GICV2_H__ */
diff --git a/include/drivers/arm/gicv3.h b/include/drivers/arm/gicv3.h
index f753ca265f..68430fb05b 100644
--- a/include/drivers/arm/gicv3.h
+++ b/include/drivers/arm/gicv3.h
@@ -355,6 +355,8 @@ void gicv3_enable_interrupt(unsigned int id, unsigned int proc_num);
void gicv3_disable_interrupt(unsigned int id, unsigned int proc_num);
void gicv3_set_interrupt_priority(unsigned int id, unsigned int proc_num,
unsigned int priority);
+void gicv3_set_interrupt_type(unsigned int id, unsigned int proc_num,
+ unsigned int group);
#endif /* __ASSEMBLY__ */
#endif /* __GICV3_H__ */
diff --git a/include/plat/common/platform.h b/include/plat/common/platform.h
index 03f529cb0d..c97b7d386f 100644
--- a/include/plat/common/platform.h
+++ b/include/plat/common/platform.h
@@ -79,6 +79,8 @@ int plat_ic_is_sgi(unsigned int id);
unsigned int plat_ic_get_interrupt_active(unsigned int id);
void plat_ic_disable_interrupt(unsigned int id);
void plat_ic_enable_interrupt(unsigned int id);
+int plat_ic_has_interrupt_type(unsigned int type);
+void plat_ic_set_interrupt_type(unsigned int id, unsigned int type);
void plat_ic_set_interrupt_priority(unsigned int id, unsigned int priority);
/*******************************************************************************
diff --git a/make_helpers/defaults.mk b/make_helpers/defaults.mk
index 860104605c..412c3b7257 100644
--- a/make_helpers/defaults.mk
+++ b/make_helpers/defaults.mk
@@ -77,6 +77,10 @@ FWU_FIP_NAME := fwu_fip.bin
# For Chain of Trust
GENERATE_COT := 0
+# Hint platform interrupt control layer that Group 0 interrupts are for EL3. By
+# default, they are for Secure EL1.
+GICV2_G0_FOR_EL3 := 0
+
# Whether system coherency is managed in hardware, without explicit software
# operations.
HW_ASSISTED_COHERENCY := 0
diff --git a/plat/common/plat_gicv2.c b/plat/common/plat_gicv2.c
index d50138edf4..c785d8317d 100644
--- a/plat/common/plat_gicv2.c
+++ b/plat/common/plat_gicv2.c
@@ -28,6 +28,7 @@
#pragma weak plat_ic_enable_interrupt
#pragma weak plat_ic_disable_interrupt
#pragma weak plat_ic_set_interrupt_priority
+#pragma weak plat_ic_set_interrupt_type
/*
* This function returns the highest priority pending interrupt at
@@ -62,8 +63,13 @@ uint32_t plat_ic_get_pending_interrupt_type(void)
id = gicv2_get_pending_interrupt_type();
/* Assume that all secure interrupts are S-EL1 interrupts */
- if (id < PENDING_G1_INTID)
+ if (id < PENDING_G1_INTID) {
+#if GICV2_G0_FOR_EL3
+ return INTR_TYPE_EL3;
+#else
return INTR_TYPE_S_EL1;
+#endif
+ }
if (id == GIC_SPURIOUS_INTERRUPT)
return INTR_TYPE_INVAL;
@@ -92,7 +98,12 @@ uint32_t plat_ic_get_interrupt_type(uint32_t id)
type = gicv2_get_interrupt_group(id);
/* Assume that all secure interrupts are S-EL1 interrupts */
- return (type) ? INTR_TYPE_NS : INTR_TYPE_S_EL1;
+ return type == GICV2_INTR_GROUP1 ? INTR_TYPE_NS :
+#if GICV2_G0_FOR_EL3
+ INTR_TYPE_EL3;
+#else
+ INTR_TYPE_S_EL1;
+#endif
}
/*
@@ -171,3 +182,41 @@ void plat_ic_set_interrupt_priority(unsigned int id, unsigned int priority)
{
gicv2_set_interrupt_priority(id, priority);
}
+
+int plat_ic_has_interrupt_type(unsigned int type)
+{
+ switch (type) {
+#if GICV2_G0_FOR_EL3
+ case INTR_TYPE_EL3:
+#else
+ case INTR_TYPE_S_EL1:
+#endif
+ case INTR_TYPE_NS:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+void plat_ic_set_interrupt_type(unsigned int id, unsigned int type)
+{
+ int gicv2_type = 0;
+
+ /* Map canonical interrupt type to GICv2 type */
+ switch (type) {
+#if GICV2_G0_FOR_EL3
+ case INTR_TYPE_EL3:
+#else
+ case INTR_TYPE_S_EL1:
+#endif
+ gicv2_type = GICV2_INTR_GROUP0;
+ break;
+ case INTR_TYPE_NS:
+ gicv2_type = GICV2_INTR_GROUP1;
+ break;
+ default:
+ assert(0);
+ }
+
+ gicv2_set_interrupt_type(id, gicv2_type);
+}
diff --git a/plat/common/plat_gicv3.c b/plat/common/plat_gicv3.c
index 230d90ca95..f4279ec449 100644
--- a/plat/common/plat_gicv3.c
+++ b/plat/common/plat_gicv3.c
@@ -34,6 +34,7 @@
#pragma weak plat_ic_enable_interrupt
#pragma weak plat_ic_disable_interrupt
#pragma weak plat_ic_set_interrupt_priority
+#pragma weak plat_ic_set_interrupt_type
CASSERT((INTR_TYPE_S_EL1 == INTR_GROUP1S) &&
(INTR_TYPE_NS == INTR_GROUP1NS) &&
@@ -204,6 +205,18 @@ void plat_ic_set_interrupt_priority(unsigned int id, unsigned int priority)
{
gicv3_set_interrupt_priority(id, plat_my_core_pos(), priority);
}
+
+int plat_ic_has_interrupt_type(unsigned int type)
+{
+ assert((type == INTR_TYPE_EL3) || (type == INTR_TYPE_S_EL1) ||
+ (type == INTR_TYPE_NS));
+ return 1;
+}
+
+void plat_ic_set_interrupt_type(unsigned int id, unsigned int type)
+{
+ gicv3_set_interrupt_type(id, plat_my_core_pos(), type);
+}
#endif
#ifdef IMAGE_BL32