kopia lustrzana https://github.com/espressif/esp-idf
341 wiersze
12 KiB
C
341 wiersze
12 KiB
C
/*
|
|
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
#include <string.h>
|
|
#include <stdbool.h>
|
|
#include "sdkconfig.h"
|
|
#include "soc/soc_memory_layout.h"
|
|
#include "freertos/FreeRTOS.h"
|
|
#include "esp_private/freertos_debug.h"
|
|
#include "esp_rom_sys.h"
|
|
#include "esp_core_dump_port.h"
|
|
#include "esp_core_dump_common.h"
|
|
#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD
|
|
#include "esp_private/hw_stack_guard.h"
|
|
#endif // CONFIG_ESP_SYSTEM_HW_STACK_GUARD
|
|
|
|
const static char TAG[] __attribute__((unused)) = "esp_core_dump_common";
|
|
|
|
#if CONFIG_ESP_COREDUMP_ENABLE
|
|
|
|
#define COREDUMP_GET_MEMORY_SIZE(end, start) (end - start)
|
|
|
|
/**
|
|
* @brief Memory regions to dump, defined at compile time.
|
|
*/
|
|
extern int _coredump_dram_start;
|
|
extern int _coredump_dram_end;
|
|
extern int _coredump_iram_start;
|
|
extern int _coredump_iram_end;
|
|
#if SOC_RTC_MEM_SUPPORTED
|
|
extern int _coredump_rtc_start;
|
|
extern int _coredump_rtc_end;
|
|
extern int _coredump_rtc_fast_start;
|
|
extern int _coredump_rtc_fast_end;
|
|
#endif
|
|
|
|
/**
|
|
* @brief In the menconfig, it is possible to specify a specific stack size for
|
|
* core dump generation.
|
|
*/
|
|
#if CONFIG_ESP_COREDUMP_STACK_SIZE > 0
|
|
|
|
/**
|
|
* @brief If stack size has been specified for the core dump generation, create
|
|
* a stack that will be used during the whole core dump generation.
|
|
*/
|
|
#if LOG_LOCAL_LEVEL >= ESP_LOG_DEBUG
|
|
/* Increase stack size in verbose mode */
|
|
#define ESP_COREDUMP_STACK_SIZE (CONFIG_ESP_COREDUMP_STACK_SIZE+100)
|
|
#else
|
|
#define ESP_COREDUMP_STACK_SIZE CONFIG_ESP_COREDUMP_STACK_SIZE
|
|
#endif
|
|
|
|
#define COREDUMP_STACK_FILL_BYTE (0xa5U)
|
|
|
|
static uint8_t s_coredump_stack[ESP_COREDUMP_STACK_SIZE];
|
|
static uint8_t* s_core_dump_sp = NULL;
|
|
static core_dump_stack_context_t s_stack_context;
|
|
|
|
/**
|
|
* @brief Function setting up the core dump stack.
|
|
*
|
|
* @note This function **must** be aligned as it modifies the
|
|
* stack pointer register.
|
|
*/
|
|
FORCE_INLINE_ATTR void esp_core_dump_setup_stack(void)
|
|
{
|
|
s_core_dump_sp = (uint8_t *)((uint32_t)(s_coredump_stack + ESP_COREDUMP_STACK_SIZE - 1) & ~0xf);
|
|
memset(s_coredump_stack, COREDUMP_STACK_FILL_BYTE, ESP_COREDUMP_STACK_SIZE);
|
|
|
|
/* watchpoint 1 can be used for task stack overflow detection, re-use it, it is no more necessary */
|
|
//esp_cpu_clear_watchpoint(1);
|
|
//esp_cpu_set_watchpoint(1, s_coredump_stack, 1, ESP_WATCHPOINT_STORE);
|
|
|
|
#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD
|
|
/* Save the current area we are watching to restore it later */
|
|
esp_hw_stack_guard_get_bounds(&s_stack_context.sp_min, &s_stack_context.sp_max);
|
|
/* Since the stack is going to change, make sure we disable protection or an exception would be triggered */
|
|
esp_hw_stack_guard_monitor_stop();
|
|
#endif // CONFIG_ESP_SYSTEM_HW_STACK_GUARD
|
|
|
|
/* Replace the stack pointer depending on the architecture, but save the
|
|
* current stack pointer, in order to be able too restore it later.
|
|
* This function must be inlined. */
|
|
esp_core_dump_replace_sp(s_core_dump_sp, &s_stack_context);
|
|
ESP_COREDUMP_LOGI("Backing up stack @ 0x%" PRIx32 " and use core dump stack @ %p",
|
|
s_stack_context.sp, esp_cpu_get_sp());
|
|
|
|
#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD
|
|
/* Re-enable the stack guard to check if the stack is big enough for coredump generation */
|
|
esp_hw_stack_guard_set_bounds((uint32_t) s_coredump_stack, (uint32_t) s_core_dump_sp);
|
|
esp_hw_stack_guard_monitor_start();
|
|
#endif // CONFIG_ESP_SYSTEM_HW_STACK_GUARD
|
|
}
|
|
|
|
/**
|
|
* @brief Calculate how many bytes are free on the stack set up earlier.
|
|
*
|
|
* @return Size, in bytes, of the available space on the stack.
|
|
*/
|
|
FORCE_INLINE_ATTR uint32_t esp_core_dump_free_stack_space(const uint8_t *pucStackByte)
|
|
{
|
|
uint32_t ulCount = 0U;
|
|
while (ulCount < ESP_COREDUMP_STACK_SIZE &&
|
|
*pucStackByte == (uint8_t)COREDUMP_STACK_FILL_BYTE) {
|
|
pucStackByte -= portSTACK_GROWTH;
|
|
ulCount++;
|
|
}
|
|
ulCount /= sizeof(uint8_t);
|
|
return ulCount;
|
|
}
|
|
|
|
/**
|
|
* @brief Print how many bytes have been used on the stack to create the core
|
|
* dump.
|
|
*/
|
|
FORCE_INLINE_ATTR void esp_core_dump_report_stack_usage(void)
|
|
{
|
|
#if CONFIG_ESP_COREDUMP_LOGS
|
|
uint32_t bytes_free = esp_core_dump_free_stack_space(s_coredump_stack);
|
|
ESP_COREDUMP_LOGI("Core dump used %" PRIu32 " bytes on stack. %" PRIu32 " bytes left free.",
|
|
s_core_dump_sp - s_coredump_stack - bytes_free, bytes_free);
|
|
#endif
|
|
|
|
/* Restore the stack pointer. */
|
|
ESP_COREDUMP_LOGI("Restoring stack @ 0x%" PRIx32, s_stack_context.sp);
|
|
#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD
|
|
esp_hw_stack_guard_monitor_stop();
|
|
#endif // CONFIG_ESP_SYSTEM_HW_STACK_GUARD
|
|
esp_core_dump_restore_sp(&s_stack_context);
|
|
#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD
|
|
/* Monitor the same stack area that was set before replacing the stack pointer */
|
|
esp_hw_stack_guard_set_bounds(s_stack_context.sp_min, s_stack_context.sp_max);
|
|
esp_hw_stack_guard_monitor_start();
|
|
#endif // CONFIG_ESP_SYSTEM_HW_STACK_GUARD
|
|
}
|
|
|
|
#else // CONFIG_ESP_COREDUMP_STACK_SIZE == 0
|
|
|
|
/* Here, we are not going to use a custom stack for coredump. Make sure the current configuration doesn't require one. */
|
|
#if CONFIG_ESP_COREDUMP_USE_STACK_SIZE
|
|
#pragma error "CONFIG_ESP_COREDUMP_STACK_SIZE must not be 0 in the current configuration"
|
|
#endif // ESP_COREDUMP_USE_STACK_SIZE
|
|
|
|
FORCE_INLINE_ATTR void esp_core_dump_setup_stack(void)
|
|
{
|
|
/* If we are in ISR set watchpoint to the end of ISR stack */
|
|
if (esp_core_dump_in_isr_context()) {
|
|
uint8_t* topStack = esp_core_dump_get_isr_stack_top();
|
|
esp_cpu_clear_watchpoint(1);
|
|
esp_cpu_set_watchpoint(1, topStack + xPortGetCoreID()*configISR_STACK_SIZE, 1, ESP_CPU_WATCHPOINT_STORE);
|
|
} else {
|
|
/* for tasks user should enable stack overflow detection in menuconfig
|
|
TODO: if not enabled in menuconfig enable it ourselves */
|
|
}
|
|
}
|
|
|
|
FORCE_INLINE_ATTR void esp_core_dump_report_stack_usage(void)
|
|
{
|
|
}
|
|
#endif // CONFIG_ESP_COREDUMP_STACK_SIZE > 0
|
|
|
|
static void* s_exc_frame = NULL;
|
|
|
|
inline static void esp_core_dump_write_internal(panic_info_t *info)
|
|
{
|
|
bool isr_context = esp_core_dump_in_isr_context();
|
|
|
|
s_exc_frame = (void *)info->frame;
|
|
|
|
esp_core_dump_setup_stack();
|
|
esp_core_dump_port_init(info, isr_context);
|
|
esp_err_t err = esp_core_dump_store();
|
|
if (err != ESP_OK) {
|
|
ESP_COREDUMP_LOGE("Core dump write failed with error=%d", err);
|
|
}
|
|
esp_core_dump_report_stack_usage();
|
|
}
|
|
|
|
void __attribute__((weak)) esp_core_dump_init(void)
|
|
{
|
|
/* do nothing by default */
|
|
}
|
|
|
|
/**
|
|
* Common functions related to core dump generation.
|
|
*/
|
|
static void esp_core_dump_switch_task_stack_to_isr(core_dump_task_header_t *task,
|
|
core_dump_mem_seg_header_t *stack)
|
|
{
|
|
if (stack != NULL) {
|
|
stack->start = task->stack_start;
|
|
stack->size = esp_core_dump_get_memory_len(task->stack_start, task->stack_end);
|
|
}
|
|
task->stack_start = (uint32_t) s_exc_frame;
|
|
task->stack_end = esp_core_dump_get_isr_stack_end();
|
|
ESP_COREDUMP_LOG_PROCESS("Switched task %p to ISR stack [%" PRIx32 "...%" PRIx32 "]", task->tcb_addr,
|
|
task->stack_start,
|
|
task->stack_end);
|
|
}
|
|
|
|
inline void esp_core_dump_reset_tasks_snapshots_iter(void)
|
|
{
|
|
esp_core_dump_reset_fake_stacks();
|
|
}
|
|
|
|
bool esp_core_dump_get_task_snapshot(void *handle, core_dump_task_header_t *task,
|
|
core_dump_mem_seg_header_t *interrupted_stack)
|
|
{
|
|
TaskSnapshot_t rtos_snapshot = { 0 };
|
|
|
|
if (interrupted_stack != NULL) {
|
|
interrupted_stack->size = 0;
|
|
}
|
|
|
|
vTaskGetSnapshot(handle, &rtos_snapshot);
|
|
task->tcb_addr = handle;
|
|
task->stack_start = (uint32_t)rtos_snapshot.pxTopOfStack;
|
|
task->stack_end = (uint32_t)rtos_snapshot.pxEndOfStack;
|
|
|
|
if (!esp_core_dump_in_isr_context() && handle == esp_core_dump_get_current_task_handle()) {
|
|
// Set correct stack top for current task; only modify if we came from the task,
|
|
// and not an ISR that crashed.
|
|
task->stack_start = (uint32_t) s_exc_frame;
|
|
}
|
|
if (!esp_core_dump_check_task(task)) {
|
|
ESP_COREDUMP_LOG_PROCESS("Task %p is broken!", handle);
|
|
return false;
|
|
}
|
|
if (handle == esp_core_dump_get_current_task_handle()) {
|
|
ESP_COREDUMP_LOG_PROCESS("Crashed task %p", handle);
|
|
esp_core_dump_port_set_crashed_tcb((uint32_t)handle);
|
|
if (esp_core_dump_in_isr_context()) {
|
|
esp_core_dump_switch_task_stack_to_isr(task, interrupted_stack);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
uint32_t esp_core_dump_get_user_ram_segments(void)
|
|
{
|
|
uint32_t total_sz = 0;
|
|
|
|
// count number of memory segments to insert into ELF structure
|
|
total_sz += COREDUMP_GET_MEMORY_SIZE(&_coredump_dram_end, &_coredump_dram_start) > 0 ? 1 : 0;
|
|
#if SOC_RTC_MEM_SUPPORTED
|
|
total_sz += COREDUMP_GET_MEMORY_SIZE(&_coredump_rtc_end, &_coredump_rtc_start) > 0 ? 1 : 0;
|
|
total_sz += COREDUMP_GET_MEMORY_SIZE(&_coredump_rtc_fast_end, &_coredump_rtc_fast_start) > 0 ? 1 : 0;
|
|
#endif
|
|
total_sz += COREDUMP_GET_MEMORY_SIZE(&_coredump_iram_end, &_coredump_iram_start) > 0 ? 1 : 0;
|
|
|
|
return total_sz;
|
|
}
|
|
|
|
uint32_t esp_core_dump_get_user_ram_size(void)
|
|
{
|
|
uint32_t total_sz = 0;
|
|
|
|
total_sz += COREDUMP_GET_MEMORY_SIZE(&_coredump_dram_end, &_coredump_dram_start);
|
|
#if SOC_RTC_MEM_SUPPORTED
|
|
total_sz += COREDUMP_GET_MEMORY_SIZE(&_coredump_rtc_end, &_coredump_rtc_start);
|
|
total_sz += COREDUMP_GET_MEMORY_SIZE(&_coredump_rtc_fast_end, &_coredump_rtc_fast_start);
|
|
#endif
|
|
total_sz += COREDUMP_GET_MEMORY_SIZE(&_coredump_iram_end, &_coredump_iram_start);
|
|
|
|
return total_sz;
|
|
}
|
|
|
|
int esp_core_dump_get_user_ram_info(coredump_region_t region, uint32_t *start)
|
|
{
|
|
int total_sz = -1;
|
|
|
|
ESP_COREDUMP_DEBUG_ASSERT(start != NULL);
|
|
|
|
switch (region) {
|
|
case COREDUMP_MEMORY_DRAM:
|
|
*start = (uint32_t)&_coredump_dram_start;
|
|
total_sz = (uint8_t *)&_coredump_dram_end - (uint8_t *)&_coredump_dram_start;
|
|
break;
|
|
|
|
case COREDUMP_MEMORY_IRAM:
|
|
*start = (uint32_t)&_coredump_iram_start;
|
|
total_sz = (uint8_t *)&_coredump_iram_end - (uint8_t *)&_coredump_iram_start;
|
|
break;
|
|
|
|
#if SOC_RTC_MEM_SUPPORTED
|
|
case COREDUMP_MEMORY_RTC:
|
|
*start = (uint32_t)&_coredump_rtc_start;
|
|
total_sz = (uint8_t *)&_coredump_rtc_end - (uint8_t *)&_coredump_rtc_start;
|
|
break;
|
|
|
|
case COREDUMP_MEMORY_RTC_FAST:
|
|
*start = (uint32_t)&_coredump_rtc_fast_start;
|
|
total_sz = (uint8_t *)&_coredump_rtc_fast_end - (uint8_t *)&_coredump_rtc_fast_start;
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return total_sz;
|
|
}
|
|
|
|
inline bool esp_core_dump_tcb_addr_is_sane(uint32_t addr)
|
|
{
|
|
return esp_core_dump_mem_seg_is_sane(addr, esp_core_dump_get_tcb_len());
|
|
}
|
|
|
|
inline bool esp_core_dump_in_isr_context(void)
|
|
{
|
|
#if CONFIG_ESP_TASK_WDT_EN
|
|
/* This function will be used to check whether a panic occurred in an ISR.
|
|
* In that case, the execution frame must be switch to the interrupt stack.
|
|
* However, in case where the task watchdog ISR calls the panic handler,
|
|
* `xPortInterruptedFromISRContext` returns true, BUT, we don't want to
|
|
* switch the frame to the ISR context. Thus, check that we are not
|
|
* coming from TWDT ISR. This should be refactored.
|
|
* TODO: IDF-5694. */
|
|
extern bool g_twdt_isr;
|
|
return xPortInterruptedFromISRContext() && !g_twdt_isr;
|
|
#else // CONFIG_ESP_TASK_WDT_EN
|
|
return xPortInterruptedFromISRContext();
|
|
#endif // CONFIG_ESP_TASK_WDT_EN
|
|
}
|
|
|
|
void esp_core_dump_write(panic_info_t *info)
|
|
{
|
|
#if CONFIG_ESP_COREDUMP_ENABLE_TO_UART && CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT
|
|
return;
|
|
#endif
|
|
|
|
esp_core_dump_print_write_start();
|
|
esp_core_dump_write_internal(info);
|
|
esp_core_dump_print_write_end();
|
|
}
|
|
|
|
#endif
|