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:
commit
207914a13a
52 changed files with 1290 additions and 1574 deletions
2
Kconfig
2
Kconfig
|
@ -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=
|
||||
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,8 +1,3 @@
|
|||
[mapping:esp32]
|
||||
archive: libesp32.a
|
||||
entries:
|
||||
panic (noflash)
|
||||
|
||||
[mapping:gcc]
|
||||
archive: libgcc.a
|
||||
entries:
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -1,8 +1,3 @@
|
|||
[mapping:esp32s2]
|
||||
archive: libesp32s2.a
|
||||
entries:
|
||||
panic (noflash)
|
||||
|
||||
[mapping:gcc]
|
||||
archive: libgcc.a
|
||||
entries:
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -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"
|
||||
|
|
4
components/esp_common/linker.lf
Normal file
4
components/esp_common/linker.lf
Normal file
|
@ -0,0 +1,4 @@
|
|||
[mapping:esp_common]
|
||||
archive: lib_esp_common.a
|
||||
entries:
|
||||
esp_err (noflash)
|
47
components/esp_common/src/esp_err.c
Normal file
47
components/esp_common/src/esp_err.c
Normal 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();
|
||||
}
|
7
components/esp_system/CMakeLists.txt
Normal file
7
components/esp_system/CMakeLists.txt
Normal 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)
|
35
components/esp_system/Kconfig
Normal file
35
components/esp_system/Kconfig
Normal 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
|
8
components/esp_system/component.mk
Normal file
8
components/esp_system/component.mk
Normal 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
|
|
@ -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
|
||||
*/
|
7
components/esp_system/linker.lf
Normal file
7
components/esp_system/linker.lf
Normal 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)
|
334
components/esp_system/panic.c
Normal file
334
components/esp_system/panic.c
Normal 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);
|
||||
}
|
9
components/esp_system/port/CMakeLists.txt
Normal file
9
components/esp_system/port/CMakeLists.txt
Normal 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})
|
2
components/esp_system/port/esp32/CMakeLists.txt
Normal file
2
components/esp_system/port/esp32/CMakeLists.txt
Normal 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")
|
1
components/esp_system/port/esp32/component.mk
Normal file
1
components/esp_system/port/esp32/component.mk
Normal file
|
@ -0,0 +1 @@
|
|||
COMPONENT_SRCDIRS += port port/esp32
|
2
components/esp_system/port/esp32s2/CMakeLists.txt
Normal file
2
components/esp_system/port/esp32s2/CMakeLists.txt
Normal 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")
|
17
components/esp_system/port/include/port/panic_funcs.h
Normal file
17
components/esp_system/port/include/port/panic_funcs.h
Normal 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);
|
538
components/esp_system/port/panic_handler.c
Normal file
538
components/esp_system/port/panic_handler.c
Normal 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();
|
||||
}
|
||||
}
|
64
components/esp_system/port/panic_handler_asm.S
Normal file
64
components/esp_system/port/panic_handler_asm.S
Normal 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
|
70
components/esp_system/private_include/panic_internal.h
Normal file
70
components/esp_system/private_include/panic_internal.h
Normal 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);
|
13
components/esp_system/sdkconfig.rename
Normal file
13
components/esp_system/sdkconfig.rename
Normal 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
|
||||
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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.
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
set(srcs
|
||||
"abort.c"
|
||||
"heap.c"
|
||||
"locks.c"
|
||||
"poll.c"
|
||||
|
|
47
components/newlib/abort.c
Normal file
47
components/newlib/abort.c
Normal 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);
|
||||
}
|
|
@ -1,6 +1,5 @@
|
|||
# Places the heap related functions from heap.c into IRAM
|
||||
|
||||
[mapping:newlib]
|
||||
archive: libnewlib.a
|
||||
entries:
|
||||
heap (noflash)
|
||||
abort (noflash)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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 \
|
||||
|
|
|
@ -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:
|
||||
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 — default option.
|
||||
- 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::
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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,输出会类似于::
|
||||
|
||||
|
|
|
@ -92,7 +92,7 @@ IDF 监视器在后台运行以下命令,解码各地址::
|
|||
|
||||
或者选择配置 panic 处理器以运行 GDBStub,GDBStub 工具可以与 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 串口线,请按复位键,手动复位开发板。
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue