From eb68ea9b10c190c237216dee92166f9e7b2ce3d4 Mon Sep 17 00:00:00 2001 From: Jeenu Viswambharan Date: Fri, 22 Sep 2017 08:32:09 +0100 Subject: [PATCH 01/14] GIC: Add API to get running priority Document the API in separate platform interrupt controller API document. Change-Id: If18f208e10a8a243f5c59d226fcf48e985941949 Co-authored-by: Yousuf A Signed-off-by: Jeenu Viswambharan --- docs/platform-interrupt-controller-API.rst | 32 ++++++++++++++++++++++ docs/porting-guide.rst | 6 +++- drivers/arm/gic/v2/gicv2_main.c | 12 ++++++++ drivers/arm/gic/v2/gicv2_private.h | 7 ++++- drivers/arm/gic/v3/gicv3_main.c | 9 ++++++ include/drivers/arm/gicv2.h | 3 +- include/drivers/arm/gicv3.h | 2 ++ include/lib/aarch32/arch_helpers.h | 1 + include/lib/aarch64/arch.h | 1 + include/lib/aarch64/arch_helpers.h | 1 + include/plat/common/platform.h | 5 ++++ plat/common/plat_gicv2.c | 9 +++++- plat/common/plat_gicv3.c | 10 ++++++- 13 files changed, 93 insertions(+), 5 deletions(-) create mode 100644 docs/platform-interrupt-controller-API.rst diff --git a/docs/platform-interrupt-controller-API.rst b/docs/platform-interrupt-controller-API.rst new file mode 100644 index 000000000..9ef2e3f67 --- /dev/null +++ b/docs/platform-interrupt-controller-API.rst @@ -0,0 +1,32 @@ +Platform Interrupt Controller API documentation +=============================================== + +.. section-numbering:: + :suffix: . + +.. contents:: + +This document lists the optional platform interrupt controller API that +abstracts the runtime configuration and control of interrupt controller from the +generic code. The mandatory APIs are described in the `porting guide`__. + +.. __: porting-guide.rst#interrupt-management-framework-in-bl31 + +Function: unsigned int plat_ic_get_running_priority(void); [optional] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +:: + + Argument : void + Return : unsigned int + +This API should return the priority of the interrupt the PE is currently +servicing. This must be be called only after an interrupt has already been +acknowledged via. ``plat_ic_acknowledge_interrupt``. + +In the case of ARM standard platforms using GIC, the *Running Priority Register* +is read to determine the priority of the interrupt. + +---- + +*Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.* diff --git a/docs/porting-guide.rst b/docs/porting-guide.rst index 6c07b2eb3..c0b173c6a 100644 --- a/docs/porting-guide.rst +++ b/docs/porting-guide.rst @@ -2289,6 +2289,10 @@ Standard layer to use GICv2 and the FVP can be configured to use either GICv2 or GICv3 depending on the build flag ``FVP_USE_GIC_DRIVER`` (See FVP platform specific build options in `User Guide`_ for more details). +See also: `Interrupt Controller Abstraction APIs`__. + +.. __: platform-interrupt-controller-API.rst + Function : plat\_interrupt\_type\_to\_line() [mandatory] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -2636,7 +2640,7 @@ amount of open resources per driver. -------------- -*Copyright (c) 2013-2016, ARM Limited and Contributors. All rights reserved.* +*Copyright (c) 2013-2017, ARM Limited and Contributors. All rights reserved.* .. _Migration Guide: platform-migration-guide.rst .. _include/plat/common/platform.h: ../include/plat/common/platform.h diff --git a/drivers/arm/gic/v2/gicv2_main.c b/drivers/arm/gic/v2/gicv2_main.c index deac927ca..7759a551a 100644 --- a/drivers/arm/gic/v2/gicv2_main.c +++ b/drivers/arm/gic/v2/gicv2_main.c @@ -240,3 +240,15 @@ unsigned int gicv2_get_interrupt_group(unsigned int id) return gicd_get_igroupr(driver_data->gicd_base, id); } + +/******************************************************************************* + * This function returns the priority of the interrupt the processor is + * currently servicing. + ******************************************************************************/ +unsigned int gicv2_get_running_priority(void) +{ + assert(driver_data); + assert(driver_data->gicc_base); + + return gicc_read_rpr(driver_data->gicc_base); +} diff --git a/drivers/arm/gic/v2/gicv2_private.h b/drivers/arm/gic/v2/gicv2_private.h index 91ab43a2b..7703cbe55 100644 --- a/drivers/arm/gic/v2/gicv2_private.h +++ b/drivers/arm/gic/v2/gicv2_private.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2017, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -80,6 +80,11 @@ static inline unsigned int gicc_read_iidr(uintptr_t base) return mmio_read_32(base + GICC_IIDR); } +static inline unsigned int gicc_read_rpr(uintptr_t base) +{ + return mmio_read_32(base + GICC_RPR); +} + /******************************************************************************* * GIC CPU interface accessors for writing entire registers ******************************************************************************/ diff --git a/drivers/arm/gic/v3/gicv3_main.c b/drivers/arm/gic/v3/gicv3_main.c index 1a018d809..7e7574306 100644 --- a/drivers/arm/gic/v3/gicv3_main.c +++ b/drivers/arm/gic/v3/gicv3_main.c @@ -769,3 +769,12 @@ void gicv3_distif_init_restore(const gicv3_dist_ctx_t * const dist_ctx) gicd_wait_for_pending_write(gicd_base); } + +/******************************************************************************* + * This function gets the priority of the interrupt the processor is currently + * servicing. + ******************************************************************************/ +unsigned int gicv3_get_running_priority(void) +{ + return read_icc_rpr_el1(); +} diff --git a/include/drivers/arm/gicv2.h b/include/drivers/arm/gicv2.h index a78802545..1ef0779b9 100644 --- a/include/drivers/arm/gicv2.h +++ b/include/drivers/arm/gicv2.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2017, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -136,6 +136,7 @@ unsigned int gicv2_get_pending_interrupt_id(void); unsigned int gicv2_acknowledge_interrupt(void); void gicv2_end_of_interrupt(unsigned int id); unsigned int gicv2_get_interrupt_group(unsigned int id); +unsigned int gicv2_get_running_priority(void); #endif /* __ASSEMBLY__ */ #endif /* __GICV2_H__ */ diff --git a/include/drivers/arm/gicv3.h b/include/drivers/arm/gicv3.h index c52fe4833..4548a87c8 100644 --- a/include/drivers/arm/gicv3.h +++ b/include/drivers/arm/gicv3.h @@ -349,5 +349,7 @@ void gicv3_rdistif_save(unsigned int proc_num, gicv3_redist_ctx_t * const rdist_ void gicv3_its_save_disable(uintptr_t gits_base, gicv3_its_ctx_t * const its_ctx); void gicv3_its_restore(uintptr_t gits_base, const gicv3_its_ctx_t * const its_ctx); +unsigned int gicv3_get_running_priority(void); + #endif /* __ASSEMBLY__ */ #endif /* __GICV3_H__ */ diff --git a/include/lib/aarch32/arch_helpers.h b/include/lib/aarch32/arch_helpers.h index 5d3183603..1f4f21625 100644 --- a/include/lib/aarch32/arch_helpers.h +++ b/include/lib/aarch32/arch_helpers.h @@ -257,6 +257,7 @@ DEFINE_COPROCR_RW_FUNCS(icc_sre_el1, ICC_SRE) DEFINE_COPROCR_RW_FUNCS(icc_sre_el2, ICC_HSRE) DEFINE_COPROCR_RW_FUNCS(icc_sre_el3, ICC_MSRE) DEFINE_COPROCR_RW_FUNCS(icc_pmr_el1, ICC_PMR) +DEFINE_COPROCR_RW_FUNCS(icc_rpr_el1, ICC_RPR) DEFINE_COPROCR_RW_FUNCS(icc_igrpen1_el3, ICC_MGRPEN1) DEFINE_COPROCR_RW_FUNCS(icc_igrpen0_el1, ICC_IGRPEN0) DEFINE_COPROCR_RW_FUNCS(icc_hppir0_el1, ICC_HPPIR0) diff --git a/include/lib/aarch64/arch.h b/include/lib/aarch64/arch.h index f85e78971..3fb6846c8 100644 --- a/include/lib/aarch64/arch.h +++ b/include/lib/aarch64/arch.h @@ -68,6 +68,7 @@ #define ICC_CTLR_EL1 S3_0_C12_C12_4 #define ICC_CTLR_EL3 S3_6_C12_C12_4 #define ICC_PMR_EL1 S3_0_C4_C6_0 +#define ICC_RPR_EL1 S3_0_C12_C11_3 #define ICC_IGRPEN1_EL3 S3_6_c12_c12_7 #define ICC_IGRPEN0_EL1 S3_0_c12_c12_6 #define ICC_HPPIR0_EL1 S3_0_c12_c8_2 diff --git a/include/lib/aarch64/arch_helpers.h b/include/lib/aarch64/arch_helpers.h index 0d0d7d335..dbbbaeb0b 100644 --- a/include/lib/aarch64/arch_helpers.h +++ b/include/lib/aarch64/arch_helpers.h @@ -313,6 +313,7 @@ DEFINE_RENAME_SYSREG_RW_FUNCS(icc_sre_el1, ICC_SRE_EL1) DEFINE_RENAME_SYSREG_RW_FUNCS(icc_sre_el2, ICC_SRE_EL2) DEFINE_RENAME_SYSREG_RW_FUNCS(icc_sre_el3, ICC_SRE_EL3) DEFINE_RENAME_SYSREG_RW_FUNCS(icc_pmr_el1, ICC_PMR_EL1) +DEFINE_RENAME_SYSREG_READ_FUNC(icc_rpr_el1, ICC_RPR_EL1) DEFINE_RENAME_SYSREG_RW_FUNCS(icc_igrpen1_el3, ICC_IGRPEN1_EL3) DEFINE_RENAME_SYSREG_RW_FUNCS(icc_igrpen0_el1, ICC_IGRPEN0_EL1) DEFINE_RENAME_SYSREG_READ_FUNC(icc_hppir0_el1, ICC_HPPIR0_EL1) diff --git a/include/plat/common/platform.h b/include/plat/common/platform.h index e189f648a..7468352d1 100644 --- a/include/plat/common/platform.h +++ b/include/plat/common/platform.h @@ -69,6 +69,11 @@ void plat_ic_end_of_interrupt(uint32_t id); uint32_t plat_interrupt_type_to_line(uint32_t type, uint32_t security_state); +/******************************************************************************* + * Optional interrupt management functions, depending on chosen EL3 components. + ******************************************************************************/ +unsigned int plat_ic_get_running_priority(void); + /******************************************************************************* * Optional common functions (may be overridden) ******************************************************************************/ diff --git a/plat/common/plat_gicv2.c b/plat/common/plat_gicv2.c index 50a81818c..1be693bb3 100644 --- a/plat/common/plat_gicv2.c +++ b/plat/common/plat_gicv2.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2017, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -20,6 +20,8 @@ #pragma weak plat_ic_end_of_interrupt #pragma weak plat_interrupt_type_to_line +#pragma weak plat_ic_get_running_priority + /* * This function returns the highest priority pending interrupt at * the Interrupt controller @@ -122,3 +124,8 @@ uint32_t plat_interrupt_type_to_line(uint32_t type, return ((gicv2_is_fiq_enabled()) ? __builtin_ctz(SCR_FIQ_BIT) : __builtin_ctz(SCR_IRQ_BIT)); } + +unsigned int plat_ic_get_running_priority(void) +{ + return gicv2_get_running_priority(); +} diff --git a/plat/common/plat_gicv3.c b/plat/common/plat_gicv3.c index 030a1d902..02317f1df 100644 --- a/plat/common/plat_gicv3.c +++ b/plat/common/plat_gicv3.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2016, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2017, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -26,6 +26,8 @@ #pragma weak plat_ic_end_of_interrupt #pragma weak plat_interrupt_type_to_line +#pragma weak plat_ic_get_running_priority + CASSERT((INTR_TYPE_S_EL1 == INTR_GROUP1S) && (INTR_TYPE_NS == INTR_GROUP1NS) && (INTR_TYPE_EL3 == INTR_GROUP0), assert_interrupt_type_mismatch); @@ -155,6 +157,12 @@ uint32_t plat_interrupt_type_to_line(uint32_t type, return __builtin_ctz(SCR_FIQ_BIT); } } + +unsigned int plat_ic_get_running_priority(void) +{ + return gicv3_get_running_priority(); +} + #endif #ifdef IMAGE_BL32 From fa9db4230aef4411cc6c56557f0c407326421876 Mon Sep 17 00:00:00 2001 From: Jeenu Viswambharan Date: Fri, 22 Sep 2017 08:32:09 +0100 Subject: [PATCH 02/14] GICv2: Add driver API to set PE target mask The PE target mask is used to translate linear PE index (returned by platform core position) to a bit mask used when targeting interrupts to a PE, viz. when raising SGIs and routing SPIs. The platform shall: - Populate the driver data with a pointer to array that's to contain per-PE target masks. - Invoke the new driver API 'gicv2_set_pe_target_mask()' during per-CPU initialization so that the driver populates the target mask for that CPU. Platforms that don't intend to target interrupts or raise SGIs need not populate this. Change-Id: Ic0db54da86915e9dccd82fff51479bc3c1fdc968 Signed-off-by: Jeenu Viswambharan --- drivers/arm/gic/v2/gicv2_main.c | 23 +++++++++++++++++++++++ include/drivers/arm/gicv2.h | 26 ++++++++++++++++++-------- 2 files changed, 41 insertions(+), 8 deletions(-) diff --git a/drivers/arm/gic/v2/gicv2_main.c b/drivers/arm/gic/v2/gicv2_main.c index 7759a551a..28266807c 100644 --- a/drivers/arm/gic/v2/gicv2_main.c +++ b/drivers/arm/gic/v2/gicv2_main.c @@ -252,3 +252,26 @@ unsigned int gicv2_get_running_priority(void) return gicc_read_rpr(driver_data->gicc_base); } + +/******************************************************************************* + * This function sets the GICv2 target mask pattern for the current PE. The PE + * target mask is used to translate linear PE index (returned by platform core + * position) to a bit mask used when targeting interrupts to a PE, viz. when + * raising SGIs and routing SPIs. + ******************************************************************************/ +void gicv2_set_pe_target_mask(unsigned int proc_num) +{ + assert(driver_data); + assert(driver_data->gicd_base); + assert(driver_data->target_masks); + assert(proc_num < GICV2_MAX_TARGET_PE); + assert(proc_num < driver_data->target_masks_num); + + /* Return if the target mask is already populated */ + if (driver_data->target_masks[proc_num]) + return; + + /* Read target register corresponding to this CPU */ + driver_data->target_masks[proc_num] = + gicv2_get_cpuif_id(driver_data->gicd_base); +} diff --git a/include/drivers/arm/gicv2.h b/include/drivers/arm/gicv2.h index 1ef0779b9..c91cc1b04 100644 --- a/include/drivers/arm/gicv2.h +++ b/include/drivers/arm/gicv2.h @@ -13,6 +13,9 @@ /* Interrupt IDs reported by the HPPIR and IAR registers */ #define PENDING_G1_INTID 1022 +/* GICv2 can only target up to 8 PEs */ +#define GICV2_MAX_TARGET_PE 8 + /******************************************************************************* * GICv2 specific Distributor interface register offsets and constants. ******************************************************************************/ @@ -103,23 +106,29 @@ * in order to initialize the GICv2 driver. The attributes are described * below. * - * 1. The 'gicd_base' field contains the base address of the Distributor - * interface programmer's view. + * The 'gicd_base' field contains the base address of the Distributor interface + * programmer's view. * - * 2. The 'gicc_base' field contains the base address of the CPU Interface - * programmer's view. + * The 'gicc_base' field contains the base address of the CPU Interface + * programmer's view. * - * 3. The 'g0_interrupt_array' field is a pointer to an array in which each - * entry corresponds to an ID of a Group 0 interrupt. + * The 'g0_interrupt_array' field is a pointer to an array in which each + * entry corresponds to an ID of a Group 0 interrupt. * - * 4. The 'g0_interrupt_num' field contains the number of entries in the - * 'g0_interrupt_array'. + * The 'g0_interrupt_num' field contains the number of entries in the + * 'g0_interrupt_array'. + * + * The 'target_masks' is a pointer to an array containing 'target_masks_num' + * elements. The GIC driver will populate the array with per-PE target mask to + * use to when targeting interrupts. ******************************************************************************/ typedef struct gicv2_driver_data { uintptr_t gicd_base; uintptr_t gicc_base; unsigned int g0_interrupt_num; const unsigned int *g0_interrupt_array; + unsigned int *target_masks; + unsigned int target_masks_num; } gicv2_driver_data_t; /******************************************************************************* @@ -137,6 +146,7 @@ unsigned int gicv2_acknowledge_interrupt(void); void gicv2_end_of_interrupt(unsigned int id); unsigned int gicv2_get_interrupt_group(unsigned int id); unsigned int gicv2_get_running_priority(void); +void gicv2_set_pe_target_mask(unsigned int proc_num); #endif /* __ASSEMBLY__ */ #endif /* __GICV2_H__ */ From dea417cdd640a3e258db3b817223ffaa5f53e416 Mon Sep 17 00:00:00 2001 From: Jeenu Viswambharan Date: Fri, 22 Sep 2017 08:32:09 +0100 Subject: [PATCH 03/14] ARM platforms: supply per-PE target mask array Call the GICv2 driver API to initialise per-PE target mask. Change-Id: Idc7eb0d906a5379f4c05917af05c90613057ab97 Signed-off-by: Jeenu Viswambharan --- plat/arm/common/arm_gicv2.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/plat/arm/common/arm_gicv2.c b/plat/arm/common/arm_gicv2.c index 521fa8cd6..6dd847b2f 100644 --- a/plat/arm/common/arm_gicv2.c +++ b/plat/arm/common/arm_gicv2.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2017, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -28,11 +28,15 @@ static const unsigned int g0_interrupt_array[] = { PLAT_ARM_G0_IRQS }; +static unsigned int target_mask_array[PLATFORM_CORE_COUNT]; + static const gicv2_driver_data_t arm_gic_data = { .gicd_base = PLAT_ARM_GICD_BASE, .gicc_base = PLAT_ARM_GICC_BASE, .g0_interrupt_num = ARRAY_SIZE(g0_interrupt_array), .g0_interrupt_array = g0_interrupt_array, + .target_masks = target_mask_array, + .target_masks_num = ARRAY_SIZE(target_mask_array), }; /****************************************************************************** @@ -72,6 +76,7 @@ void plat_arm_gic_cpuif_disable(void) void plat_arm_gic_pcpu_init(void) { gicv2_pcpu_distif_init(); + gicv2_set_pe_target_mask(plat_my_core_pos()); } /****************************************************************************** From ca43b55d22f3a48f408d16d4bedbf677cbdf8f48 Mon Sep 17 00:00:00 2001 From: Jeenu Viswambharan Date: Fri, 22 Sep 2017 08:32:09 +0100 Subject: [PATCH 04/14] GIC: Add APIs to query interrupt types These APIs allow the GIC implementation to categorize interrupt numbers into SPIs, PPIs, and SGIs. The default implementations for GICv2 and GICv3 follows interrupt numbering as specified by the ARM GIC architecture. API documentation updated. Change-Id: Ia6aa379dc955994333232e6138f259535d4fa087 Signed-off-by: Jeenu Viswambharan --- docs/platform-interrupt-controller-API.rst | 39 ++++++++++++++++++++++ include/drivers/arm/gic_common.h | 1 + include/plat/common/platform.h | 3 ++ plat/common/plat_gicv2.c | 18 ++++++++++ plat/common/plat_gicv3.c | 17 ++++++++++ 5 files changed, 78 insertions(+) diff --git a/docs/platform-interrupt-controller-API.rst b/docs/platform-interrupt-controller-API.rst index 9ef2e3f67..a94a90c75 100644 --- a/docs/platform-interrupt-controller-API.rst +++ b/docs/platform-interrupt-controller-API.rst @@ -27,6 +27,45 @@ acknowledged via. ``plat_ic_acknowledge_interrupt``. In the case of ARM standard platforms using GIC, the *Running Priority Register* is read to determine the priority of the interrupt. +Function: int plat_ic_is_spi(unsigned int id); [optional] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +:: + + Argument : unsigned int + Return : int + +The API should return whether the interrupt ID (first parameter) is categorized +as a Shared Peripheral Interrupt. Shared Peripheral Interrupts are typically +associated to system-wide peripherals, and these interrupts can target any PE in +the system. + +Function: int plat_ic_is_ppi(unsigned int id); [optional] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +:: + + Argument : unsigned int + Return : int + +The API should return whether the interrupt ID (first parameter) is categorized +as a Private Peripheral Interrupt. Private Peripheral Interrupts are typically +associated with peripherals that are private to each PE. Interrupts from private +peripherals target to that PE only. + +Function: int plat_ic_is_sgi(unsigned int id); [optional] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +:: + + Argument : unsigned int + Return : int + +The API should return whether the interrupt ID (first parameter) is categorized +as a Software Generated Interrupt. Software Generated Interrupts are raised by +explicit programming by software, and are typically used in inter-PE +communication. Secure SGIs are reserved for use by Secure world software. + ---- *Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.* diff --git a/include/drivers/arm/gic_common.h b/include/drivers/arm/gic_common.h index b9cae802f..f4e2c5266 100644 --- a/include/drivers/arm/gic_common.h +++ b/include/drivers/arm/gic_common.h @@ -12,6 +12,7 @@ ******************************************************************************/ /* Constants to categorise interrupts */ #define MIN_SGI_ID 0 +#define MIN_SEC_SGI_ID 8 #define MIN_PPI_ID 16 #define MIN_SPI_ID 32 #define MAX_SPI_ID 1019 diff --git a/include/plat/common/platform.h b/include/plat/common/platform.h index 7468352d1..f00db0db2 100644 --- a/include/plat/common/platform.h +++ b/include/plat/common/platform.h @@ -73,6 +73,9 @@ uint32_t plat_interrupt_type_to_line(uint32_t type, * Optional interrupt management functions, depending on chosen EL3 components. ******************************************************************************/ unsigned int plat_ic_get_running_priority(void); +int plat_ic_is_spi(unsigned int id); +int plat_ic_is_ppi(unsigned int id); +int plat_ic_is_sgi(unsigned int id); /******************************************************************************* * Optional common functions (may be overridden) diff --git a/plat/common/plat_gicv2.c b/plat/common/plat_gicv2.c index 1be693bb3..7d91f7922 100644 --- a/plat/common/plat_gicv2.c +++ b/plat/common/plat_gicv2.c @@ -21,6 +21,9 @@ #pragma weak plat_interrupt_type_to_line #pragma weak plat_ic_get_running_priority +#pragma weak plat_ic_is_spi +#pragma weak plat_ic_is_ppi +#pragma weak plat_ic_is_sgi /* * This function returns the highest priority pending interrupt at @@ -129,3 +132,18 @@ unsigned int plat_ic_get_running_priority(void) { return gicv2_get_running_priority(); } + +int plat_ic_is_spi(unsigned int id) +{ + return (id >= MIN_SPI_ID) && (id <= MAX_SPI_ID); +} + +int plat_ic_is_ppi(unsigned int id) +{ + return (id >= MIN_PPI_ID) && (id < MIN_SPI_ID); +} + +int plat_ic_is_sgi(unsigned int id) +{ + return (id >= MIN_SGI_ID) && (id < MIN_PPI_ID); +} diff --git a/plat/common/plat_gicv3.c b/plat/common/plat_gicv3.c index 02317f1df..5a6021c4a 100644 --- a/plat/common/plat_gicv3.c +++ b/plat/common/plat_gicv3.c @@ -27,6 +27,9 @@ #pragma weak plat_interrupt_type_to_line #pragma weak plat_ic_get_running_priority +#pragma weak plat_ic_is_spi +#pragma weak plat_ic_is_ppi +#pragma weak plat_ic_is_sgi CASSERT((INTR_TYPE_S_EL1 == INTR_GROUP1S) && (INTR_TYPE_NS == INTR_GROUP1NS) && @@ -163,6 +166,20 @@ unsigned int plat_ic_get_running_priority(void) return gicv3_get_running_priority(); } +int plat_ic_is_spi(unsigned int id) +{ + return (id >= MIN_SPI_ID) && (id <= MAX_SPI_ID); +} + +int plat_ic_is_ppi(unsigned int id) +{ + return (id >= MIN_PPI_ID) && (id < MIN_SPI_ID); +} + +int plat_ic_is_sgi(unsigned int id) +{ + return (id >= MIN_SGI_ID) && (id < MIN_PPI_ID); +} #endif #ifdef IMAGE_BL32 From cbd3f3706d4217ee3669deeb52b158e84eb97f56 Mon Sep 17 00:00:00 2001 From: Jeenu Viswambharan Date: Fri, 22 Sep 2017 08:32:09 +0100 Subject: [PATCH 05/14] GIC: Add API to get interrupt active status API documentation updated. Change-Id: I6d61785af0d5330930c709de971a904dc7c3516c Co-authored-by: Yousuf A Signed-off-by: Jeenu Viswambharan --- docs/platform-interrupt-controller-API.rst | 15 ++++++++++++ drivers/arm/gic/common/gic_common.c | 10 +++++++- drivers/arm/gic/common/gic_common_private.h | 3 ++- drivers/arm/gic/v2/gicv2_main.c | 13 ++++++++++ drivers/arm/gic/v3/gicv3_helpers.c | 14 ++++++++++- drivers/arm/gic/v3/gicv3_main.c | 27 +++++++++++++++++++++ drivers/arm/gic/v3/gicv3_private.h | 1 + include/drivers/arm/gicv2.h | 1 + include/drivers/arm/gicv3.h | 1 + include/plat/common/platform.h | 1 + plat/common/plat_gicv2.c | 6 +++++ plat/common/plat_gicv3.c | 6 +++++ 12 files changed, 95 insertions(+), 3 deletions(-) diff --git a/docs/platform-interrupt-controller-API.rst b/docs/platform-interrupt-controller-API.rst index a94a90c75..d0a2ad276 100644 --- a/docs/platform-interrupt-controller-API.rst +++ b/docs/platform-interrupt-controller-API.rst @@ -66,6 +66,21 @@ as a Software Generated Interrupt. Software Generated Interrupts are raised by explicit programming by software, and are typically used in inter-PE communication. Secure SGIs are reserved for use by Secure world software. +Function: unsigned int plat_ic_get_interrupt_active(unsigned int id); [optional] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +:: + + Argument : unsigned int + Return : int + +This API should return the *active* status of the interrupt ID specified by the +first parameter, ``id``. + +In case of ARM standard platforms using GIC, the implementation of the API reads +the GIC *Set Active Register* to read and return the active status of the +interrupt. + ---- *Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.* diff --git a/drivers/arm/gic/common/gic_common.c b/drivers/arm/gic/common/gic_common.c index 6535813f9..71155a970 100644 --- a/drivers/arm/gic/common/gic_common.c +++ b/drivers/arm/gic/common/gic_common.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2016, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2017, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -273,6 +273,14 @@ void gicd_set_icpendr(uintptr_t base, unsigned int id) gicd_write_icpendr(base, id, (1 << bit_num)); } +unsigned int gicd_get_isactiver(uintptr_t base, unsigned int id) +{ + unsigned int bit_num = id & ((1 << ISACTIVER_SHIFT) - 1); + unsigned int reg_val = gicd_read_isactiver(base, id); + + return (reg_val >> bit_num) & 0x1; +} + void gicd_set_isactiver(uintptr_t base, unsigned int id) { unsigned bit_num = id & ((1 << ISACTIVER_SHIFT) - 1); diff --git a/drivers/arm/gic/common/gic_common_private.h b/drivers/arm/gic/common/gic_common_private.h index 2077cc464..8b96b37bb 100644 --- a/drivers/arm/gic/common/gic_common_private.h +++ b/drivers/arm/gic/common/gic_common_private.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2016-2017, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -73,6 +73,7 @@ void gicd_set_isenabler(uintptr_t base, unsigned int id); void gicd_set_icenabler(uintptr_t base, unsigned int id); void gicd_set_ispendr(uintptr_t base, unsigned int id); void gicd_set_icpendr(uintptr_t base, unsigned int id); +unsigned int gicd_get_isactiver(uintptr_t base, unsigned int id); void gicd_set_isactiver(uintptr_t base, unsigned int id); void gicd_set_icactiver(uintptr_t base, unsigned int id); void gicd_set_ipriorityr(uintptr_t base, unsigned int id, unsigned int pri); diff --git a/drivers/arm/gic/v2/gicv2_main.c b/drivers/arm/gic/v2/gicv2_main.c index 28266807c..e33353a88 100644 --- a/drivers/arm/gic/v2/gicv2_main.c +++ b/drivers/arm/gic/v2/gicv2_main.c @@ -275,3 +275,16 @@ void gicv2_set_pe_target_mask(unsigned int proc_num) driver_data->target_masks[proc_num] = gicv2_get_cpuif_id(driver_data->gicd_base); } + +/******************************************************************************* + * This function returns the active status of the interrupt (either because the + * state is active, or active and pending). + ******************************************************************************/ +unsigned int gicv2_get_interrupt_active(unsigned int id) +{ + assert(driver_data); + assert(driver_data->gicd_base); + assert(id <= MAX_SPI_ID); + + return gicd_get_isactiver(driver_data->gicd_base, id); +} diff --git a/drivers/arm/gic/v3/gicv3_helpers.c b/drivers/arm/gic/v3/gicv3_helpers.c index 73ad060f6..81d50ad45 100644 --- a/drivers/arm/gic/v3/gicv3_helpers.c +++ b/drivers/arm/gic/v3/gicv3_helpers.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2016, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2017, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -171,6 +171,18 @@ void gicr_set_isenabler0(uintptr_t base, unsigned int id) gicr_write_isenabler0(base, (1 << bit_num)); } +/* + * Accessor to set the bit corresponding to interrupt ID in GIC Re-distributor + * ISACTIVER0. + */ +unsigned int gicr_get_isactiver0(uintptr_t base, unsigned int id) +{ + unsigned bit_num = id & ((1 << ISACTIVER_SHIFT) - 1); + unsigned int reg_val = gicr_read_isactiver0(base); + + return (reg_val >> bit_num) & 0x1; +} + /* * Accessor to set the byte corresponding to interrupt ID * in GIC Re-distributor IPRIORITYR. diff --git a/drivers/arm/gic/v3/gicv3_main.c b/drivers/arm/gic/v3/gicv3_main.c index 7e7574306..08cf0957d 100644 --- a/drivers/arm/gic/v3/gicv3_main.c +++ b/drivers/arm/gic/v3/gicv3_main.c @@ -778,3 +778,30 @@ unsigned int gicv3_get_running_priority(void) { return read_icc_rpr_el1(); } + +/******************************************************************************* + * This function checks if the interrupt identified by id is active (whether the + * state is either active, or active and pending). The proc_num is used if the + * interrupt is SGI or PPI and programs the corresponding Redistributor + * interface. + ******************************************************************************/ +unsigned int gicv3_get_interrupt_active(unsigned int id, unsigned int proc_num) +{ + unsigned int value; + + 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); + assert(id <= MAX_SPI_ID); + + if (id < MIN_SPI_ID) { + /* For SGIs and PPIs */ + value = gicr_get_isactiver0( + gicv3_driver_data->rdistif_base_addrs[proc_num], id); + } else { + value = gicd_get_isactiver(gicv3_driver_data->gicd_base, id); + } + + return value; +} diff --git a/drivers/arm/gic/v3/gicv3_private.h b/drivers/arm/gic/v3/gicv3_private.h index 59298edf2..bb8ad9ae8 100644 --- a/drivers/arm/gic/v3/gicv3_private.h +++ b/drivers/arm/gic/v3/gicv3_private.h @@ -67,6 +67,7 @@ void gicr_write_ipriorityr(uintptr_t base, unsigned int id, unsigned int val); unsigned int gicd_get_igrpmodr(uintptr_t base, unsigned int id); unsigned int gicr_get_igrpmodr0(uintptr_t base, unsigned int id); unsigned int gicr_get_igroupr0(uintptr_t base, unsigned int id); +unsigned int gicr_get_isactiver0(uintptr_t base, unsigned int id); void gicd_set_igrpmodr(uintptr_t base, unsigned int id); void gicr_set_igrpmodr0(uintptr_t base, unsigned int id); void gicr_set_isenabler0(uintptr_t base, unsigned int id); diff --git a/include/drivers/arm/gicv2.h b/include/drivers/arm/gicv2.h index c91cc1b04..2b4612637 100644 --- a/include/drivers/arm/gicv2.h +++ b/include/drivers/arm/gicv2.h @@ -147,6 +147,7 @@ void gicv2_end_of_interrupt(unsigned int id); unsigned int gicv2_get_interrupt_group(unsigned int id); unsigned int gicv2_get_running_priority(void); void gicv2_set_pe_target_mask(unsigned int proc_num); +unsigned int gicv2_get_interrupt_active(unsigned int id); #endif /* __ASSEMBLY__ */ #endif /* __GICV2_H__ */ diff --git a/include/drivers/arm/gicv3.h b/include/drivers/arm/gicv3.h index 4548a87c8..ec272ea7c 100644 --- a/include/drivers/arm/gicv3.h +++ b/include/drivers/arm/gicv3.h @@ -350,6 +350,7 @@ void gicv3_its_save_disable(uintptr_t gits_base, gicv3_its_ctx_t * const its_ctx void gicv3_its_restore(uintptr_t gits_base, const gicv3_its_ctx_t * const its_ctx); unsigned int gicv3_get_running_priority(void); +unsigned int gicv3_get_interrupt_active(unsigned int id, unsigned int proc_num); #endif /* __ASSEMBLY__ */ #endif /* __GICV3_H__ */ diff --git a/include/plat/common/platform.h b/include/plat/common/platform.h index f00db0db2..e4fb6a9f9 100644 --- a/include/plat/common/platform.h +++ b/include/plat/common/platform.h @@ -76,6 +76,7 @@ unsigned int plat_ic_get_running_priority(void); int plat_ic_is_spi(unsigned int id); int plat_ic_is_ppi(unsigned int id); int plat_ic_is_sgi(unsigned int id); +unsigned int plat_ic_get_interrupt_active(unsigned int id); /******************************************************************************* * Optional common functions (may be overridden) diff --git a/plat/common/plat_gicv2.c b/plat/common/plat_gicv2.c index 7d91f7922..5a92b66b9 100644 --- a/plat/common/plat_gicv2.c +++ b/plat/common/plat_gicv2.c @@ -24,6 +24,7 @@ #pragma weak plat_ic_is_spi #pragma weak plat_ic_is_ppi #pragma weak plat_ic_is_sgi +#pragma weak plat_ic_get_interrupt_active /* * This function returns the highest priority pending interrupt at @@ -147,3 +148,8 @@ int plat_ic_is_sgi(unsigned int id) { return (id >= MIN_SGI_ID) && (id < MIN_PPI_ID); } + +unsigned int plat_ic_get_interrupt_active(unsigned int id) +{ + return gicv2_get_interrupt_active(id); +} diff --git a/plat/common/plat_gicv3.c b/plat/common/plat_gicv3.c index 5a6021c4a..abb3f1d16 100644 --- a/plat/common/plat_gicv3.c +++ b/plat/common/plat_gicv3.c @@ -30,6 +30,7 @@ #pragma weak plat_ic_is_spi #pragma weak plat_ic_is_ppi #pragma weak plat_ic_is_sgi +#pragma weak plat_ic_get_interrupt_active CASSERT((INTR_TYPE_S_EL1 == INTR_GROUP1S) && (INTR_TYPE_NS == INTR_GROUP1NS) && @@ -180,6 +181,11 @@ int plat_ic_is_sgi(unsigned int id) { return (id >= MIN_SGI_ID) && (id < MIN_PPI_ID); } + +unsigned int plat_ic_get_interrupt_active(unsigned int id) +{ + return gicv3_get_interrupt_active(id, plat_my_core_pos()); +} #endif #ifdef IMAGE_BL32 From 979225f4eed00d631bb57ebd09068edd91b8df7b Mon Sep 17 00:00:00 2001 From: Jeenu Viswambharan Date: Fri, 22 Sep 2017 08:32:09 +0100 Subject: [PATCH 06/14] GIC: Add APIs to enable and disable interrupt API documentation updated. Change-Id: Ice7511f8df5356851001d2f7dc2a46cfe318f9ba Co-authored-by: Yousuf A Signed-off-by: Jeenu Viswambharan --- docs/platform-interrupt-controller-API.rst | 30 ++++++++++ drivers/arm/gic/v2/gicv2_main.c | 34 ++++++++++++ drivers/arm/gic/v3/gicv3_helpers.c | 11 ++++ drivers/arm/gic/v3/gicv3_main.c | 64 ++++++++++++++++++++++ drivers/arm/gic/v3/gicv3_private.h | 1 + include/drivers/arm/gicv2.h | 2 + include/drivers/arm/gicv3.h | 2 + include/plat/common/platform.h | 2 + plat/common/plat_gicv2.c | 12 ++++ plat/common/plat_gicv3.c | 12 ++++ 10 files changed, 170 insertions(+) diff --git a/docs/platform-interrupt-controller-API.rst b/docs/platform-interrupt-controller-API.rst index d0a2ad276..bea1a64b5 100644 --- a/docs/platform-interrupt-controller-API.rst +++ b/docs/platform-interrupt-controller-API.rst @@ -81,6 +81,36 @@ In case of ARM standard platforms using GIC, the implementation of the API reads the GIC *Set Active Register* to read and return the active status of the interrupt. +Function: void plat_ic_enable_interrupt(unsigned int id); [optional] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +:: + + Argument : unsigned int + Return : void + +This API should enable the interrupt ID specified by the first parameter, +``id``. PEs in the system are expected to receive only enabled interrupts. + +In case of ARM standard platforms using GIC, the implementation of the API +inserts barrier to make memory updates visible before enabling interrupt, and +then writes to GIC *Set Enable Register* to enable the interrupt. + +Function: void plat_ic_disable_interrupt(unsigned int id); [optional] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +:: + + Argument : unsigned int + Return : void + +This API should disable the interrupt ID specified by the first parameter, +``id``. PEs in the system are not expected to receive disabled interrupts. + +In case of ARM standard platforms using GIC, the implementation of the API +writes to GIC *Clear Enable Register* to disable the interrupt, and inserts +barrier to make memory updates visible afterwards. + ---- *Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.* diff --git a/drivers/arm/gic/v2/gicv2_main.c b/drivers/arm/gic/v2/gicv2_main.c index e33353a88..e0058ad13 100644 --- a/drivers/arm/gic/v2/gicv2_main.c +++ b/drivers/arm/gic/v2/gicv2_main.c @@ -288,3 +288,37 @@ unsigned int gicv2_get_interrupt_active(unsigned int id) return gicd_get_isactiver(driver_data->gicd_base, id); } + +/******************************************************************************* + * This function enables the interrupt identified by id. + ******************************************************************************/ +void gicv2_enable_interrupt(unsigned int id) +{ + assert(driver_data); + assert(driver_data->gicd_base); + assert(id <= MAX_SPI_ID); + + /* + * Ensure that any shared variable updates depending on out of band + * interrupt trigger are observed before enabling interrupt. + */ + dsbishst(); + gicd_set_isenabler(driver_data->gicd_base, id); +} + +/******************************************************************************* + * This function disables the interrupt identified by id. + ******************************************************************************/ +void gicv2_disable_interrupt(unsigned int id) +{ + assert(driver_data); + assert(driver_data->gicd_base); + assert(id <= MAX_SPI_ID); + + /* + * Disable interrupt, and ensure that any shared variable updates + * depending on out of band interrupt trigger are observed afterwards. + */ + gicd_set_icenabler(driver_data->gicd_base, id); + dsbishst(); +} diff --git a/drivers/arm/gic/v3/gicv3_helpers.c b/drivers/arm/gic/v3/gicv3_helpers.c index 81d50ad45..ee874f92f 100644 --- a/drivers/arm/gic/v3/gicv3_helpers.c +++ b/drivers/arm/gic/v3/gicv3_helpers.c @@ -171,6 +171,17 @@ void gicr_set_isenabler0(uintptr_t base, unsigned int id) gicr_write_isenabler0(base, (1 << bit_num)); } +/* + * Accessor to set the bit corresponding to interrupt ID in GIC Re-distributor + * ICENABLER0. + */ +void gicr_set_icenabler0(uintptr_t base, unsigned int id) +{ + unsigned bit_num = id & ((1 << ICENABLER_SHIFT) - 1); + + gicr_write_icenabler0(base, (1 << bit_num)); +} + /* * Accessor to set the bit corresponding to interrupt ID in GIC Re-distributor * ISACTIVER0. diff --git a/drivers/arm/gic/v3/gicv3_main.c b/drivers/arm/gic/v3/gicv3_main.c index 08cf0957d..b3231993e 100644 --- a/drivers/arm/gic/v3/gicv3_main.c +++ b/drivers/arm/gic/v3/gicv3_main.c @@ -805,3 +805,67 @@ unsigned int gicv3_get_interrupt_active(unsigned int id, unsigned int proc_num) return value; } + +/******************************************************************************* + * This function enables the interrupt identified by id. The proc_num + * is used if the interrupt is SGI or PPI, and programs the corresponding + * Redistributor interface. + ******************************************************************************/ +void gicv3_enable_interrupt(unsigned int id, unsigned int proc_num) +{ + 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); + assert(id <= MAX_SPI_ID); + + /* + * Ensure that any shared variable updates depending on out of band + * interrupt trigger are observed before enabling interrupt. + */ + dsbishst(); + if (id < MIN_SPI_ID) { + /* For SGIs and PPIs */ + gicr_set_isenabler0( + gicv3_driver_data->rdistif_base_addrs[proc_num], + id); + } else { + gicd_set_isenabler(gicv3_driver_data->gicd_base, id); + } +} + +/******************************************************************************* + * This function disables the interrupt identified by id. The proc_num + * is used if the interrupt is SGI or PPI, and programs the corresponding + * Redistributor interface. + ******************************************************************************/ +void gicv3_disable_interrupt(unsigned int id, unsigned int proc_num) +{ + 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); + assert(id <= MAX_SPI_ID); + + /* + * Disable interrupt, and ensure that any shared variable updates + * depending on out of band interrupt trigger are observed afterwards. + */ + if (id < MIN_SPI_ID) { + /* For SGIs and PPIs */ + gicr_set_icenabler0( + gicv3_driver_data->rdistif_base_addrs[proc_num], + id); + + /* Write to clear enable requires waiting for pending writes */ + gicr_wait_for_pending_write( + gicv3_driver_data->rdistif_base_addrs[proc_num]); + } else { + gicd_set_icenabler(gicv3_driver_data->gicd_base, id); + + /* Write to clear enable requires waiting for pending writes */ + gicd_wait_for_pending_write(gicv3_driver_data->gicd_base); + } + + dsbishst(); +} diff --git a/drivers/arm/gic/v3/gicv3_private.h b/drivers/arm/gic/v3/gicv3_private.h index bb8ad9ae8..19fce2e73 100644 --- a/drivers/arm/gic/v3/gicv3_private.h +++ b/drivers/arm/gic/v3/gicv3_private.h @@ -71,6 +71,7 @@ unsigned int gicr_get_isactiver0(uintptr_t base, unsigned int id); void gicd_set_igrpmodr(uintptr_t base, unsigned int id); void gicr_set_igrpmodr0(uintptr_t base, unsigned int id); void gicr_set_isenabler0(uintptr_t base, unsigned int id); +void gicr_set_icenabler0(uintptr_t base, unsigned int id); void gicr_set_igroupr0(uintptr_t base, unsigned int id); void gicd_clr_igrpmodr(uintptr_t base, unsigned int id); void gicr_clr_igrpmodr0(uintptr_t base, unsigned int id); diff --git a/include/drivers/arm/gicv2.h b/include/drivers/arm/gicv2.h index 2b4612637..0af1a25e2 100644 --- a/include/drivers/arm/gicv2.h +++ b/include/drivers/arm/gicv2.h @@ -148,6 +148,8 @@ unsigned int gicv2_get_interrupt_group(unsigned int id); unsigned int gicv2_get_running_priority(void); void gicv2_set_pe_target_mask(unsigned int proc_num); unsigned int gicv2_get_interrupt_active(unsigned int id); +void gicv2_enable_interrupt(unsigned int id); +void gicv2_disable_interrupt(unsigned int id); #endif /* __ASSEMBLY__ */ #endif /* __GICV2_H__ */ diff --git a/include/drivers/arm/gicv3.h b/include/drivers/arm/gicv3.h index ec272ea7c..6e6a47b97 100644 --- a/include/drivers/arm/gicv3.h +++ b/include/drivers/arm/gicv3.h @@ -351,6 +351,8 @@ void gicv3_its_restore(uintptr_t gits_base, const gicv3_its_ctx_t * const its_ct unsigned int gicv3_get_running_priority(void); unsigned int gicv3_get_interrupt_active(unsigned int id, unsigned int proc_num); +void gicv3_enable_interrupt(unsigned int id, unsigned int proc_num); +void gicv3_disable_interrupt(unsigned int id, unsigned int proc_num); #endif /* __ASSEMBLY__ */ #endif /* __GICV3_H__ */ diff --git a/include/plat/common/platform.h b/include/plat/common/platform.h index e4fb6a9f9..671aa61e8 100644 --- a/include/plat/common/platform.h +++ b/include/plat/common/platform.h @@ -77,6 +77,8 @@ int plat_ic_is_spi(unsigned int id); int plat_ic_is_ppi(unsigned int id); 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); /******************************************************************************* * Optional common functions (may be overridden) diff --git a/plat/common/plat_gicv2.c b/plat/common/plat_gicv2.c index 5a92b66b9..2591805de 100644 --- a/plat/common/plat_gicv2.c +++ b/plat/common/plat_gicv2.c @@ -25,6 +25,8 @@ #pragma weak plat_ic_is_ppi #pragma weak plat_ic_is_sgi #pragma weak plat_ic_get_interrupt_active +#pragma weak plat_ic_enable_interrupt +#pragma weak plat_ic_disable_interrupt /* * This function returns the highest priority pending interrupt at @@ -153,3 +155,13 @@ unsigned int plat_ic_get_interrupt_active(unsigned int id) { return gicv2_get_interrupt_active(id); } + +void plat_ic_enable_interrupt(unsigned int id) +{ + gicv2_enable_interrupt(id); +} + +void plat_ic_disable_interrupt(unsigned int id) +{ + gicv2_disable_interrupt(id); +} diff --git a/plat/common/plat_gicv3.c b/plat/common/plat_gicv3.c index abb3f1d16..2a1f0f5c5 100644 --- a/plat/common/plat_gicv3.c +++ b/plat/common/plat_gicv3.c @@ -31,6 +31,8 @@ #pragma weak plat_ic_is_ppi #pragma weak plat_ic_is_sgi #pragma weak plat_ic_get_interrupt_active +#pragma weak plat_ic_enable_interrupt +#pragma weak plat_ic_disable_interrupt CASSERT((INTR_TYPE_S_EL1 == INTR_GROUP1S) && (INTR_TYPE_NS == INTR_GROUP1NS) && @@ -186,6 +188,16 @@ unsigned int plat_ic_get_interrupt_active(unsigned int id) { return gicv3_get_interrupt_active(id, plat_my_core_pos()); } + +void plat_ic_enable_interrupt(unsigned int id) +{ + gicv3_enable_interrupt(id, plat_my_core_pos()); +} + +void plat_ic_disable_interrupt(unsigned int id) +{ + gicv3_disable_interrupt(id, plat_my_core_pos()); +} #endif #ifdef IMAGE_BL32 From f3a866004ea8f9a0cd5420f3dd4d4683f638e6da Mon Sep 17 00:00:00 2001 From: Jeenu Viswambharan Date: Fri, 22 Sep 2017 08:32:09 +0100 Subject: [PATCH 07/14] GIC: Add API to set interrupt priority API documentation updated. Change-Id: Ib700eb1b8ca65503aeed0ac4ce0e7b934df67ff9 Co-authored-by: Yousuf A Signed-off-by: Jeenu Viswambharan --- docs/platform-interrupt-controller-API.rst | 15 ++++++++++++++ drivers/arm/gic/v2/gicv2_main.c | 13 ++++++++++++ drivers/arm/gic/v3/gicv3_main.c | 23 ++++++++++++++++++++++ include/drivers/arm/gicv2.h | 1 + include/drivers/arm/gicv3.h | 2 ++ include/plat/common/platform.h | 1 + plat/common/plat_gicv2.c | 6 ++++++ plat/common/plat_gicv3.c | 6 ++++++ 8 files changed, 67 insertions(+) diff --git a/docs/platform-interrupt-controller-API.rst b/docs/platform-interrupt-controller-API.rst index bea1a64b5..3161c20a2 100644 --- a/docs/platform-interrupt-controller-API.rst +++ b/docs/platform-interrupt-controller-API.rst @@ -111,6 +111,21 @@ In case of ARM standard platforms using GIC, the implementation of the API writes to GIC *Clear Enable Register* to disable the interrupt, and inserts barrier to make memory updates visible afterwards. +Function: void plat_ic_set_interrupt_priority(unsigned int id, unsigned int priority); [optional] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +:: + + Argument : unsigned int + Argument : unsigned int + Return : void + +This API should set the priority of the interrupt specified by first parameter +``id`` to the value set by the second parameter ``priority``. + +In case of ARM standard platforms using GIC, the implementation of the API +writes to GIC *Priority Register* set interrupt priority. + ---- *Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.* diff --git a/drivers/arm/gic/v2/gicv2_main.c b/drivers/arm/gic/v2/gicv2_main.c index e0058ad13..4861e02df 100644 --- a/drivers/arm/gic/v2/gicv2_main.c +++ b/drivers/arm/gic/v2/gicv2_main.c @@ -322,3 +322,16 @@ void gicv2_disable_interrupt(unsigned int id) gicd_set_icenabler(driver_data->gicd_base, id); dsbishst(); } + +/******************************************************************************* + * This function sets the interrupt priority as supplied for the given interrupt + * id. + ******************************************************************************/ +void gicv2_set_interrupt_priority(unsigned int id, unsigned int priority) +{ + assert(driver_data); + assert(driver_data->gicd_base); + assert(id <= MAX_SPI_ID); + + gicd_set_ipriorityr(driver_data->gicd_base, id, priority); +} diff --git a/drivers/arm/gic/v3/gicv3_main.c b/drivers/arm/gic/v3/gicv3_main.c index b3231993e..d0ecab615 100644 --- a/drivers/arm/gic/v3/gicv3_main.c +++ b/drivers/arm/gic/v3/gicv3_main.c @@ -869,3 +869,26 @@ void gicv3_disable_interrupt(unsigned int id, unsigned int proc_num) dsbishst(); } + +/******************************************************************************* + * This function sets the interrupt priority as supplied for the given interrupt + * id. + ******************************************************************************/ +void gicv3_set_interrupt_priority(unsigned int id, unsigned int proc_num, + unsigned int priority) +{ + 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); + assert(id <= MAX_SPI_ID); + + if (id < MIN_SPI_ID) { + gicr_base = gicv3_driver_data->rdistif_base_addrs[proc_num]; + gicr_set_ipriorityr(gicr_base, id, priority); + } else { + gicd_set_ipriorityr(gicv3_driver_data->gicd_base, id, priority); + } +} diff --git a/include/drivers/arm/gicv2.h b/include/drivers/arm/gicv2.h index 0af1a25e2..91674fd36 100644 --- a/include/drivers/arm/gicv2.h +++ b/include/drivers/arm/gicv2.h @@ -150,6 +150,7 @@ void gicv2_set_pe_target_mask(unsigned int proc_num); 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); #endif /* __ASSEMBLY__ */ #endif /* __GICV2_H__ */ diff --git a/include/drivers/arm/gicv3.h b/include/drivers/arm/gicv3.h index 6e6a47b97..f753ca265 100644 --- a/include/drivers/arm/gicv3.h +++ b/include/drivers/arm/gicv3.h @@ -353,6 +353,8 @@ unsigned int gicv3_get_running_priority(void); unsigned int gicv3_get_interrupt_active(unsigned int id, unsigned int proc_num); 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); #endif /* __ASSEMBLY__ */ #endif /* __GICV3_H__ */ diff --git a/include/plat/common/platform.h b/include/plat/common/platform.h index 671aa61e8..03f529cb0 100644 --- a/include/plat/common/platform.h +++ b/include/plat/common/platform.h @@ -79,6 +79,7 @@ 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); +void plat_ic_set_interrupt_priority(unsigned int id, unsigned int priority); /******************************************************************************* * Optional common functions (may be overridden) diff --git a/plat/common/plat_gicv2.c b/plat/common/plat_gicv2.c index 2591805de..d50138edf 100644 --- a/plat/common/plat_gicv2.c +++ b/plat/common/plat_gicv2.c @@ -27,6 +27,7 @@ #pragma weak plat_ic_get_interrupt_active #pragma weak plat_ic_enable_interrupt #pragma weak plat_ic_disable_interrupt +#pragma weak plat_ic_set_interrupt_priority /* * This function returns the highest priority pending interrupt at @@ -165,3 +166,8 @@ void plat_ic_disable_interrupt(unsigned int id) { gicv2_disable_interrupt(id); } + +void plat_ic_set_interrupt_priority(unsigned int id, unsigned int priority) +{ + gicv2_set_interrupt_priority(id, priority); +} diff --git a/plat/common/plat_gicv3.c b/plat/common/plat_gicv3.c index 2a1f0f5c5..230d90ca9 100644 --- a/plat/common/plat_gicv3.c +++ b/plat/common/plat_gicv3.c @@ -33,6 +33,7 @@ #pragma weak plat_ic_get_interrupt_active #pragma weak plat_ic_enable_interrupt #pragma weak plat_ic_disable_interrupt +#pragma weak plat_ic_set_interrupt_priority CASSERT((INTR_TYPE_S_EL1 == INTR_GROUP1S) && (INTR_TYPE_NS == INTR_GROUP1NS) && @@ -198,6 +199,11 @@ void plat_ic_disable_interrupt(unsigned int id) { gicv3_disable_interrupt(id, plat_my_core_pos()); } + +void plat_ic_set_interrupt_priority(unsigned int id, unsigned int priority) +{ + gicv3_set_interrupt_priority(id, plat_my_core_pos(), priority); +} #endif #ifdef IMAGE_BL32 From 74dce7fa6e42cab3aa54a9543e4a546c1450b2ae Mon Sep 17 00:00:00 2001 From: Jeenu Viswambharan Date: Fri, 22 Sep 2017 08:32:09 +0100 Subject: [PATCH 08/14] 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 From 8db978b5a8606a658c65b16fab7edd7a17c7c940 Mon Sep 17 00:00:00 2001 From: Jeenu Viswambharan Date: Fri, 22 Sep 2017 08:32:09 +0100 Subject: [PATCH 09/14] GIC: Add API to raise secure SGI API documentation updated. Change-Id: I129725059299af6cc612bafa8d74817f779d7c4f Signed-off-by: Jeenu Viswambharan --- docs/platform-interrupt-controller-API.rst | 17 ++++++++++ drivers/arm/gic/v2/gicv2_main.c | 35 +++++++++++++++++++ drivers/arm/gic/v2/gicv2_private.h | 8 +++++ drivers/arm/gic/v3/gicv3_main.c | 39 ++++++++++++++++++++++ include/drivers/arm/gicv2.h | 14 ++++++++ include/drivers/arm/gicv3.h | 25 ++++++++++++++ include/lib/aarch32/arch.h | 1 + include/lib/aarch32/arch_helpers.h | 4 +++ include/lib/aarch64/arch.h | 1 + include/lib/aarch64/arch_helpers.h | 1 + include/plat/common/platform.h | 1 + plat/common/plat_gicv2.c | 20 +++++++++++ plat/common/plat_gicv3.c | 12 +++++++ 13 files changed, 178 insertions(+) diff --git a/docs/platform-interrupt-controller-API.rst b/docs/platform-interrupt-controller-API.rst index 3d46cf344..74291bcaa 100644 --- a/docs/platform-interrupt-controller-API.rst +++ b/docs/platform-interrupt-controller-API.rst @@ -200,6 +200,23 @@ For GICv2: ``INTR_TYPE_S_EL1`` maps to Group 0. Otherwise, ``INTR_TYPE_EL3`` maps to Group 0 interrupt. +Function: void plat_ic_raise_el3_sgi(int sgi_num, u_register_t target); [optional] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +:: + + Argument : int + Argument : u_register_t + Return : void + +This API should raise an EL3 SGI. The first parameter, ``sgi_num``, specifies +the ID of the SGI. The second parameter, ``target``, must be the MPIDR of the +target PE. + +In case of ARM standard platforms using GIC, the implementation of the API +inserts barrier to make memory updates visible before raising SGI, then writes +to appropriate *SGI Register* in order to raise the EL3 SGI. + ---- *Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.* diff --git a/drivers/arm/gic/v2/gicv2_main.c b/drivers/arm/gic/v2/gicv2_main.c index 8048c6a7c..b59e4586f 100644 --- a/drivers/arm/gic/v2/gicv2_main.c +++ b/drivers/arm/gic/v2/gicv2_main.c @@ -368,3 +368,38 @@ void gicv2_set_interrupt_type(unsigned int id, unsigned int type) } spin_unlock(&gic_lock); } + +/******************************************************************************* + * This function raises the specified SGI to requested targets. + * + * The proc_num parameter must be the linear index of the target PE in the + * system. + ******************************************************************************/ +void gicv2_raise_sgi(int sgi_num, int proc_num) +{ + unsigned int sgir_val, target; + + assert(driver_data); + assert(proc_num < GICV2_MAX_TARGET_PE); + assert(driver_data->gicd_base); + + /* + * Target masks array must have been supplied, and the core position + * should be valid. + */ + assert(driver_data->target_masks); + assert(proc_num < driver_data->target_masks_num); + + /* Don't raise SGI if the mask hasn't been populated */ + target = driver_data->target_masks[proc_num]; + assert(target != 0); + + sgir_val = GICV2_SGIR_VALUE(SGIR_TGT_SPECIFIC, target, sgi_num); + + /* + * Ensure that any shared variable updates depending on out of band + * interrupt trigger are observed before raising SGI. + */ + dsbishst(); + gicd_write_sgir(driver_data->gicd_base, sgir_val); +} diff --git a/drivers/arm/gic/v2/gicv2_private.h b/drivers/arm/gic/v2/gicv2_private.h index 7703cbe55..915fc3a7f 100644 --- a/drivers/arm/gic/v2/gicv2_private.h +++ b/drivers/arm/gic/v2/gicv2_private.h @@ -31,6 +31,14 @@ static inline unsigned int gicd_read_pidr2(uintptr_t base) return mmio_read_32(base + GICD_PIDR2_GICV2); } +/******************************************************************************* + * GIC Distributor interface accessors for writing entire registers + ******************************************************************************/ +static inline void gicd_write_sgir(uintptr_t base, unsigned int val) +{ + mmio_write_32(base + GICD_SGIR, val); +} + /******************************************************************************* * GIC CPU interface accessors for reading entire registers ******************************************************************************/ diff --git a/drivers/arm/gic/v3/gicv3_main.c b/drivers/arm/gic/v3/gicv3_main.c index 04b471290..a5067322f 100644 --- a/drivers/arm/gic/v3/gicv3_main.c +++ b/drivers/arm/gic/v3/gicv3_main.c @@ -960,3 +960,42 @@ void gicv3_set_interrupt_type(unsigned int id, unsigned int proc_num, spin_unlock(&gic_lock); } } + +/******************************************************************************* + * This function raises the specified Secure Group 0 SGI. + * + * The target parameter must be a valid MPIDR in the system. + ******************************************************************************/ +void gicv3_raise_secure_g0_sgi(int sgi_num, u_register_t target) +{ + unsigned int tgt, aff3, aff2, aff1, aff0; + uint64_t sgi_val; + + /* Verify interrupt number is in the SGI range */ + assert((sgi_num >= MIN_SGI_ID) && (sgi_num < MIN_PPI_ID)); + + /* Extract affinity fields from target */ + aff0 = MPIDR_AFFLVL0_VAL(target); + aff1 = MPIDR_AFFLVL1_VAL(target); + aff2 = MPIDR_AFFLVL2_VAL(target); + aff3 = MPIDR_AFFLVL3_VAL(target); + + /* + * Make target list from affinity 0, and ensure GICv3 SGI can target + * this PE. + */ + assert(aff0 < GICV3_MAX_SGI_TARGETS); + tgt = BIT(aff0); + + /* Raise SGI to PE specified by its affinity */ + sgi_val = GICV3_SGIR_VALUE(aff3, aff2, aff1, sgi_num, SGIR_IRM_TO_AFF, + tgt); + + /* + * Ensure that any shared variable updates depending on out of band + * interrupt trigger are observed before raising SGI. + */ + dsbishst(); + write_icc_sgi0r_el1(sgi_val); + isb(); +} diff --git a/include/drivers/arm/gicv2.h b/include/drivers/arm/gicv2.h index 229355ccf..c9bcbb659 100644 --- a/include/drivers/arm/gicv2.h +++ b/include/drivers/arm/gicv2.h @@ -36,6 +36,19 @@ #define CPENDSGIR_SHIFT 2 #define SPENDSGIR_SHIFT CPENDSGIR_SHIFT +#define SGIR_TGTLSTFLT_SHIFT 24 +#define SGIR_TGTLSTFLT_MASK 0x3 +#define SGIR_TGTLST_SHIFT 16 +#define SGIR_TGTLST_MASK 0xff +#define SGIR_INTID_MASK 0xf + +#define SGIR_TGT_SPECIFIC 0 + +#define GICV2_SGIR_VALUE(tgt_lst_flt, tgt, intid) \ + ((((tgt_lst_flt) & SGIR_TGTLSTFLT_MASK) << SGIR_TGTLSTFLT_SHIFT) | \ + (((tgt) & SGIR_TGTLST_MASK) << SGIR_TGTLST_SHIFT) | \ + ((intid) & SGIR_INTID_MASK)) + /******************************************************************************* * GICv2 specific CPU interface register offsets and constants. ******************************************************************************/ @@ -157,6 +170,7 @@ 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); +void gicv2_raise_sgi(int sgi_num, int proc_num); #endif /* __ASSEMBLY__ */ #endif /* __GICV2_H__ */ diff --git a/include/drivers/arm/gicv3.h b/include/drivers/arm/gicv3.h index 68430fb05..bf294f137 100644 --- a/include/drivers/arm/gicv3.h +++ b/include/drivers/arm/gicv3.h @@ -24,6 +24,9 @@ /* Constant to categorize LPI interrupt */ #define MIN_LPI_ID 8192 +/* GICv3 can only target up to 16 PEs with SGI */ +#define GICV3_MAX_SGI_TARGETS 16 + /******************************************************************************* * GICv3 specific Distributor interface register offsets and constants. ******************************************************************************/ @@ -165,6 +168,27 @@ #define IAR1_EL1_INTID_SHIFT 0 #define IAR1_EL1_INTID_MASK 0xffffff +/* ICC SGI macros */ +#define SGIR_TGT_MASK 0xffff +#define SGIR_AFF1_SHIFT 16 +#define SGIR_INTID_SHIFT 24 +#define SGIR_INTID_MASK 0xf +#define SGIR_AFF2_SHIFT 32 +#define SGIR_IRM_SHIFT 40 +#define SGIR_IRM_MASK 0x1 +#define SGIR_AFF3_SHIFT 48 +#define SGIR_AFF_MASK 0xf + +#define SGIR_IRM_TO_AFF 0 + +#define GICV3_SGIR_VALUE(aff3, aff2, aff1, intid, irm, tgt) \ + ((((uint64_t) (aff3) & SGIR_AFF_MASK) << SGIR_AFF3_SHIFT) | \ + (((uint64_t) (irm) & SGIR_IRM_MASK) << SGIR_IRM_SHIFT) | \ + (((uint64_t) (aff2) & SGIR_AFF_MASK) << SGIR_AFF2_SHIFT) | \ + (((intid) & SGIR_INTID_MASK) << SGIR_INTID_SHIFT) | \ + (((aff1) & SGIR_AFF_MASK) << SGIR_AFF1_SHIFT) | \ + ((tgt) & SGIR_TGT_MASK)) + /***************************************************************************** * GICv3 ITS registers and constants *****************************************************************************/ @@ -357,6 +381,7 @@ 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); +void gicv3_raise_secure_g0_sgi(int sgi_num, u_register_t target); #endif /* __ASSEMBLY__ */ #endif /* __GICV3_H__ */ diff --git a/include/lib/aarch32/arch.h b/include/lib/aarch32/arch.h index 5fbb83a6c..e2c5f24f4 100644 --- a/include/lib/aarch32/arch.h +++ b/include/lib/aarch32/arch.h @@ -44,6 +44,7 @@ (((mpidr) >> MPIDR_AFF1_SHIFT) & MPIDR_AFFLVL_MASK) #define MPIDR_AFFLVL2_VAL(mpidr) \ (((mpidr) >> MPIDR_AFF2_SHIFT) & MPIDR_AFFLVL_MASK) +#define MPIDR_AFFLVL3_VAL(mpidr) 0 /* * The MPIDR_MAX_AFFLVL count starts from 0. Take care to diff --git a/include/lib/aarch32/arch_helpers.h b/include/lib/aarch32/arch_helpers.h index 1f4f21625..c8caea286 100644 --- a/include/lib/aarch32/arch_helpers.h +++ b/include/lib/aarch32/arch_helpers.h @@ -266,6 +266,7 @@ DEFINE_COPROCR_RW_FUNCS(icc_iar0_el1, ICC_IAR0) DEFINE_COPROCR_RW_FUNCS(icc_iar1_el1, ICC_IAR1) DEFINE_COPROCR_RW_FUNCS(icc_eoir0_el1, ICC_EOIR0) DEFINE_COPROCR_RW_FUNCS(icc_eoir1_el1, ICC_EOIR1) +DEFINE_COPROCR_RW_FUNCS_64(icc_sgi0r_el1, ICC_SGI0R_EL1_64) DEFINE_COPROCR_RW_FUNCS(hdcr, HDCR) DEFINE_COPROCR_RW_FUNCS(cnthp_ctl, CNTHP_CTL) @@ -325,4 +326,7 @@ DEFINE_DCOP_PARAM_FUNC(cvac, DCCMVAC) #define read_ctr_el0() read_ctr() +#define write_icc_sgi0r_el1(_v) \ + write64_icc_sgi0r_el1(_v) + #endif /* __ARCH_HELPERS_H__ */ diff --git a/include/lib/aarch64/arch.h b/include/lib/aarch64/arch.h index 3fb6846c8..bbe1e4f93 100644 --- a/include/lib/aarch64/arch.h +++ b/include/lib/aarch64/arch.h @@ -77,6 +77,7 @@ #define ICC_IAR1_EL1 S3_0_c12_c12_0 #define ICC_EOIR0_EL1 S3_0_c12_c8_1 #define ICC_EOIR1_EL1 S3_0_c12_c12_1 +#define ICC_SGI0R_EL1 S3_0_c12_c11_7 /******************************************************************************* * Generic timer memory mapped registers & offsets diff --git a/include/lib/aarch64/arch_helpers.h b/include/lib/aarch64/arch_helpers.h index dbbbaeb0b..b880497ce 100644 --- a/include/lib/aarch64/arch_helpers.h +++ b/include/lib/aarch64/arch_helpers.h @@ -322,6 +322,7 @@ DEFINE_RENAME_SYSREG_READ_FUNC(icc_iar0_el1, ICC_IAR0_EL1) DEFINE_RENAME_SYSREG_READ_FUNC(icc_iar1_el1, ICC_IAR1_EL1) DEFINE_RENAME_SYSREG_WRITE_FUNC(icc_eoir0_el1, ICC_EOIR0_EL1) DEFINE_RENAME_SYSREG_WRITE_FUNC(icc_eoir1_el1, ICC_EOIR1_EL1) +DEFINE_RENAME_SYSREG_WRITE_FUNC(icc_sgi0r_el1, ICC_SGI0R_EL1) #define IS_IN_EL(x) \ diff --git a/include/plat/common/platform.h b/include/plat/common/platform.h index c97b7d386..b4e33d9bc 100644 --- a/include/plat/common/platform.h +++ b/include/plat/common/platform.h @@ -82,6 +82,7 @@ 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_raise_el3_sgi(int sgi_num, u_register_t target); /******************************************************************************* * Optional common functions (may be overridden) diff --git a/plat/common/plat_gicv2.c b/plat/common/plat_gicv2.c index c785d8317..5df9c79b9 100644 --- a/plat/common/plat_gicv2.c +++ b/plat/common/plat_gicv2.c @@ -7,6 +7,7 @@ #include #include #include +#include /* * The following platform GIC functions are weakly defined. They @@ -29,6 +30,7 @@ #pragma weak plat_ic_disable_interrupt #pragma weak plat_ic_set_interrupt_priority #pragma weak plat_ic_set_interrupt_type +#pragma weak plat_ic_raise_el3_sgi /* * This function returns the highest priority pending interrupt at @@ -220,3 +222,21 @@ void plat_ic_set_interrupt_type(unsigned int id, unsigned int type) gicv2_set_interrupt_type(id, gicv2_type); } + +void plat_ic_raise_el3_sgi(int sgi_num, u_register_t target) +{ +#if GICV2_G0_FOR_EL3 + int id; + + /* Target must be a valid MPIDR in the system */ + id = plat_core_pos_by_mpidr(target); + assert(id >= 0); + + /* Verify that this is a secure SGI */ + assert(plat_ic_get_interrupt_type(sgi_num) == INTR_TYPE_EL3); + + gicv2_raise_sgi(sgi_num, id); +#else + assert(0); +#endif +} diff --git a/plat/common/plat_gicv3.c b/plat/common/plat_gicv3.c index f4279ec44..819ca4513 100644 --- a/plat/common/plat_gicv3.c +++ b/plat/common/plat_gicv3.c @@ -35,6 +35,7 @@ #pragma weak plat_ic_disable_interrupt #pragma weak plat_ic_set_interrupt_priority #pragma weak plat_ic_set_interrupt_type +#pragma weak plat_ic_raise_el3_sgi CASSERT((INTR_TYPE_S_EL1 == INTR_GROUP1S) && (INTR_TYPE_NS == INTR_GROUP1NS) && @@ -217,6 +218,17 @@ void plat_ic_set_interrupt_type(unsigned int id, unsigned int type) { gicv3_set_interrupt_type(id, plat_my_core_pos(), type); } + +void plat_ic_raise_el3_sgi(int sgi_num, u_register_t target) +{ + /* Target must be a valid MPIDR in the system */ + assert(plat_core_pos_by_mpidr(target) >= 0); + + /* Verify that this is a secure EL3 SGI */ + assert(plat_ic_get_interrupt_type(sgi_num) == INTR_TYPE_EL3); + + gicv3_raise_secure_g0_sgi(sgi_num, target); +} #endif #ifdef IMAGE_BL32 From fc529fee720de5fef8388c52bfefcb807ac764b0 Mon Sep 17 00:00:00 2001 From: Jeenu Viswambharan Date: Fri, 22 Sep 2017 08:32:09 +0100 Subject: [PATCH 10/14] GIC: Add API to set interrupt routing SPIs can be routed to either a specific PE, or to any one of all available PEs. API documentation updated. Change-Id: I28675f634568aaf4ea1aa8aa7ebf25b419a963ed Co-authored-by: Yousuf A Signed-off-by: Jeenu Viswambharan --- docs/platform-interrupt-controller-API.rst | 26 +++++++++++++++ drivers/arm/gic/v2/gicv2_helpers.c | 11 +------ drivers/arm/gic/v2/gicv2_main.c | 35 ++++++++++++++++++++ drivers/arm/gic/v2/gicv2_private.h | 11 +++++++ drivers/arm/gic/v3/gicv3_main.c | 38 ++++++++++++++++++++++ include/bl31/interrupt_mgmt.h | 7 +++- include/drivers/arm/gic_common.h | 1 + include/drivers/arm/gicv2.h | 1 + include/drivers/arm/gicv3.h | 5 +++ include/plat/common/platform.h | 2 ++ plat/common/plat_gicv2.c | 22 +++++++++++++ plat/common/plat_gicv3.c | 21 ++++++++++++ 12 files changed, 169 insertions(+), 11 deletions(-) diff --git a/docs/platform-interrupt-controller-API.rst b/docs/platform-interrupt-controller-API.rst index 74291bcaa..df4139e3d 100644 --- a/docs/platform-interrupt-controller-API.rst +++ b/docs/platform-interrupt-controller-API.rst @@ -217,6 +217,32 @@ In case of ARM standard platforms using GIC, the implementation of the API inserts barrier to make memory updates visible before raising SGI, then writes to appropriate *SGI Register* in order to raise the EL3 SGI. +Function: void plat_ic_set_spi_routing(unsigned int id, unsigned int routing_mode, u_register_t mpidr); [optional] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +:: + + Argument : unsigned int + Argument : unsigned int + Argument : u_register_t + Return : void + +This API should set the routing mode of Share Peripheral Interrupt (SPI) +specified by first parameter ``id`` to that specified by the second parameter +``routing_mode``. + +The ``routing_mode`` parameter can be one of: + +- ``INTR_ROUTING_MODE_ANY`` means the interrupt can be routed to any PE in the + system. The ``mpidr`` parameter is ignored in this case. + +- ``INTR_ROUTING_MODE_PE`` means the interrupt is routed to the PE whose MPIDR + value is specified by the parameter ``mpidr``. + +In case of ARM standard platforms using GIC, the implementation of the API +writes to the GIC *Target Register* (GICv2) or *Route Register* (GICv3) to set +the routing. + ---- *Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.* diff --git a/drivers/arm/gic/v2/gicv2_helpers.c b/drivers/arm/gic/v2/gicv2_helpers.c index 7cdbc27c9..26930769b 100644 --- a/drivers/arm/gic/v2/gicv2_helpers.c +++ b/drivers/arm/gic/v2/gicv2_helpers.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2016, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2017, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -72,15 +72,6 @@ void gicd_write_spendsgir(uintptr_t base, unsigned int id, unsigned int val) mmio_write_32(base + GICD_SPENDSGIR + (n << 2), val); } -/* - * Accessor to write the GIC Distributor ITARGETSR corresponding to the - * interrupt `id`. - */ -void gicd_set_itargetsr(uintptr_t base, unsigned int id, unsigned int target) -{ - mmio_write_8(base + GICD_ITARGETSR + id, target & GIC_TARGET_CPU_MASK); -} - /******************************************************************************* * Get the current CPU bit mask from GICD_ITARGETSR0 ******************************************************************************/ diff --git a/drivers/arm/gic/v2/gicv2_main.c b/drivers/arm/gic/v2/gicv2_main.c index b59e4586f..f0b902cf9 100644 --- a/drivers/arm/gic/v2/gicv2_main.c +++ b/drivers/arm/gic/v2/gicv2_main.c @@ -403,3 +403,38 @@ void gicv2_raise_sgi(int sgi_num, int proc_num) dsbishst(); gicd_write_sgir(driver_data->gicd_base, sgir_val); } + +/******************************************************************************* + * This function sets the interrupt routing for the given SPI interrupt id. + * The interrupt routing is specified in routing mode. The proc_num parameter is + * linear index of the PE to target SPI. When proc_num < 0, the SPI may target + * all PEs. + ******************************************************************************/ +void gicv2_set_spi_routing(unsigned int id, int proc_num) +{ + int target; + + assert(driver_data); + assert(driver_data->gicd_base); + + assert(id >= MIN_SPI_ID && id <= MAX_SPI_ID); + + /* + * Target masks array must have been supplied, and the core position + * should be valid. + */ + assert(driver_data->target_masks); + assert(proc_num < GICV2_MAX_TARGET_PE); + assert(proc_num < driver_data->target_masks_num); + + if (proc_num < 0) { + /* Target all PEs */ + target = GIC_TARGET_CPU_MASK; + } else { + /* Don't route interrupt if the mask hasn't been populated */ + target = driver_data->target_masks[proc_num]; + assert(target != 0); + } + + gicd_set_itargetsr(driver_data->gicd_base, id, target); +} diff --git a/drivers/arm/gic/v2/gicv2_private.h b/drivers/arm/gic/v2/gicv2_private.h index 915fc3a7f..70f059768 100644 --- a/drivers/arm/gic/v2/gicv2_private.h +++ b/drivers/arm/gic/v2/gicv2_private.h @@ -34,6 +34,17 @@ static inline unsigned int gicd_read_pidr2(uintptr_t base) /******************************************************************************* * GIC Distributor interface accessors for writing entire registers ******************************************************************************/ +static inline unsigned int gicd_get_itargetsr(uintptr_t base, unsigned int id) +{ + return mmio_read_8(base + GICD_ITARGETSR + id); +} + +static inline void gicd_set_itargetsr(uintptr_t base, unsigned int id, + unsigned int target) +{ + mmio_write_8(base + GICD_ITARGETSR + id, target & GIC_TARGET_CPU_MASK); +} + static inline void gicd_write_sgir(uintptr_t base, unsigned int val) { mmio_write_32(base + GICD_SGIR, val); diff --git a/drivers/arm/gic/v3/gicv3_main.c b/drivers/arm/gic/v3/gicv3_main.c index a5067322f..c81ba9504 100644 --- a/drivers/arm/gic/v3/gicv3_main.c +++ b/drivers/arm/gic/v3/gicv3_main.c @@ -999,3 +999,41 @@ void gicv3_raise_secure_g0_sgi(int sgi_num, u_register_t target) write_icc_sgi0r_el1(sgi_val); isb(); } + +/******************************************************************************* + * This function sets the interrupt routing for the given SPI interrupt id. + * The interrupt routing is specified in routing mode and mpidr. + * + * The routing mode can be either of: + * - GICV3_IRM_ANY + * - GICV3_IRM_PE + * + * The mpidr is the affinity of the PE to which the interrupt will be routed, + * and is ignored for routing mode GICV3_IRM_ANY. + ******************************************************************************/ +void gicv3_set_spi_routing(unsigned int id, unsigned int irm, u_register_t mpidr) +{ + unsigned long long aff; + uint64_t router; + + assert(gicv3_driver_data); + assert(gicv3_driver_data->gicd_base); + + assert((irm == GICV3_IRM_ANY) || (irm == GICV3_IRM_PE)); + assert(id >= MIN_SPI_ID && id <= MAX_SPI_ID); + + aff = gicd_irouter_val_from_mpidr(mpidr, irm); + gicd_write_irouter(gicv3_driver_data->gicd_base, id, aff); + + /* + * In implementations that do not require 1 of N distribution of SPIs, + * IRM might be RAZ/WI. Read back and verify IRM bit. + */ + if (irm == GICV3_IRM_ANY) { + router = gicd_read_irouter(gicv3_driver_data->gicd_base, id); + if (!((router >> IROUTER_IRM_SHIFT) & IROUTER_IRM_MASK)) { + ERROR("GICv3 implementation doesn't support routing ANY\n"); + panic(); + } + } +} diff --git a/include/bl31/interrupt_mgmt.h b/include/bl31/interrupt_mgmt.h index 9a6a7faa7..4f5a6013a 100644 --- a/include/bl31/interrupt_mgmt.h +++ b/include/bl31/interrupt_mgmt.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2014-2017, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -17,6 +17,11 @@ #define INTR_TYPE_NS U(2) #define MAX_INTR_TYPES U(3) #define INTR_TYPE_INVAL MAX_INTR_TYPES + +/* Interrupt routing modes */ +#define INTR_ROUTING_MODE_PE 0 +#define INTR_ROUTING_MODE_ANY 1 + /* * Constant passed to the interrupt handler in the 'id' field when the * framework does not read the gic registers to determine the interrupt id. diff --git a/include/drivers/arm/gic_common.h b/include/drivers/arm/gic_common.h index f4e2c5266..14979cca4 100644 --- a/include/drivers/arm/gic_common.h +++ b/include/drivers/arm/gic_common.h @@ -74,6 +74,7 @@ #define ISACTIVER_SHIFT 5 #define ICACTIVER_SHIFT ISACTIVER_SHIFT #define IPRIORITYR_SHIFT 2 +#define ITARGETSR_SHIFT 2 #define ICFGR_SHIFT 4 #define NSACR_SHIFT 4 diff --git a/include/drivers/arm/gicv2.h b/include/drivers/arm/gicv2.h index c9bcbb659..6d224c3fb 100644 --- a/include/drivers/arm/gicv2.h +++ b/include/drivers/arm/gicv2.h @@ -171,6 +171,7 @@ 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); void gicv2_raise_sgi(int sgi_num, int proc_num); +void gicv2_set_spi_routing(unsigned int id, int proc_num); #endif /* __ASSEMBLY__ */ #endif /* __GICV2_H__ */ diff --git a/include/drivers/arm/gicv3.h b/include/drivers/arm/gicv3.h index bf294f137..4f7cd8ab7 100644 --- a/include/drivers/arm/gicv3.h +++ b/include/drivers/arm/gicv3.h @@ -75,6 +75,9 @@ #define IROUTER_IRM_SHIFT 31 #define IROUTER_IRM_MASK 0x1 +#define GICV3_IRM_PE 0 +#define GICV3_IRM_ANY 1 + #define NUM_OF_DIST_REGS 30 /******************************************************************************* @@ -382,6 +385,8 @@ void gicv3_set_interrupt_priority(unsigned int id, unsigned int proc_num, void gicv3_set_interrupt_type(unsigned int id, unsigned int proc_num, unsigned int group); void gicv3_raise_secure_g0_sgi(int sgi_num, u_register_t target); +void gicv3_set_spi_routing(unsigned int id, unsigned int irm, + u_register_t mpidr); #endif /* __ASSEMBLY__ */ #endif /* __GICV3_H__ */ diff --git a/include/plat/common/platform.h b/include/plat/common/platform.h index b4e33d9bc..5c4e0588d 100644 --- a/include/plat/common/platform.h +++ b/include/plat/common/platform.h @@ -83,6 +83,8 @@ 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_raise_el3_sgi(int sgi_num, u_register_t target); +void plat_ic_set_spi_routing(unsigned int id, unsigned int routing_mode, + u_register_t mpidr); /******************************************************************************* * Optional common functions (may be overridden) diff --git a/plat/common/plat_gicv2.c b/plat/common/plat_gicv2.c index 5df9c79b9..c30d872a7 100644 --- a/plat/common/plat_gicv2.c +++ b/plat/common/plat_gicv2.c @@ -31,6 +31,7 @@ #pragma weak plat_ic_set_interrupt_priority #pragma weak plat_ic_set_interrupt_type #pragma weak plat_ic_raise_el3_sgi +#pragma weak plat_ic_set_spi_routing /* * This function returns the highest priority pending interrupt at @@ -240,3 +241,24 @@ void plat_ic_raise_el3_sgi(int sgi_num, u_register_t target) assert(0); #endif } + +void plat_ic_set_spi_routing(unsigned int id, unsigned int routing_mode, + u_register_t mpidr) +{ + int proc_num = 0; + + switch (routing_mode) { + case INTR_ROUTING_MODE_PE: + proc_num = plat_core_pos_by_mpidr(mpidr); + assert(proc_num >= 0); + break; + case INTR_ROUTING_MODE_ANY: + /* Bit mask selecting all 8 CPUs as candidates */ + proc_num = -1; + break; + default: + assert(0); + } + + gicv2_set_spi_routing(id, proc_num); +} diff --git a/plat/common/plat_gicv3.c b/plat/common/plat_gicv3.c index 819ca4513..7b3ea337e 100644 --- a/plat/common/plat_gicv3.c +++ b/plat/common/plat_gicv3.c @@ -36,6 +36,7 @@ #pragma weak plat_ic_set_interrupt_priority #pragma weak plat_ic_set_interrupt_type #pragma weak plat_ic_raise_el3_sgi +#pragma weak plat_ic_set_spi_routing CASSERT((INTR_TYPE_S_EL1 == INTR_GROUP1S) && (INTR_TYPE_NS == INTR_GROUP1NS) && @@ -229,6 +230,26 @@ void plat_ic_raise_el3_sgi(int sgi_num, u_register_t target) gicv3_raise_secure_g0_sgi(sgi_num, target); } + +void plat_ic_set_spi_routing(unsigned int id, unsigned int routing_mode, + u_register_t mpidr) +{ + unsigned int irm = 0; + + switch (routing_mode) { + case INTR_ROUTING_MODE_PE: + assert(plat_core_pos_by_mpidr(mpidr) >= 0); + irm = GICV3_IRM_PE; + break; + case INTR_ROUTING_MODE_ANY: + irm = GICV3_IRM_ANY; + break; + default: + assert(0); + } + + gicv3_set_spi_routing(id, irm, mpidr); +} #endif #ifdef IMAGE_BL32 From a2816a16440d9eb1223ba505bc30faf6cd31b0ee Mon Sep 17 00:00:00 2001 From: Jeenu Viswambharan Date: Fri, 22 Sep 2017 08:32:09 +0100 Subject: [PATCH 11/14] GIC: Add API to set/clear interrupt pending API documentation updated. Change-Id: I14e33cfc7dfa93257c82d76fae186b17a1b6d266 Co-authored-by: Yousuf A Signed-off-by: Jeenu Viswambharan --- docs/platform-interrupt-controller-API.rst | 31 +++++++++++++ drivers/arm/gic/v2/gicv2_main.c | 38 ++++++++++++++++ drivers/arm/gic/v3/gicv3_helpers.c | 22 +++++++++ drivers/arm/gic/v3/gicv3_main.c | 52 ++++++++++++++++++++++ drivers/arm/gic/v3/gicv3_private.h | 7 +++ include/drivers/arm/gicv2.h | 2 + include/drivers/arm/gicv3.h | 2 + include/plat/common/platform.h | 2 + plat/common/plat_gicv2.c | 10 +++++ plat/common/plat_gicv3.c | 16 +++++++ 10 files changed, 182 insertions(+) diff --git a/docs/platform-interrupt-controller-API.rst b/docs/platform-interrupt-controller-API.rst index df4139e3d..d5c167339 100644 --- a/docs/platform-interrupt-controller-API.rst +++ b/docs/platform-interrupt-controller-API.rst @@ -243,6 +243,37 @@ In case of ARM standard platforms using GIC, the implementation of the API writes to the GIC *Target Register* (GICv2) or *Route Register* (GICv3) to set the routing. +Function: void plat_ic_set_interrupt_pending(unsigned int id); [optional] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +:: + + Argument : unsigned int + Return : void + +This API should set the interrupt specified by first parameter ``id`` to +*Pending*. + +In case of ARM standard platforms using GIC, the implementation of the API +inserts barrier to make memory updates visible before setting interrupt pending, +and writes to the GIC *Set Pending Register* to set the interrupt pending +status. + +Function: void plat_ic_clear_interrupt_pending(unsigned int id); [optional] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +:: + + Argument : unsigned int + Return : void + +This API should clear the *Pending* status of the interrupt specified by first +parameter ``id``. + +In case of ARM standard platforms using GIC, the implementation of the API +writes to the GIC *Clear Pending Register* to clear the interrupt pending +status, and inserts barrier to make memory updates visible afterwards. + ---- *Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.* diff --git a/drivers/arm/gic/v2/gicv2_main.c b/drivers/arm/gic/v2/gicv2_main.c index f0b902cf9..eab4c3bfe 100644 --- a/drivers/arm/gic/v2/gicv2_main.c +++ b/drivers/arm/gic/v2/gicv2_main.c @@ -438,3 +438,41 @@ void gicv2_set_spi_routing(unsigned int id, int proc_num) gicd_set_itargetsr(driver_data->gicd_base, id, target); } + +/******************************************************************************* + * This function clears the pending status of an interrupt identified by id. + ******************************************************************************/ +void gicv2_clear_interrupt_pending(unsigned int id) +{ + assert(driver_data); + assert(driver_data->gicd_base); + + /* SGIs can't be cleared pending */ + assert(id >= MIN_PPI_ID); + + /* + * Clear pending interrupt, and ensure that any shared variable updates + * depending on out of band interrupt trigger are observed afterwards. + */ + gicd_set_icpendr(driver_data->gicd_base, id); + dsbishst(); +} + +/******************************************************************************* + * This function sets the pending status of an interrupt identified by id. + ******************************************************************************/ +void gicv2_set_interrupt_pending(unsigned int id) +{ + assert(driver_data); + assert(driver_data->gicd_base); + + /* SGIs can't be cleared pending */ + assert(id >= MIN_PPI_ID); + + /* + * Ensure that any shared variable updates depending on out of band + * interrupt trigger are observed before setting interrupt pending. + */ + dsbishst(); + gicd_set_ispendr(driver_data->gicd_base, id); +} diff --git a/drivers/arm/gic/v3/gicv3_helpers.c b/drivers/arm/gic/v3/gicv3_helpers.c index ee874f92f..3abc6a5ca 100644 --- a/drivers/arm/gic/v3/gicv3_helpers.c +++ b/drivers/arm/gic/v3/gicv3_helpers.c @@ -194,6 +194,28 @@ unsigned int gicr_get_isactiver0(uintptr_t base, unsigned int id) return (reg_val >> bit_num) & 0x1; } +/* + * Accessor to clear the bit corresponding to interrupt ID in GIC Re-distributor + * ICPENDRR0. + */ +void gicr_set_icpendr0(uintptr_t base, unsigned int id) +{ + unsigned bit_num = id & ((1 << ICPENDR_SHIFT) - 1); + + gicr_write_icpendr0(base, (1 << bit_num)); +} + +/* + * Accessor to set the bit corresponding to interrupt ID in GIC Re-distributor + * ISPENDR0. + */ +void gicr_set_ispendr0(uintptr_t base, unsigned int id) +{ + unsigned bit_num = id & ((1 << ISPENDR_SHIFT) - 1); + + gicr_write_ispendr0(base, (1 << bit_num)); +} + /* * Accessor to set the byte corresponding to interrupt ID * in GIC Re-distributor IPRIORITYR. diff --git a/drivers/arm/gic/v3/gicv3_main.c b/drivers/arm/gic/v3/gicv3_main.c index c81ba9504..43dd77f12 100644 --- a/drivers/arm/gic/v3/gicv3_main.c +++ b/drivers/arm/gic/v3/gicv3_main.c @@ -1037,3 +1037,55 @@ void gicv3_set_spi_routing(unsigned int id, unsigned int irm, u_register_t mpidr } } } + +/******************************************************************************* + * This function clears the pending status of an interrupt identified by id. + * The proc_num is used if the interrupt is SGI or PPI, and programs the + * corresponding Redistributor interface. + ******************************************************************************/ +void gicv3_clear_interrupt_pending(unsigned int id, unsigned int proc_num) +{ + 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); + + /* + * Clear pending interrupt, and ensure that any shared variable updates + * depending on out of band interrupt trigger are observed afterwards. + */ + if (id < MIN_SPI_ID) { + /* For SGIs and PPIs */ + gicr_set_icpendr0(gicv3_driver_data->rdistif_base_addrs[proc_num], + id); + } else { + gicd_set_icpendr(gicv3_driver_data->gicd_base, id); + } + dsbishst(); +} + +/******************************************************************************* + * This function sets the pending status of an interrupt identified by id. + * The proc_num is used if the interrupt is SGI or PPI and programs the + * corresponding Redistributor interface. + ******************************************************************************/ +void gicv3_set_interrupt_pending(unsigned int id, unsigned int proc_num) +{ + 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); + + /* + * Ensure that any shared variable updates depending on out of band + * interrupt trigger are observed before setting interrupt pending. + */ + dsbishst(); + if (id < MIN_SPI_ID) { + /* For SGIs and PPIs */ + gicr_set_ispendr0(gicv3_driver_data->rdistif_base_addrs[proc_num], + id); + } else { + gicd_set_ispendr(gicv3_driver_data->gicd_base, id); + } +} diff --git a/drivers/arm/gic/v3/gicv3_private.h b/drivers/arm/gic/v3/gicv3_private.h index 19fce2e73..43529c5a5 100644 --- a/drivers/arm/gic/v3/gicv3_private.h +++ b/drivers/arm/gic/v3/gicv3_private.h @@ -72,6 +72,8 @@ void gicd_set_igrpmodr(uintptr_t base, unsigned int id); void gicr_set_igrpmodr0(uintptr_t base, unsigned int id); void gicr_set_isenabler0(uintptr_t base, unsigned int id); void gicr_set_icenabler0(uintptr_t base, unsigned int id); +void gicr_set_ispendr0(uintptr_t base, unsigned int id); +void gicr_set_icpendr0(uintptr_t base, unsigned int id); void gicr_set_igroupr0(uintptr_t base, unsigned int id); void gicd_clr_igrpmodr(uintptr_t base, unsigned int id); void gicr_clr_igrpmodr0(uintptr_t base, unsigned int id); @@ -221,6 +223,11 @@ static inline unsigned int gicr_read_isenabler0(uintptr_t base) return mmio_read_32(base + GICR_ISENABLER0); } +static inline void gicr_write_icpendr0(uintptr_t base, unsigned int val) +{ + mmio_write_32(base + GICR_ICPENDR0, val); +} + static inline void gicr_write_isenabler0(uintptr_t base, unsigned int val) { mmio_write_32(base + GICR_ISENABLER0, val); diff --git a/include/drivers/arm/gicv2.h b/include/drivers/arm/gicv2.h index 6d224c3fb..bc2822ee1 100644 --- a/include/drivers/arm/gicv2.h +++ b/include/drivers/arm/gicv2.h @@ -172,6 +172,8 @@ void gicv2_set_interrupt_priority(unsigned int id, unsigned int priority); void gicv2_set_interrupt_type(unsigned int id, unsigned int type); void gicv2_raise_sgi(int sgi_num, int proc_num); void gicv2_set_spi_routing(unsigned int id, int proc_num); +void gicv2_set_interrupt_pending(unsigned int id); +void gicv2_clear_interrupt_pending(unsigned int id); #endif /* __ASSEMBLY__ */ #endif /* __GICV2_H__ */ diff --git a/include/drivers/arm/gicv3.h b/include/drivers/arm/gicv3.h index 4f7cd8ab7..09d6d8084 100644 --- a/include/drivers/arm/gicv3.h +++ b/include/drivers/arm/gicv3.h @@ -387,6 +387,8 @@ void gicv3_set_interrupt_type(unsigned int id, unsigned int proc_num, void gicv3_raise_secure_g0_sgi(int sgi_num, u_register_t target); void gicv3_set_spi_routing(unsigned int id, unsigned int irm, u_register_t mpidr); +void gicv3_set_interrupt_pending(unsigned int id, unsigned int proc_num); +void gicv3_clear_interrupt_pending(unsigned int id, unsigned int proc_num); #endif /* __ASSEMBLY__ */ #endif /* __GICV3_H__ */ diff --git a/include/plat/common/platform.h b/include/plat/common/platform.h index 5c4e0588d..ab5d68ed5 100644 --- a/include/plat/common/platform.h +++ b/include/plat/common/platform.h @@ -85,6 +85,8 @@ void plat_ic_set_interrupt_priority(unsigned int id, unsigned int priority); void plat_ic_raise_el3_sgi(int sgi_num, u_register_t target); void plat_ic_set_spi_routing(unsigned int id, unsigned int routing_mode, u_register_t mpidr); +void plat_ic_set_interrupt_pending(unsigned int id); +void plat_ic_clear_interrupt_pending(unsigned int id); /******************************************************************************* * Optional common functions (may be overridden) diff --git a/plat/common/plat_gicv2.c b/plat/common/plat_gicv2.c index c30d872a7..646049489 100644 --- a/plat/common/plat_gicv2.c +++ b/plat/common/plat_gicv2.c @@ -262,3 +262,13 @@ void plat_ic_set_spi_routing(unsigned int id, unsigned int routing_mode, gicv2_set_spi_routing(id, proc_num); } + +void plat_ic_set_interrupt_pending(unsigned int id) +{ + gicv2_set_interrupt_pending(id); +} + +void plat_ic_clear_interrupt_pending(unsigned int id) +{ + gicv2_clear_interrupt_pending(id); +} diff --git a/plat/common/plat_gicv3.c b/plat/common/plat_gicv3.c index 7b3ea337e..e5bf014d2 100644 --- a/plat/common/plat_gicv3.c +++ b/plat/common/plat_gicv3.c @@ -37,6 +37,8 @@ #pragma weak plat_ic_set_interrupt_type #pragma weak plat_ic_raise_el3_sgi #pragma weak plat_ic_set_spi_routing +#pragma weak plat_ic_set_interrupt_pending +#pragma weak plat_ic_clear_interrupt_pending CASSERT((INTR_TYPE_S_EL1 == INTR_GROUP1S) && (INTR_TYPE_NS == INTR_GROUP1NS) && @@ -250,6 +252,20 @@ void plat_ic_set_spi_routing(unsigned int id, unsigned int routing_mode, gicv3_set_spi_routing(id, irm, mpidr); } + +void plat_ic_set_interrupt_pending(unsigned int id) +{ + /* Disallow setting SGIs pending */ + assert(id >= MIN_PPI_ID); + gicv3_set_interrupt_pending(id, plat_my_core_pos()); +} + +void plat_ic_clear_interrupt_pending(unsigned int id) +{ + /* Disallow setting SGIs pending */ + assert(id >= MIN_PPI_ID); + gicv3_clear_interrupt_pending(id, plat_my_core_pos()); +} #endif #ifdef IMAGE_BL32 From d55a445069736e2652b44ddfeb9ea4d306796a0a Mon Sep 17 00:00:00 2001 From: Jeenu Viswambharan Date: Fri, 22 Sep 2017 08:32:09 +0100 Subject: [PATCH 12/14] GIC: Add API to set priority mask API documentation updated. Change-Id: I40feec1fe67a960d035061b54dd55610bc34ce1d Signed-off-by: Jeenu Viswambharan --- docs/platform-interrupt-controller-API.rst | 18 ++++++++++++++++ drivers/arm/gic/v2/gicv2_main.c | 24 ++++++++++++++++++++++ drivers/arm/gic/v3/gicv3_main.c | 22 ++++++++++++++++++++ include/drivers/arm/gicv2.h | 1 + include/drivers/arm/gicv3.h | 1 + include/lib/aarch32/arch_helpers.h | 1 + include/lib/aarch64/arch_helpers.h | 1 + include/plat/common/platform.h | 1 + plat/common/plat_gicv2.c | 5 +++++ plat/common/plat_gicv3.c | 5 +++++ 10 files changed, 79 insertions(+) diff --git a/docs/platform-interrupt-controller-API.rst b/docs/platform-interrupt-controller-API.rst index d5c167339..795c08562 100644 --- a/docs/platform-interrupt-controller-API.rst +++ b/docs/platform-interrupt-controller-API.rst @@ -274,6 +274,24 @@ In case of ARM standard platforms using GIC, the implementation of the API writes to the GIC *Clear Pending Register* to clear the interrupt pending status, and inserts barrier to make memory updates visible afterwards. +Function: unsigned int plat_ic_set_priority_mask(unsigned int id); [optional] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +:: + + Argument : unsigned int + Return : int + +This API should set the priority mask (first parameter) in the interrupt +controller such that only interrupts of higher priority than the supplied one +may be signalled to the PE. The API should return the current priority value +that it's overwriting. + +In case of ARM standard platforms using GIC, the implementation of the API +inserts to order memory updates before updating mask, then writes to the GIC +*Priority Mask Register*, and make sure memory updates are visible before +potential trigger due to mask update. + ---- *Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.* diff --git a/drivers/arm/gic/v2/gicv2_main.c b/drivers/arm/gic/v2/gicv2_main.c index eab4c3bfe..59b663232 100644 --- a/drivers/arm/gic/v2/gicv2_main.c +++ b/drivers/arm/gic/v2/gicv2_main.c @@ -476,3 +476,27 @@ void gicv2_set_interrupt_pending(unsigned int id) dsbishst(); gicd_set_ispendr(driver_data->gicd_base, id); } + +/******************************************************************************* + * This function sets the PMR register with the supplied value. Returns the + * original PMR. + ******************************************************************************/ +unsigned int gicv2_set_pmr(unsigned int mask) +{ + unsigned int old_mask; + + assert(driver_data); + assert(driver_data->gicc_base); + + old_mask = gicc_read_pmr(driver_data->gicc_base); + + /* + * Order memory updates w.r.t. PMR write, and ensure they're visible + * before potential out of band interrupt trigger because of PMR update. + */ + dmbishst(); + gicc_write_pmr(driver_data->gicc_base, mask); + dsbishst(); + + return old_mask; +} diff --git a/drivers/arm/gic/v3/gicv3_main.c b/drivers/arm/gic/v3/gicv3_main.c index 43dd77f12..0f50f6d76 100644 --- a/drivers/arm/gic/v3/gicv3_main.c +++ b/drivers/arm/gic/v3/gicv3_main.c @@ -1089,3 +1089,25 @@ void gicv3_set_interrupt_pending(unsigned int id, unsigned int proc_num) gicd_set_ispendr(gicv3_driver_data->gicd_base, id); } } + +/******************************************************************************* + * This function sets the PMR register with the supplied value. Returns the + * original PMR. + ******************************************************************************/ +unsigned int gicv3_set_pmr(unsigned int mask) +{ + unsigned int old_mask; + + old_mask = read_icc_pmr_el1(); + + /* + * Order memory updates w.r.t. PMR write, and ensure they're visible + * before potential out of band interrupt trigger because of PMR update. + * PMR system register writes are self-synchronizing, so no ISB required + * thereafter. + */ + dsbishst(); + write_icc_pmr_el1(mask); + + return old_mask; +} diff --git a/include/drivers/arm/gicv2.h b/include/drivers/arm/gicv2.h index bc2822ee1..9b8510aab 100644 --- a/include/drivers/arm/gicv2.h +++ b/include/drivers/arm/gicv2.h @@ -174,6 +174,7 @@ void gicv2_raise_sgi(int sgi_num, int proc_num); void gicv2_set_spi_routing(unsigned int id, int proc_num); void gicv2_set_interrupt_pending(unsigned int id); void gicv2_clear_interrupt_pending(unsigned int id); +unsigned int gicv2_set_pmr(unsigned int mask); #endif /* __ASSEMBLY__ */ #endif /* __GICV2_H__ */ diff --git a/include/drivers/arm/gicv3.h b/include/drivers/arm/gicv3.h index 09d6d8084..95b6e3bb9 100644 --- a/include/drivers/arm/gicv3.h +++ b/include/drivers/arm/gicv3.h @@ -389,6 +389,7 @@ void gicv3_set_spi_routing(unsigned int id, unsigned int irm, u_register_t mpidr); void gicv3_set_interrupt_pending(unsigned int id, unsigned int proc_num); void gicv3_clear_interrupt_pending(unsigned int id, unsigned int proc_num); +unsigned int gicv3_set_pmr(unsigned int mask); #endif /* __ASSEMBLY__ */ #endif /* __GICV3_H__ */ diff --git a/include/lib/aarch32/arch_helpers.h b/include/lib/aarch32/arch_helpers.h index c8caea286..469e9b0d0 100644 --- a/include/lib/aarch32/arch_helpers.h +++ b/include/lib/aarch32/arch_helpers.h @@ -213,6 +213,7 @@ DEFINE_SYSOP_TYPE_FUNC(dmb, ld) DEFINE_SYSOP_TYPE_FUNC(dsb, ish) DEFINE_SYSOP_TYPE_FUNC(dsb, ishst) DEFINE_SYSOP_TYPE_FUNC(dmb, ish) +DEFINE_SYSOP_TYPE_FUNC(dmb, ishst) DEFINE_SYSOP_FUNC(isb) void __dead2 smc(uint32_t r0, uint32_t r1, uint32_t r2, uint32_t r3, diff --git a/include/lib/aarch64/arch_helpers.h b/include/lib/aarch64/arch_helpers.h index b880497ce..03110fd5a 100644 --- a/include/lib/aarch64/arch_helpers.h +++ b/include/lib/aarch64/arch_helpers.h @@ -204,6 +204,7 @@ DEFINE_SYSOP_TYPE_FUNC(dmb, ld) DEFINE_SYSOP_TYPE_FUNC(dsb, ish) DEFINE_SYSOP_TYPE_FUNC(dsb, ishst) DEFINE_SYSOP_TYPE_FUNC(dmb, ish) +DEFINE_SYSOP_TYPE_FUNC(dmb, ishst) DEFINE_SYSOP_FUNC(isb) uint32_t get_afflvl_shift(uint32_t); diff --git a/include/plat/common/platform.h b/include/plat/common/platform.h index ab5d68ed5..f03a3997a 100644 --- a/include/plat/common/platform.h +++ b/include/plat/common/platform.h @@ -87,6 +87,7 @@ void plat_ic_set_spi_routing(unsigned int id, unsigned int routing_mode, u_register_t mpidr); void plat_ic_set_interrupt_pending(unsigned int id); void plat_ic_clear_interrupt_pending(unsigned int id); +unsigned int plat_ic_set_priority_mask(unsigned int mask); /******************************************************************************* * Optional common functions (may be overridden) diff --git a/plat/common/plat_gicv2.c b/plat/common/plat_gicv2.c index 646049489..05fabcab1 100644 --- a/plat/common/plat_gicv2.c +++ b/plat/common/plat_gicv2.c @@ -272,3 +272,8 @@ void plat_ic_clear_interrupt_pending(unsigned int id) { gicv2_clear_interrupt_pending(id); } + +unsigned int plat_ic_set_priority_mask(unsigned int mask) +{ + return gicv2_set_pmr(mask); +} diff --git a/plat/common/plat_gicv3.c b/plat/common/plat_gicv3.c index e5bf014d2..52ceb6a7c 100644 --- a/plat/common/plat_gicv3.c +++ b/plat/common/plat_gicv3.c @@ -266,6 +266,11 @@ void plat_ic_clear_interrupt_pending(unsigned int id) assert(id >= MIN_PPI_ID); gicv3_clear_interrupt_pending(id, plat_my_core_pos()); } + +unsigned int plat_ic_set_priority_mask(unsigned int mask) +{ + return gicv3_set_pmr(mask); +} #endif #ifdef IMAGE_BL32 From 22966106967b01768db5140ce20f62dd7f20358f Mon Sep 17 00:00:00 2001 From: Jeenu Viswambharan Date: Fri, 22 Sep 2017 08:32:09 +0100 Subject: [PATCH 13/14] GIC: Add helpers to set interrupt configuration The helpers perform read-modify-write on GIC*_ICFGR registers, but don't serialise callers. Any serialisation must be taken care of by the callers. Change-Id: I71995f82ff2c7f70d37af0ede30d6ee18682fd3f Signed-off-by: Jeenu Viswambharan --- drivers/arm/gic/common/gic_common.c | 12 ++++++++ drivers/arm/gic/common/gic_common_private.h | 1 + drivers/arm/gic/v3/gicv3_helpers.c | 32 +++++++++++++++++++++ include/drivers/arm/gic_common.h | 3 ++ 4 files changed, 48 insertions(+) diff --git a/drivers/arm/gic/common/gic_common.c b/drivers/arm/gic/common/gic_common.c index 71155a970..d523772b1 100644 --- a/drivers/arm/gic/common/gic_common.c +++ b/drivers/arm/gic/common/gic_common.c @@ -299,3 +299,15 @@ void gicd_set_ipriorityr(uintptr_t base, unsigned int id, unsigned int pri) { mmio_write_8(base + GICD_IPRIORITYR + id, pri & GIC_PRI_MASK); } + +void gicd_set_icfgr(uintptr_t base, unsigned int id, unsigned int cfg) +{ + unsigned bit_num = id & ((1 << ICFGR_SHIFT) - 1); + uint32_t reg_val = gicd_read_icfgr(base, id); + + /* Clear the field, and insert required configuration */ + reg_val &= ~(GIC_CFG_MASK << bit_num); + reg_val |= ((cfg & GIC_CFG_MASK) << bit_num); + + gicd_write_icfgr(base, id, reg_val); +} diff --git a/drivers/arm/gic/common/gic_common_private.h b/drivers/arm/gic/common/gic_common_private.h index 8b96b37bb..2021f9aae 100644 --- a/drivers/arm/gic/common/gic_common_private.h +++ b/drivers/arm/gic/common/gic_common_private.h @@ -77,5 +77,6 @@ unsigned int gicd_get_isactiver(uintptr_t base, unsigned int id); void gicd_set_isactiver(uintptr_t base, unsigned int id); void gicd_set_icactiver(uintptr_t base, unsigned int id); void gicd_set_ipriorityr(uintptr_t base, unsigned int id, unsigned int pri); +void gicd_set_icfgr(uintptr_t base, unsigned int id, unsigned int cfg); #endif /* GIC_COMMON_PRIVATE_H_ */ diff --git a/drivers/arm/gic/v3/gicv3_helpers.c b/drivers/arm/gic/v3/gicv3_helpers.c index 3abc6a5ca..33dbe2c8e 100644 --- a/drivers/arm/gic/v3/gicv3_helpers.c +++ b/drivers/arm/gic/v3/gicv3_helpers.c @@ -225,6 +225,38 @@ void gicr_set_ipriorityr(uintptr_t base, unsigned int id, unsigned int pri) mmio_write_8(base + GICR_IPRIORITYR + id, pri & GIC_PRI_MASK); } +/* + * Accessor to set the bit fields corresponding to interrupt ID + * in GIC Re-distributor ICFGR0. + */ +void gicr_set_icfgr0(uintptr_t base, unsigned int id, unsigned int cfg) +{ + unsigned bit_num = id & ((1 << ICFGR_SHIFT) - 1); + uint32_t reg_val = gicr_read_icfgr0(base); + + /* Clear the field, and insert required configuration */ + reg_val &= ~(GIC_CFG_MASK << bit_num); + reg_val |= ((cfg & GIC_CFG_MASK) << bit_num); + + gicr_write_icfgr0(base, reg_val); +} + +/* + * Accessor to set the bit fields corresponding to interrupt ID + * in GIC Re-distributor ICFGR1. + */ +void gicr_set_icfgr1(uintptr_t base, unsigned int id, unsigned int cfg) +{ + unsigned bit_num = id & ((1 << ICFGR_SHIFT) - 1); + uint32_t reg_val = gicr_read_icfgr1(base); + + /* Clear the field, and insert required configuration */ + reg_val &= ~(GIC_CFG_MASK << bit_num); + reg_val |= ((cfg & GIC_CFG_MASK) << bit_num); + + gicr_write_icfgr1(base, reg_val); +} + /****************************************************************************** * This function marks the core as awake in the re-distributor and * ensures that the interface is active. diff --git a/include/drivers/arm/gic_common.h b/include/drivers/arm/gic_common.h index 14979cca4..f7fc8ceb6 100644 --- a/include/drivers/arm/gic_common.h +++ b/include/drivers/arm/gic_common.h @@ -23,6 +23,9 @@ /* Mask for the priority field common to all GIC interfaces */ #define GIC_PRI_MASK 0xff +/* Mask for the configuration field common to all GIC interfaces */ +#define GIC_CFG_MASK 0x3 + /* Constant to indicate a spurious interrupt in all GIC versions */ #define GIC_SPURIOUS_INTERRUPT 1023 From c639e8ebeeb152fc32f2feff65c84a37825400b3 Mon Sep 17 00:00:00 2001 From: Jeenu Viswambharan Date: Fri, 22 Sep 2017 08:32:09 +0100 Subject: [PATCH 14/14] GIC: Allow specifying interrupt properties The GIC driver initialization currently allows an array of interrupts to be configured as secure. Future use cases would require more interrupt configuration other than just security, such as priority. This patch introduces a new interrupt property array as part of both GICv2 and GICv3 driver data. The platform can populate the array with interrupt numbers and respective properties. The corresponding driver initialization iterates through the array, and applies interrupt configuration as required. This capability, and the current way of supplying array (or arrays, in case of GICv3) of secure interrupts, are however mutually exclusive. Henceforth, the platform should supply either: - A list of interrupts to be mapped as secure (the current way). Platforms that do this will continue working as they were. With this patch, this scheme is deprecated. - A list of interrupt properties (properties include interrupt group). Individual interrupt properties are specified via. descriptors of type 'interrupt_prop_desc_t', which can be populated with the macro INTR_PROP_DESC(). A run time assert checks that the platform doesn't specify both. Henceforth the old scheme of providing list of secure interrupts is deprecated. When built with ERROR_DEPRECATED=1, GIC drivers will require that the interrupt properties are supplied instead of an array of secure interrupts. Add a section to firmware design about configuring secure interrupts. Fixes ARM-software/tf-issues#262 Change-Id: I8eec29e72eb69dbb6bce77879febf32c95376942 Signed-off-by: Jeenu Viswambharan --- docs/firmware-design.rst | 50 +++++++++++++ drivers/arm/gic/v2/gicv2_helpers.c | 107 +++++++++++++++++++++++++++ drivers/arm/gic/v2/gicv2_main.c | 71 ++++++++++++------ drivers/arm/gic/v2/gicv2_private.h | 8 +++ drivers/arm/gic/v3/gicv3_helpers.c | 111 +++++++++++++++++++++++++++++ drivers/arm/gic/v3/gicv3_main.c | 106 +++++++++++++++++---------- drivers/arm/gic/v3/gicv3_private.h | 8 +++ include/bl31/interrupt_mgmt.h | 2 + include/common/interrupt_props.h | 29 ++++++++ include/drivers/arm/gic_common.h | 4 ++ include/drivers/arm/gicv2.h | 21 +++++- include/drivers/arm/gicv3.h | 78 ++++++++++++-------- 12 files changed, 504 insertions(+), 91 deletions(-) create mode 100644 include/common/interrupt_props.h diff --git a/docs/firmware-design.rst b/docs/firmware-design.rst index 997d29b87..aeb883ab7 100644 --- a/docs/firmware-design.rst +++ b/docs/firmware-design.rst @@ -1161,6 +1161,56 @@ In other words, the reset handler should be able to detect whether an action has already been performed and act as appropriate. Possible courses of actions are, e.g. skip the action the second time, or undo/redo it. +Configuring secure interrupts +----------------------------- + +The GIC driver is responsible for performing initial configuration of secure +interrupts on the platform. To this end, the platform is expected to provide the +GIC driver (either GICv2 or GICv3, as selected by the platform) with the +interrupt configuration during the driver initialisation. + +There are two ways to specify secure interrupt configuration: + +#. Array of secure interrupt properties: In this scheme, in both GICv2 and GICv3 + driver data structures, the ``interrupt_props`` member points to an array of + interrupt properties. Each element of the array specifies the interrupt + number and its configuration, viz. priority, group, configuration. Each + element of the array shall be populated by the macro ``INTR_PROP_DESC()``. + The macro takes the following arguments: + + - 10-bit interrupt number, + + - 8-bit interrupt priority, + + - Interrupt type (one of ``INTR_TYPE_EL3``, ``INTR_TYPE_S_EL1``, + ``INTR_TYPE_NS``), + + - Interrupt configuration (either ``GIC_INTR_CFG_LEVEL`` or + ``GIC_INTR_CFG_EDGE``). + +#. Array of secure interrupts: In this scheme, the GIC driver is provided an + array of secure interrupt numbers. The GIC driver, at the time of + initialisation, iterates through the array and assigns each interrupt + the appropriate group. + + - For the GICv2 driver, in ``gicv2_driver_data`` structure, the + ``g0_interrupt_array`` member of the should point to the array of + interrupts to be assigned to *Group 0*, and the ``g0_interrupt_num`` + member of the should be set to the number of interrupts in the array. + + - For the GICv3 driver, in ``gicv3_driver_data`` structure: + + - The ``g0_interrupt_array`` member of the should point to the array of + interrupts to be assigned to *Group 0*, and the ``g0_interrupt_num`` + member of the should be set to the number of interrupts in the array. + + - The ``g1s_interrupt_array`` member of the should point to the array of + interrupts to be assigned to *Group 1 Secure*, and the + ``g1s_interrupt_num`` member of the should be set to the number of + interrupts in the array. + + **Note that this scheme is deprecated.** + CPU specific operations framework --------------------------------- diff --git a/drivers/arm/gic/v2/gicv2_helpers.c b/drivers/arm/gic/v2/gicv2_helpers.c index 26930769b..0df50fb00 100644 --- a/drivers/arm/gic/v2/gicv2_helpers.c +++ b/drivers/arm/gic/v2/gicv2_helpers.c @@ -9,6 +9,7 @@ #include #include #include +#include #include "../common/gic_common_private.h" #include "gicv2_private.h" @@ -112,6 +113,7 @@ void gicv2_spis_configure_defaults(uintptr_t gicd_base) gicd_write_icfgr(gicd_base, index, 0); } +#if !ERROR_DEPRECATED /******************************************************************************* * Helper function to configure secure G0 SPIs. ******************************************************************************/ @@ -145,7 +147,49 @@ void gicv2_secure_spis_configure(uintptr_t gicd_base, } } +#endif +/******************************************************************************* + * Helper function to configure properties of secure G0 SPIs. + ******************************************************************************/ +void gicv2_secure_spis_configure_props(uintptr_t gicd_base, + const interrupt_prop_t *interrupt_props, + unsigned int interrupt_props_num) +{ + unsigned int i; + const interrupt_prop_t *prop_desc; + + /* Make sure there's a valid property array */ + assert(interrupt_props_num != 0 ? (uintptr_t) interrupt_props : 1); + + for (i = 0; i < interrupt_props_num; i++) { + prop_desc = &interrupt_props[i]; + + if (prop_desc->intr_num < MIN_SPI_ID) + continue; + + /* Configure this interrupt as a secure interrupt */ + assert(prop_desc->intr_grp == GICV2_INTR_GROUP0); + gicd_clr_igroupr(gicd_base, prop_desc->intr_num); + + /* Set the priority of this interrupt */ + gicd_set_ipriorityr(gicd_base, prop_desc->intr_num, + prop_desc->intr_pri); + + /* Target the secure interrupts to primary CPU */ + gicd_set_itargetsr(gicd_base, prop_desc->intr_num, + gicv2_get_cpuif_id(gicd_base)); + + /* Set interrupt configuration */ + gicd_set_icfgr(gicd_base, prop_desc->intr_num, + prop_desc->intr_cfg); + + /* Enable this interrupt */ + gicd_set_isenabler(gicd_base, prop_desc->intr_num); + } +} + +#if !ERROR_DEPRECATED /******************************************************************************* * Helper function to configure secure G0 SGIs and PPIs. ******************************************************************************/ @@ -193,3 +237,66 @@ void gicv2_secure_ppi_sgi_setup(uintptr_t gicd_base, /* Enable the Group 0 SGIs and PPIs */ gicd_write_isenabler(gicd_base, 0, sec_ppi_sgi_mask); } +#endif + +/******************************************************************************* + * Helper function to configure properties of secure G0 SGIs and PPIs. + ******************************************************************************/ +void gicv2_secure_ppi_sgi_setup_props(uintptr_t gicd_base, + const interrupt_prop_t *interrupt_props, + unsigned int interrupt_props_num) +{ + unsigned int i; + uint32_t sec_ppi_sgi_mask = 0; + const interrupt_prop_t *prop_desc; + + /* Make sure there's a valid property array */ + assert(interrupt_props_num != 0 ? (uintptr_t) interrupt_props : 1); + + /* + * Disable all SGIs (imp. def.)/PPIs before configuring them. This is a + * more scalable approach as it avoids clearing the enable bits in the + * GICD_CTLR. + */ + gicd_write_icenabler(gicd_base, 0, ~0); + + /* Setup the default PPI/SGI priorities doing four at a time */ + for (i = 0; i < MIN_SPI_ID; i += 4) + gicd_write_ipriorityr(gicd_base, i, GICD_IPRIORITYR_DEF_VAL); + + for (i = 0; i < interrupt_props_num; i++) { + prop_desc = &interrupt_props[i]; + + if (prop_desc->intr_num >= MIN_SPI_ID) + continue; + + /* Configure this interrupt as a secure interrupt */ + assert(prop_desc->intr_grp == GICV2_INTR_GROUP0); + + /* + * Set interrupt configuration for PPIs. Configuration for SGIs + * are ignored. + */ + if ((prop_desc->intr_num >= MIN_PPI_ID) && + (prop_desc->intr_num < MIN_SPI_ID)) { + gicd_set_icfgr(gicd_base, prop_desc->intr_num, + prop_desc->intr_cfg); + } + + /* We have an SGI or a PPI. They are Group0 at reset */ + sec_ppi_sgi_mask |= (1u << prop_desc->intr_num); + + /* Set the priority of this interrupt */ + gicd_set_ipriorityr(gicd_base, prop_desc->intr_num, + prop_desc->intr_pri); + } + + /* + * Invert the bitmask to create a mask for non-secure PPIs and SGIs. + * Program the GICD_IGROUPR0 with this bit mask. + */ + gicd_write_igroupr(gicd_base, 0, ~sec_ppi_sgi_mask); + + /* Enable the Group 0 SGIs and PPIs */ + gicd_write_isenabler(gicd_base, 0, sec_ppi_sgi_mask); +} diff --git a/drivers/arm/gic/v2/gicv2_main.c b/drivers/arm/gic/v2/gicv2_main.c index 59b663232..25296a63e 100644 --- a/drivers/arm/gic/v2/gicv2_main.c +++ b/drivers/arm/gic/v2/gicv2_main.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include "../common/gic_common_private.h" #include "gicv2_private.h" @@ -73,11 +74,21 @@ void gicv2_pcpu_distif_init(void) { assert(driver_data); assert(driver_data->gicd_base); - assert(driver_data->g0_interrupt_array); - gicv2_secure_ppi_sgi_setup(driver_data->gicd_base, - driver_data->g0_interrupt_num, - driver_data->g0_interrupt_array); +#if !ERROR_DEPRECATED + if (driver_data->interrupt_props != NULL) { +#endif + gicv2_secure_ppi_sgi_setup_props(driver_data->gicd_base, + driver_data->interrupt_props, + driver_data->interrupt_props_num); +#if !ERROR_DEPRECATED + } else { + assert(driver_data->g0_interrupt_array); + gicv2_secure_ppi_sgi_setup(driver_data->gicd_base, + driver_data->g0_interrupt_num, + driver_data->g0_interrupt_array); + } +#endif } /******************************************************************************* @@ -91,7 +102,6 @@ void gicv2_distif_init(void) assert(driver_data); assert(driver_data->gicd_base); - assert(driver_data->g0_interrupt_array); /* Disable the distributor before going further */ ctlr = gicd_read_ctlr(driver_data->gicd_base); @@ -101,10 +111,22 @@ void gicv2_distif_init(void) /* Set the default attribute of all SPIs */ gicv2_spis_configure_defaults(driver_data->gicd_base); - /* Configure the G0 SPIs */ - gicv2_secure_spis_configure(driver_data->gicd_base, - driver_data->g0_interrupt_num, - driver_data->g0_interrupt_array); +#if !ERROR_DEPRECATED + if (driver_data->interrupt_props != NULL) { +#endif + gicv2_secure_spis_configure_props(driver_data->gicd_base, + driver_data->interrupt_props, + driver_data->interrupt_props_num); +#if !ERROR_DEPRECATED + } else { + assert(driver_data->g0_interrupt_array); + + /* Configure the G0 SPIs */ + gicv2_secure_spis_configure(driver_data->gicd_base, + driver_data->g0_interrupt_num, + driver_data->g0_interrupt_array); + } +#endif /* Re-enable the secure SPIs now that they have been configured */ gicd_write_ctlr(driver_data->gicd_base, ctlr | CTLR_ENABLE_G0_BIT); @@ -120,19 +142,26 @@ void gicv2_driver_init(const gicv2_driver_data_t *plat_driver_data) assert(plat_driver_data->gicd_base); assert(plat_driver_data->gicc_base); - /* - * The platform should provide a list of atleast one type of - * interrupts - */ - assert(plat_driver_data->g0_interrupt_array); +#if !ERROR_DEPRECATED + if (plat_driver_data->interrupt_props == NULL) { + /* Interrupt properties array size must be 0 */ + assert(plat_driver_data->interrupt_props_num == 0); - /* - * If there are no interrupts of a particular type, then the number of - * interrupts of that type should be 0 and vice-versa. - */ - assert(plat_driver_data->g0_interrupt_array ? - plat_driver_data->g0_interrupt_num : - plat_driver_data->g0_interrupt_num == 0); + /* The platform should provide a list of secure interrupts */ + assert(plat_driver_data->g0_interrupt_array); + + /* + * If there are no interrupts of a particular type, then the + * number of interrupts of that type should be 0 and vice-versa. + */ + assert(plat_driver_data->g0_interrupt_array ? + plat_driver_data->g0_interrupt_num : + plat_driver_data->g0_interrupt_num == 0); + } +#else + assert(plat_driver_data->interrupt_props != NULL); + assert(plat_driver_data->interrupt_props_num > 0); +#endif /* Ensure that this is a GICv2 system */ gic_version = gicd_read_pidr2(plat_driver_data->gicd_base); diff --git a/drivers/arm/gic/v2/gicv2_private.h b/drivers/arm/gic/v2/gicv2_private.h index 70f059768..25600deba 100644 --- a/drivers/arm/gic/v2/gicv2_private.h +++ b/drivers/arm/gic/v2/gicv2_private.h @@ -15,12 +15,20 @@ * Private function prototypes ******************************************************************************/ void gicv2_spis_configure_defaults(uintptr_t gicd_base); +#if !ERROR_DEPRECATED void gicv2_secure_spis_configure(uintptr_t gicd_base, unsigned int num_ints, const unsigned int *sec_intr_list); void gicv2_secure_ppi_sgi_setup(uintptr_t gicd_base, unsigned int num_ints, const unsigned int *sec_intr_list); +#endif +void gicv2_secure_spis_configure_props(uintptr_t gicd_base, + const interrupt_prop_t *interrupt_props, + unsigned int interrupt_props_num); +void gicv2_secure_ppi_sgi_setup_props(uintptr_t gicd_base, + const interrupt_prop_t *interrupt_props, + unsigned int interrupt_props_num); unsigned int gicv2_get_cpuif_id(uintptr_t base); /******************************************************************************* diff --git a/drivers/arm/gic/v3/gicv3_helpers.c b/drivers/arm/gic/v3/gicv3_helpers.c index 33dbe2c8e..252269568 100644 --- a/drivers/arm/gic/v3/gicv3_helpers.c +++ b/drivers/arm/gic/v3/gicv3_helpers.c @@ -9,6 +9,7 @@ #include #include #include +#include #include "../common/gic_common_private.h" #include "gicv3_private.h" @@ -364,6 +365,7 @@ void gicv3_spis_configure_defaults(uintptr_t gicd_base) gicd_write_icfgr(gicd_base, index, 0); } +#if !ERROR_DEPRECATED /******************************************************************************* * Helper function to configure secure G0 and G1S SPIs. ******************************************************************************/ @@ -410,6 +412,63 @@ void gicv3_secure_spis_configure(uintptr_t gicd_base, } } +#endif + +/******************************************************************************* + * Helper function to configure properties of secure SPIs + ******************************************************************************/ +unsigned int gicv3_secure_spis_configure_props(uintptr_t gicd_base, + const interrupt_prop_t *interrupt_props, + unsigned int interrupt_props_num) +{ + unsigned int i; + const interrupt_prop_t *current_prop; + unsigned long long gic_affinity_val; + unsigned int ctlr_enable = 0; + + /* Make sure there's a valid property array */ + assert(interrupt_props != NULL); + assert(interrupt_props_num > 0); + + for (i = 0; i < interrupt_props_num; i++) { + current_prop = &interrupt_props[i]; + + if (current_prop->intr_num < MIN_SPI_ID) + continue; + + /* Configure this interrupt as a secure interrupt */ + gicd_clr_igroupr(gicd_base, current_prop->intr_num); + + /* Configure this interrupt as G0 or a G1S interrupt */ + assert((current_prop->intr_grp == INTR_GROUP0) || + (current_prop->intr_grp == INTR_GROUP1S)); + if (current_prop->intr_grp == INTR_GROUP1S) { + gicd_set_igrpmodr(gicd_base, current_prop->intr_num); + ctlr_enable |= CTLR_ENABLE_G1S_BIT; + } else { + gicd_clr_igrpmodr(gicd_base, current_prop->intr_num); + ctlr_enable |= CTLR_ENABLE_G0_BIT; + } + + /* Set interrupt configuration */ + gicd_set_icfgr(gicd_base, current_prop->intr_num, + current_prop->intr_cfg); + + /* Set the priority of this interrupt */ + gicd_set_ipriorityr(gicd_base, current_prop->intr_num, + current_prop->intr_pri); + + /* Target SPIs to the primary CPU */ + gic_affinity_val = gicd_irouter_val_from_mpidr(read_mpidr(), 0); + gicd_write_irouter(gicd_base, current_prop->intr_num, + gic_affinity_val); + + /* Enable this interrupt */ + gicd_set_isenabler(gicd_base, current_prop->intr_num); + } + + return ctlr_enable; +} /******************************************************************************* * Helper function to configure the default attributes of SPIs. @@ -439,6 +498,7 @@ void gicv3_ppi_sgi_configure_defaults(uintptr_t gicr_base) gicr_write_icfgr1(gicr_base, 0); } +#if !ERROR_DEPRECATED /******************************************************************************* * Helper function to configure secure G0 and G1S SPIs. ******************************************************************************/ @@ -476,3 +536,54 @@ void gicv3_secure_ppi_sgi_configure(uintptr_t gicr_base, } } } +#endif + +/******************************************************************************* + * Helper function to configure properties of secure G0 and G1S PPIs and SGIs. + ******************************************************************************/ +void gicv3_secure_ppi_sgi_configure_props(uintptr_t gicr_base, + const interrupt_prop_t *interrupt_props, + unsigned int interrupt_props_num) +{ + unsigned int i; + const interrupt_prop_t *current_prop; + + /* Make sure there's a valid property array */ + assert(interrupt_props != NULL); + assert(interrupt_props_num > 0); + + for (i = 0; i < interrupt_props_num; i++) { + current_prop = &interrupt_props[i]; + + if (current_prop->intr_num >= MIN_SPI_ID) + continue; + + /* Configure this interrupt as a secure interrupt */ + gicr_clr_igroupr0(gicr_base, current_prop->intr_num); + + /* Configure this interrupt as G0 or a G1S interrupt */ + assert((current_prop->intr_grp == INTR_GROUP0) || + (current_prop->intr_grp == INTR_GROUP1S)); + if (current_prop->intr_grp == INTR_GROUP1S) + gicr_set_igrpmodr0(gicr_base, current_prop->intr_num); + else + gicr_clr_igrpmodr0(gicr_base, current_prop->intr_num); + + /* Set the priority of this interrupt */ + gicr_set_ipriorityr(gicr_base, current_prop->intr_num, + current_prop->intr_pri); + + /* + * Set interrupt configuration for PPIs. Configuration for SGIs + * are ignored. + */ + if ((current_prop->intr_num >= MIN_PPI_ID) && + (current_prop->intr_num < MIN_SPI_ID)) { + gicr_set_icfgr1(gicr_base, current_prop->intr_num, + current_prop->intr_cfg); + } + + /* Enable this interrupt */ + gicr_set_isenabler0(gicr_base, current_prop->intr_num); + } +} diff --git a/drivers/arm/gic/v3/gicv3_main.c b/drivers/arm/gic/v3/gicv3_main.c index 0f50f6d76..8c4f50847 100644 --- a/drivers/arm/gic/v3/gicv3_main.c +++ b/drivers/arm/gic/v3/gicv3_main.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include "gicv3_private.h" @@ -66,23 +67,33 @@ void gicv3_driver_init(const gicv3_driver_data_t *plat_driver_data) assert(IS_IN_EL3()); - /* - * The platform should provide a list of at least one type of - * interrupts - */ - assert(plat_driver_data->g0_interrupt_array || - plat_driver_data->g1s_interrupt_array); +#if !ERROR_DEPRECATED + if (plat_driver_data->interrupt_props == NULL) { + /* Interrupt properties array size must be 0 */ + assert(plat_driver_data->interrupt_props_num == 0); - /* - * If there are no interrupts of a particular type, then the number of - * interrupts of that type should be 0 and vice-versa. - */ - assert(plat_driver_data->g0_interrupt_array ? - plat_driver_data->g0_interrupt_num : - plat_driver_data->g0_interrupt_num == 0); - assert(plat_driver_data->g1s_interrupt_array ? - plat_driver_data->g1s_interrupt_num : - plat_driver_data->g1s_interrupt_num == 0); + /* + * The platform should provide a list of at least one type of + * interrupt. + */ + assert(plat_driver_data->g0_interrupt_array || + plat_driver_data->g1s_interrupt_array); + + /* + * If there are no interrupts of a particular type, then the + * number of interrupts of that type should be 0 and vice-versa. + */ + assert(plat_driver_data->g0_interrupt_array ? + plat_driver_data->g0_interrupt_num : + plat_driver_data->g0_interrupt_num == 0); + assert(plat_driver_data->g1s_interrupt_array ? + plat_driver_data->g1s_interrupt_num : + plat_driver_data->g1s_interrupt_num == 0); + } +#else + assert(plat_driver_data->interrupt_props != NULL); + assert(plat_driver_data->interrupt_props_num > 0); +#endif /* Check for system register support */ #ifdef AARCH32 @@ -148,8 +159,6 @@ void gicv3_distif_init(void) assert(gicv3_driver_data); assert(gicv3_driver_data->gicd_base); - assert(gicv3_driver_data->g1s_interrupt_array || - gicv3_driver_data->g0_interrupt_array); assert(IS_IN_EL3()); @@ -171,23 +180,37 @@ void gicv3_distif_init(void) /* Set the default attribute of all SPIs */ gicv3_spis_configure_defaults(gicv3_driver_data->gicd_base); - /* Configure the G1S SPIs */ - if (gicv3_driver_data->g1s_interrupt_array) { - gicv3_secure_spis_configure(gicv3_driver_data->gicd_base, +#if !ERROR_DEPRECATED + if (gicv3_driver_data->interrupt_props != NULL) { +#endif + bitmap = gicv3_secure_spis_configure_props( + gicv3_driver_data->gicd_base, + gicv3_driver_data->interrupt_props, + gicv3_driver_data->interrupt_props_num); +#if !ERROR_DEPRECATED + } else { + assert(gicv3_driver_data->g1s_interrupt_array || + gicv3_driver_data->g0_interrupt_array); + + /* Configure the G1S SPIs */ + if (gicv3_driver_data->g1s_interrupt_array) { + gicv3_secure_spis_configure(gicv3_driver_data->gicd_base, gicv3_driver_data->g1s_interrupt_num, gicv3_driver_data->g1s_interrupt_array, INTR_GROUP1S); - bitmap |= CTLR_ENABLE_G1S_BIT; - } + bitmap |= CTLR_ENABLE_G1S_BIT; + } - /* Configure the G0 SPIs */ - if (gicv3_driver_data->g0_interrupt_array) { - gicv3_secure_spis_configure(gicv3_driver_data->gicd_base, + /* Configure the G0 SPIs */ + if (gicv3_driver_data->g0_interrupt_array) { + gicv3_secure_spis_configure(gicv3_driver_data->gicd_base, gicv3_driver_data->g0_interrupt_num, gicv3_driver_data->g0_interrupt_array, INTR_GROUP0); - bitmap |= CTLR_ENABLE_G0_BIT; + bitmap |= CTLR_ENABLE_G0_BIT; + } } +#endif /* Enable the secure SPIs now that they have been configured */ gicd_set_ctlr(gicv3_driver_data->gicd_base, bitmap, RWP_TRUE); @@ -207,8 +230,6 @@ void gicv3_rdistif_init(unsigned int proc_num) assert(gicv3_driver_data->rdistif_base_addrs); assert(gicv3_driver_data->gicd_base); assert(gicd_read_ctlr(gicv3_driver_data->gicd_base) & CTLR_ARE_S_BIT); - assert(gicv3_driver_data->g1s_interrupt_array || - gicv3_driver_data->g0_interrupt_array); assert(IS_IN_EL3()); @@ -220,21 +241,34 @@ void gicv3_rdistif_init(unsigned int proc_num) /* Set the default attribute of all SGIs and PPIs */ gicv3_ppi_sgi_configure_defaults(gicr_base); - /* Configure the G1S SGIs/PPIs */ - if (gicv3_driver_data->g1s_interrupt_array) { - gicv3_secure_ppi_sgi_configure(gicr_base, +#if !ERROR_DEPRECATED + if (gicv3_driver_data->interrupt_props != NULL) { +#endif + gicv3_secure_ppi_sgi_configure_props(gicr_base, + gicv3_driver_data->interrupt_props, + gicv3_driver_data->interrupt_props_num); +#if !ERROR_DEPRECATED + } else { + assert(gicv3_driver_data->g1s_interrupt_array || + gicv3_driver_data->g0_interrupt_array); + + /* Configure the G1S SGIs/PPIs */ + if (gicv3_driver_data->g1s_interrupt_array) { + gicv3_secure_ppi_sgi_configure(gicr_base, gicv3_driver_data->g1s_interrupt_num, gicv3_driver_data->g1s_interrupt_array, INTR_GROUP1S); - } + } - /* Configure the G0 SGIs/PPIs */ - if (gicv3_driver_data->g0_interrupt_array) { - gicv3_secure_ppi_sgi_configure(gicr_base, + /* Configure the G0 SGIs/PPIs */ + if (gicv3_driver_data->g0_interrupt_array) { + gicv3_secure_ppi_sgi_configure(gicr_base, gicv3_driver_data->g0_interrupt_num, gicv3_driver_data->g0_interrupt_array, INTR_GROUP0); + } } +#endif } /******************************************************************************* diff --git a/drivers/arm/gic/v3/gicv3_private.h b/drivers/arm/gic/v3/gicv3_private.h index 43529c5a5..a5093d0c7 100644 --- a/drivers/arm/gic/v3/gicv3_private.h +++ b/drivers/arm/gic/v3/gicv3_private.h @@ -85,6 +85,7 @@ void gicr_set_ipriorityr(uintptr_t base, unsigned int id, unsigned int pri); ******************************************************************************/ void gicv3_spis_configure_defaults(uintptr_t gicd_base); void gicv3_ppi_sgi_configure_defaults(uintptr_t gicr_base); +#if !ERROR_DEPRECATED void gicv3_secure_spis_configure(uintptr_t gicd_base, unsigned int num_ints, const unsigned int *sec_intr_list, @@ -93,6 +94,13 @@ void gicv3_secure_ppi_sgi_configure(uintptr_t gicr_base, unsigned int num_ints, const unsigned int *sec_intr_list, unsigned int int_grp); +#endif +void gicv3_secure_ppi_sgi_configure_props(uintptr_t gicr_base, + const interrupt_prop_t *interrupt_props, + unsigned int interrupt_props_num); +unsigned int gicv3_secure_spis_configure_props(uintptr_t gicd_base, + const interrupt_prop_t *interrupt_props, + unsigned int interrupt_props_num); void gicv3_rdistif_base_addrs_probe(uintptr_t *rdistif_base_addrs, unsigned int rdistif_num, uintptr_t gicr_base, diff --git a/include/bl31/interrupt_mgmt.h b/include/bl31/interrupt_mgmt.h index 4f5a6013a..cccad3add 100644 --- a/include/bl31/interrupt_mgmt.h +++ b/include/bl31/interrupt_mgmt.h @@ -98,6 +98,8 @@ #ifndef __ASSEMBLY__ +#include + /* Prototype for defining a handler for an interrupt type */ typedef uint64_t (*interrupt_type_handler_t)(uint32_t id, uint32_t flags, diff --git a/include/common/interrupt_props.h b/include/common/interrupt_props.h new file mode 100644 index 000000000..9786b40c8 --- /dev/null +++ b/include/common/interrupt_props.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef __INTERRUPT_PROPS_H__ +#define __INTERRUPT_PROPS_H__ + +#ifndef __ASSEMBLY__ + +/* Create an interrupt property descriptor from various interrupt properties */ +#define INTR_PROP_DESC(num, pri, grp, cfg) \ + { \ + .intr_num = num, \ + .intr_pri = pri, \ + .intr_grp = grp, \ + .intr_cfg = cfg, \ + } + +typedef struct interrupt_prop { + unsigned int intr_num:10; + unsigned int intr_pri:8; + unsigned int intr_grp:2; + unsigned int intr_cfg:2; +} interrupt_prop_t; + +#endif /* __ASSEMBLY__ */ +#endif /* __INTERRUPT_PROPS_H__ */ diff --git a/include/drivers/arm/gic_common.h b/include/drivers/arm/gic_common.h index f7fc8ceb6..9e126a854 100644 --- a/include/drivers/arm/gic_common.h +++ b/include/drivers/arm/gic_common.h @@ -29,6 +29,10 @@ /* Constant to indicate a spurious interrupt in all GIC versions */ #define GIC_SPURIOUS_INTERRUPT 1023 +/* Interrupt configurations */ +#define GIC_INTR_CFG_LEVEL 0 +#define GIC_INTR_CFG_EDGE 1 + /* Constants to categorise priorities */ #define GIC_HIGHEST_SEC_PRIORITY 0 #define GIC_LOWEST_SEC_PRIORITY 127 diff --git a/include/drivers/arm/gicv2.h b/include/drivers/arm/gicv2.h index 9b8510aab..6e8322e1b 100644 --- a/include/drivers/arm/gicv2.h +++ b/include/drivers/arm/gicv2.h @@ -116,6 +116,7 @@ #ifndef __ASSEMBLY__ +#include #include /******************************************************************************* @@ -130,23 +131,37 @@ * The 'gicc_base' field contains the base address of the CPU Interface * programmer's view. * - * The 'g0_interrupt_array' field is a pointer to an array in which each - * entry corresponds to an ID of a Group 0 interrupt. + * The 'g0_interrupt_array' field is a pointer to an array in which each entry + * corresponds to an ID of a Group 0 interrupt. This field is ignored when + * 'interrupt_props' field is used. This field is deprecated. * * The 'g0_interrupt_num' field contains the number of entries in the - * 'g0_interrupt_array'. + * 'g0_interrupt_array'. This field is ignored when 'interrupt_props' field is + * used. This field is deprecated. * * The 'target_masks' is a pointer to an array containing 'target_masks_num' * elements. The GIC driver will populate the array with per-PE target mask to * use to when targeting interrupts. + * + * The 'interrupt_props' field is a pointer to an array that enumerates secure + * interrupts and their properties. If this field is not NULL, both + * 'g0_interrupt_array' and 'g1s_interrupt_array' fields are ignored. + * + * The 'interrupt_props_num' field contains the number of entries in the + * 'interrupt_props' array. If this field is non-zero, 'g0_interrupt_num' is + * ignored. ******************************************************************************/ typedef struct gicv2_driver_data { uintptr_t gicd_base; uintptr_t gicc_base; +#if !ERROR_DEPRECATED unsigned int g0_interrupt_num; const unsigned int *g0_interrupt_array; +#endif unsigned int *target_masks; unsigned int target_masks_num; + const interrupt_prop_t *interrupt_props; + unsigned int interrupt_props_num; } gicv2_driver_data_t; /******************************************************************************* diff --git a/include/drivers/arm/gicv3.h b/include/drivers/arm/gicv3.h index 95b6e3bb9..b2e4d4c5e 100644 --- a/include/drivers/arm/gicv3.h +++ b/include/drivers/arm/gicv3.h @@ -7,8 +7,6 @@ #ifndef __GICV3_H__ #define __GICV3_H__ -#include "utils_def.h" - /******************************************************************************* * GICv3 miscellaneous definitions ******************************************************************************/ @@ -212,6 +210,7 @@ #ifndef __ASSEMBLY__ #include +#include #include #include #include @@ -251,53 +250,70 @@ * GICv3 IP. It is used by the platform port to specify these attributes in order * to initialise the GICV3 driver. The attributes are described below. * - * 1. The 'gicd_base' field contains the base address of the Distributor - * interface programmer's view. + * The 'gicd_base' field contains the base address of the Distributor interface + * programmer's view. * - * 2. The 'gicr_base' field contains the base address of the Re-distributor - * interface programmer's view. + * The 'gicr_base' field contains the base address of the Re-distributor + * interface programmer's view. * - * 3. The 'g0_interrupt_array' field is a ponter to an array in which each - * entry corresponds to an ID of a Group 0 interrupt. + * The 'g0_interrupt_array' field is a pointer to an array in which each entry + * corresponds to an ID of a Group 0 interrupt. This field is ignored when + * 'interrupt_props' field is used. This field is deprecated. * - * 4. The 'g0_interrupt_num' field contains the number of entries in the - * 'g0_interrupt_array'. + * The 'g0_interrupt_num' field contains the number of entries in the + * 'g0_interrupt_array'. This field is ignored when 'interrupt_props' field is + * used. This field is deprecated. * - * 5. The 'g1s_interrupt_array' field is a ponter to an array in which each - * entry corresponds to an ID of a Group 1 interrupt. + * The 'g1s_interrupt_array' field is a pointer to an array in which each entry + * corresponds to an ID of a Group 1 interrupt. This field is ignored when + * 'interrupt_props' field is used. This field is deprecated. * - * 6. The 'g1s_interrupt_num' field contains the number of entries in the - * 'g1s_interrupt_array'. + * The 'g1s_interrupt_num' field contains the number of entries in the + * 'g1s_interrupt_array'. This field must be 0 if 'interrupt_props' field is + * used. This field is ignored when 'interrupt_props' field is used. This field + * is deprecated. * - * 7. The 'rdistif_num' field contains the number of Redistributor interfaces - * the GIC implements. This is equal to the number of CPUs or CPU interfaces - * instantiated in the GIC. + * The 'interrupt_props' field is a pointer to an array that enumerates secure + * interrupts and their properties. If this field is not NULL, both + * 'g0_interrupt_array' and 'g1s_interrupt_array' fields are ignored. * - * 8. The 'rdistif_base_addrs' field is a pointer to an array that has an entry - * for storing the base address of the Redistributor interface frame of each - * CPU in the system. The size of the array = 'rdistif_num'. The base - * addresses are detected during driver initialisation. + * The 'interrupt_props_num' field contains the number of entries in the + * 'interrupt_props' array. If this field is non-zero, both 'g0_interrupt_num' + * and 'g1s_interrupt_num' are ignored. * - * 9. The 'mpidr_to_core_pos' field is a pointer to a hash function which the - * driver will use to convert an MPIDR value to a linear core index. This - * index will be used for accessing the 'rdistif_base_addrs' array. This is - * an optional field. A GICv3 implementation maps each MPIDR to a linear core - * index as well. This mapping can be found by reading the "Affinity Value" - * and "Processor Number" fields in the GICR_TYPER. It is IMP. DEF. if the - * "Processor Numbers" are suitable to index into an array to access core - * specific information. If this not the case, the platform port must provide - * a hash function. Otherwise, the "Processor Number" field will be used to - * access the array elements. + * The 'rdistif_num' field contains the number of Redistributor interfaces the + * GIC implements. This is equal to the number of CPUs or CPU interfaces + * instantiated in the GIC. + * + * The 'rdistif_base_addrs' field is a pointer to an array that has an entry for + * storing the base address of the Redistributor interface frame of each CPU in + * the system. The size of the array = 'rdistif_num'. The base addresses are + * detected during driver initialisation. + * + * The 'mpidr_to_core_pos' field is a pointer to a hash function which the + * driver will use to convert an MPIDR value to a linear core index. This index + * will be used for accessing the 'rdistif_base_addrs' array. This is an + * optional field. A GICv3 implementation maps each MPIDR to a linear core index + * as well. This mapping can be found by reading the "Affinity Value" and + * "Processor Number" fields in the GICR_TYPER. It is IMP. DEF. if the + * "Processor Numbers" are suitable to index into an array to access core + * specific information. If this not the case, the platform port must provide a + * hash function. Otherwise, the "Processor Number" field will be used to access + * the array elements. ******************************************************************************/ typedef unsigned int (*mpidr_hash_fn)(u_register_t mpidr); typedef struct gicv3_driver_data { uintptr_t gicd_base; uintptr_t gicr_base; +#if !ERROR_DEPRECATED unsigned int g0_interrupt_num; unsigned int g1s_interrupt_num; const unsigned int *g0_interrupt_array; const unsigned int *g1s_interrupt_array; +#endif + const interrupt_prop_t *interrupt_props; + unsigned int interrupt_props_num; unsigned int rdistif_num; uintptr_t *rdistif_base_addrs; mpidr_hash_fn mpidr_to_core_pos;