diff --git a/components/esp8266/include/driver/soc.h b/components/esp8266/include/driver/soc.h index c319b0f7..ac3e330d 100644 --- a/components/esp8266/include/driver/soc.h +++ b/components/esp8266/include/driver/soc.h @@ -134,6 +134,21 @@ static inline void soc_wait_int(void) ); } +static inline uint32_t soc_debug_reason(void) +{ + uint32_t tmp; + + __asm__ __volatile__( + "movi %0, 0\n" + "wsr %0, dbreakc0\n" + "rsr.debugcause %0\n" + : "=r"(tmp) + : + : "memory"); + + return tmp; +} + #ifdef __cplusplus } #endif diff --git a/components/esp8266/source/backtrace.c b/components/esp8266/source/backtrace.c index dfbed099..053ca771 100644 --- a/components/esp8266/source/backtrace.c +++ b/components/esp8266/source/backtrace.c @@ -57,7 +57,12 @@ int xt_retaddr_callee(const void *i_pc, const void *i_sp, const void *i_lr, void } sp -= stk_size; - pc = *(uint32_t *)(sp - 4); + + if (off <= 3) { + pc = lr; + } else { + pc = *(uint32_t *)(sp - 4); + } *o_sp = (void *)sp; *o_pc = (void *)pc; diff --git a/components/freertos/Kconfig b/components/freertos/Kconfig index 9515e8ae..10ec69ac 100644 --- a/components/freertos/Kconfig +++ b/components/freertos/Kconfig @@ -173,4 +173,19 @@ config FREERTOS_RUN_TIME_STATS_USING_CPU_CLK endchoice +config FREERTOS_WATCHPOINT_END_OF_STACK + bool "Set a debug watchpoint as a stack overflow check" + default y + help + FreeRTOS can check if a stack has overflown its bounds by checking either the value of + the stack pointer or by checking the integrity of canary bytes. (See FREERTOS_CHECK_STACKOVERFLOW + for more information.) These checks only happen on a context switch, and the situation that caused + the stack overflow may already be long gone by then. This option will use the debug memory + watchpoint 0 to allow breaking into the debugger (or panic'ing) as soon as any + of the last 16 bytes on the stack of a task are overwritten. + + This check only triggers if the stack overflow writes within 4 bytes of the end of the stack, rather than + overshooting further, so it is worth combining this approach with one of the other stack overflow check + methods. + endmenu diff --git a/components/freertos/port/esp8266/include/freertos/FreeRTOSConfig.h b/components/freertos/port/esp8266/include/freertos/FreeRTOSConfig.h index 6fadd7d2..bc7f3115 100644 --- a/components/freertos/port/esp8266/include/freertos/FreeRTOSConfig.h +++ b/components/freertos/port/esp8266/include/freertos/FreeRTOSConfig.h @@ -72,7 +72,10 @@ #define INCLUDE_xTaskGetIdleTaskHandle 1 #define INCLUDE_xTimerGetTimerDaemonTaskHandle 1 +#ifndef CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK #define configCHECK_FOR_STACK_OVERFLOW 2 +#endif + #define configUSE_MUTEXES 1 #define configUSE_RECURSIVE_MUTEXES 1 #define configUSE_COUNTING_SEMAPHORES 1 diff --git a/components/freertos/port/esp8266/os_cpu_a.S b/components/freertos/port/esp8266/os_cpu_a.S index 101a09f7..e0ba9a29 100644 --- a/components/freertos/port/esp8266/os_cpu_a.S +++ b/components/freertos/port/esp8266/os_cpu_a.S @@ -97,6 +97,18 @@ _xt_int_exit: l32i sp, sp, 0 l32i sp, sp, 0 +#ifdef CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK + movi a2, pxCurrentTCB + l32i a2, a2, 0 + l32i a2, a2, 48 + movi a4, ~(0xf) + and a4, a4, a2 + wsr a4, dbreaka0 + + movi a3, 0xc0000030 + wsr a3, dbreakc0 +#endif + /* We come here only if there was no context switch, that is if this is a nested interrupt or the interrupted task was not preempted. @@ -229,6 +241,18 @@ _xt_enter_first_task: l32i sp, sp, 0 l32i sp, sp, 0 +#ifdef CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK + movi a2, pxCurrentTCB + l32i a2, a2, 0 + l32i a2, a2, 48 + movi a4, ~(0xf) + and a4, a4, a2 + wsr a4, dbreaka0 + + movi a3, 0xc0000030 + wsr a3, dbreakc0 +#endif + movi a14, pxCurrentTCB l32i a14, a14, 0 addi a15, sp, XT_STK_FRMSZ diff --git a/components/freertos/port/esp8266/panic.c b/components/freertos/port/esp8266/panic.c index e1ec4034..3f010b08 100644 --- a/components/freertos/port/esp8266/panic.c +++ b/components/freertos/port/esp8266/panic.c @@ -13,7 +13,7 @@ // limitations under the License. #include "stdlib.h" - +#include #include "esp_attr.h" #include "esp_libc.h" #include "esp_system.h" @@ -27,6 +27,7 @@ #include "esp_err.h" #include "FreeRTOS.h" +#include "task.h" #include "freertos/xtensa_context.h" #define PANIC(_fmt, ...) ets_printf(_fmt, ##__VA_ARGS__) @@ -77,17 +78,31 @@ static inline void panic_frame(XtExcFrame *frame) void *o_pc; void *o_sp; - const char *reason = frame->exccause < 30 ? edesc[frame->exccause] : "unknown"; - const uint32_t *regs = (const uint32_t *)frame; +#ifdef CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK + if (frame->exccause == 1) { + uint32_t reason = soc_debug_reason(); - PANIC("Guru Meditation Error: Core 0 panic'ed (%s). Exception was unhandled.\r\n", reason); - PANIC("Core 0 register dump:\n"); + if (reason & XCHAL_DEBUGCAUSE_DBREAK_MASK) { + char name[configMAX_TASK_NAME_LEN]; - for (int i = 0; i < 20; i += 4) { - for (int j = 0; j < 4; j++) { - PANIC("%-8s: 0x%08x ", sdesc[i + j], regs[i + j + 1]); + strncpy(name, pcTaskGetTaskName(NULL), configMAX_TASK_NAME_LEN); + ets_printf("Stack canary watchpoint triggered (%s)\n", name); + } + } else +#endif + { + const char *reason = frame->exccause < 30 ? edesc[frame->exccause] : "unknown"; + const uint32_t *regs = (const uint32_t *)frame; + + PANIC("Guru Meditation Error: Core 0 panic'ed (%s). Exception was unhandled.\r\n", reason); + PANIC("Core 0 register dump:\n"); + + for (int i = 0; i < 20; i += 4) { + for (int j = 0; j < 4; j++) { + PANIC("%-8s: 0x%08x ", sdesc[i + j], regs[i + j + 1]); + } + PANIC("\r\n"); } - PANIC("\r\n"); } PANIC("\r\nBacktrace: %p:%p ", i_pc, i_sp); diff --git a/components/freertos/port/esp8266/xtensa_vectors.S b/components/freertos/port/esp8266/xtensa_vectors.S index 476d0afc..755edc1a 100644 --- a/components/freertos/port/esp8266/xtensa_vectors.S +++ b/components/freertos/port/esp8266/xtensa_vectors.S @@ -107,6 +107,7 @@ STRUCT_END(HighPriFrame) .align 16 _chip_nmi_stk: .space PRI_N_STACK_SIZE + HESF_TOTALSIZE + PRI_N_STACK_SIZE2 + HESF_TOTALSIZE + .global LoadStoreErrorHandlerStack .balign 16 LoadStoreErrorHandlerStack: @@ -559,15 +560,23 @@ Debug Exception. .align 4 .literal_position _DebugExceptionVector: - wsr a0, EXCSAVE + XCHAL_DEBUGLEVEL /* save original a0 somewhere */ wsr a0, EXCSAVE_1 wsr a1, EXCSAVE_2 - call0 _xt_ext_panic /* does not return */ - rfi XCHAL_DEBUGLEVEL /* make a0 point here not later */ + movi a0, 1 + wsr a0, EXCCAUSE + call0 _xt_debug_exc .end literal_prefix + .section .text + .type _xt_user_exc,@function + .align 4 +_xt_debug_exc: + rsr a0, (EPC + XCHAL_DEBUGLEVEL) + wsr a0, EPC1 + call0 _xt_ext_panic /* does not return */ + /* -------------------------------------------------------------------------------- Double Exception.