From 74dce7fa6e42cab3aa54a9543e4a546c1450b2ae Mon Sep 17 00:00:00 2001 From: Jeenu Viswambharan Date: Fri, 22 Sep 2017 08:32:09 +0100 Subject: [PATCH] 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: Yousuf A Signed-off-by: Jeenu Viswambharan --- Makefile | 2 + docs/platform-interrupt-controller-API.rst | 74 ++++++++++++++++++++++ docs/user-guide.rst | 13 ++++ drivers/arm/gic/v2/gicv2_main.c | 33 ++++++++++ drivers/arm/gic/v3/gicv3_main.c | 68 ++++++++++++++++++++ include/drivers/arm/gicv2.h | 6 ++ include/drivers/arm/gicv3.h | 2 + include/plat/common/platform.h | 2 + make_helpers/defaults.mk | 4 ++ plat/common/plat_gicv2.c | 53 +++++++++++++++- plat/common/plat_gicv3.c | 13 ++++ 11 files changed, 268 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index a7d3a8789..b32b417b6 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 3161c20a2..3d46cf344 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 67af42562..80273be2b 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 4861e02df..8048c6a7c 100644 --- a/drivers/arm/gic/v2/gicv2_main.c +++ b/drivers/arm/gic/v2/gicv2_main.c @@ -10,11 +10,19 @@ #include #include #include +#include #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 d0ecab615..04b471290 100644 --- a/drivers/arm/gic/v3/gicv3_main.c +++ b/drivers/arm/gic/v3/gicv3_main.c @@ -9,11 +9,19 @@ #include #include #include +#include #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 91674fd36..229355ccf 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 f753ca265..68430fb05 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 03f529cb0..c97b7d386 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 860104605..412c3b725 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 d50138edf..c785d8317 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 230d90ca9..f4279ec44 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