Merge branch 'refactor/common_code_panic_handler' into 'master'

Panic handling common code refactor

See merge request espressif/esp-idf!7489
This commit is contained in:
Angus Gratton 2020-03-19 11:23:57 +08:00
commit 207914a13a
52 changed files with 1290 additions and 1574 deletions

View file

@ -133,7 +133,7 @@ mainmenu "Espressif IoT Development Framework Configuration"
CONFIG_APP_BUILD_TYPE_ELF_RAM=y
CONFIG_VFS_SUPPORT_TERMIOS=
CONFIG_NEWLIB_NANO_FORMAT=y
CONFIG_ESP32_PANIC_PRINT_HALT=y
CONFIG_ESP_SYSTEM_PANIC_PRINT_HALT=y
CONFIG_ESP32_DEBUG_STUBS_ENABLE=
CONFIG_ESP_ERR_TO_NAME_LOOKUP=

View file

@ -414,7 +414,7 @@ static void bootloader_check_wdt_reset(void)
void abort(void)
{
#if !CONFIG_ESP32_PANIC_SILENT_REBOOT
#if !CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT
ets_printf("abort() was called at PC 0x%08x\r\n", (intptr_t)__builtin_return_address(0) - 3);
#endif
if (esp_cpu_in_ocd_debug_mode()) {

View file

@ -330,7 +330,7 @@ static void bootloader_check_wdt_reset(void)
void abort(void)
{
#if !CONFIG_ESP32S2_PANIC_SILENT_REBOOT
#if !CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT
ets_printf("abort() was called at PC 0x%08x\r\n", (intptr_t)__builtin_return_address(0) - 3);
#endif
if (esp_cpu_in_ocd_debug_mode()) {

View file

@ -18,15 +18,12 @@ else()
"cpu_start.c"
"crosscore_int.c"
"dport_access.c"
"dport_panic_highint_hdl.S"
"esp_himem.c"
"hw_random.c"
"int_wdt.c"
"intr_alloc.c"
"panic.c"
"pm_esp32.c"
"pm_trace.c"
"reset_reason.c"
"sleep_modes.c"
"spiram.c"
"spiram_psram.c"

View file

@ -356,38 +356,6 @@ menu "ESP32-specific"
Data is reserved at the beginning of RTC slow memory.
choice ESP32_PANIC
prompt "Panic handler behaviour"
default ESP32_PANIC_PRINT_REBOOT
help
If FreeRTOS detects unexpected behaviour or an unhandled exception, the panic handler is
invoked. Configure the panic handlers action here.
config ESP32_PANIC_PRINT_HALT
bool "Print registers and halt"
help
Outputs the relevant registers over the serial port and halt the
processor. Needs a manual reset to restart.
config ESP32_PANIC_PRINT_REBOOT
bool "Print registers and reboot"
help
Outputs the relevant registers over the serial port and immediately
reset the processor.
config ESP32_PANIC_SILENT_REBOOT
bool "Silent reboot"
help
Just resets the processor without outputting anything
config ESP32_PANIC_GDBSTUB
bool "Invoke GDBStub"
select ESP_GDBSTUB_ENABLED
help
Invoke gdbstub on the serial port, allowing for gdb to attach to it to do a postmortem
of the crash.
endchoice
config ESP32_DEBUG_OCDAWARE
bool "Make exception and panic handlers JTAG/OCD aware"
default y

View file

@ -1,8 +1,3 @@
[mapping:esp32]
archive: libesp32.a
entries:
panic (noflash)
[mapping:gcc]
archive: libgcc.a
entries:

View file

@ -1,688 +0,0 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed 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.
#include <stdlib.h>
#include <xtensa/config/core.h>
#include "esp32/rom/rtc.h"
#include "esp32/rom/uart.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/xtensa_api.h"
#include "soc/uart_periph.h"
#include "soc/gpio_periph.h"
#include "soc/dport_reg.h"
#include "soc/rtc_periph.h"
#include "soc/timer_periph.h"
#include "soc/cpu.h"
#include "soc/rtc.h"
#include "soc/rtc_wdt.h"
#include "soc/soc_memory_layout.h"
#include "esp_private/gdbstub.h"
#include "esp_debug_helpers.h"
#include "esp_private/panic_reason.h"
#include "esp_attr.h"
#include "esp_err.h"
#include "esp_core_dump.h"
#include "esp_spi_flash.h"
#include "esp32/cache_err_int.h"
#include "esp_app_trace.h"
#include "esp_private/system_internal.h"
#include "sdkconfig.h"
#include "esp_ota_ops.h"
#include "driver/timer.h"
#include "hal/timer_ll.h"
#if CONFIG_SYSVIEW_ENABLE
#include "SEGGER_RTT.h"
#endif
#if CONFIG_APPTRACE_ONPANIC_HOST_FLUSH_TMO == -1
#define APPTRACE_ONPANIC_HOST_FLUSH_TMO ESP_APPTRACE_TMO_INFINITE
#else
#define APPTRACE_ONPANIC_HOST_FLUSH_TMO (1000*CONFIG_APPTRACE_ONPANIC_HOST_FLUSH_TMO)
#endif
/*
Panic handlers; these get called when an unhandled exception occurs or the assembly-level
task switching / interrupt code runs into an unrecoverable error. The default task stack
overflow handler and abort handler are also in here.
*/
/*
Note: The linker script will put everything in this file in IRAM/DRAM, so it also works with flash cache disabled.
*/
#if !CONFIG_ESP32_PANIC_SILENT_REBOOT
//printf may be broken, so we fix our own printing fns...
static void panicPutChar(char c)
{
while (((READ_PERI_REG(UART_STATUS_REG(CONFIG_ESP_CONSOLE_UART_NUM)) >> UART_TXFIFO_CNT_S)&UART_TXFIFO_CNT) >= 126) ;
WRITE_PERI_REG(UART_FIFO_REG(CONFIG_ESP_CONSOLE_UART_NUM), c);
}
static void panicPutStr(const char *c)
{
int x = 0;
while (c[x] != 0) {
panicPutChar(c[x]);
x++;
}
}
static void panicPutHex(int a)
{
int x;
int c;
for (x = 0; x < 8; x++) {
c = (a >> 28) & 0xf;
if (c < 10) {
panicPutChar('0' + c);
} else {
panicPutChar('a' + c - 10);
}
a <<= 4;
}
}
static void panicPutDec(int a)
{
int n1, n2;
n1 = a % 10;
n2 = a / 10;
if (n2 == 0) {
panicPutChar(' ');
} else {
panicPutChar(n2 + '0');
}
panicPutChar(n1 + '0');
}
#else
//No printing wanted. Stub out these functions.
static void panicPutChar(char c) { }
static void panicPutStr(const char *c) { }
static void panicPutHex(int a) { }
static void panicPutDec(int a) { }
#endif
void __attribute__((weak)) vApplicationStackOverflowHook( TaskHandle_t xTask, signed char *pcTaskName )
{
panicPutStr("***ERROR*** A stack overflow in task ");
panicPutStr((char *)pcTaskName);
panicPutStr(" has been detected.\r\n");
abort();
}
/* These two weak stubs for esp_reset_reason_{get,set}_hint are used when
* the application does not call esp_reset_reason() function, and
* reset_reason.c is not linked into the output file.
*/
void __attribute__((weak)) esp_reset_reason_set_hint(esp_reset_reason_t hint)
{
}
esp_reset_reason_t __attribute__((weak)) esp_reset_reason_get_hint(void)
{
return ESP_RST_UNKNOWN;
}
static bool abort_called;
static __attribute__((noreturn)) inline void invoke_abort(void)
{
abort_called = true;
#if CONFIG_APPTRACE_ENABLE
#if CONFIG_SYSVIEW_ENABLE
SEGGER_RTT_ESP32_FlushNoLock(CONFIG_APPTRACE_POSTMORTEM_FLUSH_THRESH, APPTRACE_ONPANIC_HOST_FLUSH_TMO);
#else
esp_apptrace_flush_nolock(ESP_APPTRACE_DEST_TRAX, CONFIG_APPTRACE_POSTMORTEM_FLUSH_THRESH,
APPTRACE_ONPANIC_HOST_FLUSH_TMO);
#endif
#endif
while (1) {
if (esp_cpu_in_ocd_debug_mode()) {
__asm__ ("break 0,0");
}
*((int *) 0) = 0;
}
}
void abort(void)
{
#if !CONFIG_ESP32_PANIC_SILENT_REBOOT
ets_printf("abort() was called at PC 0x%08x on core %d\r\n", (intptr_t)__builtin_return_address(0) - 3, xPortGetCoreID());
#endif
/* Calling code might have set other reset reason hint (such as Task WDT),
* don't overwrite that.
*/
if (esp_reset_reason_get_hint() == ESP_RST_UNKNOWN) {
esp_reset_reason_set_hint(ESP_RST_PANIC);
}
invoke_abort();
}
static const char *edesc[] = {
"IllegalInstruction", "Syscall", "InstructionFetchError", "LoadStoreError",
"Level1Interrupt", "Alloca", "IntegerDivideByZero", "PCValue",
"Privileged", "LoadStoreAlignment", "res", "res",
"InstrPDAddrError", "LoadStorePIFDataError", "InstrPIFAddrError", "LoadStorePIFAddrError",
"InstTLBMiss", "InstTLBMultiHit", "InstFetchPrivilege", "res",
"InstrFetchProhibited", "res", "res", "res",
"LoadStoreTLBMiss", "LoadStoreTLBMultihit", "LoadStorePrivilege", "res",
"LoadProhibited", "StoreProhibited", "res", "res",
"Cp0Dis", "Cp1Dis", "Cp2Dis", "Cp3Dis",
"Cp4Dis", "Cp5Dis", "Cp6Dis", "Cp7Dis"
};
#define NUM_EDESCS (sizeof(edesc) / sizeof(char *))
static void commonErrorHandler(XtExcFrame *frame);
static inline void disableAllWdts(void);
static void illegal_instruction_helper(XtExcFrame *frame);
//The fact that we've panic'ed probably means the other CPU is now running wild, possibly
//messing up the serial output, so we stall it here.
static void haltOtherCore(void)
{
esp_cpu_stall( xPortGetCoreID() == 0 ? 1 : 0 );
}
static void setFirstBreakpoint(uint32_t pc)
{
asm(
"wsr.ibreaka0 %0\n" \
"rsr.ibreakenable a3\n" \
"movi a4,1\n" \
"or a4, a4, a3\n" \
"wsr.ibreakenable a4\n" \
::"r"(pc):"a3", "a4");
}
//When interrupt watchdog happen in one core, both cores will be interrupted.
//The core which doesn't trigger the interrupt watchdog will save the frame and return.
//The core which triggers the interrupt watchdog will use the saved frame, and dump frames for both cores.
#if !CONFIG_FREERTOS_UNICORE
static volatile XtExcFrame * other_core_frame = NULL;
#endif //!CONFIG_FREERTOS_UNICORE
void panicHandler(XtExcFrame *frame)
{
int core_id = xPortGetCoreID();
//Please keep in sync with PANIC_RSN_* defines
const char *reasons[] = {
"Unknown reason",
"Unhandled debug exception",
"Double exception",
"Unhandled kernel exception",
"Coprocessor exception",
"Interrupt wdt timeout on CPU0",
"Interrupt wdt timeout on CPU1",
"Cache disabled but cached memory region accessed",
};
const char *reason = reasons[0];
//The panic reason is stored in the EXCCAUSE register.
if (frame->exccause <= PANIC_RSN_MAX) {
reason = reasons[frame->exccause];
}
#if !CONFIG_FREERTOS_UNICORE
//Save frame for other core.
if ((frame->exccause == PANIC_RSN_INTWDT_CPU0 && core_id == 1) || (frame->exccause == PANIC_RSN_INTWDT_CPU1 && core_id == 0)) {
other_core_frame = frame;
while (1);
}
//The core which triggers the interrupt watchdog will delay 1 us, so the other core can save its frame.
if (frame->exccause == PANIC_RSN_INTWDT_CPU0 || frame->exccause == PANIC_RSN_INTWDT_CPU1) {
ets_delay_us(1);
}
if (frame->exccause == PANIC_RSN_CACHEERR && esp_cache_err_get_cpuid() != core_id) {
// Cache error interrupt will be handled by the panic handler
// on the other CPU.
while (1);
}
#endif //!CONFIG_FREERTOS_UNICORE
if (frame->exccause == PANIC_RSN_INTWDT_CPU0 || frame->exccause == PANIC_RSN_INTWDT_CPU1) {
esp_reset_reason_set_hint(ESP_RST_INT_WDT);
}
haltOtherCore();
esp_dport_access_int_abort();
panicPutStr("Guru Meditation Error: Core ");
panicPutDec(core_id);
panicPutStr(" panic'ed (");
panicPutStr(reason);
panicPutStr(")\r\n");
if (frame->exccause == PANIC_RSN_DEBUGEXCEPTION) {
int debugRsn;
asm("rsr.debugcause %0":"=r"(debugRsn));
panicPutStr("Debug exception reason: ");
if (debugRsn & XCHAL_DEBUGCAUSE_ICOUNT_MASK) {
panicPutStr("SingleStep ");
}
if (debugRsn & XCHAL_DEBUGCAUSE_IBREAK_MASK) {
panicPutStr("HwBreakpoint ");
}
if (debugRsn & XCHAL_DEBUGCAUSE_DBREAK_MASK) {
//Unlike what the ISA manual says, this core seemingly distinguishes from a DBREAK
//reason caused by watchdog 0 and one caused by watchdog 1 by setting bit 8 of the
//debugcause if the cause is watchdog 1 and clearing it if it's watchdog 0.
if (debugRsn & (1 << 8)) {
#if CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK
const char *name = pcTaskGetTaskName(xTaskGetCurrentTaskHandleForCPU(core_id));
panicPutStr("Stack canary watchpoint triggered (");
panicPutStr(name);
panicPutStr(") ");
#else
panicPutStr("Watchpoint 1 triggered ");
#endif
} else {
panicPutStr("Watchpoint 0 triggered ");
}
}
if (debugRsn & XCHAL_DEBUGCAUSE_BREAK_MASK) {
panicPutStr("BREAK instr ");
}
if (debugRsn & XCHAL_DEBUGCAUSE_BREAKN_MASK) {
panicPutStr("BREAKN instr ");
}
if (debugRsn & XCHAL_DEBUGCAUSE_DEBUGINT_MASK) {
panicPutStr("DebugIntr ");
}
panicPutStr("\r\n");
}
if (esp_cpu_in_ocd_debug_mode()) {
disableAllWdts();
if (frame->exccause == PANIC_RSN_INTWDT_CPU0 ||
frame->exccause == PANIC_RSN_INTWDT_CPU1) {
timer_ll_wdt_clear_intr_status(&TIMERG1);
}
#if CONFIG_APPTRACE_ENABLE
#if CONFIG_SYSVIEW_ENABLE
SEGGER_RTT_ESP32_FlushNoLock(CONFIG_APPTRACE_POSTMORTEM_FLUSH_THRESH, APPTRACE_ONPANIC_HOST_FLUSH_TMO);
#else
esp_apptrace_flush_nolock(ESP_APPTRACE_DEST_TRAX, CONFIG_APPTRACE_POSTMORTEM_FLUSH_THRESH,
APPTRACE_ONPANIC_HOST_FLUSH_TMO);
#endif
#endif
setFirstBreakpoint(frame->pc);
return;
}
commonErrorHandler(frame);
}
void xt_unhandled_exception(XtExcFrame *frame)
{
haltOtherCore();
esp_dport_access_int_abort();
if (!abort_called) {
panicPutStr("Guru Meditation Error: Core ");
panicPutDec(xPortGetCoreID());
panicPutStr(" panic'ed (");
int exccause = frame->exccause;
if (exccause < NUM_EDESCS) {
panicPutStr(edesc[exccause]);
} else {
panicPutStr("Unknown");
}
panicPutStr(")");
if (esp_cpu_in_ocd_debug_mode()) {
panicPutStr(" at pc=");
panicPutHex(frame->pc);
panicPutStr(". Setting bp and returning..\r\n");
#if CONFIG_APPTRACE_ENABLE
#if CONFIG_SYSVIEW_ENABLE
SEGGER_RTT_ESP32_FlushNoLock(CONFIG_APPTRACE_POSTMORTEM_FLUSH_THRESH, APPTRACE_ONPANIC_HOST_FLUSH_TMO);
#else
esp_apptrace_flush_nolock(ESP_APPTRACE_DEST_TRAX, CONFIG_APPTRACE_POSTMORTEM_FLUSH_THRESH,
APPTRACE_ONPANIC_HOST_FLUSH_TMO);
#endif
#endif
//Stick a hardware breakpoint on the address the handler returns to. This way, the OCD debugger
//will kick in exactly at the context the error happened.
setFirstBreakpoint(frame->pc);
return;
}
panicPutStr(". Exception was unhandled.\r\n");
if (exccause == 0 /* IllegalInstruction */) {
illegal_instruction_helper(frame);
}
esp_reset_reason_set_hint(ESP_RST_PANIC);
}
commonErrorHandler(frame);
}
static void illegal_instruction_helper(XtExcFrame *frame)
{
/* Print out memory around the instruction word */
uint32_t epc = frame->pc;
epc = (epc & ~0x3) - 4;
/* check that the address was sane */
if (epc < SOC_IROM_MASK_LOW || epc >= SOC_IROM_HIGH) {
return;
}
volatile uint32_t* pepc = (uint32_t*)epc;
panicPutStr("Memory dump at 0x");
panicPutHex(epc);
panicPutStr(": ");
panicPutHex(*pepc);
panicPutStr(" ");
panicPutHex(*(pepc + 1));
panicPutStr(" ");
panicPutHex(*(pepc + 2));
panicPutStr("\r\n");
}
/*
If watchdogs are enabled, the panic handler runs the risk of getting aborted pre-emptively because
an overzealous watchdog decides to reset it. On the other hand, if we disable all watchdogs, we run
the risk of somehow halting in the panic handler and not resetting. That is why this routine kills
all watchdogs except the timer group 0 watchdog, and it reconfigures that to reset the chip after
one second.
*/
static void reconfigureAllWdts(void)
{
timer_ll_wdt_set_protect(&TIMERG0, false);
timer_ll_wdt_feed(&TIMERG0);
timer_ll_wdt_init(&TIMERG0);
timer_ll_wdt_set_tick(&TIMERG0, TG0_WDT_TICK_US); //Prescaler: wdt counts in ticks of TG0_WDT_TICK_US
//1st stage timeout: reset system
timer_ll_wdt_set_timeout_behavior(&TIMERG0, 0, TIMER_WDT_RESET_SYSTEM);
//1 second before reset
timer_ll_wdt_set_timeout(&TIMERG0, 0, 1000*1000/TG0_WDT_TICK_US);
timer_ll_wdt_set_enable(&TIMERG0, true);
timer_ll_wdt_set_protect(&TIMERG0, true);
//Disable wdt 1
timer_ll_wdt_set_protect(&TIMERG1, false);
timer_ll_wdt_set_enable(&TIMERG1, false);
timer_ll_wdt_set_protect(&TIMERG1, true);
}
/*
This disables all the watchdogs for when we call the gdbstub.
*/
static inline void disableAllWdts(void)
{
timer_ll_wdt_set_protect(&TIMERG0, false);
timer_ll_wdt_set_enable(&TIMERG0, false);
timer_ll_wdt_set_protect(&TIMERG0, true);
timer_ll_wdt_set_protect(&TIMERG1, false);
timer_ll_wdt_set_enable(&TIMERG1, false);
timer_ll_wdt_set_protect(&TIMERG1, true);
}
#if CONFIG_ESP32_PANIC_PRINT_REBOOT || CONFIG_ESP32_PANIC_SILENT_REBOOT
static void esp_panic_dig_reset(void) __attribute__((noreturn));
static void esp_panic_dig_reset(void)
{
// make sure all the panic handler output is sent from UART FIFO
uart_tx_wait_idle(CONFIG_ESP_CONSOLE_UART_NUM);
// switch to XTAL (otherwise we will keep running from the PLL)
rtc_clk_cpu_freq_set_xtal();
// reset the digital part
esp_cpu_unstall(PRO_CPU_NUM);
SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_SW_SYS_RST);
while (true) {
;
}
}
#endif // CONFIG_ESP32_PANIC_PRINT_REBOOT || CONFIG_ESP32_PANIC_SILENT_REBOOT
static void putEntry(uint32_t pc, uint32_t sp)
{
panicPutStr(" 0x");
panicPutHex(pc);
panicPutStr(":0x");
panicPutHex(sp);
}
static void doBacktrace(XtExcFrame *exc_frame, int depth)
{
//Initialize stk_frame with first frame of stack
esp_backtrace_frame_t stk_frame = {.pc = exc_frame->pc, .sp = exc_frame->a1, .next_pc = exc_frame->a0};
panicPutStr("\r\nBacktrace:");
putEntry(esp_cpu_process_stack_pc(stk_frame.pc), stk_frame.sp);
//Check if first frame is valid
bool corrupted = (esp_stack_ptr_is_sane(stk_frame.sp) &&
esp_ptr_executable((void*)esp_cpu_process_stack_pc(stk_frame.pc))) ?
false : true;
uint32_t i = ((depth <= 0) ? INT32_MAX : depth) - 1; //Account for stack frame that's already printed
while (i-- > 0 && stk_frame.next_pc != 0 && !corrupted) {
if (!esp_backtrace_get_next_frame(&stk_frame)) { //Get next stack frame
corrupted = true;
}
putEntry(esp_cpu_process_stack_pc(stk_frame.pc), stk_frame.sp);
}
//Print backtrace termination marker
if (corrupted) {
panicPutStr(" |<-CORRUPTED");
} else if (stk_frame.next_pc != 0) { //Backtrace continues
panicPutStr(" |<-CONTINUES");
}
panicPutStr("\r\n");
}
/*
* Dump registers and do backtrace.
*/
static void commonErrorHandler_dump(XtExcFrame *frame, int core_id)
{
int *regs = (int *)frame;
int x, y;
const char *sdesc[] = {
"PC ", "PS ", "A0 ", "A1 ", "A2 ", "A3 ", "A4 ", "A5 ",
"A6 ", "A7 ", "A8 ", "A9 ", "A10 ", "A11 ", "A12 ", "A13 ",
"A14 ", "A15 ", "SAR ", "EXCCAUSE", "EXCVADDR", "LBEG ", "LEND ", "LCOUNT "
};
/* only dump registers for 'real' crashes, if crashing via abort()
the register window is no longer useful.
*/
if (!abort_called) {
panicPutStr("Core");
panicPutDec(core_id);
panicPutStr(" register dump:\r\n");
for (x = 0; x < 24; x += 4) {
for (y = 0; y < 4; y++) {
if (sdesc[x + y][0] != 0) {
panicPutStr(sdesc[x + y]);
panicPutStr(": 0x");
panicPutHex(regs[x + y + 1]);
panicPutStr(" ");
}
}
panicPutStr("\r\n");
}
if (xPortInterruptedFromISRContext()
#if !CONFIG_FREERTOS_UNICORE
&& other_core_frame != frame
#endif //!CONFIG_FREERTOS_UNICORE
) {
//If the core which triggers the interrupt watchdog was in ISR context, dump the epc registers.
uint32_t __value;
panicPutStr("Core");
panicPutDec(core_id);
panicPutStr(" was running in ISR context:\r\n");
__asm__("rsr.epc1 %0" : "=a"(__value));
panicPutStr("EPC1 : 0x");
panicPutHex(__value);
__asm__("rsr.epc2 %0" : "=a"(__value));
panicPutStr(" EPC2 : 0x");
panicPutHex(__value);
__asm__("rsr.epc3 %0" : "=a"(__value));
panicPutStr(" EPC3 : 0x");
panicPutHex(__value);
__asm__("rsr.epc4 %0" : "=a"(__value));
panicPutStr(" EPC4 : 0x");
panicPutHex(__value);
panicPutStr("\r\n");
}
}
panicPutStr("\r\nELF file SHA256: ");
char sha256_buf[65];
esp_ota_get_app_elf_sha256(sha256_buf, sizeof(sha256_buf));
panicPutStr(sha256_buf);
panicPutStr("\r\n");
/* With windowed ABI backtracing is easy, let's do it. */
doBacktrace(frame, 100);
panicPutStr("\r\n");
}
/*
We arrive here after a panic or unhandled exception, when no OCD is detected. Dump the registers to the
serial port and either jump to the gdb stub, halt the CPU or reboot.
*/
static __attribute__((noreturn)) void commonErrorHandler(XtExcFrame *frame)
{
int core_id = xPortGetCoreID();
// start panic WDT to restart system if we hang in this handler
if (!rtc_wdt_is_on()) {
rtc_wdt_protect_off();
rtc_wdt_disable();
rtc_wdt_set_length_of_reset_signal(RTC_WDT_SYS_RESET_SIG, RTC_WDT_LENGTH_3_2us);
rtc_wdt_set_length_of_reset_signal(RTC_WDT_CPU_RESET_SIG, RTC_WDT_LENGTH_3_2us);
rtc_wdt_set_stage(RTC_WDT_STAGE0, RTC_WDT_STAGE_ACTION_RESET_SYSTEM);
// 64KB of core dump data (stacks of about 30 tasks) will produce ~85KB base64 data.
// @ 115200 UART speed it will take more than 6 sec to print them out.
rtc_wdt_set_time(RTC_WDT_STAGE0, 7000);
rtc_wdt_enable();
rtc_wdt_protect_on();
}
//Feed the watchdogs, so they will give us time to print out debug info
reconfigureAllWdts();
commonErrorHandler_dump(frame, core_id);
#if !CONFIG_FREERTOS_UNICORE
if (other_core_frame != NULL) {
commonErrorHandler_dump((XtExcFrame *)other_core_frame, (core_id ? 0 : 1));
}
#endif //!CONFIG_FREERTOS_UNICORE
#if CONFIG_APPTRACE_ENABLE
disableAllWdts();
#if CONFIG_SYSVIEW_ENABLE
SEGGER_RTT_ESP32_FlushNoLock(CONFIG_APPTRACE_POSTMORTEM_FLUSH_THRESH, APPTRACE_ONPANIC_HOST_FLUSH_TMO);
#else
esp_apptrace_flush_nolock(ESP_APPTRACE_DEST_TRAX, CONFIG_APPTRACE_POSTMORTEM_FLUSH_THRESH,
APPTRACE_ONPANIC_HOST_FLUSH_TMO);
#endif
reconfigureAllWdts();
#endif
#if !CONFIG_ESP_PANIC_HANDLER_IRAM
// Re-enable CPU cache for current CPU if it was disabled
if (!spi_flash_cache_enabled()) {
spi_flash_enable_cache(core_id);
panicPutStr("Re-enable cpu cache.\r\n");
}
#endif
#if CONFIG_ESP32_PANIC_GDBSTUB
disableAllWdts();
rtc_wdt_disable();
panicPutStr("Entering gdb stub now.\r\n");
esp_gdbstub_panic_handler(frame);
#else
#if CONFIG_ESP32_ENABLE_COREDUMP
static bool s_dumping_core;
if (s_dumping_core) {
panicPutStr("Re-entered core dump! Exception happened during core dump!\r\n");
} else {
disableAllWdts();
s_dumping_core = true;
#if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH
esp_core_dump_to_flash(frame);
#endif
#if CONFIG_ESP32_ENABLE_COREDUMP_TO_UART && !CONFIG_ESP32_PANIC_SILENT_REBOOT
esp_core_dump_to_uart(frame);
#endif
s_dumping_core = false;
reconfigureAllWdts();
}
#endif /* CONFIG_ESP32_ENABLE_COREDUMP */
rtc_wdt_disable();
#if CONFIG_ESP32_PANIC_PRINT_REBOOT || CONFIG_ESP32_PANIC_SILENT_REBOOT
panicPutStr("Rebooting...\r\n");
if (esp_cache_err_get_cpuid() == -1) {
esp_restart_noos();
} else {
// The only way to clear invalid cache access interrupt is to reset the digital part
esp_panic_dig_reset();
}
#else
disableAllWdts();
panicPutStr("CPU halted.\r\n");
while (1);
#endif /* CONFIG_ESP32_PANIC_PRINT_REBOOT || CONFIG_ESP32_PANIC_SILENT_REBOOT */
#endif /* CONFIG_ESP32_PANIC_GDBSTUB */
}
void esp_set_breakpoint_if_jtag(void *fn)
{
if (esp_cpu_in_ocd_debug_mode()) {
setFirstBreakpoint((uint32_t)fn);
}
}
static void esp_error_check_failed_print(const char *msg, esp_err_t rc, const char *file, int line, const char *function, const char *expression)
{
ets_printf("%s failed: esp_err_t 0x%x", msg, rc);
#ifdef CONFIG_ESP_ERR_TO_NAME_LOOKUP
ets_printf(" (%s)", esp_err_to_name(rc));
#endif //CONFIG_ESP_ERR_TO_NAME_LOOKUP
ets_printf(" at 0x%08x\n", (intptr_t)__builtin_return_address(0) - 3);
if (spi_flash_cache_enabled()) { // strings may be in flash cache
ets_printf("file: \"%s\" line %d\nfunc: %s\nexpression: %s\n", file, line, function, expression);
}
}
void _esp_error_check_failed_without_abort(esp_err_t rc, const char *file, int line, const char *function, const char *expression)
{
esp_error_check_failed_print("ESP_ERROR_CHECK_WITHOUT_ABORT", rc, file, line, function, expression);
}
void _esp_error_check_failed(esp_err_t rc, const char *file, int line, const char *function, const char *expression)
{
esp_error_check_failed_print("ESP_ERROR_CHECK", rc, file, line, function, expression);
invoke_abort();
}

View file

@ -34,6 +34,13 @@
#include "hal/timer_ll.h"
#include "freertos/xtensa_api.h"
#if CONFIG_IDF_TARGET_ESP32
#include "esp32/cache_err_int.h"
#elif CONFIG_IDF_TARGET_ESP32S2
#include "esp32s2/cache_err_int.h"
#endif
/* "inner" restart function for after RTOS, interrupts & anything else on this
* core are already stopped. Stalls other core, resets hardware,
* triggers restart.
@ -94,10 +101,10 @@ void IRAM_ATTR esp_restart_noos(void)
// Reset wifi/bluetooth/ethernet/sdio (bb/mac)
DPORT_SET_PERI_REG_MASK(DPORT_CORE_RST_EN_REG,
DPORT_BB_RST | DPORT_FE_RST | DPORT_MAC_RST |
DPORT_BT_RST | DPORT_BTMAC_RST | DPORT_SDIO_RST |
DPORT_SDIO_HOST_RST | DPORT_EMAC_RST | DPORT_MACPWR_RST |
DPORT_RW_BTMAC_RST | DPORT_RW_BTLP_RST);
DPORT_BB_RST | DPORT_FE_RST | DPORT_MAC_RST |
DPORT_BT_RST | DPORT_BTMAC_RST | DPORT_SDIO_RST |
DPORT_SDIO_HOST_RST | DPORT_EMAC_RST | DPORT_MACPWR_RST |
DPORT_RW_BTMAC_RST | DPORT_RW_BTLP_RST);
DPORT_REG_WRITE(DPORT_CORE_RST_EN_REG, 0);
// Reset timer/spi/uart

View file

@ -16,14 +16,11 @@ else()
"cpu_start.c"
"crosscore_int.c"
"dport_access.c"
"dport_panic_highint_hdl.S"
"hw_random.c"
"int_wdt.c"
"intr_alloc.c"
"panic.c"
"pm_esp32s2.c"
"pm_trace.c"
"reset_reason.c"
"sleep_modes.c"
"spiram.c"
"spiram_psram.c"

View file

@ -296,38 +296,6 @@ menu "ESP32S2-specific"
Data is reserved at the beginning of RTC slow memory.
choice ESP32S2_PANIC
prompt "Panic handler behaviour"
default ESP32S2_PANIC_PRINT_REBOOT
help
If FreeRTOS detects unexpected behaviour or an unhandled exception, the panic handler is
invoked. Configure the panic handlers action here.
config ESP32S2_PANIC_PRINT_HALT
bool "Print registers and halt"
help
Outputs the relevant registers over the serial port and halt the
processor. Needs a manual reset to restart.
config ESP32S2_PANIC_PRINT_REBOOT
bool "Print registers and reboot"
help
Outputs the relevant registers over the serial port and immediately
reset the processor.
config ESP32S2_PANIC_SILENT_REBOOT
bool "Silent reboot"
help
Just resets the processor without outputting anything
config ESP32S2_PANIC_GDBSTUB
bool "Invoke GDBStub"
select ESP_GDBSTUB_ENABLED
help
Invoke gdbstub on the serial port, allowing for gdb to attach to it to do a postmortem
of the crash.
endchoice
config ESP32S2_DEBUG_OCDAWARE
bool "Make exception and panic handlers JTAG/OCD aware"
default y

View file

@ -78,5 +78,7 @@ void esp_cache_err_int_init(void)
int IRAM_ATTR esp_cache_err_get_cpuid(void)
{
// TODO: The description for this seem to indicate that when cache is not in error
// state, return -1.
return PRO_CPU_NUM;
}

View file

@ -1,8 +1,3 @@
[mapping:esp32s2]
archive: libesp32s2.a
entries:
panic (noflash)
[mapping:gcc]
archive: libgcc.a
entries:

View file

@ -1,678 +0,0 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed 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.
#include <stdlib.h>
#include <xtensa/config/core.h>
#include "esp32s2/rom/rtc.h"
#include "esp32s2/rom/uart.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/xtensa_api.h"
#include "soc/uart_reg.h"
#include "soc/io_mux_reg.h"
#include "soc/dport_reg.h"
#include "soc/extmem_reg.h"
#include "soc/cache_memory.h"
#include "soc/rtc_cntl_reg.h"
#include "soc/timer_group_struct.h"
#include "soc/timer_group_reg.h"
#include "soc/cpu.h"
#include "soc/soc_memory_layout.h"
#include "soc/rtc.h"
#include "soc/rtc_wdt.h"
#include "esp_private/gdbstub.h"
#include "esp_debug_helpers.h"
#include "esp_private/panic_reason.h"
#include "esp_attr.h"
#include "esp_err.h"
#include "esp_core_dump.h"
#include "esp_spi_flash.h"
#include "esp32s2/cache_err_int.h"
#include "esp_app_trace.h"
#include "esp_private/system_internal.h"
#include "sdkconfig.h"
#if CONFIG_SYSVIEW_ENABLE
#include "SEGGER_RTT.h"
#endif
#if CONFIG_APPTRACE_ONPANIC_HOST_FLUSH_TMO == -1
#define APPTRACE_ONPANIC_HOST_FLUSH_TMO ESP_APPTRACE_TMO_INFINITE
#else
#define APPTRACE_ONPANIC_HOST_FLUSH_TMO (1000*CONFIG_APPTRACE_ONPANIC_HOST_FLUSH_TMO)
#endif
/*
Panic handlers; these get called when an unhandled exception occurs or the assembly-level
task switching / interrupt code runs into an unrecoverable error. The default task stack
overflow handler and abort handler are also in here.
*/
/*
Note: The linker script will put everything in this file in IRAM/DRAM, so it also works with flash cache disabled.
*/
#if !CONFIG_ESP32S2_PANIC_SILENT_REBOOT
//printf may be broken, so we fix our own printing fns...
static void panicPutChar(char c)
{
while (((READ_PERI_REG(UART_STATUS_REG(CONFIG_ESP_CONSOLE_UART_NUM)) >> UART_TXFIFO_CNT_S)&UART_TXFIFO_CNT) >= 126) ;
WRITE_PERI_REG(UART_FIFO_AHB_REG(CONFIG_ESP_CONSOLE_UART_NUM), c);
}
static void panicPutStr(const char *c)
{
int x = 0;
while (c[x] != 0) {
panicPutChar(c[x]);
x++;
}
}
static void panicPutHex(int a)
{
int x;
int c;
for (x = 0; x < 8; x++) {
c = (a >> 28) & 0xf;
if (c < 10) {
panicPutChar('0' + c);
} else {
panicPutChar('a' + c - 10);
}
a <<= 4;
}
}
static void panicPutDec(int a)
{
int n1, n2;
n1 = a % 10;
n2 = a / 10;
if (n2 == 0) {
panicPutChar(' ');
} else {
panicPutChar(n2 + '0');
}
panicPutChar(n1 + '0');
}
#else
//No printing wanted. Stub out these functions.
static void panicPutChar(char c) { }
static void panicPutStr(const char *c) { }
static void panicPutHex(int a) { }
static void panicPutDec(int a) { }
#endif
void __attribute__((weak)) vApplicationStackOverflowHook( TaskHandle_t xTask, signed char *pcTaskName )
{
panicPutStr("***ERROR*** A stack overflow in task ");
panicPutStr((char *)pcTaskName);
panicPutStr(" has been detected.\r\n");
abort();
}
/* These two weak stubs for esp_reset_reason_{get,set}_hint are used when
* the application does not call esp_reset_reason() function, and
* reset_reason.c is not linked into the output file.
*/
void __attribute__((weak)) esp_reset_reason_set_hint(esp_reset_reason_t hint)
{
}
esp_reset_reason_t __attribute__((weak)) esp_reset_reason_get_hint(void)
{
return ESP_RST_UNKNOWN;
}
static bool abort_called;
static __attribute__((noreturn)) inline void invoke_abort(void)
{
abort_called = true;
#if CONFIG_APPTRACE_ENABLE
#if CONFIG_SYSVIEW_ENABLE
SEGGER_RTT_ESP32_FlushNoLock(CONFIG_APPTRACE_POSTMORTEM_FLUSH_THRESH, APPTRACE_ONPANIC_HOST_FLUSH_TMO);
#else
esp_apptrace_flush_nolock(ESP_APPTRACE_DEST_TRAX, CONFIG_APPTRACE_POSTMORTEM_FLUSH_THRESH,
APPTRACE_ONPANIC_HOST_FLUSH_TMO);
#endif
#endif
while (1) {
if (esp_cpu_in_ocd_debug_mode()) {
__asm__ ("break 0,0");
}
*((int *) 0) = 0;
}
}
void abort(void)
{
#if !CONFIG_ESP32S2_PANIC_SILENT_REBOOT
ets_printf("abort() was called at PC 0x%08x on core %d\r\n", (intptr_t)__builtin_return_address(0) - 3, xPortGetCoreID());
#endif
/* Calling code might have set other reset reason hint (such as Task WDT),
* don't overwrite that.
*/
if (esp_reset_reason_get_hint() == ESP_RST_UNKNOWN) {
esp_reset_reason_set_hint(ESP_RST_PANIC);
}
invoke_abort();
}
static const char *edesc[] = {
"IllegalInstruction", "Syscall", "InstructionFetchError", "LoadStoreError",
"Level1Interrupt", "Alloca", "IntegerDivideByZero", "PCValue",
"Privileged", "LoadStoreAlignment", "res", "res",
"InstrPDAddrError", "LoadStorePIFDataError", "InstrPIFAddrError", "LoadStorePIFAddrError",
"InstTLBMiss", "InstTLBMultiHit", "InstFetchPrivilege", "res",
"InstrFetchProhibited", "res", "res", "res",
"LoadStoreTLBMiss", "LoadStoreTLBMultihit", "LoadStorePrivilege", "res",
"LoadProhibited", "StoreProhibited", "res", "res",
"Cp0Dis", "Cp1Dis", "Cp2Dis", "Cp3Dis",
"Cp4Dis", "Cp5Dis", "Cp6Dis", "Cp7Dis"
};
#define NUM_EDESCS (sizeof(edesc) / sizeof(char *))
static void commonErrorHandler(XtExcFrame *frame);
static inline void disableAllWdts(void);
//The fact that we've panic'ed probably means the other CPU is now running wild, possibly
//messing up the serial output, so we stall it here.
static void setFirstBreakpoint(uint32_t pc)
{
asm(
"wsr.ibreaka0 %0\n" \
"rsr.ibreakenable a3\n" \
"movi a4,1\n" \
"or a4, a4, a3\n" \
"wsr.ibreakenable a4\n" \
::"r"(pc):"a3", "a4");
}
static inline void printCacheError(void)
{
uint32_t vaddr = 0, size = 0;
uint32_t status[2];
status[0] = REG_READ(EXTMEM_CACHE_DBG_STATUS0_REG);
status[1] = REG_READ(EXTMEM_CACHE_DBG_STATUS1_REG);
for (int i = 0; i < 32; i++) {
switch (status[0] & BIT(i)) {
case EXTMEM_IC_SYNC_SIZE_FAULT_ST:
vaddr = REG_READ(EXTMEM_PRO_ICACHE_MEM_SYNC0_REG);
size = REG_READ(EXTMEM_PRO_ICACHE_MEM_SYNC1_REG);
panicPutStr("Icache sync parameter configuration error, the error address and size is 0x");
panicPutHex(vaddr);
panicPutStr("(0x");
panicPutHex(size);
panicPutStr(")\r\n");
break;
case EXTMEM_IC_PRELOAD_SIZE_FAULT_ST:
vaddr = REG_READ(EXTMEM_PRO_ICACHE_PRELOAD_ADDR_REG);
size = REG_READ(EXTMEM_PRO_ICACHE_PRELOAD_SIZE_REG);
panicPutStr("Icache preload parameter configuration error, the error address and size is 0x");
panicPutHex(vaddr);
panicPutStr("(0x");
panicPutHex(size);
panicPutStr(")\r\n");
break;
case EXTMEM_ICACHE_REJECT_ST:
vaddr = REG_READ(EXTMEM_PRO_ICACHE_REJECT_VADDR_REG);
panicPutStr("Icache reject error occurred while accessing the address 0x");
panicPutHex(vaddr);
if (REG_READ(EXTMEM_PRO_CACHE_MMU_FAULT_CONTENT_REG) & MMU_INVALID) {
panicPutStr(" (invalid mmu entry)");
}
panicPutStr("\r\n");
break;
default:
break;
}
switch (status[1] & BIT(i)) {
case EXTMEM_DC_SYNC_SIZE_FAULT_ST:
vaddr = REG_READ(EXTMEM_PRO_DCACHE_MEM_SYNC0_REG);
size = REG_READ(EXTMEM_PRO_DCACHE_MEM_SYNC1_REG);
panicPutStr("Dcache sync parameter configuration error, the error address and size is 0x");
panicPutHex(vaddr);
panicPutStr("(0x");
panicPutHex(size);
panicPutStr(")\r\n");
break;
case EXTMEM_DC_PRELOAD_SIZE_FAULT_ST:
vaddr = REG_READ(EXTMEM_PRO_DCACHE_PRELOAD_ADDR_REG);
size = REG_READ(EXTMEM_PRO_DCACHE_PRELOAD_SIZE_REG);
panicPutStr("Dcache preload parameter configuration error, the error address and size is 0x");
panicPutHex(vaddr);
panicPutStr("(0x");
panicPutHex(size);
panicPutStr(")\r\n");
break;
case EXTMEM_DCACHE_WRITE_FLASH_ST:
panicPutStr("Write back error occurred while dcache tries to write back to flash\r\n");
break;
case EXTMEM_DCACHE_REJECT_ST:
vaddr = REG_READ(EXTMEM_PRO_DCACHE_REJECT_VADDR_REG);
panicPutStr("Dcache reject error occurred while accessing the address 0x");
panicPutHex(vaddr);
if (REG_READ(EXTMEM_PRO_CACHE_MMU_FAULT_CONTENT_REG) & MMU_INVALID) {
panicPutStr(" (invalid mmu entry)");
}
panicPutStr("\r\n");
break;
case EXTMEM_MMU_ENTRY_FAULT_ST:
vaddr = REG_READ(EXTMEM_PRO_CACHE_MMU_FAULT_VADDR_REG);
panicPutStr("MMU entry fault error occurred while accessing the address 0x");
panicPutHex(vaddr);
if (REG_READ(EXTMEM_PRO_CACHE_MMU_FAULT_CONTENT_REG) & MMU_INVALID) {
panicPutStr(" (invalid mmu entry)");
}
panicPutStr("\r\n");
break;
default:
break;
}
}
panicPutStr("\r\n");
}
void panicHandler(XtExcFrame *frame)
{
int core_id = xPortGetCoreID();
//Please keep in sync with PANIC_RSN_* defines
const char *reasons[] = {
"Unknown reason",
"Unhandled debug exception",
"Double exception",
"Unhandled kernel exception",
"Coprocessor exception",
"Interrupt wdt timeout on CPU0",
"Interrupt wdt timeout on CPU1",
"Cache exception",
};
const char *reason = reasons[0];
//The panic reason is stored in the EXCCAUSE register.
if (frame->exccause <= PANIC_RSN_MAX) {
reason = reasons[frame->exccause];
}
if (frame->exccause == PANIC_RSN_INTWDT_CPU0) {
esp_reset_reason_set_hint(ESP_RST_INT_WDT);
}
panicPutStr("Guru Meditation Error: Core ");
panicPutDec(core_id);
panicPutStr(" panic'ed (");
panicPutStr(reason);
panicPutStr(")\r\n");
if (frame->exccause == PANIC_RSN_DEBUGEXCEPTION) {
int debugRsn;
asm("rsr.debugcause %0":"=r"(debugRsn));
panicPutStr("Debug exception reason: ");
if (debugRsn & XCHAL_DEBUGCAUSE_ICOUNT_MASK) {
panicPutStr("SingleStep ");
}
if (debugRsn & XCHAL_DEBUGCAUSE_IBREAK_MASK) {
panicPutStr("HwBreakpoint ");
}
if (debugRsn & XCHAL_DEBUGCAUSE_DBREAK_MASK) {
//Unlike what the ISA manual says, this core seemingly distinguishes from a DBREAK
//reason caused by watchdog 0 and one caused by watchdog 1 by setting bit 8 of the
//debugcause if the cause is watchdog 1 and clearing it if it's watchdog 0.
if (debugRsn & (1 << 8)) {
#if CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK
const char *name = pcTaskGetTaskName(xTaskGetCurrentTaskHandleForCPU(core_id));
panicPutStr("Stack canary watchpoint triggered (");
panicPutStr(name);
panicPutStr(") ");
#else
panicPutStr("Watchpoint 1 triggered ");
#endif
} else {
panicPutStr("Watchpoint 0 triggered ");
}
}
if (debugRsn & XCHAL_DEBUGCAUSE_BREAK_MASK) {
panicPutStr("BREAK instr ");
}
if (debugRsn & XCHAL_DEBUGCAUSE_BREAKN_MASK) {
panicPutStr("BREAKN instr ");
}
if (debugRsn & XCHAL_DEBUGCAUSE_DEBUGINT_MASK) {
panicPutStr("DebugIntr ");
}
panicPutStr("\r\n");
} else if (frame->exccause == PANIC_RSN_CACHEERR) {
panicPutStr(" ^~~~~~~~~~~~~~~\r\n");
printCacheError();
}
if (esp_cpu_in_ocd_debug_mode()) {
disableAllWdts();
if (frame->exccause == PANIC_RSN_INTWDT_CPU0 ||
frame->exccause == PANIC_RSN_INTWDT_CPU1) {
TIMERG1.int_clr.wdt = 1;
}
#if CONFIG_APPTRACE_ENABLE
#if CONFIG_SYSVIEW_ENABLE
SEGGER_RTT_ESP32_FlushNoLock(CONFIG_APPTRACE_POSTMORTEM_FLUSH_THRESH, APPTRACE_ONPANIC_HOST_FLUSH_TMO);
#else
esp_apptrace_flush_nolock(ESP_APPTRACE_DEST_TRAX, CONFIG_APPTRACE_POSTMORTEM_FLUSH_THRESH,
APPTRACE_ONPANIC_HOST_FLUSH_TMO);
#endif
#endif
setFirstBreakpoint(frame->pc);
return;
}
commonErrorHandler(frame);
}
void xt_unhandled_exception(XtExcFrame *frame)
{
if (!abort_called) {
panicPutStr("Guru Meditation Error: Core ");
panicPutDec(xPortGetCoreID());
panicPutStr(" panic'ed (");
int exccause = frame->exccause;
if (exccause < NUM_EDESCS) {
panicPutStr(edesc[exccause]);
} else {
panicPutStr("Unknown");
}
panicPutStr(")");
if (esp_cpu_in_ocd_debug_mode()) {
panicPutStr(" at pc=");
panicPutHex(frame->pc);
panicPutStr(". Setting bp and returning..\r\n");
#if CONFIG_APPTRACE_ENABLE
#if CONFIG_SYSVIEW_ENABLE
SEGGER_RTT_ESP32_FlushNoLock(CONFIG_APPTRACE_POSTMORTEM_FLUSH_THRESH, APPTRACE_ONPANIC_HOST_FLUSH_TMO);
#else
esp_apptrace_flush_nolock(ESP_APPTRACE_DEST_TRAX, CONFIG_APPTRACE_POSTMORTEM_FLUSH_THRESH,
APPTRACE_ONPANIC_HOST_FLUSH_TMO);
#endif
#endif
//Stick a hardware breakpoint on the address the handler returns to. This way, the OCD debugger
//will kick in exactly at the context the error happened.
setFirstBreakpoint(frame->pc);
return;
}
panicPutStr(". Exception was unhandled.\r\n");
esp_reset_reason_set_hint(ESP_RST_PANIC);
}
commonErrorHandler(frame);
}
/*
If watchdogs are enabled, the panic handler runs the risk of getting aborted pre-emptively because
an overzealous watchdog decides to reset it. On the other hand, if we disable all watchdogs, we run
the risk of somehow halting in the panic handler and not resetting. That is why this routine kills
all watchdogs except the timer group 0 watchdog, and it reconfigures that to reset the chip after
one second.
*/
static void reconfigureAllWdts(void)
{
TIMERG0.wdt_wprotect = TIMG_WDT_WKEY_VALUE;
TIMERG0.wdt_feed = 1;
TIMERG0.wdt_config0.sys_reset_length = 7; //3.2uS
TIMERG0.wdt_config0.cpu_reset_length = 7; //3.2uS
TIMERG0.wdt_config0.stg0 = TIMG_WDT_STG_SEL_RESET_SYSTEM; //1st stage timeout: reset system
TIMERG0.wdt_config1.clk_prescale = 80 * 500; //Prescaler: wdt counts in ticks of 0.5mS
TIMERG0.wdt_config2 = 2000; //1 second before reset
TIMERG0.wdt_config0.en = 1;
TIMERG0.wdt_wprotect = 0;
//Disable wdt 1
TIMERG1.wdt_wprotect = TIMG_WDT_WKEY_VALUE;
TIMERG1.wdt_config0.en = 0;
TIMERG1.wdt_wprotect = 0;
}
/*
This disables all the watchdogs for when we call the gdbstub.
*/
static inline void disableAllWdts(void)
{
TIMERG0.wdt_wprotect = TIMG_WDT_WKEY_VALUE;
TIMERG0.wdt_config0.en = 0;
TIMERG0.wdt_wprotect = 0;
TIMERG1.wdt_wprotect = TIMG_WDT_WKEY_VALUE;
TIMERG1.wdt_config0.en = 0;
TIMERG1.wdt_wprotect = 0;
}
#if CONFIG_ESP32S2_PANIC_PRINT_REBOOT || CONFIG_ESP32S2_PANIC_SILENT_REBOOT
static void esp_panic_dig_reset(void) __attribute__((noreturn));
static void esp_panic_dig_reset(void)
{
// make sure all the panic handler output is sent from UART FIFO
uart_tx_wait_idle(CONFIG_ESP_CONSOLE_UART_NUM);
// switch to XTAL (otherwise we will keep running from the PLL)
rtc_clk_cpu_freq_set_xtal();
SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_SW_SYS_RST);
while (true) {
;
}
}
#endif
static void putEntry(uint32_t pc, uint32_t sp)
{
if (pc & 0x80000000) {
pc = (pc & 0x3fffffff) | 0x40000000;
}
panicPutStr(" 0x");
panicPutHex(pc);
panicPutStr(":0x");
panicPutHex(sp);
}
static void doBacktrace(XtExcFrame *frame)
{
uint32_t i = 0, pc = frame->pc, sp = frame->a1;
panicPutStr("\r\nBacktrace:");
/* Do not check sanity on first entry, PC could be smashed. */
putEntry(pc, sp);
pc = frame->a0;
while (i++ < 100) {
uint32_t psp = sp;
if (!esp_stack_ptr_is_sane(sp) || i++ > 100) {
break;
}
sp = *((uint32_t *) (sp - 0x10 + 4));
putEntry(pc - 3, sp); // stack frame addresses are return addresses, so subtract 3 to get the CALL address
pc = *((uint32_t *) (psp - 0x10));
if (pc < 0x40000000) {
break;
}
}
panicPutStr("\r\n\r\n");
}
/*
* Dump registers and do backtrace.
*/
static void commonErrorHandler_dump(XtExcFrame *frame, int core_id)
{
int *regs = (int *)frame;
int x, y;
const char *sdesc[] = {
"PC ", "PS ", "A0 ", "A1 ", "A2 ", "A3 ", "A4 ", "A5 ",
"A6 ", "A7 ", "A8 ", "A9 ", "A10 ", "A11 ", "A12 ", "A13 ",
"A14 ", "A15 ", "SAR ", "EXCCAUSE", "EXCVADDR", "LBEG ", "LEND ", "LCOUNT "
};
/* only dump registers for 'real' crashes, if crashing via abort()
the register window is no longer useful.
*/
if (!abort_called) {
panicPutStr("Core");
panicPutDec(core_id);
panicPutStr(" register dump:\r\n");
for (x = 0; x < 24; x += 4) {
for (y = 0; y < 4; y++) {
if (sdesc[x + y][0] != 0) {
panicPutStr(sdesc[x + y]);
panicPutStr(": 0x");
panicPutHex(regs[x + y + 1]);
panicPutStr(" ");
}
}
panicPutStr("\r\n");
}
if (xPortInterruptedFromISRContext()) {
//If the core which triggers the interrupt watchdog was in ISR context, dump the epc registers.
uint32_t __value;
panicPutStr("Core");
panicPutDec(core_id);
panicPutStr(" was running in ISR context:\r\n");
__asm__("rsr.epc1 %0" : "=a"(__value));
panicPutStr("EPC1 : 0x");
panicPutHex(__value);
__asm__("rsr.epc2 %0" : "=a"(__value));
panicPutStr(" EPC2 : 0x");
panicPutHex(__value);
__asm__("rsr.epc3 %0" : "=a"(__value));
panicPutStr(" EPC3 : 0x");
panicPutHex(__value);
__asm__("rsr.epc4 %0" : "=a"(__value));
panicPutStr(" EPC4 : 0x");
panicPutHex(__value);
panicPutStr("\r\n");
}
}
/* With windowed ABI backtracing is easy, let's do it. */
doBacktrace(frame);
}
/*
We arrive here after a panic or unhandled exception, when no OCD is detected. Dump the registers to the
serial port and either jump to the gdb stub, halt the CPU or reboot.
*/
static __attribute__((noreturn)) void commonErrorHandler(XtExcFrame *frame)
{
int core_id = xPortGetCoreID();
// start panic WDT to restart system if we hang in this handler
if (!rtc_wdt_is_on()) {
rtc_wdt_protect_off();
rtc_wdt_disable();
rtc_wdt_set_length_of_reset_signal(RTC_WDT_SYS_RESET_SIG, RTC_WDT_LENGTH_3_2us);
rtc_wdt_set_length_of_reset_signal(RTC_WDT_CPU_RESET_SIG, RTC_WDT_LENGTH_3_2us);
rtc_wdt_set_stage(RTC_WDT_STAGE0, RTC_WDT_STAGE_ACTION_RESET_SYSTEM);
// 64KB of core dump data (stacks of about 30 tasks) will produce ~85KB base64 data.
// @ 115200 UART speed it will take more than 6 sec to print them out.
rtc_wdt_set_time(RTC_WDT_STAGE0, 7000);
rtc_wdt_enable();
rtc_wdt_protect_on();
}
//Feed the watchdogs, so they will give us time to print out debug info
reconfigureAllWdts();
commonErrorHandler_dump(frame, core_id);
#if CONFIG_APPTRACE_ENABLE
disableAllWdts();
#if CONFIG_SYSVIEW_ENABLE
SEGGER_RTT_ESP32_FlushNoLock(CONFIG_APPTRACE_POSTMORTEM_FLUSH_THRESH, APPTRACE_ONPANIC_HOST_FLUSH_TMO);
#else
esp_apptrace_flush_nolock(ESP_APPTRACE_DEST_TRAX, CONFIG_APPTRACE_POSTMORTEM_FLUSH_THRESH,
APPTRACE_ONPANIC_HOST_FLUSH_TMO);
#endif
reconfigureAllWdts();
#endif
#if CONFIG_ESP32S2_PANIC_GDBSTUB
disableAllWdts();
rtc_wdt_disable();
panicPutStr("Entering gdb stub now.\r\n");
esp_gdbstub_panic_handler(frame);
#else
#if CONFIG_ESP32_ENABLE_COREDUMP
static bool s_dumping_core;
if (s_dumping_core) {
panicPutStr("Re-entered core dump! Exception happened during core dump!\r\n");
} else {
disableAllWdts();
s_dumping_core = true;
#if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH
esp_core_dump_to_flash(frame);
#endif
#if CONFIG_ESP32_ENABLE_COREDUMP_TO_UART && !CONFIG_ESP32S2_PANIC_SILENT_REBOOT
esp_core_dump_to_uart(frame);
#endif
s_dumping_core = false;
reconfigureAllWdts();
}
#endif /* CONFIG_ESP32_ENABLE_COREDUMP */
rtc_wdt_disable();
#if CONFIG_ESP32S2_PANIC_PRINT_REBOOT || CONFIG_ESP32S2_PANIC_SILENT_REBOOT
panicPutStr("Rebooting...\r\n");
if (frame->exccause != PANIC_RSN_CACHEERR) {
esp_restart_noos();
} else {
// The only way to clear invalid cache access interrupt is to reset the digital part
esp_panic_dig_reset();
}
#else
disableAllWdts();
panicPutStr("CPU halted.\r\n");
while (1);
#endif /* CONFIG_ESP32S2_PANIC_PRINT_REBOOT || CONFIG_ESP32S2_PANIC_SILENT_REBOOT */
#endif /* CONFIG_ESP32S2_PANIC_GDBSTUB */
}
void esp_set_breakpoint_if_jtag(void *fn)
{
if (esp_cpu_in_ocd_debug_mode()) {
setFirstBreakpoint((uint32_t)fn);
}
}
void _esp_error_check_failed(esp_err_t rc, const char *file, int line, const char *function, const char *expression)
{
ets_printf("ESP_ERROR_CHECK failed: esp_err_t 0x%x", rc);
#ifdef CONFIG_ESP_ERR_TO_NAME_LOOKUP
ets_printf(" (%s)", esp_err_to_name(rc));
#endif //CONFIG_ESP_ERR_TO_NAME_LOOKUP
ets_printf(" at 0x%08x\n", (intptr_t)__builtin_return_address(0) - 3);
if (spi_flash_cache_enabled()) { // strings may be in flash cache
ets_printf("file: \"%s\" line %d\nfunc: %s\nexpression: %s\n", file, line, function, expression);
}
invoke_abort();
}

View file

@ -7,13 +7,13 @@ if(BOOTLOADER_BUILD)
else()
# Regular app build
set(srcs "src/brownout.c"
"src/esp_err.c"
"src/dbg_stubs.c"
"src/esp_err_to_name.c"
"src/freertos_hooks.c"
"src/mac_addr.c"
"src/pm_locks.c"
"src/stack_check.c"
"src/system_api.c")
"src/stack_check.c")
# IPC framework is not applicable if freertos unicore config is selected
if(NOT CONFIG_FREERTOS_UNICORE)
@ -22,8 +22,9 @@ else()
idf_component_register(SRCS "${srcs}"
INCLUDE_DIRS include
REQUIRES ${target} esp_timer
PRIV_REQUIRES soc)
REQUIRES ${target} espcoredump esp_timer
PRIV_REQUIRES soc
LDFRAGMENTS "linker.lf")
set_source_files_properties(
"src/stack_check.c"

View file

@ -227,4 +227,4 @@ menu "Common ESP-related"
config ESP_MAC_ADDR_UNIVERSE_ETH
bool
endmenu # Common ESP-related
endmenu # Common ESP-related

View file

@ -0,0 +1,4 @@
[mapping:esp_common]
archive: lib_esp_common.a
entries:
esp_err (noflash)

View file

@ -25,4 +25,4 @@ CONFIG_TASK_WDT_PANIC CONFIG_ESP_TASK_WDT_PANI
CONFIG_TASK_WDT_TIMEOUT_S CONFIG_ESP_TASK_WDT_TIMEOUT_S
CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU0 CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0
CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU1 CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU1
CONFIG_ESP32_DEBUG_STUBS_ENABLE CONFIG_ESP_DEBUG_STUBS_ENABLE
CONFIG_ESP32_DEBUG_STUBS_ENABLE CONFIG_ESP_DEBUG_STUBS_ENABLE

View file

@ -0,0 +1,47 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed 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.
#include <stdbool.h>
#include <string.h>
#include "esp_err.h"
#include "esp_spi_flash.h"
#if CONFIG_IDF_TARGET_ESP32
#include "esp32/rom/ets_sys.h"
#elif CONFIG_IDF_TARGET_ESP32S2
#include "esp32s2/rom/ets_sys.h"
#endif
static void esp_error_check_failed_print(const char *msg, esp_err_t rc, const char *file, int line, const char *function, const char *expression)
{
ets_printf("%s failed: esp_err_t 0x%x", msg, rc);
#ifdef CONFIG_ESP_ERR_TO_NAME_LOOKUP
ets_printf(" (%s)", esp_err_to_name(rc));
#endif //CONFIG_ESP_ERR_TO_NAME_LOOKUP
ets_printf(" at 0x%08x\n", (intptr_t)__builtin_return_address(0) - 3);
if (spi_flash_cache_enabled()) { // strings may be in flash cache
ets_printf("file: \"%s\" line %d\nfunc: %s\nexpression: %s\n", file, line, function, expression);
}
}
void _esp_error_check_failed_without_abort(esp_err_t rc, const char *file, int line, const char *function, const char *expression)
{
esp_error_check_failed_print("ESP_ERROR_CHECK_WITHOUT_ABORT", rc, file, line, function, expression);
}
void _esp_error_check_failed(esp_err_t rc, const char *file, int line, const char *function, const char *expression)
{
esp_error_check_failed_print("ESP_ERROR_CHECK", rc, file, line, function, expression);
abort();
}

View file

@ -0,0 +1,7 @@
idf_component_register(SRCS "panic.c" "system_api.c"
INCLUDE_DIRS include
PRIV_INCLUDE_DIRS private_include
PRIV_REQUIRES spi_flash app_update
LDFRAGMENTS "linker.lf")
add_subdirectory(port)

View file

@ -0,0 +1,35 @@
menu "ESP System Settings"
choice ESP_SYSTEM_PANIC
prompt "Panic handler behaviour"
default ESP_SYSTEM_PANIC_PRINT_REBOOT
help
If FreeRTOS detects unexpected behaviour or an unhandled exception, the panic handler is
invoked. Configure the panic handler's action here.
config ESP_SYSTEM_PANIC_PRINT_HALT
bool "Print registers and halt"
help
Outputs the relevant registers over the serial port and halt the
processor. Needs a manual reset to restart.
config ESP_SYSTEM_PANIC_PRINT_REBOOT
bool "Print registers and reboot"
help
Outputs the relevant registers over the serial port and immediately
reset the processor.
config ESP_SYSTEM_PANIC_SILENT_REBOOT
bool "Silent reboot"
help
Just resets the processor without outputting anything
config ESP_SYSTEM_PANIC_GDBSTUB
bool "Invoke GDBStub"
select ESP_GDBSTUB_ENABLED
help
Invoke gdbstub on the serial port, allowing for gdb to attach to it to do a postmortem
of the crash.
endchoice
endmenu # ESP System Settings

View file

@ -0,0 +1,8 @@
SOC_NAME := $(IDF_TARGET)
COMPONENT_SRCDIRS := .
COMPONENT_ADD_INCLUDEDIRS := include
COMPONENT_PRIV_INCLUDEDIRS := private_include port/include
COMPONENT_ADD_LDFRAGMENTS += linker.lf
-include $(COMPONENT_PATH)/port/$(SOC_NAME)/component.mk

View file

@ -223,7 +223,7 @@ esp_err_t esp_efuse_mac_get_default(uint8_t *mac);
esp_err_t esp_read_mac(uint8_t* mac, esp_mac_type_t type);
/**
* @brief Derive local MAC address from universal MAC address.
* @brief Derive local MAC address from universal MAC address.
*
* This function derives a local MAC address from an universal MAC address.
* A `definition of local vs universal MAC address can be found on Wikipedia
@ -238,6 +238,13 @@ esp_err_t esp_read_mac(uint8_t* mac, esp_mac_type_t type);
*/
esp_err_t esp_derive_local_mac(uint8_t* local_mac, const uint8_t* universal_mac);
/**
* @brief Trigger a software abort
*
* @param details Details that will be displayed during panic handling.
*/
void __attribute__((noreturn)) esp_system_abort(const char* details);
/**
* @brief Chip models
*/

View file

@ -0,0 +1,7 @@
[mapping:esp_system]
archive: libesp_system.a
entries:
panic (noflash)
panic_handler (noflash)
reset_reason (noflash)
system_api:esp_system_abort (noflash)

View file

@ -0,0 +1,334 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed 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.
#include <stdlib.h>
#include "esp_err.h"
#include "esp_attr.h"
#include "esp_spi_flash.h"
#include "esp_private/system_internal.h"
#include "esp_private/gdbstub.h"
#include "esp_ota_ops.h"
#if CONFIG_APPTRACE_ENABLE
#include "esp_app_trace.h"
#if CONFIG_SYSVIEW_ENABLE
#include "SEGGER_RTT.h"
#endif
#endif // CONFIG_APPTRACE_ENABLE
#include "esp_core_dump.h"
#include "soc/rtc_wdt.h"
#include "soc/cpu.h"
#include "hal/timer_hal.h"
#include "hal/cpu_hal.h"
#if !CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT
#include <string.h>
#include "hal/uart_hal.h"
#endif
#include "panic_internal.h"
#include "port/panic_funcs.h"
#include "sdkconfig.h"
#if CONFIG_APPTRACE_ONPANIC_HOST_FLUSH_TMO == -1
#define APPTRACE_ONPANIC_HOST_FLUSH_TMO ESP_APPTRACE_TMO_INFINITE
#else
#define APPTRACE_ONPANIC_HOST_FLUSH_TMO (1000*CONFIG_APPTRACE_ONPANIC_HOST_FLUSH_TMO)
#endif
bool g_panic_abort = false;
static char *s_panic_abort_details = NULL;
#if !CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT
static uart_hal_context_t s_panic_uart = { .dev = CONFIG_ESP_CONSOLE_UART_NUM == 0 ? &UART0 : &UART1 };
void panic_print_char(const char c)
{
uint32_t sz = 0;
while(!uart_hal_get_txfifo_len(&s_panic_uart));
uart_hal_write_txfifo(&s_panic_uart, (uint8_t*) &c, 1, &sz);
}
void panic_print_str(const char *str)
{
for(int i = 0; str[i] != 0; i++) {
panic_print_char(str[i]);
}
}
void panic_print_hex(int h)
{
int x;
int c;
// Does not print '0x', only the digits (8 digits to print)
for (x = 0; x < 8; x++) {
c = (h >> 28) & 0xf; // extract the leftmost byte
if (c < 10) {
panic_print_char('0' + c);
} else {
panic_print_char('a' + c - 10);
}
h <<= 4; // move the 2nd leftmost byte to the left, to be extracted next
}
}
void panic_print_dec(int d)
{
// can print at most 2 digits!
int n1, n2;
n1 = d % 10; // extract ones digit
n2 = d / 10; // extract tens digit
if (n2 == 0) {
panic_print_char(' ');
} else {
panic_print_char(n2 + '0');
}
panic_print_char(n1 + '0');
}
#endif // CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT
/*
If watchdogs are enabled, the panic handler runs the risk of getting aborted pre-emptively because
an overzealous watchdog decides to reset it. On the other hand, if we disable all watchdogs, we run
the risk of somehow halting in the panic handler and not resetting. That is why this routine kills
all watchdogs except the timer group 0 watchdog, and it reconfigures that to reset the chip after
one second.
*/
static void reconfigure_all_wdts(void)
{
timer_ll_wdt_set_protect(&TIMERG0, false);
timer_ll_wdt_feed(&TIMERG0);
timer_ll_wdt_init(&TIMERG0);
timer_ll_wdt_set_tick(&TIMERG0, TG0_WDT_TICK_US); //Prescaler: wdt counts in ticks of TG0_WDT_TICK_US
//1st stage timeout: reset system
timer_ll_wdt_set_timeout_behavior(&TIMERG0, 0, TIMER_WDT_RESET_SYSTEM);
//1 second before reset
timer_ll_wdt_set_timeout(&TIMERG0, 0, 1000*1000/TG0_WDT_TICK_US);
timer_ll_wdt_set_enable(&TIMERG0, true);
timer_ll_wdt_set_protect(&TIMERG0, true);
//Disable wdt 1
timer_ll_wdt_set_protect(&TIMERG1, false);
timer_ll_wdt_set_enable(&TIMERG1, false);
timer_ll_wdt_set_protect(&TIMERG1, true);
}
/*
This disables all the watchdogs for when we call the gdbstub.
*/
static inline void disable_all_wdts(void)
{
timer_ll_wdt_set_protect(&TIMERG0, false);
timer_ll_wdt_set_enable(&TIMERG0, false);
timer_ll_wdt_set_protect(&TIMERG0, true);
timer_ll_wdt_set_protect(&TIMERG1, false);
timer_ll_wdt_set_enable(&TIMERG1, false);
timer_ll_wdt_set_protect(&TIMERG1, true);
}
static void print_abort_details(const void *f)
{
panic_print_str(s_panic_abort_details);
}
// Control arrives from chip-specific panic handler, environment prepared for
// the 'main' logic of panic handling. This means that chip-specific stuff have
// already been done, and panic_info_t has been filled.
void esp_panic_handler(panic_info_t *info)
{
// If the exception was due to an abort, override some of the panic info
if (g_panic_abort) {
info->description = NULL;
info->details = s_panic_abort_details ? print_abort_details : NULL;
info->reason = NULL;
info->exception = PANIC_EXCEPTION_ABORT;
}
/*
* For any supported chip, the panic handler prints the contents of panic_info_t in the following format:
*
*
* Guru Meditation Error: Core <core> (<exception>). <description>
* <details>
*
* <state>
*
* <elf_info>
*
*
* ----------------------------------------------------------------------------------------
* core - core where exception was triggered
* exception - what kind of exception occured
* description - a short description regarding the exception that occured
* details - more details about the exception
* state - processor state like register contents, and backtrace
* elf_info - details about the image currently running
*
* NULL fields in panic_info_t are not printed.
*
* */
if (info->reason) {
panic_print_str("Guru Meditation Error: Core ");
panic_print_dec(info->core);
panic_print_str(" panic'ed (");
panic_print_str(info->reason);
panic_print_str("). ");
}
if (info->description) {
panic_print_str(info->description);
}
panic_print_str("\r\n");
PANIC_INFO_DUMP(info, details);
panic_print_str("\r\n");
// If on-chip-debugger is attached, and system is configured to be aware of this,
// then only print up to details. Users should be able to probe for the other information
// in debug mode.
if (esp_cpu_in_ocd_debug_mode()) {
panic_print_str("Setting breakpoint at 0x");
panic_print_hex((uint32_t)info->addr);
panic_print_str(" and returning...\r\n");
disable_all_wdts();
#if CONFIG_APPTRACE_ENABLE
#if CONFIG_SYSVIEW_ENABLE
SEGGER_RTT_ESP32_FlushNoLock(CONFIG_APPTRACE_POSTMORTEM_FLUSH_THRESH, APPTRACE_ONPANIC_HOST_FLUSH_TMO);
#else
esp_apptrace_flush_nolock(ESP_APPTRACE_DEST_TRAX, CONFIG_APPTRACE_POSTMORTEM_FLUSH_THRESH,
APPTRACE_ONPANIC_HOST_FLUSH_TMO);
#endif
#endif
cpu_hal_set_breakpoint(0, info->addr); // use breakpoint 0
return;
}
// start panic WDT to restart system if we hang in this handler
if (!rtc_wdt_is_on()) {
rtc_wdt_protect_off();
rtc_wdt_disable();
rtc_wdt_set_length_of_reset_signal(RTC_WDT_SYS_RESET_SIG, RTC_WDT_LENGTH_3_2us);
rtc_wdt_set_length_of_reset_signal(RTC_WDT_CPU_RESET_SIG, RTC_WDT_LENGTH_3_2us);
rtc_wdt_set_stage(RTC_WDT_STAGE0, RTC_WDT_STAGE_ACTION_RESET_SYSTEM);
// 64KB of core dump data (stacks of about 30 tasks) will produce ~85KB base64 data.
// @ 115200 UART speed it will take more than 6 sec to print them out.
rtc_wdt_set_time(RTC_WDT_STAGE0, 7000);
rtc_wdt_enable();
rtc_wdt_protect_on();
}
//Feed the watchdogs, so they will give us time to print out debug info
reconfigure_all_wdts();
PANIC_INFO_DUMP(info, state);
panic_print_str("\r\n");
panic_print_str("\r\nELF file SHA256: ");
char sha256_buf[65];
esp_ota_get_app_elf_sha256(sha256_buf, sizeof(sha256_buf));
panic_print_str(sha256_buf);
panic_print_str("\r\n");
panic_print_str("\r\n");
#if CONFIG_APPTRACE_ENABLE
disable_all_wdts();
#if CONFIG_SYSVIEW_ENABLE
SEGGER_RTT_ESP32_FlushNoLock(CONFIG_APPTRACE_POSTMORTEM_FLUSH_THRESH, APPTRACE_ONPANIC_HOST_FLUSH_TMO);
#else
esp_apptrace_flush_nolock(ESP_APPTRACE_DEST_TRAX, CONFIG_APPTRACE_POSTMORTEM_FLUSH_THRESH,
APPTRACE_ONPANIC_HOST_FLUSH_TMO);
#endif
reconfigure_all_wdts();
#endif
#if CONFIG_ESP_SYSTEM_PANIC_GDBSTUB
disable_all_wdts();
rtc_wdt_disable();
panic_print_str("Entering gdb stub now.\r\n");
esp_gdbstub_panic_handler((XtExcFrame*) info->frame);
#else
#if CONFIG_ESP32_ENABLE_COREDUMP
static bool s_dumping_core;
if (s_dumping_core) {
panic_print_str("Re-entered core dump! Exception happened during core dump!\r\n");
} else {
disable_all_wdts();
s_dumping_core = true;
#if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH
esp_core_dump_to_flash((XtExcFrame*) info->frame);
#endif
#if CONFIG_ESP32_ENABLE_COREDUMP_TO_UART && !CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT
esp_core_dump_to_uart((XtExcFrame*) info->frame);
#endif
s_dumping_core = false;
reconfigure_all_wdts();
}
#endif /* CONFIG_ESP32_ENABLE_COREDUMP */
rtc_wdt_disable();
#if CONFIG_ESP_SYSTEM_PANIC_PRINT_REBOOT || CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT
if (esp_reset_reason_get_hint() == ESP_RST_UNKNOWN) {
switch (info->exception)
{
case PANIC_EXCEPTION_IWDT:
esp_reset_reason_set_hint(ESP_RST_INT_WDT);
break;
case PANIC_EXCEPTION_TWDT:
esp_reset_reason_set_hint(ESP_RST_TASK_WDT);
break;
case PANIC_EXCEPTION_ABORT:
case PANIC_EXCEPTION_FAULT:
default:
esp_reset_reason_set_hint(ESP_RST_PANIC);
break; // do not touch the previously set reset reason hint
}
}
panic_print_str("Rebooting...\r\n");
panic_restart();
#else
disable_all_wdts();
panic_print_str("CPU halted.\r\n");
while (1);
#endif /* CONFIG_ESP_SYSTEM_PANIC_PRINT_REBOOT || CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT */
#endif /* CONFIG_ESP_SYSTEM_PANIC_GDBSTUB */
}
void __attribute__((noreturn)) panic_abort(const char *details)
{
g_panic_abort = true;
s_panic_abort_details = (char*) details;
#if CONFIG_APPTRACE_ENABLE
#if CONFIG_SYSVIEW_ENABLE
SEGGER_RTT_ESP32_FlushNoLock(CONFIG_APPTRACE_POSTMORTEM_FLUSH_THRESH, APPTRACE_ONPANIC_HOST_FLUSH_TMO);
#else
esp_apptrace_flush_nolock(ESP_APPTRACE_DEST_TRAX, CONFIG_APPTRACE_POSTMORTEM_FLUSH_THRESH,
APPTRACE_ONPANIC_HOST_FLUSH_TMO);
#endif
#endif
*((int *) 0) = 0; // should be an invalid operation on targets
while(1);
}

View file

@ -0,0 +1,9 @@
target_include_directories(${COMPONENT_LIB} PRIVATE include)
set(srcs "panic_handler.c" "panic_handler_asm.S")
add_prefix(srcs "${CMAKE_CURRENT_LIST_DIR}/" ${srcs})
target_sources(${COMPONENT_LIB} PRIVATE ${srcs})
idf_build_get_property(target IDF_TARGET)
add_subdirectory(${target})

View file

@ -0,0 +1,2 @@
target_sources(${COMPONENT_LIB} PRIVATE "${CMAKE_CURRENT_LIST_DIR}/dport_panic_highint_hdl.S"
"${CMAKE_CURRENT_LIST_DIR}/reset_reason.c")

View file

@ -0,0 +1 @@
COMPONENT_SRCDIRS += port port/esp32

View file

@ -0,0 +1,2 @@
target_sources(${COMPONENT_LIB} PRIVATE "${CMAKE_CURRENT_LIST_DIR}/dport_panic_highint_hdl.S"
"${CMAKE_CURRENT_LIST_DIR}/reset_reason.c")

View file

@ -0,0 +1,17 @@
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed 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.
#pragma once
void __attribute__((noreturn)) panic_restart(void);

View file

@ -0,0 +1,538 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed 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.
#include <stdlib.h>
#include "freertos/xtensa_context.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_spi_flash.h"
#include "esp_private/panic_reason.h"
#include "esp_private/system_internal.h"
#include "esp_debug_helpers.h"
#include "soc/soc_memory_layout.h"
#include "soc/cpu.h"
#include "soc/soc_caps.h"
#include "soc/rtc.h"
#include "hal/soc_hal.h"
#include "hal/cpu_hal.h"
#include "hal/timer_hal.h"
#include "sdkconfig.h"
#if CONFIG_IDF_TARGET_ESP32
#include "esp32/cache_err_int.h"
#include "esp32/dport_access.h"
#include "esp32/rom/uart.h"
#elif CONFIG_IDF_TARGET_ESP32S2
#include "esp32s2/cache_err_int.h"
#include "esp32s2/rom/uart.h"
#include "soc/extmem_reg.h"
#include "soc/cache_memory.h"
#include "soc/rtc_cntl_reg.h"
#endif
#include "panic_internal.h"
extern void esp_panic_handler(panic_info_t*);
static XtExcFrame* xt_exc_frames[SOC_CPU_CORES_NUM] = {NULL};
/*
Panic handlers; these get called when an unhandled exception occurs or the assembly-level
task switching / interrupt code runs into an unrecoverable error. The default task stack
overflow handler and abort handler are also in here.
*/
/*
Note: The linker script will put everything in this file in IRAM/DRAM, so it also works with flash cache disabled.
*/
static void print_illegal_instruction_details(const void* f)
{
XtExcFrame* frame = (XtExcFrame*) f;
/* Print out memory around the instruction word */
uint32_t epc = frame->pc;
epc = (epc & ~0x3) - 4;
/* check that the address was sane */
if (epc < SOC_IROM_MASK_LOW || epc >= SOC_IROM_HIGH) {
return;
}
volatile uint32_t* pepc = (uint32_t*)epc;
panic_print_str("Memory dump at 0x");
panic_print_hex(epc);
panic_print_str(": ");
panic_print_hex(*pepc);
panic_print_str(" ");
panic_print_hex(*(pepc + 1));
panic_print_str(" ");
panic_print_hex(*(pepc + 2));
}
static void print_debug_exception_details(const void* f)
{
int debug_rsn;
asm("rsr.debugcause %0":"=r"(debug_rsn));
panic_print_str("Debug exception reason: ");
if (debug_rsn & XCHAL_DEBUGCAUSE_ICOUNT_MASK) {
panic_print_str("SingleStep ");
}
if (debug_rsn & XCHAL_DEBUGCAUSE_IBREAK_MASK) {
panic_print_str("HwBreakpoint ");
}
if (debug_rsn & XCHAL_DEBUGCAUSE_DBREAK_MASK) {
//Unlike what the ISA manual says, this core seemingly distinguishes from a DBREAK
//reason caused by watchdog 0 and one caused by watchdog 1 by setting bit 8 of the
//debugcause if the cause is watchpoint 1 and clearing it if it's watchpoint 0.
if (debug_rsn & (1 << 8)) {
#if CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK
int core = 0;
#if !CONFIG_FREERTOS_UNICORE
if (f == xt_exc_frames[1]) {
core = 1;
}
#endif
const char *name = pcTaskGetTaskName(xTaskGetCurrentTaskHandleForCPU(core));
panic_print_str("Stack canary watchpoint triggered (");
panic_print_str(name);
panic_print_str(") ");
#else
panic_print_str("Watchpoint 1 triggered ");
#endif
} else {
panic_print_str("Watchpoint 0 triggered ");
}
}
if (debug_rsn & XCHAL_DEBUGCAUSE_BREAK_MASK) {
panic_print_str("BREAK instr ");
}
if (debug_rsn & XCHAL_DEBUGCAUSE_BREAKN_MASK) {
panic_print_str("BREAKN instr ");
}
if (debug_rsn & XCHAL_DEBUGCAUSE_DEBUGINT_MASK) {
panic_print_str("DebugIntr ");
}
}
static void print_backtrace_entry(uint32_t pc, uint32_t sp)
{
panic_print_str("0x");
panic_print_hex(pc);
panic_print_str(":0x");
panic_print_hex(sp);
}
static void print_backtrace(const void* f, int core)
{
XtExcFrame *frame = (XtExcFrame*) f;
int depth = 100;
//Initialize stk_frame with first frame of stack
esp_backtrace_frame_t stk_frame = {.pc = frame->pc, .sp = frame->a1, .next_pc = frame->a0};
panic_print_str("\r\nBacktrace:");
print_backtrace_entry(esp_cpu_process_stack_pc(stk_frame.pc), stk_frame.sp);
//Check if first frame is valid
bool corrupted = !(esp_stack_ptr_is_sane(stk_frame.sp) &&
esp_ptr_executable((void*)esp_cpu_process_stack_pc(stk_frame.pc)));
uint32_t i = ((depth <= 0) ? INT32_MAX : depth) - 1; //Account for stack frame that's already printed
while (i-- > 0 && stk_frame.next_pc != 0 && !corrupted) {
if (!esp_backtrace_get_next_frame(&stk_frame)) { //Get next stack frame
corrupted = true;
}
panic_print_str(" ");
print_backtrace_entry(esp_cpu_process_stack_pc(stk_frame.pc), stk_frame.sp);
}
//Print backtrace termination marker
if (corrupted) {
panic_print_str(" |<-CORRUPTED");
} else if (stk_frame.next_pc != 0) { //Backtrace continues
panic_print_str(" |<-CONTINUES");
}
}
static void print_registers(const void *f, int core)
{
XtExcFrame* frame = (XtExcFrame*) f;
int *regs = (int *)frame;
int x, y;
const char *sdesc[] = {
"PC ", "PS ", "A0 ", "A1 ", "A2 ", "A3 ", "A4 ", "A5 ",
"A6 ", "A7 ", "A8 ", "A9 ", "A10 ", "A11 ", "A12 ", "A13 ",
"A14 ", "A15 ", "SAR ", "EXCCAUSE", "EXCVADDR", "LBEG ", "LEND ", "LCOUNT "
};
/* only dump registers for 'real' crashes, if crashing via abort()
the register window is no longer useful.
*/
panic_print_str("Core ");
panic_print_dec(core);
panic_print_str(" register dump:");
for (x = 0; x < 24; x += 4) {
panic_print_str("\r\n");
for (y = 0; y < 4; y++) {
if (sdesc[x + y][0] != 0) {
panic_print_str(sdesc[x + y]);
panic_print_str(": 0x");
panic_print_hex(regs[x + y + 1]);
panic_print_str(" ");
}
}
}
// If the core which triggers the interrupt watchpoint was in ISR context, dump the epc registers.
if (xPortInterruptedFromISRContext()
#if !CONFIG_FREERTOS_UNICORE
&& ((core == 0 && frame->exccause == PANIC_RSN_INTWDT_CPU0) ||
(core == 1 && frame->exccause == PANIC_RSN_INTWDT_CPU1))
#endif //!CONFIG_FREERTOS_UNICORE
) {
panic_print_str("\r\n");
uint32_t __value;
panic_print_str("Core ");
panic_print_dec(core);
panic_print_str(" was running in ISR context:\r\n");
__asm__("rsr.epc1 %0" : "=a"(__value));
panic_print_str("EPC1 : 0x");
panic_print_hex(__value);
__asm__("rsr.epc2 %0" : "=a"(__value));
panic_print_str(" EPC2 : 0x");
panic_print_hex(__value);
__asm__("rsr.epc3 %0" : "=a"(__value));
panic_print_str(" EPC3 : 0x");
panic_print_hex(__value);
__asm__("rsr.epc4 %0" : "=a"(__value));
panic_print_str(" EPC4 : 0x");
panic_print_hex(__value);
}
}
static void print_state_for_core(const void *f, int core)
{
if (!g_panic_abort) {
print_registers(f, core);
panic_print_str("\r\n");
}
print_backtrace(f, core);
}
static void print_state(const void* f)
{
#if !CONFIG_FREERTOS_UNICORE
int err_core = f == xt_exc_frames[0] ? 0 : 1;
#else
int err_core = 0;
#endif
print_state_for_core(f, err_core);
panic_print_str("\r\n");
#if !CONFIG_FREERTOS_UNICORE
// If there are other frame info, print them as well
for (int i = 0; i < SOC_CPU_CORES_NUM; i++) {
// `f` is the frame for the offending core, see note above.
if (err_core != i && xt_exc_frames[i] != NULL) {
print_state_for_core(xt_exc_frames[i], i);
panic_print_str("\r\n");
}
}
#endif
}
#if CONFIG_IDF_TARGET_ESP32S2
static inline void print_cache_err_details(const void* f)
{
uint32_t vaddr = 0, size = 0;
uint32_t status[2];
status[0] = REG_READ(EXTMEM_CACHE_DBG_STATUS0_REG);
status[1] = REG_READ(EXTMEM_CACHE_DBG_STATUS1_REG);
for (int i = 0; i < 32; i++) {
switch (status[0] & BIT(i)) {
case EXTMEM_IC_SYNC_SIZE_FAULT_ST:
vaddr = REG_READ(EXTMEM_PRO_ICACHE_MEM_SYNC0_REG);
size = REG_READ(EXTMEM_PRO_ICACHE_MEM_SYNC1_REG);
panic_print_str("Icache sync parameter configuration error, the error address and size is 0x");
panic_print_hex(vaddr);
panic_print_str("(0x");
panic_print_hex(size);
panic_print_str(")\r\n");
break;
case EXTMEM_IC_PRELOAD_SIZE_FAULT_ST:
vaddr = REG_READ(EXTMEM_PRO_ICACHE_PRELOAD_ADDR_REG);
size = REG_READ(EXTMEM_PRO_ICACHE_PRELOAD_SIZE_REG);
panic_print_str("Icache preload parameter configuration error, the error address and size is 0x");
panic_print_hex(vaddr);
panic_print_str("(0x");
panic_print_hex(size);
panic_print_str(")\r\n");
break;
case EXTMEM_ICACHE_REJECT_ST:
vaddr = REG_READ(EXTMEM_PRO_ICACHE_REJECT_VADDR_REG);
panic_print_str("Icache reject error occurred while accessing the address 0x");
panic_print_hex(vaddr);
if (REG_READ(EXTMEM_PRO_CACHE_MMU_FAULT_CONTENT_REG) & MMU_INVALID) {
panic_print_str(" (invalid mmu entry)");
}
panic_print_str("\r\n");
break;
default:
break;
}
switch (status[1] & BIT(i)) {
case EXTMEM_DC_SYNC_SIZE_FAULT_ST:
vaddr = REG_READ(EXTMEM_PRO_DCACHE_MEM_SYNC0_REG);
size = REG_READ(EXTMEM_PRO_DCACHE_MEM_SYNC1_REG);
panic_print_str("Dcache sync parameter configuration error, the error address and size is 0x");
panic_print_hex(vaddr);
panic_print_str("(0x");
panic_print_hex(size);
panic_print_str(")\r\n");
break;
case EXTMEM_DC_PRELOAD_SIZE_FAULT_ST:
vaddr = REG_READ(EXTMEM_PRO_DCACHE_PRELOAD_ADDR_REG);
size = REG_READ(EXTMEM_PRO_DCACHE_PRELOAD_SIZE_REG);
panic_print_str("Dcache preload parameter configuration error, the error address and size is 0x");
panic_print_hex(vaddr);
panic_print_str("(0x");
panic_print_hex(size);
panic_print_str(")\r\n");
break;
case EXTMEM_DCACHE_WRITE_FLASH_ST:
panic_print_str("Write back error occurred while dcache tries to write back to flash\r\n");
break;
case EXTMEM_DCACHE_REJECT_ST:
vaddr = REG_READ(EXTMEM_PRO_DCACHE_REJECT_VADDR_REG);
panic_print_str("Dcache reject error occurred while accessing the address 0x");
panic_print_hex(vaddr);
if (REG_READ(EXTMEM_PRO_CACHE_MMU_FAULT_CONTENT_REG) & MMU_INVALID) {
panic_print_str(" (invalid mmu entry)");
}
panic_print_str("\r\n");
break;
case EXTMEM_MMU_ENTRY_FAULT_ST:
vaddr = REG_READ(EXTMEM_PRO_CACHE_MMU_FAULT_VADDR_REG);
panic_print_str("MMU entry fault error occurred while accessing the address 0x");
panic_print_hex(vaddr);
if (REG_READ(EXTMEM_PRO_CACHE_MMU_FAULT_CONTENT_REG) & MMU_INVALID) {
panic_print_str(" (invalid mmu entry)");
}
panic_print_str("\r\n");
break;
default:
break;
}
}
}
#endif
static void frame_to_panic_info(XtExcFrame *frame, panic_info_t* info, bool pseudo_excause)
{
info->core = cpu_hal_get_core_id();
info->exception = PANIC_EXCEPTION_FAULT;
info->details = NULL;
if (pseudo_excause) {
if (frame->exccause == PANIC_RSN_INTWDT_CPU0) {
info->core = 0;
info->exception = PANIC_EXCEPTION_IWDT;
} else if (frame->exccause == PANIC_RSN_INTWDT_CPU1) {
info->core = 1;
info->exception = PANIC_EXCEPTION_IWDT;
} else if (frame->exccause == PANIC_RSN_CACHEERR) {
info->core = esp_cache_err_get_cpuid();
} else {}
//Please keep in sync with PANIC_RSN_* defines
static const char *pseudo_reason[] = {
"Unknown reason",
"Unhandled debug exception",
"Double exception",
"Unhandled kernel exception",
"Coprocessor exception",
"Interrupt wdt timeout on CPU0",
"Interrupt wdt timeout on CPU1",
#if CONFIG_IDF_TARGET_ESP32
"Cache disabled but cached memory region accessed",
#elif CONFIG_IDF_TARGET_ESP32S2
"Cache exception",
#endif
};
info->reason = pseudo_reason[0];
info->description = NULL;
if (frame->exccause <= PANIC_RSN_MAX) {
info->reason = pseudo_reason[frame->exccause];
}
if (frame->exccause == PANIC_RSN_DEBUGEXCEPTION) {
info->details = print_debug_exception_details;
info->exception = PANIC_EXCEPTION_DEBUG;
}
#if CONFIG_IDF_TARGET_ESP32S2
if(frame->exccause == PANIC_RSN_CACHEERR) {
info->details = print_cache_err_details;
}
#endif
} else {
static const char *reason[] = {
"IllegalInstruction", "Syscall", "InstructionFetchError", "LoadStoreError",
"Level1Interrupt", "Alloca", "IntegerDivideByZero", "PCValue",
"Privileged", "LoadStoreAlignment", "res", "res",
"InstrPDAddrError", "LoadStorePIFDataError", "InstrPIFAddrError", "LoadStorePIFAddrError",
"InstTLBMiss", "InstTLBMultiHit", "InstFetchPrivilege", "res",
"InstrFetchProhibited", "res", "res", "res",
"LoadStoreTLBMiss", "LoadStoreTLBMultihit", "LoadStorePrivilege", "res",
"LoadProhibited", "StoreProhibited", "res", "res",
"Cp0Dis", "Cp1Dis", "Cp2Dis", "Cp3Dis",
"Cp4Dis", "Cp5Dis", "Cp6Dis", "Cp7Dis"
};
if (frame->exccause < (sizeof(reason) / sizeof(char *))) {
info->reason = (reason[frame->exccause]);
} else {
info->reason = "Unknown";
}
info->description = "Exception was unhandled.";
if (info->reason == reason[0]) {
info->details = print_illegal_instruction_details;
}
}
info->state = print_state;
info->addr = ((void*) ((XtExcFrame*) frame)->pc);
info->frame = frame;
}
static void panic_handler(XtExcFrame *frame, bool pseudo_excause)
{
/*
* Setup environment and perform necessary architecture/chip specific
* steps here prior to the system panic handler.
* */
int core_id = cpu_hal_get_core_id();
// If multiple cores arrive at panic handler, save frames for all of them
xt_exc_frames[core_id] = frame;
#if !CONFIG_FREERTOS_UNICORE
// These are cases where both CPUs both go into panic handler. The following code ensures
// only one core proceeds to the system panic handler.
if (pseudo_excause) {
#define BUSY_WAIT_IF_TRUE(b) { if (b) while(1); }
// For WDT expiry, pause the non-offending core - offending core handles panic
BUSY_WAIT_IF_TRUE(frame->exccause == PANIC_RSN_INTWDT_CPU0 && core_id == 1);
BUSY_WAIT_IF_TRUE(frame->exccause == PANIC_RSN_INTWDT_CPU1 && core_id == 0);
// For cache error, pause the non-offending core - offending core handles panic
BUSY_WAIT_IF_TRUE(frame->exccause == PANIC_RSN_CACHEERR && core_id != esp_cache_err_get_cpuid());
}
ets_delay_us(1);
SOC_HAL_STALL_OTHER_CORES();
#endif
#if CONFIG_IDF_TARGET_ESP32
esp_dport_access_int_abort();
#endif
#if !CONFIG_ESP_PANIC_HANDLER_IRAM
// Re-enable CPU cache for current CPU if it was disabled
if (!spi_flash_cache_enabled()) {
spi_flash_enable_cache(core_id);
panic_print_str("Re-enable cpu cache.\r\n");
}
#endif
if (esp_cpu_in_ocd_debug_mode()) {
if (frame->exccause == PANIC_RSN_INTWDT_CPU0 ||
frame->exccause == PANIC_RSN_INTWDT_CPU1) {
timer_ll_wdt_clear_intr_status(&TIMERG1);
}
}
// Convert architecture exception frame into abstracted panic info
panic_info_t info;
frame_to_panic_info(frame, &info, pseudo_excause);
// Call the system panic handler
esp_panic_handler(&info);
}
void panicHandler(XtExcFrame *frame)
{
// This panic handler gets called for when the double exception vector,
// kernel exception vector gets used; as well as handling interrupt-based
// faults cache error, wdt expiry. EXCAUSE register gets written with
// one of PANIC_RSN_* values.
panic_handler(frame, true);
}
void xt_unhandled_exception(XtExcFrame *frame)
{
panic_handler(frame, false);
}
static __attribute__((noreturn)) void esp_digital_reset(void)
{
// make sure all the panic handler output is sent from UART FIFO
uart_tx_wait_idle(CONFIG_ESP_CONSOLE_UART_NUM);
// switch to XTAL (otherwise we will keep running from the PLL)
rtc_clk_cpu_freq_set_xtal();
#if CONFIG_IDF_TARGET_ESP32
esp_cpu_unstall(PRO_CPU_NUM);
#endif
// reset the digital part
SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_SW_SYS_RST);
while (true) {
;
}
}
void __attribute__((noreturn)) panic_restart(void)
{
// If resetting because of a cache error, reset the digital part
// Make sure that the reset reason is not a generic panic reason as well on ESP32S2,
// as esp_cache_err_get_cpuid always returns PRO_CPU_NUM
if (esp_cache_err_get_cpuid() != -1 && esp_reset_reason_get_hint() != ESP_RST_PANIC) {
esp_digital_reset();
} else {
esp_restart_noos();
}
}

