diff --git a/components/riscv/CMakeLists.txt b/components/riscv/CMakeLists.txt index 96914d9bee..43f6e42728 100644 --- a/components/riscv/CMakeLists.txt +++ b/components/riscv/CMakeLists.txt @@ -1,5 +1,6 @@ idf_build_get_property(target IDF_TARGET) idf_build_get_property(arch IDF_TARGET_ARCH) +idf_build_get_property(esp_tee_build ESP_TEE_BUILD) if(NOT "${arch}" STREQUAL "riscv") return() @@ -8,6 +9,11 @@ endif() if(BOOTLOADER_BUILD) set(priv_requires soc) +elseif(esp_tee_build) + set(priv_requires soc) + if(CONFIG_SOC_INT_PLIC_SUPPORTED) + set(srcs "interrupt_plic.c") + endif() else() set(priv_requires soc) set(srcs diff --git a/components/riscv/include/esp_private/interrupt_plic.h b/components/riscv/include/esp_private/interrupt_plic.h index 4d1fbba34e..f4eaab32e4 100644 --- a/components/riscv/include/esp_private/interrupt_plic.h +++ b/components/riscv/include/esp_private/interrupt_plic.h @@ -12,6 +12,7 @@ #include "soc/interrupt_reg.h" #include "soc/soc_caps.h" #include "riscv/csr.h" +#include "sdkconfig.h" #if SOC_INT_PLIC_SUPPORTED @@ -84,12 +85,34 @@ FORCE_INLINE_ATTR void rv_utils_restore_intlevel_regval(uint32_t restoreval) */ FORCE_INLINE_ATTR uint32_t rv_utils_set_intlevel_regval(uint32_t intlevel) { +#if CONFIG_SECURE_ENABLE_TEE + unsigned prv_mode = RV_READ_CSR(CSR_PRV_MODE); + + unsigned old_xstatus; + if (prv_mode == PRV_M) { + old_xstatus = RV_CLEAR_CSR(mstatus, MSTATUS_MIE); + } else { + old_xstatus = RV_CLEAR_CSR(ustatus, USTATUS_UIE); + } + + uint32_t old_thresh = REG_READ(INTERRUPT_CURRENT_CORE_INT_THRESH_REG); + rv_utils_restore_intlevel_regval(intlevel); + + if (prv_mode == PRV_M) { + RV_SET_CSR(mstatus, old_xstatus & MSTATUS_MIE); + } else { + RV_SET_CSR(ustatus, old_xstatus & USTATUS_UIE); + } + + return old_thresh; +#else uint32_t old_mstatus = RV_CLEAR_CSR(mstatus, MSTATUS_MIE); uint32_t old_thresh = REG_READ(INTERRUPT_CURRENT_CORE_INT_THRESH_REG); rv_utils_restore_intlevel_regval(intlevel); RV_SET_CSR(mstatus, old_mstatus & MSTATUS_MIE); return old_thresh; +#endif } diff --git a/components/riscv/include/riscv/csr.h b/components/riscv/include/riscv/csr.h index b55932f0b7..0e9d71b4be 100644 --- a/components/riscv/include/riscv/csr.h +++ b/components/riscv/include/riscv/csr.h @@ -187,6 +187,13 @@ extern "C" { #define STPC1 0xBF1 #define STPC2 0xBF2 +/* Espressif's custom CSR for the current privilege mode */ +#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 +#define CSR_PRV_MODE 0xC10 +#elif CONFIG_IDF_TARGET_ESP32C5 || CONFIG_IDF_TARGET_ESP32C61 || CONFIG_IDF_TARGET_ESP32P4 +#define CSR_PRV_MODE 0x810 +#endif + /* RISC-V CSR macros * Adapted from https://github.com/michaeljclark/riscv-probe/blob/master/libfemto/include/arch/riscv/machine.h */ diff --git a/components/riscv/include/riscv/interrupt.h b/components/riscv/include/riscv/interrupt.h index b2dab4a672..f500b6174b 100644 --- a/components/riscv/include/riscv/interrupt.h +++ b/components/riscv/include/riscv/interrupt.h @@ -146,6 +146,21 @@ bool esprv_int_is_vectored(int rv_int_num); */ void esprv_int_set_vectored(int rv_int_num, bool vectored); +/*************************** ESP-TEE specific ***************************/ + +/** Function prototype executing interrupt configuration APIs as service calls */ +typedef void (*esprv_int_mgmt_t)(int argc, ...); + +/** + * @brief Setup the callback function which executes the interrupt + * configuration APIs as TEE service calls + * + * @note This function should be called right after landing in the REE application, + * before any system initialization + * + * @param fptr Pointer to the function + */ +void esprv_int_setup_mgmt_cb(void *fptr); /** * Include the deprecated functions last since they will alias the functions declared above diff --git a/components/riscv/include/riscv/rv_utils.h b/components/riscv/include/riscv/rv_utils.h index cc8c608e4a..fcf9b3bfcf 100644 --- a/components/riscv/include/riscv/rv_utils.h +++ b/components/riscv/include/riscv/rv_utils.h @@ -15,16 +15,38 @@ #include "riscv/csr.h" #include "riscv/interrupt.h" #include "riscv/csr_pie.h" +#include "sdkconfig.h" #ifdef __cplusplus extern "C" { #endif +/* Check whether the current privilege level is Machine (M) mode */ +#if CONFIG_SECURE_ENABLE_TEE +#define IS_PRV_M_MODE() (RV_READ_CSR(CSR_PRV_MODE) == PRV_M) +#else +#define IS_PRV_M_MODE() (1UL) +#endif + +#if CONFIG_SECURE_ENABLE_TEE && !ESP_TEE_BUILD +/* [ESP-TEE] Secure service call IDs for interrupt management */ +#define TEE_INTR_ENABLE_SRV_ID (2) +#define TEE_INTR_DISABLE_SRV_ID (3) +#define TEE_INTR_SET_PRIORITY_SRV_ID (4) +#define TEE_INTR_SET_TYPE_SRV_ID (5) +#define TEE_INTR_SET_THRESHOLD_SRV_ID (6) +#define TEE_INTR_EDGE_ACK_SRV_ID (7) +#define TEE_INTR_GLOBAL_EN_SRV_ID (8) +/* [ESP-TEE] Callback function for accessing interrupt management services through REE */ +extern esprv_int_mgmt_t esp_tee_intr_sec_srv_cb; +#endif + #if SOC_CPU_HAS_CSR_PC /*performance counter*/ #define CSR_PCER_MACHINE 0x7e0 #define CSR_PCMR_MACHINE 0x7e1 #define CSR_PCCR_MACHINE 0x7e2 +#define CSR_PCCR_USER 0x802 #endif /* SOC_CPU_HAS_CSR_PC */ #if SOC_BRANCH_PREDICTOR_SUPPORTED @@ -89,7 +111,11 @@ FORCE_INLINE_ATTR uint32_t __attribute__((always_inline)) rv_utils_get_cycle_cou #if !SOC_CPU_HAS_CSR_PC return RV_READ_CSR(mcycle); #else - return RV_READ_CSR(CSR_PCCR_MACHINE); + if (IS_PRV_M_MODE()) { + return RV_READ_CSR(CSR_PCCR_MACHINE); + } else { + return RV_READ_CSR(CSR_PCCR_USER); + } #endif } @@ -98,7 +124,11 @@ FORCE_INLINE_ATTR void __attribute__((always_inline)) rv_utils_set_cycle_count(u #if !SOC_CPU_HAS_CSR_PC RV_WRITE_CSR(mcycle, ccount); #else - RV_WRITE_CSR(CSR_PCCR_MACHINE, ccount); + if (IS_PRV_M_MODE()) { + RV_WRITE_CSR(CSR_PCCR_MACHINE, ccount); + } else { + RV_WRITE_CSR(CSR_PCCR_USER, ccount); + } #endif } @@ -113,32 +143,89 @@ FORCE_INLINE_ATTR void rv_utils_set_mtvec(uint32_t mtvec_val) RV_WRITE_CSR(mtvec, mtvec_val | MTVEC_MODE_CSR); } +FORCE_INLINE_ATTR void rv_utils_set_xtvec(uint32_t xtvec_val) +{ + xtvec_val |= MTVEC_MODE_CSR; // Set MODE field to treat XTVEC as a vector base address + if (IS_PRV_M_MODE()) { + RV_WRITE_CSR(mtvec, xtvec_val); + } else { + RV_WRITE_CSR(utvec, xtvec_val); + } +} + // ------------------ Interrupt Control -------------------- FORCE_INLINE_ATTR void rv_utils_intr_enable(uint32_t intr_mask) { +#if CONFIG_SECURE_ENABLE_TEE && !ESP_TEE_BUILD + esp_tee_intr_sec_srv_cb(2, TEE_INTR_ENABLE_SRV_ID, intr_mask); +#else // Disable all interrupts to make updating of the interrupt mask atomic. unsigned old_mstatus = RV_CLEAR_CSR(mstatus, MSTATUS_MIE); esprv_int_enable(intr_mask); RV_SET_CSR(mstatus, old_mstatus & MSTATUS_MIE); +#endif } FORCE_INLINE_ATTR void rv_utils_intr_disable(uint32_t intr_mask) { +#if CONFIG_SECURE_ENABLE_TEE && !ESP_TEE_BUILD + esp_tee_intr_sec_srv_cb(2, TEE_INTR_DISABLE_SRV_ID, intr_mask); +#else // Disable all interrupts to make updating of the interrupt mask atomic. unsigned old_mstatus = RV_CLEAR_CSR(mstatus, MSTATUS_MIE); esprv_int_disable(intr_mask); RV_SET_CSR(mstatus, old_mstatus & MSTATUS_MIE); +#endif } FORCE_INLINE_ATTR void rv_utils_intr_global_enable(void) { +#if CONFIG_SECURE_ENABLE_TEE && !ESP_TEE_BUILD + esp_tee_intr_sec_srv_cb(1, TEE_INTR_GLOBAL_EN_SRV_ID); +#else RV_SET_CSR(mstatus, MSTATUS_MIE); +#endif } FORCE_INLINE_ATTR void rv_utils_intr_global_disable(void) { +#if CONFIG_SECURE_ENABLE_TEE + if (IS_PRV_M_MODE()) { + RV_CLEAR_CSR(mstatus, MSTATUS_MIE); + } else { + RV_CLEAR_CSR(ustatus, USTATUS_UIE); + } +#else RV_CLEAR_CSR(mstatus, MSTATUS_MIE); +#endif +} + +FORCE_INLINE_ATTR void rv_utils_intr_set_type(int intr_num, enum intr_type type) +{ +#if CONFIG_SECURE_ENABLE_TEE && !ESP_TEE_BUILD + esp_tee_intr_sec_srv_cb(3, TEE_INTR_SET_TYPE_SRV_ID, intr_num, type); +#else + esprv_int_set_type(intr_num, type); +#endif +} + +FORCE_INLINE_ATTR void rv_utils_intr_set_priority(int rv_int_num, int priority) +{ +#if CONFIG_SECURE_ENABLE_TEE && !ESP_TEE_BUILD + esp_tee_intr_sec_srv_cb(3, TEE_INTR_SET_PRIORITY_SRV_ID, rv_int_num, priority); +#else + esprv_int_set_priority(rv_int_num, priority); +#endif +} + +FORCE_INLINE_ATTR void rv_utils_intr_set_threshold(int priority_threshold) +{ +#if CONFIG_SECURE_ENABLE_TEE && !ESP_TEE_BUILD + esp_tee_intr_sec_srv_cb(2, TEE_INTR_SET_THRESHOLD_SRV_ID, priority_threshold); +#else + esprv_int_set_threshold(priority_threshold); +#endif } /** @@ -352,8 +439,12 @@ FORCE_INLINE_ATTR bool rv_utils_compare_and_set(volatile uint32_t *addr, uint32_ ); #else // For a single core RV target has no atomic CAS instruction, we can achieve atomicity by disabling interrupts - unsigned old_mstatus; - old_mstatus = RV_CLEAR_CSR(mstatus, MSTATUS_MIE); + unsigned old_xstatus; + if (IS_PRV_M_MODE()) { + old_xstatus = RV_CLEAR_CSR(mstatus, MSTATUS_MIE); + } else { + old_xstatus = RV_CLEAR_CSR(ustatus, USTATUS_UIE); + } // Compare and set uint32_t old_value; old_value = *addr; @@ -361,7 +452,11 @@ FORCE_INLINE_ATTR bool rv_utils_compare_and_set(volatile uint32_t *addr, uint32_ *addr = new_value; } // Restore interrupts - RV_SET_CSR(mstatus, old_mstatus & MSTATUS_MIE); + if (IS_PRV_M_MODE()) { + RV_SET_CSR(mstatus, old_xstatus & MSTATUS_MIE); + } else { + RV_SET_CSR(ustatus, old_xstatus & USTATUS_UIE); + } #endif //__riscv_atomic return (old_value == compare_value); diff --git a/components/riscv/interrupt_plic.c b/components/riscv/interrupt_plic.c index 86b99cb616..9c7aa0d2bf 100644 --- a/components/riscv/interrupt_plic.c +++ b/components/riscv/interrupt_plic.c @@ -4,7 +4,10 @@ * SPDX-License-Identifier: Apache-2.0 */ #include +#include "soc/soc_caps.h" +#include "riscv/csr.h" #include "riscv/interrupt.h" +#include "riscv/rv_utils.h" #include "esp_private/interrupt_plic.h" #include "hal/interrupt_plic_ll.h" @@ -18,19 +21,35 @@ void intr_matrix_route(int intr_src, int intr_num) uint32_t esprv_get_interrupt_unmask(void) { - return interrupt_plic_ll_get_unmask(); + if (IS_PRV_M_MODE()) { + return REG_READ(PLIC_MXINT_ENABLE_REG); + } else { + return REG_READ(PLIC_UXINT_ENABLE_REG); + } } enum intr_type esprv_int_get_type(int rv_int_num) { - return interrupt_plic_ll_get_type(rv_int_num) ? INTR_TYPE_EDGE : INTR_TYPE_LEVEL; + uint32_t intr_type_reg; + + if (IS_PRV_M_MODE()) { + intr_type_reg = REG_READ(PLIC_MXINT_TYPE_REG); + } else { + intr_type_reg = REG_READ(PLIC_UXINT_TYPE_REG); + } + + return (intr_type_reg & (1 << rv_int_num)) ? INTR_TYPE_EDGE : INTR_TYPE_LEVEL; } int esprv_int_get_priority(int rv_int_num) { - return interrupt_plic_ll_get_priority(rv_int_num); + if (IS_PRV_M_MODE()) { + return REG_READ(PLIC_MXINT0_PRI_REG + (rv_int_num) * 4); + } else { + return REG_READ(PLIC_UXINT0_PRI_REG + (rv_int_num) * 4); + } } @@ -38,3 +57,44 @@ bool esprv_int_is_vectored(int rv_int_num) { return true; } + + +#if CONFIG_SECURE_ENABLE_TEE && !ESP_TEE_BUILD +DRAM_ATTR esprv_int_mgmt_t esp_tee_intr_sec_srv_cb = NULL; + +void esprv_int_setup_mgmt_cb(void *fptr) +{ + esp_tee_intr_sec_srv_cb = (esprv_int_mgmt_t)fptr; +} + + +/* NOTE: Overriding ROM-based interrupt configuration symbols */ +void esprv_int_enable(uint32_t unmask) +{ + rv_utils_intr_enable(unmask); +} + + +void esprv_int_disable(uint32_t mask) +{ + rv_utils_intr_disable(mask); +} + + +void esprv_int_set_type(int intr_num, enum intr_type type) +{ + rv_utils_intr_set_type(intr_num, type); +} + + +void esprv_int_set_priority(int rv_int_num, int priority) +{ + rv_utils_intr_set_priority(rv_int_num, priority); +} + + +void esprv_int_set_threshold(int priority_threshold) +{ + rv_utils_intr_set_threshold(priority_threshold); +} +#endif diff --git a/components/riscv/vectors.S b/components/riscv/vectors.S index d97ad7f83d..4468155e80 100644 --- a/components/riscv/vectors.S +++ b/components/riscv/vectors.S @@ -76,11 +76,39 @@ sw t6, RV_STK_T6(sp) .endm -.macro save_mepc +/* Macro for saving special registers depending on the active execution + * environment - REE - U-mode / TEE - M-mode */ +/* Save the XEPC register */ +.macro save_xepc + #if CONFIG_SECURE_ENABLE_TEE + csrr t0, uepc + #else csrr t0, mepc + #endif sw t0, RV_STK_MEPC(sp) .endm +/* Save the required CSRs */ +.macro save_xcsr + #if CONFIG_SECURE_ENABLE_TEE + csrr t0, ustatus + sw t0, RV_STK_MSTATUS(sp) + csrr t0, utvec + sw t0, RV_STK_MTVEC(sp) + csrr t0, utval + sw t0, RV_STK_MTVAL(sp) + #else + csrr t0, mstatus + sw t0, RV_STK_MSTATUS(sp) + csrr t0, mtvec + sw t0, RV_STK_MTVEC(sp) + csrr t0, mhartid + sw t0, RV_STK_MHARTID(sp) + csrr t0, mtval + sw t0, RV_STK_MTVAL(sp) + #endif +.endm + /* Restore the general purpose registers (excluding gp) from the context on * the stack. The context is then deallocated. The default size is CONTEXT_SIZE * but it can be overridden. */ @@ -117,11 +145,49 @@ addi sp,sp, \cxt_size .endm -.macro restore_mepc +/* Macro for restoring special registers depending on the active execution + * environment - REE - U-mode / TEE - M-mode */ + +/* Restore the XEPC register depending on the active execution + * environment - REE - U-mode / TEE - M-mode */ +.macro restore_xepc lw t0, RV_STK_MEPC(sp) +#if CONFIG_SECURE_ENABLE_TEE + csrw uepc, t0 +#else csrw mepc, t0 +#endif .endm +/* Macros for enabling/disabling the global interrupts based on the + * active execution environment - REE - U-mode / TEE - M-mode */ +.macro enable_intr +#if CONFIG_SECURE_ENABLE_TEE + li t0, 0x1 + csrs ustatus, t0 +#else + li t0, 0x8 + csrs mstatus, t0 +#endif +.endm + +.macro disable_intr +#if CONFIG_SECURE_ENABLE_TEE + li t0, 0x1 + csrc ustatus, t0 +#else + li t0, 0x8 + csrc mstatus, t0 +#endif +.endm + +.macro xret +#if CONFIG_SECURE_ENABLE_TEE + uret +#else + mret +#endif +.endm .global rtos_int_enter .global rtos_int_exit @@ -148,19 +214,15 @@ _panic_handler: /* Save CSRs */ sw t0, RV_STK_SP(sp) - csrr t0, mepc - sw t0, RV_STK_MEPC(sp) - csrr t0, mstatus - sw t0, RV_STK_MSTATUS(sp) - csrr t0, mtvec - sw t0, RV_STK_MTVEC(sp) - csrr t0, mhartid - sw t0, RV_STK_MHARTID(sp) - csrr t0, mtval - sw t0, RV_STK_MTVAL(sp) + save_xepc + save_xcsr /* Keep mcause in s0, only the exception code and interrupt bit are relevant */ +#if CONFIG_SECURE_ENABLE_TEE + csrr s0, ucause +#else csrr s0, mcause +#endif li t1, VECTORS_MCAUSE_INTBIT_MASK | VECTORS_MCAUSE_REASON_MASK and s0, s0, t1 @@ -274,7 +336,7 @@ _store_mcause: * Restore the registers and return from the exception. */ _return_from_exception: - restore_mepc + restore_xepc /* MTVEC and SP are assumed to be unmodified. * MSTATUS, MHARTID, MTVAL are read-only and not restored. */ @@ -290,12 +352,14 @@ _return_from_exception: * from the stack. */ .global _interrupt_handler + .global _tee_interrupt_handler .type _interrupt_handler, @function _interrupt_handler: +_tee_interrupt_handler: /* Start by saving the general purpose registers and the PC value before * the interrupt happened. */ save_general_regs - save_mepc + save_xepc /* Though it is not necessary we save GP and SP here. * SP is necessary to help GDB to properly unwind @@ -317,8 +381,13 @@ _interrupt_handler: /* If this is a non-nested interrupt, SP now points to the interrupt stack */ /* Before dispatch c handler, restore interrupt to enable nested intr */ +#if CONFIG_SECURE_ENABLE_TEE + csrr s1, ucause + csrr s2, ustatus +#else csrr s1, mcause csrr s2, mstatus +#endif #if !SOC_INT_HW_NESTED_SUPPORTED /* Save the interrupt threshold level */ @@ -337,7 +406,7 @@ _interrupt_handler: fence #endif // !SOC_INT_HW_NESTED_SUPPORTED - csrsi mstatus, 0x8 + enable_intr /* MIE set. Nested interrupts can now occur */ #ifdef CONFIG_PM_TRACE @@ -366,7 +435,7 @@ _interrupt_handler: /* After dispatch c handler, disable interrupt to make freertos make context switch */ - csrci mstatus, 0x8 + disable_intr /* MIE cleared. Nested interrupts are disabled */ #if !SOC_INT_HW_NESTED_SUPPORTED @@ -386,10 +455,15 @@ _interrupt_handler: * In case the target uses the CLIC, it is mandatory to restore `mcause` register since it contains * the former CPU priority. When executing `mret`, the hardware will restore the former threshold, * from `mcause` to `mintstatus` CSR */ +#if CONFIG_SECURE_ENABLE_TEE + csrw ucause, s1 + csrw ustatus, a0 +#else csrw mcause, s1 csrw mstatus, a0 - restore_mepc +#endif + restore_xepc restore_general_regs /* exit, this will also re-enable the interrupts */ - mret + xret .size _interrupt_handler, .-_interrupt_handler