mirror of
https://github.com/espressif/esp-idf.git
synced 2025-08-08 04:02:27 +00:00
esp32, esp32s2: move panic handling code to new component
This commit is contained in:

committed by
Renz Bagaporo

parent
a3816bcb75
commit
2b100789b7
2
Kconfig
2
Kconfig
@@ -133,7 +133,7 @@ mainmenu "Espressif IoT Development Framework Configuration"
|
|||||||
CONFIG_APP_BUILD_TYPE_ELF_RAM=y
|
CONFIG_APP_BUILD_TYPE_ELF_RAM=y
|
||||||
CONFIG_VFS_SUPPORT_TERMIOS=
|
CONFIG_VFS_SUPPORT_TERMIOS=
|
||||||
CONFIG_NEWLIB_NANO_FORMAT=y
|
CONFIG_NEWLIB_NANO_FORMAT=y
|
||||||
CONFIG_ESP32_PANIC_PRINT_HALT=y
|
CONFIG_ESP_SYSTEM_PANIC_PRINT_HALT=y
|
||||||
CONFIG_ESP32_DEBUG_STUBS_ENABLE=
|
CONFIG_ESP32_DEBUG_STUBS_ENABLE=
|
||||||
CONFIG_ESP_ERR_TO_NAME_LOOKUP=
|
CONFIG_ESP_ERR_TO_NAME_LOOKUP=
|
||||||
|
|
||||||
|
@@ -414,7 +414,7 @@ static void bootloader_check_wdt_reset(void)
|
|||||||
|
|
||||||
void abort(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);
|
ets_printf("abort() was called at PC 0x%08x\r\n", (intptr_t)__builtin_return_address(0) - 3);
|
||||||
#endif
|
#endif
|
||||||
if (esp_cpu_in_ocd_debug_mode()) {
|
if (esp_cpu_in_ocd_debug_mode()) {
|
||||||
|
@@ -330,7 +330,7 @@ static void bootloader_check_wdt_reset(void)
|
|||||||
|
|
||||||
void abort(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);
|
ets_printf("abort() was called at PC 0x%08x\r\n", (intptr_t)__builtin_return_address(0) - 3);
|
||||||
#endif
|
#endif
|
||||||
if (esp_cpu_in_ocd_debug_mode()) {
|
if (esp_cpu_in_ocd_debug_mode()) {
|
||||||
|
@@ -18,12 +18,10 @@ else()
|
|||||||
"cpu_start.c"
|
"cpu_start.c"
|
||||||
"crosscore_int.c"
|
"crosscore_int.c"
|
||||||
"dport_access.c"
|
"dport_access.c"
|
||||||
"dport_panic_highint_hdl.S"
|
|
||||||
"esp_himem.c"
|
"esp_himem.c"
|
||||||
"hw_random.c"
|
"hw_random.c"
|
||||||
"int_wdt.c"
|
"int_wdt.c"
|
||||||
"intr_alloc.c"
|
"intr_alloc.c"
|
||||||
"panic.c"
|
|
||||||
"pm_esp32.c"
|
"pm_esp32.c"
|
||||||
"pm_trace.c"
|
"pm_trace.c"
|
||||||
"reset_reason.c"
|
"reset_reason.c"
|
||||||
|
@@ -356,38 +356,6 @@ menu "ESP32-specific"
|
|||||||
|
|
||||||
Data is reserved at the beginning of RTC slow memory.
|
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
|
config ESP32_DEBUG_OCDAWARE
|
||||||
bool "Make exception and panic handlers JTAG/OCD aware"
|
bool "Make exception and panic handlers JTAG/OCD aware"
|
||||||
default y
|
default y
|
||||||
|
@@ -1,8 +1,3 @@
|
|||||||
[mapping:esp32]
|
|
||||||
archive: libesp32.a
|
|
||||||
entries:
|
|
||||||
panic (noflash)
|
|
||||||
|
|
||||||
[mapping:gcc]
|
[mapping:gcc]
|
||||||
archive: libgcc.a
|
archive: libgcc.a
|
||||||
entries:
|
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 "hal/timer_ll.h"
|
||||||
#include "freertos/xtensa_api.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
|
/* "inner" restart function for after RTOS, interrupts & anything else on this
|
||||||
* core are already stopped. Stalls other core, resets hardware,
|
* core are already stopped. Stalls other core, resets hardware,
|
||||||
* triggers restart.
|
* triggers restart.
|
||||||
|
@@ -16,11 +16,9 @@ else()
|
|||||||
"cpu_start.c"
|
"cpu_start.c"
|
||||||
"crosscore_int.c"
|
"crosscore_int.c"
|
||||||
"dport_access.c"
|
"dport_access.c"
|
||||||
"dport_panic_highint_hdl.S"
|
|
||||||
"hw_random.c"
|
"hw_random.c"
|
||||||
"int_wdt.c"
|
"int_wdt.c"
|
||||||
"intr_alloc.c"
|
"intr_alloc.c"
|
||||||
"panic.c"
|
|
||||||
"pm_esp32s2.c"
|
"pm_esp32s2.c"
|
||||||
"pm_trace.c"
|
"pm_trace.c"
|
||||||
"reset_reason.c"
|
"reset_reason.c"
|
||||||
|
@@ -296,38 +296,6 @@ menu "ESP32S2-specific"
|
|||||||
|
|
||||||
Data is reserved at the beginning of RTC slow memory.
|
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
|
config ESP32S2_DEBUG_OCDAWARE
|
||||||
bool "Make exception and panic handlers JTAG/OCD aware"
|
bool "Make exception and panic handlers JTAG/OCD aware"
|
||||||
default y
|
default y
|
||||||
|
@@ -1,8 +1,3 @@
|
|||||||
[mapping:esp32s2]
|
|
||||||
archive: libesp32s2.a
|
|
||||||
entries:
|
|
||||||
panic (noflash)
|
|
||||||
|
|
||||||
[mapping:gcc]
|
[mapping:gcc]
|
||||||
archive: libgcc.a
|
archive: libgcc.a
|
||||||
entries:
|
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();
|
|
||||||
}
|
|
@@ -12,8 +12,7 @@ else()
|
|||||||
"src/freertos_hooks.c"
|
"src/freertos_hooks.c"
|
||||||
"src/mac_addr.c"
|
"src/mac_addr.c"
|
||||||
"src/pm_locks.c"
|
"src/pm_locks.c"
|
||||||
"src/stack_check.c"
|
"src/stack_check.c")
|
||||||
"src/system_api.c")
|
|
||||||
|
|
||||||
# IPC framework is not applicable if freertos unicore config is selected
|
# IPC framework is not applicable if freertos unicore config is selected
|
||||||
if(NOT CONFIG_FREERTOS_UNICORE)
|
if(NOT CONFIG_FREERTOS_UNICORE)
|
||||||
@@ -22,8 +21,9 @@ else()
|
|||||||
|
|
||||||
idf_component_register(SRCS "${srcs}"
|
idf_component_register(SRCS "${srcs}"
|
||||||
INCLUDE_DIRS include
|
INCLUDE_DIRS include
|
||||||
REQUIRES ${target} esp_timer
|
REQUIRES ${target} espcoredump esp_timer
|
||||||
PRIV_REQUIRES soc)
|
PRIV_REQUIRES soc
|
||||||
|
LDFRAGMENTS "linker.lf")
|
||||||
|
|
||||||
set_source_files_properties(
|
set_source_files_properties(
|
||||||
"src/stack_check.c"
|
"src/stack_check.c"
|
||||||
|
7
components/esp_system/CMakeLists.txt
Normal file
7
components/esp_system/CMakeLists.txt
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
idf_component_register(SRCS "abort.c" "panic.c" "reset_reason.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
|
81
components/esp_system/abort.c
Normal file
81
components/esp_system/abort.c
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
// 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"
|
||||||
|
#include "esp_system.h"
|
||||||
|
|
||||||
|
#include "esp_private/system_internal.h"
|
||||||
|
|
||||||
|
#include "soc/cpu.h"
|
||||||
|
#include "soc/soc_caps.h"
|
||||||
|
#include "hal/cpu_hal.h"
|
||||||
|
|
||||||
|
#if CONFIG_IDF_TARGET_ESP32
|
||||||
|
#include "esp32/rom/ets_sys.h"
|
||||||
|
#else
|
||||||
|
#include "esp32s2/rom/ets_sys.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "panic.h"
|
||||||
|
|
||||||
|
void __attribute__((noreturn)) abort(void)
|
||||||
|
{
|
||||||
|
char buf[47] = { 0 };
|
||||||
|
|
||||||
|
_Static_assert(UINTPTR_MAX == 0xffffffff, "abort() assumes 32-bit addresses");
|
||||||
|
_Static_assert(SOC_CPU_CORES_NUM < 10, "abort() assumes number of cores is [1..9]");
|
||||||
|
|
||||||
|
char addr_buf[9] = { 0 };
|
||||||
|
char core_buf[2] = { 0 };
|
||||||
|
|
||||||
|
itoa((uint32_t)(__builtin_return_address(0) - 3), addr_buf, 16);
|
||||||
|
itoa(cpu_ll_get_core_id(), core_buf, 10);
|
||||||
|
|
||||||
|
const char *str[4] = { "abort() was called at PC 0x", addr_buf, " on core ", core_buf };
|
||||||
|
for (int i = 0, k = 0; i < 4; i++) {
|
||||||
|
for (int j = 0; str[i][j] != '\0'; j++) {
|
||||||
|
buf[k] = str[i][j];
|
||||||
|
k++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_system_abort(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
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
|
||||||
|
COMPONENT_ADD_LDFRAGMENTS += linker.lf
|
||||||
|
|
||||||
|
-include $(COMPONENT_PATH)/port/$(SOC_NAME)/component.mk
|
@@ -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);
|
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
|
* @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)
|
336
components/esp_system/panic.c
Normal file
336
components/esp_system/panic.c
Normal file
@@ -0,0 +1,336 @@
|
|||||||
|
// 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 "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
|
||||||
|
|
||||||
|
static bool s_abort = false;
|
||||||
|
static char *s_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_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 (s_abort) {
|
||||||
|
info->description = NULL;
|
||||||
|
info->details = s_abort_details ? print_abort_details : NULL;
|
||||||
|
info->state = NULL; // do not display state, since it is not a 'real' crash
|
||||||
|
info->reason = "SoftwareAbort";
|
||||||
|
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.
|
||||||
|
*
|
||||||
|
* */
|
||||||
|
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");
|
||||||
|
esp_restart_noos();
|
||||||
|
#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)
|
||||||
|
{
|
||||||
|
s_abort = true;
|
||||||
|
s_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
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
if (esp_cpu_in_ocd_debug_mode()) {
|
||||||
|
cpu_hal_break();
|
||||||
|
}
|
||||||
|
*((int *) 0) = 0; // should be an invalid operation on targets
|
||||||
|
}
|
||||||
|
}
|
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})
|
1
components/esp_system/port/esp32/CMakeLists.txt
Normal file
1
components/esp_system/port/esp32/CMakeLists.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
target_sources(${COMPONENT_LIB} PRIVATE "${CMAKE_CURRENT_LIST_DIR}/dport_panic_highint_hdl.S")
|
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
|
1
components/esp_system/port/esp32s2/CMakeLists.txt
Normal file
1
components/esp_system/port/esp32s2/CMakeLists.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
target_sources(${COMPONENT_LIB} PRIVATE "${CMAKE_CURRENT_LIST_DIR}/dport_panic_highint_hdl.S")
|
15
components/esp_system/port/include/port/panic_funcs.h
Normal file
15
components/esp_system/port/include/port/panic_funcs.h
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
void __attribute__((noreturn)) panic_restart(void);
|
529
components/esp_system/port/panic_handler.c
Normal file
529
components/esp_system/port/panic_handler.c
Normal file
@@ -0,0 +1,529 @@
|
|||||||
|
// 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(const void* f)
|
||||||
|
{
|
||||||
|
#if !CONFIG_FREERTOS_UNICORE
|
||||||
|
int err_core = f == xt_exc_frames[0] ? 0 : 1;
|
||||||
|
#else
|
||||||
|
int err_core = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
print_registers(f, err_core);
|
||||||
|
panic_print_str("\r\n");
|
||||||
|
print_backtrace(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) {
|
||||||
|
panic_print_str("\r\n");
|
||||||
|
print_registers(xt_exc_frames[i], i);
|
||||||
|
panic_print_str("\r\n");
|
||||||
|
print_backtrace(xt_exc_frames[i], i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#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
|
||||||
|
if (esp_cache_err_get_cpuid() != -1) {
|
||||||
|
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
|
66
components/esp_system/private_include/panic_internal.h
Normal file
66
components/esp_system/private_include/panic_internal.h
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
// 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 "sdkconfig.h"
|
||||||
|
|
||||||
|
// 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);
|
30
components/esp_system/reset_reason.c
Normal file
30
components/esp_system/reset_reason.c
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
// 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 "esp_private/system_internal.h"
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
}
|
||||||
|
|
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/FreeRTOS.h"
|
||||||
#include "freertos/task.h"
|
#include "freertos/task.h"
|
||||||
|
|
||||||
|
#include "esp_system.h"
|
||||||
|
#include "panic_internal.h"
|
||||||
|
|
||||||
#define SHUTDOWN_HANDLERS_NO 2
|
#define SHUTDOWN_HANDLERS_NO 2
|
||||||
static shutdown_handler_t shutdown_handlers[SHUTDOWN_HANDLERS_NO];
|
static shutdown_handler_t shutdown_handlers[SHUTDOWN_HANDLERS_NO];
|
||||||
@@ -61,3 +63,7 @@ const char* esp_get_idf_version(void)
|
|||||||
return IDF_VER;
|
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
|
.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"
|
.section .rodata, "a"
|
||||||
.align 4
|
.align 4
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
Hooks to dynamically install handlers for exceptions and interrupts.
|
Hooks to dynamically install handlers for exceptions and interrupts.
|
||||||
|
@@ -84,3 +84,9 @@ bool IRAM_ATTR esp_cpu_in_ocd_debug_mode(void)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void IRAM_ATTR esp_set_breakpoint_if_jtag(void *fn)
|
||||||
|
{
|
||||||
|
if (esp_cpu_in_ocd_debug_mode()) {
|
||||||
|
cpu_hal_set_breakpoint(0, fn);
|
||||||
|
}
|
||||||
|
}
|
@@ -1,6 +1,6 @@
|
|||||||
CONFIG_APP_BUILD_TYPE_ELF_RAM=y
|
CONFIG_APP_BUILD_TYPE_ELF_RAM=y
|
||||||
CONFIG_VFS_SUPPORT_TERMIOS=n
|
CONFIG_VFS_SUPPORT_TERMIOS=n
|
||||||
CONFIG_NEWLIB_NANO_FORMAT=y
|
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_ESP32_DEBUG_STUBS_ENABLE=n
|
||||||
CONFIG_ESP_ERR_TO_NAME_LOOKUP=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
|
# Set components required by all other components in the build
|
||||||
#
|
#
|
||||||
# - lwip is here so that #include <sys/socket.h> works without any special provisions
|
# - 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}")
|
idf_build_set_property(__COMPONENT_REQUIRES_COMMON "${requires_common}")
|
||||||
|
|
||||||
__build_get_idf_git_revision()
|
__build_get_idf_git_revision()
|
||||||
|
@@ -165,10 +165,10 @@ CONFIG_ESP_CONSOLE_UART_NUM=0
|
|||||||
CONFIG_ESP_CONSOLE_UART_BAUDRATE=115200
|
CONFIG_ESP_CONSOLE_UART_BAUDRATE=115200
|
||||||
CONFIG_ESP32_ULP_COPROC_ENABLED=
|
CONFIG_ESP32_ULP_COPROC_ENABLED=
|
||||||
CONFIG_ESP32_ULP_COPROC_RESERVE_MEM=0
|
CONFIG_ESP32_ULP_COPROC_RESERVE_MEM=0
|
||||||
CONFIG_ESP32_PANIC_PRINT_HALT=
|
CONFIG_ESP_SYSTEM_PANIC_PRINT_HALT=
|
||||||
CONFIG_ESP32_PANIC_PRINT_REBOOT=y
|
CONFIG_ESP_SYSTEM_PANIC_PRINT_REBOOT=y
|
||||||
CONFIG_ESP32_PANIC_SILENT_REBOOT=
|
CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT=
|
||||||
CONFIG_ESP32_PANIC_GDBSTUB=
|
CONFIG_ESP_SYSTEM_PANIC_GDBSTUB=
|
||||||
CONFIG_ESP32_DEBUG_OCDAWARE=y
|
CONFIG_ESP32_DEBUG_OCDAWARE=y
|
||||||
CONFIG_ESP_INT_WDT=y
|
CONFIG_ESP_INT_WDT=y
|
||||||
CONFIG_ESP_INT_WDT_TIMEOUT_MS=300
|
CONFIG_ESP_INT_WDT_TIMEOUT_MS=300
|
||||||
|
Reference in New Issue
Block a user