View file

@ -0,0 +1,64 @@
#include "freertos/xtensa_rtos.h"
#include "esp_private/panic_reason.h"
#include "soc/soc.h"
#include "sdkconfig.h"
/*
--------------------------------------------------------------------------------
Panic handler.
Should be reached by call0 (preferable) or jump only. If call0, a0 says where
from. If on simulator, display panic message and abort, else loop indefinitely.
--------------------------------------------------------------------------------
*/
.section .iram1,"ax"
.global panicHandler
.global _xt_panic
.type _xt_panic,@function
.align 4
.literal_position
.align 4
_xt_panic:
/* Allocate exception frame and save minimal context. */
mov a0, sp
addi sp, sp, -XT_STK_FRMSZ
s32i a0, sp, XT_STK_A1
#if XCHAL_HAVE_WINDOWED
s32e a0, sp, -12 /* for debug backtrace */
#endif
rsr a0, PS /* save interruptee's PS */
s32i a0, sp, XT_STK_PS
rsr a0, EPC_1 /* save interruptee's PC */
s32i a0, sp, XT_STK_PC
#if XCHAL_HAVE_WINDOWED
s32e a0, sp, -16 /* for debug backtrace */
#endif
s32i a12, sp, XT_STK_A12 /* _xt_context_save requires A12- */
s32i a13, sp, XT_STK_A13 /* A13 to have already been saved */
call0 _xt_context_save
/* Save exc cause and vaddr into exception frame */
rsr a0, EXCCAUSE
s32i a0, sp, XT_STK_EXCCAUSE
rsr a0, EXCVADDR
s32i a0, sp, XT_STK_EXCVADDR
/* _xt_context_save seems to save the current a0, but we need the interuptees a0. Fix this. */
rsr a0, EXCSAVE_1 /* save interruptee's a0 */
s32i a0, sp, XT_STK_A0
/* Set up PS for C, disable all interrupts except NMI and debug, and clear EXCM. */
movi a0, PS_INTLEVEL(5) | PS_UM | PS_WOE
wsr a0, PS
//Call panic handler
mov a6,sp
call4 panicHandler
ret

View file

@ -0,0 +1,70 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed 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.
#pragma once
#include <stdint.h>
#include "port/panic_funcs.h"
#include "sdkconfig.h"
extern bool g_panic_abort;
// Function to print longer amounts of information such as the details
// and backtrace field of panic_info_t. These functions should limit themselves
// to printing to the console and should do other more involved processing,
// and must be aware that the main logic in panic.c has a watchdog timer active.
typedef void (*panic_info_dump_fn_t)(const void* frame);
// Non architecture specific exceptions (generally valid for all targets).
// Can be used to convey to the main logic what exception is being
// dealt with to perform some actions, without knowing the underlying
// architecture/chip-specific exception.
typedef enum {
PANIC_EXCEPTION_DEBUG,
PANIC_EXCEPTION_IWDT,
PANIC_EXCEPTION_TWDT,
PANIC_EXCEPTION_ABORT,
PANIC_EXCEPTION_FAULT, // catch-all for all types of faults
} panic_exception_t;
typedef struct {
int core; // core which triggered panic
panic_exception_t exception; // non-architecture-specific exception code
const char* reason; // exception string
const char* description; // short description of the exception
panic_info_dump_fn_t details; // more details on the exception
panic_info_dump_fn_t state; // processor state, usually the contents of the registers
const void* addr; // instruction address that triggered the exception
const void* frame; // reference to the frame
} panic_info_t;
#define PANIC_INFO_DUMP(info, dump_fn) {if ((info)->dump_fn) (*(info)->dump_fn)((info->frame));}
// Create own print functions, since printf might be broken, and can be silenced
// when CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT
#if !CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT
void panic_print_char(char c);
void panic_print_str(const char *str);
void panic_print_dec(int d);
void panic_print_hex(int h);
#else
#define panic_print_char(c)
#define panic_print_str(str)
#define panic_print_dec(d)
#define panic_print_hex(h)
#endif
void __attribute__((noreturn)) panic_abort(const char *details);

View file

@ -0,0 +1,13 @@
# sdkconfig replacement configurations for deprecated options formatted as
# CONFIG_DEPRECATED_OPTION CONFIG_NEW_OPTION
CONFIG_ESP32_PANIC CONFIG_ESP_SYSTEM_PANIC
CONFIG_ESP32_PANIC_PRINT_HALT CONFIG_ESP_SYSTEM_PANIC_PRINT_HALT
CONFIG_ESP32_PANIC_PRINT_REBOOT CONFIG_ESP_SYSTEM_PANIC_PRINT_REBOOT
CONFIG_ESP32_PANIC_SILENT_REBOOT CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT
CONFIG_ESP32_PANIC_GDBSTUB CONFIG_ESP_SYSTEM_PANIC_GDBSTUB
CONFIG_ESP32S2_PANIC CONFIG_ESP_SYSTEM_PANIC
CONFIG_ESP32S2_PANIC_PRINT_HALT CONFIG_ESP_SYSTEM_PANIC_PRINT_HALT
CONFIG_ESP32S2_PANIC_PRINT_REBOOT CONFIG_ESP_SYSTEM_PANIC_PRINT_REBOOT
CONFIG_ESP32S2_PANIC_SILENT_REBOOT CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT
CONFIG_ESP32S2_PANIC_GDBSTUB CONFIG_ESP_SYSTEM_PANIC_GDBSTUB

View file

@ -4,6 +4,8 @@
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "panic_internal.h"
#define SHUTDOWN_HANDLERS_NO 2
static shutdown_handler_t shutdown_handlers[SHUTDOWN_HANDLERS_NO];
@ -61,3 +63,7 @@ const char* esp_get_idf_version(void)
return IDF_VER;
}
void __attribute__((noreturn)) esp_system_abort(const char* details)
{
panic_abort(details);
}

View file

@ -420,3 +420,18 @@ void __attribute__((optimize("-O3"))) vPortExitCritical(portMUX_TYPE *mux)
}
}
}
void __attribute__((weak)) vApplicationStackOverflowHook( TaskHandle_t xTask, char *pcTaskName )
{
#define ERR_STR1 "***ERROR*** A stack overflow in task "
#define ERR_STR2 " has been detected."
const char *str[] = {ERR_STR1, pcTaskName, ERR_STR2};
char buf[sizeof(ERR_STR1) + CONFIG_FREERTOS_MAX_TASK_NAME_LEN + sizeof(ERR_STR2) + 1 /* null char */] = { 0 };
char *dest = buf;
for (int i = 0 ; i < sizeof(str)/ sizeof(str[0]); i++) {
dest = strcat(dest, str[i]);
}
esp_system_abort(buf);
}

