Commit 74dce7fa authored by Jeenu Viswambharan's avatar Jeenu Viswambharan
Browse files

GIC: Add APIs to set interrupt type and query support



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: default avatarYousuf A <yousuf.sait@arm.com>
Signed-off-by: default avatarJeenu Viswambharan <jeenu.viswambharan@arm.com>
parent f3a86600
...@@ -449,6 +449,7 @@ $(eval $(call assert_boolean,ENABLE_RUNTIME_INSTRUMENTATION)) ...@@ -449,6 +449,7 @@ $(eval $(call assert_boolean,ENABLE_RUNTIME_INSTRUMENTATION))
$(eval $(call assert_boolean,ENABLE_SPE_FOR_LOWER_ELS)) $(eval $(call assert_boolean,ENABLE_SPE_FOR_LOWER_ELS))
$(eval $(call assert_boolean,ERROR_DEPRECATED)) $(eval $(call assert_boolean,ERROR_DEPRECATED))
$(eval $(call assert_boolean,GENERATE_COT)) $(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,HW_ASSISTED_COHERENCY))
$(eval $(call assert_boolean,LOAD_IMAGE_V2)) $(eval $(call assert_boolean,LOAD_IMAGE_V2))
$(eval $(call assert_boolean,NS_TIMER_SWITCH)) $(eval $(call assert_boolean,NS_TIMER_SWITCH))
...@@ -486,6 +487,7 @@ $(eval $(call add_define,ENABLE_PSCI_STAT)) ...@@ -486,6 +487,7 @@ $(eval $(call add_define,ENABLE_PSCI_STAT))
$(eval $(call add_define,ENABLE_RUNTIME_INSTRUMENTATION)) $(eval $(call add_define,ENABLE_RUNTIME_INSTRUMENTATION))
$(eval $(call add_define,ENABLE_SPE_FOR_LOWER_ELS)) $(eval $(call add_define,ENABLE_SPE_FOR_LOWER_ELS))
$(eval $(call add_define,ERROR_DEPRECATED)) $(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,HW_ASSISTED_COHERENCY))
$(eval $(call add_define,LOAD_IMAGE_V2)) $(eval $(call add_define,LOAD_IMAGE_V2))
$(eval $(call add_define,LOG_LEVEL)) $(eval $(call add_define,LOG_LEVEL))
......
...@@ -126,6 +126,80 @@ This API should set the priority of the interrupt specified by first parameter ...@@ -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 In case of ARM standard platforms using GIC, the implementation of the API
writes to GIC *Priority Register* set interrupt priority. 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.* *Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.*
...@@ -386,6 +386,19 @@ Common build options ...@@ -386,6 +386,19 @@ Common build options
images will include support for Trusted Board Boot, but the FIP and FWU\_FIP images will include support for Trusted Board Boot, but the FIP and FWU\_FIP
will not include the corresponding certificates, causing a boot failure. 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 - ``HANDLE_EA_EL3_FIRST``: When defined External Aborts and SError Interrupts
will be always trapped in EL3 i.e. in BL31 at runtime. will be always trapped in EL3 i.e. in BL31 at runtime.
......
...@@ -10,11 +10,19 @@ ...@@ -10,11 +10,19 @@
#include <debug.h> #include <debug.h>
#include <gic_common.h> #include <gic_common.h>
#include <gicv2.h> #include <gicv2.h>
#include <spinlock.h>
#include "../common/gic_common_private.h" #include "../common/gic_common_private.h"
#include "gicv2_private.h" #include "gicv2_private.h"
static const gicv2_driver_data_t *driver_data; 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 * 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. * 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) ...@@ -335,3 +343,28 @@ void gicv2_set_interrupt_priority(unsigned int id, unsigned int priority)
gicd_set_ipriorityr(driver_data->gicd_base, id, 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);
}
...@@ -9,11 +9,19 @@ ...@@ -9,11 +9,19 @@
#include <assert.h> #include <assert.h>
#include <debug.h> #include <debug.h>
#include <gicv3.h> #include <gicv3.h>
#include <spinlock.h>
#include "gicv3_private.h" #include "gicv3_private.h"
const gicv3_driver_data_t *gicv3_driver_data; const gicv3_driver_data_t *gicv3_driver_data;
static unsigned int gicv2_compat; 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 * Redistributor power operations are weakly bound so that they can be
* overridden * overridden
...@@ -892,3 +900,63 @@ void gicv3_set_interrupt_priority(unsigned int id, unsigned int proc_num, ...@@ -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); 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);
}
}
...@@ -10,6 +10,11 @@ ...@@ -10,6 +10,11 @@
/******************************************************************************* /*******************************************************************************
* GICv2 miscellaneous definitions * 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 */ /* Interrupt IDs reported by the HPPIR and IAR registers */
#define PENDING_G1_INTID 1022 #define PENDING_G1_INTID 1022
...@@ -151,6 +156,7 @@ unsigned int gicv2_get_interrupt_active(unsigned int id); ...@@ -151,6 +156,7 @@ unsigned int gicv2_get_interrupt_active(unsigned int id);
void gicv2_enable_interrupt(unsigned int id); void gicv2_enable_interrupt(unsigned int id);
void gicv2_disable_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_priority(unsigned int id, unsigned int priority);
void gicv2_set_interrupt_type(unsigned int id, unsigned int type);
#endif /* __ASSEMBLY__ */ #endif /* __ASSEMBLY__ */
#endif /* __GICV2_H__ */ #endif /* __GICV2_H__ */
...@@ -355,6 +355,8 @@ void gicv3_enable_interrupt(unsigned int id, unsigned int proc_num); ...@@ -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_disable_interrupt(unsigned int id, unsigned int proc_num);
void gicv3_set_interrupt_priority(unsigned int id, unsigned int proc_num, void gicv3_set_interrupt_priority(unsigned int id, unsigned int proc_num,
unsigned int priority); unsigned int priority);
void gicv3_set_interrupt_type(unsigned int id, unsigned int proc_num,
unsigned int group);
#endif /* __ASSEMBLY__ */ #endif /* __ASSEMBLY__ */
#endif /* __GICV3_H__ */ #endif /* __GICV3_H__ */
...@@ -79,6 +79,8 @@ int plat_ic_is_sgi(unsigned int id); ...@@ -79,6 +79,8 @@ int plat_ic_is_sgi(unsigned int id);
unsigned int plat_ic_get_interrupt_active(unsigned int id); unsigned int plat_ic_get_interrupt_active(unsigned int id);
void plat_ic_disable_interrupt(unsigned int id); void plat_ic_disable_interrupt(unsigned int id);
void plat_ic_enable_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); void plat_ic_set_interrupt_priority(unsigned int id, unsigned int priority);
/******************************************************************************* /*******************************************************************************
......
...@@ -77,6 +77,10 @@ FWU_FIP_NAME := fwu_fip.bin ...@@ -77,6 +77,10 @@ FWU_FIP_NAME := fwu_fip.bin
# For Chain of Trust # For Chain of Trust
GENERATE_COT := 0 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 # Whether system coherency is managed in hardware, without explicit software
# operations. # operations.
HW_ASSISTED_COHERENCY := 0 HW_ASSISTED_COHERENCY := 0
......
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
#pragma weak plat_ic_enable_interrupt #pragma weak plat_ic_enable_interrupt
#pragma weak plat_ic_disable_interrupt #pragma weak plat_ic_disable_interrupt
#pragma weak plat_ic_set_interrupt_priority #pragma weak plat_ic_set_interrupt_priority
#pragma weak plat_ic_set_interrupt_type
/* /*
* This function returns the highest priority pending interrupt at * This function returns the highest priority pending interrupt at
...@@ -62,8 +63,13 @@ uint32_t plat_ic_get_pending_interrupt_type(void) ...@@ -62,8 +63,13 @@ uint32_t plat_ic_get_pending_interrupt_type(void)
id = gicv2_get_pending_interrupt_type(); id = gicv2_get_pending_interrupt_type();
/* Assume that all secure interrupts are S-EL1 interrupts */ /* 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; return INTR_TYPE_S_EL1;
#endif
}
if (id == GIC_SPURIOUS_INTERRUPT) if (id == GIC_SPURIOUS_INTERRUPT)
return INTR_TYPE_INVAL; return INTR_TYPE_INVAL;
...@@ -92,7 +98,12 @@ uint32_t plat_ic_get_interrupt_type(uint32_t id) ...@@ -92,7 +98,12 @@ uint32_t plat_ic_get_interrupt_type(uint32_t id)
type = gicv2_get_interrupt_group(id); type = gicv2_get_interrupt_group(id);
/* Assume that all secure interrupts are S-EL1 interrupts */ /* 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) ...@@ -171,3 +182,41 @@ void plat_ic_set_interrupt_priority(unsigned int id, unsigned int priority)
{ {
gicv2_set_interrupt_priority(id, 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);
}
...@@ -34,6 +34,7 @@ ...@@ -34,6 +34,7 @@
#pragma weak plat_ic_enable_interrupt #pragma weak plat_ic_enable_interrupt
#pragma weak plat_ic_disable_interrupt #pragma weak plat_ic_disable_interrupt
#pragma weak plat_ic_set_interrupt_priority #pragma weak plat_ic_set_interrupt_priority
#pragma weak plat_ic_set_interrupt_type
CASSERT((INTR_TYPE_S_EL1 == INTR_GROUP1S) && CASSERT((INTR_TYPE_S_EL1 == INTR_GROUP1S) &&
(INTR_TYPE_NS == INTR_GROUP1NS) && (INTR_TYPE_NS == INTR_GROUP1NS) &&
...@@ -204,6 +205,18 @@ void plat_ic_set_interrupt_priority(unsigned int id, unsigned int priority) ...@@ -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); 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 #endif
#ifdef IMAGE_BL32 #ifdef IMAGE_BL32
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment