diff --git a/components/esp32/panic.c b/components/esp32/panic.c index 8e31f25739..76803c95fa 100644 --- a/components/esp32/panic.c +++ b/components/esp32/panic.c @@ -621,11 +621,7 @@ static __attribute__((noreturn)) void commonErrorHandler(XtExcFrame *frame) disableAllWdts(); s_dumping_core = true; #if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH - if (esp_ptr_external_ram(get_sp())) { - panicPutStr("Stack in PSRAM, skipping core dump to Flash.") - } else { - esp_core_dump_to_flash(frame); - } + 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); diff --git a/components/espcoredump/CMakeLists.txt b/components/espcoredump/CMakeLists.txt index a58b197288..6f82362b59 100644 --- a/components/espcoredump/CMakeLists.txt +++ b/components/espcoredump/CMakeLists.txt @@ -6,6 +6,7 @@ set(COMPONENT_ADD_LDFRAGMENTS linker.lf) set(COMPONENT_SRCS "src/core_dump_common.c" "src/core_dump_flash.c" "src/core_dump_port.c" - "src/core_dump_uart.c") + "src/core_dump_uart.c" + "src/core_dump_flash_newstack.c") register_component() diff --git a/components/espcoredump/linker.lf b/components/espcoredump/linker.lf index 30d13caefc..b96fc2072c 100644 --- a/components/espcoredump/linker.lf +++ b/components/espcoredump/linker.lf @@ -4,4 +4,5 @@ entries: core_dump_uart (noflash_text) core_dump_flash (noflash_text) core_dump_common (noflash_text) - core_dump_port (noflash_text) \ No newline at end of file + core_dump_port (noflash_text) + core_dump_flash_newstack (noflash_text) diff --git a/components/espcoredump/src/core_dump_common.c b/components/espcoredump/src/core_dump_common.c index 7e00c1b40b..2def79f8b7 100644 --- a/components/espcoredump/src/core_dump_common.c +++ b/components/espcoredump/src/core_dump_common.c @@ -25,7 +25,7 @@ const static DRAM_ATTR char TAG[] __attribute__((unused)) = "esp_core_dump_commo static esp_err_t esp_core_dump_write_binary(void *frame, core_dump_write_config_t *write_cfg) { esp_err_t err; - core_dump_task_header_t tasks[CONFIG_ESP32_CORE_DUMP_MAX_TASKS_NUM]; + static core_dump_task_header_t tasks[CONFIG_ESP32_CORE_DUMP_MAX_TASKS_NUM]; uint32_t tcb_sz, task_num, tcb_sz_padded; bool task_is_valid = false; uint32_t data_len = 0, i; diff --git a/components/espcoredump/src/core_dump_flash.c b/components/espcoredump/src/core_dump_flash.c index 0aa23e9663..be6752db9e 100644 --- a/components/espcoredump/src/core_dump_flash.c +++ b/components/espcoredump/src/core_dump_flash.c @@ -198,8 +198,13 @@ static esp_err_t esp_core_dump_flash_write_data(void *priv, void * data, uint32_ return err; } +#ifdef CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY +#define esp_core_dump_to_flash esp_core_dump_to_flash_inner +#endif // CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY + void esp_core_dump_to_flash(XtExcFrame *frame) { + ESP_COREDUMP_LOGI("%s called with sp: %p frame: %p", __func__, get_sp(), frame); core_dump_write_config_t wr_cfg; core_dump_write_flash_data_t wr_data; @@ -228,5 +233,5 @@ void esp_core_dump_to_flash(XtExcFrame *frame) esp_core_dump_write((void*)frame, &wr_cfg); ESP_COREDUMP_LOGI("Core dump has been saved to flash."); } -#endif +#endif // CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH diff --git a/components/espcoredump/src/core_dump_flash_newstack.c b/components/espcoredump/src/core_dump_flash_newstack.c new file mode 100644 index 0000000000..4c0c8d8d8c --- /dev/null +++ b/components/espcoredump/src/core_dump_flash_newstack.c @@ -0,0 +1,82 @@ +// 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. + +#include +#include +#include "soc/soc.h" +#include "soc/soc_memory_layout.h" +#include "esp_core_dump_priv.h" + +#if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH && CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY + +const static DRAM_ATTR char TAG[] __attribute__((unused)) = "esp_core_dump_flash_newstack"; + +/* Temporary stack used by the core dump; Stack size is chosen to be sufficient in case logging is enabled. */ +#define CORE_DUMP_STACK_SIZE 1024 +static uint8_t __attribute__((aligned(16))) core_dump_stack[CORE_DUMP_STACK_SIZE]; + +/* This jmp_buf is used to jump *into* switch_stack_helper function, simultaneously setting + * the stack pointer to the temporary stack. It is initialized manually, not using setjmp. + */ +static jmp_buf switch_stack_jmp; + +/* This jmp_buf is used to return to the original stack (in external RAM) after the core dump is done. */ +static jmp_buf return_jmp; + +/* Internals of jmp_buf for Xtensa Window ABI. See setjmp.S for details */ +struct jmp_buf_impl { + int regs[12]; + int save[4]; + void *return_address; +}; + +/* Defined in core_dump_flash.c if CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY is set */ +extern void esp_core_dump_to_flash_inner(XtExcFrame *frame); + +/* We are going to jump to switch_stack_helper+3 (i.e. past the ENTRY instruction) + * using longjmp(switch_stack_jmp). Execution will start on the temporary stack. + */ +static void switch_stack_helper(void* arg) +{ + /* Using the new stack now, can perform the core dump */ + esp_core_dump_to_flash_inner((XtExcFrame*) arg); + /* Jump back to where setjmp(return_jmp) was called, switching to the original stack */ + longjmp(return_jmp, 1); +} + +void esp_core_dump_to_flash(XtExcFrame *frame) +{ + if (esp_ptr_external_ram(get_sp())) { + ESP_COREDUMP_LOGI("Stack in external RAM, switching to new stack %p - %p", core_dump_stack, core_dump_stack + sizeof(core_dump_stack)); + /* Prepare new stack */ + uint32_t* stack_top = (uint32_t*) (core_dump_stack + sizeof(core_dump_stack)/sizeof(core_dump_stack[0])); + stack_top -= 4; + /* Prepare jmp_buf to jump into the switch_stack_helper, simultaneously switching to the new stack */ + struct jmp_buf_impl* jb = (struct jmp_buf_impl*) &switch_stack_jmp; + jb->regs[1] = (int) stack_top; /* SP */ + jb->regs[2] = (int) frame; /* function argument (a2) */ + jb->save[1] = ((int)stack_top) + 32; /* pointer to base save area for the current frame */ + jb->return_address = ((uint8_t*)&switch_stack_helper) + 3; /* First instruction of switch_stack_helper, after ENTRY */ + + /* Set up return_jmp for returning here */ + if (!setjmp(return_jmp)) { + longjmp(switch_stack_jmp, 1); + } + } else { + ESP_COREDUMP_LOGI("Stack in DRAM"); + esp_core_dump_to_flash_inner(frame); + } +} + +#endif // CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH && CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY diff --git a/components/espcoredump/src/core_dump_port.c b/components/espcoredump/src/core_dump_port.c index e045b19a4e..34ff582313 100644 --- a/components/espcoredump/src/core_dump_port.c +++ b/components/espcoredump/src/core_dump_port.c @@ -20,9 +20,9 @@ const static DRAM_ATTR char TAG[] __attribute__((unused)) = "esp_core_dump_port" #if CONFIG_ESP32_ENABLE_COREDUMP -inline bool esp_task_stack_start_is_sane(uint32_t sp) +static inline bool esp_task_stack_start_is_sane(uint32_t sp) { - return !(sp < 0x3ffae010UL || sp > 0x3fffffffUL); + return esp_ptr_in_dram((const void*)sp) || esp_ptr_external_ram((const void*)sp); } inline bool esp_tcb_addr_is_sane(uint32_t addr, uint32_t sz)