View file

@ -361,96 +361,9 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
.endm
/*
--------------------------------------------------------------------------------
Panic handler.
Should be reached by call0 (preferable) or jump only. If call0, a0 says where
from. If on simulator, display panic message and abort, else loop indefinitely.
--------------------------------------------------------------------------------
*/
.section .iram1,"ax"
.global panicHandler
.global _xt_panic
.type _xt_panic,@function
.align 4
.literal_position
.align 4
_xt_panic:
/* Allocate exception frame and save minimal context. */
mov a0, sp
addi sp, sp, -XT_STK_FRMSZ
s32i a0, sp, XT_STK_A1
#if XCHAL_HAVE_WINDOWED
s32e a0, sp, -12 /* for debug backtrace */
#endif
rsr a0, PS /* save interruptee's PS */
s32i a0, sp, XT_STK_PS
rsr a0, EPC_1 /* save interruptee's PC */
s32i a0, sp, XT_STK_PC
#if XCHAL_HAVE_WINDOWED
s32e a0, sp, -16 /* for debug backtrace */
#endif
s32i a12, sp, XT_STK_A12 /* _xt_context_save requires A12- */
s32i a13, sp, XT_STK_A13 /* A13 to have already been saved */
call0 _xt_context_save
/* Save exc cause and vaddr into exception frame */
rsr a0, EXCCAUSE
s32i a0, sp, XT_STK_EXCCAUSE
rsr a0, EXCVADDR
s32i a0, sp, XT_STK_EXCVADDR
/* _xt_context_save seems to save the current a0, but we need the interuptees a0. Fix this. */
rsr a0, EXCSAVE_1 /* save interruptee's a0 */
s32i a0, sp, XT_STK_A0
/* Set up PS for C, disable all interrupts except NMI and debug, and clear EXCM. */
movi a0, PS_INTLEVEL(5) | PS_UM | PS_WOE
wsr a0, PS
//Call panic handler
mov a6,sp
call4 panicHandler
.align 4
//Call using call0. Prints the hex char in a2. Kills a3, a4, a5
panic_print_hex:
movi a3,0x60000000
movi a4,8
panic_print_hex_loop:
l32i a5, a3, 0x1c
extui a5, a5, 16, 8
bgei a5,64,panic_print_hex_loop
srli a5,a2,28
bgei a5,10,panic_print_hex_a
addi a5,a5,'0'
j panic_print_hex_ok
panic_print_hex_a:
addi a5,a5,'A'-10
panic_print_hex_ok:
s32i a5,a3,0
slli a2,a2,4
addi a4,a4,-1
bnei a4,0,panic_print_hex_loop
movi a5,' '
s32i a5,a3,0
ret
.section .rodata, "a"
.align 4
/*
--------------------------------------------------------------------------------
Hooks to dynamically install handlers for exceptions and interrupts.

View file

@ -1,4 +1,5 @@
set(srcs
"abort.c"
"heap.c"
"locks.c"
"poll.c"

47
components/newlib/abort.c Normal file
View file

@ -0,0 +1,47 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed 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.
#include <stdint.h>
#include <string.h>
#include "esp_system.h"
#include "soc/soc_caps.h"
#include "hal/cpu_hal.h"
void __attribute__((noreturn)) abort(void)
{
#define ERR_STR1 "abort() was called at PC 0x"
#define ERR_STR2 " on core "
_Static_assert(UINTPTR_MAX == 0xffffffff, "abort() assumes 32-bit addresses");
_Static_assert(SOC_CPU_CORES_NUM < 10, "abort() assumes number of cores is 1 to 9");
char addr_buf[9] = { 0 };
char core_buf[2] = { 0 };
char buf[sizeof(ERR_STR1) + sizeof(addr_buf) + sizeof(core_buf) + sizeof(ERR_STR2) + 1 /* null char */] = { 0 };
itoa((uint32_t)(__builtin_return_address(0) - 3), addr_buf, 16);
itoa(cpu_ll_get_core_id(), core_buf, 10);
const char *str[] = { ERR_STR1, addr_buf, ERR_STR2, core_buf };
char *dest = buf;
for (int i = 0; i < sizeof(str) / sizeof(str[0]); i++) {
strcat(dest, str[i]);
}
esp_system_abort(buf);
}

