From 03ffb6bdefca8f1aaf7ece10215f70b5ba51cae9 Mon Sep 17 00:00:00 2001 From: Soby Mathew Date: Thu, 3 Dec 2015 14:12:54 +0000 Subject: [PATCH 1/7] Rename GICv3 interrupt group macros This patch renames the GICv3 interrupt group macros from INT_TYPE_G0, INT_TYPE_G1S and INT_TYPE_G1NS to INTR_GROUP0, INTR_GROUP1S and INTR_GROUP1NS respectively. Change-Id: I40c66f589ce6234fa42205adcd91f7d6ad8f33d4 --- drivers/arm/gic/v3/gicv3_helpers.c | 8 ++++---- drivers/arm/gic/v3/gicv3_main.c | 22 +++++++++++----------- include/drivers/arm/gicv3.h | 6 +++--- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/drivers/arm/gic/v3/gicv3_helpers.c b/drivers/arm/gic/v3/gicv3_helpers.c index 6e8251d530..2fb98cbbd8 100644 --- a/drivers/arm/gic/v3/gicv3_helpers.c +++ b/drivers/arm/gic/v3/gicv3_helpers.c @@ -312,7 +312,7 @@ void gicv3_secure_spis_configure(uintptr_t gicd_base, unsigned int index, irq_num; uint64_t gic_affinity_val; - assert((int_grp == INT_TYPE_G1S) || (int_grp == INT_TYPE_G0)); + assert((int_grp == INTR_GROUP1S) || (int_grp == INTR_GROUP0)); /* If `num_ints` is not 0, ensure that `sec_intr_list` is not NULL */ assert(num_ints ? (uintptr_t)sec_intr_list : 1); @@ -324,7 +324,7 @@ void gicv3_secure_spis_configure(uintptr_t gicd_base, gicd_clr_igroupr(gicd_base, irq_num); /* Configure this interrupt as G0 or a G1S interrupt */ - if (int_grp == INT_TYPE_G1S) + if (int_grp == INTR_GROUP1S) gicd_set_igrpmodr(gicd_base, irq_num); else gicd_clr_igrpmodr(gicd_base, irq_num); @@ -386,7 +386,7 @@ void gicv3_secure_ppi_sgi_configure(uintptr_t gicr_base, { unsigned int index, irq_num; - assert((int_grp == INT_TYPE_G1S) || (int_grp == INT_TYPE_G0)); + assert((int_grp == INTR_GROUP1S) || (int_grp == INTR_GROUP0)); /* If `num_ints` is not 0, ensure that `sec_intr_list` is not NULL */ assert(num_ints ? (uintptr_t)sec_intr_list : 1); @@ -398,7 +398,7 @@ void gicv3_secure_ppi_sgi_configure(uintptr_t gicr_base, gicr_clr_igroupr0(gicr_base, irq_num); /* Configure this interrupt as G0 or a G1S interrupt */ - if (int_grp == INT_TYPE_G1S) + if (int_grp == INTR_GROUP1S) gicr_set_igrpmodr0(gicr_base, irq_num); else gicr_clr_igrpmodr0(gicr_base, irq_num); diff --git a/drivers/arm/gic/v3/gicv3_main.c b/drivers/arm/gic/v3/gicv3_main.c index 06311e3a97..d5cd0ed906 100644 --- a/drivers/arm/gic/v3/gicv3_main.c +++ b/drivers/arm/gic/v3/gicv3_main.c @@ -144,13 +144,13 @@ void gicv3_distif_init(void) gicv3_secure_spis_configure(driver_data->gicd_base, driver_data->g1s_interrupt_num, driver_data->g1s_interrupt_array, - INT_TYPE_G1S); + INTR_GROUP1S); /* Configure the G0 SPIs */ gicv3_secure_spis_configure(driver_data->gicd_base, driver_data->g0_interrupt_num, driver_data->g0_interrupt_array, - INT_TYPE_G0); + INTR_GROUP0); /* Enable the secure SPIs now that they have been configured */ gicd_set_ctlr(driver_data->gicd_base, @@ -186,13 +186,13 @@ void gicv3_rdistif_init(unsigned int proc_num) gicv3_secure_ppi_sgi_configure(gicr_base, driver_data->g1s_interrupt_num, driver_data->g1s_interrupt_array, - INT_TYPE_G1S); + INTR_GROUP1S); /* Configure the G0 SGIs/PPIs */ gicv3_secure_ppi_sgi_configure(gicr_base, driver_data->g0_interrupt_num, driver_data->g0_interrupt_array, - INT_TYPE_G0); + INTR_GROUP0); } /******************************************************************************* @@ -332,9 +332,9 @@ unsigned int gicv3_get_pending_interrupt_type(void) * this interrupt has been configured under by the interrupt controller i.e. * group0 or group1 Secure / Non Secure. The return value can be one of the * following : - * INT_TYPE_G0 : The interrupt type is a Secure Group 0 interrupt - * INT_TYPE_G1S : The interrupt type is a Secure Group 1 secure interrupt - * INT_TYPE_G1NS: The interrupt type is a Secure Group 1 non secure + * INTR_GROUP0 : The interrupt type is a Secure Group 0 interrupt + * INTR_GROUP1S : The interrupt type is a Secure Group 1 secure interrupt + * INTR_GROUP1NS: The interrupt type is a Secure Group 1 non secure * interrupt. ******************************************************************************/ unsigned int gicv3_get_interrupt_type(unsigned int id, @@ -352,7 +352,7 @@ unsigned int gicv3_get_interrupt_type(unsigned int id, /* All LPI interrupts are Group 1 non secure */ if (id >= MIN_LPI_ID) - return INT_TYPE_G1NS; + return INTR_GROUP1NS; if (id < MIN_SPI_ID) { assert(driver_data->rdistif_base_addrs); @@ -370,12 +370,12 @@ unsigned int gicv3_get_interrupt_type(unsigned int id, * interrupt */ if (igroup) - return INT_TYPE_G1NS; + return INTR_GROUP1NS; /* If the GRPMOD bit is set, then it is a Group 1 Secure interrupt */ if (grpmodr) - return INT_TYPE_G1S; + return INTR_GROUP1S; /* Else it is a Group 0 Secure interrupt */ - return INT_TYPE_G0; + return INTR_GROUP0; } diff --git a/include/drivers/arm/gicv3.h b/include/drivers/arm/gicv3.h index e874f5cd31..ae6fd91752 100644 --- a/include/drivers/arm/gicv3.h +++ b/include/drivers/arm/gicv3.h @@ -35,9 +35,9 @@ * GICv3 miscellaneous definitions ******************************************************************************/ /* Interrupt group definitions */ -#define INT_TYPE_G1S 0 -#define INT_TYPE_G0 1 -#define INT_TYPE_G1NS 2 +#define INTR_GROUP1S 0 +#define INTR_GROUP0 1 +#define INTR_GROUP1NS 2 /* Interrupt IDs reported by the HPPIR and IAR registers */ #define PENDING_G1S_INTID 1020 From 404dba53ef9be643f80babdd4ab81501bdbaba16 Mon Sep 17 00:00:00 2001 From: Soby Mathew Date: Tue, 22 Sep 2015 12:01:18 +0100 Subject: [PATCH 2/7] Unify interrupt return paths from TSP into the TSPD The TSP is expected to pass control back to EL3 if it gets preempted due to an interrupt while handling a Standard SMC in the following scenarios: 1. An FIQ preempts Standard SMC execution and that FIQ is not a TSP Secure timer interrupt or is preempted by a higher priority interrupt by the time the TSP acknowledges it. In this case, the TSP issues an SMC with the ID as `TSP_EL3_FIQ`. Currently this case is never expected to happen as only the TSP Secure Timer is expected to generate FIQ. 2. An IRQ preempts Standard SMC execution and in this case the TSP issues an SMC with the ID as `TSP_PREEMPTED`. In both the cases, the TSPD hands control back to the normal world and returns returns an error code to the normal world to indicate that the standard SMC it had issued has been preempted but not completed. This patch unifies the handling of these two cases in the TSPD and ensures that the TSP only uses TSP_PREEMPTED instead of separate SMC IDs. Also instead of 2 separate error codes, SMC_PREEMPTED and TSP_EL3_FIQ, only SMC_PREEMPTED is returned as error code back to the normal world. Background information: On a GICv3 system, when the secure world has affinity routing enabled, in 2. an FIQ will preempt TSP execution instead of an IRQ. The FIQ could be a result of a Group 0 or a Group 1 NS interrupt. In both case, the TSPD passes control back to the normal world upon receipt of the TSP_PREEMPTED SMC. A Group 0 interrupt will immediately preempt execution to EL3 where it will be handled. This allows for unified interrupt handling in TSP for both GICv3 and GICv2 systems. Change-Id: I9895344db74b188021e3f6a694701ad272fb40d4 --- bl32/tsp/aarch64/tsp_exceptions.S | 2 +- bl32/tsp/tsp_interrupt.c | 45 +++++++++++++++++------------ bl32/tsp/tsp_private.h | 3 +- include/bl32/tsp/tsp.h | 1 - services/spd/tspd/tspd_main.c | 47 +++++++++++-------------------- 5 files changed, 46 insertions(+), 52 deletions(-) diff --git a/bl32/tsp/aarch64/tsp_exceptions.S b/bl32/tsp/aarch64/tsp_exceptions.S index 4c0d4361e7..272d94b7b7 100644 --- a/bl32/tsp/aarch64/tsp_exceptions.S +++ b/bl32/tsp/aarch64/tsp_exceptions.S @@ -125,7 +125,7 @@ irq_sp_elx: save_caller_regs_and_lr /* We just update some statistics in the handler */ - bl tsp_irq_received + bl tsp_handle_preemption /* Hand over control to the normal world to handle the IRQ */ smc #0 /* The resume std smc starts from here */ diff --git a/bl32/tsp/tsp_interrupt.c b/bl32/tsp/tsp_interrupt.c index 139642d094..d5379cdc6a 100644 --- a/bl32/tsp/tsp_interrupt.c +++ b/bl32/tsp/tsp_interrupt.c @@ -67,6 +67,25 @@ void tsp_update_sync_fiq_stats(uint32_t type, uint64_t elr_el3) #endif } +/****************************************************************************** + * This function is invoked when a non S-EL1 interrupt is received and causes + * the preemption of TSP. This function returns TSP_PREEMPTED and results + * in the control being handed over to EL3 for handling the interrupt. + *****************************************************************************/ +int32_t tsp_handle_preemption(void) +{ + uint32_t linear_id = plat_my_core_pos(); + + tsp_stats[linear_id].preempt_intr_count++; +#if LOG_LEVEL >= LOG_LEVEL_VERBOSE + spin_lock(&console_lock); + VERBOSE("TSP: cpu 0x%lx: %d preempt interrupt requests\n", + read_mpidr(), tsp_stats[linear_id].preempt_intr_count); + spin_unlock(&console_lock); +#endif + return TSP_PREEMPTED; +} + /******************************************************************************* * TSP FIQ handler called as a part of both synchronous and asynchronous * handling of FIQ interrupts. It returns 0 upon successfully handling a S-EL1 @@ -82,16 +101,21 @@ int32_t tsp_fiq_handler(void) * Get the highest priority pending interrupt id and see if it is the * secure physical generic timer interrupt in which case, handle it. * Otherwise throw this interrupt at the EL3 firmware. + * + * There is a small time window between reading the highest priority + * pending interrupt and acknowledging it during which another + * interrupt of higher priority could become the highest pending + * interrupt. This is not expected to happen currently for TSP. */ id = plat_ic_get_pending_interrupt_id(); /* TSP can only handle the secure physical timer interrupt */ if (id != TSP_IRQ_SEC_PHY_TIMER) - return TSP_EL3_FIQ; + return tsp_handle_preemption(); /* - * Handle the interrupt. Also sanity check if it has been preempted by - * another secure interrupt through an assertion. + * Acknowledge and handle the secure timer interrupt. Also sanity check + * if it has been preempted by another interrupt through an assertion. */ id = plat_ic_acknowledge_interrupt(); assert(id == TSP_IRQ_SEC_PHY_TIMER); @@ -110,18 +134,3 @@ int32_t tsp_fiq_handler(void) #endif return 0; } - -int32_t tsp_irq_received(void) -{ - uint32_t linear_id = plat_my_core_pos(); - - tsp_stats[linear_id].irq_count++; -#if LOG_LEVEL >= LOG_LEVEL_VERBOSE - spin_lock(&console_lock); - VERBOSE("TSP: cpu 0x%lx received irq\n", read_mpidr()); - VERBOSE("TSP: cpu 0x%lx: %d irq requests\n", - read_mpidr(), tsp_stats[linear_id].irq_count); - spin_unlock(&console_lock); -#endif - return TSP_PREEMPTED; -} diff --git a/bl32/tsp/tsp_private.h b/bl32/tsp/tsp_private.h index 39fb5f663a..346351c0c4 100644 --- a/bl32/tsp/tsp_private.h +++ b/bl32/tsp/tsp_private.h @@ -55,9 +55,10 @@ typedef struct work_statistics { uint32_t fiq_count; /* Number of FIQs on this cpu */ - uint32_t irq_count; /* Number of IRQs on this cpu */ uint32_t sync_fiq_count; /* Number of sync. fiqs on this cpu */ uint32_t sync_fiq_ret_count; /* Number of fiq returns on this cpu */ + /* Number of non s-el1 interrupts on this cpu which preempted TSP */ + uint32_t preempt_intr_count; uint32_t smc_count; /* Number of returns on this cpu */ uint32_t eret_count; /* Number of entries on this cpu */ uint32_t cpu_on_count; /* Number of cpu on requests */ diff --git a/include/bl32/tsp/tsp.h b/include/bl32/tsp/tsp.h index c6578b788c..2286b3f19b 100644 --- a/include/bl32/tsp/tsp.h +++ b/include/bl32/tsp/tsp.h @@ -50,7 +50,6 @@ * the TSPD after handling the interrupt else execution can remain in the TSP. */ #define TSP_HANDLED_S_EL1_FIQ 0xf2000006 -#define TSP_EL3_FIQ 0xf2000007 /* SMC function ID that TSP uses to request service from secure monitor */ #define TSP_GET_ARGS 0xf2001000 diff --git a/services/spd/tspd/tspd_main.c b/services/spd/tspd/tspd_main.c index 6223160149..0d6e0d227e 100644 --- a/services/spd/tspd/tspd_main.c +++ b/services/spd/tspd/tspd_main.c @@ -72,9 +72,16 @@ DEFINE_SVC_UUID(tsp_uuid, int32_t tspd_init(void); +/* + * This helper function handles Secure EL1 preemption. The preemption could be + * due Non Secure interrupts or EL3 interrupts. In both the cases we context + * switch to the normal world and in case of EL3 interrupts, it will again be + * routed to EL3 which will get handled at the exception vectors. + */ uint64_t tspd_handle_sp_preemption(void *handle) { cpu_context_t *ns_cpu_context; + assert(handle == cm_get_context(SECURE)); cm_el1_sysregs_context_save(SECURE); /* Get a reference to the non-secure context */ @@ -88,8 +95,16 @@ uint64_t tspd_handle_sp_preemption(void *handle) cm_el1_sysregs_context_restore(NON_SECURE); cm_set_next_eret_context(NON_SECURE); + /* + * We need to restore non secure context according to + * the SEL1 context which got preempted and currently + * TSP can only be preempted when a STD SMC is ongoing. + * Return SMC_PREEMPTED in x0 and restore non secure + * context. + */ SMC_RET1(ns_cpu_context, SMC_PREEMPTED); } + /******************************************************************************* * This function is the handler registered for S-EL1 interrupts by the TSPD. It * validates the interrupt and upon success arranges entry into the TSP at @@ -356,35 +371,6 @@ uint64_t tspd_smc_handler(uint32_t smc_fid, SMC_RET0((uint64_t) ns_cpu_context); - - /* - * This function ID is used only by the TSP to indicate that it was - * interrupted due to a EL3 FIQ interrupt. Execution should resume - * in the normal world. - */ - case TSP_EL3_FIQ: - if (ns) - SMC_RET1(handle, SMC_UNK); - - assert(handle == cm_get_context(SECURE)); - - /* Assert that standard SMC execution has been preempted */ - assert(get_std_smc_active_flag(tsp_ctx->state)); - - /* Save the secure system register state */ - cm_el1_sysregs_context_save(SECURE); - - /* Get a reference to the non-secure context */ - ns_cpu_context = cm_get_context(NON_SECURE); - assert(ns_cpu_context); - - /* Restore non-secure state */ - cm_el1_sysregs_context_restore(NON_SECURE); - cm_set_next_eret_context(NON_SECURE); - - SMC_RET1(ns_cpu_context, TSP_EL3_FIQ); - - /* * This function ID is used only by the SP to indicate it has * finished initialising itself after a cold boot @@ -438,8 +424,7 @@ uint64_t tspd_smc_handler(uint32_t smc_fid, panic(); /* - * Disable the interrupt NS locally since it will be enabled globally - * within cm_init_my_context. + * Disable the NS interrupt locally. */ disable_intr_rm_local(INTR_TYPE_NS, SECURE); #endif From 02446137a4e2a504706fb1f4059467643e2930a5 Mon Sep 17 00:00:00 2001 From: Soby Mathew Date: Thu, 3 Sep 2015 18:29:38 +0100 Subject: [PATCH 3/7] Enable use of FIQs and IRQs as TSP interrupts On a GICv2 system, interrupts that should be handled in the secure world are typically signalled as FIQs. On a GICv3 system, these interrupts are signalled as IRQs instead. The mechanism for handling both types of interrupts is the same in both cases. This patch enables the TSP to run on a GICv3 system by: 1. adding support for handling IRQs in the exception handling code. 2. removing use of "fiq" in the names of data structures, macros and functions. The build option TSPD_ROUTE_IRQ_TO_EL3 is deprecated and is replaced with a new build flag TSP_NS_INTR_ASYNC_PREEMPT. For compatibility reasons, if the former build flag is defined, it will be used to define the value for the new build flag. The documentation is also updated accordingly. Change-Id: I1807d371f41c3656322dd259340a57649833065e --- bl32/tsp/aarch64/tsp_entrypoint.S | 49 +++++++++++++------------- bl32/tsp/aarch64/tsp_exceptions.S | 50 +++++++++++++-------------- bl32/tsp/tsp_interrupt.c | 55 +++++++++++++++--------------- bl32/tsp/tsp_private.h | 13 ++++--- docs/interrupt-framework-design.md | 20 +++++------ docs/user-guide.md | 11 +++--- include/bl32/tsp/tsp.h | 13 +++---- services/spd/tspd/tspd.mk | 16 +++++++-- services/spd/tspd/tspd_main.c | 50 +++++++++++++-------------- services/spd/tspd/tspd_pm.c | 2 +- services/spd/tspd/tspd_private.h | 10 +++--- 11 files changed, 153 insertions(+), 136 deletions(-) diff --git a/bl32/tsp/aarch64/tsp_entrypoint.S b/bl32/tsp/aarch64/tsp_entrypoint.S index 9732ff2cdf..d183dff9fa 100644 --- a/bl32/tsp/aarch64/tsp_entrypoint.S +++ b/bl32/tsp/aarch64/tsp_entrypoint.S @@ -177,7 +177,7 @@ func tsp_vector_table b tsp_cpu_off_entry b tsp_cpu_resume_entry b tsp_cpu_suspend_entry - b tsp_fiq_entry + b tsp_sel1_intr_entry b tsp_system_off_entry b tsp_system_reset_entry endfunc tsp_vector_table @@ -325,13 +325,13 @@ func tsp_cpu_suspend_entry restore_args_call_smc endfunc tsp_cpu_suspend_entry - /*--------------------------------------------- + /*------------------------------------------------- * This entrypoint is used by the TSPD to pass - * control for handling a pending S-EL1 FIQ. + * control for handling a pending S-EL1 Interrupt. * 'x0' contains a magic number which indicates * this. TSPD expects control to be handed back - * at the end of FIQ processing. This is done - * through an SMC. The handover agreement is: + * at the end of interrupt processing. This is + * done through an SMC. The handover agreement is: * * 1. PSTATE.DAIF are set upon entry. 'x1' has * the ELR_EL3 from the non-secure state. @@ -343,40 +343,41 @@ endfunc tsp_cpu_suspend_entry * 4. TSP can use 'x0-x18' to enable its C * runtime. * 5. TSP returns to TSPD using an SMC with - * 'x0' = TSP_HANDLED_S_EL1_FIQ - * --------------------------------------------- + * 'x0' = TSP_HANDLED_S_EL1_INTR + * ------------------------------------------------ */ -func tsp_fiq_entry +func tsp_sel1_intr_entry #if DEBUG - mov x2, #(TSP_HANDLE_FIQ_AND_RETURN & ~0xffff) - movk x2, #(TSP_HANDLE_FIQ_AND_RETURN & 0xffff) + mov x2, #(TSP_HANDLE_SEL1_INTR_AND_RETURN & ~0xffff) + movk x2, #(TSP_HANDLE_SEL1_INTR_AND_RETURN & 0xffff) cmp x0, x2 - b.ne tsp_fiq_entry_panic + b.ne tsp_sel1_int_entry_panic #endif - /*--------------------------------------------- + /*------------------------------------------------- * Save any previous context needed to perform * an exception return from S-EL1 e.g. context - * from a previous IRQ. Update statistics and - * handle the FIQ before returning to the TSPD. + * from a previous Non secure Interrupt. + * Update statistics and handle the S-EL1 + * interrupt before returning to the TSPD. * IRQ/FIQs are not enabled since that will * complicate the implementation. Execution * will be transferred back to the normal world * in any case. A non-zero return value from the - * fiq handler is an error. - * --------------------------------------------- + * interrupt handler is an error. + * ------------------------------------------------ */ save_eret_context x2 x3 - bl tsp_update_sync_fiq_stats - bl tsp_fiq_handler - cbnz x0, tsp_fiq_entry_panic + bl tsp_update_sync_sel1_intr_stats + bl tsp_common_int_handler + cbnz x0, tsp_sel1_int_entry_panic restore_eret_context x2 x3 - mov x0, #(TSP_HANDLED_S_EL1_FIQ & ~0xffff) - movk x0, #(TSP_HANDLED_S_EL1_FIQ & 0xffff) + mov x0, #(TSP_HANDLED_S_EL1_INTR & ~0xffff) + movk x0, #(TSP_HANDLED_S_EL1_INTR & 0xffff) smc #0 -tsp_fiq_entry_panic: - b tsp_fiq_entry_panic -endfunc tsp_fiq_entry +tsp_sel1_int_entry_panic: + b tsp_sel1_int_entry_panic +endfunc tsp_sel1_intr_entry /*--------------------------------------------- * This entrypoint is used by the TSPD when this diff --git a/bl32/tsp/aarch64/tsp_exceptions.S b/bl32/tsp/aarch64/tsp_exceptions.S index 272d94b7b7..d5e089f6e8 100644 --- a/bl32/tsp/aarch64/tsp_exceptions.S +++ b/bl32/tsp/aarch64/tsp_exceptions.S @@ -70,6 +70,28 @@ add sp, sp, SCRATCH_REG_SIZE .endm + /* ---------------------------------------------------- + * Common TSP interrupt handling routine + * ---------------------------------------------------- + */ + .macro handle_tsp_interrupt label + /* Enable the SError interrupt */ + msr daifclr, #DAIF_ABT_BIT + + save_caller_regs_and_lr + bl tsp_common_int_handler + cbz x0, interrupt_exit_\label + + /* + * This interrupt was not targetted to S-EL1 so send it to + * the monitor and wait for execution to resume. + */ + smc #0 +interrupt_exit_\label: + restore_caller_regs_and_lr + eret + .endm + .globl tsp_exceptions /* ----------------------------------------------------- @@ -120,36 +142,12 @@ sync_exception_sp_elx: .align 7 irq_sp_elx: - /* Enable the SError interrupt */ - msr daifclr, #DAIF_ABT_BIT - - save_caller_regs_and_lr - /* We just update some statistics in the handler */ - bl tsp_handle_preemption - /* Hand over control to the normal world to handle the IRQ */ - smc #0 - /* The resume std smc starts from here */ - restore_caller_regs_and_lr - eret + handle_tsp_interrupt irq_sp_elx check_vector_size irq_sp_elx .align 7 fiq_sp_elx: - /* Enable the SError interrupt */ - msr daifclr, #DAIF_ABT_BIT - - save_caller_regs_and_lr - bl tsp_fiq_handler - cbz x0, fiq_sp_elx_done - - /* - * This FIQ was not targetted to S-EL1 so send it to - * the monitor and wait for execution to resume. - */ - smc #0 -fiq_sp_elx_done: - restore_caller_regs_and_lr - eret + handle_tsp_interrupt fiq_sp_elx check_vector_size fiq_sp_elx .align 7 diff --git a/bl32/tsp/tsp_interrupt.c b/bl32/tsp/tsp_interrupt.c index d5379cdc6a..7654d2e86d 100644 --- a/bl32/tsp/tsp_interrupt.c +++ b/bl32/tsp/tsp_interrupt.c @@ -31,38 +31,39 @@ #include #include #include -#include #include #include #include #include "tsp_private.h" /******************************************************************************* - * This function updates the TSP statistics for FIQs handled synchronously i.e - * the ones that have been handed over by the TSPD. It also keeps count of the - * number of times control was passed back to the TSPD after handling an FIQ. - * In the future it will be possible that the TSPD hands over an FIQ to the TSP - * but does not expect it to return execution. This statistic will be useful to - * distinguish between these two models of synchronous FIQ handling. - * The 'elr_el3' parameter contains the address of the instruction in normal - * world where this FIQ was generated. + * This function updates the TSP statistics for S-EL1 interrupts handled + * synchronously i.e the ones that have been handed over by the TSPD. It also + * keeps count of the number of times control was passed back to the TSPD + * after handling the interrupt. In the future it will be possible that the + * TSPD hands over an S-EL1 interrupt to the TSP but does not expect it to + * return execution. This statistic will be useful to distinguish between these + * two models of synchronous S-EL1 interrupt handling. The 'elr_el3' parameter + * contains the address of the instruction in normal world where this S-EL1 + * interrupt was generated. ******************************************************************************/ -void tsp_update_sync_fiq_stats(uint32_t type, uint64_t elr_el3) +void tsp_update_sync_sel1_intr_stats(uint32_t type, uint64_t elr_el3) { uint32_t linear_id = plat_my_core_pos(); - tsp_stats[linear_id].sync_fiq_count++; - if (type == TSP_HANDLE_FIQ_AND_RETURN) - tsp_stats[linear_id].sync_fiq_ret_count++; + tsp_stats[linear_id].sync_sel1_intr_count++; + if (type == TSP_HANDLE_SEL1_INTR_AND_RETURN) + tsp_stats[linear_id].sync_sel1_intr_ret_count++; #if LOG_LEVEL >= LOG_LEVEL_VERBOSE spin_lock(&console_lock); - VERBOSE("TSP: cpu 0x%lx sync fiq request from 0x%lx\n", + VERBOSE("TSP: cpu 0x%lx sync s-el1 interrupt request from 0x%lx\n", read_mpidr(), elr_el3); - VERBOSE("TSP: cpu 0x%lx: %d sync fiq requests, %d sync fiq returns\n", + VERBOSE("TSP: cpu 0x%lx: %d sync s-el1 interrupt requests," + " %d sync s-el1 interrupt returns\n", read_mpidr(), - tsp_stats[linear_id].sync_fiq_count, - tsp_stats[linear_id].sync_fiq_ret_count); + tsp_stats[linear_id].sync_sel1_intr_count, + tsp_stats[linear_id].sync_sel1_intr_ret_count); spin_unlock(&console_lock); #endif } @@ -87,13 +88,13 @@ int32_t tsp_handle_preemption(void) } /******************************************************************************* - * TSP FIQ handler called as a part of both synchronous and asynchronous - * handling of FIQ interrupts. It returns 0 upon successfully handling a S-EL1 - * FIQ and treats all other FIQs as EL3 interrupts. It assumes that the GIC - * architecture version in v2.0 and the secure physical timer interrupt is the - * only S-EL1 interrupt that it needs to handle. + * TSP interrupt handler is called as a part of both synchronous and + * asynchronous handling of TSP interrupts. Currently the physical timer + * interrupt is the only S-EL1 interrupt that this handler expects. It returns + * 0 upon successfully handling the expected interrupt and all other + * interrupts are treated as normal world or EL3 interrupts. ******************************************************************************/ -int32_t tsp_fiq_handler(void) +int32_t tsp_common_int_handler(void) { uint32_t linear_id = plat_my_core_pos(), id; @@ -123,13 +124,13 @@ int32_t tsp_fiq_handler(void) plat_ic_end_of_interrupt(id); /* Update the statistics and print some messages */ - tsp_stats[linear_id].fiq_count++; + tsp_stats[linear_id].sel1_intr_count++; #if LOG_LEVEL >= LOG_LEVEL_VERBOSE spin_lock(&console_lock); - VERBOSE("TSP: cpu 0x%lx handled fiq %d\n", + VERBOSE("TSP: cpu 0x%lx handled S-EL1 interrupt %d\n", read_mpidr(), id); - VERBOSE("TSP: cpu 0x%lx: %d fiq requests\n", - read_mpidr(), tsp_stats[linear_id].fiq_count); + VERBOSE("TSP: cpu 0x%lx: %d S-EL1 requests\n", + read_mpidr(), tsp_stats[linear_id].sel1_intr_count); spin_unlock(&console_lock); #endif return 0; diff --git a/bl32/tsp/tsp_private.h b/bl32/tsp/tsp_private.h index 346351c0c4..e341cfd7da 100644 --- a/bl32/tsp/tsp_private.h +++ b/bl32/tsp/tsp_private.h @@ -54,11 +54,14 @@ typedef struct work_statistics { - uint32_t fiq_count; /* Number of FIQs on this cpu */ - uint32_t sync_fiq_count; /* Number of sync. fiqs on this cpu */ - uint32_t sync_fiq_ret_count; /* Number of fiq returns on this cpu */ + /* Number of s-el1 interrupts on this cpu */ + uint32_t sel1_intr_count; /* Number of non s-el1 interrupts on this cpu which preempted TSP */ uint32_t preempt_intr_count; + /* Number of sync s-el1 interrupts on this cpu */ + uint32_t sync_sel1_intr_count; + /* Number of s-el1 interrupts returns on this cpu */ + uint32_t sync_sel1_intr_ret_count; uint32_t smc_count; /* Number of returns on this cpu */ uint32_t eret_count; /* Number of entries on this cpu */ uint32_t cpu_on_count; /* Number of cpu on requests */ @@ -116,8 +119,8 @@ void tsp_generic_timer_stop(void); void tsp_generic_timer_save(void); void tsp_generic_timer_restore(void); -/* FIQ management functions */ -void tsp_update_sync_fiq_stats(uint32_t type, uint64_t elr_el3); +/* S-EL1 interrupt management functions */ +void tsp_update_sync_sel1_intr_stats(uint32_t type, uint64_t elr_el3); /* Data structure to keep track of TSP statistics */ diff --git a/docs/interrupt-framework-design.md b/docs/interrupt-framework-design.md index 53707ae9d8..271cd92128 100644 --- a/docs/interrupt-framework-design.md +++ b/docs/interrupt-framework-design.md @@ -399,12 +399,12 @@ requirements mentioned earlier. 1. It passes control to the Test Secure Payload to perform its initialisation. The TSP provides the address of the vector table `tsp_vectors` in the SP which also includes the handler for Secure-EL1 - interrupts in the `fiq_entry` field. The TSPD passes control to the TSP at + interrupts in the `sel1_intr_entry` field. The TSPD passes control to the TSP at this address when it receives a Secure-EL1 interrupt. The handover agreement between the TSP and the TSPD requires that the TSPD masks all interrupts (`PSTATE.DAIF` bits) when it calls - `tsp_fiq_entry()`. The TSP has to preserve the callee saved general + `tsp_sel1_intr_entry()`. The TSP has to preserve the callee saved general purpose, SP_EL1/Secure-EL0, LR, VFP and system registers. It can use `x0-x18` to enable its C runtime. @@ -514,7 +514,7 @@ runtime firmware is not aware of through its platform port. The routing model for Secure-EL1 and non-secure interrupts chosen by the TSP is described in Section 2.2.2. It is known to the TSPD service at build time. -The TSP implements an entrypoint (`tsp_fiq_entry()`) for handling Secure-EL1 +The TSP implements an entrypoint (`tsp_sel1_intr_entry()`) for handling Secure-EL1 interrupts taken in non-secure state and routed through the TSPD service (synchronous handling model). It passes the reference to this entrypoint via `tsp_vectors` to the TSPD service. @@ -700,9 +700,9 @@ takes the following actions upon being invoked. 3. It saves the system register context for the non-secure state by calling `cm_el1_sysregs_context_save(NON_SECURE);`. -4. It sets the `ELR_EL3` system register to `tsp_fiq_entry` and sets the +4. It sets the `ELR_EL3` system register to `tsp_sel1_intr_entry` and sets the `SPSR_EL3.DAIF` bits in the secure CPU context. It sets `x0` to - `TSP_HANDLE_FIQ_AND_RETURN`. If the TSP was in the middle of handling a + `TSP_HANDLE_SEL1_INTR_AND_RETURN`. If the TSP was in the middle of handling a standard SMC, then the `ELR_EL3` and `SPSR_EL3` registers in the secure CPU context are saved first. @@ -723,20 +723,20 @@ state. ![Image 1](diagrams/sec-int-handling.png?raw=true) -The TSP issues an SMC with `TSP_HANDLED_S_EL1_FIQ` as the function identifier to +The TSP issues an SMC with `TSP_HANDLED_S_EL1_INTR` as the function identifier to signal completion of interrupt handling. The TSP issues an SMC with `TSP_PREEMPTED` as the function identifier to signal generation of a non-secure interrupt in Secure-EL1. The TSPD service takes the following actions in `tspd_smc_handler()` function -upon receiving an SMC with `TSP_HANDLED_S_EL1_FIQ` and `TSP_PREEMPTED` as the +upon receiving an SMC with `TSP_HANDLED_S_EL1_INTR` and `TSP_PREEMPTED` as the function identifiers: 1. It ensures that the call originated from the secure state otherwise execution returns to the non-secure state with `SMC_UNK` in `x0`. -2. If the function identifier is `TSP_HANDLED_S_EL1_FIQ`, it restores the +2. If the function identifier is `TSP_HANDLED_S_EL1_INTR`, it restores the saved `ELR_EL3` and `SPSR_EL3` system registers back to the secure CPU context (see step 4 above) in case the TSP had been preempted by a non secure interrupt earlier. It does not save the secure context since the @@ -811,7 +811,7 @@ state. ##### 2.3.3.1 Test secure payload behavior The TSPD hands control of a Secure-EL1 interrupt to the TSP at the -`tsp_fiq_entry()`. The TSP handles the interrupt while ensuring that the +`tsp_sel1_intr_entry()`. The TSP handles the interrupt while ensuring that the handover agreement described in Section 2.2.2.1 is maintained. It updates some statistics by calling `tsp_update_sync_fiq_stats()`. It then calls `tsp_fiq_handler()` which. @@ -827,7 +827,7 @@ statistics by calling `tsp_update_sync_fiq_stats()`. It then calls end of interrupt processing. The TSP passes control back to the TSPD by issuing an SMC64 with -`TSP_HANDLED_S_EL1_FIQ` as the function identifier. +`TSP_HANDLED_S_EL1_INTR` as the function identifier. The TSP handles interrupts under the asynchronous model as follows. diff --git a/docs/user-guide.md b/docs/user-guide.md index bcdc645d34..716ed7d877 100644 --- a/docs/user-guide.md +++ b/docs/user-guide.md @@ -278,10 +278,13 @@ performed. (Coherent memory region is included) or 0 (Coherent memory region is excluded). Default is 1. -* `TSPD_ROUTE_IRQ_TO_EL3`: A non zero value enables the routing model - for non-secure interrupts in which they are routed to EL3 (TSPD). The - default model (when the value is 0) is to route non-secure interrupts - to S-EL1 (TSP). +* `TSP_NS_INTR_ASYNC_PREEMPT`: A non zero value enables the interrupt + routing model which routes non-secure interrupts asynchronously from TSP + to EL3 causing immediate preemption of TSP. The EL3 is responsible + for saving and restoring the TSP context in this routing model. The + default routing model (when the value is 0) is to route non-secure + interrupts to TSP allowing it to save its context and hand over + synchronously to EL3 via an SMC. * `TRUSTED_BOARD_BOOT`: Boolean flag to include support for the Trusted Board Boot feature. When set to '1', BL1 and BL2 images include support to load diff --git a/include/bl32/tsp/tsp.h b/include/bl32/tsp/tsp.h index 2286b3f19b..fd43fd3b33 100644 --- a/include/bl32/tsp/tsp.h +++ b/include/bl32/tsp/tsp.h @@ -45,11 +45,12 @@ #define TSP_SYSTEM_RESET_DONE 0xf2000009 /* - * Function identifiers to handle FIQs through the synchronous handling model. - * If the TSP was previously interrupted then control has to be returned to - * the TSPD after handling the interrupt else execution can remain in the TSP. + * Function identifiers to handle S-El1 interrupt through the synchronous + * handling model. If the TSP was previously interrupted then control has to + * be returned to the TSPD after handling the interrupt else execution can + * remain in the TSP. */ -#define TSP_HANDLED_S_EL1_FIQ 0xf2000006 +#define TSP_HANDLED_S_EL1_INTR 0xf2000006 /* SMC function ID that TSP uses to request service from secure monitor */ #define TSP_GET_ARGS 0xf2001000 @@ -62,7 +63,7 @@ #define TSP_SUB 0x2001 #define TSP_MUL 0x2002 #define TSP_DIV 0x2003 -#define TSP_HANDLE_FIQ_AND_RETURN 0x2004 +#define TSP_HANDLE_SEL1_INTR_AND_RETURN 0x2004 /* * Generate function IDs for TSP services to be used in SMC calls, by @@ -114,7 +115,7 @@ typedef struct tsp_vectors { tsp_vector_isn_t cpu_off_entry; tsp_vector_isn_t cpu_resume_entry; tsp_vector_isn_t cpu_suspend_entry; - tsp_vector_isn_t fiq_entry; + tsp_vector_isn_t sel1_intr_entry; tsp_vector_isn_t system_off_entry; tsp_vector_isn_t system_reset_entry; } tsp_vectors_t; diff --git a/services/spd/tspd/tspd.mk b/services/spd/tspd/tspd.mk index 139c7d777b..ede2504772 100644 --- a/services/spd/tspd/tspd.mk +++ b/services/spd/tspd/tspd.mk @@ -55,7 +55,17 @@ NEED_BL32 := yes # Flag used to enable routing of non-secure interrupts to EL3 when they are # generated while the code is executing in S-EL1/0. -TSPD_ROUTE_IRQ_TO_EL3 := 0 +TSP_NS_INTR_ASYNC_PREEMPT := 0 -$(eval $(call assert_boolean,TSPD_ROUTE_IRQ_TO_EL3)) -$(eval $(call add_define,TSPD_ROUTE_IRQ_TO_EL3)) +# If TSPD_ROUTE_IRQ_TO_EL3 build flag is defined, use it to define value for +# TSP_NS_INTR_ASYNC_PREEMPT for backward compatibility. +ifdef TSPD_ROUTE_IRQ_TO_EL3 +ifeq (${ERROR_DEPRECATED},1) +$(error "TSPD_ROUTE_IRQ_TO_EL3 is deprecated. Please use the new build flag TSP_NS_INTR_ASYNC_PREEMPT") +endif +$(warning "TSPD_ROUTE_IRQ_TO_EL3 is deprecated. Please use the new build flag TSP_NS_INTR_ASYNC_PREEMPT") +TSP_NS_INTR_ASYNC_PREEMPT := ${TSPD_ROUTE_IRQ_TO_EL3} +endif + +$(eval $(call assert_boolean,TSP_NS_INTR_ASYNC_PREEMPT)) +$(eval $(call add_define,TSP_NS_INTR_ASYNC_PREEMPT)) diff --git a/services/spd/tspd/tspd_main.c b/services/spd/tspd/tspd_main.c index 0d6e0d227e..4c4861d72a 100644 --- a/services/spd/tspd/tspd_main.c +++ b/services/spd/tspd/tspd_main.c @@ -108,7 +108,7 @@ uint64_t tspd_handle_sp_preemption(void *handle) /******************************************************************************* * This function is the handler registered for S-EL1 interrupts by the TSPD. It * validates the interrupt and upon success arranges entry into the TSP at - * 'tsp_fiq_entry()' for handling the interrupt. + * 'tsp_sel1_intr_entry()' for handling the interrupt. ******************************************************************************/ static uint64_t tspd_sel1_interrupt_handler(uint32_t id, uint32_t flags, @@ -136,44 +136,44 @@ static uint64_t tspd_sel1_interrupt_handler(uint32_t id, * Determine if the TSP was previously preempted. Its last known * context has to be preserved in this case. * The TSP should return control to the TSPD after handling this - * FIQ. Preserve essential EL3 context to allow entry into the - * TSP at the FIQ entry point using the 'cpu_context' structure. - * There is no need to save the secure system register context - * since the TSP is supposed to preserve it during S-EL1 interrupt - * handling. + * S-EL1 interrupt. Preserve essential EL3 context to allow entry into + * the TSP at the S-EL1 interrupt entry point using the 'cpu_context' + * structure. There is no need to save the secure system register + * context since the TSP is supposed to preserve it during S-EL1 + * interrupt handling. */ if (get_std_smc_active_flag(tsp_ctx->state)) { tsp_ctx->saved_spsr_el3 = SMC_GET_EL3(&tsp_ctx->cpu_ctx, CTX_SPSR_EL3); tsp_ctx->saved_elr_el3 = SMC_GET_EL3(&tsp_ctx->cpu_ctx, CTX_ELR_EL3); -#if TSPD_ROUTE_IRQ_TO_EL3 +#if TSP_NS_INTR_ASYNC_PREEMPT /*Need to save the previously interrupted secure context */ memcpy(&tsp_ctx->sp_ctx, &tsp_ctx->cpu_ctx, TSPD_SP_CTX_SIZE); #endif } cm_el1_sysregs_context_restore(SECURE); - cm_set_elr_spsr_el3(SECURE, (uint64_t) &tsp_vectors->fiq_entry, + cm_set_elr_spsr_el3(SECURE, (uint64_t) &tsp_vectors->sel1_intr_entry, SPSR_64(MODE_EL1, MODE_SP_ELX, DISABLE_ALL_EXCEPTIONS)); cm_set_next_eret_context(SECURE); /* - * Tell the TSP that it has to handle an FIQ synchronously. Also the - * instruction in normal world where the interrupt was generated is - * passed for debugging purposes. It is safe to retrieve this address - * from ELR_EL3 as the secure context will not take effect until - * el3_exit(). + * Tell the TSP that it has to handle a S-EL1 interrupt synchronously. + * Also the instruction in normal world where the interrupt was + * generated is passed for debugging purposes. It is safe to retrieve + * this address from ELR_EL3 as the secure context will not take effect + * until el3_exit(). */ - SMC_RET2(&tsp_ctx->cpu_ctx, TSP_HANDLE_FIQ_AND_RETURN, read_elr_el3()); + SMC_RET2(&tsp_ctx->cpu_ctx, TSP_HANDLE_SEL1_INTR_AND_RETURN, read_elr_el3()); } -#if TSPD_ROUTE_IRQ_TO_EL3 +#if TSP_NS_INTR_ASYNC_PREEMPT /******************************************************************************* - * This function is the handler registered for S-EL1 interrupts by the TSPD. It - * validates the interrupt and upon success arranges entry into the TSP at - * 'tsp_fiq_entry()' for handling the interrupt. + * This function is the handler registered for Non secure interrupts by the + * TSPD. It validates the interrupt and upon success arranges entry into the + * normal world for handling the interrupt. ******************************************************************************/ static uint64_t tspd_ns_interrupt_handler(uint32_t id, uint32_t flags, @@ -327,10 +327,10 @@ uint64_t tspd_smc_handler(uint32_t smc_fid, /* * This function ID is used only by the TSP to indicate that it has - * finished handling a S-EL1 FIQ interrupt. Execution should resume + * finished handling a S-EL1 interrupt. Execution should resume * in the normal world. */ - case TSP_HANDLED_S_EL1_FIQ: + case TSP_HANDLED_S_EL1_INTR: if (ns) SMC_RET1(handle, SMC_UNK); @@ -347,7 +347,7 @@ uint64_t tspd_smc_handler(uint32_t smc_fid, SMC_SET_EL3(&tsp_ctx->cpu_ctx, CTX_ELR_EL3, tsp_ctx->saved_elr_el3); -#if TSPD_ROUTE_IRQ_TO_EL3 +#if TSP_NS_INTR_ASYNC_PREEMPT /* * Need to restore the previously interrupted * secure context. @@ -408,7 +408,7 @@ uint64_t tspd_smc_handler(uint32_t smc_fid, if (rc) panic(); -#if TSPD_ROUTE_IRQ_TO_EL3 +#if TSP_NS_INTR_ASYNC_PREEMPT /* * Register an interrupt handler for NS interrupts when * generated during code executing in secure state are @@ -546,7 +546,7 @@ uint64_t tspd_smc_handler(uint32_t smc_fid, set_std_smc_active_flag(tsp_ctx->state); cm_set_elr_el3(SECURE, (uint64_t) &tsp_vectors->std_smc_entry); -#if TSPD_ROUTE_IRQ_TO_EL3 +#if TSP_NS_INTR_ASYNC_PREEMPT /* * Enable the routing of NS interrupts to EL3 * during STD SMC processing on this core. @@ -577,7 +577,7 @@ uint64_t tspd_smc_handler(uint32_t smc_fid, cm_set_next_eret_context(NON_SECURE); if (GET_SMC_TYPE(smc_fid) == SMC_TYPE_STD) { clr_std_smc_active_flag(tsp_ctx->state); -#if TSPD_ROUTE_IRQ_TO_EL3 +#if TSP_NS_INTR_ASYNC_PREEMPT /* * Disable the routing of NS interrupts to EL3 * after STD SMC processing is finished on this @@ -620,7 +620,7 @@ uint64_t tspd_smc_handler(uint32_t smc_fid, * We are done stashing the non-secure context. Ask the * secure payload to do the work now. */ -#if TSPD_ROUTE_IRQ_TO_EL3 +#if TSP_NS_INTR_ASYNC_PREEMPT /* * Enable the routing of NS interrupts to EL3 during resumption * of STD SMC call on this core. diff --git a/services/spd/tspd/tspd_pm.c b/services/spd/tspd/tspd_pm.c index 5089420d0c..55562ba453 100644 --- a/services/spd/tspd/tspd_pm.c +++ b/services/spd/tspd/tspd_pm.c @@ -130,7 +130,7 @@ static void tspd_cpu_on_finish_handler(uint64_t unused) /* Initialise this cpu's secure context */ cm_init_my_context(&tsp_on_entrypoint); -#if TSPD_ROUTE_IRQ_TO_EL3 +#if TSP_NS_INTR_ASYNC_PREEMPT /* * Disable the NS interrupt locally since it will be enabled globally * within cm_init_my_context. diff --git a/services/spd/tspd/tspd_private.h b/services/spd/tspd/tspd_private.h index 5f6fb2b754..cadc6aaaa2 100644 --- a/services/spd/tspd/tspd_private.h +++ b/services/spd/tspd/tspd_private.h @@ -183,10 +183,10 @@ CASSERT(TSPD_SP_CTX_SIZE == sizeof(sp_ctx_regs_t), \ /******************************************************************************* * Structure which helps the SPD to maintain the per-cpu state of the SP. - * 'saved_spsr_el3' - temporary copy to allow FIQ handling when the TSP has been - * preempted. - * 'saved_elr_el3' - temporary copy to allow FIQ handling when the TSP has been - * preempted. + * 'saved_spsr_el3' - temporary copy to allow S-EL1 interrupt handling when + * the TSP has been preempted. + * 'saved_elr_el3' - temporary copy to allow S-EL1 interrupt handling when + * the TSP has been preempted. * 'state' - collection of flags to track SP state e.g. on/off * 'mpidr' - mpidr to associate a context with a cpu * 'c_rt_ctx' - stack address to restore C runtime context from after @@ -207,7 +207,7 @@ typedef struct tsp_context { uint64_t c_rt_ctx; cpu_context_t cpu_ctx; uint64_t saved_tsp_args[TSP_NUM_ARGS]; -#if TSPD_ROUTE_IRQ_TO_EL3 +#if TSP_NS_INTR_ASYNC_PREEMPT sp_ctx_regs_t sp_ctx; #endif } tsp_context_t; From f14d188681b2c6f49ccd22595b112da7b02798f8 Mon Sep 17 00:00:00 2001 From: Soby Mathew Date: Mon, 26 Oct 2015 14:01:53 +0000 Subject: [PATCH 4/7] Prepare platforms to use refactored ARM GIC drivers This patch adds platform helpers for the new GICv2 and GICv3 drivers in plat_gicv2.c and plat_gicv3.c. The platforms can include the appropriate file in their build according to the GIC driver to be used. The existing plat_gic.c is only meant for the legacy GIC driver. In the case of ARM platforms, the major changes are as follows: 1. The crash reporting helper macro `arm_print_gic_regs` that prints the GIC CPU interface register values has been modified to detect the type of CPU interface being used (System register or memory mappped interface) before using the right interface to print the registers. 2. The power management helper function that is called after a core is powered up has been further refactored. This is to highlight that the per-cpu distributor interface should be initialised only when the core was originally powered down using the CPU_OFF PSCI API and not when the CPU_SUSPEND PSCI API was used. 3. In the case of CSS platforms, the system power domain restore helper `arm_system_pwr_domain_resume()` is now only invoked in the `suspend_finish` handler as the system power domain is always expected to be initialized when the `on_finish` handler is invoked. Change-Id: I7fc27d61fc6c2a60cea2436b676c5737d0257df6 --- include/plat/arm/common/aarch64/arm_macros.S | 34 ++- plat/arm/board/fvp/aarch64/fvp_helpers.S | 23 +- plat/arm/board/fvp/fvp_pm.c | 76 ++++--- plat/arm/board/fvp/include/plat_macros.S | 8 +- plat/arm/css/common/css_pm.c | 99 ++++---- plat/common/plat_gicv2.c | 148 ++++++++++++ plat/common/plat_gicv3.c | 224 +++++++++++++++++++ 7 files changed, 513 insertions(+), 99 deletions(-) create mode 100644 plat/common/plat_gicv2.c create mode 100644 plat/common/plat_gicv3.c diff --git a/include/plat/arm/common/aarch64/arm_macros.S b/include/plat/arm/common/aarch64/arm_macros.S index 594b096592..eaaa62fe1c 100644 --- a/include/plat/arm/common/aarch64/arm_macros.S +++ b/include/plat/arm/common/aarch64/arm_macros.S @@ -31,12 +31,21 @@ #define __ARM_MACROS_S__ #include -#include +#include +#include +#include #include .section .rodata.gic_reg_name, "aS" +/* Applicable only to GICv2 and GICv3 with SRE disabled (legacy mode) */ gicc_regs: .asciz "gicc_hppir", "gicc_ahppir", "gicc_ctlr", "" + +/* Applicable only to GICv3 with SRE enabled */ +icc_regs: + .asciz "icc_hppir0_el1", "icc_hppir1_el1", "icc_ctlr_el3", "" + +/* Registers common to both GICv2 and GICv3 */ gicd_pend_reg: .asciz "gicd_ispendr regs (Offsets 0x200 - 0x278)\n" \ " Offset:\t\t\tvalue\n" @@ -54,6 +63,28 @@ spacer: * --------------------------------------------- */ .macro arm_print_gic_regs + /* Check for GICv3 system register access */ + mrs x7, id_aa64pfr0_el1 + ubfx x7, x7, #ID_AA64PFR0_GIC_SHIFT, #ID_AA64PFR0_GIC_WIDTH + cmp x7, #1 + b.ne print_gicv2 + + /* Check for SRE enable */ + mrs x8, ICC_SRE_EL3 + tst x8, #ICC_SRE_SRE_BIT + b.eq print_gicv2 + + /* Load the icc reg list to x6 */ + adr x6, icc_regs + /* Load the icc regs to gp regs used by str_in_crash_buf_print */ + mrs x8, ICC_HPPIR0_EL1 + mrs x9, ICC_HPPIR1_EL1 + mrs x10, ICC_CTLR_EL3 + /* Store to the crash buf and print to console */ + bl str_in_crash_buf_print + b print_gic_common + +print_gicv2: /* Load the gicc reg list to x6 */ adr x6, gicc_regs /* Load the gicc regs to gp regs used by str_in_crash_buf_print */ @@ -63,6 +94,7 @@ spacer: /* Store to the crash buf and print to console */ bl str_in_crash_buf_print +print_gic_common: /* Print the GICD_ISPENDR regs */ add x7, x16, #GICD_ISPENDR adr x4, gicd_pend_reg diff --git a/plat/arm/board/fvp/aarch64/fvp_helpers.S b/plat/arm/board/fvp/aarch64/fvp_helpers.S index 2c24e61f95..338d15813c 100644 --- a/plat/arm/board/fvp/aarch64/fvp_helpers.S +++ b/plat/arm/board/fvp/aarch64/fvp_helpers.S @@ -30,7 +30,8 @@ #include #include -#include +#include +#include #include #include #include "../drivers/pwrc/fvp_pwrc.h" @@ -74,9 +75,26 @@ func plat_secondary_cold_boot_setup str w0, [x1, #PPOFFR_OFF] /* --------------------------------------------- - * Deactivate the gic cpu interface as well + * Disable GIC bypass as well * --------------------------------------------- */ + /* Check for GICv3 system register access */ + mrs x0, id_aa64pfr0_el1 + ubfx x0, x0, #ID_AA64PFR0_GIC_SHIFT, #ID_AA64PFR0_GIC_WIDTH + cmp x0, #1 + b.ne gicv2_bypass_disable + + /* Check for SRE enable */ + mrs x1, ICC_SRE_EL3 + tst x1, #ICC_SRE_SRE_BIT + b.eq gicv2_bypass_disable + + mrs x2, ICC_SRE_EL3 + orr x2, x2, #(ICC_SRE_DIB_BIT | ICC_SRE_DFB_BIT) + msr ICC_SRE_EL3, x2 + b secondary_cold_boot_wait + +gicv2_bypass_disable: ldr x0, =VE_GICC_BASE ldr x1, =BASE_GICC_BASE fvp_choose_gicmmap x0, x1, x2, w2, x1 @@ -84,6 +102,7 @@ func plat_secondary_cold_boot_setup orr w0, w0, #(IRQ_BYP_DIS_GRP0 | FIQ_BYP_DIS_GRP0) str w0, [x1, #GICC_CTLR] +secondary_cold_boot_wait: /* --------------------------------------------- * There is no sane reason to come out of this * wfi so panic if we do. This cpu will be pow- diff --git a/plat/arm/board/fvp/fvp_pm.c b/plat/arm/board/fvp/fvp_pm.c index 21ad14fa5e..d7b459d78f 100644 --- a/plat/arm/board/fvp/fvp_pm.c +++ b/plat/arm/board/fvp/fvp_pm.c @@ -93,6 +93,42 @@ static void fvp_cluster_pwrdwn_common(void) fvp_pwrc_write_pcoffr(mpidr); } +static void fvp_power_domain_on_finish_common(const psci_power_state_t *target_state) +{ + unsigned long mpidr; + + assert(target_state->pwr_domain_state[ARM_PWR_LVL0] == + ARM_LOCAL_STATE_OFF); + + /* Get the mpidr for this cpu */ + mpidr = read_mpidr_el1(); + + /* Perform the common cluster specific operations */ + if (target_state->pwr_domain_state[ARM_PWR_LVL1] == + ARM_LOCAL_STATE_OFF) { + /* + * This CPU might have woken up whilst the cluster was + * attempting to power down. In this case the FVP power + * controller will have a pending cluster power off request + * which needs to be cleared by writing to the PPONR register. + * This prevents the power controller from interpreting a + * subsequent entry of this cpu into a simple wfi as a power + * down request. + */ + fvp_pwrc_write_pponr(mpidr); + + /* Enable coherency if this cluster was off */ + fvp_cci_enable(); + } + + /* + * Clear PWKUPR.WEN bit to ensure interrupts do not interfere + * with a cpu power down unless the bit is set again + */ + fvp_pwrc_clr_wen(mpidr); +} + + /******************************************************************************* * FVP handler called when a CPU is about to enter standby. ******************************************************************************/ @@ -196,42 +232,11 @@ void fvp_pwr_domain_suspend(const psci_power_state_t *target_state) ******************************************************************************/ void fvp_pwr_domain_on_finish(const psci_power_state_t *target_state) { - unsigned long mpidr; - - assert(target_state->pwr_domain_state[ARM_PWR_LVL0] == - ARM_LOCAL_STATE_OFF); - - /* Get the mpidr for this cpu */ - mpidr = read_mpidr_el1(); - - /* Perform the common cluster specific operations */ - if (target_state->pwr_domain_state[ARM_PWR_LVL1] == - ARM_LOCAL_STATE_OFF) { - /* - * This CPU might have woken up whilst the cluster was - * attempting to power down. In this case the FVP power - * controller will have a pending cluster power off request - * which needs to be cleared by writing to the PPONR register. - * This prevents the power controller from interpreting a - * subsequent entry of this cpu into a simple wfi as a power - * down request. - */ - fvp_pwrc_write_pponr(mpidr); - - /* Enable coherency if this cluster was off */ - fvp_cci_enable(); - } - - /* - * Clear PWKUPR.WEN bit to ensure interrupts do not interfere - * with a cpu power down unless the bit is set again - */ - fvp_pwrc_clr_wen(mpidr); + fvp_power_domain_on_finish_common(target_state); /* Enable the gic cpu interface */ arm_gic_cpuif_setup(); - - /* TODO: This setup is needed only after a cold boot */ + /* Program the gic per-cpu distributor interface */ arm_gic_pcpu_distif_setup(); } @@ -251,7 +256,10 @@ void fvp_pwr_domain_suspend_finish(const psci_power_state_t *target_state) ARM_LOCAL_STATE_RET) return; - fvp_pwr_domain_on_finish(target_state); + fvp_power_domain_on_finish_common(target_state); + + /* Enable the gic cpu interface */ + arm_gic_cpuif_setup(); } /******************************************************************************* diff --git a/plat/arm/board/fvp/include/plat_macros.S b/plat/arm/board/fvp/include/plat_macros.S index 2feffbe521..2ed0d85e9f 100644 --- a/plat/arm/board/fvp/include/plat_macros.S +++ b/plat/arm/board/fvp/include/plat_macros.S @@ -53,16 +53,14 @@ /* Check if VE mmap */ cmp w16, #BLD_GIC_VE_MMAP b.eq use_ve_mmap - /* Check if Cortex-A53/A57 mmap */ - cmp w16, #BLD_GIC_A53A57_MMAP - b.ne exit_print_gic_regs + /* Assume Base Cortex mmap */ mov_imm x17, BASE_GICC_BASE mov_imm x16, BASE_GICD_BASE - b print_gicc_regs + b print_gic_regs use_ve_mmap: mov_imm x17, VE_GICC_BASE mov_imm x16, VE_GICD_BASE -print_gicc_regs: +print_gic_regs: arm_print_gic_regs .endm diff --git a/plat/arm/css/common/css_pm.c b/plat/arm/css/common/css_pm.c index 3f468570e1..1d9bd59479 100644 --- a/plat/arm/css/common/css_pm.c +++ b/plat/arm/css/common/css_pm.c @@ -41,6 +41,12 @@ #include #include "css_scpi.h" +/* Macros to read the CSS power domain state */ +#define CSS_CORE_PWR_STATE(state) (state)->pwr_domain_state[ARM_PWR_LVL0] +#define CSS_CLUSTER_PWR_STATE(state) (state)->pwr_domain_state[ARM_PWR_LVL1] +#define CSS_SYSTEM_PWR_STATE(state) ((PLAT_MAX_PWR_LVL > ARM_PWR_LVL1) ?\ + (state)->pwr_domain_state[ARM_PWR_LVL2] : 0) + /* Allow CSS platforms to override `plat_arm_psci_pm_ops` */ #pragma weak plat_arm_psci_pm_ops @@ -93,50 +99,37 @@ int css_pwr_domain_on(u_register_t mpidr) return PSCI_E_SUCCESS; } -/******************************************************************************* - * Handler called when a power level has just been powered on after - * being turned off earlier. The target_state encodes the low power state that - * each level has woken up from. - ******************************************************************************/ -void css_pwr_domain_on_finish(const psci_power_state_t *target_state) +static void css_pwr_domain_on_finisher_common( + const psci_power_state_t *target_state) { - assert(target_state->pwr_domain_state[ARM_PWR_LVL0] == - ARM_LOCAL_STATE_OFF); - - if (PLAT_MAX_PWR_LVL > ARM_PWR_LVL1) { - /* - * Perform system initialization if woken up from system - * suspend. - */ - if (target_state->pwr_domain_state[ARM_PWR_LVL2] == - ARM_LOCAL_STATE_OFF) - arm_system_pwr_domain_resume(); - } + assert(CSS_CORE_PWR_STATE(target_state) == ARM_LOCAL_STATE_OFF); /* * Perform the common cluster specific operations i.e enable coherency * if this cluster was off. */ - if (target_state->pwr_domain_state[ARM_PWR_LVL1] == - ARM_LOCAL_STATE_OFF) + if (CSS_CLUSTER_PWR_STATE(target_state) == ARM_LOCAL_STATE_OFF) cci_enable_snoop_dvm_reqs(MPIDR_AFFLVL1_VAL(read_mpidr_el1())); +} +/******************************************************************************* + * Handler called when a power level has just been powered on after + * being turned off earlier. The target_state encodes the low power state that + * each level has woken up from. This handler would never be invoked with + * the system power domain uninitialized as either the primary would have taken + * care of it as part of cold boot or the first core awakened from system + * suspend would have already initialized it. + ******************************************************************************/ +void css_pwr_domain_on_finish(const psci_power_state_t *target_state) +{ + /* Assert that the system power domain need not be initialized */ + assert(CSS_SYSTEM_PWR_STATE(target_state) == ARM_LOCAL_STATE_RUN); - if (PLAT_MAX_PWR_LVL > ARM_PWR_LVL1) { - /* - * Skip GIC CPU interface and per-CPU Distributor interface - * setups if woken up from system suspend as it is done as - * part of css_system_pwr_domain_resume(). - */ - if (target_state->pwr_domain_state[ARM_PWR_LVL2] == - ARM_LOCAL_STATE_OFF) - return; - } + css_pwr_domain_on_finisher_common(target_state); /* Enable the gic cpu interface */ arm_gic_cpuif_setup(); - - /* todo: Is this setup only needed after a cold boot? */ + /* Program the gic per-cpu distributor interface */ arm_gic_pcpu_distif_setup(); } @@ -154,19 +147,12 @@ static void css_power_down_common(const psci_power_state_t *target_state) /* Prevent interrupts from spuriously waking up this cpu */ arm_gic_cpuif_deactivate(); - if (PLAT_MAX_PWR_LVL > ARM_PWR_LVL1) { - /* - * Check if power down at system power domain level is - * requested. - */ - if (target_state->pwr_domain_state[ARM_PWR_LVL2] == - ARM_LOCAL_STATE_OFF) + /* Check if power down at system power domain level is requested */ + if (CSS_SYSTEM_PWR_STATE(target_state) == ARM_LOCAL_STATE_OFF) system_state = scpi_power_retention; - } /* Cluster is to be turned off, so disable coherency */ - if (target_state->pwr_domain_state[ARM_PWR_LVL1] == - ARM_LOCAL_STATE_OFF) { + if (CSS_CLUSTER_PWR_STATE(target_state) == ARM_LOCAL_STATE_OFF) { cci_disable_snoop_dvm_reqs(MPIDR_AFFLVL1_VAL(read_mpidr())); cluster_state = scpi_power_off; } @@ -187,9 +173,7 @@ static void css_power_down_common(const psci_power_state_t *target_state) ******************************************************************************/ void css_pwr_domain_off(const psci_power_state_t *target_state) { - assert(target_state->pwr_domain_state[ARM_PWR_LVL0] == - ARM_LOCAL_STATE_OFF); - + assert(CSS_CORE_PWR_STATE(target_state) == ARM_LOCAL_STATE_OFF); css_power_down_common(target_state); } @@ -200,16 +184,13 @@ void css_pwr_domain_off(const psci_power_state_t *target_state) void css_pwr_domain_suspend(const psci_power_state_t *target_state) { /* - * Juno has retention only at cpu level. Just return + * CSS currently supports retention only at cpu level. Just return * as nothing is to be done for retention. */ - if (target_state->pwr_domain_state[ARM_PWR_LVL0] == - ARM_LOCAL_STATE_RET) + if (CSS_CORE_PWR_STATE(target_state) == ARM_LOCAL_STATE_RET) return; - assert(target_state->pwr_domain_state[ARM_PWR_LVL0] == - ARM_LOCAL_STATE_OFF); - + assert(CSS_CORE_PWR_STATE(target_state) == ARM_LOCAL_STATE_OFF); css_power_down_common(target_state); } @@ -223,14 +204,18 @@ void css_pwr_domain_suspend(const psci_power_state_t *target_state) void css_pwr_domain_suspend_finish( const psci_power_state_t *target_state) { - /* - * Return as nothing is to be done on waking up from retention. - */ - if (target_state->pwr_domain_state[ARM_PWR_LVL0] == - ARM_LOCAL_STATE_RET) + /* Return as nothing is to be done on waking up from retention. */ + if (CSS_CORE_PWR_STATE(target_state) == ARM_LOCAL_STATE_RET) return; - css_pwr_domain_on_finish(target_state); + /* Perform system domain restore if woken up from system suspend */ + if (CSS_SYSTEM_PWR_STATE(target_state) == ARM_LOCAL_STATE_OFF) + arm_system_pwr_domain_resume(); + else + /* Enable the gic cpu interface */ + arm_gic_cpuif_setup(); + + css_pwr_domain_on_finisher_common(target_state); } /******************************************************************************* diff --git a/plat/common/plat_gicv2.c b/plat/common/plat_gicv2.c new file mode 100644 index 0000000000..65f89dc6a8 --- /dev/null +++ b/plat/common/plat_gicv2.c @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2015, ARM Limited and Contributors. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include +#include +#include +#include + +/* + * The following platform GIC functions are weakly defined. They + * provide typical implementations that may be re-used by multiple + * platforms but may also be overridden by a platform if required. + */ +#pragma weak plat_ic_get_pending_interrupt_id +#pragma weak plat_ic_get_pending_interrupt_type +#pragma weak plat_ic_acknowledge_interrupt +#pragma weak plat_ic_get_interrupt_type +#pragma weak plat_ic_end_of_interrupt +#pragma weak plat_interrupt_type_to_line + +/* + * This function returns the highest priority pending interrupt at + * the Interrupt controller + */ +uint32_t plat_ic_get_pending_interrupt_id(void) +{ + unsigned int id; + + id = gicv2_get_pending_interrupt_id(); + if (id == GIC_SPURIOUS_INTERRUPT) + return INTR_ID_UNAVAILABLE; + + return id; +} + +/* + * This function returns the type of the highest priority pending interrupt + * at the Interrupt controller. In the case of GICv2, the Highest Priority + * Pending interrupt register (`GICC_HPPIR`) is read to determine the id of + * the pending interrupt. The type of interrupt depends upon the id value + * as follows. + * 1. id < PENDING_G1_INTID (1022) is reported as a S-EL1 interrupt + * 2. id = PENDING_G1_INTID (1022) is reported as a Non-secure interrupt. + * 3. id = GIC_SPURIOUS_INTERRUPT (1023) is reported as an invalid interrupt + * type. + */ +uint32_t plat_ic_get_pending_interrupt_type(void) +{ + unsigned int id; + + id = gicv2_get_pending_interrupt_type(); + + /* Assume that all secure interrupts are S-EL1 interrupts */ + if (id < PENDING_G1_INTID) + return INTR_TYPE_S_EL1; + + if (id == GIC_SPURIOUS_INTERRUPT) + return INTR_TYPE_INVAL; + + return INTR_TYPE_NS; +} + +/* + * This function returns the highest priority pending interrupt at + * the Interrupt controller and indicates to the Interrupt controller + * that the interrupt processing has started. + */ +uint32_t plat_ic_acknowledge_interrupt(void) +{ + return gicv2_acknowledge_interrupt(); +} + +/* + * This function returns the type of the interrupt `id`, depending on how + * the interrupt has been configured in the interrupt controller + */ +uint32_t plat_ic_get_interrupt_type(uint32_t id) +{ + unsigned int type; + + type = gicv2_get_interrupt_group(id); + + /* Assume that all secure interrupts are S-EL1 interrupts */ + return (type) ? INTR_TYPE_NS : INTR_TYPE_S_EL1; +} + +/* + * This functions is used to indicate to the interrupt controller that + * the processing of the interrupt corresponding to the `id` has + * finished. + */ +void plat_ic_end_of_interrupt(uint32_t id) +{ + gicv2_end_of_interrupt(id); +} + +/* + * An ARM processor signals interrupt exceptions through the IRQ and FIQ pins. + * The interrupt controller knows which pin/line it uses to signal a type of + * interrupt. It lets the interrupt management framework determine + * for a type of interrupt and security state, which line should be used in the + * SCR_EL3 to control its routing to EL3. The interrupt line is represented + * as the bit position of the IRQ or FIQ bit in the SCR_EL3. + */ +uint32_t plat_interrupt_type_to_line(uint32_t type, + uint32_t security_state) +{ + assert(type == INTR_TYPE_S_EL1 || + type == INTR_TYPE_EL3 || + type == INTR_TYPE_NS); + + /* Non-secure interrupts are signaled on the IRQ line always */ + if (type == INTR_TYPE_NS) + return __builtin_ctz(SCR_IRQ_BIT); + + /* + * Secure interrupts are signaled using the IRQ line if the FIQ is + * not enabled else they are signaled using the FIQ line. + */ + return ((gicv2_is_fiq_enabled()) ? __builtin_ctz(SCR_FIQ_BIT) : + __builtin_ctz(SCR_IRQ_BIT)); +} diff --git a/plat/common/plat_gicv3.c b/plat/common/plat_gicv3.c new file mode 100644 index 0000000000..249caf8e57 --- /dev/null +++ b/plat/common/plat_gicv3.c @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2015, ARM Limited and Contributors. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#if IMAGE_BL31 + +/* + * The following platform GIC functions are weakly defined. They + * provide typical implementations that may be re-used by multiple + * platforms but may also be overridden by a platform if required. + */ +#pragma weak plat_ic_get_pending_interrupt_id +#pragma weak plat_ic_get_pending_interrupt_type +#pragma weak plat_ic_acknowledge_interrupt +#pragma weak plat_ic_get_interrupt_type +#pragma weak plat_ic_end_of_interrupt +#pragma weak plat_interrupt_type_to_line + +CASSERT((INTR_TYPE_S_EL1 == INTR_GROUP1S) && + (INTR_TYPE_NS == INTR_GROUP1NS) && + (INTR_TYPE_EL3 == INTR_GROUP0), assert_interrupt_type_mismatch); + +/* + * This function returns the highest priority pending interrupt at + * the Interrupt controller + */ +uint32_t plat_ic_get_pending_interrupt_id(void) +{ + unsigned int irqnr; + + assert(IS_IN_EL3()); + irqnr = gicv3_get_pending_interrupt_id(); + return (gicv3_is_intr_id_special_identifier(irqnr)) ? + INTR_ID_UNAVAILABLE : irqnr; +} + +/* + * This function returns the type of the highest priority pending interrupt + * at the Interrupt controller. In the case of GICv3, the Highest Priority + * Pending interrupt system register (`ICC_HPPIR0_EL1`) is read to determine + * the id of the pending interrupt. The type of interrupt depends upon the + * id value as follows. + * 1. id = PENDING_G1S_INTID (1020) is reported as a S-EL1 interrupt + * 2. id = PENDING_G1NS_INTID (1021) is reported as a Non-secure interrupt. + * 3. id = GIC_SPURIOUS_INTERRUPT (1023) is reported as an invalid interrupt + * type. + * 4. All other interrupt id's are reported as EL3 interrupt. + */ +uint32_t plat_ic_get_pending_interrupt_type(void) +{ + unsigned int irqnr; + + assert(IS_IN_EL3()); + irqnr = gicv3_get_pending_interrupt_type(); + + switch (irqnr) { + case PENDING_G1S_INTID: + return INTR_TYPE_S_EL1; + case PENDING_G1NS_INTID: + return INTR_TYPE_NS; + case GIC_SPURIOUS_INTERRUPT: + return INTR_TYPE_INVAL; + default: + return INTR_TYPE_EL3; + } +} + +/* + * This function returns the highest priority pending interrupt at + * the Interrupt controller and indicates to the Interrupt controller + * that the interrupt processing has started. + */ +uint32_t plat_ic_acknowledge_interrupt(void) +{ + assert(IS_IN_EL3()); + return gicv3_acknowledge_interrupt(); +} + +/* + * This function returns the type of the interrupt `id`, depending on how + * the interrupt has been configured in the interrupt controller + */ +uint32_t plat_ic_get_interrupt_type(uint32_t id) +{ + assert(IS_IN_EL3()); + return gicv3_get_interrupt_type(id, plat_my_core_pos()); +} + +/* + * This functions is used to indicate to the interrupt controller that + * the processing of the interrupt corresponding to the `id` has + * finished. + */ +void plat_ic_end_of_interrupt(uint32_t id) +{ + assert(IS_IN_EL3()); + gicv3_end_of_interrupt(id); +} + +/* + * An ARM processor signals interrupt exceptions through the IRQ and FIQ pins. + * The interrupt controller knows which pin/line it uses to signal a type of + * interrupt. It lets the interrupt management framework determine for a type of + * interrupt and security state, which line should be used in the SCR_EL3 to + * control its routing to EL3. The interrupt line is represented as the bit + * position of the IRQ or FIQ bit in the SCR_EL3. + */ +uint32_t plat_interrupt_type_to_line(uint32_t type, + uint32_t security_state) +{ + assert(type == INTR_TYPE_S_EL1 || + type == INTR_TYPE_EL3 || + type == INTR_TYPE_NS); + + assert(sec_state_is_valid(security_state)); + assert(IS_IN_EL3()); + + switch (type) { + case INTR_TYPE_S_EL1: + /* + * The S-EL1 interrupts are signaled as IRQ in S-EL0/1 contexts + * and as FIQ in the NS-EL0/1/2 contexts + */ + if (security_state == SECURE) + return __builtin_ctz(SCR_IRQ_BIT); + else + return __builtin_ctz(SCR_FIQ_BIT); + case INTR_TYPE_NS: + /* + * The Non secure interrupts will be signaled as FIQ in S-EL0/1 + * contexts and as IRQ in the NS-EL0/1/2 contexts. + */ + if (security_state == SECURE) + return __builtin_ctz(SCR_FIQ_BIT); + else + return __builtin_ctz(SCR_IRQ_BIT); + default: + assert(0); + /* Fall through in the release build */ + case INTR_TYPE_EL3: + /* + * The EL3 interrupts are signaled as FIQ in both S-EL0/1 and + * NS-EL0/1/2 contexts + */ + return __builtin_ctz(SCR_FIQ_BIT); + } +} +#endif +#if IMAGE_BL32 + +#pragma weak plat_ic_get_pending_interrupt_id +#pragma weak plat_ic_acknowledge_interrupt +#pragma weak plat_ic_end_of_interrupt + +/* + * This function returns the highest priority pending interrupt at + * the Interrupt controller + */ +uint32_t plat_ic_get_pending_interrupt_id(void) +{ + unsigned int irqnr; + + assert(IS_IN_EL1()); + irqnr = gicv3_get_pending_interrupt_id_sel1(); + return (irqnr == GIC_SPURIOUS_INTERRUPT) ? + INTR_ID_UNAVAILABLE : irqnr; +} + +/* + * This function returns the highest priority pending interrupt at + * the Interrupt controller and indicates to the Interrupt controller + * that the interrupt processing has started. + */ +uint32_t plat_ic_acknowledge_interrupt(void) +{ + assert(IS_IN_EL1()); + return gicv3_acknowledge_interrupt_sel1(); +} + +/* + * This functions is used to indicate to the interrupt controller that + * the processing of the interrupt corresponding to the `id` has + * finished. + */ +void plat_ic_end_of_interrupt(uint32_t id) +{ + assert(IS_IN_EL1()); + gicv3_end_of_interrupt_sel1(id); +} +#endif From 27573c59a6bf16c2330b41453f87bdd60afb2144 Mon Sep 17 00:00:00 2001 From: Achin Gupta Date: Tue, 3 Nov 2015 14:18:34 +0000 Subject: [PATCH 5/7] Rework use of ARM GIC drivers on ARM platforms Suport for ARM GIC v2.0 and v3.0 drivers has been reworked to create three separate drivers instead of providing a single driver that can work on both versions of the GIC architecture. These drivers correspond to the following software use cases: 1. A GICv2 only driver that can run only on ARM GIC v2.0 implementations e.g. GIC-400 2. A GICv3 only driver that can run only on ARM GIC v3.0 implementations e.g. GIC-500 in a mode where all interrupt regimes use GICv3 features 3. A deprecated GICv3 driver that operates in legacy mode. This driver can operate only in the GICv2 mode in the secure world. On a GICv3 system, this driver allows normal world to run in either GICv3 mode (asymmetric mode) or in the GICv2 mode. Both modes of operation are deprecated on GICv3 systems. ARM platforms implement both versions of the GIC architecture. This patch adds a layer of abstraction to help ARM platform ports chose the right GIC driver and corresponding platform support. This is as described below: 1. A set of ARM common functions have been introduced to initialise the GIC and the driver during cold and warm boot. These functions are prefixed as "plat_arm_gic_". Weak definitions of these functions have been provided for each type of driver. 2. Each platform includes the sources that implement the right functions directly into the its makefile. The FVP can be instantiated with different versions of the GIC architecture. It uses the FVP_USE_GIC_DRIVER build option to specify which of the three drivers should be included in the build. 3. A list of secure interrupts has to be provided to initialise each of the three GIC drivers. For GIC v3.0 the interrupt ids have to be further categorised as Group 0 and Group 1 Secure interrupts. For GIC v2.0, the two types are merged and treated as Group 0 interrupts. The two lists of interrupts are exported from the platform_def.h. The lists are constructed by adding a list of board specific interrupt ids to a list of ids common to all ARM platforms and Compute sub-systems. This patch also makes some fields of `arm_config` data structure in FVP redundant and these unused fields are removed. Change-Id: Ibc8c087be7a8a6b041b78c2c3bd0c648cd2035d8 --- include/plat/arm/common/arm_config.h | 6 - include/plat/arm/common/arm_def.h | 16 +++ include/plat/arm/common/plat_arm.h | 5 +- .../plat/arm/css/common/aarch64/css_macros.S | 4 +- include/plat/arm/css/common/css_def.h | 10 ++ plat/arm/board/fvp/aarch64/fvp_common.c | 70 +++++------ plat/arm/board/fvp/fvp_pm.c | 12 +- plat/arm/board/fvp/include/platform_def.h | 19 +++ plat/arm/board/fvp/platform.mk | 32 ++++- plat/arm/board/fvp/tsp/tsp-fvp.mk | 3 +- plat/arm/board/juno/include/platform_def.h | 23 ++-- plat/arm/board/juno/platform.mk | 9 +- plat/arm/board/juno/tsp/tsp-juno.mk | 3 +- plat/arm/common/arm_bl31_setup.c | 5 +- plat/arm/common/arm_common.mk | 10 +- plat/arm/common/arm_gicv2.c | 104 ++++++++++++++++ plat/arm/common/arm_gicv3.c | 117 ++++++++++++++++++ plat/arm/common/arm_gicv3_legacy.c | 96 ++++++++++++++ plat/arm/common/arm_pm.c | 8 +- plat/arm/common/tsp/arm_tsp.mk | 7 +- plat/arm/common/tsp/arm_tsp_setup.c | 2 +- plat/arm/css/common/css_common.c | 64 ---------- plat/arm/css/common/css_common.mk | 3 +- plat/arm/css/common/css_pm.c | 12 +- 24 files changed, 481 insertions(+), 159 deletions(-) create mode 100644 plat/arm/common/arm_gicv2.c create mode 100644 plat/arm/common/arm_gicv3.c create mode 100644 plat/arm/common/arm_gicv3_legacy.c delete mode 100644 plat/arm/css/common/css_common.c diff --git a/include/plat/arm/common/arm_config.h b/include/plat/arm/common/arm_config.h index 0b161276c0..24c1f0a104 100644 --- a/include/plat/arm/common/arm_config.h +++ b/include/plat/arm/common/arm_config.h @@ -42,12 +42,6 @@ enum arm_config_flags { }; typedef struct arm_config { - uintptr_t gicd_base; - uintptr_t gicc_base; - uintptr_t gich_base; - uintptr_t gicv_base; - unsigned int max_aff0; - unsigned int max_aff1; unsigned long flags; } arm_config_t; diff --git a/include/plat/arm/common/arm_def.h b/include/plat/arm/common/arm_def.h index 4726d5e532..5c03feb9a4 100644 --- a/include/plat/arm/common/arm_def.h +++ b/include/plat/arm/common/arm_def.h @@ -135,6 +135,22 @@ #define ARM_IRQ_SEC_SGI_6 14 #define ARM_IRQ_SEC_SGI_7 15 +/* + * Define a list of Group 1 Secure and Group 0 interrupts as per GICv3 + * terminology. On a GICv2 system or mode, the lists will be merged and treated + * as Group 0 interrupts. + */ +#define ARM_G1S_IRQS ARM_IRQ_SEC_PHY_TIMER, \ + ARM_IRQ_SEC_SGI_1, \ + ARM_IRQ_SEC_SGI_2, \ + ARM_IRQ_SEC_SGI_3, \ + ARM_IRQ_SEC_SGI_4, \ + ARM_IRQ_SEC_SGI_5, \ + ARM_IRQ_SEC_SGI_7 + +#define ARM_G0_IRQS ARM_IRQ_SEC_SGI_0, \ + ARM_IRQ_SEC_SGI_6 + #define ARM_SHARED_RAM_ATTR ((PLAT_ARM_SHARED_RAM_CACHED ? \ MT_MEMORY : MT_DEVICE) \ | MT_RW | MT_SECURE) diff --git a/include/plat/arm/common/plat_arm.h b/include/plat/arm/common/plat_arm.h index aadf58d838..f0b3ff6753 100644 --- a/include/plat/arm/common/plat_arm.h +++ b/include/plat/arm/common/plat_arm.h @@ -37,7 +37,6 @@ #include #include - /* * Extern declarations common to ARM standard platforms */ @@ -179,7 +178,11 @@ void arm_tsp_early_platform_setup(void); /* * Mandatory functions required in ARM standard platforms */ +void plat_arm_gic_driver_init(void); void plat_arm_gic_init(void); +void plat_arm_gic_cpuif_enable(void); +void plat_arm_gic_cpuif_disable(void); +void plat_arm_gic_pcpu_init(void); void plat_arm_security_setup(void); void plat_arm_pwrc_setup(void); diff --git a/include/plat/arm/css/common/aarch64/css_macros.S b/include/plat/arm/css/common/aarch64/css_macros.S index 2a26eb700c..9f18e09c97 100644 --- a/include/plat/arm/css/common/aarch64/css_macros.S +++ b/include/plat/arm/css/common/aarch64/css_macros.S @@ -41,8 +41,8 @@ * --------------------------------------------- */ .macro plat_print_gic_regs - mov_imm x16, PLAT_CSS_GICD_BASE - mov_imm x17, PLAT_CSS_GICC_BASE + mov_imm x16, PLAT_ARM_GICD_BASE + mov_imm x17, PLAT_ARM_GICC_BASE arm_print_gic_regs .endm diff --git a/include/plat/arm/css/common/css_def.h b/include/plat/arm/css/common/css_def.h index 98b69cb3a4..99491f88be 100644 --- a/include/plat/arm/css/common/css_def.h +++ b/include/plat/arm/css/common/css_def.h @@ -60,6 +60,16 @@ #define CSS_IRQ_TZ_WDOG 86 #define CSS_IRQ_SEC_SYS_TIMER 91 +/* + * Define a list of Group 1 Secure interrupts as per GICv3 terminology. On a + * GICv2 system or mode, the interrupts will be treated as Group 0 interrupts. + */ +#define CSS_G1S_IRQS CSS_IRQ_MHU, \ + CSS_IRQ_GPU_SMMU_0, \ + CSS_IRQ_TZC, \ + CSS_IRQ_TZ_WDOG, \ + CSS_IRQ_SEC_SYS_TIMER + /* * SCP <=> AP boot configuration * diff --git a/plat/arm/board/fvp/aarch64/fvp_common.c b/plat/arm/board/fvp/aarch64/fvp_common.c index 8771e5b4fb..e089405dc3 100644 --- a/plat/arm/board/fvp/aarch64/fvp_common.c +++ b/plat/arm/board/fvp/aarch64/fvp_common.c @@ -30,14 +30,23 @@ #include #include -#include #include #include +#include #include #include #include #include "../fvp_def.h" +#if (FVP_USE_GIC_DRIVER == FVP_GICV2) +extern gicv2_driver_data_t arm_gic_data; +#endif + +/* Defines for GIC Driver build time selection */ +#define FVP_GICV2 1 +#define FVP_GICV3 2 +#define FVP_GICV3_LEGACY 3 + /******************************************************************************* * arm_config holds the characteristics of the differences between the three FVP * platforms (Base, A53_A57 & Foundation). It will be populated during cold boot @@ -110,33 +119,6 @@ const mmap_region_t plat_arm_mmap[] = { ARM_CASSERT_MMAP -#if IMAGE_BL31 || IMAGE_BL32 -/* Array of secure interrupts to be configured by the gic driver */ -const unsigned int irq_sec_array[] = { - ARM_IRQ_SEC_PHY_TIMER, - ARM_IRQ_SEC_SGI_0, - ARM_IRQ_SEC_SGI_1, - ARM_IRQ_SEC_SGI_2, - ARM_IRQ_SEC_SGI_3, - ARM_IRQ_SEC_SGI_4, - ARM_IRQ_SEC_SGI_5, - ARM_IRQ_SEC_SGI_6, - ARM_IRQ_SEC_SGI_7, - FVP_IRQ_TZ_WDOG, - FVP_IRQ_SEC_SYS_TIMER -}; - -void plat_arm_gic_init(void) -{ - arm_gic_init(arm_config.gicc_base, - arm_config.gicd_base, - BASE_GICR_BASE, - irq_sec_array, - ARRAY_SIZE(irq_sec_array)); -} - -#endif - /******************************************************************************* * A single boot loader stack is expected to work on both the Foundation FVP * models and the two flavours of the Base FVP models (AEMv8 & Cortex). The @@ -165,16 +147,28 @@ void fvp_config_setup(void) */ switch (bld) { case BLD_GIC_VE_MMAP: - arm_config.gicd_base = VE_GICD_BASE; - arm_config.gicc_base = VE_GICC_BASE; - arm_config.gich_base = VE_GICH_BASE; - arm_config.gicv_base = VE_GICV_BASE; +#if IMAGE_BL31 || IMAGE_BL32 +#if FVP_USE_GIC_DRIVER == FVP_GICV2 + /* + * If the FVP implements the VE compatible memory map, then the + * GICv2 driver must be included in the build. Update the platform + * data with the correct GICv2 base addresses before it is used + * to initialise the driver. + * + * This update of platform data is temporary and will be removed + * once VE memory map for FVP is no longer supported by Trusted + * Firmware. + */ + arm_gic_data.gicd_base = VE_GICD_BASE; + arm_gic_data.gicc_base = VE_GICC_BASE; + +#else + ERROR("Only GICv2 driver supported for VE memory map\n"); + panic(); +#endif /* __FVP_USE_GIC_DRIVER == FVP_GICV2__ */ +#endif /* __IMAGE_BL31 || IMAGE_BL32__ */ break; case BLD_GIC_A53A57_MMAP: - arm_config.gicd_base = BASE_GICD_BASE; - arm_config.gicc_base = BASE_GICC_BASE; - arm_config.gich_base = BASE_GICH_BASE; - arm_config.gicv_base = BASE_GICV_BASE; break; default: ERROR("Unsupported board build %x\n", bld); @@ -187,8 +181,6 @@ void fvp_config_setup(void) */ switch (hbi) { case HBI_FOUNDATION_FVP: - arm_config.max_aff0 = 4; - arm_config.max_aff1 = 1; arm_config.flags = 0; /* @@ -206,8 +198,6 @@ void fvp_config_setup(void) } break; case HBI_BASE_FVP: - arm_config.max_aff0 = 4; - arm_config.max_aff1 = 2; arm_config.flags |= ARM_CONFIG_BASE_MMAP | ARM_CONFIG_HAS_CCI | ARM_CONFIG_HAS_TZC; diff --git a/plat/arm/board/fvp/fvp_pm.c b/plat/arm/board/fvp/fvp_pm.c index d7b459d78f..c5129a603e 100644 --- a/plat/arm/board/fvp/fvp_pm.c +++ b/plat/arm/board/fvp/fvp_pm.c @@ -30,7 +30,6 @@ #include #include -#include #include #include #include @@ -72,7 +71,7 @@ const unsigned int arm_pm_idle_states[] = { static void fvp_cpu_pwrdwn_common(void) { /* Prevent interrupts from spuriously waking up this cpu */ - arm_gic_cpuif_deactivate(); + plat_arm_gic_cpuif_disable(); /* Program the power controller to power off this cpu. */ fvp_pwrc_write_ppoffr(read_mpidr_el1()); @@ -235,9 +234,10 @@ void fvp_pwr_domain_on_finish(const psci_power_state_t *target_state) fvp_power_domain_on_finish_common(target_state); /* Enable the gic cpu interface */ - arm_gic_cpuif_setup(); - /* Program the gic per-cpu distributor interface */ - arm_gic_pcpu_distif_setup(); + plat_arm_gic_pcpu_init(); + + /* Program the gic per-cpu distributor or re-distributor interface */ + plat_arm_gic_cpuif_enable(); } /******************************************************************************* @@ -259,7 +259,7 @@ void fvp_pwr_domain_suspend_finish(const psci_power_state_t *target_state) fvp_power_domain_on_finish_common(target_state); /* Enable the gic cpu interface */ - arm_gic_cpuif_setup(); + plat_arm_gic_cpuif_enable(); } /******************************************************************************* diff --git a/plat/arm/board/fvp/include/platform_def.h b/plat/arm/board/fvp/include/platform_def.h index 9ada6b2aa3..9cb88de5cb 100644 --- a/plat/arm/board/fvp/include/platform_def.h +++ b/plat/arm/board/fvp/include/platform_def.h @@ -121,5 +121,24 @@ TZC_REGION_ACCESS_RDWR(FVP_NSAID_VIRTIO) | \ TZC_REGION_ACCESS_RDWR(FVP_NSAID_VIRTIO_OLD)) +/* + * GIC related constants to cater for both GICv2 and GICv3 instances of an + * FVP. They could be overriden at runtime in case the FVP implements the legacy + * VE memory map. + */ +#define PLAT_ARM_GICD_BASE BASE_GICD_BASE +#define PLAT_ARM_GICR_BASE BASE_GICR_BASE +#define PLAT_ARM_GICC_BASE BASE_GICC_BASE + +/* + * Define a list of Group 1 Secure and Group 0 interrupts as per GICv3 + * terminology. On a GICv2 system or mode, the lists will be merged and treated + * as Group 0 interrupts. + */ +#define PLAT_ARM_G1S_IRQS ARM_G1S_IRQS, \ + FVP_IRQ_TZ_WDOG, \ + FVP_IRQ_SEC_SYS_TIMER + +#define PLAT_ARM_G0_IRQS ARM_G0_IRQS #endif /* __PLATFORM_DEF_H__ */ diff --git a/plat/arm/board/fvp/platform.mk b/plat/arm/board/fvp/platform.mk index c46d3b7119..b6f0739439 100644 --- a/plat/arm/board/fvp/platform.mk +++ b/plat/arm/board/fvp/platform.mk @@ -28,6 +28,35 @@ # POSSIBILITY OF SUCH DAMAGE. # +# Use the Legacy GICv3 driver on the FVP by default to maintain compatibility. +FVP_USE_GIC_DRIVER := FVP_GICV3_LEGACY + +# The FVP platform depends on this macro to build with correct GIC driver. +$(eval $(call add_define,FVP_USE_GIC_DRIVER)) + +# Choose the GIC sources depending upon the how the FVP will be invoked +ifeq (${FVP_USE_GIC_DRIVER}, FVP_GICV3) +FVP_GIC_SOURCES := drivers/arm/gic/common/gic_common.c \ + drivers/arm/gic/v3/gicv3_main.c \ + drivers/arm/gic/v3/gicv3_helpers.c \ + plat/common/plat_gicv3.c \ + plat/arm/common/arm_gicv3.c +else ifeq (${FVP_USE_GIC_DRIVER}, FVP_GICV2) +FVP_GIC_SOURCES := drivers/arm/gic/common/gic_common.c \ + drivers/arm/gic/v2/gicv2_main.c \ + drivers/arm/gic/v2/gicv2_helpers.c \ + plat/common/plat_gicv2.c \ + plat/arm/common/arm_gicv2.c +else ifeq (${FVP_USE_GIC_DRIVER}, FVP_GICV3_LEGACY) +FVP_GIC_SOURCES := drivers/arm/gic/arm_gic.c \ + drivers/arm/gic/gic_v2.c \ + drivers/arm/gic/gic_v3.c \ + plat/common/plat_gic.c \ + plat/arm/common/arm_gicv3_legacy.c +else +$(error "Incorrect GIC driver chosen on FVP port") +endif + PLAT_INCLUDES := -Iplat/arm/board/fvp/include @@ -62,7 +91,8 @@ BL31_SOURCES += lib/cpus/aarch64/aem_generic.S \ plat/arm/board/fvp/fvp_security.c \ plat/arm/board/fvp/fvp_topology.c \ plat/arm/board/fvp/aarch64/fvp_helpers.S \ - plat/arm/board/fvp/drivers/pwrc/fvp_pwrc.c + plat/arm/board/fvp/drivers/pwrc/fvp_pwrc.c \ + ${FVP_GIC_SOURCES} # Disable the PSCI platform compatibility layer ENABLE_PLAT_COMPAT := 0 diff --git a/plat/arm/board/fvp/tsp/tsp-fvp.mk b/plat/arm/board/fvp/tsp/tsp-fvp.mk index 99db2f4951..54a76fded4 100644 --- a/plat/arm/board/fvp/tsp/tsp-fvp.mk +++ b/plat/arm/board/fvp/tsp/tsp-fvp.mk @@ -31,6 +31,7 @@ # TSP source files specific to FVP platform BL32_SOURCES += plat/arm/board/fvp/fvp_topology.c \ plat/arm/board/fvp/drivers/pwrc/fvp_pwrc.c \ - plat/arm/board/fvp/tsp/fvp_tsp_setup.c + plat/arm/board/fvp/tsp/fvp_tsp_setup.c \ + ${FVP_GIC_SOURCES} include plat/arm/common/tsp/arm_tsp.mk diff --git a/plat/arm/board/juno/include/platform_def.h b/plat/arm/board/juno/include/platform_def.h index 39283c5623..924eb0ab7c 100644 --- a/plat/arm/board/juno/include/platform_def.h +++ b/plat/arm/board/juno/include/platform_def.h @@ -94,17 +94,18 @@ */ /* GIC related constants (no GICR in GIC-400) */ -#define PLAT_CSS_GICD_BASE 0x2c010000 -#define PLAT_CSS_GICR_BASE 0x0 -#define PLAT_CSS_GICC_BASE 0x2c02f000 -#define PLAT_CSS_GICH_BASE 0x2c04f000 -#define PLAT_CSS_GICV_BASE 0x2c06f000 +#define PLAT_ARM_GICD_BASE 0x2c010000 +#define PLAT_ARM_GICC_BASE 0x2c02f000 +#define PLAT_ARM_GICH_BASE 0x2c04f000 +#define PLAT_ARM_GICV_BASE 0x2c06f000 -#define PLAT_CSS_IRQ_SEC_LIST CSS_IRQ_MHU, \ - CSS_IRQ_GPU_SMMU_0, \ - CSS_IRQ_TZC, \ - CSS_IRQ_TZ_WDOG, \ - CSS_IRQ_SEC_SYS_TIMER, \ +/* + * Define a list of Group 1 Secure and Group 0 interrupts as per GICv3 + * terminology. On a GICv2 system or mode, the lists will be merged and treated + * as Group 0 interrupts. + */ +#define PLAT_ARM_G1S_IRQS CSS_G1S_IRQS, \ + ARM_G1S_IRQS, \ JUNO_IRQ_DMA_SMMU, \ JUNO_IRQ_HDLCD0_SMMU, \ JUNO_IRQ_HDLCD1_SMMU, \ @@ -114,6 +115,8 @@ JUNO_IRQ_GPU_SMMU_1, \ JUNO_IRQ_ETR_SMMU +#define PLAT_ARM_G0_IRQS ARM_G0_IRQS + /* * Required ARM CSS SoC based platform porting definitions */ diff --git a/plat/arm/board/juno/platform.mk b/plat/arm/board/juno/platform.mk index 127dcbea86..0212fddea5 100644 --- a/plat/arm/board/juno/platform.mk +++ b/plat/arm/board/juno/platform.mk @@ -28,6 +28,12 @@ # POSSIBILITY OF SUCH DAMAGE. # +JUNO_GIC_SOURCES := drivers/arm/gic/common/gic_common.c \ + drivers/arm/gic/v2/gicv2_main.c \ + drivers/arm/gic/v2/gicv2_helpers.c \ + plat/common/plat_gicv2.c \ + plat/arm/common/arm_gicv2.c + PLAT_INCLUDES := -Iplat/arm/board/juno/include PLAT_BL_COMMON_SOURCES := plat/arm/board/juno/aarch64/juno_helpers.S @@ -44,7 +50,8 @@ BL31_SOURCES += lib/cpus/aarch64/cortex_a53.S \ lib/cpus/aarch64/cortex_a57.S \ lib/cpus/aarch64/cortex_a72.S \ plat/arm/board/juno/juno_pm.c \ - plat/arm/board/juno/juno_security.c + plat/arm/board/juno/juno_security.c \ + ${JUNO_GIC_SOURCES} # Enable workarounds for selected Cortex-A57 erratas. ERRATA_A57_806969 := 0 diff --git a/plat/arm/board/juno/tsp/tsp-juno.mk b/plat/arm/board/juno/tsp/tsp-juno.mk index bb67012f37..2ef964e8be 100644 --- a/plat/arm/board/juno/tsp/tsp-juno.mk +++ b/plat/arm/board/juno/tsp/tsp-juno.mk @@ -28,6 +28,7 @@ # POSSIBILITY OF SUCH DAMAGE. # -BL32_SOURCES += plat/arm/css/common/css_topology.c +BL32_SOURCES += plat/arm/css/common/css_topology.c \ + ${JUNO_GIC_SOURCES} include plat/arm/common/tsp/arm_tsp.mk diff --git a/plat/arm/common/arm_bl31_setup.c b/plat/arm/common/arm_bl31_setup.c index 8682fd19a8..50b98c7000 100644 --- a/plat/arm/common/arm_bl31_setup.c +++ b/plat/arm/common/arm_bl31_setup.c @@ -31,7 +31,6 @@ #include #include #include -#include #include #include #include @@ -200,9 +199,9 @@ void bl31_early_platform_setup(bl31_params_t *from_bl2, ******************************************************************************/ void arm_bl31_platform_setup(void) { - /* Initialize the gic cpu and distributor interfaces */ + /* Initialize the GIC driver, cpu and distributor interfaces */ + plat_arm_gic_driver_init(); plat_arm_gic_init(); - arm_gic_setup(); #if RESET_TO_BL31 /* diff --git a/plat/arm/common/arm_common.mk b/plat/arm/common/arm_common.mk index 1290cef3cc..1336e2307d 100644 --- a/plat/arm/common/arm_common.mk +++ b/plat/arm/common/arm_common.mk @@ -108,15 +108,11 @@ BL2_SOURCES += drivers/arm/tzc400/tzc400.c \ BL31_SOURCES += drivers/arm/cci/cci.c \ drivers/arm/ccn/ccn.c \ - drivers/arm/gic/arm_gic.c \ - drivers/arm/gic/gic_v2.c \ - drivers/arm/gic/gic_v3.c \ drivers/arm/tzc400/tzc400.c \ plat/arm/common/arm_bl31_setup.c \ plat/arm/common/arm_pm.c \ plat/arm/common/arm_security.c \ plat/arm/common/arm_topology.c \ - plat/common/plat_gic.c \ plat/common/aarch64/platform_mp_stack.S \ plat/common/aarch64/plat_psci_common.c @@ -127,9 +123,9 @@ ifneq (${TRUSTED_BOARD_BOOT},0) # Include common TBB sources AUTH_SOURCES := drivers/auth/auth_mod.c \ - drivers/auth/crypto_mod.c \ - drivers/auth/img_parser_mod.c \ - drivers/auth/tbbr/tbbr_cot.c \ + drivers/auth/crypto_mod.c \ + drivers/auth/img_parser_mod.c \ + drivers/auth/tbbr/tbbr_cot.c \ BL1_SOURCES += ${AUTH_SOURCES} BL2_SOURCES += ${AUTH_SOURCES} diff --git a/plat/arm/common/arm_gicv2.c b/plat/arm/common/arm_gicv2.c new file mode 100644 index 0000000000..76f04cd0a3 --- /dev/null +++ b/plat/arm/common/arm_gicv2.c @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2015, ARM Limited and Contributors. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +/****************************************************************************** + * The following functions are defined as weak to allow a platform to override + * the way the GICv2 driver is initialised and used. + *****************************************************************************/ +#pragma weak plat_arm_gic_driver_init +#pragma weak plat_arm_gic_init +#pragma weak plat_arm_gic_cpuif_enable +#pragma weak plat_arm_gic_cpuif_disable +#pragma weak plat_arm_gic_pcpu_init + +/****************************************************************************** + * On a GICv2 system, the Group 1 secure interrupts are treated as Group 0 + * interrupts. + *****************************************************************************/ +const unsigned int g0_interrupt_array[] = { + PLAT_ARM_G1S_IRQS, + PLAT_ARM_G0_IRQS +}; + +/* + * Ideally `arm_gic_data` structure definition should be a `const` but it is + * kept as modifiable for overwriting with different GICD and GICC base when + * running on FVP with VE memory map. + */ +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, +}; + +/****************************************************************************** + * ARM common helper to initialize the GICv2 only driver. + *****************************************************************************/ +void plat_arm_gic_driver_init(void) +{ + gicv2_driver_init(&arm_gic_data); +} + +void plat_arm_gic_init(void) +{ + gicv2_distif_init(); + gicv2_pcpu_distif_init(); + gicv2_cpuif_enable(); +} + +/****************************************************************************** + * ARM common helper to enable the GICv2 CPU interface + *****************************************************************************/ +void plat_arm_gic_cpuif_enable(void) +{ + gicv2_cpuif_enable(); +} + +/****************************************************************************** + * ARM common helper to disable the GICv2 CPU interface + *****************************************************************************/ +void plat_arm_gic_cpuif_disable(void) +{ + gicv2_cpuif_disable(); +} + +/****************************************************************************** + * ARM common helper to initialize the per cpu distributor interface in GICv2 + *****************************************************************************/ +void plat_arm_gic_pcpu_init(void) +{ + gicv2_pcpu_distif_init(); +} diff --git a/plat/arm/common/arm_gicv3.c b/plat/arm/common/arm_gicv3.c new file mode 100644 index 0000000000..33f80183c5 --- /dev/null +++ b/plat/arm/common/arm_gicv3.c @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2015, ARM Limited and Contributors. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include + +/****************************************************************************** + * The following functions are defined as weak to allow a platform to override + * the way the GICv3 driver is initialised and used. + *****************************************************************************/ +#pragma weak plat_arm_gic_driver_init +#pragma weak plat_arm_gic_init +#pragma weak plat_arm_gic_cpuif_enable +#pragma weak plat_arm_gic_cpuif_disable +#pragma weak plat_arm_gic_pcpu_init + +/* The GICv3 driver only needs to be initialized in EL3 */ +uintptr_t rdistif_base_addrs[PLATFORM_CORE_COUNT]; + +/* Array of Group1 secure interrupts to be configured by the gic driver */ +const unsigned int g1s_interrupt_array[] = { + PLAT_ARM_G1S_IRQS +}; + +/* Array of Group0 interrupts to be configured by the gic driver */ +const unsigned int g0_interrupt_array[] = { + PLAT_ARM_G0_IRQS +}; + +const gicv3_driver_data_t arm_gic_data = { + .gicd_base = PLAT_ARM_GICD_BASE, + .gicr_base = PLAT_ARM_GICR_BASE, + .g0_interrupt_num = ARRAY_SIZE(g0_interrupt_array), + .g1s_interrupt_num = ARRAY_SIZE(g1s_interrupt_array), + .g0_interrupt_array = g0_interrupt_array, + .g1s_interrupt_array = g1s_interrupt_array, + .rdistif_num = PLATFORM_CORE_COUNT, + .rdistif_base_addrs = rdistif_base_addrs, + .mpidr_to_core_pos = plat_arm_calc_core_pos +}; + +void plat_arm_gic_driver_init(void) +{ + /* + * The GICv3 driver is initialized in EL3 and does not need + * to be initialized again in SEL1. This is because the S-EL1 + * can use GIC system registers to manage interrupts and does + * not need GIC interface base addresses to be configured. + */ +#if IMAGE_BL31 + gicv3_driver_init(&arm_gic_data); +#endif +} + +/****************************************************************************** + * ARM common helper to initialize the GIC. Only invoked by BL31 + *****************************************************************************/ +void plat_arm_gic_init(void) +{ + gicv3_distif_init(); + gicv3_rdistif_init(plat_my_core_pos()); + gicv3_cpuif_enable(plat_my_core_pos()); +} + +/****************************************************************************** + * ARM common helper to enable the GIC CPU interface + *****************************************************************************/ +void plat_arm_gic_cpuif_enable(void) +{ + gicv3_cpuif_enable(plat_my_core_pos()); +} + +/****************************************************************************** + * ARM common helper to disable the GIC CPU interface + *****************************************************************************/ +void plat_arm_gic_cpuif_disable(void) +{ + gicv3_cpuif_disable(plat_my_core_pos()); +} + +/****************************************************************************** + * ARM common helper to initialize the per-cpu redistributor interface in GICv3 + *****************************************************************************/ +void plat_arm_gic_pcpu_init(void) +{ + gicv3_rdistif_init(plat_my_core_pos()); +} diff --git a/plat/arm/common/arm_gicv3_legacy.c b/plat/arm/common/arm_gicv3_legacy.c new file mode 100644 index 0000000000..617093345b --- /dev/null +++ b/plat/arm/common/arm_gicv3_legacy.c @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2015, ARM Limited and Contributors. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include + +/****************************************************************************** + * The following function is defined as weak to allow a platform to override + * the way the Legacy GICv3 driver is initialised and used. + *****************************************************************************/ +#pragma weak plat_arm_gic_driver_init +#pragma weak plat_arm_gic_init +#pragma weak plat_arm_gic_cpuif_enable +#pragma weak plat_arm_gic_cpuif_disable +#pragma weak plat_arm_gic_pcpu_init + +/* + * In the GICv3 Legacy mode, the Group 1 secure interrupts are treated as Group + * 0 interrupts. + */ +const unsigned int irq_sec_array[] = { + PLAT_ARM_G0_IRQS, + PLAT_ARM_G1S_IRQS +}; + +void plat_arm_gic_driver_init(void) +{ + arm_gic_init(PLAT_ARM_GICC_BASE, + PLAT_ARM_GICD_BASE, + PLAT_ARM_GICR_BASE, + irq_sec_array, + ARRAY_SIZE(irq_sec_array)); +} + +/****************************************************************************** + * ARM common helper to initialize the GIC. + *****************************************************************************/ +void plat_arm_gic_init(void) +{ + arm_gic_setup(); +} + +/****************************************************************************** + * ARM common helper to enable the GIC CPU interface + *****************************************************************************/ +void plat_arm_gic_cpuif_enable(void) +{ + arm_gic_cpuif_setup(); +} + +/****************************************************************************** + * ARM common helper to disable the GIC CPU interface + *****************************************************************************/ +void plat_arm_gic_cpuif_disable(void) +{ + arm_gic_cpuif_deactivate(); +} + +/****************************************************************************** + * ARM common helper to initialize the per-cpu distributor in GICv2 or + * redistributor interface in GICv3. + *****************************************************************************/ +void plat_arm_gic_pcpu_init(void) +{ + arm_gic_pcpu_distif_setup(); +} diff --git a/plat/arm/common/arm_pm.c b/plat/arm/common/arm_pm.c index cae65970fb..bd5820b55e 100644 --- a/plat/arm/common/arm_pm.c +++ b/plat/arm/common/arm_pm.c @@ -164,9 +164,13 @@ void arm_system_pwr_domain_resume(void) /* Assert system power domain is available on the platform */ assert(PLAT_MAX_PWR_LVL >= ARM_PWR_LVL2); - arm_gic_setup(); + /* + * TODO: On GICv3 systems, figure out whether the core that wakes up + * first from system suspend need to initialize the re-distributor + * interface of all the other suspended cores. + */ + plat_arm_gic_init(); plat_arm_security_setup(); - arm_configure_sys_timer(); } diff --git a/plat/arm/common/tsp/arm_tsp.mk b/plat/arm/common/tsp/arm_tsp.mk index f285f58576..691a2ab679 100644 --- a/plat/arm/common/tsp/arm_tsp.mk +++ b/plat/arm/common/tsp/arm_tsp.mk @@ -29,9 +29,6 @@ # # TSP source files common to ARM standard platforms -BL32_SOURCES += drivers/arm/gic/arm_gic.c \ - drivers/arm/gic/gic_v2.c \ - plat/arm/common/arm_topology.c \ +BL32_SOURCES += plat/arm/common/arm_topology.c \ plat/arm/common/tsp/arm_tsp_setup.c \ - plat/common/aarch64/platform_mp_stack.S \ - plat/common/plat_gic.c + plat/common/aarch64/platform_mp_stack.S diff --git a/plat/arm/common/tsp/arm_tsp_setup.c b/plat/arm/common/tsp/arm_tsp_setup.c index 78db1605a9..3631c0335a 100644 --- a/plat/arm/common/tsp/arm_tsp_setup.c +++ b/plat/arm/common/tsp/arm_tsp_setup.c @@ -89,7 +89,7 @@ void tsp_early_platform_setup(void) ******************************************************************************/ void tsp_platform_setup(void) { - plat_arm_gic_init(); + plat_arm_gic_driver_init(); } /******************************************************************************* diff --git a/plat/arm/css/common/css_common.c b/plat/arm/css/common/css_common.c deleted file mode 100644 index 91813f2c18..0000000000 --- a/plat/arm/css/common/css_common.c +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (c) 2015, ARM Limited and Contributors. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of ARM nor the names of its contributors may be used - * to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#include -#include -#include - - -#if IMAGE_BL31 || IMAGE_BL32 - -const unsigned int irq_sec_array[] = { - PLAT_CSS_IRQ_SEC_LIST, - ARM_IRQ_SEC_PHY_TIMER, - ARM_IRQ_SEC_SGI_0, - ARM_IRQ_SEC_SGI_1, - ARM_IRQ_SEC_SGI_2, - ARM_IRQ_SEC_SGI_3, - ARM_IRQ_SEC_SGI_4, - ARM_IRQ_SEC_SGI_5, - ARM_IRQ_SEC_SGI_6, - ARM_IRQ_SEC_SGI_7 -}; - - -/* Weak definitions may be overridden in specific CSS based platform */ -#pragma weak plat_arm_gic_init - -void plat_arm_gic_init(void) -{ - arm_gic_init(PLAT_CSS_GICC_BASE, - PLAT_CSS_GICD_BASE, - PLAT_CSS_GICR_BASE, - irq_sec_array, - ARRAY_SIZE(irq_sec_array)); -} - -#endif /* IMAGE_BL31 || IMAGE_BL32 */ diff --git a/plat/arm/css/common/css_common.mk b/plat/arm/css/common/css_common.mk index 6b05869e52..6394197985 100644 --- a/plat/arm/css/common/css_common.mk +++ b/plat/arm/css/common/css_common.mk @@ -32,8 +32,7 @@ PLAT_INCLUDES += -Iinclude/plat/arm/css/common \ -Iinclude/plat/arm/css/common/aarch64 -PLAT_BL_COMMON_SOURCES += plat/arm/css/common/aarch64/css_helpers.S \ - plat/arm/css/common/css_common.c +PLAT_BL_COMMON_SOURCES += plat/arm/css/common/aarch64/css_helpers.S #BL1_SOURCES += diff --git a/plat/arm/css/common/css_pm.c b/plat/arm/css/common/css_pm.c index 1d9bd59479..6d6646e043 100644 --- a/plat/arm/css/common/css_pm.c +++ b/plat/arm/css/common/css_pm.c @@ -30,7 +30,6 @@ #include #include -#include #include #include #include @@ -127,10 +126,11 @@ void css_pwr_domain_on_finish(const psci_power_state_t *target_state) css_pwr_domain_on_finisher_common(target_state); + /* Program the gic per-cpu distributor or re-distributor interface */ + plat_arm_gic_pcpu_init(); + /* Enable the gic cpu interface */ - arm_gic_cpuif_setup(); - /* Program the gic per-cpu distributor interface */ - arm_gic_pcpu_distif_setup(); + plat_arm_gic_cpuif_enable(); } /******************************************************************************* @@ -145,7 +145,7 @@ static void css_power_down_common(const psci_power_state_t *target_state) uint32_t system_state = scpi_power_on; /* Prevent interrupts from spuriously waking up this cpu */ - arm_gic_cpuif_deactivate(); + plat_arm_gic_cpuif_disable(); /* Check if power down at system power domain level is requested */ if (CSS_SYSTEM_PWR_STATE(target_state) == ARM_LOCAL_STATE_OFF) @@ -213,7 +213,7 @@ void css_pwr_domain_suspend_finish( arm_system_pwr_domain_resume(); else /* Enable the gic cpu interface */ - arm_gic_cpuif_setup(); + plat_arm_gic_cpuif_enable(); css_pwr_domain_on_finisher_common(target_state); } From 4e0e0f44f13f0bbe716a390f0ef5283d217ba154 Mon Sep 17 00:00:00 2001 From: Soby Mathew Date: Mon, 23 Nov 2015 13:58:45 +0000 Subject: [PATCH 6/7] Enable support for EL3 interrupt in IMF This patch enables support for EL3 interrupts in the Interrupt Management Framework (IMF) of ARM Trusted Firmware. Please note that although the registration of the EL3 interrupt type is now supported, it has not been tested on any of the ARM Standard platforms. Change-Id: If4dcdc7584621522a2f3ea13ea9b1ad0a76bb8a1 --- bl31/interrupt_mgmt.c | 16 ++++++++-------- include/bl31/interrupt_mgmt.h | 16 ++++++++++++---- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/bl31/interrupt_mgmt.c b/bl31/interrupt_mgmt.c index 206578b4dc..e9918515ba 100644 --- a/bl31/interrupt_mgmt.c +++ b/bl31/interrupt_mgmt.c @@ -67,18 +67,15 @@ typedef struct intr_type_desc { static intr_type_desc_t intr_type_descs[MAX_INTR_TYPES]; /******************************************************************************* - * This function validates the interrupt type. EL3 interrupts are currently not - * supported. + * This function validates the interrupt type. ******************************************************************************/ static int32_t validate_interrupt_type(uint32_t type) { - if (type == INTR_TYPE_EL3) - return -ENOTSUP; + if (type == INTR_TYPE_S_EL1 || type == INTR_TYPE_NS || + type == INTR_TYPE_EL3) + return 0; - if (type != INTR_TYPE_S_EL1 && type != INTR_TYPE_NS) - return -EINVAL; - - return 0; + return -EINVAL; } /******************************************************************************* @@ -95,6 +92,9 @@ static int32_t validate_routing_model(uint32_t type, uint32_t flags) if (type == INTR_TYPE_NS) return validate_ns_interrupt_rm(flags); + if (type == INTR_TYPE_EL3) + return validate_el3_interrupt_rm(flags); + return -EINVAL; } diff --git a/include/bl31/interrupt_mgmt.h b/include/bl31/interrupt_mgmt.h index e07ddf83bc..0172b607ea 100644 --- a/include/bl31/interrupt_mgmt.h +++ b/include/bl31/interrupt_mgmt.h @@ -63,6 +63,10 @@ #define INTR_NS_VALID_RM0 0x0 /* Routed to EL1/EL2 from NS and to EL3 from Secure */ #define INTR_NS_VALID_RM1 0x1 +/* Routed to EL3 from NS. Taken to S-EL1 from Secure and handed over to EL3 */ +#define INTR_EL3_VALID_RM0 0x2 +/* Routed to EL3 from NS and Secure */ +#define INTR_EL3_VALID_RM1 0x3 /* This is the default routing model */ #define INTR_DEFAULT_RM 0x0 @@ -87,12 +91,16 @@ * of interrupt. If the model does not match one of the valid masks * -EINVAL is returned. ******************************************************************************/ -#define validate_sel1_interrupt_rm(x) (x == INTR_SEL1_VALID_RM0 ? 0 : \ - (x == INTR_SEL1_VALID_RM1 ? 0 :\ +#define validate_sel1_interrupt_rm(x) ((x) == INTR_SEL1_VALID_RM0 ? 0 : \ + ((x) == INTR_SEL1_VALID_RM1 ? 0 :\ -EINVAL)) -#define validate_ns_interrupt_rm(x) (x == INTR_NS_VALID_RM0 ? 0 : \ - (x == INTR_NS_VALID_RM1 ? 0 :\ +#define validate_ns_interrupt_rm(x) ((x) == INTR_NS_VALID_RM0 ? 0 : \ + ((x) == INTR_NS_VALID_RM1 ? 0 :\ + -EINVAL)) + +#define validate_el3_interrupt_rm(x) ((x) == INTR_EL3_VALID_RM0 ? 0 : \ + ((x) == INTR_EL3_VALID_RM1 ? 0 :\ -EINVAL)) /******************************************************************************* From 63b8440fcc3954817e20d3ba7a0be74435a284d2 Mon Sep 17 00:00:00 2001 From: Soby Mathew Date: Fri, 13 Nov 2015 02:08:43 +0000 Subject: [PATCH 7/7] TSP: Allow preemption of synchronous S-EL1 interrupt handling Earlier the TSP only ever expected to be preempted during Standard SMC processing. If a S-EL1 interrupt triggered while in the normal world, it will routed to S-EL1 `synchronously` for handling. The `synchronous` S-EL1 interrupt handler `tsp_sel1_intr_entry` used to panic if this S-EL1 interrupt was preempted by another higher priority pending interrupt which should be handled in EL3 e.g. Group0 interrupt in GICv3. With this patch, the `tsp_sel1_intr_entry` now expects `TSP_PREEMPTED` as the return code from the `tsp_common_int_handler` in addition to 0 (interrupt successfully handled) and in both cases it issues an SMC with id `TSP_HANDLED_S_EL1_INTR`. The TSPD switches the context and returns back to normal world. In case a higher priority EL3 interrupt was pending, the execution will be routed to EL3 where interrupt will be handled. On return back to normal world, the pending S-EL1 interrupt which was preempted will get routed to S-EL1 to be handled `synchronously` via `tsp_sel1_intr_entry`. Change-Id: I2087c7fedb37746fbd9200cdda9b6dba93e16201 --- bl32/tsp/aarch64/tsp_entrypoint.S | 39 +++++++++++++++++++++---------- services/spd/tspd/tspd_main.c | 21 ++++++++++------- 2 files changed, 40 insertions(+), 20 deletions(-) diff --git a/bl32/tsp/aarch64/tsp_entrypoint.S b/bl32/tsp/aarch64/tsp_entrypoint.S index d183dff9fa..531ab9bfad 100644 --- a/bl32/tsp/aarch64/tsp_entrypoint.S +++ b/bl32/tsp/aarch64/tsp_entrypoint.S @@ -327,11 +327,13 @@ endfunc tsp_cpu_suspend_entry /*------------------------------------------------- * This entrypoint is used by the TSPD to pass - * control for handling a pending S-EL1 Interrupt. - * 'x0' contains a magic number which indicates - * this. TSPD expects control to be handed back - * at the end of interrupt processing. This is - * done through an SMC. The handover agreement is: + * control for `synchronously` handling a S-EL1 + * Interrupt which was triggered while executing + * in normal world. 'x0' contains a magic number + * which indicates this. TSPD expects control to + * be handed back at the end of interrupt + * processing. This is done through an SMC. + * The handover agreement is: * * 1. PSTATE.DAIF are set upon entry. 'x1' has * the ELR_EL3 from the non-secure state. @@ -348,8 +350,7 @@ endfunc tsp_cpu_suspend_entry */ func tsp_sel1_intr_entry #if DEBUG - mov x2, #(TSP_HANDLE_SEL1_INTR_AND_RETURN & ~0xffff) - movk x2, #(TSP_HANDLE_SEL1_INTR_AND_RETURN & 0xffff) + mov_imm x2, TSP_HANDLE_SEL1_INTR_AND_RETURN cmp x0, x2 b.ne tsp_sel1_int_entry_panic #endif @@ -362,19 +363,33 @@ func tsp_sel1_intr_entry * IRQ/FIQs are not enabled since that will * complicate the implementation. Execution * will be transferred back to the normal world - * in any case. A non-zero return value from the - * interrupt handler is an error. + * in any case. The handler can return 0 + * if the interrupt was handled or TSP_PREEMPTED + * if the expected interrupt was preempted + * by an interrupt that should be handled in EL3 + * e.g. Group 0 interrupt in GICv3. In both + * the cases switch to EL3 using SMC with id + * TSP_HANDLED_S_EL1_INTR. Any other return value + * from the handler will result in panic. * ------------------------------------------------ */ save_eret_context x2 x3 bl tsp_update_sync_sel1_intr_stats bl tsp_common_int_handler - cbnz x0, tsp_sel1_int_entry_panic + /* Check if the S-EL1 interrupt has been handled */ + cbnz x0, tsp_sel1_intr_check_preemption + b tsp_sel1_intr_return +tsp_sel1_intr_check_preemption: + /* Check if the S-EL1 interrupt has been preempted */ + mov_imm x1, TSP_PREEMPTED + cmp x0, x1 + b.ne tsp_sel1_int_entry_panic +tsp_sel1_intr_return: + mov_imm x0, TSP_HANDLED_S_EL1_INTR restore_eret_context x2 x3 - mov x0, #(TSP_HANDLED_S_EL1_INTR & ~0xffff) - movk x0, #(TSP_HANDLED_S_EL1_INTR & 0xffff) smc #0 + /* Should never reach here */ tsp_sel1_int_entry_panic: b tsp_sel1_int_entry_panic endfunc tsp_sel1_intr_entry diff --git a/services/spd/tspd/tspd_main.c b/services/spd/tspd/tspd_main.c index 4c4861d72a..4b89425891 100644 --- a/services/spd/tspd/tspd_main.c +++ b/services/spd/tspd/tspd_main.c @@ -89,18 +89,22 @@ uint64_t tspd_handle_sp_preemption(void *handle) assert(ns_cpu_context); /* - * Restore non-secure state. The secure system - * register context will be saved when required. + * To allow Secure EL1 interrupt handler to re-enter TSP while TSP + * is preempted, the secure system register context which will get + * overwritten must be additionally saved. This is currently done + * by the TSPD S-EL1 interrupt handler. + */ + + /* + * Restore non-secure state. */ cm_el1_sysregs_context_restore(NON_SECURE); cm_set_next_eret_context(NON_SECURE); /* - * We need to restore non secure context according to - * the SEL1 context which got preempted and currently - * TSP can only be preempted when a STD SMC is ongoing. - * Return SMC_PREEMPTED in x0 and restore non secure - * context. + * The TSP was preempted during STD SMC execution. + * Return back to the normal world with SMC_PREEMPTED as error + * code in x0. */ SMC_RET1(ns_cpu_context, SMC_PREEMPTED); } @@ -327,7 +331,8 @@ uint64_t tspd_smc_handler(uint32_t smc_fid, /* * This function ID is used only by the TSP to indicate that it has - * finished handling a S-EL1 interrupt. Execution should resume + * finished handling a S-EL1 interrupt or was preempted by a higher + * priority pending EL3 interrupt. Execution should resume * in the normal world. */ case TSP_HANDLED_S_EL1_INTR: