core dump: move stack into the internal memory when dumping to flash

Since SPI flash operations can not be performed when the stack is in
external RAM, we need to switch to a different stack when doing a
core dump to flash. This is achieved by a pair of longjmp's, one of
which is crafted manually to jump into a designated function with a
specific stack set up.
The cost of this feature is a bit of IRAM, plus the DRAM required for
the extra stack.

Closes AUD-1355
This commit is contained in:
Ivan Grokhotkov 2020-03-11 20:27:26 +01:00 committed by maojianxin
parent a80cf2dc69
commit de662d5e11
7 changed files with 96 additions and 11 deletions

View File

@ -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);

View File

@ -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()

View File

@ -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)
core_dump_port (noflash_text)
core_dump_flash_newstack (noflash_text)

View File

@ -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;

View File

@ -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

View File

@ -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 <setjmp.h>
#include <string.h>
#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

View File

@ -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)