View file

@ -1,6 +1,5 @@
# Places the heap related functions from heap.c into IRAM
[mapping:newlib]
archive: libnewlib.a
entries:
heap (noflash)
abort (noflash)

View file

@ -84,3 +84,9 @@ bool IRAM_ATTR esp_cpu_in_ocd_debug_mode(void)
#endif
}
void IRAM_ATTR esp_set_breakpoint_if_jtag(void *fn)
{
if (esp_cpu_in_ocd_debug_mode()) {
cpu_hal_set_breakpoint(0, fn);
}
}

View file

@ -243,7 +243,7 @@ INPUT = \
$(IDF_PATH)/components/log/include/esp_log.h \
## Base MAC address
## NOTE: for line below header_file.inc is not used
$(IDF_PATH)/components/esp_common/include/esp_system.h \
$(IDF_PATH)/components/esp_system/include/esp_system.h \
## IDF version
$(IDF_PATH)/components/esp_common/include/esp_idf_version.h \
##
@ -284,7 +284,7 @@ INPUT = \
### Helper functions for error codes
$(IDF_PATH)/components/esp_common/include/esp_err.h \
### System APIs
$(IDF_PATH)/components/esp_common/include/esp_system.h \
$(IDF_PATH)/components/esp_system/include/esp_system.h \
### Modbus controller component header file
$(IDF_PATH)/components/freemodbus/common/include/esp_modbus_common.h \
$(IDF_PATH)/components/freemodbus/common/include/esp_modbus_slave.h \

