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
52 changed files with 1290 additions and 1574 deletions

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,208 +0,0 @@
// Copyright 2015-2017 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 <xtensa/coreasm.h>
#include <xtensa/corebits.h>
#include <xtensa/config/system.h>
#include "freertos/xtensa_context.h"
#include "esp_private/panic_reason.h"
#include "sdkconfig.h"
#include "soc/soc.h"
#include "soc/dport_reg.h"
/*
Interrupt , a high-priority interrupt, is used for several things:
- Dport access mediation
- Cache error panic handler
- Interrupt watchdog panic handler
*/
#define L4_INTR_STACK_SIZE 12
#define L4_INTR_A2_OFFSET 0
#define L4_INTR_A3_OFFSET 4
#define L4_INTR_A4_OFFSET 8
.data
_l4_intr_stack:
.space L4_INTR_STACK_SIZE
.section .iram1,"ax"
.global xt_highint4
.type xt_highint4,@function
.align 4
xt_highint4:
#ifndef CONFIG_FREERTOS_UNICORE
/* See if we're here for the dport access interrupt */
rsr a0, INTERRUPT
extui a0, a0, ETS_DPORT_INUM, 1
bnez a0, .handle_dport_access_int
#endif // CONFIG_FREERTOS_UNICORE
/* 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_4 /* 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 vaddr into exception frame */
rsr a0, EXCVADDR
s32i a0, sp, XT_STK_EXCVADDR
/* Figure out reason, save into EXCCAUSE reg */
rsr a0, INTERRUPT
extui a0, a0, ETS_CACHEERR_INUM, 1 /* get cacheerr int bit */
beqz a0, 1f
/* Kill this interrupt; we cannot reset it. */
rsr a0, INTENABLE
movi a4, ~(1<<ETS_CACHEERR_INUM)
and a0, a4, a0
wsr a0, INTENABLE
movi a0, PANIC_RSN_CACHEERR
j 9f
1:
#if CONFIG_ESP_INT_WDT_CHECK_CPU1
/* Check if the cause is the app cpu failing to tick.*/
movi a0, int_wdt_app_cpu_ticked
l32i a0, a0, 0
bnez a0, 2f
/* It is. Modify cause. */
movi a0,PANIC_RSN_INTWDT_CPU1
j 9f
2:
#endif
/* Set EXCCAUSE to reflect cause of the wdt int trigger */
movi a0,PANIC_RSN_INTWDT_CPU0
9:
/* Found the reason, now save it. */
s32i a0, sp, XT_STK_EXCCAUSE
/* _xt_context_save seems to save the current a0, but we need the interuptees a0. Fix this. */
rsr a0, EXCSAVE_4 /* 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
call0 _xt_context_restore
l32i a0, sp, XT_STK_PS /* retrieve interruptee's PS */
wsr a0, PS
l32i a0, sp, XT_STK_PC /* retrieve interruptee's PC */
wsr a0, EPC_4
l32i a0, sp, XT_STK_A0 /* retrieve interruptee's A0 */
l32i sp, sp, XT_STK_A1 /* remove exception frame */
rsync /* ensure PS and EPC written */
rsr a0, EXCSAVE_4 /* restore a0 */
rfi 4
#ifndef CONFIG_FREERTOS_UNICORE
.align 4
.handle_dport_access_int:
/* This section is for dport access register protection */
/* Allocate exception frame and save minimal context. */
/* Because the interrupt cause code has protection that only
allows one cpu to enter in the dport section of the L4
interrupt at one time, there's no need to have two
_l4_intr_stack for each cpu */
/* This int is edge-triggered and needs clearing. */
movi a0, (1<<ETS_DPORT_INUM)
wsr a0, INTCLEAR
/* Save A2, A3, A4 so we can use those registers */
movi a0, _l4_intr_stack
s32i a2, a0, L4_INTR_A2_OFFSET
s32i a3, a0, L4_INTR_A3_OFFSET
s32i a4, a0, L4_INTR_A4_OFFSET
/* handle dport interrupt */
/* get CORE_ID */
getcoreid a0
beqz a0, 2f
/* current cpu is 1 */
movi a0, DPORT_CPU_INTR_FROM_CPU_3_REG
movi a2, 0
s32i a2, a0, 0 /* clear intr */
movi a0, 0 /* other cpu id */
j 3f
2:
/* current cpu is 0 */
movi a0, DPORT_CPU_INTR_FROM_CPU_2_REG
movi a2, 0
s32i a2, a0, 0 /* clear intr */
movi a0, 1 /* other cpu id */
3:
rsil a4, CONFIG_ESP32_DPORT_DIS_INTERRUPT_LVL /* disable nested iterrupt */
/* set and wait flag */
movi a2, dport_access_start
addx4 a2, a0, a2
movi a3, 1
s32i a3, a2, 0
memw
movi a2, dport_access_end
addx4 a2, a0, a2
.check_dport_access_end:
l32i a3, a2, 0
beqz a3, .check_dport_access_end
wsr a4, PS /* restore iterrupt level */
/* Done. Restore registers and return. */
movi a0, _l4_intr_stack
l32i a2, a0, L4_INTR_A2_OFFSET
l32i a3, a0, L4_INTR_A3_OFFSET
l32i a4, a0, L4_INTR_A4_OFFSET
rsync /* ensure register restored */
rsr a0, EXCSAVE_4 /* restore a0 */
rfi 4
#endif // CONFIG_FREERTOS_UNICORE
/* The linker has no reason to link in this file; all symbols it exports are already defined
(weakly!) in the default int handler. Define a symbol here so we can use it to have the
linker inspect this anyway. */
.global ld_include_panic_highint_hdl
ld_include_panic_highint_hdl:

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

