diff --git a/bl31/interrupt_mgmt.c b/bl31/interrupt_mgmt.c index 206578b4dce23b98ab951a6b3b4e246565c4f962..e9918515baf0549ad218f74070e3c91469fac675 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) - return -EINVAL; + if (type == INTR_TYPE_S_EL1 || type == INTR_TYPE_NS || + type == INTR_TYPE_EL3) + return 0; - 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/bl32/tsp/aarch64/tsp_entrypoint.S b/bl32/tsp/aarch64/tsp_entrypoint.S index 9732ff2cdf80200e0868ef9fe71500c95818a374..531ab9bfadeb476e14ed2cfae751bfed7a5f33f1 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,15 @@ 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. - * '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: + * 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. @@ -343,40 +345,54 @@ 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_imm x2, TSP_HANDLE_SEL1_INTR_AND_RETURN 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. - * --------------------------------------------- + * 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_fiq_stats - bl tsp_fiq_handler - cbnz x0, tsp_fiq_entry_panic + bl tsp_update_sync_sel1_intr_stats + bl tsp_common_int_handler + /* 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_FIQ & ~0xffff) - movk x0, #(TSP_HANDLED_S_EL1_FIQ & 0xffff) smc #0 -tsp_fiq_entry_panic: - b tsp_fiq_entry_panic -endfunc tsp_fiq_entry + /* Should never reach here */ +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 4c0d4361e76ad726d98dc4fd9a30e997e7e76b55..d5e089f6e8a8a00803ff6c68665e645de82a8d5c 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_irq_received - /* 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 139642d094be530c0f3547c7db3ac05cfb21d653..7654d2e86d9c6924f67c9bab3514ca8d57d66745 100644 --- a/bl32/tsp/tsp_interrupt.c +++ b/bl32/tsp/tsp_interrupt.c @@ -31,50 +31,70 @@ #include <arch_helpers.h> #include <assert.h> #include <debug.h> -#include <gic_v2.h> #include <platform.h> #include <platform_def.h> #include <tsp.h> #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 } +/****************************************************************************** + * 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 - * 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; @@ -82,16 +102,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); @@ -99,29 +124,14 @@ 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; } - -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 39fb5f663a785284293b0a407ec4435d14226fc8..e341cfd7da58c09002f25f55829ce21fe569bf0d 100644 --- a/bl32/tsp/tsp_private.h +++ b/bl32/tsp/tsp_private.h @@ -54,10 +54,14 @@ 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 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 */ @@ -115,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 53707ae9d8259fdf080c1f7298c6f2129ea98e20..271cd921280a607da3e41d9b07a8ff4873099353 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.  -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 bcdc645d3472424f2714236650943da7db9b4c5c..716ed7d877f364a0f39c7622cc83a45ef07e90b8 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/drivers/arm/gic/v3/gicv3_helpers.c b/drivers/arm/gic/v3/gicv3_helpers.c index 6e8251d5306c88ce79ebe0e933213873b36361ba..2fb98cbbd86627ac81134c16efe4af749dacec2a 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 06311e3a97d1b6adb0b56f93612a89a729bffc7b..d5cd0ed9066553da26d980265b3536a479a597d5 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/bl31/interrupt_mgmt.h b/include/bl31/interrupt_mgmt.h index e07ddf83bc159b0c6d8ff4996d3fa31b8a598fbf..0172b607ea36482a2fcfd600a11efe48aa6f309c 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 :\ -EINVAL)) -#define validate_ns_interrupt_rm(x) (x == INTR_NS_VALID_RM0 ? 0 : \ - (x == INTR_NS_VALID_RM1 ? 0 :\ +#define validate_el3_interrupt_rm(x) ((x) == INTR_EL3_VALID_RM0 ? 0 : \ + ((x) == INTR_EL3_VALID_RM1 ? 0 :\ -EINVAL)) /******************************************************************************* diff --git a/include/bl32/tsp/tsp.h b/include/bl32/tsp/tsp.h index c6578b788c35b9a622ea504e62f13fa43ef5c778..fd43fd3b33fcb8af74c936f0c6602a6e72b0b94e 100644 --- a/include/bl32/tsp/tsp.h +++ b/include/bl32/tsp/tsp.h @@ -45,12 +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_EL3_FIQ 0xf2000007 +#define TSP_HANDLED_S_EL1_INTR 0xf2000006 /* SMC function ID that TSP uses to request service from secure monitor */ #define TSP_GET_ARGS 0xf2001000 @@ -63,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 @@ -115,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/include/drivers/arm/gicv3.h b/include/drivers/arm/gicv3.h index e874f5cd3104aea7cdacd358913bcb4bf922f761..ae6fd91752a02b91ff584cc61e1ec333cbd326f5 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 diff --git a/include/plat/arm/common/aarch64/arm_macros.S b/include/plat/arm/common/aarch64/arm_macros.S index 594b0965928ea2704341718824ed6fc0a55a6b14..eaaa62fe1ca9e467e31e5affeb3d475b5b6b6ac6 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 <cci.h> -#include <gic_v2.h> +#include <gic_common.h> +#include <gicv2.h> +#include <gicv3.h> #include <platform_def.h> .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/include/plat/arm/common/arm_config.h b/include/plat/arm/common/arm_config.h index 0b161276c0a0e0451dcaf6838e8183ac42e5b7be..24c1f0a1044ab2844e0b5538661af137132c56da 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 4726d5e532f7c2abb24de336f68fffae98976359..5c03feb9a414bb69e5cbe1609feac13f8136daa2 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 aadf58d838b26846f45e73226b658092372e4fde..f0b3ff6753cc3725199dcc388e492151cd4e730b 100644 --- a/include/plat/arm/common/plat_arm.h +++ b/include/plat/arm/common/plat_arm.h @@ -37,7 +37,6 @@ #include <stdint.h> #include <xlat_tables.h> - /* * 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 2a26eb700ce7c1484718b9d76281ea464e0a69e1..9f18e09c97dadf25b79e279568a650f880f373c7 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 98b69cb3a47045ef1fb194e3a72612c8f6ecdd86..99491f88bee2597ad6dbdd62155136af761a7079 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 8771e5b4fb62ab4e20b6df560284b064e9488894..e089405dc3967c0294aa396b05838e49975b2163 100644 --- a/plat/arm/board/fvp/aarch64/fvp_common.c +++ b/plat/arm/board/fvp/aarch64/fvp_common.c @@ -30,14 +30,23 @@ #include <arm_config.h> #include <arm_def.h> -#include <arm_gic.h> #include <cci.h> #include <debug.h> +#include <gicv2.h> #include <mmio.h> #include <plat_arm.h> #include <v2m_def.h> #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/aarch64/fvp_helpers.S b/plat/arm/board/fvp/aarch64/fvp_helpers.S index 2c24e61f95c09874db837a3ef21c9e34465b94ad..338d15813cace5e745b5c9bfc857aee3bb9632c2 100644 --- a/plat/arm/board/fvp/aarch64/fvp_helpers.S +++ b/plat/arm/board/fvp/aarch64/fvp_helpers.S @@ -30,7 +30,8 @@ #include <arch.h> #include <asm_macros.S> -#include <gic_v2.h> +#include <gicv2.h> +#include <gicv3.h> #include <platform_def.h> #include <v2m_def.h> #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 21ad14fa5e8c52d846dc973825ca66a00d6fdace..c5129a603ee058a812faa3c96b6a90622a7e3064 100644 --- a/plat/arm/board/fvp/fvp_pm.c +++ b/plat/arm/board/fvp/fvp_pm.c @@ -30,7 +30,6 @@ #include <arch_helpers.h> #include <arm_config.h> -#include <arm_gic.h> #include <assert.h> #include <debug.h> #include <errno.h> @@ -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()); @@ -93,6 +92,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,43 +231,13 @@ 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(); + plat_arm_gic_pcpu_init(); - /* TODO: This setup is needed only after a cold boot */ - arm_gic_pcpu_distif_setup(); + /* Program the gic per-cpu distributor or re-distributor interface */ + plat_arm_gic_cpuif_enable(); } /******************************************************************************* @@ -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 */ + plat_arm_gic_cpuif_enable(); } /******************************************************************************* diff --git a/plat/arm/board/fvp/include/plat_macros.S b/plat/arm/board/fvp/include/plat_macros.S index 2feffbe521c47b94297b1a1339ac0e8eb43f4186..2ed0d85e9f1887c14fdb6788067bc0a2bfd8e95f 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/board/fvp/include/platform_def.h b/plat/arm/board/fvp/include/platform_def.h index 9ada6b2aa33672299703108a3b7775eaff873e67..9cb88de5cb4e07391493863e48ae1e2ede13ee38 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 c46d3b71194735cb5b9278cb9ba5d92381d25082..b6f073943908367a94057845931fe95f8538b1c7 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 99db2f495127a79504b6e10867ee9a5552c203b6..54a76fded4337ed73a74f6f9500c5711dd7c8e6d 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 39283c562379300874066e71171ead3a27c45f83..924eb0ab7c6be9e6e8d836f9d13f12f5a89a560a 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_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 PLAT_ARM_GICD_BASE 0x2c010000 +#define PLAT_ARM_GICC_BASE 0x2c02f000 +#define PLAT_ARM_GICH_BASE 0x2c04f000 +#define PLAT_ARM_GICV_BASE 0x2c06f000 + +/* + * 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 127dcbea86622caa224e98eeb9238372d5f984ee..0212fddea54b89e785c08f69412e8d868ed173a6 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 bb67012f3719db8261967a2dac742eb8f29efe34..2ef964e8beae4b9bb84150a93503dd3e7244b86b 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 8682fd19a88de10af8b5ea8da5be662ad58d809d..50b98c700048552a8e3aebc0585d245e0cb10333 100644 --- a/plat/arm/common/arm_bl31_setup.c +++ b/plat/arm/common/arm_bl31_setup.c @@ -31,7 +31,6 @@ #include <arch.h> #include <arch_helpers.h> #include <arm_def.h> -#include <arm_gic.h> #include <assert.h> #include <bl_common.h> #include <cci.h> @@ -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 1290cef3cc45f83311e3653692817e85abccc56a..1336e2307d8f8d95c3d525ce804fdf0fccc3a59b 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 0000000000000000000000000000000000000000..76f04cd0a3db7233f002edf517483403037dba8d --- /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 <gicv2.h> +#include <plat_arm.h> +#include <platform.h> +#include <platform_def.h> + +/****************************************************************************** + * 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 0000000000000000000000000000000000000000..33f80183c557e9e8c355771367f9e37195af9f69 --- /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 <arm_def.h> +#include <gicv3.h> +#include <plat_arm.h> +#include <platform.h> +#include <platform_def.h> + +/****************************************************************************** + * 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 0000000000000000000000000000000000000000..617093345b6e1c86620ea7f995bf788174138266 --- /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 <arm_def.h> +#include <arm_gic.h> +#include <plat_arm.h> +#include <platform.h> +#include <platform_def.h> + +/****************************************************************************** + * 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 cae65970fb66ee189e82c35b8fc709d570368c20..bd5820b55ec226c2ff043d9c355bc89c29ff2ad5 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 f285f58576602ae7baf40b3e0a519e8eae578213..691a2ab679f983dcf500b706a1fa71b3b4b5c5fd 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 78db1605a9fe6ce82206a127c9c5422f3bc7fdad..3631c0335a0c6886d462cec747b30ea61efbd075 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 91813f2c18cf2b2da40840f54980d3ab972218c0..0000000000000000000000000000000000000000 --- 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 <arm_gic.h> -#include <bl_common.h> -#include <platform_def.h> - - -#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 6b05869e5249571163a64754f52bd023052e2217..63941979850bd00ebec01ea5d080be13a3b91836 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 3f468570e164d4fa849026bc6cbeb1f5966ef6dd..6d6646e0438325f5dfbed7947512afa193c3dfb1 100644 --- a/plat/arm/css/common/css_pm.c +++ b/plat/arm/css/common/css_pm.c @@ -30,7 +30,6 @@ #include <arch_helpers.h> #include <assert.h> -#include <arm_gic.h> #include <cassert.h> #include <cci.h> #include <css_pm.h> @@ -41,6 +40,12 @@ #include <platform_def.h> #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,51 +98,39 @@ 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); + css_pwr_domain_on_finisher_common(target_state); - 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; - } + /* Program the gic per-cpu distributor or re-distributor interface */ + plat_arm_gic_pcpu_init(); /* Enable the gic cpu interface */ - arm_gic_cpuif_setup(); - - /* todo: Is this setup only needed after a cold boot? */ - arm_gic_pcpu_distif_setup(); + plat_arm_gic_cpuif_enable(); } /******************************************************************************* @@ -152,21 +145,14 @@ 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(); - - 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) + 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) 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 */ + plat_arm_gic_cpuif_enable(); + + 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 0000000000000000000000000000000000000000..65f89dc6a8dcbbd9c0c568e58957a38f0023f318 --- /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 <assert.h> +#include <gic_common.h> +#include <gicv2.h> +#include <interrupt_mgmt.h> + +/* + * 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 0000000000000000000000000000000000000000..249caf8e578f94676d3b1a67f3736685408dddcf --- /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 <arch_helpers.h> +#include <assert.h> +#include <bl_common.h> +#include <cassert.h> +#include <gic_common.h> +#include <gicv3.h> +#include <interrupt_mgmt.h> +#include <platform.h> + +#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 diff --git a/services/spd/tspd/tspd.mk b/services/spd/tspd/tspd.mk index 139c7d777b3b0f1f1494ddd887e523f85f61f24d..ede25047729fc6c002513024f1cc8b7fc6c956c5 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 622316014990a24134c52994c69b80fcd28d9bdc..4b89425891df501de32f7c5baac5e60e24082374 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 */ @@ -82,18 +89,30 @@ 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); + /* + * 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); } + /******************************************************************************* * 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, @@ -121,44 +140,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, @@ -312,10 +331,11 @@ 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 or was preempted by a higher + * priority pending EL3 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); @@ -332,7 +352,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. @@ -356,35 +376,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 @@ -422,7 +413,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 @@ -438,8 +429,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 @@ -561,7 +551,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. @@ -592,7 +582,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 @@ -635,7 +625,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 5089420d0cf017ce3b9a2610d86d16dd6c57e7b6..55562ba453fb691abe07be86bf24e781fcfe1897 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 5f6fb2b754527f63ad5b99cb43d0de07e1231697..cadc6aaaa271cb757e20320734c7586f1f9ce1a2 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;