View file

@ -38,21 +38,21 @@ For some of the system level checks (interrupt watchdog, cache access error), th
In all cases, error cause will be printed in parens. See `Guru Meditation Errors`_ for a list of possible error causes.
Subsequent behavior of the panic handler can be set using :ref:`CONFIG_{IDF_TARGET_CFG_PREFIX}_PANIC` configuration choice. The available options are:
- Print registers and reboot — default option.
Subsequent behavior of the panic handler can be set using :ref:`CONFIG_ESP_SYSTEM_PANIC` configuration choice. The available options are:
- Print registers and reboot (``CONFIG_ESP_SYSTEM_PANIC_PRINT_REBOOT``) — default option.
This will print register values at the point of the exception, print the backtrace, and restart the chip.
- Print registers and halt
- Print registers and halt (``CONFIG_ESP_SYSTEM_PANIC_PRINT_HALT``)
Similar to the above option, but halt instead of rebooting. External reset is required to restart the program.
- Silent reboot
- Silent reboot (``CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT``)
Don't print registers or backtrace, restart the chip immediately.
- Invoke GDB Stub
- Invoke GDB Stub (``CONFIG_ESP_SYSTEM_PANIC_GDBSTUB``)
Start GDB server which can communicate with GDB over console UART port. See `GDB Stub`_ for more details.
@ -116,7 +116,7 @@ The following diagram illustrates panic handler behavior:
Register Dump and Backtrace
---------------------------
Unless ``CONFIG_{IDF_TARGET_CFG_PREFIX}_PANIC_SILENT_REBOOT`` option is enabled, panic handler prints some of the CPU registers, and the backtrace, to the console::
Unless ``CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT`` option is enabled, panic handler prints some of the CPU registers, and the backtrace, to the console::
Core 0 register dump:
PC : 0x400e14ed PS : 0x00060030 A0 : 0x800d0805 A1 : 0x3ffb5030
@ -160,7 +160,7 @@ To find the location where a fatal error has happened, look at the lines which f
GDB Stub
--------
If ``CONFIG_{IDF_TARGET_CFG_PREFIX}_PANIC_GDBSTUB`` option is enabled, panic handler will not reset the chip when fatal error happens. Instead, it will start GDB remote protocol server, commonly referred to as GDB Stub. When this happens, GDB instance running on the host computer can be instructed to connect to the {IDF_TARGET_NAME} UART port.
If ``CONFIG_ESP_SYSTEM_PANIC_GDBSTUB`` option is enabled, panic handler will not reset the chip when fatal error happens. Instead, it will start GDB remote protocol server, commonly referred to as GDB Stub. When this happens, GDB instance running on the host computer can be instructed to connect to the ESP32 UART port.
If :doc:`IDF Monitor <tools/idf-monitor>` is used, GDB is started automatically when GDB Stub prompt is detected on the UART. The output would look like this::

View file

@ -103,7 +103,7 @@ By default, if esp-idf crashes, the panic handler prints relevant registers and
Optionally, the panic handler can be configured to run GDBStub, the tool which can communicate with GDB_ project debugger. GDBStub allows to read memory, examine call stack frames and variables, etc. It is not as versatile as JTAG debugging, but this method does not require any special hardware.
To enable GDBStub, open the project configuration menu (``idf.py menuconfig``) and set :ref:`CONFIG_{IDF_TARGET_CFG_PREFIX}_PANIC` to ``Invoke GDBStub``.
To enable GDBStub, open the project configuration menu (``idf.py menuconfig``) and set :ref:`CONFIG_ESP_SYSTEM_PANIC` to ``Invoke GDBStub``.
In this case, if the panic handler is triggered, as soon as IDF Monitor sees that GDBStub has loaded, it automatically pauses serial monitoring and runs GDB with necessary arguments. After GDB exits, the board is reset via the RTS serial line. If this line is not connected, please reset the board manually by pressing its Reset button.

View file

@ -39,21 +39,21 @@
不管哪种情况,错误原因都会被打印在括号中。请参阅 :ref:`Guru-Meditation-Errors` 以查看所有可能的出错原因。
紧急处理程序接下来的行为将取决于 :ref:`CONFIG_{IDF_TARGET_CFG_PREFIX}_PANIC` 的设置,支持的选项包括:
紧急处理程序接下来的行为将取决于 :ref:`CONFIG_ESP_SYSTEM_PANIC` 的设置,支持的选项包括:
- 打印 CPU 寄存器,然后重启(``CONFIG_{IDF_TARGET_CFG_PREFIX}_PANIC_PRINT_REBOOT``- 默认选项
- 打印 CPU 寄存器,然后重启(``CONFIG_ESP_SYSTEM_PANIC_PRINT_REBOOT``- 默认选项
打印系统发生异常时 CPU 寄存器的值,打印回溯,最后重启芯片。
- 打印 CPU 寄存器,然后暂停(``CONFIG_E{IDF_TARGET_CFG_PREFIX}_PANIC_PRINT_HALT``
- 打印 CPU 寄存器,然后暂停(``CONFIG_ESP_SYSTEM_PANIC_PRINT_HALT``
与上一个选项类似,但不会重启,而是选择暂停程序的运行。重启程序需要外部执行复位操作。
- 静默重启(``CONFIG_{IDF_TARGET_CFG_PREFIX}_PANIC_SILENT_REBOOT``
- 静默重启(``CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT``
不打印 CPU 寄存器的值,也不打印回溯,立即重启芯片。
- 调用 GDB Stub``CONFIG_{IDF_TARGET_CFG_PREFIX}_PANIC_GDBSTUB``
- 调用 GDB Stub``CONFIG_ESP_SYSTEM_PANIC_GDBSTUB``
启动 GDB 服务器,通过控制台 UART 接口与 GDB 进行通信。详细信息请参阅 :ref:`GDB-Stub`
@ -112,7 +112,7 @@
寄存器转储与回溯
----------------
除非启用了 ``CONFIG_{IDF_TARGET_CFG_PREFIX}_PANIC_SILENT_REBOOT`` 否则紧急处理程序会将 CPU 寄存器和回溯打印到控制台::
除非启用了 ``CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT`` 否则紧急处理程序会将 CPU 寄存器和回溯打印到控制台::
Core 0 register dump:
PC : 0x400e14ed PS : 0x00060030 A0 : 0x800d0805 A1 : 0x3ffb5030
@ -158,7 +158,7 @@
GDB Stub
--------
如果启用了 ``CONFIG_{IDF_TARGET_CFG_PREFIX}_PANIC_GDBSTUB`` 选项,在发生严重错误时,紧急处理程序不会复位芯片,相反,它将启动 GDB 远程协议服务器,通常称为 GDB Stub。发生这种情况时可以让主机上运行的 GDB 实例通过 UART 端口连接到 {IDF_TARGET_NAME}
如果启用了 ``CONFIG_ESP_SYSTEM_PANIC_GDBSTUB`` 选项,在发生严重错误时,紧急处理程序不会复位芯片,相反,它将启动 GDB 远程协议服务器,通常称为 GDB Stub。发生这种情况时可以让主机上运行的 GDB 实例通过 UART 端口连接到 ESP32
如果使用了 :doc:`IDF 监视器 <tools/idf-monitor>`,该工具会在 UART 端口检测到 GDB Stub 提示符后自动启动 GDB输出会类似于::

View file

@ -92,7 +92,7 @@ IDF 监视器在后台运行以下命令,解码各地址::
或者选择配置 panic 处理器以运行 GDBStubGDBStub 工具可以与 GDB_ 项目调试器进行通信允许读取内存、检查调用堆栈帧和变量等。GDBStub 虽然没有 JTAG 通用,但不需要使用特殊硬件。
如需启用 GDBStub请运行 ``idf.py menuconfig`` (适用于 CMake 编译系统),并将 :ref:`CONFIG_{IDF_TARGET_CFG_PREFIX}_PANIC` 选项设置为 ``Invoke GDBStub``
如需启用 GDBStub请运行 ``idf.py menuconfig`` (适用于 CMake 编译系统),并将 :ref:`CONFIG_ESP_SYSTEM_PANIC` 选项设置为 ``Invoke GDBStub``
在这种情况下,如果 panic 处理器被触发,只要 IDF 监视器监控到 GDBStub 已经加载panic 处理器就会自动暂停串行监控并使用必要的参数运行 GDB。GDB 退出后,通过 RTS 串口线复位开发板。如果未连接 RTS 串口线,请按复位键,手动复位开发板。

View file

@ -1,6 +1,6 @@
CONFIG_APP_BUILD_TYPE_ELF_RAM=y
CONFIG_VFS_SUPPORT_TERMIOS=n
CONFIG_NEWLIB_NANO_FORMAT=y
CONFIG_ESP32_PANIC_PRINT_HALT=y
CONFIG_ESP_SYSTEM_PANIC_PRINT_HALT=y
CONFIG_ESP32_DEBUG_STUBS_ENABLE=n
CONFIG_ESP_ERR_TO_NAME_LOOKUP=n

View file

@ -154,7 +154,7 @@ function(__build_init idf_path)
# Set components required by all other components in the build
#
# - lwip is here so that #include <sys/socket.h> works without any special provisions
set(requires_common cxx newlib freertos heap log lwip soc esp_rom esp_common xtensa)
set(requires_common cxx newlib freertos heap log lwip soc esp_rom esp_common esp_system xtensa)
idf_build_set_property(__COMPONENT_REQUIRES_COMMON "${requires_common}")
__build_get_idf_git_revision()

View file

@ -165,10 +165,10 @@ CONFIG_ESP_CONSOLE_UART_NUM=0
CONFIG_ESP_CONSOLE_UART_BAUDRATE=115200
CONFIG_ESP32_ULP_COPROC_ENABLED=
CONFIG_ESP32_ULP_COPROC_RESERVE_MEM=0
CONFIG_ESP32_PANIC_PRINT_HALT=
CONFIG_ESP32_PANIC_PRINT_REBOOT=y
CONFIG_ESP32_PANIC_SILENT_REBOOT=
CONFIG_ESP32_PANIC_GDBSTUB=
CONFIG_ESP_SYSTEM_PANIC_PRINT_HALT=
CONFIG_ESP_SYSTEM_PANIC_PRINT_REBOOT=y
CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT=
CONFIG_ESP_SYSTEM_PANIC_GDBSTUB=
CONFIG_ESP32_DEBUG_OCDAWARE=y
CONFIG_ESP_INT_WDT=y
CONFIG_ESP_INT_WDT_TIMEOUT_MS=300