| /* |
| * Copyright (c) 2014, 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 <context.h> |
| #include <context_mgmt.h> |
| #include <console.h> |
| #include <debug.h> |
| #include <delay_timer.h> |
| #include <fiq_smp_call.h> |
| #include <log.h> |
| #include <mt_common.h> |
| #include <plat_private.h> |
| #include <platform.h> |
| #include <runtime_svc.h> |
| #include <spinlock.h> |
| #include <stdio.h> |
| #include <xlat_tables.h> |
| |
| #if 0 |
| /* For check mode test case */ |
| #define MTK_AEE_TC_CHECK_MODE |
| #endif |
| |
| #if 0 |
| /* For early exception, coming cpu dump its stack */ |
| #define MTK_AEE_TC_KWDT_NOT_READY |
| #endif |
| |
| #define DCACHE_IS_ENABLE (read_sctlr_el3() & SCTLR_C_BIT) |
| extern atf_arg_t gteearg; |
| extern uint64_t wdt_kernel_cb_addr; |
| extern uint64_t get_kernel_k32_64(void); |
| |
| spinlock_t aee_wdt_dump_lock; |
| extern atf_log_ctrl_t *p_atf_log_ctrl; |
| void aee_flush_log_cache (void) |
| { |
| flush_dcache_range((unsigned long)p_atf_log_ctrl->info.atf_buf_addr, p_atf_log_ctrl->info.atf_buf_size); |
| } |
| void aee_wdt_dump(void __unused *cookie) |
| { |
| struct atf_aee_regs *regs; |
| cpu_context_t *ns_cpu_context; |
| uint64_t mpidr = read_mpidr(); |
| uint32_t linear_id = platform_get_core_pos(mpidr); |
| unsigned long sp_phys, count; |
| uint64_t *pp; |
| uint64_t spsr; |
| int i; |
| uint64_t sp_el0; |
| uint64_t el1_lr; |
| |
| /* save context if from non-secure */ |
| if(read_scr_el3() & SCR_NS_BIT) |
| cm_el1_sysregs_context_save(NON_SECURE); |
| |
| ns_cpu_context = cm_get_context_by_mpidr(mpidr, NON_SECURE); |
| sp_el0 = read_ctx_reg(get_gpregs_ctx(ns_cpu_context), CTX_GPREG_SP_EL0); |
| el1_lr = read_ctx_reg(get_gpregs_ctx(ns_cpu_context), CTX_GPREG_LR); |
| /* This cpu maybe not yet enter EL1 before */ |
| if (el1_lr == 0 && sp_el0 == 0) { |
| NOTICE("EL1_LR and SP_EL0 is 0x0, no need to dump\n"); |
| return; |
| } |
| |
| /* lock aee_wdt_dump_lock for fine print */ |
| if (DCACHE_IS_ENABLE) |
| spin_lock(&aee_wdt_dump_lock); |
| |
| /* Log starts here... */ |
| INFO("%s: on cpu%d\n", __func__, (int)linear_id); |
| |
| /* compatible to the earlier chipset */ |
| #if (defined(MACH_TYPE_MT6735) || defined(MACH_TYPE_MT6735M) || \ |
| defined(MACH_TYPE_MT6753) || defined(MACH_TYPE_MT8173)) |
| atf_arg_t_ptr teearg = (atf_arg_t_ptr)(uintptr_t)TEE_BOOT_INFO_ADDR; |
| #else |
| atf_arg_t_ptr teearg = >eearg; |
| #endif |
| regs = (void *)(teearg->atf_aee_debug_buf_start + (linear_id * sizeof(struct atf_aee_regs))); |
| |
| /* save debug infos */ |
| regs->pstate = SMC_GET_EL3(ns_cpu_context, CTX_SPSR_EL3) |
| regs->pc = SMC_GET_EL3(ns_cpu_context, CTX_ELR_EL3) |
| regs->sp = read_ctx_reg(get_sysregs_ctx(ns_cpu_context), CTX_SP_EL1); |
| for (i=0; i<31; i++) |
| regs->regs[i] = SMC_GET_GP(ns_cpu_context, (CTX_GPREG_X0 + (i<<3))); |
| |
| /* dump debug infos in pretty print */ |
| INFO("(%d) pc:<%016lx> lr:<%016lx> sp:<%016lx> pstate=%lx\n", |
| (int)linear_id, regs->pc, regs->regs[30], regs->sp, regs->pstate); |
| for (i=29; i>=0; i-=3) { |
| INFO("(%d) x%02d: %016lx x%02d: %016lx x%02d: %016lx\n", |
| (int)linear_id, i, regs->regs[i], (i-1), regs->regs[i-1], (i-2), regs->regs[i-2]); |
| } |
| |
| #ifdef MTK_AEE_TC_KWDT_NOT_READY |
| wdt_kernel_cb_addr = 0; |
| #endif |
| |
| if (0 == wdt_kernel_cb_addr) { |
| |
| NOTICE("Kernel WDT not ready. cpu%d\n", (int)linear_id); |
| |
| if (0 == regs->sp) { |
| NOTICE("NULL sp, skip stack dump.\n"); |
| } else { |
| /* physical address of sp */ |
| sp_phys = (regs->sp - 0xffffffc000000000) + 0x40000000; |
| NOTICE("sp_phys:0x%lx\n", sp_phys); |
| |
| NOTICE("sp_phys(aligned):0x%lx\n", (sp_phys & ~(PAGE_SIZE_2MB_MASK))); |
| |
| /* map spphyscial memory for 2MB */ |
| mmap_add_region((sp_phys & ~(PAGE_SIZE_2MB_MASK)), |
| (sp_phys & ~(PAGE_SIZE_2MB_MASK)), |
| PAGE_SIZE_2MB, |
| MT_DEVICE | MT_RW | MT_NS); |
| |
| /* re-fill translation table */ |
| init_xlat_tables(); |
| |
| /* flush sp content */ |
| flush_dcache_range((uint64_t)sp_phys, (uint64_t)0x2000); |
| |
| /* dump 8k */ |
| pp = (uint64_t *)(uintptr_t)sp_phys; |
| count = (0x4000 - (sp_phys &(0x4000-1)))/8; |
| |
| NOTICE("dump sp(16k), count:%ld, mask: 0x3fff\n", count); |
| for(i=0; i<count ; i+=4,pp+=4) |
| INFO("%016lx| %016lx %016lx %016lx %016lx\n",(unsigned long)(uintptr_t)pp, \ |
| (unsigned long)(uintptr_t)*pp, (unsigned long)(uintptr_t)*(pp+1), \ |
| (unsigned long)(uintptr_t)*(pp+2), (unsigned long)(uintptr_t)*(pp+3)); |
| } |
| NOTICE("Wait timeout.\n"); |
| aee_flush_log_cache(); |
| if (DCACHE_IS_ENABLE) |
| spin_unlock(&aee_wdt_dump_lock); |
| while(1); |
| } |
| |
| /* release aee_wdt_dump_lock */ |
| if (DCACHE_IS_ENABLE) |
| spin_unlock(&aee_wdt_dump_lock); |
| |
| /* default enter EL1(64b) or SVC(32b) when enter AEE dump */ |
| spsr = SMC_GET_EL3(ns_cpu_context, CTX_SPSR_EL3); |
| if (LINUX_KERNEL_32 == get_kernel_k32_64()){ |
| spsr = (spsr & ~((MODE32_MASK) << MODE32_SHIFT)); |
| spsr = (spsr | ((MODE32_svc) << MODE32_SHIFT)); |
| } else { |
| spsr = (spsr & ~((MODE_EL_MASK << MODE_EL_SHIFT)|(MODE_SP_MASK << MODE_SP_SHIFT)|(MODE_RW_MASK << MODE_RW_SHIFT))); |
| spsr = (spsr | (MODE_EL1 << MODE_EL_SHIFT)|(MODE_SP_ELX << MODE_SP_SHIFT)|(MODE_RW_64 << MODE_RW_SHIFT)); |
| } |
| |
| /* disable IRQ when enter AEE dump */ |
| spsr = (spsr | (DAIF_IRQ_BIT << SPSR_AIF_SHIFT)); |
| #ifdef MTK_AEE_TC_CHECK_MODE |
| /* ready to make test case */ |
| if (linear_id == 0) { |
| spsr = 0x000000000000008d; |
| SMC_SET_EL3(ns_cpu_context, CTX_SPSR_EL3, spsr); |
| } |
| #else |
| SMC_SET_EL3(ns_cpu_context, CTX_SPSR_EL3, spsr); |
| #endif |
| |
| /* wdt kernel callback addr should be ready now... */ |
| SMC_SET_EL3(ns_cpu_context, CTX_ELR_EL3, wdt_kernel_cb_addr); |
| |
| /* Restore non-secure state */ |
| cm_el1_sysregs_context_restore(NON_SECURE); |
| cm_set_next_eret_context(NON_SECURE); |
| aee_flush_log_cache(); |
| |
| } |
| void aee_wdt_dump_all_core(void __unused *cookie) { |
| mt_fiq_smp_call_function(aee_wdt_dump, 0, 0); |
| aee_wdt_dump(NULL); |
| } |