GIC: Add APIs to set interrupt type and query support
authorJeenu Viswambharan <jeenu.viswambharan@arm.com>
Fri, 22 Sep 2017 07:32:09 +0000 (08:32 +0100)
committerJeenu Viswambharan <jeenu.viswambharan@arm.com>
Mon, 16 Oct 2017 15:50:01 +0000 (16:50 +0100)
The back end GIC driver converts and assigns the interrupt type to
suitable group.

For GICv2, a build option GICV2_G0_FOR_EL3 is introduced, which
determines to which type Group 0 interrupts maps to.

 - When the build option is set 0 (the default), Group 0 interrupts are
   meant for Secure EL1. This is presently the case.

 - Otherwise, Group 0 interrupts are meant for EL3. This means the SPD
   will have to synchronously hand over the interrupt to Secure EL1.

The query API allows the platform to query whether the platform supports
interrupts of a given type.

API documentation updated.

Change-Id: I60fdb4053ffe0bd006b3b20914914ebd311fc858
Co-authored-by: Yousuf A <yousuf.sait@arm.com>
Signed-off-by: Jeenu Viswambharan <jeenu.viswambharan@arm.com>
Makefile
docs/platform-interrupt-controller-API.rst
docs/user-guide.rst
drivers/arm/gic/v2/gicv2_main.c
drivers/arm/gic/v3/gicv3_main.c
include/drivers/arm/gicv2.h
include/drivers/arm/gicv3.h
include/plat/common/platform.h
make_helpers/defaults.mk
plat/common/plat_gicv2.c
plat/common/plat_gicv3.c

index a7d3a87892685d05c1daf5353d851216f121e500..b32b417b6f35b5edd9cbc6b1fe5ee6df77d8efd7 100644 (file)
--- 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))
index 3161c20a275a2d2d75cc1135b5385c995d46decc..3d46cf3441b2ccd4f34613df75495c0ca1b92a93 100644 (file)
@@ -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.*
index 67af42562c04a63b3c5635a5086bb49b714f872d..80273be2b54f648633755677c130e6d319f614c0 100644 (file)
@@ -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.
 
index 4861e02df92781f8e070342a226573e32de9ca28..8048c6a7c8879d0dede7e7444ea09b8a64594a11 100644 (file)
 #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);
+}
index d0ecab6158828150599749aed45f30cb2ce4b297..04b471290b6fff56cee207b8392107bad82bdf90 100644 (file)
@@ -9,11 +9,19 @@
 #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);
+       }
+}
index 91674fd36a590b0dcecdde4d4414e4b1cb1a53ef..229355ccf886d1ec4c442586aa0f8434141fea15 100644 (file)
 /*******************************************************************************
  * 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__ */
index f753ca265fde82002b43414be846c0c7908a0f2b..68430fb05bc4b8acaa34b44e67660e19fc2f58ab 100644 (file)
@@ -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__ */
index 03f529cb0dd4fd1608e18b81b2d10f555afdbdd9..c97b7d386f2297c439b85159868d64f9ce0966ba 100644 (file)
@@ -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);
 
 /*******************************************************************************
index 860104605ccef1e4b96bbe901309518be6838081..412c3b725734577b128ab1504632fb1732ce0de5 100644 (file)
@@ -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
index d50138edf4cd6dc9725fdab6e504f8b332cba0e2..c785d8317d8379ddf5429e0ebc1b063aef422809 100644 (file)
@@ -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);
+}
index 230d90ca95b04b888c568ff99b36265939749879..f4279ec4493ad72225dbf6ea49585cc5c59370f7 100644 (file)
@@ -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