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