Merge branch 'feat/esp_tee_hw_stack_guard' into 'master'

feat(esp_tee): Support for the RISC-V H/W stack guard with ESP-TEE enabled

Closes IDF-10770 and IDF-14350

See merge request espressif/esp-idf!43162
This commit is contained in:
Mahavir Jain
2025-11-26 09:42:38 +05:30
17 changed files with 523 additions and 79 deletions
+8 -4
View File
@@ -24,10 +24,14 @@ if(CONFIG_IDF_ENV_FPGA OR CONFIG_ESP_BRINGUP_BYPASS_RANDOM_SETTING)
endif()
if(BOOTLOADER_BUILD OR esp_tee_build)
# "_esp_error_check_failed()" requires spi_flash module
# Bootloader relies on some Kconfig options defined in esp_system.
idf_component_register(SRCS "${srcs}" REQUIRES spi_flash
INCLUDE_DIRS include)
set(include_dirs "include")
if(esp_tee_build)
list(APPEND include_dirs "port/include/private")
endif()
# "_esp_error_check_failed()" requires spi_flash module
# Bootloader relies on some Kconfig options defined in esp_system.
idf_component_register(SRCS "${srcs}" INCLUDE_DIRS "${include_dirs}"
REQUIRES spi_flash)
else()
list(APPEND srcs "crosscore_int.c"
"esp_ipc.c"
+1 -2
View File
@@ -447,8 +447,7 @@ menu "ESP System Settings"
config ESP_SYSTEM_HW_STACK_GUARD
bool "Hardware stack guard"
# TODO: [ESP-TEE] IDF-10770
depends on SOC_ASSIST_DEBUG_SUPPORTED && !SECURE_ENABLE_TEE
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
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -39,10 +39,10 @@ uint32_t esp_hw_stack_guard_get_pc(uint32_t core_id);
#define ASSIST_DEBUG_CORE_0_SP_MAX_OFFSET (ASSIST_DEBUG_CORE_0_SP_MAX_REG - DR_REG_ASSIST_DEBUG_BASE)
#define ASSIST_DEBUG_CORE_0_SP_SPILL_OFFSET (ASSIST_DEBUG_CORE_0_MONITOR_REG - DR_REG_ASSIST_DEBUG_BASE)
.macro ESP_HW_STACK_GUARD_SET_BOUNDS_CPU0 reg1
.macro ESP_HW_STACK_GUARD_SET_BOUNDS_CPU0 reg1 sp_min sp_max
lui \reg1, ASSIST_DEBUG_CORE_0_MONITOR_REG_IMM
sw a0, ASSIST_DEBUG_CORE_0_SP_MIN_OFFSET(\reg1)
sw a1, ASSIST_DEBUG_CORE_0_SP_MAX_OFFSET(\reg1)
sw \sp_min, ASSIST_DEBUG_CORE_0_SP_MIN_OFFSET(\reg1)
sw \sp_max, ASSIST_DEBUG_CORE_0_SP_MAX_OFFSET(\reg1)
.endm
.macro ESP_HW_STACK_GUARD_MONITOR_STOP_CPU0 reg1 reg2
@@ -59,6 +59,18 @@ ori \reg2, \reg2, ASSIST_DEBUG_SP_SPILL_BITS
sw \reg2, ASSIST_DEBUG_CORE_0_SP_SPILL_OFFSET(\reg1)
.endm
.macro ESP_HW_STACK_GUARD_GET_BOUNDS_CPU0 reg1 sp_min sp_max
lui \reg1, ASSIST_DEBUG_CORE_0_MONITOR_REG_IMM
lw \sp_min, ASSIST_DEBUG_CORE_0_SP_MIN_OFFSET(\reg1)
lw \sp_max, ASSIST_DEBUG_CORE_0_SP_MAX_OFFSET(\reg1)
.endm
.macro ESP_HW_STACK_GUARD_MONITOR_QUERY_CPU0 reg1 reg2
lui \reg1, ASSIST_DEBUG_CORE_0_MONITOR_REG_IMM
lw \reg2, ASSIST_DEBUG_CORE_0_SP_SPILL_OFFSET(\reg1)
andi \reg2, \reg2, ASSIST_DEBUG_SP_SPILL_BITS
.endm
#if SOC_CPU_CORES_NUM > 1
#define ASSIST_DEBUG_CORE_1_MONITOR_REG_IMM (ASSIST_DEBUG_CORE_1_MONITOR_REG >> 12)
@@ -66,10 +78,10 @@ sw \reg2, ASSIST_DEBUG_CORE_0_SP_SPILL_OFFSET(\reg1)
#define ASSIST_DEBUG_CORE_1_SP_MAX_OFFSET (ASSIST_DEBUG_CORE_1_SP_MAX_REG - DR_REG_ASSIST_DEBUG_BASE)
#define ASSIST_DEBUG_CORE_1_SP_SPILL_OFFSET (ASSIST_DEBUG_CORE_1_MONITOR_REG - DR_REG_ASSIST_DEBUG_BASE)
.macro ESP_HW_STACK_GUARD_SET_BOUNDS_CPU1 reg1
.macro ESP_HW_STACK_GUARD_SET_BOUNDS_CPU1 reg1 sp_min sp_max
lui \reg1, ASSIST_DEBUG_CORE_1_MONITOR_REG_IMM
sw a0, ASSIST_DEBUG_CORE_1_SP_MIN_OFFSET(\reg1)
sw a1, ASSIST_DEBUG_CORE_1_SP_MAX_OFFSET(\reg1)
sw \sp_min, ASSIST_DEBUG_CORE_1_SP_MIN_OFFSET(\reg1)
sw \sp_max, ASSIST_DEBUG_CORE_1_SP_MAX_OFFSET(\reg1)
.endm
.macro ESP_HW_STACK_GUARD_MONITOR_STOP_CPU1 reg1 reg2
@@ -86,16 +98,28 @@ ori \reg2, \reg2, ASSIST_DEBUG_SP_SPILL_BITS
sw \reg2, ASSIST_DEBUG_CORE_1_SP_SPILL_OFFSET(\reg1)
.endm
.macro ESP_HW_STACK_GUARD_SET_BOUNDS_CUR_CORE reg1
.macro ESP_HW_STACK_GUARD_GET_BOUNDS_CPU1 reg1 sp_min sp_max
lui \reg1, ASSIST_DEBUG_CORE_1_MONITOR_REG_IMM
lw \sp_min, ASSIST_DEBUG_CORE_1_SP_MIN_OFFSET(\reg1)
lw \sp_max, ASSIST_DEBUG_CORE_1_SP_MAX_OFFSET(\reg1)
.endm
.macro ESP_HW_STACK_GUARD_MONITOR_QUERY_CPU1 reg1 reg2
lui \reg1, ASSIST_DEBUG_CORE_1_MONITOR_REG_IMM
lw \reg2, ASSIST_DEBUG_CORE_1_SP_SPILL_OFFSET(\reg1)
andi \reg2, \reg2, ASSIST_DEBUG_SP_SPILL_BITS
.endm
.macro ESP_HW_STACK_GUARD_SET_BOUNDS_CUR_CORE reg1 sp_min sp_max
/* Check the current core ID */
csrr \reg1, mhartid
beqz \reg1, 1f
/* Core 1 */
ESP_HW_STACK_GUARD_SET_BOUNDS_CPU1 \reg1
ESP_HW_STACK_GUARD_SET_BOUNDS_CPU1 \reg1 \sp_min \sp_max
j 2f
1:
/* Core 0 */
ESP_HW_STACK_GUARD_SET_BOUNDS_CPU0 \reg1
ESP_HW_STACK_GUARD_SET_BOUNDS_CPU0 \reg1 \sp_min \sp_max
2:
.endm
@@ -125,11 +149,39 @@ ESP_HW_STACK_GUARD_MONITOR_STOP_CPU0 \reg1 \reg2
2:
.endm
.macro ESP_HW_STACK_GUARD_GET_BOUNDS_CUR_CORE reg1 sp_min sp_max
/* Check the current core ID */
csrr \reg1, mhartid
beqz \reg1, 1f
/* Core 1 */
ESP_HW_STACK_GUARD_GET_BOUNDS_CPU1 \reg1 \sp_min \sp_max
j 2f
1:
/* Core 0 */
ESP_HW_STACK_GUARD_GET_BOUNDS_CPU0 \reg1 \sp_min \sp_max
2:
.endm
.macro ESP_HW_STACK_GUARD_MONITOR_QUERY_CUR_CORE reg1 reg2
/* Check the current core ID */
csrr \reg1, mhartid
beqz \reg1, 1f
/* Core 1 */
ESP_HW_STACK_GUARD_MONITOR_QUERY_CPU1 \reg1 \reg2
j 2f
1:
/* Core 0 */
ESP_HW_STACK_GUARD_MONITOR_QUERY_CPU0 \reg1 \reg2
2:
.endm
#else // SOC_CPU_CORES_NUM <= 1
#define ESP_HW_STACK_GUARD_SET_BOUNDS_CUR_CORE ESP_HW_STACK_GUARD_SET_BOUNDS_CPU0
#define ESP_HW_STACK_GUARD_MONITOR_START_CUR_CORE ESP_HW_STACK_GUARD_MONITOR_START_CPU0
#define ESP_HW_STACK_GUARD_MONITOR_STOP_CUR_CORE ESP_HW_STACK_GUARD_MONITOR_STOP_CPU0
#define ESP_HW_STACK_GUARD_GET_BOUNDS_CUR_CORE ESP_HW_STACK_GUARD_GET_BOUNDS_CPU0
#define ESP_HW_STACK_GUARD_MONITOR_QUERY_CUR_CORE ESP_HW_STACK_GUARD_MONITOR_QUERY_CPU0
#endif // SOC_CPU_CORES_NUM > 1
@@ -688,7 +688,11 @@ esp_err_t esp_tee_sec_storage_ecdsa_sign_pbkdf2(const esp_tee_sec_storage_pbkdf2
}
int cfg_key_id = (int)CONFIG_SECURE_TEE_PBKDF2_EFUSE_HMAC_KEY_ID;
if (cfg_key_id < 0 || cfg_key_id >= HMAC_KEY_MAX) {
/* Split the checks to keep Coverity happy */
if (cfg_key_id < 0) {
return ESP_ERR_INVALID_ARG;
}
if (cfg_key_id >= HMAC_KEY_MAX) {
return ESP_ERR_INVALID_ARG;
}
@@ -9,9 +9,13 @@
/* The system interrupts are not used for now, so trigger a panic every time one occurs. */
#define _system_int_handler _tee_panic_handler
/* Handlers defined in the `vector.S` file, common to all RISC-V targets */
.global _tee_ns_intr_handler
.global _tee_panic_handler
#define MEMPROT_ISR _tee_ns_intr_handler
#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD
#define ASTDBG_ISR _tee_panic_handler
#else
#define ASTDBG_ISR _tee_ns_intr_handler
#endif // CONFIG_ESP_SYSTEM_HW_STACK_GUARD
.section .exception_vectors_table.text
@@ -32,7 +36,7 @@
.type _vector_table, @function
.balign 0x40
_vector_table:
j _tee_panic_handler
j _tee_exception_handler
.size _vector_table, .-_vector_table
@@ -94,10 +98,8 @@ _mtvt_table:
.word _tee_ns_intr_handler /* 39: Free interrupt number */
.word _tee_panic_handler /* 40: ETS_INT_WDT_INUM (+16) panic-interrupt (soc-level panic) */
.word _tee_panic_handler /* 41: ETS_CACHEERR_INUM (+16) panic-interrupt (soc-level panic) */
/* NOTE: Triggers panic irrespective of the Kconfig setting with ESP-TEE */
.word _tee_panic_handler /* 42: ETS_MEMPROT_ERR_INUM (+16) handler (soc-level panic) */
/* TODO: [IDF-10770] Not supported yet with ESP-TEE */
.word _tee_panic_handler /* 43: ETS_ASSIST_DEBUG_INUM (+16) handler (soc-level panic) */
.word MEMPROT_ISR /* 42: ETS_MEMPROT_ERR_INUM (+16) handler (soc-level panic) */
.word ASTDBG_ISR /* 43: ETS_ASSIST_DEBUG_INUM (+16) handler (soc-level panic) */
.word _tee_ns_intr_handler /* 44: ETS_IPC_ISR_INUM (+16) handler*/
.word _tee_ns_intr_handler /* 45: Free interrupt number */
.word _tee_ns_intr_handler /* 46: ESP-TEE: U-mode interrupt delegation entry */
@@ -10,10 +10,13 @@
#error "ETS_INT_WDT_INUM expected to be 24"
#endif
/* Handlers defined in the `esp_tee_vectors.S` file */
.global _tee_panic_handler
.global _tee_ns_intr_handler
.global _tee_s_intr_handler
#define MEMPROT_ISR _tee_ns_intr_handler
#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD
#define ASTDBG_ISR _tee_panic_handler
#else
#define ASTDBG_ISR _tee_ns_intr_handler
#endif // CONFIG_ESP_SYSTEM_HW_STACK_GUARD
.section .exception_vectors_table.text, "ax"
@@ -36,7 +39,7 @@
.global _vector_table
.type _vector_table, @function
_vector_table:
j _tee_panic_handler /* 0: Exception entry */
j _tee_exception_handler /* 0: Exception entry */
/* NOTE: All of the free interrupts are used by the REE */
j _tee_ns_intr_handler /* 1: Free interrupt number */
j _tee_ns_intr_handler /* 2: Free interrupt number */
@@ -63,10 +66,8 @@ _vector_table:
j _tee_ns_intr_handler /* 23: Free interrupt number */
j _tee_panic_handler /* 24: ETS_INT_WDT_INUM panic-interrupt (soc-level panic) */
j _tee_panic_handler /* 25: ETS_CACHEERR_INUM panic-interrupt (soc-level panic) */
/* NOTE: Triggers panic irrespective of the Kconfig setting with ESP-TEE */
j _tee_panic_handler /* 26: ETS_MEMPROT_ERR_INUM handler (soc-level panic) */
/* TODO: [IDF-10770] Not supported yet with ESP-TEE */
j _tee_panic_handler /* 27: ETS_ASSIST_DEBUG_INUM handler (soc-level panic) */
j MEMPROT_ISR /* 26: ETS_MEMPROT_ERR_INUM handler (soc-level panic) */
j ASTDBG_ISR /* 27: ETS_ASSIST_DEBUG_INUM handler (soc-level panic) */
j _tee_ns_intr_handler /* 28: Free interrupt number */
j _tee_ns_intr_handler /* 29: Free interrupt number */
j _tee_ns_intr_handler /* 30: Free interrupt number */
@@ -16,6 +16,10 @@
#include "esp_tee_intr_defs.h"
#include "sdkconfig.h"
#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD
#include "esp_private/hw_stack_guard.h"
#endif
.equ SAVE_REGS, 32
.equ CONTEXT_SIZE, (SAVE_REGS * 4)
.equ panic_from_exception, tee_panic_from_exc
@@ -32,22 +36,99 @@
.global _tee_s_entry
.section .data
.balign 4
.align 4
.global _ns_sp
_ns_sp:
.word 0
.align 4
.global _s_sp
_s_sp:
.word 0
.align 4
.global _s_intr_thresh
_s_intr_thresh:
.word 0
.global _ns_sp_min
_ns_sp_min:
.word 0
.global _ns_sp_max
_ns_sp_max:
.word 0
/**
* STACK_GUARD_PRE_SWITCH
* Stops HW stack-guard monitoring and optionally saves current bounds.
*
* Args:
* op_reg output register for monitoring enabled state (must be reused)
* to_save 1=save bounds to memory, 0=skip saving
* sp_min symbol to store lower bound (if to_save=1)
* sp_max symbol to store upper bound (if to_save=1)
*
* Clobbers: t0, t1, t2, op_reg
*/
.macro STACK_GUARD_PRE_SWITCH op_reg, to_save, sp_min, sp_max
#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD
/* Query if monitoring is enabled: result goes into \op_reg */
ESP_HW_STACK_GUARD_MONITOR_QUERY_CUR_CORE t2 \op_reg
beqz \op_reg, 1f
.if \to_save
/* Save current REE/U-mode stack bounds */
ESP_HW_STACK_GUARD_GET_BOUNDS_CUR_CORE t2 t0 t1
la t2, \sp_min
sw t0, 0(t2)
la t2, \sp_max
sw t1, 0(t2)
.endif
/* Stop monitoring */
ESP_HW_STACK_GUARD_MONITOR_STOP_CUR_CORE t0 t1
fence
1:
#endif
.endm
/**
* STACK_GUARD_POST_SWITCH
* Restores or applies new bounds after switching stacks, then restarts monitoring.
*
* Args:
* op_reg saved monitoring state from PRE_SWITCH
* to_restore 1=restore from memory, 0=set static bounds
* sp_min saved bound (restore) or static lower bound (S-mode)
* sp_max saved bound (restore) or static upper bound (S-mode)
*
* Clobbers: t0, t1, t2
*/
.macro STACK_GUARD_POST_SWITCH op_reg, to_restore, sp_min, sp_max
#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD
/* Check if monitoring was enabled (using saved state from op_reg) */
beqz \op_reg, 1f
.if \to_restore
/* Restore saved REE/U-mode bounds from memory */
la t2, \sp_min
lw t0, 0(t2)
la t2, \sp_max
lw t1, 0(t2)
.else
/* Use new TEE/S-mode stack bounds (static symbols) */
la t0, \sp_min
la t1, \sp_max
.endif
/* Apply bounds to hardware stack guard */
ESP_HW_STACK_GUARD_SET_BOUNDS_CUR_CORE t2 t0 t1
/* Restart monitoring */
ESP_HW_STACK_GUARD_MONITOR_START_CUR_CORE t0 t1
1:
#endif
.endm
/* Macro which first allocates space on the stack to save general
* purpose registers, and then save them. GP register is excluded.
* The default size allocated on the stack is CONTEXT_SIZE, but it
@@ -179,9 +260,10 @@ _s_intr_thresh:
.section .exception_vectors.text, "ax"
/* Exception handler. */
.global _tee_exception_handler
.global _tee_panic_handler
.type _tee_panic_handler, @function
_tee_panic_handler:
_tee_exception_handler:
/* Backup t0, t1 on the stack before using it */
addi sp, sp, -16
sw t0, 0(sp)
@@ -205,7 +287,7 @@ _tee_panic_handler:
lw t1, 4(sp)
addi sp, sp, 16
_actual_panic:
_tee_panic_handler:
/* Not an ecall, proceed to the panic handler */
/* Allocate space on the stack and store general purpose registers */
save_general_regs RV_STK_FRMSZ
@@ -267,7 +349,7 @@ _return_from_exception:
restore_general_regs RV_STK_FRMSZ
mret
.size _tee_panic_handler, .-_tee_panic_handler
.size _tee_exception_handler, .-_tee_exception_handler
/* ECALL handler. */
.type _ecall_handler, @function
@@ -301,9 +383,13 @@ _1:
csrw CSR_MINTTHRESH, t1
_skip_thresh_restore:
STACK_GUARD_PRE_SWITCH t3 0
/* Switching back to the saved REE stack */
la t0, _ns_sp
lw sp, 0(t0)
STACK_GUARD_POST_SWITCH t3 1 _ns_sp_min _ns_sp_max
fence
/* Backup the A0 register
@@ -353,9 +439,13 @@ _process_ecall:
la t0, _ns_sp
sw sp, 0(t0)
STACK_GUARD_PRE_SWITCH t3 1 _ns_sp_min _ns_sp_max
/* Switch to the M-mode (i.e. TEE) stack */
la sp, _tee_stack
STACK_GUARD_POST_SWITCH t3 0 _tee_stack_bottom _tee_stack
/* Load the TEE entry point (see _tee_s_entry) in the mepc */
la t0, _tee_s_entry
csrw mepc, t0
@@ -389,10 +479,14 @@ _3:
lw t2, 0(t0)
bne t2, t1, _3
STACK_GUARD_PRE_SWITCH t3 0
/* Restore the secure stack pointer */
la t0, _s_sp
lw sp, 0(t0)
STACK_GUARD_POST_SWITCH t3 0 _tee_stack_bottom _tee_stack
/* Set the privilege mode to transition to after mret to M-mode */
li t0, MSTATUS_MPP
csrs mstatus, t0
@@ -504,17 +598,23 @@ _4:
li t1, MSTATUS_MPP
csrc mstatus, t1
/* Save the current secure stack pointer and switch to the U-mode interrupt stack
* saved while entering the secure service call routine (see `_tee_s_entry`) */
/* Save the current secure stack pointer */
la t0, _s_sp
sw sp, 0(t0)
STACK_GUARD_PRE_SWITCH t3 0
/* Switch to the saved U-mode stack */
la t1, _ns_sp
lw sp, 0(t1)
fence
/* Set a flag to identify the next U2M switch would be after handling a U-mode interrupt */
lui t0, RTNVAL
csrw mscratch, t0
STACK_GUARD_POST_SWITCH t3 1 _ns_sp_min _ns_sp_max
/* Place magic bytes in all the general registers */
store_magic_general_regs
@@ -577,8 +677,28 @@ _save_reg_ctx:
_continue:
/* Before doing anything preserve the stack pointer */
mv s11, sp
/* NOTE: Cannot use the stack-guard macros here because they expect
* static symbols, not register-based bounds. */
#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD
/* Check if stack monitoring is enabled */
ESP_HW_STACK_GUARD_MONITOR_QUERY_CUR_CORE t2 t3
beqz t3, 1f
/* Save the REE stack guard bounds in s9/s10 (callee-saved registers) */
ESP_HW_STACK_GUARD_GET_BOUNDS_CUR_CORE t2 s9 s10
/* Stop the stack monitoring */
ESP_HW_STACK_GUARD_MONITOR_STOP_CUR_CORE t0 t1
fence
1:
#endif
/* Switch to the TEE interrupt stack */
la sp, _tee_intr_stack
STACK_GUARD_POST_SWITCH t3 0 _tee_intr_stack_bottom _tee_intr_stack
fence
/* If this is a non-nested interrupt, SP now points to the interrupt stack */
/* Before dispatch c handler, restore interrupt to enable nested intr */
@@ -623,9 +743,24 @@ _intr_hdlr_exec:
csrw mcause, s1
csrw mstatus, s2
STACK_GUARD_PRE_SWITCH t3 0
/* Restoring the stack pointer */
mv sp, s11
/* NOTE: Cannot use the stack-guard macros here because they expect
* static symbols, not register-based bounds. */
#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD
/* Check if stack monitoring was enabled (using saved state from t3) */
beqz t3, 2f # Skip if monitoring was not enabled
/* Restore the REE stack bounds from s9/s10 (callee-saved registers) */
ESP_HW_STACK_GUARD_SET_BOUNDS_CUR_CORE t2 s9 s10
/* Resume stack monitoring */
ESP_HW_STACK_GUARD_MONITOR_START_CUR_CORE t0 t1
2:
#endif
restore_mepc
restore_general_regs RV_STK_FRMSZ
/* exit, this will also re-enable the interrupts */
@@ -635,7 +770,7 @@ _intr_hdlr_exec:
.section .text, "ax"
.align 4
.balign 4
.type _tee_s_entry, @function
_tee_s_entry:
/* Jump to the secure service dispatcher */
@@ -16,6 +16,10 @@
#include "esp_tee_intr_defs.h"
#include "sdkconfig.h"
#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD
#include "esp_private/hw_stack_guard.h"
#endif
.equ SAVE_REGS, 32
.equ CONTEXT_SIZE, (SAVE_REGS * 4)
.equ panic_from_exception, tee_panic_from_exc
@@ -24,31 +28,112 @@
.equ RTNVAL, 0xc0de
.equ ECALL_U_MODE, 0x8
.equ ECALL_M_MODE, 0xb
/* NOTE: INTWDT timeout and Cache error interrupts trigger the panic
* handler before reset, so they dont need to be delegated. */
.equ TEE_INTR_DELEG_MASK, ~((1U << TEE_SECURE_INUM) | (1U << ETS_INT_WDT_INUM) | (1U << ETS_CACHEERR_INUM))
/* NOTE: INTWDT timeout, Cache error and Stack protection interrupts
* trigger the panic handler before reset, so they don't need to be delegated. */
.equ TEE_INTR_DELEG_BASE, ((1U << TEE_SECURE_INUM) | (1U << ETS_INT_WDT_INUM) | (1U << ETS_CACHEERR_INUM))
#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD
.equ TEE_INTR_DELEG_MASK, ~(TEE_INTR_DELEG_BASE | (1U << ETS_ASSIST_DEBUG_INUM))
#else
.equ TEE_INTR_DELEG_MASK, ~(TEE_INTR_DELEG_BASE)
#endif
.global esp_tee_global_interrupt_handler
.global esp_tee_service_dispatcher
.global _tee_s_entry
.section .data
.balign 4
.align 4
.global _ns_sp
_ns_sp:
.word 0
.align 4
.global _s_sp
_s_sp:
.word 0
.align 4
.global _s_intr_thresh
_s_intr_thresh:
.word 0
.global _ns_sp_min
_ns_sp_min:
.word 0
.global _ns_sp_max
_ns_sp_max:
.word 0
/**
* STACK_GUARD_PRE_SWITCH
* Stops HW stack-guard monitoring and optionally saves current bounds.
*
* Args:
* op_reg output register for monitoring enabled state (must be reused)
* to_save 1=save bounds to memory, 0=skip saving
* sp_min symbol to store lower bound (if to_save=1)
* sp_max symbol to store upper bound (if to_save=1)
*
* Clobbers: t0, t1, t2, op_reg
*/
.macro STACK_GUARD_PRE_SWITCH op_reg, to_save, sp_min, sp_max
#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD
/* Query if monitoring is enabled: result goes into \op_reg */
ESP_HW_STACK_GUARD_MONITOR_QUERY_CUR_CORE t2 \op_reg
beqz \op_reg, 1f
.if \to_save
/* Save current REE/U-mode stack bounds */
ESP_HW_STACK_GUARD_GET_BOUNDS_CUR_CORE t2 t0 t1
la t2, \sp_min
sw t0, 0(t2)
la t2, \sp_max
sw t1, 0(t2)
.endif
/* Stop monitoring */
ESP_HW_STACK_GUARD_MONITOR_STOP_CUR_CORE t0 t1
fence
1:
#endif
.endm
/**
* STACK_GUARD_POST_SWITCH
* Restores or applies new bounds after switching stacks, then restarts monitoring.
*
* Args:
* op_reg saved monitoring state from PRE_SWITCH
* to_restore 1=restore from memory, 0=set static bounds
* sp_min saved bound (restore) or static lower bound (S-mode)
* sp_max saved bound (restore) or static upper bound (S-mode)
*
* Clobbers: t0, t1, t2
*/
.macro STACK_GUARD_POST_SWITCH op_reg, to_restore, sp_min, sp_max
#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD
/* Check if monitoring was enabled (using saved state from op_reg) */
beqz \op_reg, 1f
.if \to_restore
/* Restore saved REE/U-mode bounds from memory */
la t2, \sp_min
lw t0, 0(t2)
la t2, \sp_max
lw t1, 0(t2)
.else
/* Use new TEE/S-mode stack bounds (static symbols) */
la t0, \sp_min
la t1, \sp_max
.endif
/* Apply bounds to hardware stack guard */
ESP_HW_STACK_GUARD_SET_BOUNDS_CUR_CORE t2 t0 t1
/* Restart monitoring */
ESP_HW_STACK_GUARD_MONITOR_START_CUR_CORE t0 t1
1:
#endif
.endm
/* Macro which first allocates space on the stack to save general
* purpose registers, and then save them. GP register is excluded.
* The default size allocated on the stack is CONTEXT_SIZE, but it
@@ -180,9 +265,10 @@ _s_intr_thresh:
.section .exception_vectors.text, "ax"
/* Exception handler. */
.global _tee_exception_handler
.global _tee_panic_handler
.type _tee_panic_handler, @function
_tee_panic_handler:
.type _tee_exception_handler, @function
_tee_exception_handler:
/* Backup t0, t1 on the stack before using it */
addi sp, sp, -16
sw t0, 0(sp)
@@ -206,7 +292,7 @@ _tee_panic_handler:
lw t1, 4(sp)
addi sp, sp, 16
_actual_panic:
_tee_panic_handler:
/* Not an ecall, proceed to the panic handler */
/* Allocate space on the stack and store general purpose registers */
save_general_regs RV_STK_FRMSZ
@@ -259,7 +345,7 @@ _return_from_exception:
restore_general_regs RV_STK_FRMSZ
mret
.size _tee_panic_handler, .-_tee_panic_handler
.size _tee_exception_handler, .-_tee_exception_handler
/* ECALL handler. */
.type _ecall_handler, @function
@@ -289,9 +375,13 @@ _machine_ecall:
sw t1, 0(t0)
_skip_thresh_restore:
STACK_GUARD_PRE_SWITCH t3 0
/* Switching back to the saved REE stack */
la t0, _ns_sp
lw sp, 0(t0)
STACK_GUARD_POST_SWITCH t3 1 _ns_sp_min _ns_sp_max
fence
/* Backup the A0 register
@@ -344,13 +434,16 @@ _process_ecall:
la t0, _ns_sp
sw sp, 0(t0)
STACK_GUARD_PRE_SWITCH t3 1 _ns_sp_min _ns_sp_max
/* Switch to the M-mode (i.e. TEE) stack */
la sp, _tee_stack
STACK_GUARD_POST_SWITCH t3 0 _tee_stack_bottom _tee_stack
/* Load the TEE entry point (see _tee_s_entry) in the mepc */
la t0, _tee_s_entry
csrw mepc, t0
fence
/* Disable the U-mode delegation of all interrupts */
csrwi mideleg, 0
@@ -367,10 +460,14 @@ _rtn_from_ns_int:
/* Disable the U-mode interrupt delegation */
csrwi mideleg, 0
STACK_GUARD_PRE_SWITCH t3 0
/* Restore the secure stack pointer */
la t0, _s_sp
lw sp, 0(t0)
STACK_GUARD_POST_SWITCH t3 0 _tee_stack_bottom _tee_stack
/* Set the privilege mode to transition to after mret to M-mode */
li t0, MSTATUS_MPP
csrs mstatus, t0
@@ -427,12 +524,16 @@ _tee_ns_intr_handler:
li t1, MSTATUS_MPP
csrc mstatus, t1
/* Save the current secure stack pointer and switch to the U-mode interrupt stack
* saved while entering the secure service call routine (see `_tee_s_entry`) */
/* Save the current secure stack pointer */
la t0, _s_sp
sw sp, 0(t0)
STACK_GUARD_PRE_SWITCH t3 0
/* Switch to the saved U-mode stack */
la t1, _ns_sp
lw sp, 0(t1)
fence
/* Set a flag to identify the next U2M switch would be after handling a U-mode interrupt */
lui t0, RTNVAL
@@ -442,6 +543,8 @@ _tee_ns_intr_handler:
li t0, TEE_INTR_DELEG_MASK
csrs mideleg, t0
STACK_GUARD_POST_SWITCH t3 1 _ns_sp_min _ns_sp_max
/* Place magic bytes in all the general registers */
store_magic_general_regs
@@ -506,8 +609,28 @@ _save_reg_ctx:
_continue:
/* Before doing anything preserve the stack pointer */
mv s11, sp
/* NOTE: Cannot use the stack-guard macros here because they expect
* static symbols, not register-based bounds. */
#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD
/* Check if stack monitoring is enabled */
ESP_HW_STACK_GUARD_MONITOR_QUERY_CUR_CORE t2 t3
beqz t3, 1f
/* Save the REE stack guard bounds in s9/s10 (callee-saved registers) */
ESP_HW_STACK_GUARD_GET_BOUNDS_CUR_CORE t2 s9 s10
/* Stop the stack monitoring */
ESP_HW_STACK_GUARD_MONITOR_STOP_CUR_CORE t0 t1
fence
1:
#endif
/* Switch to the TEE interrupt stack */
la sp, _tee_intr_stack
STACK_GUARD_POST_SWITCH t3 0 _tee_intr_stack_bottom _tee_intr_stack
fence
/* If this is a non-nested interrupt, SP now points to the interrupt stack */
/* Before dispatch c handler, restore interrupt to enable nested intr */
@@ -572,9 +695,24 @@ _intr_hdlr_exec:
csrw mcause, s1
csrw mstatus, s2
STACK_GUARD_PRE_SWITCH t3 0
/* Restoring the stack pointer */
mv sp, s11
/* NOTE: Cannot use the stack-guard macros here because they expect
* static symbols, not register-based bounds. */
#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD
/* Check if stack monitoring was enabled (using saved state from t3) */
beqz t3, 2f # Skip if monitoring was not enabled
/* Restore the REE stack bounds from s9/s10 (callee-saved registers) */
ESP_HW_STACK_GUARD_SET_BOUNDS_CUR_CORE t2 s9 s10
/* Resume stack monitoring */
ESP_HW_STACK_GUARD_MONITOR_START_CUR_CORE t0 t1
2:
#endif
restore_mepc
restore_general_regs RV_STK_FRMSZ
/* exit, this will also re-enable the interrupts */
@@ -584,7 +722,7 @@ _intr_hdlr_exec:
.section .text, "ax"
.align 4
.balign 4
.type _tee_s_entry, @function
_tee_s_entry:
/* Jump to the secure service dispatcher */
@@ -154,6 +154,9 @@ void panic_print_isrcause(const void *f, int core)
rsn = "Interrupt wdt timeout on CPU1";
break;
#endif
case ETS_ASSIST_DEBUG_INUM:
rsn = "Stack protection fault";
break;
}
panic_print_rsn(f, core, rsn);
@@ -70,7 +70,7 @@ void esp_tee_route_intr_matrix(int cpu_no, uint32_t model_num, uint32_t intr_num
ESP_LOGV(TAG, "Connected src %d to int %d (cpu %d)", model_num, intr_num, cpu_no);
#if SOC_INT_CLIC_SUPPORTED
if (intr_num != ETS_T1_WDT_INUM && intr_num != ETS_CACHEERR_INUM) {
if (intr_num != ETS_T1_WDT_INUM && intr_num != ETS_CACHEERR_INUM && intr_num != ETS_ASSIST_DEBUG_INUM) {
/* Configure interrupts to be visible in the TEE (M-mode) */
REG_SET_BIT(DR_REG_INTMTX_BASE + 4 * model_num, BIT(8));
/* Configure interrupts to be serviced in the REE (U-mode) */
@@ -1,6 +1,6 @@
# Reducing TEE I/DRAM sizes
# 28KB
CONFIG_SECURE_TEE_IRAM_SIZE=0x7000
# 29KB
CONFIG_SECURE_TEE_IRAM_SIZE=0x7400
# TEE Secure Storage: Release mode
CONFIG_SECURE_TEE_SEC_STG_MODE_RELEASE=y
@@ -69,3 +69,11 @@ secure_services:
type: custom
function: esp_tee_test_heap_malloc_write_free
args: 0
- id: 217
type: custom
function: esp_tee_test_stack_overflow
args: 0
- id: 218
type: custom
function: esp_tee_test_stack_underflow
args: 0
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -46,6 +46,45 @@ void _ss_esp_tee_test_iram_reg2_write_violation(void)
*test_addr = RND_VAL;
}
static void do_stack_smash(bool underflow, int depth, volatile uint8_t *sink)
{
/* Overflow path */
if (!underflow) {
if (depth == -1) {
return; // unreachable
}
uint8_t buffer[1024];
buffer[0] = (uint8_t)depth;
*sink = buffer[0];
do_stack_smash(false, depth + 1, sink);
return;
}
/* Underflow path */
asm volatile(
"li t0, 2048\n"
"add sp, sp, t0\n"
);
volatile uint8_t a = 1;
volatile uint8_t b = a;
(void)b;
}
void _ss_esp_tee_test_stack_overflow(void)
{
volatile uint8_t sink = 0;
do_stack_smash(false, 1, &sink);
}
void _ss_esp_tee_test_stack_underflow(void)
{
volatile uint8_t sink = 0;
do_stack_smash(true, 0, &sink);
}
#pragma GCC pop_options
static void foo_d(void)
@@ -5,6 +5,7 @@
*/
#include "soc/soc_caps.h"
#include "esp_attr.h"
#if SOC_AES_SUPPORTED
#include "soc/aes_reg.h"
@@ -21,6 +22,7 @@
#include "soc/lp_wdt_reg.h"
#include "soc/spi_mem_reg.h"
#include "soc/ext_mem_defs.h"
#include "soc/assist_debug_reg.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
@@ -226,3 +228,55 @@ TEST_CASE("Test REE-TEE isolation: DROM-W1", "[exception]")
*(uint32_t *)(test_addr - 0x04) = 0xbadc0de;
TEST_FAIL_MESSAGE("Exception should have been generated");
}
static void do_stack_smash(bool underflow, int depth, volatile uint8_t *sink)
{
/* Overflow path */
if (!underflow) {
if (depth == -1) {
return; // unreachable
}
uint8_t buffer[1024];
buffer[0] = (uint8_t)depth;
*sink = buffer[0];
do_stack_smash(false, depth + 1, sink);
return;
}
/* Underflow path */
asm volatile(
"li t0, 4096\n"
"add sp, sp, t0\n"
);
volatile uint8_t temp = 1;
(void)temp;
}
TEST_CASE("Test REE stack overflow", "[exception]")
{
volatile uint8_t sink = 0;
do_stack_smash(false, 1, &sink);
TEST_FAIL_MESSAGE("Exception should have been generated");
}
TEST_CASE("Test REE stack underflow", "[exception]")
{
volatile uint8_t sink = 0;
do_stack_smash(true, 0, &sink);
TEST_FAIL_MESSAGE("Exception should have been generated");
}
TEST_CASE("Test TEE stack overflow", "[exception]")
{
esp_tee_service_call(1, SS_ESP_TEE_TEST_STACK_OVERFLOW);
TEST_FAIL_MESSAGE("Exception should have been generated");
}
TEST_CASE("Test TEE stack underflow", "[exception]")
{
esp_tee_service_call(1, SS_ESP_TEE_TEST_STACK_UNDERFLOW);
TEST_FAIL_MESSAGE("Exception should have been generated");
}
@@ -39,6 +39,7 @@ TEE_VIOLATION_TEST_EXC_RSN: dict[str, str] = {
('IRAM-W2'): 'Store access fault',
('DRAM-X1'): 'Instruction access fault',
('DRAM-X2'): 'Instruction access fault',
('Illegal Instruction'): 'Illegal instruction',
}
REE_ISOLATION_TEST_EXC_RSN: dict[str, str] = {
@@ -128,19 +129,6 @@ def test_esp_tee_apm_violation(dut: IdfDut) -> None:
raise RuntimeError('Incorrect exception received!')
@idf_parametrize(
'config, target, markers',
CONFIG_DEFAULT,
indirect=['config', 'target'],
)
def test_esp_tee_illegal_instruction(dut: IdfDut) -> None:
dut.expect_exact('Press ENTER to see the list of tests')
dut.write('"Test TEE-TEE violation: Illegal Instruction"')
exc = dut.expect(r'Core ([01]) panic\'ed \(([^)]+)\)', timeout=30).group(2).decode()
if exc != 'Illegal instruction':
raise RuntimeError('Incorrect exception received!')
@idf_parametrize(
'config, target, markers',
CONFIG_DEFAULT,
@@ -183,6 +171,23 @@ def test_esp_tee_isolation_checks(dut: IdfDut) -> None:
dut.expect('Origin: U-mode')
@idf_parametrize(
'config, target, markers',
CONFIG_DEFAULT,
indirect=['config', 'target'],
)
def test_esp_tee_stack_smashing(dut: IdfDut) -> None:
for env in ('REE', 'TEE'):
for case in ('overflow', 'underflow'):
dut.expect_exact('Press ENTER to see the list of tests')
dut.write(f'"Test {env} stack {case}"')
match = dut.expect(r"Core ([01]) panic'ed \(([^)]+)\)", timeout=30)
exc = match.group(2).decode()
if exc != 'Stack protection fault':
raise RuntimeError('Incorrect exception received!')
# ---------------- TEE Flash Protection Tests ----------------
@@ -66,7 +66,7 @@ rtos_int_enter:
/* esp_hw_stack_guard_set_bounds(xIsrStack, xIsrStackTop); */
la a0, xIsrStack
mv a1, sp
ESP_HW_STACK_GUARD_SET_BOUNDS_CUR_CORE a2
ESP_HW_STACK_GUARD_SET_BOUNDS_CUR_CORE a2 a0 a1
/* esp_hw_stack_guard_monitor_start(); */
ESP_HW_STACK_GUARD_MONITOR_START_CUR_CORE a0 a1
#endif /* CONFIG_ESP_SYSTEM_HW_STACK_GUARD */
@@ -143,7 +143,7 @@ no_switch:
*/
lw a0, PORT_OFFSET_PX_STACK(t0)
lw a1, PORT_OFFSET_PX_END_OF_STACK(t0)
ESP_HW_STACK_GUARD_SET_BOUNDS_CUR_CORE a2
ESP_HW_STACK_GUARD_SET_BOUNDS_CUR_CORE a2 a0 a1
/* esp_hw_stack_guard_monitor_start(); */
ESP_HW_STACK_GUARD_MONITOR_START_CUR_CORE a0 a1
#endif /* CONFIG_ESP_SYSTEM_HW_STACK_GUARD */
@@ -684,7 +684,7 @@ rtos_int_enter:
mv a1, sp
/* esp_hw_stack_guard_set_bounds(xIsrStackBottom[coreID], xIsrStackTop[coreID]);
*/
ESP_HW_STACK_GUARD_SET_BOUNDS_CUR_CORE a2
ESP_HW_STACK_GUARD_SET_BOUNDS_CUR_CORE a2 a0 a1
ESP_HW_STACK_GUARD_MONITOR_START_CUR_CORE a0 a1
#endif /* CONFIG_ESP_SYSTEM_HW_STACK_GUARD */
@@ -841,7 +841,7 @@ restore_stack_pointer:
*/
lw a1, PORT_OFFSET_PX_END_OF_STACK(a0)
lw a0, PORT_OFFSET_PX_STACK(a0)
ESP_HW_STACK_GUARD_SET_BOUNDS_CUR_CORE a2
ESP_HW_STACK_GUARD_SET_BOUNDS_CUR_CORE a2 a0 a1
/* esp_hw_stack_guard_monitor_start(); */
ESP_HW_STACK_GUARD_MONITOR_START_CUR_CORE a0 a1
#endif /* CONFIG_ESP_SYSTEM_HW_STACK_GUARD */