Merge branch 'fix/int_wdt_double_exception_v5.5' into 'release/v5.5'

fix(espcoredump): prevent double exception during int_wdt panic with custom stack (v5.5)

See merge request espressif/esp-idf!44174
This commit is contained in:
Alexey Gerenkov
2026-01-21 23:03:41 +08:00
12 changed files with 376 additions and 273 deletions
+2 -3
View File
@@ -19,13 +19,12 @@ set(priv_includes "include_core_dump")
idf_build_get_property(target IDF_TARGET)
if(CONFIG_IDF_TARGET_ARCH_XTENSA)
list(APPEND srcs "src/port/xtensa/core_dump_port.c")
list(APPEND srcs "src/port/xtensa/core_dump_port.c"
"src/port/xtensa/core_dump_stack_switch.S")
list(APPEND includes "include/port/xtensa")
list(APPEND priv_includes "include_core_dump/port/xtensa")
elseif(CONFIG_IDF_TARGET_ARCH_RISCV)
list(APPEND srcs "src/port/riscv/core_dump_port.c")
list(APPEND includes "include/port/riscv")
list(APPEND priv_includes "include_core_dump/port/riscv")
endif()
idf_component_register(SRCS ${srcs}
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -20,7 +20,6 @@
#include "freertos/FreeRTOS.h"
#include "esp_app_format.h"
#include "esp_core_dump_types.h"
#include "esp_core_dump_port_impl.h"
#include "esp_core_dump.h"
#ifdef __cplusplus
@@ -106,10 +105,10 @@ uint32_t esp_core_dump_get_stack(core_dump_task_header_t* task_snapshot,
* @note The goal of this function is to check whether the task passed is the
* task that crashed or not. If this is the case and if it didn't crash
* within an ISR, its stack pointer will be set to the panic frame,
* containing all the registers values when the error occured. This
* containing all the registers values when the error occurred. This
* function also checks if the TCB address is sane or not.
*
* @param task Pointer to the frame exception generated when the panic occured.
* @param task Pointer to the frame exception generated when the panic occurred.
*
* @return True if the TCB is sane, false else.
*/
@@ -120,7 +119,7 @@ bool esp_core_dump_check_task(core_dump_task_header_t *task);
*
* @note In practice, this function is used to fill the ELF file with the
* PR_STATUS sections for all the existing tasks. This structure
* contains the CPU registers value when the exception occured.
* contains the CPU registers value when the exception occurred.
*
* @param task Task to dump the registers from.
* @param reg_dump Pointer that will be filled with the registers dump.
@@ -182,6 +181,16 @@ void esp_core_dump_summary_parse_backtrace_info(esp_core_dump_bt_info_t *bt_info
const void *paddr, uint32_t stack_size);
#endif /* CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH && CONFIG_ESP_COREDUMP_DATA_FORMAT_ELF */
/**
* @brief Stack switching implemented in C for RISC-V, Assembly for Xtensa
*
* This function switches to the coredump stack, writes the ELF core dump,
* reports stack usage, and restores the original stack.
* @param new_stack new stack buffer address
* @param new_sp aligned stack pointer address
*/
void esp_core_dump_port_write(uint32_t new_stack, uint32_t new_sp);
#ifdef __cplusplus
}
#endif
@@ -1,64 +0,0 @@
/*
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ESP_CORE_DUMP_PORT_IMPL_H_
#define ESP_CORE_DUMP_PORT_IMPL_H_
/**
* @file
* @brief Core dump port interface implementation for RISC-V.
*/
#include "sdkconfig.h"
#include "esp_core_dump_types.h"
#include "esp_app_format.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Define the type that will be used to describe the current context when
* doing a backup of the current stack. This same structure is used to restore the stack.
*/
typedef struct {
uint32_t sp;
#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD
uint32_t sp_min;
uint32_t sp_max;
#endif // CONFIG_ESP_SYSTEM_HW_STACK_GUARD
} core_dump_stack_context_t;
/**
* @brief Set the stack pointer to the address passed as a parameter.
* @note This function must be inlined.
*
* @param new_sp New stack pointer to set in sp register.
* @param old_ctx CPU context, related to SP, to fill. It will be given back when restoring SP.
*/
FORCE_INLINE_ATTR void esp_core_dump_replace_sp(void* new_sp, core_dump_stack_context_t* old_ctx)
{
asm volatile("mv %0, sp \n\t\
mv sp, %1 \n\t\
"
: "=&r"(old_ctx->sp)
: "r"(new_sp));
}
/**
* @brief Restore the stack pointer that was returned when calling `esp_core_dump_replace_sp()` function.
*
* @param ctx CPU context, related to SP, to restore.
*/
FORCE_INLINE_ATTR void esp_core_dump_restore_sp(core_dump_stack_context_t* old_ctx)
{
asm volatile("mv sp, %0 \n\t" :: "r"(old_ctx->sp));
}
#ifdef __cplusplus
}
#endif
#endif
@@ -1,99 +0,0 @@
/*
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ESP_CORE_DUMP_PORT_IMPL_H_
#define ESP_CORE_DUMP_PORT_IMPL_H_
/**
* @file
* @brief Core dump port interface implementation for Xtensa boards.
*/
#include "esp_core_dump_types.h"
#include "esp_app_format.h"
/**
* Included for SET_STACK macro
*/
#include <xtensa/xtruntime.h>
#include <xt_instr_macros.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Define the type that will be used to describe the current context when
* doing a backup of the current stack. This same structure is used to restore the stack.
*/
typedef struct {
uint32_t sp;
uint32_t a0;
uint32_t ps;
uint32_t windowbase;
uint32_t windowstart;
} core_dump_stack_context_t;
/**
* @brief Set the stack pointer to the address passed as a parameter.
* @note This function must be inlined.
*
* @param new_sp New stack pointer to set in sp register.
* @param old_ctx CPU context, related to SP, to fill. It will be given back when restoring SP.
*/
FORCE_INLINE_ATTR void esp_core_dump_replace_sp(void* new_sp, core_dump_stack_context_t* old_ctx)
{
/* We have to spill all the windows to the stack first as the new stack pointer
* represents a clean new environment. */
xthal_window_spill();
/* Backup the special registers PS, WindowBase and WindowStart. We will need to restore them later */
asm volatile("mov %0, sp \n" \
"mov %1, a0 \n" \
"rsr.ps %2 \n"\
"rsr.windowbase %3 \n"\
"rsr.windowstart %4 \n"\
: "=r"(old_ctx->sp),
"=r"(old_ctx->a0),
"=r"(old_ctx->ps),
"=r"(old_ctx->windowbase),
"=r"(old_ctx->windowstart) :);
/* Set the new stack */
SET_STACK(new_sp);
}
/**
* @brief Restore the stack pointer that was returned when calling `esp_core_dump_replace_sp()` function.
*
* @param ctx CPU context, related to SP, to restore.
*/
FORCE_INLINE_ATTR void esp_core_dump_restore_sp(core_dump_stack_context_t* old_ctx)
{
/* Start by disabling WindowOverflowEnable bit from PS to make sure we won't get a Window Overflow exception
* restoring WindowBase and WindowStart registers */
const uint32_t ps_woe = old_ctx->ps & ~(PS_WOE_MASK);
asm volatile(\
"wsr.ps %0 \n"\
"rsync \n"\
"wsr.windowbase %1 \n"\
"rsync \n"\
"wsr.windowstart %2 \n"\
"rsync \n"\
"mov sp, %3 \n" \
"mov a0, %4 \n" \
"wsr.ps %5 \n"\
"rsync \n"\
:: "r"(ps_woe),
"r"(old_ctx->windowbase),
"r"(old_ctx->windowstart),
"r"(old_ctx->sp),
"r"(old_ctx->a0),
"r"(old_ctx->ps));
}
#ifdef __cplusplus
}
#endif
#endif
+78 -88
View File
@@ -1,8 +1,11 @@
/*
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "sdkconfig.h"
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include "sdkconfig.h"
@@ -12,9 +15,7 @@
#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
#include "esp_cpu.h"
const static char TAG[] __attribute__((unused)) = "esp_core_dump_common";
@@ -55,9 +56,34 @@ extern int _coredump_rtc_fast_start;
extern int _coredump_rtc_fast_end;
#endif
static void* s_exc_frame = NULL;
static uint32_t s_coredump_sp = 0;
/**
* @brief In the menconfig, it is possible to specify a specific stack size for
* core dump generation.
* @brief Configuration validation: If USE_STACK_SIZE is enabled, STACK_SIZE must be > 0
*/
#if CONFIG_ESP_COREDUMP_USE_STACK_SIZE && CONFIG_ESP_COREDUMP_STACK_SIZE == 0
#error "CONFIG_ESP_COREDUMP_STACK_SIZE must not be 0 when CONFIG_ESP_COREDUMP_USE_STACK_SIZE is enabled"
#endif
/**
* @brief Write ELF core dump and log any errors.
*
* This function wraps esp_core_dump_write_elf() and handles error logging.
* Can be called from both C and assembly code.
*
* @return esp_err_t ESP_OK on success, error code otherwise
*/
void esp_core_dump_write_elf_and_check(void)
{
esp_err_t err = esp_core_dump_store();
if (err != ESP_OK) {
ESP_COREDUMP_LOGE("Core dump write failed with error=%d", err);
}
}
/**
* @brief In the menconfig, it is possible to specify a specific stack size for core dump generation.
*/
#if CONFIG_ESP_COREDUMP_STACK_SIZE > 0
@@ -67,7 +93,7 @@ extern int _coredump_rtc_fast_end;
*/
#if LOG_LOCAL_LEVEL >= ESP_LOG_DEBUG
/* Increase stack size in verbose mode */
#define ESP_COREDUMP_STACK_SIZE (CONFIG_ESP_COREDUMP_STACK_SIZE+100)
#define ESP_COREDUMP_STACK_SIZE (CONFIG_ESP_COREDUMP_STACK_SIZE + 100)
#else
#define ESP_COREDUMP_STACK_SIZE CONFIG_ESP_COREDUMP_STACK_SIZE
#endif
@@ -75,43 +101,15 @@ extern int _coredump_rtc_fast_end;
#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)
void esp_core_dump_setup_stack(void)
{
s_core_dump_sp = (uint8_t *)((uint32_t)(s_coredump_stack + ESP_COREDUMP_STACK_SIZE - 1) & ~0xf);
s_coredump_sp = (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, reuse 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(xPortGetCoreID(), &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
/* Stack overflow detection. Watchpoint is set to the end of the core dump stack */
esp_cpu_clear_watchpoint(1);
esp_cpu_set_watchpoint(1, s_coredump_stack, 1, ESP_CPU_WATCHPOINT_STORE);
}
/**
@@ -132,73 +130,60 @@ FORCE_INLINE_ATTR uint32_t esp_core_dump_free_stack_space(const uint8_t *pucStac
}
/**
* @brief Print how many bytes have been used on the stack to create the core
* dump.
* @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)
void esp_core_dump_report_stack_usage(uint32_t new_sp)
{
#if CONFIG_ESP_COREDUMP_LOGS
uint32_t bytes_free = esp_core_dump_free_stack_space(s_coredump_stack);
uint32_t __attribute__((unused)) 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
new_sp - (uint32_t)s_coredump_stack - bytes_free, bytes_free);
}
/* 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
void esp_core_dump_report_backup_stack(uint32_t old_sp)
{
ESP_COREDUMP_LOGI("Backing up stack @ 0x%" PRIx32 " and use core dump stack @ %p",
old_sp, esp_cpu_get_sp());
}
void esp_core_dump_report_restore_stack(uint32_t old_sp)
{
ESP_COREDUMP_LOGI("Restoring stack @ 0x%" PRIx32, old_sp);
}
#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
static uint8_t *s_coredump_stack = NULL;
FORCE_INLINE_ATTR void esp_core_dump_setup_stack(void)
void esp_core_dump_setup_stack(void) { /* No stack setup is needed */ }
/**
* @brief Setup watchpoint for stack overflow detection when no custom stack is configured.
*
* If we are in ISR context, sets up a watchpoint at the end of the ISR stack.
* For tasks, stack overflow detection should be enabled in menuconfig.
* TODO: If not enabled in menuconfig enable it ourselves.
*/
static void esp_core_dump_enable_stack_overflow_detection(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();
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 */
esp_cpu_set_watchpoint(1, topStack + xPortGetCoreID() * configISR_STACK_SIZE, 1, ESP_CPU_WATCHPOINT_STORE);
}
}
FORCE_INLINE_ATTR void esp_core_dump_report_stack_usage(void)
void esp_core_dump_port_write(uint32_t new_stack, uint32_t new_sp)
{
(void)new_stack;
(void)new_sp;
esp_core_dump_enable_stack_overflow_detection();
esp_core_dump_write_elf_and_check();
}
#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 */
@@ -340,8 +325,13 @@ void esp_core_dump_write(panic_info_t *info)
return;
#endif
s_exc_frame = (void *)info->frame;
esp_core_dump_print_write_start();
esp_core_dump_write_internal(info);
esp_core_dump_port_init(info, esp_core_dump_in_isr_context());
esp_core_dump_setup_stack();
esp_core_dump_port_write((uint32_t)s_coredump_stack, s_coredump_sp);
esp_core_dump_print_write_end();
}
@@ -10,7 +10,6 @@
#include "sdkconfig.h"
#include "core_dump_checksum.h"
#include "esp_core_dump_port.h"
#include "esp_core_dump_port_impl.h"
#include "esp_core_dump_common.h"
#include "hal/efuse_hal.h"
#include "esp_task_wdt.h"
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -447,4 +447,89 @@ void esp_core_dump_summary_parse_backtrace_info(esp_core_dump_bt_info_t *bt_info
#endif /* #if CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH && CONFIG_ESP_COREDUMP_DATA_FORMAT_ELF */
#if CONFIG_ESP_COREDUMP_STACK_SIZE > 0
#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD
#include "esp_private/hw_stack_guard.h"
#endif
/**
* @brief Define the type that will be used to describe the current context when
* doing a backup of the current stack. This same structure is used to restore the stack.
*/
typedef struct {
uint32_t sp;
#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD
uint32_t sp_min;
uint32_t sp_max;
#endif // CONFIG_ESP_SYSTEM_HW_STACK_GUARD
} core_dump_stack_context_t;
static core_dump_stack_context_t s_stack_context;
/* Helper functions defined in core_dump_common.c */
void esp_core_dump_report_backup_stack(uint32_t old_sp);
void esp_core_dump_report_restore_stack(uint32_t old_sp);
void esp_core_dump_write_elf_and_check(void);
void esp_core_dump_report_stack_usage(uint32_t new_sp);
/**
* @brief Function setting up the core dump stack.
*
* @note This function **must** be inlined as it modifies the stack pointer.
*/
FORCE_INLINE_ATTR void esp_core_dump_replace_stack(uint32_t new_stack, uint32_t new_sp)
{
#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD
/* Save the current area we are watching to restore it later */
esp_hw_stack_guard_get_bounds(xPortGetCoreID(), &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
/* Save the current stack pointer and replace it with the core dump stack pointer */
asm volatile("mv %0, sp \n\t\
mv sp, %1 \n\t\
"
: "=&r"(s_stack_context.sp)
: "r"(new_sp));
esp_core_dump_report_backup_stack(s_stack_context.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(new_stack, new_sp);
esp_hw_stack_guard_monitor_start();
#endif
}
FORCE_INLINE_ATTR void esp_core_dump_restore_stack(void)
{
/* Restore the stack pointer. */
esp_core_dump_report_restore_stack(s_stack_context.sp);
#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD
esp_hw_stack_guard_monitor_stop();
#endif
/* Restore the stack pointer to the original value */
asm volatile("mv sp, %0 \n\t" :: "r"(s_stack_context.sp));
#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
}
void esp_core_dump_port_write(uint32_t new_stack, uint32_t new_sp)
{
esp_core_dump_replace_stack(new_stack, new_sp);
esp_core_dump_write_elf_and_check();
esp_core_dump_report_stack_usage(new_sp);
esp_core_dump_restore_stack();
}
#endif // CONFIG_ESP_COREDUMP_STACK_SIZE > 0
#endif
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -565,4 +565,16 @@ void esp_core_dump_summary_parse_backtrace_info(esp_core_dump_bt_info_t *bt_info
#endif /* #if CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH && CONFIG_ESP_COREDUMP_DATA_FORMAT_ELF */
#endif
#if CONFIG_ESP_COREDUMP_STACK_SIZE > 0
/* Helper function defined in core_dump_stack_switch.S */
void esp_core_dump_port_write_on_new_stack(uint32_t new_sp);
void esp_core_dump_port_write(uint32_t new_stack, uint32_t new_sp)
{
(void)new_stack;
esp_core_dump_port_write_on_new_stack(new_sp);
}
#endif // CONFIG_ESP_COREDUMP_STACK_SIZE > 0
#endif // CONFIG_ESP_COREDUMP_ENABLE
@@ -0,0 +1,170 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
* @brief Assembly implementation for switching to a new stack during coredump.
*
* This implementation properly handles window spilling without lowering the
* interrupt level, making it safe to use from high-priority interrupt contexts
* (e.g., interrupt watchdog at level 4).
*/
#include <xtensa/coreasm.h>
#include <xtensa/corebits.h>
#include <xtensa/xtruntime.h>
#include "xt_asm_utils.h"
#include "xt_instr_macros.h" /* For SET_STACK macro */
#include "sdkconfig.h"
.extern esp_core_dump_write_elf_and_check
.extern esp_core_dump_report_stack_usage
.extern esp_core_dump_report_backup_stack
.extern esp_core_dump_report_restore_stack
/**
* @brief Stack context structure layout (offsets in bytes)
*
* This structure defines the layout of s_stack_context used for saving/restoring
* the original stack context when switching to the coredump stack.
*/
.equ STACK_CTX_SP, 0
.equ STACK_CTX_A0, 4
.equ STACK_CTX_PS, 8
.equ STACK_CTX_WINDOWBASE, 12
.equ STACK_CTX_WINDOWSTART, 16
.equ STACK_CTX_SIZE, 20
.section .bss
.align 4
.global s_stack_context
.type s_stack_context,@object
s_stack_context:
.space STACK_CTX_SIZE
.size s_stack_context, STACK_CTX_SIZE
.text
/**
* @brief Execute core dump write on a new stack
*
* This function spills register windows and switches to new stack.
* Calls C functions and restores original stack and context.
*/
#if CONFIG_ESP_COREDUMP_STACK_SIZE > 0
.global esp_core_dump_port_write_on_new_stack
.type esp_core_dump_port_write_on_new_stack,@function
.align 4
esp_core_dump_port_write_on_new_stack:
#ifndef __XTENSA_CALL0_ABI__
entry a1, 32
/* Save necessary parameters on stack before window spilling */
s32i a0, a1, 0 /* Save return address */
s32i a2, a1, 4 /* Save coredump_sp parameter */
/* Prepare for window spilling:
* - Clear PS.EXCM and set PS.WOE to enable window overflow exceptions
* - Raise INTLEVEL to at least XCHAL_EXCM_LEVEL (to maintain interrupt masking)
* - Save/restore EPC1 (clobbered by window overflow exceptions)
* - Only use a0-a3 here (a4-a15 may hold live data from previous windows)
*/
rsr a2, PS /* Save PS to be restored after SPILL_ALL_WINDOWS */
movi a0, PS_INTLEVEL_MASK
and a3, a2, a0 /* Extract current INTLEVEL */
bgeui a3, XCHAL_EXCM_LEVEL, 1f /* Ensure INTLEVEL >= XCHAL_EXCM_LEVEL */
movi a3, XCHAL_EXCM_LEVEL
1:
movi a0, PS_UM | PS_WOE /* Clear EXCM, enable WOE */
or a3, a3, a0
wsr a3, PS
rsr a0, EPC1 /* Save EPC1 to be restored after SPILL_ALL_WINDOWS */
SPILL_ALL_WINDOWS
/* Restore PS and EPC1 */
wsr a2, PS
rsync
wsr a0, EPC1
/* Restore return address and save context to s_stack_context */
mov a6, a1 /* a6 = SP */
l32i a7, a1, 0 /* a7 = return address (from stack) */
l32i a12, a1, 4 /* a12 = coredump_sp (from stack) */
rsr a8, PS
rsr a9, WINDOWBASE
rsr a10, WINDOWSTART
movi a11, s_stack_context
s32i a6, a11, STACK_CTX_SP /* Save SP */
s32i a7, a11, STACK_CTX_A0 /* Save A0 */
s32i a8, a11, STACK_CTX_PS /* Save PS */
s32i a9, a11, STACK_CTX_WINDOWBASE /* Save WindowBase */
s32i a10, a11, STACK_CTX_WINDOWSTART /* Save WindowStart */
/* Switch to coredump stack (use saved coredump_sp from stack) */
mov a6, a12 /* a6 = coredump_sp */
SET_STACK a6, a7, a8
/* Report backup stack */
l32i a10, a11, STACK_CTX_SP /* a10 = old SP */
movi a14, esp_core_dump_report_backup_stack
callx8 a14
/* Write ELF core dump */
movi a14, esp_core_dump_write_elf_and_check
callx8 a14
/* Report stack usage */
mov a10, a12 /* a10 = coredump_sp */
movi a14, esp_core_dump_report_stack_usage
callx8 a14
/* Report restore stack */
l32i a10, a11, STACK_CTX_SP /* a10 = old SP */
movi a14, esp_core_dump_report_restore_stack
callx8 a14
/* Load original context from s_stack_context */
movi a11, s_stack_context
l32i a2, a11, STACK_CTX_SP /* Load SP */
l32i a3, a11, STACK_CTX_A0 /* Load A0 */
l32i a4, a11, STACK_CTX_PS /* Load PS */
l32i a5, a11, STACK_CTX_WINDOWBASE /* Load WindowBase */
l32i a6, a11, STACK_CTX_WINDOWSTART /* Load WindowStart */
/* Write PS with WOE disabled */
movi a8, ~PS_WOE_MASK
and a7, a4, a8
wsr a7, PS
rsync
/* Restore WindowBase */
wsr a5, WINDOWBASE
rsync
/* Restore WindowStart */
wsr a6, WINDOWSTART
rsync
/* Restore SP */
mov a1, a2
/* Restore A0 */
mov a0, a3
/* Restore PS (final with original WOE setting) */
wsr a4, PS
rsync
retw
#else
#error "this code is written for Window ABI"
#endif
#endif // CONFIG_ESP_COREDUMP_STACK_SIZE > 0
+5 -5
View File
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2020-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -53,10 +53,10 @@
*/
#ifdef __ASSEMBLER__
.macro SET_STACK new_sp tmp1 tmp2
addi tmp1, new_sp, -SAVE_AREA_OFFSET
addi tmp2, tmp1, -BASE_AREA_SP_OFFSET
s32i new_sp, tmp2, 0
addi new_sp, tmp1, 0
addi \tmp1, \new_sp, -SAVE_AREA_OFFSET
addi \tmp2, \tmp1, -BASE_AREA_SP_OFFSET
s32i \new_sp, \tmp2, 0
addi \new_sp, \tmp1, 0
rsr.ps \tmp1
movi \tmp2, ~(PS_WOE_MASK | PS_OWB_MASK | PS_CALLINC_MASK)
and \tmp1, \tmp1, \tmp2
@@ -37,8 +37,10 @@
#define STRUCT_FIELD(ctype,size,pre,name) .set pre##name, XT_STRUCT_OFFSET; .set XT_STRUCT_OFFSET, pre##name + size
#define STRUCT_AFIELD(ctype,size,pre,name,n) .set pre##name, XT_STRUCT_OFFSET;\
.set XT_STRUCT_OFFSET, pre##name + (size)*(n);
#define STRUCT_AFIELD_A(ctype,size,align,pre,name,n) .set pre##name, XT_STRUCT_OFFSET\
.ifgt (align-1); .set pre##name, XT_STRUCT_OFFSET + (align - (XT_STRUCT_OFFSET & (align-1))); .endif\
#define STRUCT_AFIELD_A(ctype,size,align,pre,name,n) .set pre##name, XT_STRUCT_OFFSET; \
.ifgt (align-1); \
.set pre##name, XT_STRUCT_OFFSET + (align - (XT_STRUCT_OFFSET & (align-1))); \
.endif; \
.set XT_STRUCT_OFFSET, pre##name + (size)*(n);
#define STRUCT_END(sname) .set sname##Size, XT_STRUCT_OFFSET;
#else /* __clang__ */
@@ -67,7 +69,7 @@
* stack frame is limited to 128 bytes (currently at 64).
*/
STRUCT_BEGIN
STRUCT_FIELD (long,4,KEXC_,pc) /* "parm" */
STRUCT_FIELD (long,4,KEXC_,pc) /* "param" */
STRUCT_FIELD (long,4,KEXC_,ps)
STRUCT_AFIELD(long,4,KEXC_,areg, 4) /* a12 .. a15 */
STRUCT_FIELD (long,4,KEXC_,sar) /* "save" */
+2 -2
View File
@@ -39,12 +39,12 @@ CONFIGS = list(
'coredump_flash_elf_soft_sha',
'coredump_uart_bin_crc',
'coredump_uart_elf_crc',
'coredump_flash_custom_stack',
'gdbstub',
'panic',
],
TARGETS_ALL,
),
itertools.product(['coredump_flash_custom_stack'], TARGETS_RISCV),
)
)
)