From 2b100789b76aaff49457f085ec290164ce7224e5 Mon Sep 17 00:00:00 2001 From: Renz Christian Bagaporo Date: Sun, 2 Feb 2020 23:23:16 +0800 Subject: [PATCH 1/9] esp32, esp32s2: move panic handling code to new component --- Kconfig | 2 +- .../src/esp32/bootloader_esp32.c | 2 +- .../src/esp32s2/bootloader_esp32s2.c | 2 +- components/esp32/CMakeLists.txt | 2 - components/esp32/Kconfig | 32 - components/esp32/linker.lf | 5 - components/esp32/panic.c | 688 ------------------ components/esp32/system_api_esp32.c | 15 +- components/esp32s2/CMakeLists.txt | 2 - components/esp32s2/Kconfig | 32 - components/esp32s2/linker.lf | 5 - components/esp32s2/panic.c | 678 ----------------- components/esp_common/CMakeLists.txt | 8 +- components/esp_common/Kconfig | 2 +- components/esp_common/sdkconfig.rename | 2 +- components/esp_system/CMakeLists.txt | 7 + components/esp_system/Kconfig | 35 + components/esp_system/abort.c | 81 +++ components/esp_system/component.mk | 8 + .../include/esp_system.h | 9 +- components/esp_system/linker.lf | 7 + components/esp_system/panic.c | 336 +++++++++ components/esp_system/port/CMakeLists.txt | 9 + .../esp_system/port/esp32/CMakeLists.txt | 1 + components/esp_system/port/esp32/component.mk | 1 + .../port}/esp32/dport_panic_highint_hdl.S | 0 .../esp_system/port/esp32s2/CMakeLists.txt | 1 + .../port}/esp32s2/dport_panic_highint_hdl.S | 0 .../port/include/port/panic_funcs.h | 15 + components/esp_system/port/panic_handler.c | 529 ++++++++++++++ .../esp_system/port/panic_handler_asm.S | 64 ++ .../private_include/panic_internal.h | 66 ++ components/esp_system/reset_reason.c | 30 + components/esp_system/sdkconfig.rename | 13 + .../src => esp_system}/system_api.c | 6 + components/freertos/xtensa/port.c | 15 + components/freertos/xtensa/xtensa_vectors.S | 87 --- components/soc/src/cpu_util.c | 6 + examples/get-started/hello_world/sdkconfig.ci | 2 +- tools/cmake/build.cmake | 2 +- tools/ldgen/samples/sdkconfig | 8 +- 41 files changed, 1264 insertions(+), 1551 deletions(-) delete mode 100644 components/esp32/panic.c delete mode 100644 components/esp32s2/panic.c create mode 100644 components/esp_system/CMakeLists.txt create mode 100644 components/esp_system/Kconfig create mode 100644 components/esp_system/abort.c create mode 100644 components/esp_system/component.mk rename components/{esp_common => esp_system}/include/esp_system.h (97%) create mode 100644 components/esp_system/linker.lf create mode 100644 components/esp_system/panic.c create mode 100644 components/esp_system/port/CMakeLists.txt create mode 100644 components/esp_system/port/esp32/CMakeLists.txt create mode 100644 components/esp_system/port/esp32/component.mk rename components/{ => esp_system/port}/esp32/dport_panic_highint_hdl.S (100%) create mode 100644 components/esp_system/port/esp32s2/CMakeLists.txt rename components/{ => esp_system/port}/esp32s2/dport_panic_highint_hdl.S (100%) create mode 100644 components/esp_system/port/include/port/panic_funcs.h create mode 100644 components/esp_system/port/panic_handler.c create mode 100644 components/esp_system/port/panic_handler_asm.S create mode 100644 components/esp_system/private_include/panic_internal.h create mode 100644 components/esp_system/reset_reason.c create mode 100644 components/esp_system/sdkconfig.rename rename components/{esp_common/src => esp_system}/system_api.c (90%) diff --git a/Kconfig b/Kconfig index e2f659e48..b428f059f 100644 --- a/Kconfig +++ b/Kconfig @@ -133,7 +133,7 @@ mainmenu "Espressif IoT Development Framework Configuration" CONFIG_APP_BUILD_TYPE_ELF_RAM=y CONFIG_VFS_SUPPORT_TERMIOS= CONFIG_NEWLIB_NANO_FORMAT=y - CONFIG_ESP32_PANIC_PRINT_HALT=y + CONFIG_ESP_SYSTEM_PANIC_PRINT_HALT=y CONFIG_ESP32_DEBUG_STUBS_ENABLE= CONFIG_ESP_ERR_TO_NAME_LOOKUP= diff --git a/components/bootloader_support/src/esp32/bootloader_esp32.c b/components/bootloader_support/src/esp32/bootloader_esp32.c index 6a06d8cd1..b5d214976 100644 --- a/components/bootloader_support/src/esp32/bootloader_esp32.c +++ b/components/bootloader_support/src/esp32/bootloader_esp32.c @@ -414,7 +414,7 @@ static void bootloader_check_wdt_reset(void) void abort(void) { -#if !CONFIG_ESP32_PANIC_SILENT_REBOOT +#if !CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT ets_printf("abort() was called at PC 0x%08x\r\n", (intptr_t)__builtin_return_address(0) - 3); #endif if (esp_cpu_in_ocd_debug_mode()) { diff --git a/components/bootloader_support/src/esp32s2/bootloader_esp32s2.c b/components/bootloader_support/src/esp32s2/bootloader_esp32s2.c index 2a2652437..972c9bfd2 100644 --- a/components/bootloader_support/src/esp32s2/bootloader_esp32s2.c +++ b/components/bootloader_support/src/esp32s2/bootloader_esp32s2.c @@ -330,7 +330,7 @@ static void bootloader_check_wdt_reset(void) void abort(void) { -#if !CONFIG_ESP32S2_PANIC_SILENT_REBOOT +#if !CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT ets_printf("abort() was called at PC 0x%08x\r\n", (intptr_t)__builtin_return_address(0) - 3); #endif if (esp_cpu_in_ocd_debug_mode()) { diff --git a/components/esp32/CMakeLists.txt b/components/esp32/CMakeLists.txt index ed15a774f..269ca7020 100644 --- a/components/esp32/CMakeLists.txt +++ b/components/esp32/CMakeLists.txt @@ -18,12 +18,10 @@ else() "cpu_start.c" "crosscore_int.c" "dport_access.c" - "dport_panic_highint_hdl.S" "esp_himem.c" "hw_random.c" "int_wdt.c" "intr_alloc.c" - "panic.c" "pm_esp32.c" "pm_trace.c" "reset_reason.c" diff --git a/components/esp32/Kconfig b/components/esp32/Kconfig index 3cac1cc54..eb898326d 100644 --- a/components/esp32/Kconfig +++ b/components/esp32/Kconfig @@ -356,38 +356,6 @@ menu "ESP32-specific" Data is reserved at the beginning of RTC slow memory. - choice ESP32_PANIC - prompt "Panic handler behaviour" - default ESP32_PANIC_PRINT_REBOOT - help - If FreeRTOS detects unexpected behaviour or an unhandled exception, the panic handler is - invoked. Configure the panic handlers action here. - - config ESP32_PANIC_PRINT_HALT - bool "Print registers and halt" - help - Outputs the relevant registers over the serial port and halt the - processor. Needs a manual reset to restart. - - config ESP32_PANIC_PRINT_REBOOT - bool "Print registers and reboot" - help - Outputs the relevant registers over the serial port and immediately - reset the processor. - - config ESP32_PANIC_SILENT_REBOOT - bool "Silent reboot" - help - Just resets the processor without outputting anything - - config ESP32_PANIC_GDBSTUB - bool "Invoke GDBStub" - select ESP_GDBSTUB_ENABLED - help - Invoke gdbstub on the serial port, allowing for gdb to attach to it to do a postmortem - of the crash. - endchoice - config ESP32_DEBUG_OCDAWARE bool "Make exception and panic handlers JTAG/OCD aware" default y diff --git a/components/esp32/linker.lf b/components/esp32/linker.lf index db73d2dc9..cd39081ac 100644 --- a/components/esp32/linker.lf +++ b/components/esp32/linker.lf @@ -1,8 +1,3 @@ -[mapping:esp32] -archive: libesp32.a -entries: - panic (noflash) - [mapping:gcc] archive: libgcc.a entries: diff --git a/components/esp32/panic.c b/components/esp32/panic.c deleted file mode 100644 index cbf7090a3..000000000 --- a/components/esp32/panic.c +++ /dev/null @@ -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 - -#include - -#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(); -} diff --git a/components/esp32/system_api_esp32.c b/components/esp32/system_api_esp32.c index 019bb5e0d..504da1dda 100644 --- a/components/esp32/system_api_esp32.c +++ b/components/esp32/system_api_esp32.c @@ -34,6 +34,13 @@ #include "hal/timer_ll.h" #include "freertos/xtensa_api.h" +#if CONFIG_IDF_TARGET_ESP32 +#include "esp32/cache_err_int.h" +#elif CONFIG_IDF_TARGET_ESP32S2 +#include "esp32s2/cache_err_int.h" +#endif + + /* "inner" restart function for after RTOS, interrupts & anything else on this * core are already stopped. Stalls other core, resets hardware, * triggers restart. @@ -94,10 +101,10 @@ void IRAM_ATTR esp_restart_noos(void) // Reset wifi/bluetooth/ethernet/sdio (bb/mac) DPORT_SET_PERI_REG_MASK(DPORT_CORE_RST_EN_REG, - DPORT_BB_RST | DPORT_FE_RST | DPORT_MAC_RST | - DPORT_BT_RST | DPORT_BTMAC_RST | DPORT_SDIO_RST | - DPORT_SDIO_HOST_RST | DPORT_EMAC_RST | DPORT_MACPWR_RST | - DPORT_RW_BTMAC_RST | DPORT_RW_BTLP_RST); + DPORT_BB_RST | DPORT_FE_RST | DPORT_MAC_RST | + DPORT_BT_RST | DPORT_BTMAC_RST | DPORT_SDIO_RST | + DPORT_SDIO_HOST_RST | DPORT_EMAC_RST | DPORT_MACPWR_RST | + DPORT_RW_BTMAC_RST | DPORT_RW_BTLP_RST); DPORT_REG_WRITE(DPORT_CORE_RST_EN_REG, 0); // Reset timer/spi/uart diff --git a/components/esp32s2/CMakeLists.txt b/components/esp32s2/CMakeLists.txt index 940e8d1c9..ff6d7038c 100644 --- a/components/esp32s2/CMakeLists.txt +++ b/components/esp32s2/CMakeLists.txt @@ -16,11 +16,9 @@ else() "cpu_start.c" "crosscore_int.c" "dport_access.c" - "dport_panic_highint_hdl.S" "hw_random.c" "int_wdt.c" "intr_alloc.c" - "panic.c" "pm_esp32s2.c" "pm_trace.c" "reset_reason.c" diff --git a/components/esp32s2/Kconfig b/components/esp32s2/Kconfig index abdaef4a8..d09bd906e 100644 --- a/components/esp32s2/Kconfig +++ b/components/esp32s2/Kconfig @@ -296,38 +296,6 @@ menu "ESP32S2-specific" Data is reserved at the beginning of RTC slow memory. - choice ESP32S2_PANIC - prompt "Panic handler behaviour" - default ESP32S2_PANIC_PRINT_REBOOT - help - If FreeRTOS detects unexpected behaviour or an unhandled exception, the panic handler is - invoked. Configure the panic handlers action here. - - config ESP32S2_PANIC_PRINT_HALT - bool "Print registers and halt" - help - Outputs the relevant registers over the serial port and halt the - processor. Needs a manual reset to restart. - - config ESP32S2_PANIC_PRINT_REBOOT - bool "Print registers and reboot" - help - Outputs the relevant registers over the serial port and immediately - reset the processor. - - config ESP32S2_PANIC_SILENT_REBOOT - bool "Silent reboot" - help - Just resets the processor without outputting anything - - config ESP32S2_PANIC_GDBSTUB - bool "Invoke GDBStub" - select ESP_GDBSTUB_ENABLED - help - Invoke gdbstub on the serial port, allowing for gdb to attach to it to do a postmortem - of the crash. - endchoice - config ESP32S2_DEBUG_OCDAWARE bool "Make exception and panic handlers JTAG/OCD aware" default y diff --git a/components/esp32s2/linker.lf b/components/esp32s2/linker.lf index 80ed1158e..87e068ccd 100644 --- a/components/esp32s2/linker.lf +++ b/components/esp32s2/linker.lf @@ -1,8 +1,3 @@ -[mapping:esp32s2] -archive: libesp32s2.a -entries: - panic (noflash) - [mapping:gcc] archive: libgcc.a entries: diff --git a/components/esp32s2/panic.c b/components/esp32s2/panic.c deleted file mode 100644 index b4436397d..000000000 --- a/components/esp32s2/panic.c +++ /dev/null @@ -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 - -#include - -#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(); -} diff --git a/components/esp_common/CMakeLists.txt b/components/esp_common/CMakeLists.txt index 185d8f7e6..98b178af6 100644 --- a/components/esp_common/CMakeLists.txt +++ b/components/esp_common/CMakeLists.txt @@ -12,8 +12,7 @@ else() "src/freertos_hooks.c" "src/mac_addr.c" "src/pm_locks.c" - "src/stack_check.c" - "src/system_api.c") + "src/stack_check.c") # IPC framework is not applicable if freertos unicore config is selected if(NOT CONFIG_FREERTOS_UNICORE) @@ -22,8 +21,9 @@ else() idf_component_register(SRCS "${srcs}" INCLUDE_DIRS include - REQUIRES ${target} esp_timer - PRIV_REQUIRES soc) + REQUIRES ${target} espcoredump esp_timer + PRIV_REQUIRES soc + LDFRAGMENTS "linker.lf") set_source_files_properties( "src/stack_check.c" diff --git a/components/esp_common/Kconfig b/components/esp_common/Kconfig index d394393b8..c72079024 100644 --- a/components/esp_common/Kconfig +++ b/components/esp_common/Kconfig @@ -227,4 +227,4 @@ menu "Common ESP-related" config ESP_MAC_ADDR_UNIVERSE_ETH bool -endmenu # Common ESP-related +endmenu # Common ESP-related \ No newline at end of file diff --git a/components/esp_common/sdkconfig.rename b/components/esp_common/sdkconfig.rename index 2241ecf33..c7f23dde5 100644 --- a/components/esp_common/sdkconfig.rename +++ b/components/esp_common/sdkconfig.rename @@ -25,4 +25,4 @@ CONFIG_TASK_WDT_PANIC CONFIG_ESP_TASK_WDT_PANI CONFIG_TASK_WDT_TIMEOUT_S CONFIG_ESP_TASK_WDT_TIMEOUT_S CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU0 CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0 CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU1 CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU1 -CONFIG_ESP32_DEBUG_STUBS_ENABLE CONFIG_ESP_DEBUG_STUBS_ENABLE +CONFIG_ESP32_DEBUG_STUBS_ENABLE CONFIG_ESP_DEBUG_STUBS_ENABLE \ No newline at end of file diff --git a/components/esp_system/CMakeLists.txt b/components/esp_system/CMakeLists.txt new file mode 100644 index 000000000..a1439ea79 --- /dev/null +++ b/components/esp_system/CMakeLists.txt @@ -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) diff --git a/components/esp_system/Kconfig b/components/esp_system/Kconfig new file mode 100644 index 000000000..73b54ad48 --- /dev/null +++ b/components/esp_system/Kconfig @@ -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 diff --git a/components/esp_system/abort.c b/components/esp_system/abort.c new file mode 100644 index 000000000..5933b4a35 --- /dev/null +++ b/components/esp_system/abort.c @@ -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 +#include + +#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(); +} \ No newline at end of file diff --git a/components/esp_system/component.mk b/components/esp_system/component.mk new file mode 100644 index 000000000..e7f5eb293 --- /dev/null +++ b/components/esp_system/component.mk @@ -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 \ No newline at end of file diff --git a/components/esp_common/include/esp_system.h b/components/esp_system/include/esp_system.h similarity index 97% rename from components/esp_common/include/esp_system.h rename to components/esp_system/include/esp_system.h index 5947773d4..9b0abf431 100644 --- a/components/esp_common/include/esp_system.h +++ b/components/esp_system/include/esp_system.h @@ -223,7 +223,7 @@ esp_err_t esp_efuse_mac_get_default(uint8_t *mac); esp_err_t esp_read_mac(uint8_t* mac, esp_mac_type_t type); /** - * @brief Derive local MAC address from universal MAC address. + * @brief Derive local MAC address from universal MAC address. * * This function derives a local MAC address from an universal MAC address. * A `definition of local vs universal MAC address can be found on Wikipedia @@ -238,6 +238,13 @@ esp_err_t esp_read_mac(uint8_t* mac, esp_mac_type_t type); */ esp_err_t esp_derive_local_mac(uint8_t* local_mac, const uint8_t* universal_mac); +/** + * @brief Trigger a software abort + * + * @param details Details that will be displayed during panic handling. + */ +void __attribute__((noreturn)) esp_system_abort(const char* details); + /** * @brief Chip models */ diff --git a/components/esp_system/linker.lf b/components/esp_system/linker.lf new file mode 100644 index 000000000..8ae6c7c70 --- /dev/null +++ b/components/esp_system/linker.lf @@ -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) \ No newline at end of file diff --git a/components/esp_system/panic.c b/components/esp_system/panic.c new file mode 100644 index 000000000..87d579786 --- /dev/null +++ b/components/esp_system/panic.c @@ -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 + +#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 +#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 - 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 + } +} \ No newline at end of file diff --git a/components/esp_system/port/CMakeLists.txt b/components/esp_system/port/CMakeLists.txt new file mode 100644 index 000000000..75def881d --- /dev/null +++ b/components/esp_system/port/CMakeLists.txt @@ -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}) diff --git a/components/esp_system/port/esp32/CMakeLists.txt b/components/esp_system/port/esp32/CMakeLists.txt new file mode 100644 index 000000000..ed1a5652d --- /dev/null +++ b/components/esp_system/port/esp32/CMakeLists.txt @@ -0,0 +1 @@ +target_sources(${COMPONENT_LIB} PRIVATE "${CMAKE_CURRENT_LIST_DIR}/dport_panic_highint_hdl.S") \ No newline at end of file diff --git a/components/esp_system/port/esp32/component.mk b/components/esp_system/port/esp32/component.mk new file mode 100644 index 000000000..442effc30 --- /dev/null +++ b/components/esp_system/port/esp32/component.mk @@ -0,0 +1 @@ +COMPONENT_SRCDIRS += port port/esp32 diff --git a/components/esp32/dport_panic_highint_hdl.S b/components/esp_system/port/esp32/dport_panic_highint_hdl.S similarity index 100% rename from components/esp32/dport_panic_highint_hdl.S rename to components/esp_system/port/esp32/dport_panic_highint_hdl.S diff --git a/components/esp_system/port/esp32s2/CMakeLists.txt b/components/esp_system/port/esp32s2/CMakeLists.txt new file mode 100644 index 000000000..ed1a5652d --- /dev/null +++ b/components/esp_system/port/esp32s2/CMakeLists.txt @@ -0,0 +1 @@ +target_sources(${COMPONENT_LIB} PRIVATE "${CMAKE_CURRENT_LIST_DIR}/dport_panic_highint_hdl.S") \ No newline at end of file diff --git a/components/esp32s2/dport_panic_highint_hdl.S b/components/esp_system/port/esp32s2/dport_panic_highint_hdl.S similarity index 100% rename from components/esp32s2/dport_panic_highint_hdl.S rename to components/esp_system/port/esp32s2/dport_panic_highint_hdl.S diff --git a/components/esp_system/port/include/port/panic_funcs.h b/components/esp_system/port/include/port/panic_funcs.h new file mode 100644 index 000000000..28ab5ba24 --- /dev/null +++ b/components/esp_system/port/include/port/panic_funcs.h @@ -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); \ No newline at end of file diff --git a/components/esp_system/port/panic_handler.c b/components/esp_system/port/panic_handler.c new file mode 100644 index 000000000..b0423c368 --- /dev/null +++ b/components/esp_system/port/panic_handler.c @@ -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 + +#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(); + } +} \ No newline at end of file diff --git a/components/esp_system/port/panic_handler_asm.S b/components/esp_system/port/panic_handler_asm.S new file mode 100644 index 000000000..cb6efe8b5 --- /dev/null +++ b/components/esp_system/port/panic_handler_asm.S @@ -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 \ No newline at end of file diff --git a/components/esp_system/private_include/panic_internal.h b/components/esp_system/private_include/panic_internal.h new file mode 100644 index 000000000..5656ac487 --- /dev/null +++ b/components/esp_system/private_include/panic_internal.h @@ -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 + +#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); \ No newline at end of file diff --git a/components/esp_system/reset_reason.c b/components/esp_system/reset_reason.c new file mode 100644 index 000000000..a4bebbfb9 --- /dev/null +++ b/components/esp_system/reset_reason.c @@ -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; +} + diff --git a/components/esp_system/sdkconfig.rename b/components/esp_system/sdkconfig.rename new file mode 100644 index 000000000..87310ef6d --- /dev/null +++ b/components/esp_system/sdkconfig.rename @@ -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 + diff --git a/components/esp_common/src/system_api.c b/components/esp_system/system_api.c similarity index 90% rename from components/esp_common/src/system_api.c rename to components/esp_system/system_api.c index 2c30a45d6..7b4ed130f 100644 --- a/components/esp_common/src/system_api.c +++ b/components/esp_system/system_api.c @@ -4,6 +4,8 @@ #include "freertos/FreeRTOS.h" #include "freertos/task.h" +#include "esp_system.h" +#include "panic_internal.h" #define SHUTDOWN_HANDLERS_NO 2 static shutdown_handler_t shutdown_handlers[SHUTDOWN_HANDLERS_NO]; @@ -61,3 +63,7 @@ const char* esp_get_idf_version(void) return IDF_VER; } +void __attribute__((noreturn)) esp_system_abort(const char* details) +{ + panic_abort(details); +} \ No newline at end of file diff --git a/components/freertos/xtensa/port.c b/components/freertos/xtensa/port.c index 487d260e2..dbf0824be 100644 --- a/components/freertos/xtensa/port.c +++ b/components/freertos/xtensa/port.c @@ -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); +} \ No newline at end of file diff --git a/components/freertos/xtensa/xtensa_vectors.S b/components/freertos/xtensa/xtensa_vectors.S index 760fc5134..b7ddedbad 100644 --- a/components/freertos/xtensa/xtensa_vectors.S +++ b/components/freertos/xtensa/xtensa_vectors.S @@ -361,96 +361,9 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. .endm -/* --------------------------------------------------------------------------------- - Panic handler. - Should be reached by call0 (preferable) or jump only. If call0, a0 says where - from. If on simulator, display panic message and abort, else loop indefinitely. --------------------------------------------------------------------------------- -*/ - - .section .iram1,"ax" - .global panicHandler - - .global _xt_panic - .type _xt_panic,@function - .align 4 - .literal_position - .align 4 - -_xt_panic: - /* Allocate exception frame and save minimal context. */ - mov a0, sp - addi sp, sp, -XT_STK_FRMSZ - s32i a0, sp, XT_STK_A1 - #if XCHAL_HAVE_WINDOWED - s32e a0, sp, -12 /* for debug backtrace */ - #endif - rsr a0, PS /* save interruptee's PS */ - s32i a0, sp, XT_STK_PS - rsr a0, EPC_1 /* save interruptee's PC */ - s32i a0, sp, XT_STK_PC - #if XCHAL_HAVE_WINDOWED - s32e a0, sp, -16 /* for debug backtrace */ - #endif - s32i a12, sp, XT_STK_A12 /* _xt_context_save requires A12- */ - s32i a13, sp, XT_STK_A13 /* A13 to have already been saved */ - call0 _xt_context_save - - /* Save exc cause and vaddr into exception frame */ - rsr a0, EXCCAUSE - s32i a0, sp, XT_STK_EXCCAUSE - rsr a0, EXCVADDR - s32i a0, sp, XT_STK_EXCVADDR - - /* _xt_context_save seems to save the current a0, but we need the interuptees a0. Fix this. */ - rsr a0, EXCSAVE_1 /* save interruptee's a0 */ - - s32i a0, sp, XT_STK_A0 - - /* Set up PS for C, disable all interrupts except NMI and debug, and clear EXCM. */ - movi a0, PS_INTLEVEL(5) | PS_UM | PS_WOE - wsr a0, PS - - //Call panic handler - mov a6,sp - call4 panicHandler - - - .align 4 -//Call using call0. Prints the hex char in a2. Kills a3, a4, a5 -panic_print_hex: - movi a3,0x60000000 - movi a4,8 -panic_print_hex_loop: - l32i a5, a3, 0x1c - extui a5, a5, 16, 8 - bgei a5,64,panic_print_hex_loop - - srli a5,a2,28 - bgei a5,10,panic_print_hex_a - addi a5,a5,'0' - j panic_print_hex_ok -panic_print_hex_a: - addi a5,a5,'A'-10 -panic_print_hex_ok: - s32i a5,a3,0 - slli a2,a2,4 - - addi a4,a4,-1 - bnei a4,0,panic_print_hex_loop - movi a5,' ' - s32i a5,a3,0 - - ret - - - .section .rodata, "a" .align 4 - - /* -------------------------------------------------------------------------------- Hooks to dynamically install handlers for exceptions and interrupts. diff --git a/components/soc/src/cpu_util.c b/components/soc/src/cpu_util.c index eadeb2f52..1f0713d22 100644 --- a/components/soc/src/cpu_util.c +++ b/components/soc/src/cpu_util.c @@ -84,3 +84,9 @@ bool IRAM_ATTR esp_cpu_in_ocd_debug_mode(void) #endif } +void IRAM_ATTR esp_set_breakpoint_if_jtag(void *fn) +{ + if (esp_cpu_in_ocd_debug_mode()) { + cpu_hal_set_breakpoint(0, fn); + } +} \ No newline at end of file diff --git a/examples/get-started/hello_world/sdkconfig.ci b/examples/get-started/hello_world/sdkconfig.ci index f180aef23..fbc92b16d 100644 --- a/examples/get-started/hello_world/sdkconfig.ci +++ b/examples/get-started/hello_world/sdkconfig.ci @@ -1,6 +1,6 @@ CONFIG_APP_BUILD_TYPE_ELF_RAM=y CONFIG_VFS_SUPPORT_TERMIOS=n CONFIG_NEWLIB_NANO_FORMAT=y -CONFIG_ESP32_PANIC_PRINT_HALT=y +CONFIG_ESP_SYSTEM_PANIC_PRINT_HALT=y CONFIG_ESP32_DEBUG_STUBS_ENABLE=n CONFIG_ESP_ERR_TO_NAME_LOOKUP=n diff --git a/tools/cmake/build.cmake b/tools/cmake/build.cmake index e010a4401..e6984b76e 100644 --- a/tools/cmake/build.cmake +++ b/tools/cmake/build.cmake @@ -154,7 +154,7 @@ function(__build_init idf_path) # Set components required by all other components in the build # # - lwip is here so that #include works without any special provisions - set(requires_common cxx newlib freertos heap log lwip soc esp_rom esp_common xtensa) + set(requires_common cxx newlib freertos heap log lwip soc esp_rom esp_common esp_system xtensa) idf_build_set_property(__COMPONENT_REQUIRES_COMMON "${requires_common}") __build_get_idf_git_revision() diff --git a/tools/ldgen/samples/sdkconfig b/tools/ldgen/samples/sdkconfig index a11335693..34bef9f38 100644 --- a/tools/ldgen/samples/sdkconfig +++ b/tools/ldgen/samples/sdkconfig @@ -165,10 +165,10 @@ CONFIG_ESP_CONSOLE_UART_NUM=0 CONFIG_ESP_CONSOLE_UART_BAUDRATE=115200 CONFIG_ESP32_ULP_COPROC_ENABLED= CONFIG_ESP32_ULP_COPROC_RESERVE_MEM=0 -CONFIG_ESP32_PANIC_PRINT_HALT= -CONFIG_ESP32_PANIC_PRINT_REBOOT=y -CONFIG_ESP32_PANIC_SILENT_REBOOT= -CONFIG_ESP32_PANIC_GDBSTUB= +CONFIG_ESP_SYSTEM_PANIC_PRINT_HALT= +CONFIG_ESP_SYSTEM_PANIC_PRINT_REBOOT=y +CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT= +CONFIG_ESP_SYSTEM_PANIC_GDBSTUB= CONFIG_ESP32_DEBUG_OCDAWARE=y CONFIG_ESP_INT_WDT=y CONFIG_ESP_INT_WDT_TIMEOUT_MS=300 From 20cfc2ecdd4e45be1d89618b583158f88474b526 Mon Sep 17 00:00:00 2001 From: Renz Christian Bagaporo Date: Sun, 2 Feb 2020 23:25:39 +0800 Subject: [PATCH 2/9] docs: update docs with panic handler changes --- docs/Doxyfile | 4 ++-- docs/en/api-guides/fatal-errors.rst | 16 ++++++++-------- docs/en/api-guides/tools/idf-monitor.rst | 2 +- docs/zh_CN/api-guides/fatal-errors.rst | 14 +++++++------- docs/zh_CN/api-guides/tools/idf-monitor.rst | 2 +- 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/docs/Doxyfile b/docs/Doxyfile index 3f6c13085..5ffc249d2 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -241,7 +241,7 @@ INPUT = \ $(IDF_PATH)/components/log/include/esp_log.h \ ## Base MAC address ## NOTE: for line below header_file.inc is not used - $(IDF_PATH)/components/esp_common/include/esp_system.h \ + $(IDF_PATH)/components/esp_system/include/esp_system.h \ ## IDF version $(IDF_PATH)/components/esp_common/include/esp_idf_version.h \ ## @@ -282,7 +282,7 @@ INPUT = \ ### Helper functions for error codes $(IDF_PATH)/components/esp_common/include/esp_err.h \ ### System APIs - $(IDF_PATH)/components/esp_common/include/esp_system.h \ + $(IDF_PATH)/components/esp_system/include/esp_system.h \ ### Modbus controller component header file $(IDF_PATH)/components/freemodbus/common/include/esp_modbus_common.h \ $(IDF_PATH)/components/freemodbus/common/include/esp_modbus_slave.h \ diff --git a/docs/en/api-guides/fatal-errors.rst b/docs/en/api-guides/fatal-errors.rst index c01a70c78..31657eac7 100644 --- a/docs/en/api-guides/fatal-errors.rst +++ b/docs/en/api-guides/fatal-errors.rst @@ -38,21 +38,21 @@ For some of the system level checks (interrupt watchdog, cache access error), th In all cases, error cause will be printed in parens. See `Guru Meditation Errors`_ for a list of possible error causes. -Subsequent behavior of the panic handler can be set using :ref:`CONFIG_{IDF_TARGET_CFG_PREFIX}_PANIC` configuration choice. The available options are: - -- Print registers and reboot — default option. +Subsequent behavior of the panic handler can be set using :ref:`CONFIG_ESP_SYSTEM_PANIC` configuration choice. The available options are: +- Print registers and reboot (``CONFIG_ESP_SYSTEM_PANIC_PRINT_REBOOT``) — default option. + This will print register values at the point of the exception, print the backtrace, and restart the chip. -- Print registers and halt +- Print registers and halt (``CONFIG_ESP_SYSTEM_PANIC_PRINT_HALT``) Similar to the above option, but halt instead of rebooting. External reset is required to restart the program. -- Silent reboot +- Silent reboot (``CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT``) Don't print registers or backtrace, restart the chip immediately. -- Invoke GDB Stub +- Invoke GDB Stub (``CONFIG_ESP_SYSTEM_PANIC_GDBSTUB``) Start GDB server which can communicate with GDB over console UART port. See `GDB Stub`_ for more details. @@ -116,7 +116,7 @@ The following diagram illustrates panic handler behavior: Register Dump and Backtrace --------------------------- -Unless ``CONFIG_{IDF_TARGET_CFG_PREFIX}_PANIC_SILENT_REBOOT`` option is enabled, panic handler prints some of the CPU registers, and the backtrace, to the console:: +Unless ``CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT`` option is enabled, panic handler prints some of the CPU registers, and the backtrace, to the console:: Core 0 register dump: PC : 0x400e14ed PS : 0x00060030 A0 : 0x800d0805 A1 : 0x3ffb5030 @@ -160,7 +160,7 @@ To find the location where a fatal error has happened, look at the lines which f GDB Stub -------- -If ``CONFIG_{IDF_TARGET_CFG_PREFIX}_PANIC_GDBSTUB`` option is enabled, panic handler will not reset the chip when fatal error happens. Instead, it will start GDB remote protocol server, commonly referred to as GDB Stub. When this happens, GDB instance running on the host computer can be instructed to connect to the {IDF_TARGET_NAME} UART port. +If ``CONFIG_ESP_SYSTEM_PANIC_GDBSTUB`` option is enabled, panic handler will not reset the chip when fatal error happens. Instead, it will start GDB remote protocol server, commonly referred to as GDB Stub. When this happens, GDB instance running on the host computer can be instructed to connect to the ESP32 UART port. If :doc:`IDF Monitor ` is used, GDB is started automatically when GDB Stub prompt is detected on the UART. The output would look like this:: diff --git a/docs/en/api-guides/tools/idf-monitor.rst b/docs/en/api-guides/tools/idf-monitor.rst index b5cd8bb4e..1ea5ed31b 100644 --- a/docs/en/api-guides/tools/idf-monitor.rst +++ b/docs/en/api-guides/tools/idf-monitor.rst @@ -103,7 +103,7 @@ By default, if esp-idf crashes, the panic handler prints relevant registers and Optionally, the panic handler can be configured to run GDBStub, the tool which can communicate with GDB_ project debugger. GDBStub allows to read memory, examine call stack frames and variables, etc. It is not as versatile as JTAG debugging, but this method does not require any special hardware. -To enable GDBStub, open the project configuration menu (``idf.py menuconfig``) and set :ref:`CONFIG_{IDF_TARGET_CFG_PREFIX}_PANIC` to ``Invoke GDBStub``. +To enable GDBStub, open the project configuration menu (``idf.py menuconfig``) and set :ref:`CONFIG_ESP_SYSTEM_PANIC` to ``Invoke GDBStub``. In this case, if the panic handler is triggered, as soon as IDF Monitor sees that GDBStub has loaded, it automatically pauses serial monitoring and runs GDB with necessary arguments. After GDB exits, the board is reset via the RTS serial line. If this line is not connected, please reset the board manually by pressing its Reset button. diff --git a/docs/zh_CN/api-guides/fatal-errors.rst b/docs/zh_CN/api-guides/fatal-errors.rst index b3c97201c..8c99acc7d 100644 --- a/docs/zh_CN/api-guides/fatal-errors.rst +++ b/docs/zh_CN/api-guides/fatal-errors.rst @@ -39,21 +39,21 @@ 不管哪种情况,错误原因都会被打印在括号中。请参阅 :ref:`Guru-Meditation-Errors` 以查看所有可能的出错原因。 -紧急处理程序接下来的行为将取决于 :ref:`CONFIG_{IDF_TARGET_CFG_PREFIX}_PANIC` 的设置,支持的选项包括: +紧急处理程序接下来的行为将取决于 :ref:`CONFIG_ESP_SYSTEM_PANIC` 的设置,支持的选项包括: -- 打印 CPU 寄存器,然后重启(``CONFIG_{IDF_TARGET_CFG_PREFIX}_PANIC_PRINT_REBOOT``)- 默认选项 +- 打印 CPU 寄存器,然后重启(``CONFIG_ESP_SYSTEM_PANIC_PRINT_REBOOT``)- 默认选项 打印系统发生异常时 CPU 寄存器的值,打印回溯,最后重启芯片。 -- 打印 CPU 寄存器,然后暂停(``CONFIG_E{IDF_TARGET_CFG_PREFIX}_PANIC_PRINT_HALT``) +- 打印 CPU 寄存器,然后暂停(``CONFIG_ESP_SYSTEM_PANIC_PRINT_HALT``) 与上一个选项类似,但不会重启,而是选择暂停程序的运行。重启程序需要外部执行复位操作。 -- 静默重启(``CONFIG_{IDF_TARGET_CFG_PREFIX}_PANIC_SILENT_REBOOT``) +- 静默重启(``CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT``) 不打印 CPU 寄存器的值,也不打印回溯,立即重启芯片。 -- 调用 GDB Stub(``CONFIG_{IDF_TARGET_CFG_PREFIX}_PANIC_GDBSTUB``) +- 调用 GDB Stub(``CONFIG_ESP_SYSTEM_PANIC_GDBSTUB``) 启动 GDB 服务器,通过控制台 UART 接口与 GDB 进行通信。详细信息请参阅 :ref:`GDB-Stub`。 @@ -112,7 +112,7 @@ 寄存器转储与回溯 ---------------- -除非启用了 ``CONFIG_{IDF_TARGET_CFG_PREFIX}_PANIC_SILENT_REBOOT`` 否则紧急处理程序会将 CPU 寄存器和回溯打印到控制台:: +除非启用了 ``CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT`` 否则紧急处理程序会将 CPU 寄存器和回溯打印到控制台:: Core 0 register dump: PC : 0x400e14ed PS : 0x00060030 A0 : 0x800d0805 A1 : 0x3ffb5030 @@ -158,7 +158,7 @@ GDB Stub -------- -如果启用了 ``CONFIG_{IDF_TARGET_CFG_PREFIX}_PANIC_GDBSTUB`` 选项,在发生严重错误时,紧急处理程序不会复位芯片,相反,它将启动 GDB 远程协议服务器,通常称为 GDB Stub。发生这种情况时,可以让主机上运行的 GDB 实例通过 UART 端口连接到 {IDF_TARGET_NAME}。 +如果启用了 ``CONFIG_ESP_SYSTEM_PANIC_GDBSTUB`` 选项,在发生严重错误时,紧急处理程序不会复位芯片,相反,它将启动 GDB 远程协议服务器,通常称为 GDB Stub。发生这种情况时,可以让主机上运行的 GDB 实例通过 UART 端口连接到 ESP32。 如果使用了 :doc:`IDF 监视器 `,该工具会在 UART 端口检测到 GDB Stub 提示符后自动启动 GDB,输出会类似于:: diff --git a/docs/zh_CN/api-guides/tools/idf-monitor.rst b/docs/zh_CN/api-guides/tools/idf-monitor.rst index 4d83e6573..0b8d8b7b1 100644 --- a/docs/zh_CN/api-guides/tools/idf-monitor.rst +++ b/docs/zh_CN/api-guides/tools/idf-monitor.rst @@ -92,7 +92,7 @@ IDF 监视器在后台运行以下命令,解码各地址:: 或者选择配置 panic 处理器以运行 GDBStub,GDBStub 工具可以与 GDB_ 项目调试器进行通信,允许读取内存、检查调用堆栈帧和变量等。GDBStub 虽然没有 JTAG 通用,但不需要使用特殊硬件。 -如需启用 GDBStub,请运行 ``idf.py menuconfig`` (适用于 CMake 编译系统),并将 :ref:`CONFIG_{IDF_TARGET_CFG_PREFIX}_PANIC` 选项设置为 ``Invoke GDBStub``。 +如需启用 GDBStub,请运行 ``idf.py menuconfig`` (适用于 CMake 编译系统),并将 :ref:`CONFIG_ESP_SYSTEM_PANIC` 选项设置为 ``Invoke GDBStub``。 在这种情况下,如果 panic 处理器被触发,只要 IDF 监视器监控到 GDBStub 已经加载,panic 处理器就会自动暂停串行监控并使用必要的参数运行 GDB。GDB 退出后,通过 RTS 串口线复位开发板。如果未连接 RTS 串口线,请按复位键,手动复位开发板。 From 2855bb6f0ae61cc07cc08b4727524356f99fbc7c Mon Sep 17 00:00:00 2001 From: Renz Christian Bagaporo Date: Sun, 2 Feb 2020 23:18:29 +0800 Subject: [PATCH 3/9] newlib: move abort to newlib --- components/esp_common/CMakeLists.txt | 1 + components/esp_common/linker.lf | 4 ++ .../abort.c => esp_common/src/esp_err.c} | 36 +-------------- components/esp_system/CMakeLists.txt | 2 +- components/newlib/CMakeLists.txt | 1 + components/newlib/abort.c | 46 +++++++++++++++++++ components/newlib/newlib.lf | 3 +- 7 files changed, 55 insertions(+), 38 deletions(-) create mode 100644 components/esp_common/linker.lf rename components/{esp_system/abort.c => esp_common/src/esp_err.c} (69%) create mode 100644 components/newlib/abort.c diff --git a/components/esp_common/CMakeLists.txt b/components/esp_common/CMakeLists.txt index 98b178af6..a5f37842d 100644 --- a/components/esp_common/CMakeLists.txt +++ b/components/esp_common/CMakeLists.txt @@ -7,6 +7,7 @@ if(BOOTLOADER_BUILD) else() # Regular app build set(srcs "src/brownout.c" + "src/esp_err.c" "src/dbg_stubs.c" "src/esp_err_to_name.c" "src/freertos_hooks.c" diff --git a/components/esp_common/linker.lf b/components/esp_common/linker.lf new file mode 100644 index 000000000..589f73740 --- /dev/null +++ b/components/esp_common/linker.lf @@ -0,0 +1,4 @@ +[mapping:esp_common] +archive: lib_esp_common.a +entries: + esp_err (noflash) \ No newline at end of file diff --git a/components/esp_system/abort.c b/components/esp_common/src/esp_err.c similarity index 69% rename from components/esp_system/abort.c rename to components/esp_common/src/esp_err.c index 5933b4a35..3286b0058 100644 --- a/components/esp_system/abort.c +++ b/components/esp_common/src/esp_err.c @@ -11,52 +11,18 @@ // 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 #include #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 +#elif CONFIG_IDF_TARGET_ESP32S2 #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); diff --git a/components/esp_system/CMakeLists.txt b/components/esp_system/CMakeLists.txt index a1439ea79..fbc5c49c1 100644 --- a/components/esp_system/CMakeLists.txt +++ b/components/esp_system/CMakeLists.txt @@ -1,4 +1,4 @@ -idf_component_register(SRCS "abort.c" "panic.c" "reset_reason.c" "system_api.c" +idf_component_register(SRCS "panic.c" "reset_reason.c" "system_api.c" INCLUDE_DIRS include PRIV_INCLUDE_DIRS private_include PRIV_REQUIRES spi_flash app_update diff --git a/components/newlib/CMakeLists.txt b/components/newlib/CMakeLists.txt index 01f0234c2..fd3d5a5d9 100644 --- a/components/newlib/CMakeLists.txt +++ b/components/newlib/CMakeLists.txt @@ -1,4 +1,5 @@ set(srcs + "abort.c" "heap.c" "locks.c" "poll.c" diff --git a/components/newlib/abort.c b/components/newlib/abort.c new file mode 100644 index 000000000..4137d74fc --- /dev/null +++ b/components/newlib/abort.c @@ -0,0 +1,46 @@ +// 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 +#include + +#include "esp_system.h" + +#include "soc/soc_caps.h" +#include "hal/cpu_hal.h" + +void __attribute__((noreturn)) abort(void) +{ + #define ERR_STR1 "abort() was called at PC 0x" + #define ERR_STR2 " on core " + + _Static_assert(UINTPTR_MAX == 0xffffffff, "abort() assumes 32-bit addresses"); + _Static_assert(SOC_CPU_CORES_NUM < 10, "abort() assumes number of cores is 1 to 9"); + + char addr_buf[9] = { 0 }; + char core_buf[2] = { 0 }; + + char buf[sizeof(ERR_STR1) + sizeof(addr_buf) + sizeof(core_buf) + sizeof(ERR_STR2) + 1 /* null char */] = { 0 }; + + itoa((uint32_t)(__builtin_return_address(0) - 3), addr_buf, 16); + itoa(cpu_ll_get_core_id(), core_buf, 10); + + const char *str[] = { ERR_STR1, addr_buf, ERR_STR2, core_buf }; + + char *dest = buf; + for (int i = 0; i < sizeof(str) / sizeof(str[0]); i++) { + strcat(dest, str[i]); + } + + esp_system_abort(buf); +} diff --git a/components/newlib/newlib.lf b/components/newlib/newlib.lf index 59d3ace55..63a93164a 100644 --- a/components/newlib/newlib.lf +++ b/components/newlib/newlib.lf @@ -1,6 +1,5 @@ -# Places the heap related functions from heap.c into IRAM - [mapping:newlib] archive: libnewlib.a entries: heap (noflash) + abort (noflash) From af9b1131a3685d0aad61a7bfbe0ce54d2d895bcf Mon Sep 17 00:00:00 2001 From: Renz Christian Bagaporo Date: Tue, 4 Feb 2020 22:16:37 +0800 Subject: [PATCH 4/9] esp_system: share abort panic with port layer --- components/esp_system/component.mk | 2 +- components/esp_system/panic.c | 18 +++++++++--------- .../port/include/port/panic_funcs.h | 2 ++ components/esp_system/port/panic_handler.c | 18 ++++++++++++------ .../private_include/panic_internal.h | 4 ++++ components/newlib/abort.c | 19 ++++++++++--------- 6 files changed, 38 insertions(+), 25 deletions(-) diff --git a/components/esp_system/component.mk b/components/esp_system/component.mk index e7f5eb293..7979a2664 100644 --- a/components/esp_system/component.mk +++ b/components/esp_system/component.mk @@ -2,7 +2,7 @@ SOC_NAME := $(IDF_TARGET) COMPONENT_SRCDIRS := . COMPONENT_ADD_INCLUDEDIRS := include -COMPONENT_PRIV_INCLUDEDIRS := private_include +COMPONENT_PRIV_INCLUDEDIRS := private_include port/include COMPONENT_ADD_LDFRAGMENTS += linker.lf -include $(COMPONENT_PATH)/port/$(SOC_NAME)/component.mk \ No newline at end of file diff --git a/components/esp_system/panic.c b/components/esp_system/panic.c index 87d579786..7418a4b44 100644 --- a/components/esp_system/panic.c +++ b/components/esp_system/panic.c @@ -41,6 +41,7 @@ #endif #include "panic_internal.h" +#include "port/panic_funcs.h" #include "sdkconfig.h" @@ -50,8 +51,8 @@ #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; +bool g_panic_abort = false; +static char *s_panic_abort_details = NULL; #if !CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT @@ -144,7 +145,7 @@ static inline void disable_all_wdts(void) static void print_abort_details(const void *f) { - panic_print_str(s_abort_details); + panic_print_str(s_panic_abort_details); } // Control arrives from chip-specific panic handler, environment prepared for @@ -153,10 +154,9 @@ static void print_abort_details(const void *f) 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) { + if (g_panic_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->details = s_panic_abort_details ? print_abort_details : NULL; info->reason = "SoftwareAbort"; info->exception = PANIC_EXCEPTION_ABORT; } @@ -304,7 +304,7 @@ void esp_panic_handler(panic_info_t *info) } panic_print_str("Rebooting...\r\n"); - esp_restart_noos(); + panic_restart(); #else disable_all_wdts(); panic_print_str("CPU halted.\r\n"); @@ -315,8 +315,8 @@ void esp_panic_handler(panic_info_t *info) void __attribute__((noreturn)) panic_abort(const char *details) { - s_abort = true; - s_abort_details = (char*) details; + g_panic_abort = true; + s_panic_abort_details = (char*) details; #if CONFIG_APPTRACE_ENABLE #if CONFIG_SYSVIEW_ENABLE diff --git a/components/esp_system/port/include/port/panic_funcs.h b/components/esp_system/port/include/port/panic_funcs.h index 28ab5ba24..6eafc70c4 100644 --- a/components/esp_system/port/include/port/panic_funcs.h +++ b/components/esp_system/port/include/port/panic_funcs.h @@ -12,4 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. +#pragma once + void __attribute__((noreturn)) panic_restart(void); \ No newline at end of file diff --git a/components/esp_system/port/panic_handler.c b/components/esp_system/port/panic_handler.c index b0423c368..f1f410609 100644 --- a/components/esp_system/port/panic_handler.c +++ b/components/esp_system/port/panic_handler.c @@ -233,6 +233,15 @@ static void print_registers(const void *f, int core) } } +static void print_state_for_core(const void *f, int core) +{ + if (!g_panic_abort) { + print_registers(f, core); + panic_print_str("\r\n"); + } + print_backtrace(f, core); +} + static void print_state(const void* f) { #if !CONFIG_FREERTOS_UNICORE @@ -241,9 +250,8 @@ static void print_state(const void* f) int err_core = 0; #endif - print_registers(f, err_core); - panic_print_str("\r\n"); - print_backtrace(f, err_core); + print_state_for_core(f, err_core); + panic_print_str("\r\n"); #if !CONFIG_FREERTOS_UNICORE @@ -251,10 +259,8 @@ static void print_state(const void* f) for (int i = 0; i < SOC_CPU_CORES_NUM; i++) { // `f` is the frame for the offending core, see note above. if (err_core != i && xt_exc_frames[i] != NULL) { + print_state_for_core(xt_exc_frames[i], i); panic_print_str("\r\n"); - print_registers(xt_exc_frames[i], i); - panic_print_str("\r\n"); - print_backtrace(xt_exc_frames[i], i); } } #endif diff --git a/components/esp_system/private_include/panic_internal.h b/components/esp_system/private_include/panic_internal.h index 5656ac487..fb9accfff 100644 --- a/components/esp_system/private_include/panic_internal.h +++ b/components/esp_system/private_include/panic_internal.h @@ -16,8 +16,12 @@ #include +#include "port/panic_funcs.h" + #include "sdkconfig.h" +extern bool g_panic_abort; + // Function to print longer amounts of information such as the details // and backtrace field of panic_info_t. These functions should limit themselves // to printing to the console and should do other more involved processing, diff --git a/components/newlib/abort.c b/components/newlib/abort.c index 4137d74fc..7f9f7075d 100644 --- a/components/newlib/abort.c +++ b/components/newlib/abort.c @@ -21,25 +21,26 @@ void __attribute__((noreturn)) abort(void) { - #define ERR_STR1 "abort() was called at PC 0x" - #define ERR_STR2 " on core " - + #define ERR_STR1 "abort() was called at PC 0x" + #define ERR_STR2 " on core " + _Static_assert(UINTPTR_MAX == 0xffffffff, "abort() assumes 32-bit addresses"); _Static_assert(SOC_CPU_CORES_NUM < 10, "abort() assumes number of cores is 1 to 9"); - + char addr_buf[9] = { 0 }; char core_buf[2] = { 0 }; - + char buf[sizeof(ERR_STR1) + sizeof(addr_buf) + sizeof(core_buf) + sizeof(ERR_STR2) + 1 /* null char */] = { 0 }; - + itoa((uint32_t)(__builtin_return_address(0) - 3), addr_buf, 16); itoa(cpu_ll_get_core_id(), core_buf, 10); - + const char *str[] = { ERR_STR1, addr_buf, ERR_STR2, core_buf }; + + char *dest = buf; - char *dest = buf; for (int i = 0; i < sizeof(str) / sizeof(str[0]); i++) { - strcat(dest, str[i]); + strcat(dest, str[i]); } esp_system_abort(buf); From 29ebfc3f462f92f20cb157406999693db6ae21a0 Mon Sep 17 00:00:00 2001 From: Renz Christian Bagaporo Date: Tue, 18 Feb 2020 13:27:30 +0500 Subject: [PATCH 5/9] esp_system: let panic handler break on debug mode on software abort --- components/esp_system/panic.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/components/esp_system/panic.c b/components/esp_system/panic.c index 7418a4b44..2a88f22bb 100644 --- a/components/esp_system/panic.c +++ b/components/esp_system/panic.c @@ -327,10 +327,6 @@ void __attribute__((noreturn)) panic_abort(const char *details) #endif #endif - while (1) { - if (esp_cpu_in_ocd_debug_mode()) { - cpu_hal_break(); - } - *((int *) 0) = 0; // should be an invalid operation on targets - } + *((int *) 0) = 0; // should be an invalid operation on targets + while(1); } \ No newline at end of file From 84e80a3e50e2dca2d6e43dee56cf34011ce6bddc Mon Sep 17 00:00:00 2001 From: Renz Christian Bagaporo Date: Mon, 2 Mar 2020 10:02:23 +0500 Subject: [PATCH 6/9] soc: remove sdio slave hal source file duplicate --- components/soc/src/esp32/CMakeLists.txt | 1 - components/soc/src/esp32/sdio_slave_hal.c | 728 ---------------------- 2 files changed, 729 deletions(-) delete mode 100644 components/soc/src/esp32/sdio_slave_hal.c diff --git a/components/soc/src/esp32/CMakeLists.txt b/components/soc/src/esp32/CMakeLists.txt index e4c31149f..fd81a73f2 100644 --- a/components/soc/src/esp32/CMakeLists.txt +++ b/components/soc/src/esp32/CMakeLists.txt @@ -6,7 +6,6 @@ set(srcs "brownout_hal.c" "rtc_sleep.c" "rtc_time.c" "rtc_wdt.c" - "sdio_slave_hal.c" "soc_memory_layout.c" "touch_sensor_hal.c") diff --git a/components/soc/src/esp32/sdio_slave_hal.c b/components/soc/src/esp32/sdio_slave_hal.c deleted file mode 100644 index 485fb09df..000000000 --- a/components/soc/src/esp32/sdio_slave_hal.c +++ /dev/null @@ -1,728 +0,0 @@ -// Copyright 2015-2019 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. - -// The HAL layer for SDIO slave (common part) - -#include -#include -#include -#include -#include -#include "hal/sdio_slave_hal.h" -#include "hal/hal_defs.h" -#include "esp_attr.h" - - -#define SDIO_SLAVE_CHECK(res, str, ret_val) do { if(!(res)){\ - HAL_LOGE(TAG, "%s", str);\ - return ret_val;\ -} }while (0) - -static const char TAG[] = "SDIO_HAL"; - -static esp_err_t init_send_queue(sdio_slave_context_t *hal); - -/**************** Ring buffer for SDIO sending use *****************/ -typedef enum { - RINGBUF_GET_ONE = 0, - RINGBUF_GET_ALL = 1, -} ringbuf_get_all_t; - -typedef enum { - RINGBUF_WRITE_PTR, - RINGBUF_READ_PTR, - RINGBUF_FREE_PTR, -} sdio_ringbuf_pointer_t; - -static esp_err_t sdio_ringbuf_send(sdio_ringbuf_t *buf, esp_err_t (*copy_callback)(uint8_t *, void *), void *arg); -static inline esp_err_t sdio_ringbuf_recv(sdio_ringbuf_t *buf, uint8_t **start, uint8_t **end, ringbuf_get_all_t get_all); -static inline int sdio_ringbuf_return(sdio_ringbuf_t* buf, uint8_t *ptr); - -#define _SEND_DESC_NEXT(x) STAILQ_NEXT(&((sdio_slave_hal_send_desc_t*)x)->dma_desc, qe) -#define SEND_DESC_NEXT(x) (sdio_slave_hal_send_desc_t*)_SEND_DESC_NEXT(x) -#define SEND_DESC_NEXT_SET(x, target) do { \ - _SEND_DESC_NEXT(x)=(lldesc_t*)target; \ - }while(0) - -static esp_err_t link_desc_to_last(uint8_t* desc, void* arg) -{ - SEND_DESC_NEXT_SET(arg, desc); - return ESP_OK; -} - -//calculate a pointer with offset to a original pointer of the specific ringbuffer -static inline uint8_t* sdio_ringbuf_offset_ptr(sdio_ringbuf_t *buf, sdio_ringbuf_pointer_t ptr, uint32_t offset) -{ - uint8_t *buf_ptr; - switch (ptr) { - case RINGBUF_WRITE_PTR: - buf_ptr = buf->write_ptr; - break; - case RINGBUF_READ_PTR: - buf_ptr = buf->read_ptr; - break; - case RINGBUF_FREE_PTR: - buf_ptr = buf->free_ptr; - break; - default: - abort(); - } - - uint8_t *offset_ptr=buf_ptr+offset; - if (offset_ptr >= buf->data + buf->size) { - offset_ptr -= buf->size; - } - return offset_ptr; -} - -static esp_err_t sdio_ringbuf_send(sdio_ringbuf_t *buf, esp_err_t (*copy_callback)(uint8_t *, void *), void *arg) -{ - uint8_t* get_ptr = sdio_ringbuf_offset_ptr(buf, RINGBUF_WRITE_PTR, SDIO_SLAVE_SEND_DESC_SIZE); - esp_err_t err = ESP_OK; - if (copy_callback) { - (*copy_callback)(get_ptr, arg); - } - if (err != ESP_OK) return err; - - buf->write_ptr = get_ptr; - return ESP_OK; -} - -// this ringbuf is a return-before-recv-again strategy -// since this is designed to be called in the ISR, no parallel logic -static inline esp_err_t sdio_ringbuf_recv(sdio_ringbuf_t *buf, uint8_t **start, uint8_t **end, ringbuf_get_all_t get_all) -{ - assert(buf->free_ptr == buf->read_ptr); //must return before recv again - if (start == NULL && end == NULL) return ESP_ERR_INVALID_ARG; // must have a output - if (buf->read_ptr == buf->write_ptr) return ESP_ERR_NOT_FOUND; // no data - - uint8_t *get_start = sdio_ringbuf_offset_ptr(buf, RINGBUF_READ_PTR, SDIO_SLAVE_SEND_DESC_SIZE); - - if (get_all != RINGBUF_GET_ONE) { - buf->read_ptr = buf->write_ptr; - } else { - buf->read_ptr = get_start; - } - - if (start != NULL) { - *start = get_start; - } - if (end != NULL) { - *end = buf->read_ptr; - } - return ESP_OK; -} - -static inline int sdio_ringbuf_return(sdio_ringbuf_t* buf, uint8_t *ptr) -{ - assert(sdio_ringbuf_offset_ptr(buf, RINGBUF_FREE_PTR, SDIO_SLAVE_SEND_DESC_SIZE) == ptr); - int size = (buf->read_ptr + buf->size - buf->free_ptr) % buf->size; - int count = size / SDIO_SLAVE_SEND_DESC_SIZE; - assert(count * SDIO_SLAVE_SEND_DESC_SIZE==size); - buf->free_ptr = buf->read_ptr; - return count; -} - -static inline uint8_t* sdio_ringbuf_peek_front(sdio_ringbuf_t* buf) -{ - if (buf->read_ptr != buf->write_ptr) { - return sdio_ringbuf_offset_ptr(buf, RINGBUF_READ_PTR, SDIO_SLAVE_SEND_DESC_SIZE); - } else { - return NULL; - } -} - -static inline uint8_t* sdio_ringbuf_peek_rear(sdio_ringbuf_t *buf) -{ - return buf->write_ptr; -} - -static inline bool sdio_ringbuf_empty(sdio_ringbuf_t* buf) -{ - return (buf->read_ptr == buf->write_ptr); -} - -/**************** End of Ring buffer *****************/ - -void sdio_slave_hal_init(sdio_slave_context_t *hal) -{ - hal->host = sdio_slave_ll_get_host(0); - hal->slc = sdio_slave_ll_get_slc(0); - hal->hinf = sdio_slave_ll_get_hinf(0); - hal->send_state = STATE_IDLE; - hal->recv_link_list = (sdio_slave_hal_recv_stailq_t)STAILQ_HEAD_INITIALIZER(hal->recv_link_list); - - init_send_queue(hal); -} - -void sdio_slave_hal_hw_init(sdio_slave_context_t *hal) -{ - sdio_slave_ll_init(hal->slc); - sdio_slave_ll_enable_hs(hal->hinf, true); - sdio_slave_ll_set_timing(hal->host, hal->timing); - sdio_slave_ll_slvint_t intr_ena = 0xff; - sdio_slave_ll_slvint_set_ena(hal->slc, &intr_ena); -} - -static esp_err_t init_send_queue(sdio_slave_context_t *hal) -{ - esp_err_t ret; - esp_err_t rcv_res; - sdio_ringbuf_t *buf = &(hal->send_desc_queue); - - //initialize pointers - buf->write_ptr = buf->data; - buf->read_ptr = buf->data; - buf->free_ptr = buf->data; - - sdio_slave_hal_send_desc_t *first = NULL, *last = NULL; - //no copy for the first descriptor - - ret = sdio_ringbuf_send(buf, NULL, NULL); - if (ret != ESP_OK) return ret; - - //loop in the ringbuf to link all the desc one after another as a ring - for (int i = 0; i < hal->send_queue_size + 1; i++) { - rcv_res = sdio_ringbuf_recv(buf, (uint8_t **) &last, NULL, RINGBUF_GET_ONE); - assert (rcv_res == ESP_OK); - - ret = sdio_ringbuf_send(buf, link_desc_to_last, last); - if (ret != ESP_OK) return ret; - - sdio_ringbuf_return(buf, (uint8_t *) last); - } - - first = NULL; - last = NULL; - //clear the queue - rcv_res = sdio_ringbuf_recv(buf, (uint8_t **) &first, (uint8_t **) &last, RINGBUF_GET_ALL); - assert (rcv_res == ESP_OK); - assert(first == last); //there should be only one desc remain - sdio_ringbuf_return(buf, (uint8_t *) first); - return ESP_OK; -} - -void sdio_slave_hal_set_ioready(sdio_slave_context_t *hal, bool ready) -{ - sdio_slave_ll_set_ioready(hal->hinf, ready); //set IO ready to 1 to allow host to use -} - - -/*--------------------------------------------------------------------------- - * Send - * - * The hardware has a cache, so that once a descriptor is loaded onto the linked-list, it cannot be modified - * until returned (used) by the hardware. This forbids us from loading descriptors onto the linked list during - * the transfer (or the time waiting for host to start a transfer). However, we use a "ringbuffer" (different from - * the one in ``freertos/`` folder) holding descriptors to solve this: - - * 1. The driver allocates continuous memory for several buffer descriptors (the maximum buffer number) during - * initialization. Then the driver points the STAILQ_NEXT pointer of all the descriptors except the last one - * to the next descriptor of each of them. Then the pointer of the last descriptor points back to the first one: - * now the descriptor is in a ring. - - * 2. The "ringbuffer" has a write pointer points to where app can write new descriptor. The app writes the new descriptor - * indicated by the write pointer without touching the STAILQ_NEXT pointer so that the descriptors are always in a - * ring-like linked-list. The app never touches the part of linked-list being used by the hardware. - - * 3. When the hardware needs some data to send, it automatically pick a part of linked descriptors. According to the mode: - * - Buffer mode: only pick the next one to the last one sent; - * - Stream mode: pick the whole unsent linked list, starting from the one above, to the latest linked one. - - * The driver removes the STAILQ_NEXT pointer of the last descriptor and put the head of the part to the DMA controller so - * that it looks like just a linear linked-list rather than a ring to the hardware. - - * 4. The counter of sending FIFO can increase when app load new buffers (in STREAM_MODE) or when new transfer should - * start (in PACKET_MODE). - - * 5. When the sending transfer is finished, the driver goes through the descriptors just send in the ISR and push all - * the ``arg`` member of descriptors to the queue back to the app, so that the app can handle finished buffers. The - * driver also fix the STAILQ_NEXT pointer of the last descriptor so that the descriptors are now in a ring again. -----------------------------------------------------------------------------*/ -static inline void send_set_state(sdio_slave_context_t *hal, send_state_t state) -{ - hal->send_state = state; -} - -static inline send_state_t send_get_state(sdio_slave_context_t* hal) -{ - return hal->send_state; -} - -DMA_ATTR static const lldesc_t start_desc = { - .owner = 1, - .buf = (void*)0x3ffbbbbb, //assign a dma-capable pointer other than NULL, which will not be used - .size = 1, - .length = 1, - .eof = 1, -}; - -//force trigger rx_done interrupt. the interrupt is abused to invoke ISR from the app by the enable bit and never cleared. -static void send_isr_invoker_enable(const sdio_slave_context_t *hal) -{ - sdio_slave_ll_send_reset(hal->slc); - sdio_slave_ll_send_start(hal->slc, &start_desc); - //wait for rx_done - while(!sdio_slave_ll_send_invoker_ready(hal->slc)); - sdio_slave_ll_send_stop(hal->slc); - sdio_slave_ll_send_hostint_clr(hal->host); -} - -static void send_isr_invoker_disable(sdio_slave_context_t *hal) -{ - sdio_slave_ll_send_part_done_clear(hal->slc); -} - -void sdio_slave_hal_send_handle_isr_invoke(sdio_slave_context_t *hal) -{ - sdio_slave_ll_send_part_done_intr_ena(hal->slc, false); -} - -//start hw operation with existing data (if exist) -esp_err_t sdio_slave_hal_send_start(sdio_slave_context_t *hal) -{ - SDIO_SLAVE_CHECK(send_get_state(hal) == STATE_IDLE, - "already started", ESP_ERR_INVALID_STATE); - send_set_state(hal, STATE_WAIT_FOR_START); - send_isr_invoker_enable(hal); - sdio_slave_ll_send_intr_clr(hal->slc); - sdio_slave_ll_send_intr_ena(hal->slc, true); - return ESP_OK; -} - -//only stop hw operations, no touch to data as well as counter -void sdio_slave_hal_send_stop(sdio_slave_context_t *hal) -{ - sdio_slave_ll_send_stop(hal->slc); - send_isr_invoker_disable(hal); - sdio_slave_ll_send_intr_ena(hal->slc, false); - send_set_state(hal, STATE_IDLE); -} - -static void send_new_packet(sdio_slave_context_t *hal) -{ - // since eof is changed, we have to stop and reset the link list, - // and restart new link list operation - sdio_slave_hal_send_desc_t *const start_desc = hal->in_flight_head; - sdio_slave_hal_send_desc_t *const end_desc = hal->in_flight_end; - assert(start_desc != NULL && end_desc != NULL); - - sdio_slave_ll_send_stop(hal->slc); - sdio_slave_ll_send_reset(hal->slc); - sdio_slave_ll_send_start(hal->slc, (lldesc_t*)start_desc); - - // update pkt_len register to allow host reading. - sdio_slave_ll_send_write_len(hal->slc, end_desc->pkt_len); - ESP_EARLY_LOGV(TAG, "send_length_write: %d, last_len: %08X", end_desc->pkt_len, sdio_slave_ll_send_read_len(hal->host)); - - send_set_state(hal, STATE_SENDING); - - ESP_EARLY_LOGD(TAG, "restart new send: %p->%p, pkt_len: %d", start_desc, end_desc, end_desc->pkt_len); -} - -static esp_err_t send_check_new_packet(sdio_slave_context_t *hal) -{ - esp_err_t ret; - sdio_slave_hal_send_desc_t *start = NULL; - sdio_slave_hal_send_desc_t *end = NULL; - if (hal->sending_mode == SDIO_SLAVE_SEND_PACKET) { - ret = sdio_ringbuf_recv(&(hal->send_desc_queue), (uint8_t **) &start, (uint8_t **) &end, RINGBUF_GET_ONE); - } else { //stream mode - ret = sdio_ringbuf_recv(&(hal->send_desc_queue), (uint8_t **) &start, (uint8_t **) &end, RINGBUF_GET_ALL); - } - if (ret == ESP_OK) { - hal->in_flight_head = start; - hal->in_flight_end = end; - end->dma_desc.eof = 1; - //temporarily break the link ring here, the ring will be re-connected in ``send_isr_eof()``. - hal->in_flight_next = SEND_DESC_NEXT(end); - SEND_DESC_NEXT_SET(end, NULL); - } - return ESP_OK; -} - -bool sdio_slave_hal_send_eof_happened(sdio_slave_context_t* hal) -{ - // Goto idle state (cur_start=NULL) if transmission done, - // also update sequence and recycle descs. - if (sdio_slave_ll_send_done(hal->slc)) { - //check current state - assert(send_get_state(hal) == STATE_SENDING); - sdio_slave_ll_send_intr_clr(hal->slc); - return true; - } else { - return false; - } -} - -//clear counter but keep data -esp_err_t sdio_slave_hal_send_reset_counter(sdio_slave_context_t* hal) -{ - SDIO_SLAVE_CHECK(send_get_state(hal) == STATE_IDLE, - "reset counter when transmission started", ESP_ERR_INVALID_STATE); - - sdio_slave_ll_send_write_len(hal->slc, 0); - ESP_EARLY_LOGV(TAG, "last_len: %08X", sdio_slave_ll_send_read_len(hal->host)); - - hal->tail_pkt_len = 0; - sdio_slave_hal_send_desc_t *desc = hal->in_flight_head; - while(desc != NULL) { - hal->tail_pkt_len += desc->dma_desc.length; - desc->pkt_len = hal->tail_pkt_len; - desc = SEND_DESC_NEXT(desc); - } - // in theory the desc should be the one right next to the last of in_flight_head, - // but the link of last is NULL, so get the desc from the ringbuf directly. - desc = (sdio_slave_hal_send_desc_t*)sdio_ringbuf_peek_front(&(hal->send_desc_queue)); - while(desc != NULL) { - hal->tail_pkt_len += desc->dma_desc.length; - desc->pkt_len = hal->tail_pkt_len; - desc = SEND_DESC_NEXT(desc); - } - - return ESP_OK; -} - -static esp_err_t send_get_inflight_desc(sdio_slave_context_t *hal, void **out_arg, uint32_t *out_returned_cnt, - bool init) -{ - esp_err_t ret; - if (init) { - assert(hal->returned_desc == NULL); - hal->returned_desc = hal->in_flight_head; - send_set_state(hal, STATE_GETTING_RESULT); - } - - if (hal->returned_desc != NULL) { - *out_arg = hal->returned_desc->arg; - hal->returned_desc = SEND_DESC_NEXT(hal->returned_desc); - ret = ESP_OK; - } else { - if (hal->in_flight_head != NULL) { - // fix the link broken of last desc when being sent - assert(hal->in_flight_end != NULL); - SEND_DESC_NEXT_SET(hal->in_flight_end, hal->in_flight_next); - - *out_returned_cnt = sdio_ringbuf_return(&(hal->send_desc_queue), (uint8_t*)hal->in_flight_head); - } - - hal->in_flight_head = NULL; - hal->in_flight_end = NULL; - - ret = ESP_ERR_NOT_FOUND; - } - return ret; -} - -static esp_err_t send_get_unsent_desc(sdio_slave_context_t *hal, void **out_arg, uint32_t *out_return_cnt) -{ - esp_err_t ret; - sdio_slave_hal_send_desc_t *head, *tail; - ret = sdio_ringbuf_recv(&(hal->send_desc_queue), (uint8_t **) &head, (uint8_t **) &tail, RINGBUF_GET_ONE); - - if (ret == ESP_OK) { - //currently each packet takes only one desc. - assert(head == tail); - (*out_arg) = head->arg; - (*out_return_cnt) = sdio_ringbuf_return(&(hal->send_desc_queue), (uint8_t*) head); - } else if (ret == ESP_ERR_NOT_FOUND) { - // if in wait to send state, set the sequence number of tail to the value last sent, just as if the packet wait to - // send never queued. - // Go to idle state (cur_end!=NULL and cur_start=NULL) - send_set_state(hal, STATE_IDLE); - hal->tail_pkt_len = sdio_slave_ll_send_read_len(hal->host); - } - return ret; -} - -esp_err_t sdio_slave_hal_send_get_next_finished_arg(sdio_slave_context_t *hal, void **out_arg, uint32_t* out_returned_cnt) -{ - bool init = (send_get_state(hal) == STATE_SENDING); - if (init) { - assert(hal->in_flight_head != NULL); - } else { - assert(send_get_state(hal) == STATE_GETTING_RESULT); - } - *out_returned_cnt = 0; - - esp_err_t ret = send_get_inflight_desc(hal, out_arg, out_returned_cnt, init); - - if (ret == ESP_ERR_NOT_FOUND) { - // Go to wait for packet state - send_set_state(hal, STATE_WAIT_FOR_START); - } - return ret; -} - - -esp_err_t sdio_slave_hal_send_flush_next_buffer(sdio_slave_context_t *hal, void **out_arg, uint32_t *out_return_cnt) -{ - esp_err_t ret = ESP_OK; - *out_return_cnt = 0; - bool init = (send_get_state(hal) == STATE_IDLE); - if (!init) { - if (send_get_state(hal) != STATE_GETTING_RESULT && send_get_state(hal) != STATE_GETTING_UNSENT_DESC) { - return ESP_ERR_INVALID_STATE; - } - } - - if (init || send_get_state(hal) == STATE_GETTING_RESULT) { - ret = send_get_inflight_desc(hal, out_arg, out_return_cnt, init); - if (ret == ESP_ERR_NOT_FOUND) { - send_set_state(hal, STATE_GETTING_UNSENT_DESC); - } - } - if (send_get_state(hal) == STATE_GETTING_UNSENT_DESC) { - ret = send_get_unsent_desc(hal, out_arg, out_return_cnt); - if (ret == ESP_ERR_NOT_FOUND) { - send_set_state(hal, STATE_IDLE); - } - } - return ret; -} - -esp_err_t sdio_slave_hal_send_new_packet_if_exist(sdio_slave_context_t *hal) -{ - esp_err_t ret; - // Go to wait sending state (cur_start!=NULL && cur_end==NULL) if not sending and new packet ready. - // Note we may also enter this state by stopping sending in the app. - if (send_get_state(hal) == STATE_WAIT_FOR_START) { - if (hal->in_flight_head == NULL) { - send_check_new_packet(hal); - } - // Go to sending state (cur_start and cur_end != NULL) if has packet to send. - if (hal->in_flight_head) { - send_new_packet(hal); - ret = ESP_OK; - } else { - ret = ESP_ERR_NOT_FOUND; - } - } else { - ret = ESP_ERR_INVALID_STATE; - } - return ret; -} - -static esp_err_t send_write_desc(uint8_t* desc, void* arg) -{ - sdio_slave_hal_send_desc_t* next_desc = SEND_DESC_NEXT(desc); - memcpy(desc, arg, sizeof(sdio_slave_hal_send_desc_t)); - SEND_DESC_NEXT_SET(desc, next_desc); - return ESP_OK; -} - -static void send_isr_invoke(sdio_slave_context_t *hal) -{ - sdio_slave_ll_send_part_done_intr_ena(hal->slc, true); -} - -esp_err_t sdio_slave_hal_send_queue(sdio_slave_context_t* hal, uint8_t *addr, size_t len, void *arg) -{ - hal->tail_pkt_len += len; - sdio_slave_hal_send_desc_t new_desc = { - .dma_desc = { - .size = len, - .length = len, - .buf = addr, - .owner = 1, - // in stream mode, the eof is only appended (in ISR) when new packet is ready to be sent - .eof = (hal->sending_mode == SDIO_SLAVE_SEND_PACKET), - }, - .arg = arg, - .pkt_len = hal->tail_pkt_len, - }; - - esp_err_t ret = sdio_ringbuf_send(&(hal->send_desc_queue), send_write_desc, &new_desc); - send_isr_invoke(hal); - return ret; -} - -/*--------------------------------------------------------------------------- - * Receive - *--------------------------------------------------------------------------*/ - -static lldesc_t* recv_get_first_empty_buf(sdio_slave_context_t* hal) -{ - sdio_slave_hal_recv_stailq_t *const queue = &(hal->recv_link_list); - lldesc_t *desc = STAILQ_FIRST(queue); - while(desc && desc->owner == 0) { - desc = STAILQ_NEXT(desc, qe); - } - return desc; -} - -void sdio_slave_hal_recv_stop(sdio_slave_context_t* hal) -{ - sdio_slave_ll_set_ioready(hal->hinf, false); //set IO ready to 0 to stop host from using - sdio_slave_ll_send_stop(hal->slc); - sdio_slave_ll_recv_stop(hal->slc); - sdio_slave_ll_recv_intr_ena(hal->slc, false); -} - -//touching linked list, should be protected by spinlock -bool sdio_slave_hal_recv_has_next_item(sdio_slave_context_t* hal) -{ - - if (hal->recv_cur_ret == NULL || hal->recv_cur_ret->owner != 0) return false; - - // This may cause the ``cur_ret`` pointer to be NULL, indicating the list is empty, - // in this case the ``tx_done`` should happen no longer until new desc is appended. - // The app is responsible to place the pointer to the right place again when appending new desc. - - hal->recv_cur_ret = STAILQ_NEXT(hal->recv_cur_ret, qe); - return true; -} - -bool sdio_slave_hal_recv_done(sdio_slave_context_t *hal) -{ - bool ret = sdio_slave_ll_recv_done(hal->slc); - if (ret) { - sdio_slave_ll_recv_done_clear(hal->slc); - } - return ret; -} - -lldesc_t *sdio_slave_hal_recv_unload_desc(sdio_slave_context_t *hal) -{ - sdio_slave_hal_recv_stailq_t *const queue = &hal->recv_link_list; - lldesc_t *desc = STAILQ_FIRST(queue); - if (desc) { - STAILQ_REMOVE_HEAD(queue, qe); - } - return desc; -} - -void sdio_slave_hal_recv_init_desc(sdio_slave_context_t* hal, lldesc_t *desc, uint8_t *start) -{ - *desc = (lldesc_t) { - .size = hal->recv_buffer_size, - .buf = start, - }; -} - -void sdio_slave_hal_recv_start(sdio_slave_context_t *hal) -{ - sdio_slave_ll_recv_reset(hal->slc); - lldesc_t *desc = recv_get_first_empty_buf(hal); - if (!desc) { - HAL_LOGD(TAG, "recv: restart without desc"); - } else { - //the counter is handled when add/flush/reset - sdio_slave_ll_recv_start(hal->slc, desc); - sdio_slave_ll_recv_intr_ena(hal->slc, true); - } -} - -void sdio_slave_hal_recv_reset_counter(sdio_slave_context_t *hal) -{ - sdio_slave_ll_recv_size_reset(hal->slc); - lldesc_t *desc = recv_get_first_empty_buf(hal); - while (desc != NULL) { - sdio_slave_ll_recv_size_inc(hal->slc); - desc = STAILQ_NEXT(desc, qe); - } -} - -void sdio_slave_hal_recv_flush_one_buffer(sdio_slave_context_t *hal) -{ - sdio_slave_hal_recv_stailq_t *const queue = &hal->recv_link_list; - lldesc_t *desc = STAILQ_FIRST(queue); - assert (desc != NULL && desc->owner == 0); - STAILQ_REMOVE_HEAD(queue, qe); - desc->owner = 1; - STAILQ_INSERT_TAIL(queue, desc, qe); - sdio_slave_ll_recv_size_inc(hal->slc); - //we only add it to the tail here, without start the DMA nor increase buffer num. -} - -void sdio_slave_hal_load_buf(sdio_slave_context_t *hal, lldesc_t *desc) -{ - sdio_slave_hal_recv_stailq_t *const queue = &(hal->recv_link_list); - desc->owner = 1; - - lldesc_t *const tail = STAILQ_LAST(queue, lldesc_s, qe); - - STAILQ_INSERT_TAIL(queue, desc, qe); - if (hal->recv_cur_ret == NULL) { - hal->recv_cur_ret = desc; - } - - if (tail == NULL) { - //no one in the ll, start new ll operation. - sdio_slave_ll_recv_start(hal->slc, desc); - sdio_slave_ll_recv_intr_ena(hal->slc, true); - HAL_LOGV(TAG, "recv_load_buf: start new"); - } else { - //restart former ll operation - sdio_slave_ll_recv_restart(hal->slc); - HAL_LOGV(TAG, "recv_load_buf: restart"); - } - sdio_slave_ll_recv_size_inc(hal->slc); -} - -static inline void show_queue_item(lldesc_t *item) -{ - ESP_EARLY_LOGI(TAG, "=> %p: size: %d(%d), eof: %d, owner: %d", item, item->size, item->length, item->eof, item->owner); - ESP_EARLY_LOGI(TAG, " buf: %p, stqe_next: %p", item->buf, item->qe.stqe_next); -} - -static void __attribute((unused)) dump_queue(sdio_slave_hal_recv_stailq_t *queue) -{ - int cnt = 0; - lldesc_t *item = NULL; - ESP_EARLY_LOGI(TAG, ">>>>> first: %p, last: %p <<<<<", queue->stqh_first, queue->stqh_last); - STAILQ_FOREACH(item, queue, qe) { - cnt++; - show_queue_item(item); - } - ESP_EARLY_LOGI(TAG, "total: %d", cnt); -} - -/*--------------------------------------------------------------------------- - * Host - *--------------------------------------------------------------------------*/ -void sdio_slave_hal_hostint_get_ena(sdio_slave_context_t *hal, sdio_slave_hostint_t *out_int_mask) -{ - *out_int_mask = sdio_slave_ll_host_get_intena(hal->host); -} - -void sdio_slave_hal_hostint_clear(sdio_slave_context_t *hal, const sdio_slave_hostint_t *mask) -{ - sdio_slave_ll_host_intr_clear(hal->host, mask);//clear all interrupts -} - -void sdio_slave_hal_hostint_set_ena(sdio_slave_context_t *hal, const sdio_slave_hostint_t *mask) -{ - sdio_slave_ll_host_set_intena(hal->host, mask); -} - -void sdio_slave_hal_hostint_send(sdio_slave_context_t *hal, const sdio_slave_hostint_t *mask) -{ - sdio_slave_ll_host_send_int(hal->slc, mask); -} - -uint8_t sdio_slave_hal_host_get_reg(sdio_slave_context_t *hal, int pos) -{ - return sdio_slave_ll_host_get_reg(hal->host, pos); -} -void sdio_slave_hal_host_set_reg(sdio_slave_context_t *hal, int pos, uint8_t reg) -{ - sdio_slave_ll_host_set_reg(hal->host, pos, reg); -} - -void sdio_slave_hal_slvint_fetch_clear(sdio_slave_context_t *hal, sdio_slave_ll_slvint_t *out_int_mask) -{ - sdio_slave_ll_slvint_fetch_clear(hal->slc, out_int_mask); -} - From 890510aecd1732870a603f34702639734759171c Mon Sep 17 00:00:00 2001 From: Renz Bagaporo Date: Wed, 4 Mar 2020 17:12:06 +0800 Subject: [PATCH 7/9] esp32, esp32s2: move reset reason source to esp_system --- components/esp32/CMakeLists.txt | 1 - components/esp32s2/CMakeLists.txt | 1 - components/esp32s2/cache_err_int.c | 2 ++ components/esp_system/CMakeLists.txt | 2 +- .../esp_system/port/esp32/CMakeLists.txt | 3 +- .../port}/esp32/reset_reason.c | 0 .../esp_system/port/esp32s2/CMakeLists.txt | 3 +- .../port}/esp32s2/reset_reason.c | 0 components/esp_system/port/panic_handler.c | 5 +++- components/esp_system/reset_reason.c | 30 ------------------- 10 files changed, 11 insertions(+), 36 deletions(-) rename components/{ => esp_system/port}/esp32/reset_reason.c (100%) rename components/{ => esp_system/port}/esp32s2/reset_reason.c (100%) delete mode 100644 components/esp_system/reset_reason.c diff --git a/components/esp32/CMakeLists.txt b/components/esp32/CMakeLists.txt index 269ca7020..51d524d32 100644 --- a/components/esp32/CMakeLists.txt +++ b/components/esp32/CMakeLists.txt @@ -24,7 +24,6 @@ else() "intr_alloc.c" "pm_esp32.c" "pm_trace.c" - "reset_reason.c" "sleep_modes.c" "spiram.c" "spiram_psram.c" diff --git a/components/esp32s2/CMakeLists.txt b/components/esp32s2/CMakeLists.txt index ff6d7038c..f1cf31f4b 100644 --- a/components/esp32s2/CMakeLists.txt +++ b/components/esp32s2/CMakeLists.txt @@ -21,7 +21,6 @@ else() "intr_alloc.c" "pm_esp32s2.c" "pm_trace.c" - "reset_reason.c" "sleep_modes.c" "spiram.c" "spiram_psram.c" diff --git a/components/esp32s2/cache_err_int.c b/components/esp32s2/cache_err_int.c index ab7328456..966479456 100644 --- a/components/esp32s2/cache_err_int.c +++ b/components/esp32s2/cache_err_int.c @@ -78,5 +78,7 @@ void esp_cache_err_int_init(void) int IRAM_ATTR esp_cache_err_get_cpuid(void) { + // TODO: The description for this seem to indicate that when cache is not in error + // state, return -1. return PRO_CPU_NUM; } diff --git a/components/esp_system/CMakeLists.txt b/components/esp_system/CMakeLists.txt index fbc5c49c1..7e5b4d57c 100644 --- a/components/esp_system/CMakeLists.txt +++ b/components/esp_system/CMakeLists.txt @@ -1,4 +1,4 @@ -idf_component_register(SRCS "panic.c" "reset_reason.c" "system_api.c" +idf_component_register(SRCS "panic.c" "system_api.c" INCLUDE_DIRS include PRIV_INCLUDE_DIRS private_include PRIV_REQUIRES spi_flash app_update diff --git a/components/esp_system/port/esp32/CMakeLists.txt b/components/esp_system/port/esp32/CMakeLists.txt index ed1a5652d..ca83bcc61 100644 --- a/components/esp_system/port/esp32/CMakeLists.txt +++ b/components/esp_system/port/esp32/CMakeLists.txt @@ -1 +1,2 @@ -target_sources(${COMPONENT_LIB} PRIVATE "${CMAKE_CURRENT_LIST_DIR}/dport_panic_highint_hdl.S") \ No newline at end of file +target_sources(${COMPONENT_LIB} PRIVATE "${CMAKE_CURRENT_LIST_DIR}/dport_panic_highint_hdl.S" + "${CMAKE_CURRENT_LIST_DIR}/reset_reason.c") diff --git a/components/esp32/reset_reason.c b/components/esp_system/port/esp32/reset_reason.c similarity index 100% rename from components/esp32/reset_reason.c rename to components/esp_system/port/esp32/reset_reason.c diff --git a/components/esp_system/port/esp32s2/CMakeLists.txt b/components/esp_system/port/esp32s2/CMakeLists.txt index ed1a5652d..ca83bcc61 100644 --- a/components/esp_system/port/esp32s2/CMakeLists.txt +++ b/components/esp_system/port/esp32s2/CMakeLists.txt @@ -1 +1,2 @@ -target_sources(${COMPONENT_LIB} PRIVATE "${CMAKE_CURRENT_LIST_DIR}/dport_panic_highint_hdl.S") \ No newline at end of file +target_sources(${COMPONENT_LIB} PRIVATE "${CMAKE_CURRENT_LIST_DIR}/dport_panic_highint_hdl.S" + "${CMAKE_CURRENT_LIST_DIR}/reset_reason.c") diff --git a/components/esp32s2/reset_reason.c b/components/esp_system/port/esp32s2/reset_reason.c similarity index 100% rename from components/esp32s2/reset_reason.c rename to components/esp_system/port/esp32s2/reset_reason.c diff --git a/components/esp_system/port/panic_handler.c b/components/esp_system/port/panic_handler.c index f1f410609..57ad3c552 100644 --- a/components/esp_system/port/panic_handler.c +++ b/components/esp_system/port/panic_handler.c @@ -517,6 +517,7 @@ static __attribute__((noreturn)) void esp_digital_reset(void) #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) { @@ -527,7 +528,9 @@ static __attribute__((noreturn)) void esp_digital_reset(void) void __attribute__((noreturn)) panic_restart(void) { // If resetting because of a cache error, reset the digital part - if (esp_cache_err_get_cpuid() != -1) { + // Make sure that the reset reason is not a generic panic reason as well on ESP32S2, + // as esp_cache_err_get_cpuid always returns PRO_CPU_NUM + if (esp_cache_err_get_cpuid() != -1 && esp_reset_reason_get_hint() != ESP_RST_PANIC) { esp_digital_reset(); } else { esp_restart_noos(); diff --git a/components/esp_system/reset_reason.c b/components/esp_system/reset_reason.c deleted file mode 100644 index a4bebbfb9..000000000 --- a/components/esp_system/reset_reason.c +++ /dev/null @@ -1,30 +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 "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; -} - From 7cc8cb68bd508f57ef72143296237bf9d8abd0aa Mon Sep 17 00:00:00 2001 From: Renz Bagaporo Date: Wed, 4 Mar 2020 10:25:49 +0800 Subject: [PATCH 8/9] esp_system: suppress reason display on software abort panic --- components/esp_system/panic.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/components/esp_system/panic.c b/components/esp_system/panic.c index 2a88f22bb..655da68e9 100644 --- a/components/esp_system/panic.c +++ b/components/esp_system/panic.c @@ -157,7 +157,7 @@ void esp_panic_handler(panic_info_t *info) if (g_panic_abort) { info->description = NULL; info->details = s_panic_abort_details ? print_abort_details : NULL; - info->reason = "SoftwareAbort"; + info->reason = NULL; info->exception = PANIC_EXCEPTION_ABORT; } @@ -184,11 +184,13 @@ void esp_panic_handler(panic_info_t *info) * 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->reason) { + panic_print_str("Guru Meditation Error: Core "); + panic_print_dec(info->core); + panic_print_str(" panic'ed ("); + panic_print_str(info->reason); + panic_print_str("). "); + } if (info->description) { panic_print_str(info->description); From ee519634a5222d5fe86f8d51cb288f4cb7f9eb6d Mon Sep 17 00:00:00 2001 From: Renz Bagaporo Date: Wed, 4 Mar 2020 13:23:30 +0800 Subject: [PATCH 9/9] esp_system: retain backtrace no space --- components/esp_system/port/panic_handler.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/esp_system/port/panic_handler.c b/components/esp_system/port/panic_handler.c index 57ad3c552..7fe4f8bcd 100644 --- a/components/esp_system/port/panic_handler.c +++ b/components/esp_system/port/panic_handler.c @@ -146,7 +146,7 @@ static void print_backtrace(const void* f, int core) 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: "); + 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