diff --git a/components/esp_system/CMakeLists.txt b/components/esp_system/CMakeLists.txt index 7973182871..c63a27df0b 100644 --- a/components/esp_system/CMakeLists.txt +++ b/components/esp_system/CMakeLists.txt @@ -53,6 +53,10 @@ else() list(APPEND srcs "systick_etm.c") endif() + if(CONFIG_ESP_SYSTEM_HW_STACK_GUARD) + list(APPEND srcs "hw_stack_guard.c") + endif() + idf_component_register(SRCS "${srcs}" INCLUDE_DIRS include PRIV_REQUIRES spi_flash esp_timer esp_mm diff --git a/components/esp_system/Kconfig b/components/esp_system/Kconfig index 3006f40b60..7f9f0a2727 100644 --- a/components/esp_system/Kconfig +++ b/components/esp_system/Kconfig @@ -561,6 +561,14 @@ menu "ESP System Settings" (2). For special workflow, the chip needs do more things instead of restarting directly. This part needs to be done in callback function of interrupt. + config ESP_SYSTEM_HW_STACK_GUARD + bool "Hardware stack guard" + depends on SOC_ASSIST_DEBUG_SUPPORTED + default y + help + This config allows to trigger a panic interrupt when Stack Pointer register goes out of allocated stack + memory bounds. + endmenu # ESP System Settings menu "IPC (Inter-Processor Call)" diff --git a/components/esp_system/hw_stack_guard.c b/components/esp_system/hw_stack_guard.c new file mode 100644 index 0000000000..1f5eee429c --- /dev/null +++ b/components/esp_system/hw_stack_guard.c @@ -0,0 +1,95 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "hal/assist_debug_hal.h" +#include "esp_private/hw_stack_guard.h" +#include "esp_private/periph_ctrl.h" +#include "esp_private/startup_internal.h" +#include "soc/soc_caps.h" +#include "esp_rom_sys.h" +#include "esp_cpu.h" + +ESP_SYSTEM_INIT_FN(esp_hw_stack_guard_init, ESP_SYSTEM_INIT_ALL_CORES, 101) +{ + uint32_t core_id = esp_cpu_get_core_id(); + + if (core_id == 0) { + /* initialize the peripheral only when running on core 0 */ + periph_module_enable(PERIPH_ASSIST_DEBUG_MODULE); + periph_module_reset(PERIPH_ASSIST_DEBUG_MODULE); + } + + /* just in case, disable the interrupt and clear pending status */ + assist_debug_hal_sp_int_disable(core_id); + assist_debug_hal_sp_int_clear(core_id); + + /* + * enable interrupt + * Note: to control hw_stack_guard use monitor enable/disable because in case: + * - monitor == active + * - interrupt != active + * - trigger event happened + * - you get an interrupt right after enabling interrupts + * So, use monitor to disable hw_guard to avoid false-positives. + * And keep interrupt always enabled for better performace (don't spend cpu time for enable/disable) + */ + assist_debug_hal_sp_int_enable(core_id); + + /* enable interrup routine */ + esp_rom_route_intr_matrix(core_id, ETS_ASSIST_DEBUG_INTR_SOURCE, ETS_ASSIST_DEBUG_INUM); + + esprv_intc_int_set_type(ETS_ASSIST_DEBUG_INUM, INTR_TYPE_LEVEL); + esprv_intc_int_set_priority(ETS_ASSIST_DEBUG_INUM, SOC_INTERRUPT_LEVEL_MEDIUM); + + ESP_INTR_ENABLE(ETS_ASSIST_DEBUG_INUM); + return ESP_OK; +} + + +/* The functions below are designed to be used in interrupt/panic handler + * In case using them in user's code put them into critical section */ + +void esp_hw_stack_guard_monitor_start(void) +{ + uint32_t core_id = esp_cpu_get_core_id(); + + /* enable monitor. Interrupt is always enabled (see comment in esp_hw_stack_guard_init()) */ + assist_debug_hal_sp_mon_enable(core_id); +} + +void esp_hw_stack_guard_monitor_stop(void) +{ + uint32_t core_id = esp_cpu_get_core_id(); + /* disable monitor. Interrupt is always enabled (see comment in esp_hw_stack_guard_init()) */ + assist_debug_hal_sp_mon_disable(core_id); +} + +void esp_hw_stack_guard_set_bounds(uint32_t sp_min, uint32_t sp_max) +{ + uint32_t core_id = esp_cpu_get_core_id(); + + assist_debug_hal_set_sp_bounds(core_id, sp_min, sp_max); +} + +void esp_hw_stack_guard_get_bounds(uint32_t *sp_min, uint32_t *sp_max) +{ + uint32_t core_id = esp_cpu_get_core_id(); + + assist_debug_hal_get_sp_bounds(core_id, sp_min, sp_max); +} + +bool esp_hw_stack_guard_is_fired(void) +{ + uint32_t core_id = esp_cpu_get_core_id(); + + return assist_debug_hal_is_sp_ovf_fired(core_id); +} + +uint32_t esp_hw_stack_guard_get_pc(void) +{ + uint32_t core_id = esp_cpu_get_core_id(); + + return assist_debug_hal_get_sp_ovf_pc(core_id); +} diff --git a/components/esp_system/linker.lf b/components/esp_system/linker.lf index e2fb09cc78..54be6b2783 100644 --- a/components/esp_system/linker.lf +++ b/components/esp_system/linker.lf @@ -7,6 +7,10 @@ entries: panic_arch (noflash) cache_err_int:esp_cache_err_get_cpuid (noflash) reset_reason:esp_reset_reason_get_hint (noflash) + if ESP_SYSTEM_HW_STACK_GUARD = y: + hw_stack_guard:esp_hw_stack_guard_get_bounds (noflash) + hw_stack_guard:esp_hw_stack_guard_is_fired (noflash) + hw_stack_guard:esp_hw_stack_guard_get_pc (noflash) esp_err (noflash) esp_system_chip:esp_system_abort (noflash) diff --git a/components/esp_system/port/arch/riscv/expression_with_stack.c b/components/esp_system/port/arch/riscv/expression_with_stack.c index a47d3f1193..94190bb614 100644 --- a/components/esp_system/port/arch/riscv/expression_with_stack.c +++ b/components/esp_system/port/arch/riscv/expression_with_stack.c @@ -9,35 +9,50 @@ #include #include "freertos/FreeRTOS.h" #include "freertos/portmacro.h" +#include "sdkconfig.h" +#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD +#include "esp_private/hw_stack_guard.h" +#endif -static portMUX_TYPE shared_stack_spinlock = portMUX_INITIALIZER_UNLOCKED; -static void *current_task_stack = NULL; - -extern void esp_shared_stack_invoke_function(shared_stack_function function, void *stack); - -static StackType_t *esp_switch_stack_setup(StackType_t *stack, size_t stack_size) +static StackType_t *esp_shared_stack_setup_context(StaticTask_t *tcb, void **sp_min, void **sp_max, StackType_t *stack, size_t stack_size) { //We need also to tweak current task stackpointer to avoid erroneous //stack overflow indication, so fills the stack with freertos known pattern: memset(stack, 0xa5U, stack_size * sizeof(StackType_t)); - StaticTask_t *current = (StaticTask_t *)xTaskGetCurrentTaskHandle(); - //Then put the fake stack inside of TCB: - current_task_stack = current->pxDummy6; - current->pxDummy6 = (void *)stack; - - StackType_t *top_of_stack = stack + stack_size; - - //Align stack to a 16byte boundary, as required by CPU specific: - top_of_stack = (StackType_t *)(((UBaseType_t)(top_of_stack - 16) & ~0xf)); + //Align stack to a 16-byte boundary, as required by CPU specific: + StackType_t *top_of_stack = (StackType_t *) ALIGNUP(0x10, (uint32_t) (stack + stack_size)); StackType_t *adjusted_top_of_stack = top_of_stack - RV_STK_FRMSZ; + //Then put the fake stack inside of TCB: + *sp_min = tcb->pxDummy6; + tcb->pxDummy6 = (void *)stack; + + *sp_max = tcb->pxDummy8; + tcb->pxDummy8 = adjusted_top_of_stack; + #if CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK vPortSetStackWatchpoint(stack); +#endif +#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD + esp_hw_stack_guard_monitor_stop(); + esp_hw_stack_guard_set_bounds((uint32_t) tcb->pxDummy6, (uint32_t) tcb->pxDummy8); #endif return ((StackType_t *)adjusted_top_of_stack); } +static void esp_shared_stack_restore_context(StaticTask_t *tcb, void *sp_min, void *sp_max) { + tcb->pxDummy6 = sp_min; + tcb->pxDummy8 = sp_max; + +#if CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK + vPortSetStackWatchpoint(sp_min); +#endif +#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD + esp_hw_stack_guard_set_bounds((uint32_t) sp_min, (uint32_t) sp_max); + esp_hw_stack_guard_monitor_start(); +#endif +} void esp_execute_shared_stack_function(SemaphoreHandle_t lock, void *stack, size_t stack_size, shared_stack_function function) { @@ -46,21 +61,33 @@ void esp_execute_shared_stack_function(SemaphoreHandle_t lock, void *stack, size assert(stack_size > 0 && stack_size >= CONFIG_ESP_MINIMAL_SHARED_STACK_SIZE); assert(function); + static portMUX_TYPE shared_stack_spinlock = portMUX_INITIALIZER_UNLOCKED; + StaticTask_t *tcb = (StaticTask_t *)xTaskGetCurrentTaskHandle(); + void *sp_min = NULL; + void *sp_max = (void *) UINTPTR_MAX; + xSemaphoreTake(lock, portMAX_DELAY); + portENTER_CRITICAL(&shared_stack_spinlock); - stack = esp_switch_stack_setup(stack, stack_size); + stack = esp_shared_stack_setup_context(tcb, &sp_min, &sp_max, stack, stack_size); + __asm__ volatile ("mv t0, sp \n" /* save current SP */ + "mv sp, %0 \n" /* set shared stack as new SP */ + "addi sp, sp, -16 \n" /* allocate memory for previous SP */ + "sw t0, 0(sp) \n" /* store previous SP in a safe place */ + :: "r"(stack)); +#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD + esp_hw_stack_guard_monitor_start(); +#endif portEXIT_CRITICAL(&shared_stack_spinlock); - esp_shared_stack_invoke_function(function, stack); + function(); portENTER_CRITICAL(&shared_stack_spinlock); - StaticTask_t *current = (StaticTask_t *)xTaskGetCurrentTaskHandle(); - - //Restore current task stack: - current->pxDummy6 = (StackType_t *)current_task_stack; -#if CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK - vPortSetStackWatchpoint(current->pxDummy6); +#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD + esp_hw_stack_guard_monitor_stop(); #endif + __asm__ volatile ("lw sp, 0(sp)"); /* restore real SP of current task */ + esp_shared_stack_restore_context(tcb, sp_min, sp_max); portEXIT_CRITICAL(&shared_stack_spinlock); xSemaphoreGive(lock); diff --git a/components/esp_system/port/arch/riscv/expression_with_stack_asm.S b/components/esp_system/port/arch/riscv/expression_with_stack_asm.S deleted file mode 100644 index b8a56a59ff..0000000000 --- a/components/esp_system/port/arch/riscv/expression_with_stack_asm.S +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2015-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. - - .section .text - - .global esp_shared_stack_invoke_function - .type esp_shared_stack_invoke_function, @function -esp_shared_stack_invoke_function: - /* save current stack and return address */ - mv t0, sp - mv t1, ra - - /* Set shared stack as new stack pointer */ - mv sp, a1 - - /* store the ra and previous stack pointer in a safe place - stack pointer for riscv should always be 16 byte aligned */ - addi sp,sp,-16 - sw t0, 0(sp) - sw t1, 4(sp) - - /* call the subroutine */ - jalr a0, 0 - - /* gets the ra and stack pointer saved previously */ - lw t0, 0(sp) - lw t1, 4(sp) - addi sp, sp, 16 - - /* restore both ra and real stack pointer of current task */ - mv ra, t1 - mv sp, t0 - ret diff --git a/components/esp_system/port/arch/riscv/panic_arch.c b/components/esp_system/port/arch/riscv/panic_arch.c index f225cd6e11..f1ddd51030 100644 --- a/components/esp_system/port/arch/riscv/panic_arch.c +++ b/components/esp_system/port/arch/riscv/panic_arch.c @@ -25,6 +25,13 @@ #include "esp_private/cache_utils.h" #endif +#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_cpu.h" +#include "esp_private/hw_stack_guard.h" +#endif + #define DIM(array) (sizeof(array)/sizeof(*array)) @@ -149,6 +156,34 @@ static inline void print_cache_err_details(const void *frame) #endif } +#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD +static inline void print_assist_debug_details(const void *frame) +{ + uint32_t core_id = esp_cpu_get_core_id(); + uint32_t sp_min, sp_max; + const char *task_name = pcTaskGetName(xTaskGetCurrentTaskHandleForCPU(core_id)); + esp_hw_stack_guard_get_bounds(&sp_min, &sp_max); + + panic_print_str("\r\n"); + if (!esp_hw_stack_guard_is_fired()) { + panic_print_str("ASSIST_DEBUG is not triggered BUT interrupt occured!\r\n\r\n"); + } + + panic_print_str("Detected in task \""); + panic_print_str(task_name); + panic_print_str("\" at 0x"); + panic_print_hex((int) esp_hw_stack_guard_get_pc()); + panic_print_str("\r\n"); + panic_print_str("Stack pointer: 0x"); + panic_print_hex((int) ((RvExcFrame *)frame)->sp); + panic_print_str("\r\n"); + panic_print_str("Stack bounds: 0x"); + panic_print_hex((int) sp_min); + panic_print_str(" - 0x"); + panic_print_hex((int) sp_max); + panic_print_str("\r\n\r\n"); +} +#endif // CONFIG_ESP_SYSTEM_HW_STACK_GUARD /** * Function called when a memory protection error occurs (PMS). It prints details such as the @@ -257,20 +292,7 @@ void panic_soc_fill_info(void *f, panic_info_t *info) { RvExcFrame *frame = (RvExcFrame *) f; - /* Please keep in sync with PANIC_RSN_* defines */ - static const char *pseudo_reason[PANIC_RSN_COUNT] = { - "Unknown reason", - "Interrupt wdt timeout on CPU0", -#if SOC_CPU_NUM > 1 - "Interrupt wdt timeout on CPU1", -#endif - "Cache error", -#if CONFIG_ESP_SYSTEM_MEMPROT_FEATURE - "Memory protection fault", -#endif - }; - - info->reason = pseudo_reason[0]; + info->reason = "Unknown reason"; info->addr = (void *) frame->mepc; /* The mcause has been set by the CPU when the panic occured. @@ -283,7 +305,7 @@ void panic_soc_fill_info(void *f, panic_info_t *info) * about why the error happened. */ info->core = esp_cache_err_get_cpuid(); - info->reason = pseudo_reason[PANIC_RSN_CACHEERR]; + info->reason = "Cache error"; info->details = print_cache_err_details; } else if (frame->mcause == ETS_INT_WDT_INUM) { @@ -295,14 +317,24 @@ void panic_soc_fill_info(void *f, panic_info_t *info) info->exception = PANIC_EXCEPTION_IWDT; #if SOC_CPU_NUM > 1 +#error "TODO: define PANIC_RSN_INTWDT_CPU1 in panic_reason.h" _Static_assert(PANIC_RSN_INTWDT_CPU0 + 1 == PANIC_RSN_INTWDT_CPU1, "PANIC_RSN_INTWDT_CPU1 must be equal to PANIC_RSN_INTWDT_CPU0 + 1"); + info->reason = core == 0 ? "Interrupt wdt timeout on CPU0" : "Interrupt wdt timeout on CPU1"; +#else + info->reason = "Interrupt wdt timeout on CPU0"; #endif - info->reason = pseudo_reason[PANIC_RSN_INTWDT_CPU0 + core]; } +#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD + else if (frame->mcause == ETS_ASSIST_DEBUG_INUM) { + info->core = esp_cache_err_get_cpuid(); + info->reason = "Stack protection fault"; + info->details = print_assist_debug_details; + } +#endif #if CONFIG_ESP_SYSTEM_MEMPROT_FEATURE else if (frame->mcause == ETS_MEMPROT_ERR_INUM) { - info->reason = pseudo_reason[PANIC_RSN_MEMPROT]; + info->reason = "Memory protection fault"; info->details = print_memprot_err_details; info->core = esp_mprot_get_active_intr(&s_memp_intr) == ESP_OK ? s_memp_intr.core : -1; } @@ -315,7 +347,6 @@ void panic_arch_fill_info(void *frame, panic_info_t *info) info->core = 0; info->exception = PANIC_EXCEPTION_FAULT; - //Please keep in sync with PANIC_RSN_* defines static const char *reason[] = { "Instruction address misaligned", "Instruction access fault", diff --git a/components/esp_system/port/include/private/esp_private/hw_stack_guard.h b/components/esp_system/port/include/private/esp_private/hw_stack_guard.h new file mode 100644 index 0000000000..c2284cbc3a --- /dev/null +++ b/components/esp_system/port/include/private/esp_private/hw_stack_guard.h @@ -0,0 +1,58 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#ifndef __ASSEMBLER__ +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* The functions below are designed to be used in interrupt/panic handler + * In case using them in user's code put them into critical section.*/ + +void esp_hw_stack_guard_monitor_start(void); +void esp_hw_stack_guard_monitor_stop(void); +void esp_hw_stack_guard_set_bounds(uint32_t sp_min, uint32_t sp_max); +void esp_hw_stack_guard_get_bounds(uint32_t *sp_min, uint32_t *sp_max); +bool esp_hw_stack_guard_is_fired(void); +uint32_t esp_hw_stack_guard_get_pc(void); + +#ifdef __cplusplus +}; +#endif + +#else // __ASSEMBLER__ + +#include "hal/assist_debug_ll.h" + +#define ASSIST_DEBUG_CORE_0_INTR_ENA_REG_IMM (ASSIST_DEBUG_CORE_0_INTR_ENA_REG >> 12) +#define ASSIST_DEBUG_CORE_0_SP_MIN_OFFSET (ASSIST_DEBUG_CORE_0_SP_MIN_REG - ASSIST_DEBUG_CORE_0_INTR_ENA_REG) +#define ASSIST_DEBUG_CORE_0_SP_MAX_OFFSET (ASSIST_DEBUG_CORE_0_SP_MAX_REG - ASSIST_DEBUG_CORE_0_INTR_ENA_REG) + +.macro ESP_HW_STACK_GUARD_SET_BOUNDS_CPU0 + lui t0, ASSIST_DEBUG_CORE_0_INTR_ENA_REG_IMM + sw a0, ASSIST_DEBUG_CORE_0_SP_MIN_OFFSET(t0) + sw a1, ASSIST_DEBUG_CORE_0_SP_MAX_OFFSET(t0) +.endm + +.macro ESP_HW_STACK_GUARD_MONITOR_STOP_CPU0 + lui t0, ASSIST_DEBUG_CORE_0_INTR_ENA_REG_IMM + lw t1, 0(t0) + andi t1, t1, ~ASSIST_DEBUG_SP_SPILL_BITS + sw t1, 0(t0) +.endm + +.macro ESP_HW_STACK_GUARD_MONITOR_START_CPU0 + lui t0, ASSIST_DEBUG_CORE_0_INTR_ENA_REG_IMM + lw t1, 0(t0) + ori t1, t1, ASSIST_DEBUG_SP_SPILL_BITS + sw t1, 0(t0) +.endm + +#endif // __ASSEMBLER__ diff --git a/components/esp_system/port/soc/esp32c2/CMakeLists.txt b/components/esp_system/port/soc/esp32c2/CMakeLists.txt index d77109f3b5..09b915cd7e 100644 --- a/components/esp_system/port/soc/esp32c2/CMakeLists.txt +++ b/components/esp_system/port/soc/esp32c2/CMakeLists.txt @@ -3,7 +3,6 @@ set(srcs "clk.c" "system_internal.c" "cache_err_int.c" "../../arch/riscv/expression_with_stack.c" - "../../arch/riscv/expression_with_stack_asm.S" "../../arch/riscv/panic_arch.c" "../../arch/riscv/debug_stubs.c") diff --git a/components/esp_system/port/soc/esp32c3/CMakeLists.txt b/components/esp_system/port/soc/esp32c3/CMakeLists.txt index a8034a3b9d..69579319fb 100644 --- a/components/esp_system/port/soc/esp32c3/CMakeLists.txt +++ b/components/esp_system/port/soc/esp32c3/CMakeLists.txt @@ -4,7 +4,6 @@ set(srcs "clk.c" "cache_err_int.c" "apb_backup_dma.c" "../../arch/riscv/expression_with_stack.c" - "../../arch/riscv/expression_with_stack_asm.S" "../../arch/riscv/panic_arch.c" "../../arch/riscv/debug_stubs.c") diff --git a/components/esp_system/port/soc/esp32c6/CMakeLists.txt b/components/esp_system/port/soc/esp32c6/CMakeLists.txt index d77109f3b5..09b915cd7e 100644 --- a/components/esp_system/port/soc/esp32c6/CMakeLists.txt +++ b/components/esp_system/port/soc/esp32c6/CMakeLists.txt @@ -3,7 +3,6 @@ set(srcs "clk.c" "system_internal.c" "cache_err_int.c" "../../arch/riscv/expression_with_stack.c" - "../../arch/riscv/expression_with_stack_asm.S" "../../arch/riscv/panic_arch.c" "../../arch/riscv/debug_stubs.c") diff --git a/components/esp_system/port/soc/esp32h2/CMakeLists.txt b/components/esp_system/port/soc/esp32h2/CMakeLists.txt index 75863a3fd7..890af3fd42 100644 --- a/components/esp_system/port/soc/esp32h2/CMakeLists.txt +++ b/components/esp_system/port/soc/esp32h2/CMakeLists.txt @@ -4,7 +4,6 @@ set(srcs "system_internal.c" "cache_err_int.c" "../../arch/riscv/expression_with_stack.c" - "../../arch/riscv/expression_with_stack_asm.S" "../../arch/riscv/panic_arch.c" "../../arch/riscv/debug_stubs.c") diff --git a/components/esp_system/system_init_fn.txt b/components/esp_system/system_init_fn.txt index 0818eedf84..b336c1c599 100644 --- a/components/esp_system/system_init_fn.txt +++ b/components/esp_system/system_init_fn.txt @@ -16,6 +16,9 @@ # esp_timer has to be initialized early, since it is used by several other components 100: esp_timer_startup_init in components/esp_timer/src/esp_timer.c on CONFIG_ESP_TIMER_ISR_AFFINITY +# HW stack guard via assist-debug module. +101: esp_hw_stack_guard_init in components/esp_system/hw_stack_guard.c on ESP_SYSTEM_INIT_ALL_CORES + # esp_sleep doesn't have init dependencies 105: esp_sleep_startup_init in components/esp_hw_support/sleep_gpio.c on BIT(0) 106: sleep_clock_startup_init in components/esp_hw_support/sleep_clock.c on BIT(0) diff --git a/components/freertos/FreeRTOS-Kernel-SMP/portable/riscv/include/freertos/portmacro.h b/components/freertos/FreeRTOS-Kernel-SMP/portable/riscv/include/freertos/portmacro.h index 0c9074c510..acae7f1b18 100644 --- a/components/freertos/FreeRTOS-Kernel-SMP/portable/riscv/include/freertos/portmacro.h +++ b/components/freertos/FreeRTOS-Kernel-SMP/portable/riscv/include/freertos/portmacro.h @@ -7,6 +7,17 @@ #pragma once #include "sdkconfig.h" + +/* Macros used instead ofsetoff() for better performance of interrupt handler */ +#define PORT_OFFSET_PX_STACK 0x30 +#define PORT_OFFSET_PX_END_OF_STACK (PORT_OFFSET_PX_STACK + \ + /* void * pxDummy6 */ 4 + \ + /* BaseType_t xDummy23[ 2 ] */ 8 + \ + /* uint8_t ucDummy7[ configMAX_TASK_NAME_LEN ] */ CONFIG_FREERTOS_MAX_TASK_NAME_LEN + \ + /* BaseType_t xDummy24 */ 4) + +#ifndef __ASSEMBLER__ + #include #include "spinlock.h" #include "soc/interrupt_reg.h" @@ -355,3 +366,5 @@ portmacro.h. Therefore, we need to keep these headers around for now to allow th #ifdef __cplusplus } #endif + +#endif // __ASSEMBLER__ diff --git a/components/freertos/FreeRTOS-Kernel-SMP/portable/riscv/port.c b/components/freertos/FreeRTOS-Kernel-SMP/portable/riscv/port.c index 8f43a25749..055864c078 100644 --- a/components/freertos/FreeRTOS-Kernel-SMP/portable/riscv/port.c +++ b/components/freertos/FreeRTOS-Kernel-SMP/portable/riscv/port.c @@ -41,6 +41,17 @@ #endif //CONFIG_PM_TRACE _Static_assert(portBYTE_ALIGNMENT == 16, "portBYTE_ALIGNMENT must be set to 16"); +#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD +/** + * offsetof() can not be used in asm code. Then we need make sure that + * PORT_OFFSET_PX_STACK and PORT_OFFSET_PX_END_OF_STACK have expected values. + * Macro used in the portasm.S instead of variables to save at least 4 instruction calls + * which accessing DRAM memory. This optimization saves CPU time in the interrupt handling. + */ + +_Static_assert(offsetof( StaticTask_t, pxDummy6 ) == PORT_OFFSET_PX_STACK); +_Static_assert(offsetof( StaticTask_t, pxDummy8 ) == PORT_OFFSET_PX_END_OF_STACK); +#endif // CONFIG_ESP_SYSTEM_HW_STACK_GUARD /* ---------------------------------------------------- Variables ------------------------------------------------------ * @@ -51,7 +62,7 @@ volatile UBaseType_t uxInterruptNesting = 0; portMUX_TYPE port_xTaskLock = portMUX_INITIALIZER_UNLOCKED; portMUX_TYPE port_xISRLock = portMUX_INITIALIZER_UNLOCKED; volatile BaseType_t xPortSwitchFlag = 0; -__attribute__((aligned(16))) static StackType_t xIsrStack[configISR_STACK_SIZE]; +__attribute__((aligned(16))) StackType_t xIsrStack[configISR_STACK_SIZE]; StackType_t *xIsrStackTop = &xIsrStack[0] + (configISR_STACK_SIZE & (~((portPOINTER_SIZE_TYPE)portBYTE_ALIGNMENT_MASK))); // Variables used for IDF style critical sections. These are orthogonal to FreeRTOS critical sections diff --git a/components/freertos/FreeRTOS-Kernel-SMP/portable/riscv/portasm.S b/components/freertos/FreeRTOS-Kernel-SMP/portable/riscv/portasm.S index e27e9de2c1..421fa7b1b7 100644 --- a/components/freertos/FreeRTOS-Kernel-SMP/portable/riscv/portasm.S +++ b/components/freertos/FreeRTOS-Kernel-SMP/portable/riscv/portasm.S @@ -1,8 +1,13 @@ /* - * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ +#include "sdkconfig.h" +#include "portmacro.h" +#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD +#include "esp_private/hw_stack_guard.h" +#endif .global uxInterruptNesting .global uxSchedulerRunning @@ -10,23 +15,26 @@ .global pxCurrentTCBs .global vTaskSwitchContext .global xPortSwitchFlag +#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD + .global xIsrStack + .global port_offset_pxStack + .global port_offset_pxEndOfStack + .global esp_hw_stack_guard_monitor_stop + .global esp_hw_stack_guard_monitor_start + .global esp_hw_stack_guard_set_bounds +#endif /* CONFIG_ESP_SYSTEM_HW_STACK_GUARD */ .section .text /** * This function makes the RTOS aware about a ISR entering, it takes the - * current task stack saved, places into the TCB, loads the ISR stack - * the interrupted stack must be passed in a0. It needs to receive the - * ISR nesting code improvements + * current task stack saved, places into the TCB, loads the ISR stack. + * TODO: ISR nesting code improvements ? */ .global rtos_int_enter .type rtos_int_enter, @function rtos_int_enter: - /* preserve the return address */ - mv t1, ra - mv t2, a0 - /* scheduler not enabled, jump directly to ISR handler */ lw t0, uxSchedulerRunning beq t0,zero, rtos_enter_end @@ -40,19 +48,30 @@ rtos_int_enter: /* If reached here from another low-prio ISR, skip stack pushing to TCB */ bne t4,zero, rtos_enter_end +#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD + /* esp_hw_stack_guard_monitor_stop(); */ + ESP_HW_STACK_GUARD_MONITOR_STOP_CPU0 +#endif /* CONFIG_ESP_SYSTEM_HW_STACK_GUARD */ + /* Save current TCB and load the ISR stack */ lw t0, pxCurrentTCBs - sw t2, 0x0(t0) + sw sp, 0x0(t0) lw sp, xIsrStackTop +#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD + /* esp_hw_stack_guard_set_bounds(xIsrStack, xIsrStackTop); */ + la a0, xIsrStack + mv a1, sp + ESP_HW_STACK_GUARD_SET_BOUNDS_CPU0 + /* esp_hw_stack_guard_monitor_start(); */ + ESP_HW_STACK_GUARD_MONITOR_START_CPU0 +#endif /* CONFIG_ESP_SYSTEM_HW_STACK_GUARD */ + rtos_enter_end: - mv ra, t1 ret /** - * Recovers the next task to run stack pointer and place it into - * a0, then the interrupt handler can restore the context of - * the next task + * Restores the context of the next task. */ .global rtos_int_exit .type rtos_int_exit, @function @@ -96,9 +115,26 @@ isr_skip_decrement: sw t2, 0x0(t0) no_switch: - /* Recover the stack of next task and prepare to exit : */ - lw a0, pxCurrentTCBs - lw a0, 0x0(a0) + +#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD + /* esp_hw_stack_guard_monitor_stop(); */ + ESP_HW_STACK_GUARD_MONITOR_STOP_CPU0 +#endif /* CONFIG_ESP_SYSTEM_HW_STACK_GUARD */ + + /* Recover the stack of next task */ + lw t0, pxCurrentTCBs + lw sp, 0x0(t0) + +#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD + /* esp_hw_stack_guard_set_bounds(pxCurrentTCBs[0]->pxStack, + * pxCurrentTCBs[0]->pxEndOfStack); + */ + lw a0, PORT_OFFSET_PX_STACK(t0) + lw a1, PORT_OFFSET_PX_END_OF_STACK(t0) + ESP_HW_STACK_GUARD_SET_BOUNDS_CPU0 + /* esp_hw_stack_guard_monitor_start(); */ + ESP_HW_STACK_GUARD_MONITOR_START_CPU0 +#endif /* CONFIG_ESP_SYSTEM_HW_STACK_GUARD */ rtos_exit_end: ret diff --git a/components/freertos/FreeRTOS-Kernel/portable/riscv/include/freertos/portmacro.h b/components/freertos/FreeRTOS-Kernel/portable/riscv/include/freertos/portmacro.h index b889f02bba..ba2b45b217 100644 --- a/components/freertos/FreeRTOS-Kernel/portable/riscv/include/freertos/portmacro.h +++ b/components/freertos/FreeRTOS-Kernel/portable/riscv/include/freertos/portmacro.h @@ -35,9 +35,17 @@ #ifndef PORTMACRO_H #define PORTMACRO_H +#include "sdkconfig.h" + +/* Macros used instead ofsetoff() for better performance of interrupt handler */ +#define PORT_OFFSET_PX_STACK 0x30 +#define PORT_OFFSET_PX_END_OF_STACK (PORT_OFFSET_PX_STACK + \ + /* void * pxDummy6 */ 4 + \ + /* uint8_t ucDummy7[ configMAX_TASK_NAME_LEN ] */ CONFIG_FREERTOS_MAX_TASK_NAME_LEN + \ + /* BaseType_t xDummyCoreID */ 4) + #ifndef __ASSEMBLER__ -#include "sdkconfig.h" #include #include #include diff --git a/components/freertos/FreeRTOS-Kernel/portable/riscv/port.c b/components/freertos/FreeRTOS-Kernel/portable/riscv/port.c index 65c3861493..ba2a29e8b7 100644 --- a/components/freertos/FreeRTOS-Kernel/portable/riscv/port.c +++ b/components/freertos/FreeRTOS-Kernel/portable/riscv/port.c @@ -58,6 +58,17 @@ #include "esp_memory_utils.h" _Static_assert(portBYTE_ALIGNMENT == 16, "portBYTE_ALIGNMENT must be set to 16"); +#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD +/** + * offsetof() can not be used in asm code. Then we need make sure that + * PORT_OFFSET_PX_STACK and PORT_OFFSET_PX_END_OF_STACK have expected values. + * Macro used in the portasm.S instead of variables to save at least 4 instruction calls + * which accessing DRAM memory. This optimization saves CPU time in the interrupt handling. + */ + +_Static_assert(offsetof( StaticTask_t, pxDummy6 ) == PORT_OFFSET_PX_STACK); +_Static_assert(offsetof( StaticTask_t, pxDummy8 ) == PORT_OFFSET_PX_END_OF_STACK); +#endif // CONFIG_ESP_SYSTEM_HW_STACK_GUARD /* ---------------------------------------------------- Variables ------------------------------------------------------ * @@ -74,7 +85,7 @@ static UBaseType_t uxSavedInterruptState = 0; BaseType_t uxSchedulerRunning = 0; // Duplicate of xSchedulerRunning, accessible to port files UBaseType_t uxInterruptNesting = 0; BaseType_t xPortSwitchFlag = 0; -__attribute__((aligned(16))) static StackType_t xIsrStack[configISR_STACK_SIZE]; +__attribute__((aligned(16))) StackType_t xIsrStack[configISR_STACK_SIZE]; StackType_t *xIsrStackTop = &xIsrStack[0] + (configISR_STACK_SIZE & (~((portPOINTER_SIZE_TYPE)portBYTE_ALIGNMENT_MASK))); diff --git a/components/freertos/FreeRTOS-Kernel/portable/riscv/portasm.S b/components/freertos/FreeRTOS-Kernel/portable/riscv/portasm.S index 8d8a7b3256..fc1c33042d 100644 --- a/components/freertos/FreeRTOS-Kernel/portable/riscv/portasm.S +++ b/components/freertos/FreeRTOS-Kernel/portable/riscv/portasm.S @@ -3,6 +3,11 @@ * * SPDX-License-Identifier: Apache-2.0 */ +#include "sdkconfig.h" +#include "portmacro.h" +#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD +#include "esp_private/hw_stack_guard.h" +#endif .global uxInterruptNesting .global uxSchedulerRunning @@ -10,23 +15,26 @@ .global pxCurrentTCB .global vTaskSwitchContext .global xPortSwitchFlag +#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD + .global xIsrStack + .global port_offset_pxStack + .global port_offset_pxEndOfStack + .global esp_hw_stack_guard_monitor_stop + .global esp_hw_stack_guard_monitor_start + .global esp_hw_stack_guard_set_bounds +#endif /* CONFIG_ESP_SYSTEM_HW_STACK_GUARD */ .section .text /** * This function makes the RTOS aware about a ISR entering, it takes the - * current task stack saved, places into the TCB, loads the ISR stack - * the interrupted stack must be passed in a0. It needs to receive the - * ISR nesting code improvements + * current task stack saved, places into the TCB, loads the ISR stack. + * TODO: ISR nesting code improvements ? */ .global rtos_int_enter .type rtos_int_enter, @function rtos_int_enter: - /* preserve the return address */ - mv t1, ra - mv t2, a0 - /* scheduler not enabled, jump directly to ISR handler */ lw t0, uxSchedulerRunning beq t0,zero, rtos_enter_end @@ -40,19 +48,29 @@ rtos_int_enter: /* If reached here from another low-prio ISR, skip stack pushing to TCB */ bne t4,zero, rtos_enter_end +#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD + /* esp_hw_stack_guard_monitor_stop(); */ + ESP_HW_STACK_GUARD_MONITOR_STOP_CPU0 +#endif /* CONFIG_ESP_SYSTEM_HW_STACK_GUARD */ + /* Save current TCB and load the ISR stack */ lw t0, pxCurrentTCB - sw t2, 0x0(t0) + sw sp, 0x0(t0) lw sp, xIsrStackTop +#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD + /* esp_hw_stack_guard_set_bounds(xIsrStack, xIsrStackTop); */ + la a0, xIsrStack + mv a1, sp + ESP_HW_STACK_GUARD_SET_BOUNDS_CPU0 + ESP_HW_STACK_GUARD_MONITOR_START_CPU0 +#endif /* CONFIG_ESP_SYSTEM_HW_STACK_GUARD */ + rtos_enter_end: - mv ra, t1 ret /** - * Recovers the next task to run stack pointer and place it into - * a0, then the interrupt handler can restore the context of - * the next task + * Recovers the next task to run stack pointer. */ .global rtos_int_exit .type rtos_int_exit, @function @@ -94,9 +112,26 @@ isr_skip_decrement: sw t2, 0x0(t0) no_switch: - /* Recover the stack of next task and prepare to exit : */ - lw a0, pxCurrentTCB - lw a0, 0x0(a0) + +#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD + /* esp_hw_stack_guard_monitor_stop(); */ + ESP_HW_STACK_GUARD_MONITOR_STOP_CPU0 +#endif /* CONFIG_ESP_SYSTEM_HW_STACK_GUARD */ + + /* Recover the stack of next task */ + lw t0, pxCurrentTCB + lw sp, 0x0(t0) + +#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD + /* esp_hw_stack_guard_set_bounds(pxCurrentTCB[0]->pxStack, + * pxCurrentTCB[0]->pxEndOfStack); + */ + lw a0, PORT_OFFSET_PX_STACK(t0) + lw a1, PORT_OFFSET_PX_END_OF_STACK(t0) + ESP_HW_STACK_GUARD_SET_BOUNDS_CPU0 + /* esp_hw_stack_guard_monitor_start(); */ + ESP_HW_STACK_GUARD_MONITOR_START_CPU0 +#endif /* CONFIG_ESP_SYSTEM_HW_STACK_GUARD */ rtos_exit_end: ret diff --git a/components/freertos/esp_additions/freertos_tasks_c_additions.h b/components/freertos/esp_additions/freertos_tasks_c_additions.h index 85ad5b926e..9b70e719db 100644 --- a/components/freertos/esp_additions/freertos_tasks_c_additions.h +++ b/components/freertos/esp_additions/freertos_tasks_c_additions.h @@ -18,6 +18,18 @@ * additional API. */ +/* ------------------------------------------------- Static asserts ---------------------------------------------------- + * + * ------------------------------------------------------------------------------------------------------------------ */ + +/** + * Both StaticTask_t and TCB_t structures are provided by FreeRTOS sources. + * This is just an additional check of the consistency of these structures. + */ + +_Static_assert(offsetof( StaticTask_t, pxDummy6 ) == offsetof( TCB_t, pxStack )); +_Static_assert(offsetof( StaticTask_t, pxDummy8 ) == offsetof( TCB_t, pxEndOfStack )); + /* ----------------------------------------------------- Newlib -------------------------------------------------------- * * ------------------------------------------------------------------------------------------------------------------ */ diff --git a/components/freertos/test_apps/freertos/sdkconfig.ci.release b/components/freertos/test_apps/freertos/sdkconfig.ci.release index 9a981808d7..14d4e05461 100644 --- a/components/freertos/test_apps/freertos/sdkconfig.ci.release +++ b/components/freertos/test_apps/freertos/sdkconfig.ci.release @@ -3,3 +3,6 @@ CONFIG_COMPILER_OPTIMIZATION_SIZE=y CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y + +# Test if disabled hardware stack guard works as expected (enabled by default) +CONFIG_ESP_SYSTEM_HW_STACK_GUARD=n diff --git a/components/hal/esp32c2/include/hal/assist_debug_ll.h b/components/hal/esp32c2/include/hal/assist_debug_ll.h new file mode 100644 index 0000000000..02c7e4089a --- /dev/null +++ b/components/hal/esp32c2/include/hal/assist_debug_ll.h @@ -0,0 +1,119 @@ +/* + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +// The LL layer for DEBUG_ASSIST peripheral + +#pragma once + +#include "soc/assist_debug_reg.h" +#define ASSIST_DEBUG_SP_SPILL_BITS (ASSIST_DEBUG_CORE_0_SP_SPILL_MIN_ENA | ASSIST_DEBUG_CORE_0_SP_SPILL_MAX_ENA) + +#ifndef __ASSEMBLER__ + +#include +#include +#include "esp_attr.h" +#include "hal/assert.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Most other peripherals have 4 interrupt-related registers: INT_ENA_REG, INT_CLR_REG, INT_RAW_REG, INT_ST_REG, the + * meaning of which is well-understood. + * + * Assist_debug peripheral uses a different structure of interrupt registers: + * INT_ENA_REG, INT_RLS_REG, INT_CLR_REG, INT_RAW_REG. + * + * Their behavior can be explained using the following (verilog-like) pseudo-code: + * reg sp_spill_max_st + * assign sp_spill_max = (sp > SP_MAX_REG) + * assign SP_SPILL_MAX_RAW = sp_spill_max & SPILL_MAX_ENA + * always (@posedge clk) begin + * if (reset) then sp_spill_max_st <= 0 + * elif SP_SPILL_MAX_CLR then sp_spill_max_st <= 0 + * else sp_spill_max_st <= SP_SPILL_MAX_RAW & SP_SPILL_MAX_RLS + * end + * // ...same for sp_spill_min and other things debug_assist can check. + * + * // this is the final interrupt line coming out of the peripheral: + * assign DEBUG_ASSIST_INT = sp_spill_max_st | sp_spill_min_st | ... + * + * Basically, there is no "ST" register showing the final (latched) interrupt state, and there is an additional + * "RLS" register which just like "ENA" can be used to mask the interrupt. + * Note that writing to CLR clears the (internal) latched interrupt state 'sp_spill_max_st', + * but doesn't affect the software-readable RAW register. + * + * In this code, we use "ENA" to enable monitoring of a particular condition, and "RLS" to enable the interrupt. + * This allows checking whether the condition (e.g. sp > SP_MAX) has occurred by reading the RAW register, without + * actually triggering the interrupt. Hence you will see the somewhat counter-intuitive use of "RLS" to enable the + * interrupt, instead of "ENA". + */ + + /* These functions are optimazed and designed for internal usage. + * So, the API may differ from general ll layer pattern */ + +FORCE_INLINE_ATTR void assist_debug_ll_sp_spill_monitor_enable(__attribute__((unused)) uint32_t core_id) +{ + REG_SET_BIT(ASSIST_DEBUG_CORE_0_INTR_ENA_REG, ASSIST_DEBUG_SP_SPILL_BITS); +} + +FORCE_INLINE_ATTR void assist_debug_ll_sp_spill_monitor_disable(__attribute__((unused)) uint32_t core_id) +{ + REG_CLR_BIT(ASSIST_DEBUG_CORE_0_INTR_ENA_REG, ASSIST_DEBUG_SP_SPILL_BITS); +} + +FORCE_INLINE_ATTR void assist_debug_ll_sp_spill_interrupt_enable(__attribute__((unused)) uint32_t core_id) +{ + REG_SET_BIT(ASSIST_DEBUG_CORE_0_INTR_RLS_REG, ASSIST_DEBUG_SP_SPILL_BITS); +} + +FORCE_INLINE_ATTR void assist_debug_ll_sp_spill_interrupt_disable(__attribute__((unused)) uint32_t core_id) +{ + REG_CLR_BIT(ASSIST_DEBUG_CORE_0_INTR_RLS_REG, ASSIST_DEBUG_SP_SPILL_BITS); +} + +FORCE_INLINE_ATTR bool assist_debug_ll_sp_spill_is_fired(__attribute__((unused)) uint32_t core_id) +{ + return REG_READ(ASSIST_DEBUG_CORE_0_INTR_RAW_REG) & ASSIST_DEBUG_SP_SPILL_BITS; +} + +FORCE_INLINE_ATTR void assist_debug_ll_sp_spill_interrupt_clear(__attribute__((unused)) uint32_t core_id) +{ + REG_WRITE(ASSIST_DEBUG_CORE_0_INTR_CLR_REG, ASSIST_DEBUG_SP_SPILL_BITS); +} + +FORCE_INLINE_ATTR void assist_debug_ll_sp_spill_set_min(__attribute__((unused)) uint32_t core_id, uint32_t min) +{ + REG_WRITE(ASSIST_DEBUG_CORE_0_SP_MIN_REG, min); +} + +FORCE_INLINE_ATTR uint32_t assist_debug_ll_sp_spill_get_min(__attribute__((unused)) uint32_t core_id) +{ + return REG_READ(ASSIST_DEBUG_CORE_0_SP_MIN_REG); +} + +FORCE_INLINE_ATTR void assist_debug_ll_sp_spill_set_max(__attribute__((unused)) uint32_t core_id, uint32_t max) +{ + REG_WRITE(ASSIST_DEBUG_CORE_0_SP_MAX_REG, max); +} + +FORCE_INLINE_ATTR uint32_t assist_debug_ll_sp_spill_get_max(__attribute__((unused)) uint32_t core_id) +{ + return REG_READ(ASSIST_DEBUG_CORE_0_SP_MAX_REG); +} + +FORCE_INLINE_ATTR uint32_t assist_debug_ll_sp_spill_get_pc(__attribute__((unused)) uint32_t core_id) +{ + return REG_READ(ASSIST_DEBUG_CORE_0_SP_PC_REG); +} + +#ifdef __cplusplus +} +#endif + +#endif // __ASSEMBLER__ diff --git a/components/hal/esp32c2/include/hal/clk_gate_ll.h b/components/hal/esp32c2/include/hal/clk_gate_ll.h index 522efa77ff..fe9671fe72 100644 --- a/components/hal/esp32c2/include/hal/clk_gate_ll.h +++ b/components/hal/esp32c2/include/hal/clk_gate_ll.h @@ -59,6 +59,8 @@ static inline uint32_t periph_ll_get_clk_en_mask(periph_module_t periph) return SYSTEM_BT_LC_EN; case PERIPH_TEMPSENSOR_MODULE: return SYSTEM_TSENS_CLK_EN; + case PERIPH_ASSIST_DEBUG_MODULE: + return SYSTEM_CLK_EN_ASSIST_DEBUG; default: return 0; } @@ -106,6 +108,8 @@ static inline uint32_t periph_ll_get_rst_en_mask(periph_module_t periph, bool en } case PERIPH_MODEM_RPA_MODULE: return BLE_RPA_REST_BIT; + case PERIPH_ASSIST_DEBUG_MODULE: + return SYSTEM_RST_EN_ASSIST_DEBUG; default: return 0; } @@ -127,6 +131,10 @@ static uint32_t periph_ll_get_clk_en_reg(periph_module_t periph) case PERIPH_ECC_MODULE: case PERIPH_TEMPSENSOR_MODULE: return SYSTEM_PERIP_CLK_EN1_REG; + + case PERIPH_ASSIST_DEBUG_MODULE: + return SYSTEM_CPU_PERI_CLK_EN_REG; + default: return SYSTEM_PERIP_CLK_EN0_REG; } @@ -149,6 +157,10 @@ static uint32_t periph_ll_get_rst_en_reg(periph_module_t periph) case PERIPH_ECC_MODULE: case PERIPH_TEMPSENSOR_MODULE: return SYSTEM_PERIP_RST_EN1_REG; + + case PERIPH_ASSIST_DEBUG_MODULE: + return SYSTEM_CPU_PERI_RST_EN_REG; + default: return SYSTEM_PERIP_RST_EN0_REG; } diff --git a/components/hal/esp32c3/include/hal/assist_debug_ll.h b/components/hal/esp32c3/include/hal/assist_debug_ll.h new file mode 100644 index 0000000000..02c7e4089a --- /dev/null +++ b/components/hal/esp32c3/include/hal/assist_debug_ll.h @@ -0,0 +1,119 @@ +/* + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +// The LL layer for DEBUG_ASSIST peripheral + +#pragma once + +#include "soc/assist_debug_reg.h" +#define ASSIST_DEBUG_SP_SPILL_BITS (ASSIST_DEBUG_CORE_0_SP_SPILL_MIN_ENA | ASSIST_DEBUG_CORE_0_SP_SPILL_MAX_ENA) + +#ifndef __ASSEMBLER__ + +#include +#include +#include "esp_attr.h" +#include "hal/assert.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Most other peripherals have 4 interrupt-related registers: INT_ENA_REG, INT_CLR_REG, INT_RAW_REG, INT_ST_REG, the + * meaning of which is well-understood. + * + * Assist_debug peripheral uses a different structure of interrupt registers: + * INT_ENA_REG, INT_RLS_REG, INT_CLR_REG, INT_RAW_REG. + * + * Their behavior can be explained using the following (verilog-like) pseudo-code: + * reg sp_spill_max_st + * assign sp_spill_max = (sp > SP_MAX_REG) + * assign SP_SPILL_MAX_RAW = sp_spill_max & SPILL_MAX_ENA + * always (@posedge clk) begin + * if (reset) then sp_spill_max_st <= 0 + * elif SP_SPILL_MAX_CLR then sp_spill_max_st <= 0 + * else sp_spill_max_st <= SP_SPILL_MAX_RAW & SP_SPILL_MAX_RLS + * end + * // ...same for sp_spill_min and other things debug_assist can check. + * + * // this is the final interrupt line coming out of the peripheral: + * assign DEBUG_ASSIST_INT = sp_spill_max_st | sp_spill_min_st | ... + * + * Basically, there is no "ST" register showing the final (latched) interrupt state, and there is an additional + * "RLS" register which just like "ENA" can be used to mask the interrupt. + * Note that writing to CLR clears the (internal) latched interrupt state 'sp_spill_max_st', + * but doesn't affect the software-readable RAW register. + * + * In this code, we use "ENA" to enable monitoring of a particular condition, and "RLS" to enable the interrupt. + * This allows checking whether the condition (e.g. sp > SP_MAX) has occurred by reading the RAW register, without + * actually triggering the interrupt. Hence you will see the somewhat counter-intuitive use of "RLS" to enable the + * interrupt, instead of "ENA". + */ + + /* These functions are optimazed and designed for internal usage. + * So, the API may differ from general ll layer pattern */ + +FORCE_INLINE_ATTR void assist_debug_ll_sp_spill_monitor_enable(__attribute__((unused)) uint32_t core_id) +{ + REG_SET_BIT(ASSIST_DEBUG_CORE_0_INTR_ENA_REG, ASSIST_DEBUG_SP_SPILL_BITS); +} + +FORCE_INLINE_ATTR void assist_debug_ll_sp_spill_monitor_disable(__attribute__((unused)) uint32_t core_id) +{ + REG_CLR_BIT(ASSIST_DEBUG_CORE_0_INTR_ENA_REG, ASSIST_DEBUG_SP_SPILL_BITS); +} + +FORCE_INLINE_ATTR void assist_debug_ll_sp_spill_interrupt_enable(__attribute__((unused)) uint32_t core_id) +{ + REG_SET_BIT(ASSIST_DEBUG_CORE_0_INTR_RLS_REG, ASSIST_DEBUG_SP_SPILL_BITS); +} + +FORCE_INLINE_ATTR void assist_debug_ll_sp_spill_interrupt_disable(__attribute__((unused)) uint32_t core_id) +{ + REG_CLR_BIT(ASSIST_DEBUG_CORE_0_INTR_RLS_REG, ASSIST_DEBUG_SP_SPILL_BITS); +} + +FORCE_INLINE_ATTR bool assist_debug_ll_sp_spill_is_fired(__attribute__((unused)) uint32_t core_id) +{ + return REG_READ(ASSIST_DEBUG_CORE_0_INTR_RAW_REG) & ASSIST_DEBUG_SP_SPILL_BITS; +} + +FORCE_INLINE_ATTR void assist_debug_ll_sp_spill_interrupt_clear(__attribute__((unused)) uint32_t core_id) +{ + REG_WRITE(ASSIST_DEBUG_CORE_0_INTR_CLR_REG, ASSIST_DEBUG_SP_SPILL_BITS); +} + +FORCE_INLINE_ATTR void assist_debug_ll_sp_spill_set_min(__attribute__((unused)) uint32_t core_id, uint32_t min) +{ + REG_WRITE(ASSIST_DEBUG_CORE_0_SP_MIN_REG, min); +} + +FORCE_INLINE_ATTR uint32_t assist_debug_ll_sp_spill_get_min(__attribute__((unused)) uint32_t core_id) +{ + return REG_READ(ASSIST_DEBUG_CORE_0_SP_MIN_REG); +} + +FORCE_INLINE_ATTR void assist_debug_ll_sp_spill_set_max(__attribute__((unused)) uint32_t core_id, uint32_t max) +{ + REG_WRITE(ASSIST_DEBUG_CORE_0_SP_MAX_REG, max); +} + +FORCE_INLINE_ATTR uint32_t assist_debug_ll_sp_spill_get_max(__attribute__((unused)) uint32_t core_id) +{ + return REG_READ(ASSIST_DEBUG_CORE_0_SP_MAX_REG); +} + +FORCE_INLINE_ATTR uint32_t assist_debug_ll_sp_spill_get_pc(__attribute__((unused)) uint32_t core_id) +{ + return REG_READ(ASSIST_DEBUG_CORE_0_SP_PC_REG); +} + +#ifdef __cplusplus +} +#endif + +#endif // __ASSEMBLER__ diff --git a/components/hal/esp32c3/include/hal/clk_gate_ll.h b/components/hal/esp32c3/include/hal/clk_gate_ll.h index 8f8526e1a9..67ef4a883f 100644 --- a/components/hal/esp32c3/include/hal/clk_gate_ll.h +++ b/components/hal/esp32c3/include/hal/clk_gate_ll.h @@ -75,6 +75,8 @@ static inline uint32_t periph_ll_get_clk_en_mask(periph_module_t periph) return SYSTEM_BT_LC_EN; case PERIPH_TEMPSENSOR_MODULE: return SYSTEM_TSENS_CLK_EN; + case PERIPH_ASSIST_DEBUG_MODULE: + return SYSTEM_CLK_EN_ASSIST_DEBUG; default: return 0; } @@ -150,6 +152,8 @@ static inline uint32_t periph_ll_get_rst_en_mask(periph_module_t periph, bool en } case PERIPH_DS_MODULE: return SYSTEM_CRYPTO_DS_RST; + case PERIPH_ASSIST_DEBUG_MODULE: + return SYSTEM_RST_EN_ASSIST_DEBUG; default: return 0; } @@ -174,6 +178,10 @@ static uint32_t periph_ll_get_clk_en_reg(periph_module_t periph) case PERIPH_GDMA_MODULE: case PERIPH_TEMPSENSOR_MODULE: return SYSTEM_PERIP_CLK_EN1_REG; + + case PERIPH_ASSIST_DEBUG_MODULE: + return SYSTEM_CPU_PERI_CLK_EN_REG; + default: return SYSTEM_PERIP_CLK_EN0_REG; } @@ -198,6 +206,10 @@ static uint32_t periph_ll_get_rst_en_reg(periph_module_t periph) case PERIPH_GDMA_MODULE: case PERIPH_TEMPSENSOR_MODULE: return SYSTEM_PERIP_RST_EN1_REG; + + case PERIPH_ASSIST_DEBUG_MODULE: + return SYSTEM_CPU_PERI_RST_EN_REG; + default: return SYSTEM_PERIP_RST_EN0_REG; } diff --git a/components/hal/esp32c6/include/hal/assist_debug_ll.h b/components/hal/esp32c6/include/hal/assist_debug_ll.h new file mode 100644 index 0000000000..02c7e4089a --- /dev/null +++ b/components/hal/esp32c6/include/hal/assist_debug_ll.h @@ -0,0 +1,119 @@ +/* + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +// The LL layer for DEBUG_ASSIST peripheral + +#pragma once + +#include "soc/assist_debug_reg.h" +#define ASSIST_DEBUG_SP_SPILL_BITS (ASSIST_DEBUG_CORE_0_SP_SPILL_MIN_ENA | ASSIST_DEBUG_CORE_0_SP_SPILL_MAX_ENA) + +#ifndef __ASSEMBLER__ + +#include +#include +#include "esp_attr.h" +#include "hal/assert.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Most other peripherals have 4 interrupt-related registers: INT_ENA_REG, INT_CLR_REG, INT_RAW_REG, INT_ST_REG, the + * meaning of which is well-understood. + * + * Assist_debug peripheral uses a different structure of interrupt registers: + * INT_ENA_REG, INT_RLS_REG, INT_CLR_REG, INT_RAW_REG. + * + * Their behavior can be explained using the following (verilog-like) pseudo-code: + * reg sp_spill_max_st + * assign sp_spill_max = (sp > SP_MAX_REG) + * assign SP_SPILL_MAX_RAW = sp_spill_max & SPILL_MAX_ENA + * always (@posedge clk) begin + * if (reset) then sp_spill_max_st <= 0 + * elif SP_SPILL_MAX_CLR then sp_spill_max_st <= 0 + * else sp_spill_max_st <= SP_SPILL_MAX_RAW & SP_SPILL_MAX_RLS + * end + * // ...same for sp_spill_min and other things debug_assist can check. + * + * // this is the final interrupt line coming out of the peripheral: + * assign DEBUG_ASSIST_INT = sp_spill_max_st | sp_spill_min_st | ... + * + * Basically, there is no "ST" register showing the final (latched) interrupt state, and there is an additional + * "RLS" register which just like "ENA" can be used to mask the interrupt. + * Note that writing to CLR clears the (internal) latched interrupt state 'sp_spill_max_st', + * but doesn't affect the software-readable RAW register. + * + * In this code, we use "ENA" to enable monitoring of a particular condition, and "RLS" to enable the interrupt. + * This allows checking whether the condition (e.g. sp > SP_MAX) has occurred by reading the RAW register, without + * actually triggering the interrupt. Hence you will see the somewhat counter-intuitive use of "RLS" to enable the + * interrupt, instead of "ENA". + */ + + /* These functions are optimazed and designed for internal usage. + * So, the API may differ from general ll layer pattern */ + +FORCE_INLINE_ATTR void assist_debug_ll_sp_spill_monitor_enable(__attribute__((unused)) uint32_t core_id) +{ + REG_SET_BIT(ASSIST_DEBUG_CORE_0_INTR_ENA_REG, ASSIST_DEBUG_SP_SPILL_BITS); +} + +FORCE_INLINE_ATTR void assist_debug_ll_sp_spill_monitor_disable(__attribute__((unused)) uint32_t core_id) +{ + REG_CLR_BIT(ASSIST_DEBUG_CORE_0_INTR_ENA_REG, ASSIST_DEBUG_SP_SPILL_BITS); +} + +FORCE_INLINE_ATTR void assist_debug_ll_sp_spill_interrupt_enable(__attribute__((unused)) uint32_t core_id) +{ + REG_SET_BIT(ASSIST_DEBUG_CORE_0_INTR_RLS_REG, ASSIST_DEBUG_SP_SPILL_BITS); +} + +FORCE_INLINE_ATTR void assist_debug_ll_sp_spill_interrupt_disable(__attribute__((unused)) uint32_t core_id) +{ + REG_CLR_BIT(ASSIST_DEBUG_CORE_0_INTR_RLS_REG, ASSIST_DEBUG_SP_SPILL_BITS); +} + +FORCE_INLINE_ATTR bool assist_debug_ll_sp_spill_is_fired(__attribute__((unused)) uint32_t core_id) +{ + return REG_READ(ASSIST_DEBUG_CORE_0_INTR_RAW_REG) & ASSIST_DEBUG_SP_SPILL_BITS; +} + +FORCE_INLINE_ATTR void assist_debug_ll_sp_spill_interrupt_clear(__attribute__((unused)) uint32_t core_id) +{ + REG_WRITE(ASSIST_DEBUG_CORE_0_INTR_CLR_REG, ASSIST_DEBUG_SP_SPILL_BITS); +} + +FORCE_INLINE_ATTR void assist_debug_ll_sp_spill_set_min(__attribute__((unused)) uint32_t core_id, uint32_t min) +{ + REG_WRITE(ASSIST_DEBUG_CORE_0_SP_MIN_REG, min); +} + +FORCE_INLINE_ATTR uint32_t assist_debug_ll_sp_spill_get_min(__attribute__((unused)) uint32_t core_id) +{ + return REG_READ(ASSIST_DEBUG_CORE_0_SP_MIN_REG); +} + +FORCE_INLINE_ATTR void assist_debug_ll_sp_spill_set_max(__attribute__((unused)) uint32_t core_id, uint32_t max) +{ + REG_WRITE(ASSIST_DEBUG_CORE_0_SP_MAX_REG, max); +} + +FORCE_INLINE_ATTR uint32_t assist_debug_ll_sp_spill_get_max(__attribute__((unused)) uint32_t core_id) +{ + return REG_READ(ASSIST_DEBUG_CORE_0_SP_MAX_REG); +} + +FORCE_INLINE_ATTR uint32_t assist_debug_ll_sp_spill_get_pc(__attribute__((unused)) uint32_t core_id) +{ + return REG_READ(ASSIST_DEBUG_CORE_0_SP_PC_REG); +} + +#ifdef __cplusplus +} +#endif + +#endif // __ASSEMBLER__ diff --git a/components/hal/esp32c6/include/hal/clk_gate_ll.h b/components/hal/esp32c6/include/hal/clk_gate_ll.h index 5460ee9bce..3a3a0ebc37 100644 --- a/components/hal/esp32c6/include/hal/clk_gate_ll.h +++ b/components/hal/esp32c6/include/hal/clk_gate_ll.h @@ -78,6 +78,8 @@ static inline uint32_t periph_ll_get_clk_en_mask(periph_module_t periph) return PCR_SDIO_SLAVE_CLK_EN; case PERIPH_REGDMA_MODULE: return PCR_REGDMA_CLK_EN; + case PERIPH_ASSIST_DEBUG_MODULE: + return PCR_ASSIST_CLK_EN; default: return 0; } @@ -159,6 +161,8 @@ static inline uint32_t periph_ll_get_rst_en_mask(periph_module_t periph, bool en return PCR_SDIO_SLAVE_RST_EN; case PERIPH_REGDMA_MODULE: return PCR_REGDMA_RST_EN; + case PERIPH_ASSIST_DEBUG_MODULE: + return PCR_ASSIST_RST_EN; default: return 0; } @@ -225,6 +229,8 @@ static uint32_t periph_ll_get_clk_en_reg(periph_module_t periph) return PCR_SDIO_SLAVE_CONF_REG; case PERIPH_REGDMA_MODULE: return PCR_REGDMA_CONF_REG; + case PERIPH_ASSIST_DEBUG_MODULE: + return PCR_ASSIST_CONF_REG; default: return 0; } @@ -291,6 +297,8 @@ static uint32_t periph_ll_get_rst_en_reg(periph_module_t periph) return PCR_SDIO_SLAVE_CONF_REG; case PERIPH_REGDMA_MODULE: return PCR_REGDMA_CONF_REG; + case PERIPH_ASSIST_DEBUG_MODULE: + return PCR_ASSIST_CONF_REG; default: return 0; } diff --git a/components/hal/esp32h2/include/hal/assist_debug_ll.h b/components/hal/esp32h2/include/hal/assist_debug_ll.h new file mode 100644 index 0000000000..02c7e4089a --- /dev/null +++ b/components/hal/esp32h2/include/hal/assist_debug_ll.h @@ -0,0 +1,119 @@ +/* + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +// The LL layer for DEBUG_ASSIST peripheral + +#pragma once + +#include "soc/assist_debug_reg.h" +#define ASSIST_DEBUG_SP_SPILL_BITS (ASSIST_DEBUG_CORE_0_SP_SPILL_MIN_ENA | ASSIST_DEBUG_CORE_0_SP_SPILL_MAX_ENA) + +#ifndef __ASSEMBLER__ + +#include +#include +#include "esp_attr.h" +#include "hal/assert.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Most other peripherals have 4 interrupt-related registers: INT_ENA_REG, INT_CLR_REG, INT_RAW_REG, INT_ST_REG, the + * meaning of which is well-understood. + * + * Assist_debug peripheral uses a different structure of interrupt registers: + * INT_ENA_REG, INT_RLS_REG, INT_CLR_REG, INT_RAW_REG. + * + * Their behavior can be explained using the following (verilog-like) pseudo-code: + * reg sp_spill_max_st + * assign sp_spill_max = (sp > SP_MAX_REG) + * assign SP_SPILL_MAX_RAW = sp_spill_max & SPILL_MAX_ENA + * always (@posedge clk) begin + * if (reset) then sp_spill_max_st <= 0 + * elif SP_SPILL_MAX_CLR then sp_spill_max_st <= 0 + * else sp_spill_max_st <= SP_SPILL_MAX_RAW & SP_SPILL_MAX_RLS + * end + * // ...same for sp_spill_min and other things debug_assist can check. + * + * // this is the final interrupt line coming out of the peripheral: + * assign DEBUG_ASSIST_INT = sp_spill_max_st | sp_spill_min_st | ... + * + * Basically, there is no "ST" register showing the final (latched) interrupt state, and there is an additional + * "RLS" register which just like "ENA" can be used to mask the interrupt. + * Note that writing to CLR clears the (internal) latched interrupt state 'sp_spill_max_st', + * but doesn't affect the software-readable RAW register. + * + * In this code, we use "ENA" to enable monitoring of a particular condition, and "RLS" to enable the interrupt. + * This allows checking whether the condition (e.g. sp > SP_MAX) has occurred by reading the RAW register, without + * actually triggering the interrupt. Hence you will see the somewhat counter-intuitive use of "RLS" to enable the + * interrupt, instead of "ENA". + */ + + /* These functions are optimazed and designed for internal usage. + * So, the API may differ from general ll layer pattern */ + +FORCE_INLINE_ATTR void assist_debug_ll_sp_spill_monitor_enable(__attribute__((unused)) uint32_t core_id) +{ + REG_SET_BIT(ASSIST_DEBUG_CORE_0_INTR_ENA_REG, ASSIST_DEBUG_SP_SPILL_BITS); +} + +FORCE_INLINE_ATTR void assist_debug_ll_sp_spill_monitor_disable(__attribute__((unused)) uint32_t core_id) +{ + REG_CLR_BIT(ASSIST_DEBUG_CORE_0_INTR_ENA_REG, ASSIST_DEBUG_SP_SPILL_BITS); +} + +FORCE_INLINE_ATTR void assist_debug_ll_sp_spill_interrupt_enable(__attribute__((unused)) uint32_t core_id) +{ + REG_SET_BIT(ASSIST_DEBUG_CORE_0_INTR_RLS_REG, ASSIST_DEBUG_SP_SPILL_BITS); +} + +FORCE_INLINE_ATTR void assist_debug_ll_sp_spill_interrupt_disable(__attribute__((unused)) uint32_t core_id) +{ + REG_CLR_BIT(ASSIST_DEBUG_CORE_0_INTR_RLS_REG, ASSIST_DEBUG_SP_SPILL_BITS); +} + +FORCE_INLINE_ATTR bool assist_debug_ll_sp_spill_is_fired(__attribute__((unused)) uint32_t core_id) +{ + return REG_READ(ASSIST_DEBUG_CORE_0_INTR_RAW_REG) & ASSIST_DEBUG_SP_SPILL_BITS; +} + +FORCE_INLINE_ATTR void assist_debug_ll_sp_spill_interrupt_clear(__attribute__((unused)) uint32_t core_id) +{ + REG_WRITE(ASSIST_DEBUG_CORE_0_INTR_CLR_REG, ASSIST_DEBUG_SP_SPILL_BITS); +} + +FORCE_INLINE_ATTR void assist_debug_ll_sp_spill_set_min(__attribute__((unused)) uint32_t core_id, uint32_t min) +{ + REG_WRITE(ASSIST_DEBUG_CORE_0_SP_MIN_REG, min); +} + +FORCE_INLINE_ATTR uint32_t assist_debug_ll_sp_spill_get_min(__attribute__((unused)) uint32_t core_id) +{ + return REG_READ(ASSIST_DEBUG_CORE_0_SP_MIN_REG); +} + +FORCE_INLINE_ATTR void assist_debug_ll_sp_spill_set_max(__attribute__((unused)) uint32_t core_id, uint32_t max) +{ + REG_WRITE(ASSIST_DEBUG_CORE_0_SP_MAX_REG, max); +} + +FORCE_INLINE_ATTR uint32_t assist_debug_ll_sp_spill_get_max(__attribute__((unused)) uint32_t core_id) +{ + return REG_READ(ASSIST_DEBUG_CORE_0_SP_MAX_REG); +} + +FORCE_INLINE_ATTR uint32_t assist_debug_ll_sp_spill_get_pc(__attribute__((unused)) uint32_t core_id) +{ + return REG_READ(ASSIST_DEBUG_CORE_0_SP_PC_REG); +} + +#ifdef __cplusplus +} +#endif + +#endif // __ASSEMBLER__ diff --git a/components/hal/esp32h2/include/hal/clk_gate_ll.h b/components/hal/esp32h2/include/hal/clk_gate_ll.h index 7a8cf3b84d..f1894080c0 100644 --- a/components/hal/esp32h2/include/hal/clk_gate_ll.h +++ b/components/hal/esp32h2/include/hal/clk_gate_ll.h @@ -89,6 +89,8 @@ static inline uint32_t periph_ll_get_clk_en_mask(periph_module_t periph) // return PCR_BT_BASEBAND_EN; // case PERIPH_BT_LC_MODULE: // return PCR_BT_LC_EN; + case PERIPH_ASSIST_DEBUG_MODULE: + return PCR_ASSIST_CLK_EN; default: return 0; } @@ -189,6 +191,8 @@ static inline uint32_t periph_ll_get_rst_en_mask(periph_module_t periph, bool en // return PCR_BT_BASEBAND_EN; // case PERIPH_BT_LC_MODULE: // return PCR_BT_LC_EN; + case PERIPH_ASSIST_DEBUG_MODULE: + return PCR_ASSIST_RST_EN; default: return 0; } @@ -263,6 +267,8 @@ static uint32_t periph_ll_get_clk_en_reg(periph_module_t periph) return PCR_TSENS_CLK_CONF_REG; case PERIPH_REGDMA_MODULE: return PCR_REGDMA_CONF_REG; + case PERIPH_ASSIST_DEBUG_MODULE: + return PCR_ASSIST_CONF_REG; default: return 0; } @@ -330,6 +336,8 @@ static uint32_t periph_ll_get_rst_en_reg(periph_module_t periph) return PCR_TSENS_CLK_CONF_REG; case PERIPH_REGDMA_MODULE: return PCR_REGDMA_CONF_REG; + case PERIPH_ASSIST_DEBUG_MODULE: + return PCR_ASSIST_CONF_REG; default: return 0; } diff --git a/components/hal/include/hal/assist_debug_hal.h b/components/hal/include/hal/assist_debug_hal.h new file mode 100644 index 0000000000..b7f0fdd2ca --- /dev/null +++ b/components/hal/include/hal/assist_debug_hal.h @@ -0,0 +1,70 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include +#include +#include "esp_attr.h" +#include "hal/assist_debug_ll.h" + +#ifdef __cplusplus +extern "C" { +#endif + +FORCE_INLINE_ATTR void assist_debug_hal_sp_int_enable(uint32_t core_id) +{ + assist_debug_ll_sp_spill_interrupt_enable(core_id); +} + +FORCE_INLINE_ATTR void assist_debug_hal_sp_int_disable(uint32_t core_id) +{ + assist_debug_ll_sp_spill_interrupt_disable(core_id); +} + +FORCE_INLINE_ATTR void assist_debug_hal_sp_int_clear(uint32_t core_id) +{ + assist_debug_ll_sp_spill_interrupt_clear(core_id); +} + +FORCE_INLINE_ATTR void assist_debug_hal_sp_mon_enable(uint32_t core_id) +{ + assist_debug_ll_sp_spill_monitor_enable(core_id); +} + +FORCE_INLINE_ATTR void assist_debug_hal_sp_mon_disable(uint32_t core_id) +{ + assist_debug_ll_sp_spill_monitor_disable(core_id); +} + +FORCE_INLINE_ATTR uint32_t assist_debug_hal_get_sp_ovf_pc(uint32_t core_id) +{ + return assist_debug_ll_sp_spill_get_pc(core_id); +} + +FORCE_INLINE_ATTR void assist_debug_hal_get_sp_bounds(uint32_t core_id, uint32_t *sp_min, uint32_t *sp_max) +{ + if (sp_min) { + *sp_min = assist_debug_ll_sp_spill_get_min(core_id); + } + if (sp_max) { + *sp_max = assist_debug_ll_sp_spill_get_max(core_id); + } +} + +FORCE_INLINE_ATTR void assist_debug_hal_set_sp_bounds(uint32_t core_id, uint32_t sp_min, uint32_t sp_max) +{ + assist_debug_ll_sp_spill_set_min(core_id, sp_min); + assist_debug_ll_sp_spill_set_max(core_id, sp_max); +} + +FORCE_INLINE_ATTR uint32_t assist_debug_hal_is_sp_ovf_fired(uint32_t core_id) +{ + return assist_debug_ll_sp_spill_is_fired(core_id); +} + +#ifdef __cplusplus +} +#endif diff --git a/components/riscv/include/esp_private/panic_reason.h b/components/riscv/include/esp_private/panic_reason.h index 09da6cbb43..5981bae0c7 100644 --- a/components/riscv/include/esp_private/panic_reason.h +++ b/components/riscv/include/esp_private/panic_reason.h @@ -4,16 +4,10 @@ * SPDX-License-Identifier: Apache-2.0 */ #pragma once +#include "soc/soc.h" -typedef enum { - PANIC_RSN_NONE = 0, - PANIC_RSN_INTWDT_CPU0, -#if SOC_CPU_NUM > 1 - PANIC_RSN_INTWDT_CPU1, -#endif - PANIC_RSN_CACHEERR, -#if CONFIG_ESP_SYSTEM_MEMPROT_FEATURE - PANIC_RSN_MEMPROT, -#endif - PANIC_RSN_COUNT -} panic_reasons_t; +/* Since riscv does not replace mcause with "pseudo_reason" as it xtensa does + * PANIC_RSN_* defined with original interrupt numbers to make it work in + * common code + */ +#define PANIC_RSN_INTWDT_CPU0 ETS_INT_WDT_INUM diff --git a/components/riscv/vectors.S b/components/riscv/vectors.S index 5a0c8ed9ad..b475910ad5 100644 --- a/components/riscv/vectors.S +++ b/components/riscv/vectors.S @@ -124,18 +124,26 @@ _vector_table: .option push .option norvc j _panic_handler /* exception handler, entry 0 */ +#if ETS_INT_WDT_INUM != 24 + #error "ETS_INT_WDT_INUM expected to be 24" +#endif .rept (ETS_INT_WDT_INUM - 1) - j _interrupt_handler /* 24 identical entries, all pointing to the interrupt handler */ + j _interrupt_handler /* 23 identical entries, all pointing to the interrupt handler */ .endr - j _panic_handler /* Call panic handler for ETS_INT_WDT_INUM interrupt (soc-level panic)*/ - j _panic_handler /* Call panic handler for ETS_CACHEERR_INUM interrupt (soc-level panic)*/ - #ifdef CONFIG_ESP_SYSTEM_MEMPROT_FEATURE - j _panic_handler /* Call panic handler for ETS_MEMPROT_ERR_INUM interrupt (soc-level panic)*/ - .rept (ETS_MAX_INUM - ETS_MEMPROT_ERR_INUM) - #else - .rept (ETS_MAX_INUM - ETS_CACHEERR_INUM) - #endif //CONFIG_ESP_SYSTEM_MEMPROT_FEATURE - j _interrupt_handler /* 6 identical entries, all pointing to the interrupt handler */ + j _panic_handler /* 24: ETS_INT_WDT_INUM panic-interrupt (soc-level panic) */ + j _panic_handler /* 25: ETS_CACHEERR_INUM panic-interrupt (soc-level panic) */ +#if CONFIG_ESP_SYSTEM_MEMPROT_FEATURE + j _panic_handler /* 26: ETS_MEMPROT_ERR_INUM panic-interrupt (soc-level panic) */ +#else + j _interrupt_handler /* 26: interrupt-handler */ +#endif // CONFIG_ESP_SYSTEM_MEMPROT_FEATURE +#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD + j _panic_handler /* 27: ETS_ASSIST_DEBUG_INUM panic-interrupt (soc-level panic) */ +#else + j _interrupt_handler /* 27: interrupt-handler */ +#endif // CONFIG_ESP_SYSTEM_HW_STACK_GUARD + .rept (ETS_MAX_INUM - ETS_ASSIST_DEBUG_INUM) + j _interrupt_handler /* remain entries are identical, all pointing to the interrupt handler */ .endr .option pop @@ -241,9 +249,6 @@ _interrupt_handler: /* Save SP */ sw t0, RV_STK_SP(sp) - /* Before doing anythig preserve the stack pointer */ - /* It will be saved in current TCB, if needed */ - mv a0, sp call rtos_int_enter /* If this is a non-nested interrupt, SP now points to the interrupt stack */ @@ -305,13 +310,7 @@ _interrupt_handler: sw s3, 0(t0) fence - /* Yield to the next task is needed: */ - mv a0, sp call rtos_int_exit - /* If this is a non-nested interrupt, context switch called, SP now points to back to task stack. */ - - /* The next (or current) stack pointer is returned in a0 */ - mv sp, a0 /* restore the rest of the registers */ csrw mcause, s1 diff --git a/components/soc/esp32c2/include/soc/Kconfig.soc_caps.in b/components/soc/esp32c2/include/soc/Kconfig.soc_caps.in index 650107b7eb..7041a4918b 100644 --- a/components/soc/esp32c2/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32c2/include/soc/Kconfig.soc_caps.in @@ -91,6 +91,10 @@ config SOC_CLK_TREE_SUPPORTED bool default y +config SOC_ASSIST_DEBUG_SUPPORTED + bool + default y + config SOC_XTAL_SUPPORT_26M bool default y diff --git a/components/soc/esp32c2/include/soc/assist_debug_reg.h b/components/soc/esp32c2/include/soc/assist_debug_reg.h index 960a91e20a..2734f5e298 100644 --- a/components/soc/esp32c2/include/soc/assist_debug_reg.h +++ b/components/soc/esp32c2/include/soc/assist_debug_reg.h @@ -5,7 +5,6 @@ */ #pragma once -#include #include "soc/soc.h" #ifdef __cplusplus extern "C" { @@ -15,77 +14,77 @@ extern "C" { * core0 monitor enable configuration register */ #define ASSIST_DEBUG_CORE_0_INTR_ENA_REG (DR_REG_ASSIST_DEBUG_BASE + 0x0) -/** ASSIST_DEBUG_CORE_0_AREA_DRAM0_0_RD_ENA : R/W; bitpos: [0]; default: 0; +/** ASSIST_DEBUG_CORE_0_SP_SPILL_MIN_ENA : R/W; bitpos: [0]; default: 0; * enbale sp underlow monitor */ -#define ASSIST_DEBUG_CORE_0_AREA_DRAM0_0_RD_ENA (BIT(0)) -#define ASSIST_DEBUG_CORE_0_AREA_DRAM0_0_RD_ENA_M (ASSIST_DEBUG_CORE_0_AREA_DRAM0_0_RD_ENA_V << ASSIST_DEBUG_CORE_0_AREA_DRAM0_0_RD_ENA_S) -#define ASSIST_DEBUG_CORE_0_AREA_DRAM0_0_RD_ENA_V 0x00000001U -#define ASSIST_DEBUG_CORE_0_AREA_DRAM0_0_RD_ENA_S 0 -/** ASSIST_DEBUG_CORE_0_AREA_DRAM0_0_WR_ENA : R/W; bitpos: [1]; default: 0; +#define ASSIST_DEBUG_CORE_0_SP_SPILL_MIN_ENA (BIT(0)) +#define ASSIST_DEBUG_CORE_0_SP_SPILL_MIN_ENA_M (ASSIST_DEBUG_CORE_0_SP_SPILL_MIN_ENA_V << ASSIST_DEBUG_CORE_0_SP_SPILL_MIN_ENA_S) +#define ASSIST_DEBUG_CORE_0_SP_SPILL_MIN_ENA_V 0x00000001U +#define ASSIST_DEBUG_CORE_0_SP_SPILL_MIN_ENA_S 0 +/** ASSIST_DEBUG_CORE_0_SP_SPILL_MAX_ENA : R/W; bitpos: [1]; default: 0; * enbale sp overflow monitor */ -#define ASSIST_DEBUG_CORE_0_AREA_DRAM0_0_WR_ENA (BIT(1)) -#define ASSIST_DEBUG_CORE_0_AREA_DRAM0_0_WR_ENA_M (ASSIST_DEBUG_CORE_0_AREA_DRAM0_0_WR_ENA_V << ASSIST_DEBUG_CORE_0_AREA_DRAM0_0_WR_ENA_S) -#define ASSIST_DEBUG_CORE_0_AREA_DRAM0_0_WR_ENA_V 0x00000001U -#define ASSIST_DEBUG_CORE_0_AREA_DRAM0_0_WR_ENA_S 1 +#define ASSIST_DEBUG_CORE_0_SP_SPILL_MAX_ENA (BIT(1)) +#define ASSIST_DEBUG_CORE_0_SP_SPILL_MAX_ENA_M (ASSIST_DEBUG_CORE_0_SP_SPILL_MAX_ENA_V << ASSIST_DEBUG_CORE_0_SP_SPILL_MAX_ENA_S) +#define ASSIST_DEBUG_CORE_0_SP_SPILL_MAX_ENA_V 0x00000001U +#define ASSIST_DEBUG_CORE_0_SP_SPILL_MAX_ENA_S 1 /** ASSIST_DEBUG_CORE_0_INTR_RAW_REG register * core0 monitor interrupt status register */ #define ASSIST_DEBUG_CORE_0_INTR_RAW_REG (DR_REG_ASSIST_DEBUG_BASE + 0x4) -/** ASSIST_DEBUG_CORE_0_AREA_DRAM0_0_RD_RAW : RO; bitpos: [0]; default: 0; +/** ASSIST_DEBUG_CORE_0_SP_SPILL_MIN_RAW : RO; bitpos: [0]; default: 0; * sp underlow monitor interrupt status register */ -#define ASSIST_DEBUG_CORE_0_AREA_DRAM0_0_RD_RAW (BIT(0)) -#define ASSIST_DEBUG_CORE_0_AREA_DRAM0_0_RD_RAW_M (ASSIST_DEBUG_CORE_0_AREA_DRAM0_0_RD_RAW_V << ASSIST_DEBUG_CORE_0_AREA_DRAM0_0_RD_RAW_S) -#define ASSIST_DEBUG_CORE_0_AREA_DRAM0_0_RD_RAW_V 0x00000001U -#define ASSIST_DEBUG_CORE_0_AREA_DRAM0_0_RD_RAW_S 0 -/** ASSIST_DEBUG_CORE_0_AREA_DRAM0_0_WR_RAW : RO; bitpos: [1]; default: 0; +#define ASSIST_DEBUG_CORE_0_SP_SPILL_MIN_RAW (BIT(0)) +#define ASSIST_DEBUG_CORE_0_SP_SPILL_MIN_RAW_M (ASSIST_DEBUG_CORE_0_SP_SPILL_MIN_RAW_V << ASSIST_DEBUG_CORE_0_SP_SPILL_MIN_RAW_S) +#define ASSIST_DEBUG_CORE_0_SP_SPILL_MIN_RAW_V 0x00000001U +#define ASSIST_DEBUG_CORE_0_SP_SPILL_MIN_RAW_S 0 +/** ASSIST_DEBUG_CORE_0_SP_SPILL_MAX_RAW : RO; bitpos: [1]; default: 0; * sp overflow monitor interupt status register */ -#define ASSIST_DEBUG_CORE_0_AREA_DRAM0_0_WR_RAW (BIT(1)) -#define ASSIST_DEBUG_CORE_0_AREA_DRAM0_0_WR_RAW_M (ASSIST_DEBUG_CORE_0_AREA_DRAM0_0_WR_RAW_V << ASSIST_DEBUG_CORE_0_AREA_DRAM0_0_WR_RAW_S) -#define ASSIST_DEBUG_CORE_0_AREA_DRAM0_0_WR_RAW_V 0x00000001U -#define ASSIST_DEBUG_CORE_0_AREA_DRAM0_0_WR_RAW_S 1 +#define ASSIST_DEBUG_CORE_0_SP_SPILL_MAX_RAW (BIT(1)) +#define ASSIST_DEBUG_CORE_0_SP_SPILL_MAX_RAW_M (ASSIST_DEBUG_CORE_0_SP_SPILL_MAX_RAW_V << ASSIST_DEBUG_CORE_0_SP_SPILL_MAX_RAW_S) +#define ASSIST_DEBUG_CORE_0_SP_SPILL_MAX_RAW_V 0x00000001U +#define ASSIST_DEBUG_CORE_0_SP_SPILL_MAX_RAW_S 1 /** ASSIST_DEBUG_CORE_0_INTR_RLS_REG register * core0 monitor interrupt enable register */ #define ASSIST_DEBUG_CORE_0_INTR_RLS_REG (DR_REG_ASSIST_DEBUG_BASE + 0x8) -/** ASSIST_DEBUG_CORE_0_AREA_DRAM0_0_RD_RLS : R/W; bitpos: [0]; default: 0; +/** ASSIST_DEBUG_CORE_0_SP_SPILL_MIN_RLS : R/W; bitpos: [0]; default: 0; * enbale sp underlow monitor interrupt */ -#define ASSIST_DEBUG_CORE_0_AREA_DRAM0_0_RD_RLS (BIT(0)) -#define ASSIST_DEBUG_CORE_0_AREA_DRAM0_0_RD_RLS_M (ASSIST_DEBUG_CORE_0_AREA_DRAM0_0_RD_RLS_V << ASSIST_DEBUG_CORE_0_AREA_DRAM0_0_RD_RLS_S) -#define ASSIST_DEBUG_CORE_0_AREA_DRAM0_0_RD_RLS_V 0x00000001U -#define ASSIST_DEBUG_CORE_0_AREA_DRAM0_0_RD_RLS_S 0 -/** ASSIST_DEBUG_CORE_0_AREA_DRAM0_0_WR_RLS : R/W; bitpos: [1]; default: 0; +#define ASSIST_DEBUG_CORE_0_SP_SPILL_MIN_RLS (BIT(0)) +#define ASSIST_DEBUG_CORE_0_SP_SPILL_MIN_RLS_M (ASSIST_DEBUG_CORE_0_SP_SPILL_MIN_RLS_V << ASSIST_DEBUG_CORE_0_SP_SPILL_MIN_RLS_S) +#define ASSIST_DEBUG_CORE_0_SP_SPILL_MIN_RLS_V 0x00000001U +#define ASSIST_DEBUG_CORE_0_SP_SPILL_MIN_RLS_S 0 +/** ASSIST_DEBUG_CORE_0_SP_SPILL_MAX_RLS : R/W; bitpos: [1]; default: 0; * enbale sp overflow monitor interrupt */ -#define ASSIST_DEBUG_CORE_0_AREA_DRAM0_0_WR_RLS (BIT(1)) -#define ASSIST_DEBUG_CORE_0_AREA_DRAM0_0_WR_RLS_M (ASSIST_DEBUG_CORE_0_AREA_DRAM0_0_WR_RLS_V << ASSIST_DEBUG_CORE_0_AREA_DRAM0_0_WR_RLS_S) -#define ASSIST_DEBUG_CORE_0_AREA_DRAM0_0_WR_RLS_V 0x00000001U -#define ASSIST_DEBUG_CORE_0_AREA_DRAM0_0_WR_RLS_S 1 +#define ASSIST_DEBUG_CORE_0_SP_SPILL_MAX_RLS (BIT(1)) +#define ASSIST_DEBUG_CORE_0_SP_SPILL_MAX_RLS_M (ASSIST_DEBUG_CORE_0_SP_SPILL_MAX_RLS_V << ASSIST_DEBUG_CORE_0_SP_SPILL_MAX_RLS_S) +#define ASSIST_DEBUG_CORE_0_SP_SPILL_MAX_RLS_V 0x00000001U +#define ASSIST_DEBUG_CORE_0_SP_SPILL_MAX_RLS_S 1 /** ASSIST_DEBUG_CORE_0_INTR_CLR_REG register * core0 monitor interrupt clr register */ #define ASSIST_DEBUG_CORE_0_INTR_CLR_REG (DR_REG_ASSIST_DEBUG_BASE + 0xc) -/** ASSIST_DEBUG_CORE_0_AREA_DRAM0_0_RD_CLR : WT; bitpos: [0]; default: 0; +/** ASSIST_DEBUG_CORE_0_SP_SPILL_MIN_CLR : WT; bitpos: [0]; default: 0; * clr sp underlow monitor interrupt */ -#define ASSIST_DEBUG_CORE_0_AREA_DRAM0_0_RD_CLR (BIT(0)) -#define ASSIST_DEBUG_CORE_0_AREA_DRAM0_0_RD_CLR_M (ASSIST_DEBUG_CORE_0_AREA_DRAM0_0_RD_CLR_V << ASSIST_DEBUG_CORE_0_AREA_DRAM0_0_RD_CLR_S) -#define ASSIST_DEBUG_CORE_0_AREA_DRAM0_0_RD_CLR_V 0x00000001U -#define ASSIST_DEBUG_CORE_0_AREA_DRAM0_0_RD_CLR_S 0 -/** ASSIST_DEBUG_CORE_0_AREA_DRAM0_0_WR_CLR : WT; bitpos: [1]; default: 0; +#define ASSIST_DEBUG_CORE_0_SP_SPILL_MIN_CLR (BIT(0)) +#define ASSIST_DEBUG_CORE_0_SP_SPILL_MIN_CLR_M (ASSIST_DEBUG_CORE_0_SP_SPILL_MIN_CLR_V << ASSIST_DEBUG_CORE_0_SP_SPILL_MIN_CLR_S) +#define ASSIST_DEBUG_CORE_0_SP_SPILL_MIN_CLR_V 0x00000001U +#define ASSIST_DEBUG_CORE_0_SP_SPILL_MIN_CLR_S 0 +/** ASSIST_DEBUG_CORE_0_SP_SPILL_MAX_CLR : WT; bitpos: [1]; default: 0; * clr sp overflow monitor interrupt */ -#define ASSIST_DEBUG_CORE_0_AREA_DRAM0_0_WR_CLR (BIT(1)) -#define ASSIST_DEBUG_CORE_0_AREA_DRAM0_0_WR_CLR_M (ASSIST_DEBUG_CORE_0_AREA_DRAM0_0_WR_CLR_V << ASSIST_DEBUG_CORE_0_AREA_DRAM0_0_WR_CLR_S) -#define ASSIST_DEBUG_CORE_0_AREA_DRAM0_0_WR_CLR_V 0x00000001U -#define ASSIST_DEBUG_CORE_0_AREA_DRAM0_0_WR_CLR_S 1 +#define ASSIST_DEBUG_CORE_0_SP_SPILL_MAX_CLR (BIT(1)) +#define ASSIST_DEBUG_CORE_0_SP_SPILL_MAX_CLR_M (ASSIST_DEBUG_CORE_0_SP_SPILL_MAX_CLR_V << ASSIST_DEBUG_CORE_0_SP_SPILL_MAX_CLR_S) +#define ASSIST_DEBUG_CORE_0_SP_SPILL_MAX_CLR_V 0x00000001U +#define ASSIST_DEBUG_CORE_0_SP_SPILL_MAX_CLR_S 1 /** ASSIST_DEBUG_CORE_0_SP_MIN_REG register * stack min value diff --git a/components/soc/esp32c2/include/soc/periph_defs.h b/components/soc/esp32c2/include/soc/periph_defs.h index 68344e08d1..fc3e9b5602 100644 --- a/components/soc/esp32c2/include/soc/periph_defs.h +++ b/components/soc/esp32c2/include/soc/periph_defs.h @@ -34,6 +34,7 @@ typedef enum { PERIPH_SARADC_MODULE, PERIPH_TEMPSENSOR_MODULE, PERIPH_MODEM_RPA_MODULE, + PERIPH_ASSIST_DEBUG_MODULE, PERIPH_MODULE_MAX } periph_module_t; diff --git a/components/soc/esp32c2/include/soc/soc.h b/components/soc/esp32c2/include/soc/soc.h index 038ecf3816..b7f448fe01 100644 --- a/components/soc/esp32c2/include/soc/soc.h +++ b/components/soc/esp32c2/include/soc/soc.h @@ -209,12 +209,11 @@ //On RISC-V CPUs, the interrupt sources are all external interrupts, whose type, source and priority are configured by SW. //There is no HW NMI conception. SW should controlled the masked levels through INT_THRESH_REG. -//CPU0 Interrupt number reserved in riscv/vector.S, not touch this. - +//CPU0 Interrupt numbers used in components/riscv/vectors.S. Change it's logic if modifying #define ETS_T0_WDT_INUM 24 #define ETS_CACHEERR_INUM 25 #define ETS_MEMPROT_ERR_INUM 26 -#define ETS_DPORT_INUM 28 +#define ETS_ASSIST_DEBUG_INUM 27 // Note: this interrupt can be combined with others (e.g., CACHEERR), as we can identify its trigger is activated //CPU0 Max valid interrupt number #define ETS_MAX_INUM 31 diff --git a/components/soc/esp32c2/include/soc/soc_caps.h b/components/soc/esp32c2/include/soc/soc_caps.h index 40e7963399..b1663fea31 100644 --- a/components/soc/esp32c2/include/soc/soc_caps.h +++ b/components/soc/esp32c2/include/soc/soc_caps.h @@ -16,7 +16,7 @@ * If this file is changed the script will automatically run the script * and generate the kconfig variables as part of the pre-commit hooks. * - * It can also be ran manually with `./tools/gen_soc_caps_kconfig/gen_soc_caps_kconfig.py 'components/soc/esp32c3/include/soc/'` + * It can also be ran manually with `./tools/gen_soc_caps_kconfig/gen_soc_caps_kconfig.py -d 'components/soc/esp32c2/include/soc/'` * * For more information see `tools/gen_soc_caps_kconfig/README.md` * @@ -47,6 +47,7 @@ #define SOC_SYSTIMER_SUPPORTED 1 #define SOC_BOD_SUPPORTED 1 #define SOC_CLK_TREE_SUPPORTED 1 +#define SOC_ASSIST_DEBUG_SUPPORTED 1 /*-------------------------- XTAL CAPS ---------------------------------------*/ #define SOC_XTAL_SUPPORT_26M 1 diff --git a/components/soc/esp32c3/include/soc/Kconfig.soc_caps.in b/components/soc/esp32c3/include/soc/Kconfig.soc_caps.in index b48f9d0b98..e0eb140787 100644 --- a/components/soc/esp32c3/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32c3/include/soc/Kconfig.soc_caps.in @@ -143,6 +143,10 @@ config SOC_CLK_TREE_SUPPORTED bool default y +config SOC_ASSIST_DEBUG_SUPPORTED + bool + default y + config SOC_XTAL_SUPPORT_40M bool default y diff --git a/components/soc/esp32c3/include/soc/periph_defs.h b/components/soc/esp32c3/include/soc/periph_defs.h index 0ff884736c..b066b2962b 100644 --- a/components/soc/esp32c3/include/soc/periph_defs.h +++ b/components/soc/esp32c3/include/soc/periph_defs.h @@ -39,6 +39,7 @@ typedef enum { PERIPH_SYSTIMER_MODULE, PERIPH_SARADC_MODULE, PERIPH_TEMPSENSOR_MODULE, + PERIPH_ASSIST_DEBUG_MODULE, PERIPH_MODULE_MAX } periph_module_t; diff --git a/components/soc/esp32c3/include/soc/soc.h b/components/soc/esp32c3/include/soc/soc.h index 1fb082c844..4a0880804e 100644 --- a/components/soc/esp32c3/include/soc/soc.h +++ b/components/soc/esp32c3/include/soc/soc.h @@ -209,10 +209,12 @@ //On RISC-V CPUs, the interrupt sources are all external interrupts, whose type, source and priority are configured by SW. //There is no HW NMI conception. SW should controlled the masked levels through INT_THRESH_REG. -//CPU0 Interrupt number reserved in riscv/vector.S, not touch this. +//CPU0 Interrupt numbers used in components/riscv/vectors.S. Change it's logic if modifying #define ETS_T1_WDT_INUM 24 #define ETS_CACHEERR_INUM 25 #define ETS_MEMPROT_ERR_INUM 26 +#define ETS_ASSIST_DEBUG_INUM 27 // Note: this interrupt can be combined with others (e.g., CACHEERR), as we can identify its trigger is activated + //CPU0 Max valid interrupt number #define ETS_MAX_INUM 31 diff --git a/components/soc/esp32c3/include/soc/soc_caps.h b/components/soc/esp32c3/include/soc/soc_caps.h index 32dc7519d9..52d8431186 100644 --- a/components/soc/esp32c3/include/soc/soc_caps.h +++ b/components/soc/esp32c3/include/soc/soc_caps.h @@ -16,7 +16,7 @@ * If this file is changed the script will automatically run the script * and generate the kconfig variables as part of the pre-commit hooks. * - * It can also be ran manually with `./tools/gen_soc_caps_kconfig/gen_soc_caps_kconfig.py 'components/soc/esp32c3/include/soc/'` + * It can also be ran manually with `./tools/gen_soc_caps_kconfig/gen_soc_caps_kconfig.py -d 'components/soc/esp32c3/include/soc/'` * * For more information see `tools/gen_soc_caps_kconfig/README.md` * @@ -63,6 +63,7 @@ #define SOC_MEMPROT_SUPPORTED 1 #define SOC_BOD_SUPPORTED 1 #define SOC_CLK_TREE_SUPPORTED 1 +#define SOC_ASSIST_DEBUG_SUPPORTED 1 /*-------------------------- XTAL CAPS ---------------------------------------*/ #define SOC_XTAL_SUPPORT_40M 1 diff --git a/components/soc/esp32c6/include/soc/Kconfig.soc_caps.in b/components/soc/esp32c6/include/soc/Kconfig.soc_caps.in index 402e01d122..a3e54d240a 100644 --- a/components/soc/esp32c6/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32c6/include/soc/Kconfig.soc_caps.in @@ -195,6 +195,10 @@ config SOC_CLK_TREE_SUPPORTED bool default y +config SOC_ASSIST_DEBUG_SUPPORTED + bool + default y + config SOC_XTAL_SUPPORT_40M bool default y diff --git a/components/soc/esp32c6/include/soc/assist_debug_reg.h b/components/soc/esp32c6/include/soc/assist_debug_reg.h index e77c2575d6..8e0426dc6f 100644 --- a/components/soc/esp32c6/include/soc/assist_debug_reg.h +++ b/components/soc/esp32c6/include/soc/assist_debug_reg.h @@ -1,11 +1,10 @@ /** - * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include #include "soc/soc.h" #ifdef __cplusplus extern "C" { diff --git a/components/soc/esp32c6/include/soc/periph_defs.h b/components/soc/esp32c6/include/soc/periph_defs.h index c599db7fae..d4ef7c9663 100644 --- a/components/soc/esp32c6/include/soc/periph_defs.h +++ b/components/soc/esp32c6/include/soc/periph_defs.h @@ -48,6 +48,7 @@ typedef enum { PERIPH_IEEE802154_MODULE, PERIPH_COEX_MODULE, PERIPH_PHY_MODULE, + PERIPH_ASSIST_DEBUG_MODULE, PERIPH_MODULE_MAX } periph_module_t; diff --git a/components/soc/esp32c6/include/soc/soc.h b/components/soc/esp32c6/include/soc/soc.h index 70e3a947f4..b3a5a3abb2 100644 --- a/components/soc/esp32c6/include/soc/soc.h +++ b/components/soc/esp32c6/include/soc/soc.h @@ -214,10 +214,12 @@ //On RISC-V CPUs, the interrupt sources are all external interrupts, whose type, source and priority are configured by SW. //There is no HW NMI conception. SW should controlled the masked levels through INT_THRESH_REG. -//CPU0 Interrupt number reserved in riscv/vector.S, not touch this. +//CPU0 Interrupt numbers used in components/riscv/vectors.S. Change it's logic if modifying #define ETS_T1_WDT_INUM 24 #define ETS_CACHEERR_INUM 25 #define ETS_MEMPROT_ERR_INUM 26 +#define ETS_ASSIST_DEBUG_INUM 27 // Note: this interrupt can be combined with others (e.g., CACHEERR), as we can identify its trigger is activated + //CPU0 Max valid interrupt number #define ETS_MAX_INUM 31 diff --git a/components/soc/esp32c6/include/soc/soc_caps.h b/components/soc/esp32c6/include/soc/soc_caps.h index 683c9465fc..e93e64db8b 100644 --- a/components/soc/esp32c6/include/soc/soc_caps.h +++ b/components/soc/esp32c6/include/soc/soc_caps.h @@ -16,7 +16,7 @@ * If this file is changed the script will automatically run the script * and generate the kconfig variables as part of the pre-commit hooks. * - * It can also be ran manually with `./tools/gen_soc_caps_kconfig/gen_soc_caps_kconfig.py 'components/soc/esp32c6/include/soc/'` + * It can also be ran manually with `./tools/gen_soc_caps_kconfig/gen_soc_caps_kconfig.py -d 'components/soc/esp32c6/include/soc/'` * * For more information see `tools/gen_soc_caps_kconfig/README.md` * @@ -73,6 +73,7 @@ #define SOC_LP_PERIPHERALS_SUPPORTED 1 #define SOC_LP_I2C_SUPPORTED 1 #define SOC_CLK_TREE_SUPPORTED 1 +#define SOC_ASSIST_DEBUG_SUPPORTED 1 /*-------------------------- XTAL CAPS ---------------------------------------*/ #define SOC_XTAL_SUPPORT_40M 1 diff --git a/components/soc/esp32h2/include/soc/Kconfig.soc_caps.in b/components/soc/esp32h2/include/soc/Kconfig.soc_caps.in index 9e37c11849..3dade6c325 100644 --- a/components/soc/esp32h2/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32h2/include/soc/Kconfig.soc_caps.in @@ -179,6 +179,10 @@ config SOC_CLK_TREE_SUPPORTED bool default y +config SOC_ASSIST_DEBUG_SUPPORTED + bool + default y + config SOC_XTAL_SUPPORT_32M bool default y diff --git a/components/soc/esp32h2/include/soc/assist_debug_reg.h b/components/soc/esp32h2/include/soc/assist_debug_reg.h index e77c2575d6..8e0426dc6f 100644 --- a/components/soc/esp32h2/include/soc/assist_debug_reg.h +++ b/components/soc/esp32h2/include/soc/assist_debug_reg.h @@ -1,11 +1,10 @@ /** - * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include #include "soc/soc.h" #ifdef __cplusplus extern "C" { diff --git a/components/soc/esp32h2/include/soc/periph_defs.h b/components/soc/esp32h2/include/soc/periph_defs.h index b4f30cd754..a51c09f208 100644 --- a/components/soc/esp32h2/include/soc/periph_defs.h +++ b/components/soc/esp32h2/include/soc/periph_defs.h @@ -47,6 +47,7 @@ typedef enum { PERIPH_IEEE802154_MODULE, PERIPH_COEX_MODULE, PERIPH_PHY_MODULE, + PERIPH_ASSIST_DEBUG_MODULE, PERIPH_MODULE_MAX } periph_module_t; diff --git a/components/soc/esp32h2/include/soc/soc.h b/components/soc/esp32h2/include/soc/soc.h index 5a95925d27..a02adb9d0e 100644 --- a/components/soc/esp32h2/include/soc/soc.h +++ b/components/soc/esp32h2/include/soc/soc.h @@ -211,10 +211,12 @@ //On RISC-V CPUs, the interrupt sources are all external interrupts, whose type, source and priority are configured by SW. //There is no HW NMI conception. SW should controlled the masked levels through INT_THRESH_REG. -//CPU0 Interrupt number reserved in riscv/vector.S, not touch this. +//CPU0 Interrupt numbers used in components/riscv/vectors.S. Change it's logic if modifying #define ETS_T1_WDT_INUM 24 #define ETS_CACHEERR_INUM 25 #define ETS_MEMPROT_ERR_INUM 26 +#define ETS_ASSIST_DEBUG_INUM 27 // Note: this interrupt can be combined with others (e.g., CACHEERR), as we can identify its trigger is activated + //CPU0 Max valid interrupt number #define ETS_MAX_INUM 31 diff --git a/components/soc/esp32h2/include/soc/soc_caps.h b/components/soc/esp32h2/include/soc/soc_caps.h index cd3cc4762d..16e3c617cb 100644 --- a/components/soc/esp32h2/include/soc/soc_caps.h +++ b/components/soc/esp32h2/include/soc/soc_caps.h @@ -16,7 +16,7 @@ * If this file is changed the script will automatically run the script * and generate the kconfig variables as part of the pre-commit hooks. * - * It can also be ran manually with `./tools/gen_soc_caps_kconfig/gen_soc_caps_kconfig.py 'components/soc/esp32h2/include/soc/'` + * It can also be ran manually with `./tools/gen_soc_caps_kconfig/gen_soc_caps_kconfig.py -d 'components/soc/esp32h2/include/soc/'` * * For more information see `tools/gen_soc_caps_kconfig/README.md` * @@ -70,6 +70,7 @@ #define SOC_LP_TIMER_SUPPORTED 1 #define SOC_PAU_SUPPORTED 1 #define SOC_CLK_TREE_SUPPORTED 1 +#define SOC_ASSIST_DEBUG_SUPPORTED 1 /*-------------------------- XTAL CAPS ---------------------------------------*/ #define SOC_XTAL_SUPPORT_32M 1 diff --git a/docs/en/api-guides/fatal-errors.rst b/docs/en/api-guides/fatal-errors.rst index 7610ab4ed3..b9a1f0cfd8 100644 --- a/docs/en/api-guides/fatal-errors.rst +++ b/docs/en/api-guides/fatal-errors.rst @@ -7,7 +7,7 @@ Fatal Errors Overview -------- -In certain situations, execution of the program can not be continued in a well defined way. In ESP-IDF, these situations include: +In certain situations, the execution of the program can not be continued in a well-defined way. In ESP-IDF, these situations include: - CPU Exceptions: |CPU_EXCEPTIONS_LIST| - System level checks and safeguards: @@ -67,7 +67,7 @@ Subsequent behavior of the panic handler can be set using :ref:`CONFIG_ESP_SYSTE - Invoke dynamic GDB Stub (``ESP_SYSTEM_GDBSTUB_RUNTIME``) - Start GDB server which can communicate with GDB over console UART port. This option allows the user to debug a program at run time and set break points, alter the execution, etc. See `GDB Stub`_ for more details. + Start the GDB server which can communicate with GDB over the console UART port. This option allows the user to debug a program at run time and set breakpoints, alter the execution, etc. See `GDB Stub`_ for more details. The behavior of the panic handler is affected by three other configuration options. @@ -216,7 +216,7 @@ If :doc:`IDF Monitor ` is used, Program Counter values will b MSTATUS : 0x00001881 MTVEC : 0x40380001 MCAUSE : 0x00000007 MTVAL : 0x00000000 MHARTID : 0x00000000 - Moreover, the :doc:`IDF Monitor ` is also capable of generating and printing a backtrace thanks to the stack dump provided by the board in the panic handler. + Moreover, :doc:`IDF Monitor ` is also capable of generating and printing a backtrace thanks to the stack dump provided by the board in the panic handler. The output looks like this: :: @@ -313,7 +313,7 @@ This section explains the meaning of different error causes, printed in parens a |ILLEGAL_INSTR_MSG| ^^^^^^^^^^^^^^^^^^^ -This CPU exception indicates that the instruction which was executed was not a valid instruction. Most common reasons for this error include: +This CPU exception indicates that the instruction which was executed was not a valid instruction. The most common reasons for this error include: - FreeRTOS task function has returned. In FreeRTOS, if a task function needs to terminate, it should call :cpp:func:`vTaskDelete` and delete itself, instead of returning. @@ -361,11 +361,7 @@ This CPU exception indicates that the instruction which was executed was not a v Unhandled debug exception ^^^^^^^^^^^^^^^^^^^^^^^^^ - This will usually be followed by a message like:: - - Debug exception reason: Stack canary watchpoint triggered (task_name) - - This error indicates that the application has written past the end of the stack of the task with name ``task_name``. Note that not every stack overflow is guaranteed to trigger this error. It is possible that the task writes to memory beyond the stack canary location, in which case the watchpoint will not be triggered. + This CPU exception happens when the instruction ``BREAK`` is executed. .. only:: CONFIG_IDF_TARGET_ARCH_RISCV @@ -382,7 +378,7 @@ This CPU exception indicates that the instruction which was executed was not a v Breakpoint ^^^^^^^^^^ - This CPU exception happens when the instruction ``EBREAK`` is executed. + This CPU exception happens when the instruction ``EBREAK`` is executed. See also :ref:`FreeRTOS-End-Of-Stack-Watchpoint`. Load address misaligned, Store address misaligned ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -440,6 +436,64 @@ ESP-IDF's heap implementation contains a number of run-time checks of the heap s Consult :doc:`Heap Memory Debugging <../api-reference/system/heap_debug>` documentation for further information. +|STACK_OVERFLOW| +^^^^^^^^^^^^^^^^ + +.. only:: SOC_ASSIST_DEBUG_SUPPORTED + + Hardware Stack Guard + """""""""""""""""""" + + {IDF_TARGET_NAME} has an integrated assist-debug module that can watch the SP register to ensure that it is within the bounds of allocated stack memory. The assist-debug module needs to set new stack bounds on every interrupt handling and FreeRTOS context switch. This can have a small impact on performance. + + Here are some additional details about the assist-debug module: + + - Implemented in hardware + - Watches Stack Pointer register value + - Requires no additional CPU time or memory while watching stack bounds + + When the assist-debug module detects a stack overflow, the panic handler will run and display a message that resembles the following: + + .. parsed-literal:: + + Guru Meditation Error: Core 0 panic'ed (Stack protection fault). + + Hardware stack guard can be disabled using :ref:`CONFIG_ESP_SYSTEM_HW_STACK_GUARD` options. + +.. _FreeRTOS-End-Of-Stack-Watchpoint: + +FreeRTOS End of Stack Watchpoint +"""""""""""""""""""""""""""""""" + +ESP-IDF provides a custom FreeRTOS stack overflow detecting mechanism based on watchpoints. Every time FreeRTOS switches task context, one of the watchpoints is set to watch the last 32 bytes of stack. + +Generally, this may cause the watchpoint to be triggered up to 28 bytes earlier than expected. The value 32 is chosen because it is larger than the stack canary size in FreeRTOS (20 bytes). Adopting this approach ensures that the watchpoint triggers before the stack canary is corrupted, not after. + +.. note:: + Not every stack overflow is guaranteed to trigger the watchpoint. It is possible that the task writes to memory beyond the stack canary location, in which case the watchpoint will not be triggered. + +If watchpoint triggers, the message will be similar to: + +.. only:: CONFIG_IDF_TARGET_ARCH_XTENSA + + :: + + Debug exception reason: Stack canary watchpoint triggered (task_name) + +.. only:: CONFIG_IDF_TARGET_ARCH_RISCV + + :: + + Guru Meditation Error: Core 0 panic'ed (Breakpoint). Exception was unhandled. + +This feature can be enabled by using the :ref:`CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK` option. + + +FreeRTOS Stack Checks +""""""""""""""""""""" + +See :ref:`CONFIG_FREERTOS_CHECK_STACKOVERFLOW` + Stack Smashing ^^^^^^^^^^^^^^ @@ -459,12 +513,14 @@ The backtrace should point to the function where stack smashing has occurred. Ch .. |CPU_EXCEPTIONS_LIST| replace:: Illegal Instruction, Load/Store Alignment Error, Load/Store Prohibited error, Double Exception. .. |ILLEGAL_INSTR_MSG| replace:: IllegalInstruction .. |CACHE_ERR_MSG| replace:: Cache disabled but cached memory region accessed + .. |STACK_OVERFLOW| replace:: Stack overflow .. only:: CONFIG_IDF_TARGET_ARCH_RISCV .. |CPU_EXCEPTIONS_LIST| replace:: Illegal Instruction, Load/Store Alignment Error, Load/Store Prohibited error. .. |ILLEGAL_INSTR_MSG| replace:: Illegal instruction .. |CACHE_ERR_MSG| replace:: Cache error + .. |STACK_OVERFLOW| replace:: Stack overflow Undefined Behavior Sanitizer (UBSAN) Checks ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/docs/en/api-guides/performance/speed.rst b/docs/en/api-guides/performance/speed.rst index 62be872700..76fb398cd2 100644 --- a/docs/en/api-guides/performance/speed.rst +++ b/docs/en/api-guides/performance/speed.rst @@ -82,6 +82,7 @@ The following optimizations improve the execution of nearly all code, including :esp32: - Set :ref:`CONFIG_ESPTOOLPY_FLASHFREQ` to 80 MHz. This is double the 40 MHz default value and doubles the speed at which code is loaded or executed from flash. You should verify that the board or module that connects the {IDF_TARGET_NAME} to the flash chip is rated for 80 MHz operation at the relevant temperature ranges before changing this setting. This information is contained in the hardware datasheet(s). - Set :ref:`CONFIG_ESPTOOLPY_FLASHMODE` to QIO or QOUT mode (Quad I/O). Both almost double the speed at which code is loaded or executed from flash compared to the default DIO mode. QIO is slightly faster than QOUT if both are supported. Note that both the flash chip model, and the electrical connections between the {IDF_TARGET_NAME} and the flash chip must support quad I/O modes or the SoC will not work correctly. - Set :ref:`CONFIG_COMPILER_OPTIMIZATION` to ``Optimize for performance (-O2)`` . This may slightly increase binary size compared to the default setting, but almost certainly increases the performance of some code. Note that if your code contains C or C++ Undefined Behavior, then increasing the compiler optimization level may expose bugs that otherwise are not seen. + :SOC_ASSIST_DEBUG_SUPPORTED: - Set :ref:`CONFIG_ESP_SYSTEM_HW_STACK_GUARD` to disabled. This may slightly increase the performance of some code, especially in cases where a lot of interrupts occur on the device. :esp32: - If the application uses PSRAM and is based on ESP32 rev. 3 (ECO3), setting :ref:`CONFIG_ESP32_REV_MIN` to ``3`` disables PSRAM bug workarounds, reducing the code size and improving overall performance. :SOC_CPU_HAS_FPU: - Avoid using floating point arithmetic ``float``. Even though {IDF_TARGET_NAME} has a single precision hardware floating point unit, floating point calculations are always slower than integer calculations. If possible then use fixed point representations, a different method of integer representation, or convert part of the calculation to be integer only before switching to floating point. :not SOC_CPU_HAS_FPU: - Avoid using floating point arithmetic ``float``. On {IDF_TARGET_NAME} these calculations are emulated in software and are very slow. If possible, use fixed point representations, a different method of integer representation, or convert part of the calculation to be integer only before switching to floating point. diff --git a/docs/zh_CN/api-guides/fatal-errors.rst b/docs/zh_CN/api-guides/fatal-errors.rst index ff12ca5fbe..bcfc4139cf 100644 --- a/docs/zh_CN/api-guides/fatal-errors.rst +++ b/docs/zh_CN/api-guides/fatal-errors.rst @@ -361,11 +361,7 @@ Guru Meditation 错误 Unhandled debug exception ^^^^^^^^^^^^^^^^^^^^^^^^^ - 这后面通常会再跟一条消息:: - - Debug exception reason: Stack canary watchpoint triggered (task_name) - - 此错误表示应用程序写入的位置越过了 ``task_name`` 任务堆栈的末尾,请注意,并非每次堆栈溢出都会触发此错误。任务有可能会绕过堆栈金丝雀(stack canary)的位置访问内存,在这种情况下,监视点就不会被触发。 + 执行指令 ``BREAK`` 时,会发生此 CPU 异常。 .. only:: CONFIG_IDF_TARGET_ARCH_RISCV @@ -382,7 +378,7 @@ Guru Meditation 错误 Breakpoint ^^^^^^^^^^ - 当执行 ``EBREAK`` 指令时,会发生此 CPU 异常。 + 执行 ``EBREAK`` 指令时,会发生此 CPU 异常。请参见 :ref:`FreeRTOS-End-Of-Stack-Watchpoint`。 Load address misaligned, Store address misaligned ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -440,6 +436,64 @@ ESP-IDF 堆的实现包含许多运行时的堆结构检查,可以在 menuconf 更多详细信息,请查阅 :doc:`堆内存调试 <../api-reference/system/heap_debug>` 文档。 +|STACK_OVERFLOW| +^^^^^^^^^^^^^^^^ + +.. only:: SOC_ASSIST_DEBUG_SUPPORTED + + 硬件堆栈保护 + """""""""""""""""""" + + {IDF_TARGET_NAME} 集成了辅助调试模块,支持监测堆栈指针 (SP) 寄存器,确保其值位于已分配给堆栈的内存范围内。发生中断处理或 FreeRTOS 切换上下文时,辅助调试模块都会设置新的堆栈监测范围。注意,该操作会对性能产生一定影响。 + + 以下为辅助调试模块的部分相关特性: + + - 采用硬件实现 + - 支持监测堆栈指针寄存器的值 + - 无需占用额外 CPU 时间或内存,即可监测堆栈内存范围 + + 当辅助调试模块检测到堆栈溢出时,将触发紧急处理程序并打印类似如下信息: + + .. parsed-literal:: + + Guru Meditation Error: Core 0 panic'ed (Stack protection fault). + + 可以通过 :ref:`CONFIG_ESP_SYSTEM_HW_STACK_GUARD` 选项,禁用硬件堆栈保护。 + +.. _FreeRTOS-End-Of-Stack-Watchpoint: + +FreeRTOS 任务堆栈末尾监视点 +"""""""""""""""""""""""""""""""" + +ESP-IDF 支持基于监视点的 FreeRTOS 堆栈溢出检测机制。每次 FreeRTOS 切换任务上下文时,都会设置一个监视点,用于监视堆栈的最后 32 字节。 + +通常,该设置会提前触发监视点,触发点可能会比预期提前多达 28 字节。基于 FreeRTOS 中堆栈金丝雀的大小为 20 字节,故将观察范围设置为 32 字节,确保可以在堆栈金丝雀遭到破坏前及时触发监测点。 + +.. note:: + 并非每次堆栈溢出都能触发监视点。如果任务绕过堆栈金丝雀的位置访问堆栈,则无法触发监视点。 + +监视点触发后,将打印类似如下信息: + +.. only:: CONFIG_IDF_TARGET_ARCH_XTENSA + + :: + + Debug exception reason: Stack canary watchpoint triggered (task_name) + +.. only:: CONFIG_IDF_TARGET_ARCH_RISCV + + :: + + Guru Meditation Error: Core 0 panic'ed (Breakpoint). Exception was unhandled. + +可以通过 :ref:`CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK` 选项启用该功能。 + + +FreeRTOS 堆栈检查 +""""""""""""""""""""" + +请参见 :ref:`CONFIG_FREERTOS_CHECK_STACKOVERFLOW`。 + 堆栈粉碎 ^^^^^^^^^^ @@ -457,16 +511,18 @@ ESP-IDF 堆的实现包含许多运行时的堆结构检查,可以在 menuconf .. only:: CONFIG_IDF_TARGET_ARCH_XTENSA .. |CPU_EXCEPTIONS_LIST| replace:: 非法指令,加载/存储时的内存对齐错误,加载/存储时的访问权限错误,双重异常。 - .. |ILLEGAL_INSTR_MSG| replace:: IllegalInstruction - .. |CACHE_ERR_MSG| replace:: Cache disabled but cached memory region accessed + .. |ILLEGAL_INSTR_MSG| replace:: 非法指令 + .. |CACHE_ERR_MSG| replace:: cache 已禁用,但仍可访问缓存内存区域 + .. |STACK_OVERFLOW| replace:: 堆栈溢出 .. only:: CONFIG_IDF_TARGET_ARCH_RISCV .. |CPU_EXCEPTIONS_LIST| replace:: 非法指令,加载/存储时的内存对齐错误,加载/存储时的访问权限错误。 - .. |ILLEGAL_INSTR_MSG| replace:: Illegal instruction - .. |CACHE_ERR_MSG| replace:: Cache error + .. |ILLEGAL_INSTR_MSG| replace:: 非法指令 + .. |CACHE_ERR_MSG| replace:: cache 错误 + .. |STACK_OVERFLOW| replace:: 堆栈溢出 -未定义行为清理器(UBSAN)检查 +未定义行为清理器 (UBSAN) 检查 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 未定义行为清理器 (UBSAN) 是一种编译器功能,它会为可能不正确的操作添加运行时检查,例如: diff --git a/tools/test_apps/system/panic/main/CMakeLists.txt b/tools/test_apps/system/panic/main/CMakeLists.txt index 786768666c..dea3633295 100644 --- a/tools/test_apps/system/panic/main/CMakeLists.txt +++ b/tools/test_apps/system/panic/main/CMakeLists.txt @@ -15,3 +15,6 @@ idf_component_register(SRCS "${srcs}" INCLUDE_DIRS "include" REQUIRES spi_flash esp_psram esp_system esp_partition PRIV_REQUIRES esp_gdbstub) + +target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-unused-variable" + "-Wno-infinite-recursion") diff --git a/tools/test_apps/system/panic/main/include/test_panic.h b/tools/test_apps/system/panic/main/include/test_panic.h index c4be49af10..1121a62648 100644 --- a/tools/test_apps/system/panic/main/include/test_panic.h +++ b/tools/test_apps/system/panic/main/include/test_panic.h @@ -24,6 +24,8 @@ void test_int_wdt(void); void test_task_wdt_cpu0(void); +void test_hw_stack_guard_cpu0(void); + #if CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH && CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY void test_panic_extram_stack(void); #endif diff --git a/tools/test_apps/system/panic/main/test_app_main.c b/tools/test_apps/system/panic/main/test_app_main.c index f5edc684fe..d97d734173 100644 --- a/tools/test_apps/system/panic/main/test_app_main.c +++ b/tools/test_apps/system/panic/main/test_app_main.c @@ -83,6 +83,7 @@ void app_main(void) HANDLE_TEST(test_name, test_abort_cache_disabled); HANDLE_TEST(test_name, test_int_wdt); HANDLE_TEST(test_name, test_task_wdt_cpu0); + HANDLE_TEST(test_name, test_hw_stack_guard_cpu0); #if CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH && CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY HANDLE_TEST(test_name, test_panic_extram_stack); #endif diff --git a/tools/test_apps/system/panic/main/test_panic.c b/tools/test_apps/system/panic/main/test_panic.c index 19f200ad76..9c33b74cca 100644 --- a/tools/test_apps/system/panic/main/test_panic.c +++ b/tools/test_apps/system/panic/main/test_panic.c @@ -61,6 +61,13 @@ void test_task_wdt_cpu0(void) } } +__attribute__((optimize("-O0"))) +void test_hw_stack_guard_cpu0(void) +{ + uint32_t buf[128]; + test_hw_stack_guard_cpu0(); +} + #if CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH && CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY static void stack_in_extram(void* arg) { diff --git a/tools/test_apps/system/panic/pytest_panic.py b/tools/test_apps/system/panic/pytest_panic.py index 0415cc0b02..5d6b12b541 100644 --- a/tools/test_apps/system/panic/pytest_panic.py +++ b/tools/test_apps/system/panic/pytest_panic.py @@ -52,6 +52,21 @@ CONFIGS_EXTRAM_STACK = [ pytest.param('coredump_extram_stack', marks=[pytest.mark.esp32, pytest.mark.esp32s2, pytest.mark.psram, pytest.mark.esp32s3, pytest.mark.quad_psram]) ] +TARGETS_HW_STACK_GUARD = [ + pytest.mark.esp32c2, + pytest.mark.esp32c3, + pytest.mark.esp32c6, + pytest.mark.esp32h2, +] + +CONFIGS_HW_STACK_GUARD = [ + pytest.param('coredump_flash_bin_crc', marks=TARGETS_HW_STACK_GUARD), + pytest.param('coredump_uart_bin_crc', marks=TARGETS_HW_STACK_GUARD), + pytest.param('coredump_uart_elf_crc', marks=TARGETS_HW_STACK_GUARD), + pytest.param('gdbstub', marks=TARGETS_HW_STACK_GUARD), + pytest.param('panic', marks=TARGETS_HW_STACK_GUARD), +] + def get_default_backtrace(config: str) -> List[str]: return [config, 'app_main', 'main_task', 'vPortTaskWrapper'] @@ -773,3 +788,15 @@ def test_gdbstub_coredump(dut: PanicTestDut) -> None: dut.verify_gdb_backtrace(frames, get_default_backtrace(test_func_name)) dut.revert_log_level() return # don't expect "Rebooting" output below + + +@pytest.mark.parametrize('config', CONFIGS_HW_STACK_GUARD, indirect=True) +@pytest.mark.generic +def test_hw_stack_guard_cpu0(dut: PanicTestDut, config: str, test_func_name: str) -> None: + dut.run_test_func(test_func_name) + dut.expect_exact('Guru Meditation Error: Core 0 panic\'ed (Stack protection fault).') + dut.expect_none('ASSIST_DEBUG is not triggered BUT interrupt occured!') + dut.expect(r'Detected in task(.*)at 0x') + dut.expect_exact('Stack pointer: 0x') + dut.expect(r'Stack bounds: 0x(.*) - 0x') + common_test(dut, config)