@@ -1,126 +0,0 @@
// Copyright 2018 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 "esp_system.h"
#include "esp32/rom/rtc.h"
#include "esp_private/system_internal.h"
#include "soc/rtc_periph.h"
static void esp_reset_reason_clear_hint(void);
static esp_reset_reason_t s_reset_reason;
static esp_reset_reason_t get_reset_reason(RESET_REASON rtc_reset_reason, esp_reset_reason_t reset_reason_hint)
{
switch (rtc_reset_reason) {
case POWERON_RESET:
return ESP_RST_POWERON;
/* For ESP32, ESP_RST_EXT is never returned */
case SW_CPU_RESET:
case SW_RESET:
case EXT_CPU_RESET: /* unused */
if (reset_reason_hint == ESP_RST_PANIC ||
reset_reason_hint == ESP_RST_BROWNOUT ||
reset_reason_hint == ESP_RST_TASK_WDT ||
reset_reason_hint == ESP_RST_INT_WDT) {
return reset_reason_hint;
}
return ESP_RST_SW;
case DEEPSLEEP_RESET:
return ESP_RST_DEEPSLEEP;
case TG0WDT_SYS_RESET:
return ESP_RST_TASK_WDT;
case TG1WDT_SYS_RESET:
return ESP_RST_INT_WDT;
case OWDT_RESET:
case RTCWDT_SYS_RESET:
case RTCWDT_RTC_RESET:
case RTCWDT_CPU_RESET: /* unused */
case TGWDT_CPU_RESET: /* unused */
return ESP_RST_WDT;
case RTCWDT_BROWN_OUT_RESET: /* unused */
return ESP_RST_BROWNOUT;
case SDIO_RESET:
return ESP_RST_SDIO;
case INTRUSION_RESET: /* unused */
default:
return ESP_RST_UNKNOWN;
}
}
static void __attribute__((constructor)) esp_reset_reason_init(void)
{
esp_reset_reason_t hint = esp_reset_reason_get_hint();
s_reset_reason = get_reset_reason(rtc_get_reset_reason(PRO_CPU_NUM),
hint);
if (hint != ESP_RST_UNKNOWN) {
esp_reset_reason_clear_hint();
}
}
esp_reset_reason_t esp_reset_reason(void)
{
return s_reset_reason;
}
/* Reset reason hint is stored in RTC_RESET_CAUSE_REG, a.k.a. RTC_CNTL_STORE6_REG,
* a.k.a. RTC_ENTRY_ADDR_REG. It is safe to use this register both for the
* deep sleep wake stub entry address and for reset reason hint, since wake stub
* is only used for deep sleep reset, and in this case the reason provided by
* rtc_get_reset_reason is unambiguous.
*
* Same layout is used as for RTC_APB_FREQ_REG (a.k.a. RTC_CNTL_STORE5_REG):
* the value is replicated in low and high half-words. In addition to that,
* MSB is set to 1, which doesn't happen when RTC_CNTL_STORE6_REG contains
* deep sleep wake stub address.
*/
#define RST_REASON_BIT 0x80000000
#define RST_REASON_MASK 0x7FFF
#define RST_REASON_SHIFT 16
/* in IRAM, can be called from panic handler */
void IRAM_ATTR esp_reset_reason_set_hint(esp_reset_reason_t hint)
{
assert((hint & (~RST_REASON_MASK)) == 0);
uint32_t val = hint | (hint << RST_REASON_SHIFT) | RST_REASON_BIT;
REG_WRITE(RTC_RESET_CAUSE_REG, val);
}
/* in IRAM, can be called from panic handler */
esp_reset_reason_t IRAM_ATTR esp_reset_reason_get_hint(void)
{
uint32_t reset_reason_hint = REG_READ(RTC_RESET_CAUSE_REG);
uint32_t high = (reset_reason_hint >> RST_REASON_SHIFT) & RST_REASON_MASK;
uint32_t low = reset_reason_hint & RST_REASON_MASK;
if ((reset_reason_hint & RST_REASON_BIT) == 0 || high != low) {
return ESP_RST_UNKNOWN;
}
return (esp_reset_reason_t) low;
}
static void esp_reset_reason_clear_hint(void)
{
REG_WRITE(RTC_RESET_CAUSE_REG, 0);
}

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