diff --git a/plat/nvidia/tegra/common/tegra_common.mk b/plat/nvidia/tegra/common/tegra_common.mk index de6cd04e1c550c7c83712cd903497dfa711a1dd9..7f789c57739db22229a82658c7069930db8fa9d2 100644 --- a/plat/nvidia/tegra/common/tegra_common.mk +++ b/plat/nvidia/tegra/common/tegra_common.mk @@ -55,6 +55,7 @@ BL31_SOURCES += drivers/arm/gic/gic_v2.c \ ${COMMON_DIR}/drivers/pmc/pmc.c \ ${COMMON_DIR}/tegra_bl31_setup.c \ ${COMMON_DIR}/tegra_delay_timer.c \ + ${COMMON_DIR}/tegra_fiq_glue.c \ ${COMMON_DIR}/tegra_gic.c \ ${COMMON_DIR}/tegra_pm.c \ ${COMMON_DIR}/tegra_sip_calls.c \ diff --git a/plat/nvidia/tegra/common/tegra_fiq_glue.c b/plat/nvidia/tegra/common/tegra_fiq_glue.c new file mode 100644 index 0000000000000000000000000000000000000000..0c4d82c219645e2c5cfea7427e20c4ca9c2a5adc --- /dev/null +++ b/plat/nvidia/tegra/common/tegra_fiq_glue.c @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2016, ARM Limited and Contributors. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +DEFINE_BAKERY_LOCK(tegra_fiq_lock); + +/******************************************************************************* + * Static variables + ******************************************************************************/ +static uint64_t ns_fiq_handler_addr; +static unsigned int fiq_handler_active; +static pcpu_fiq_state_t fiq_state[PLATFORM_CORE_COUNT]; + +/******************************************************************************* + * Handler for FIQ interrupts + ******************************************************************************/ +static uint64_t tegra_fiq_interrupt_handler(uint32_t id, + uint32_t flags, + void *handle, + void *cookie) +{ + cpu_context_t *ctx = cm_get_context(NON_SECURE); + el3_state_t *el3state_ctx = get_el3state_ctx(ctx); + int cpu = plat_my_core_pos(); + uint32_t irq; + + bakery_lock_get(&tegra_fiq_lock); + + /* + * The FIQ was generated when the execution was in the non-secure + * world. Save the context registers to start with. + */ + cm_el1_sysregs_context_save(NON_SECURE); + + /* + * Save elr_el3 and spsr_el3 from the saved context, and overwrite + * the context with the NS fiq_handler_addr and SPSR value. + */ + fiq_state[cpu].elr_el3 = read_ctx_reg(el3state_ctx, CTX_ELR_EL3); + fiq_state[cpu].spsr_el3 = read_ctx_reg(el3state_ctx, CTX_SPSR_EL3); + + /* + * Set the new ELR to continue execution in the NS world using the + * FIQ handler registered earlier. + */ + assert(ns_fiq_handler_addr); + write_ctx_reg(el3state_ctx, CTX_ELR_EL3, ns_fiq_handler_addr); + + /* + * Mark this interrupt as complete to avoid a FIQ storm. + */ + irq = plat_ic_acknowledge_interrupt(); + if (irq < 1022) + plat_ic_end_of_interrupt(irq); + + bakery_lock_release(&tegra_fiq_lock); + + return 0; +} + +/******************************************************************************* + * Setup handler for FIQ interrupts + ******************************************************************************/ +void tegra_fiq_handler_setup(void) +{ + uint64_t flags; + int rc; + + /* return if already registered */ + if (fiq_handler_active) + return; + + /* + * Register an interrupt handler for FIQ interrupts generated for + * NS interrupt sources + */ + flags = 0; + set_interrupt_rm_flag(flags, NON_SECURE); + rc = register_interrupt_type_handler(INTR_TYPE_S_EL1, + tegra_fiq_interrupt_handler, + flags); + if (rc) + panic(); + + /* handler is now active */ + fiq_handler_active = 1; +} + +/******************************************************************************* + * Validate and store NS world's entrypoint for FIQ interrupts + ******************************************************************************/ +void tegra_fiq_set_ns_entrypoint(uint64_t entrypoint) +{ + ns_fiq_handler_addr = entrypoint; +} + +/******************************************************************************* + * Handler to return the NS EL1/EL0 CPU context + ******************************************************************************/ +int tegra_fiq_get_intr_context(void) +{ + cpu_context_t *ctx = cm_get_context(NON_SECURE); + gp_regs_t *gpregs_ctx = get_gpregs_ctx(ctx); + el1_sys_regs_t *el1state_ctx = get_sysregs_ctx(ctx); + int cpu = plat_my_core_pos(); + uint64_t val; + + /* + * We store the ELR_EL3, SPSR_EL3, SP_EL0 and SP_EL1 registers so + * that el3_exit() sends these values back to the NS world. + */ + write_ctx_reg(gpregs_ctx, CTX_GPREG_X0, fiq_state[cpu].elr_el3); + write_ctx_reg(gpregs_ctx, CTX_GPREG_X1, fiq_state[cpu].spsr_el3); + + val = read_ctx_reg(gpregs_ctx, CTX_GPREG_SP_EL0); + write_ctx_reg(gpregs_ctx, CTX_GPREG_X2, val); + + val = read_ctx_reg(el1state_ctx, CTX_SP_EL1); + write_ctx_reg(gpregs_ctx, CTX_GPREG_X3, val); + + return 0; +} diff --git a/plat/nvidia/tegra/common/tegra_sip_calls.c b/plat/nvidia/tegra/common/tegra_sip_calls.c index 77f039a42291e252f20898c3cc3b689ecbb9112b..ba0e1ef52a268c88037472da066666b71e6078df 100644 --- a/plat/nvidia/tegra/common/tegra_sip_calls.c +++ b/plat/nvidia/tegra/common/tegra_sip_calls.c @@ -42,6 +42,8 @@ * Common Tegra SiP SMCs ******************************************************************************/ #define TEGRA_SIP_NEW_VIDEOMEM_REGION 0x82000003 +#define TEGRA_SIP_FIQ_NS_ENTRYPOINT 0x82000005 +#define TEGRA_SIP_FIQ_NS_GET_CONTEXT 0x82000006 /******************************************************************************* * SoC specific SiP handler @@ -108,6 +110,41 @@ uint64_t tegra_sip_handler(uint32_t smc_fid, SMC_RET1(handle, 0); break; + /* + * The NS world registers the address of its handler to be + * used for processing the FIQ. This is normally used by the + * NS FIQ debugger driver to detect system hangs by programming + * a watchdog timer to fire a FIQ interrupt. + */ + case TEGRA_SIP_FIQ_NS_ENTRYPOINT: + + if (!x1) + SMC_RET1(handle, SMC_UNK); + + /* + * TODO: Check if x1 contains a valid DRAM address + */ + + /* store the NS world's entrypoint */ + tegra_fiq_set_ns_entrypoint(x1); + + SMC_RET1(handle, 0); + break; + + /* + * The NS world's FIQ handler issues this SMC to get the NS EL1/EL0 + * CPU context when the FIQ interrupt was triggered. This allows the + * NS world to understand the CPU state when the watchdog interrupt + * triggered. + */ + case TEGRA_SIP_FIQ_NS_GET_CONTEXT: + + /* retrieve context registers when FIQ triggered */ + tegra_fiq_get_intr_context(); + + SMC_RET0(handle); + break; + default: ERROR("%s: unhandled SMC (0x%x)\n", __func__, smc_fid); break; diff --git a/plat/nvidia/tegra/include/tegra_private.h b/plat/nvidia/tegra/include/tegra_private.h index f5ffa9e0d704b704838f717a5b24f453d1942757..c65bacae73f88e2d89b7bc0a7f2cfea61f7d1ba6 100644 --- a/plat/nvidia/tegra/include/tegra_private.h +++ b/plat/nvidia/tegra/include/tegra_private.h @@ -54,6 +54,14 @@ typedef struct plat_params_from_bl2 { int uart_id; } plat_params_from_bl2_t; +/******************************************************************************* + * Per-CPU struct describing FIQ state to be stored + ******************************************************************************/ +typedef struct pcpu_fiq_state { + uint64_t elr_el3; + uint64_t spsr_el3; +} pcpu_fiq_state_t; + /* Declarations for plat_psci_handlers.c */ int32_t tegra_soc_validate_power_state(unsigned int power_state, psci_power_state_t *req_state); @@ -67,6 +75,11 @@ void plat_gic_setup(void); void plat_secondary_setup(void); int plat_lock_cpu_vectors(void); +/* Declarations for tegra_fiq_glue.c */ +void tegra_fiq_handler_setup(void); +int tegra_fiq_get_intr_context(void); +void tegra_fiq_set_ns_entrypoint(uint64_t entrypoint); + /* Declarations for tegra_gic.c */ void tegra_gic_setup(const unsigned int *irq_sec_ptr, unsigned int num_irqs); void tegra_gic_cpuif_deactivate(void);