From 9d28687b6f91ff39c377880b72c8c1a2dcbdcc2c Mon Sep 17 00:00:00 2001 From: Abdelatif Guettouche Date: Thu, 12 Nov 2020 09:45:00 +0000 Subject: [PATCH] arch/xtensa: Print backtrace on assertions. Signed-off-by: Abdelatif Guettouche --- arch/xtensa/Kconfig | 14 +++ arch/xtensa/src/common/xtensa.h | 4 + arch/xtensa/src/common/xtensa_backtrace.S | 103 ++++++++++++++++++ arch/xtensa/src/common/xtensa_dumpstate.c | 115 ++++++++++++++++++++- arch/xtensa/src/esp32/Make.defs | 5 +- arch/xtensa/src/esp32/hardware/esp32_soc.h | 33 ++++++ 6 files changed, 270 insertions(+), 4 deletions(-) create mode 100644 arch/xtensa/src/common/xtensa_backtrace.S diff --git a/arch/xtensa/Kconfig b/arch/xtensa/Kconfig index 250384c020..5ef6d021e2 100644 --- a/arch/xtensa/Kconfig +++ b/arch/xtensa/Kconfig @@ -94,6 +94,20 @@ config XTENSA_CP_INITSET is provided by CONFIG_XTENSA_CP_INITSET. Each bit corresponds to one coprocessor with the same bit layout as for the CPENABLE register. +config XTENSA_DUMPBT_ON_ASSERT + bool "Dump backtrace on assertions" + default y + depends on DEBUG_ALERT + ---help--- + Enable a backtrace dump on assertions. + +config XTENSA_BTDEPTH + int "Backtrace depth" + default 50 + depends on XTENSA_DUMPBT_ON_ASSERT + ---help--- + This is the depth of the backtrace. + config XTENSA_USE_SEPARATE_IMEM bool "Use a separate heap for internal memory" default n diff --git a/arch/xtensa/src/common/xtensa.h b/arch/xtensa/src/common/xtensa.h index 27f2de96f3..5b770a4880 100644 --- a/arch/xtensa/src/common/xtensa.h +++ b/arch/xtensa/src/common/xtensa.h @@ -361,5 +361,9 @@ void xtensa_pminitialize(void); void up_stack_color(FAR void *stackbase, size_t nbytes); #endif +#ifdef CONFIG_XTENSA_DUMPBT_ON_ASSERT +void xtensa_backtrace_start(uint32_t *pc, uint32_t *sp, uint32_t *next_pc); +#endif + #endif /* __ASSEMBLY__ */ #endif /* __ARCH_XTENSA_SRC_COMMON_XTENSA_H */ diff --git a/arch/xtensa/src/common/xtensa_backtrace.S b/arch/xtensa/src/common/xtensa_backtrace.S new file mode 100644 index 0000000000..f436046604 --- /dev/null +++ b/arch/xtensa/src/common/xtensa_backtrace.S @@ -0,0 +1,103 @@ +/**************************************************************************** + * arch/xtensa/src/common/xtensa_dumpstate.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include + +#include "xtensa_abi.h" + +/* + * This is how the stack looks like when calling the function below: + * + * High Addr + * .................. + * | i-3 BSA | + * | i-1 locals | Function B + * .................. i-1 SP + * | i-2 BSA | + * | i locals | Function A (Start of backtrace) + * ------------------ i SP + * | i-1 BSA | + * | i+1 locals | Backtracing function (e.g. xtensa_btdump()) + * ------------------ i+1 SP + * | i BSA | + * | i+2 locals | xtensa_backtrace_start() + * ------------------ i+2 SP + * | i+1 BSA | + * | i+3 locals | xtensa_window_spill() + * ------------------ i+3 SP + * .................. Low Addr + */ + +/**************************************************************************** + * Name: xtensa_backtrace_start + * + * Description: + * Get the first frame of the current stack's backtrace + * + * Given the following function call flow (B -> A -> X -> xtensa_backtrace_start), + * this function will do the following: + * - Flush CPU registers and window frames onto the current stack + * - Return PC and SP of function A (i.e. start of the stack's backtrace) + * - Return PC of function B (i.e. next_pc) + * + * Input Parameters: + * pc - PC of the first frame in the backtrace + * sp - SP of the first frame in the backtrace + * next_pc - PC of the first frame's caller + * + * C callable as: + * void xtensa_backtrace_start(uint32_t *pc, uint32_t *sp, uint32_t *next_pc) + * + ****************************************************************************/ + + .section .iram1, "ax" + .global xtensa_backtrace_start + .type xtensa_backtrace_start, @function + .align 4 + +xtensa_backtrace_start: + + ENTRY(32) + + call8 xtensa_window_spill /* Spill registers onto stack (excluding this + * function) */ + + /* a2, a3, a4 should be out arguments for i SP, i PC, i-1 PC respectively. + * Use a5 and a6 as scratch. + */ + + l32e a5, sp, -16 /* Get i PC, which is ret addres of i+1 */ + s32i a5, a2, 0 /* Store i PC to arg *pc */ + l32e a6, sp, -12 /* Get i+1 SP. Used to access i BS */ + l32e a5, a6, -12 /* Get i SP */ + s32i a5, a3, 0 /* Store i SP to arg *sp */ + l32e a5, a6, -16 /* Get i-1 PC, which is ret address of i */ + s32i a5, a4, 0 /* Store i-1 PC to arg *next_pc */ + RET0 + + .size xtensa_backtrace_start, . - xtensa_backtrace_start + diff --git a/arch/xtensa/src/common/xtensa_dumpstate.c b/arch/xtensa/src/common/xtensa_dumpstate.c index 85461760a6..7b31d325e3 100644 --- a/arch/xtensa/src/common/xtensa_dumpstate.c +++ b/arch/xtensa/src/common/xtensa_dumpstate.c @@ -32,12 +32,12 @@ #include #include +#include #include #include - #include "sched/sched.h" #include "xtensa.h" -#include "chip_macros.h" +#include "chip_memory.h" #ifdef CONFIG_DEBUG_ALERT @@ -150,6 +150,109 @@ static inline void xtensa_registerdump(void) #endif } +#ifdef CONFIG_XTENSA_DUMPBT_ON_ASSERT + +/**************************************************************************** + * Name: xtensa_getcause + ****************************************************************************/ + +static inline uint32_t xtensa_getcause(void) +{ + uint32_t cause; + + __asm__ __volatile__ + ( + "rsr %0, EXCCAUSE" : "=r"(cause) + ); + + return cause; +} + +/**************************************************************************** + * Name: stackpc + ****************************************************************************/ + +static inline uint32_t stackpc(uint32_t pc) +{ + if (pc & 0x80000000) + { + /* Top two bits of a0 (return address) specify window increment. + * Overwrite to map to address space. + */ + + pc = (pc & 0x3fffffff) | 0x40000000; + } + + /* Minus 3 to get PC of previous instruction (i.e. instruction executed + * before return address). + */ + + return pc - 3; +} + +/**************************************************************************** + * Name: corruptedframe + ****************************************************************************/ + +static inline bool corruptedframe(uint32_t pc, uint32_t sp) +{ + return !(xtensa_ptr_exec((void *)stackpc(pc)) || xtensa_sp_sane(sp)); +} + +/**************************************************************************** + * Name: nextframe + ****************************************************************************/ + +static bool nextframe(uint32_t *pc, uint32_t *sp, uint32_t *npc) +{ + /* Use frame(i - 1)'s base save area located below frame(i)'s sp to get + * frame(i - 1)'s sp and frame(i - 2)'s pc. Base save area consists of + * 4 words under SP. + */ + + void *bsa = (void *)*sp; + + *pc = *npc; + *npc = *((uint32_t *)(bsa - 16)); + *sp = *((uint32_t *)(bsa - 12)); + + return !corruptedframe(*pc, *sp); +} + +/**************************************************************************** + * Name: xtensa_btdump + ****************************************************************************/ + +static inline void xtensa_btdump(void) +{ + uint32_t pc; + uint32_t sp; + uint32_t npc; + int i; + bool corrupted = false; + + xtensa_backtrace_start(&pc, &sp, &npc); + + _alert("Backtrace0: %x:%x\n", stackpc(pc), sp); + + corrupted = corruptedframe(pc, sp) && + !(xtensa_getcause() == EXCCAUSE_INSTR_PROHIBITED); + + for (i = 1; i <= CONFIG_XTENSA_BTDEPTH && npc != 0 && !corrupted; i++) + { + if (!nextframe(&pc, &sp, &npc)) + { + corrupted = true; + } + + _alert("Backtrace%d: %x:%x\n", i, stackpc(pc), sp); + } + + _alert("BACKTRACE %s\n", + (corrupted ? "CORRUPTED!" : (npc == 0 ? "Done":"CONTINUES..."))); +} +#endif /* CONFIG_XTENSA_DUMPBT_ON_ASSERT */ + /**************************************************************************** * Public Functions ****************************************************************************/ @@ -179,6 +282,12 @@ void xtensa_dumpstate(void) xtensa_registerdump(); + /* Dump the backtrace */ + +#ifdef CONFIG_XTENSA_DUMPBT_ON_ASSERT + xtensa_btdump(); +#endif + /* Get the limits on the user stack memory */ ustackbase = (uint32_t)rtcb->adj_stack_ptr; @@ -267,4 +376,4 @@ void xtensa_dumpstate(void) up_showtasks(); } -#endif /* CONFIG_ARCH_STACKDUMP */ +#endif /* CONFIG_DEBUG_ALERT */ diff --git a/arch/xtensa/src/esp32/Make.defs b/arch/xtensa/src/esp32/Make.defs index b60cd80cfd..c53ce20fee 100644 --- a/arch/xtensa/src/esp32/Make.defs +++ b/arch/xtensa/src/esp32/Make.defs @@ -65,6 +65,10 @@ ifeq ($(CONFIG_DEBUG_ALERT),y) CMN_CSRCS += xtensa_dumpstate.c endif +ifeq ($(CONFIG_XTENSA_DUMPBT_ON_ASSERT),y) + CMN_ASRCS += xtensa_backtrace.S +endif + ifeq ($(CONFIG_SPINLOCK),y) CMN_CSRCS += xtensa_testset.c endif @@ -165,7 +169,6 @@ CHIP_CSRCS += esp32_wtd.c endif endif - ifeq ($(CONFIG_ARCH_USE_MODULE_TEXT),y) CHIP_CSRCS += esp32_modtext.c CMN_ASRCS += xtensa_loadstore.S diff --git a/arch/xtensa/src/esp32/hardware/esp32_soc.h b/arch/xtensa/src/esp32/hardware/esp32_soc.h index 214108ea04..12baf3e7f0 100644 --- a/arch/xtensa/src/esp32/hardware/esp32_soc.h +++ b/arch/xtensa/src/esp32/hardware/esp32_soc.h @@ -794,6 +794,19 @@ extern int rom_i2c_writeReg(int block, int block_id, int reg_add, * Inline Functions ****************************************************************************/ +/**************************************************************************** + * Name: esp32_sp_dram + * + * Description: + * Check if the stack pointer is in DRAM. + * + ****************************************************************************/ + +static inline bool IRAM_ATTR esp32_sp_dram(uint32_t sp) +{ + return (sp >= SOC_DRAM_LOW + 0x10 && sp < SOC_DRAM_HIGH - 0x10); +} + /**************************************************************************** * Name: esp32_ptr_extram * @@ -808,4 +821,24 @@ static inline bool IRAM_ATTR esp32_ptr_extram(const void *p) (intptr_t)p < SOC_EXTRAM_DATA_HIGH); } +/**************************************************************************** + * Name: esp32_ptr_exec + * + * Description: + * Check if the pointer is within an executable range. + * + ****************************************************************************/ + +static inline bool IRAM_ATTR esp32_ptr_exec(const void *p) +{ + intptr_t ip = (intptr_t)p; + return (ip >= SOC_IROM_LOW && ip < SOC_IROM_HIGH) + || (ip >= SOC_IRAM_LOW && ip < SOC_IRAM_HIGH) + || (ip >= SOC_IROM_MASK_LOW && ip < SOC_IROM_MASK_HIGH) +#if defined(SOC_CACHE_APP_LOW) && !defined(CONFIG_SMP) + || (ip >= SOC_CACHE_APP_LOW && ip < SOC_CACHE_APP_HIGH) +#endif + || (ip >= SOC_RTC_IRAM_LOW && ip < SOC_RTC_IRAM_HIGH); +} + #endif /* __ARCH_XTENSA_SRC_ESP32_HARDWARE_ESP32_SOC_H */