ci(hal): Extend the PMS hal test-app for verifying TEE-based interrupt scenarios

This commit is contained in:
Laukik Hase
2025-06-30 18:39:45 +05:30
parent 90d3d658bb
commit 3d402ca938
47 changed files with 1798 additions and 176 deletions

View File

@@ -128,7 +128,7 @@
/components/freertos/ @esp-idf-codeowners/system
/components/hal/ @esp-idf-codeowners/peripherals
/components/hal/test_apps/crypto/ @esp-idf-codeowners/peripherals @esp-idf-codeowners/security
/components/hal/test_apps/tee_apm/ @esp-idf-codeowners/peripherals @esp-idf-codeowners/security
/components/hal/test_apps/tee/ @esp-idf-codeowners/peripherals @esp-idf-codeowners/security
/components/heap/ @esp-idf-codeowners/system
/components/http_parser/ @esp-idf-codeowners/app-utilities
/components/idf_test/ @esp-idf-codeowners/peripherals @esp-idf-codeowners/system

View File

@@ -12,6 +12,6 @@ components/hal/test_apps/hal_utils:
enable:
- if: IDF_TARGET == "linux"
components/hal/test_apps/tee_apm:
components/hal/test_apps/tee:
disable:
- if: IDF_TARGET not in ["esp32c6", "esp32h2", "esp32c5", "esp32c61"]

View File

@@ -6,7 +6,7 @@ set(COMPONENTS main)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(test_tee_apm)
project(test_tee_pms_cpu_intr)
include($ENV{IDF_PATH}/tools/ci/check_register_rw_half_word.cmake)
message(STATUS "Checking tee/apm registers are not read-write by half-word")

View File

@@ -1,15 +1,21 @@
| Supported Targets | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 |
| ----------------- | -------- | -------- | --------- | -------- |
# APM (Access Permission Management) Peripheral Test App
# TEE (Trusted Execution Environment) Test Application
This application validates region-based memory and peripheral access control via the APM (Access Permission Management) subsystem. It is primarily intended for bring-up and SoC-level functional testing.
This application is designed to validate the key components of the **ESP-TEE** framework, with a focus on:
Tests exercise various master-to-region accesses under different security modes (`TEE`, `REE0`, `REE1`, `REE2`). Outcomes are validated against expected APM behavior and known SoC-specific quirks.
1. **Permission Management (PMS: TEE controller + APM module)**
- Region-based memory and peripheral access control using the TEE controller and the Access Permission Management (APM) module
2. **Interrupts**
- Interrupt handling in Machine mode (M), User mode (U), and cross-mode contexts
It is primarily intended for early bring-up and SoC-level functional validation.
---
## Test Coverage
## Test Coverage: PMS (TEE + APM module)
### TEE mode default access behavior
@@ -51,6 +57,13 @@ Validates the per-peripheral access permissions for all security modes.
---
## Test Coverage: Interrupts
- **M-mode interrupts in M-mode**
- **U-mode interrupts in U-mode**
- **M-mode interrupts in U-mode**
- **U-mode interrupts in M-mode**
## Target Extension Guide
To add support for a new SoC target, create a test configuration header at:

View File

@@ -1,14 +1,21 @@
cmake_minimum_required(VERSION 3.16)
idf_build_get_property(target IDF_TARGET)
set(srcs "src/test_tee_vectors.S"
"src/test_panic_handler.c"
"src/test_intr_utils.c"
"src/test_apm_utils.c"
"src/test_setup_utils.c"
"src/test_tee_sys_apm.c")
if(CONFIG_SOC_APM_SUPPORT_TEE_PERI_ACCESS_CTRL)
list(APPEND srcs "src/test_tee_peri_apm.c")
set(srcs "src/common/test_apm_utils.c"
"src/common/test_intr_utils.c"
"src/common/test_setup_utils.c"
"src/common/test_panic_handler.c")
list(APPEND srcs "src/pms/test_tee_sys_apm.c"
"src/pms/test_tee_vectors.S")
if(CONFIG_SOC_SUPPORT_TEE_PERI_APM_TEST)
list(APPEND srcs "src/pms/test_tee_peri_apm.c")
endif()
if(CONFIG_SOC_SUPPORT_TEE_INTR_TEST)
list(APPEND srcs "src/cpu_intr/test_interrupt.c"
"src/cpu_intr/test_vectors_m.S"
"src/cpu_intr/test_vectors_u.S")
endif()
idf_component_register(SRCS "${srcs}"
@@ -20,6 +27,5 @@ idf_component_register(SRCS "${srcs}"
if(CONFIG_ULP_COPROC_ENABLED)
set(ulp_app_name ulp_lp_core_${COMPONENT_NAME})
set(ulp_rv_srcs "src/ulp/ulp_lp_core_main.c" "src/ulp/ulp_vectors.S")
set(ulp_exp_dep_srcs "src/test_tee_apm_pms.c")
ulp_embed_binary(${ulp_app_name} "${ulp_rv_srcs}" "${ulp_exp_dep_srcs}")
endif()

View File

@@ -0,0 +1,18 @@
menu "Test-app related"
config SOC_SUPPORT_TEE_SYS_APM_TEST
bool
depends on SOC_APM_CTRL_FILTER_SUPPORTED
default y
config SOC_SUPPORT_TEE_PERI_APM_TEST
bool
depends on SOC_APM_SUPPORT_TEE_PERI_ACCESS_CTRL
default y
config SOC_SUPPORT_TEE_INTR_TEST
bool
depends on IDF_TARGET_ESP32C5 || IDF_TARGET_ESP32C61
default y
endmenu

View File

@@ -0,0 +1,16 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
/* Test-cases */
void test_m_mode_intr_in_m_mode(void);
void test_u_mode_intr_in_u_mode(void);
void test_m_mode_intr_in_u_mode(void);
void test_u_mode_intr_in_m_mode(void);

View File

@@ -1,5 +1,5 @@
[mapping:pms]
archive: libpms.a
[mapping:test_pms_and_cpu_intr]
archive: libpms_and_cpu_intr.a
entries:
test_intr_utils (noflash)
test_panic_handler (noflash)

View File

@@ -0,0 +1,16 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#define TEST_INTR_NUM_PASS_IN_SEC (31)
#ifndef __ASSEMBLER__
#include "soc/interrupts.h"
#define TG0_T0_INTR_SRC (ETS_TG0_T0_LEVEL_INTR_SOURCE)
#define CPU_FROM_CPU_N_INTR_SRC(n) (ETS_FROM_CPU_INTR0_SOURCE + n)
#endif

View File

@@ -198,3 +198,45 @@
#define TEST_LP_PERI_ADDR7 TEST_LP_PERI_REGION7_START
#define TEST_LP_PERI_RESV_MASK BIT(0) | BIT(2)
/********* For PERI_APM *********/
#include "soc/reg_base.h"
/********* For PERI_APM *********/
#include "soc/uart_reg.h"
#include "soc/i2c_reg.h"
#include "soc/i2s_reg.h"
#include "soc/mcpwm_reg.h"
#include "soc/twaifd_reg.h"
#include "soc/ahb_dma_reg.h"
#include "soc/pau_reg.h"
#include "soc/interrupt_matrix_reg.h"
#include "soc/apb_saradc_reg.h"
#include "soc/timer_group_reg.h"
#include "soc/pcnt_reg.h"
#include "soc/io_mux_reg.h"
#include "soc/hp_system_reg.h"
#include "soc/pcr_reg.h"
#include "soc/spi_mem_reg.h"
#include "soc/hp_apm_reg.h"
#include "soc/cpu_apm_reg.h"
#include "soc/sha_reg.h"
#include "soc/cache_reg.h"
#include "soc/spi_reg.h"
#include "soc/bitscrambler_reg.h"
#include "soc/keymng_reg.h"
#include "soc/sdio_slc_host_reg.h"
#include "soc/efuse_reg.h"
#include "soc/pmu_reg.h"
#include "soc/lp_clkrst_reg.h"
#include "soc/lp_aon_reg.h"
#include "soc/lp_wdt_reg.h"
#include "soc/lpperi_reg.h"
#include "soc/lp_analog_peri_reg.h"
#include "soc/lp_uart_reg.h"
#include "soc/lp_i2c_reg.h"
#include "soc/lp_i2c_ana_mst_reg.h"
#include "soc/huk_reg.h"
#include "soc/lp_apm_reg.h"

View File

@@ -0,0 +1,152 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include <assert.h>
#include "esp_attr.h"
#include "soc/soc_caps.h"
#include "soc/clic_reg.h"
#include "hal/interrupt_clic_ll.h"
#include "riscv/csr.h"
#include "riscv/encoding.h"
#include "riscv/interrupt.h"
#include "esp_private/interrupt_clic.h"
#include "sdkconfig.h"
#define UTVT_CSR (0x007)
#define UINTSTATUS_CSR (0xCB1)
#define UINTTHRESH_CSR (0x047)
#ifdef __cplusplus
extern "C" {
#endif
FORCE_INLINE_ATTR void test_rv_utils_set_xtvec(uint32_t xtvec_val, int mode)
{
xtvec_val |= MTVEC_MODE_CSR;
if (mode == PRV_M) {
RV_WRITE_CSR(mtvec, xtvec_val);
} else {
RV_WRITE_CSR(utvec, xtvec_val);
}
}
FORCE_INLINE_ATTR void test_rv_utils_set_xtvt(uint32_t xtvt_val, int mode)
{
if (mode == PRV_M) {
RV_WRITE_CSR(MTVT_CSR, xtvt_val);
} else {
RV_WRITE_CSR(UTVT_CSR, xtvt_val);
}
}
FORCE_INLINE_ATTR void test_rv_utils_intr_global_enable(int mode)
{
if (mode == PRV_M) {
RV_SET_CSR(mstatus, MSTATUS_MIE);
RV_SET_CSR(mstatus, MSTATUS_UIE);
} else {
RV_SET_CSR(ustatus, USTATUS_UIE);
}
}
FORCE_INLINE_ATTR void test_rv_utils_intr_global_disable(int mode)
{
if (mode == PRV_M) {
RV_CLEAR_CSR(mstatus, MSTATUS_MIE);
RV_CLEAR_CSR(mstatus, MSTATUS_UIE);
} else {
RV_CLEAR_CSR(ustatus, USTATUS_UIE);
}
}
FORCE_INLINE_ATTR void test_rv_utils_intr_enable(uint32_t intr_mask)
{
// Machine mode
// Disable all interrupts to make updating of the interrupt mask atomic.
unsigned old_xstatus = RV_CLEAR_CSR(mstatus, MSTATUS_MIE);
while (intr_mask != 0) {
// __builtin_ffs returns one plus the index of the lsb 1-bit of x. If x is zero, returns zero
uint32_t intr_num = __builtin_ffs(intr_mask) - 1;
*(uint8_t volatile *)(BYTE_CLIC_INT_IE_REG(intr_num + CLIC_EXT_INTR_NUM_OFFSET)) = BYTE_CLIC_INT_IE;
intr_mask &= (intr_mask - 1);
}
RV_SET_CSR(mstatus, old_xstatus & MSTATUS_MIE);
}
FORCE_INLINE_ATTR void test_rv_utils_intr_disable(uint32_t intr_mask)
{
// Machine mode
// Disable all interrupts to make updating of the interrupt mask atomic.
unsigned old_xstatus = RV_CLEAR_CSR(mstatus, MSTATUS_MIE);
while (intr_mask != 0) {
// __builtin_ffs returns one plus the index of the lsb 1-bit of x. If x is zero, returns zero
uint32_t intr_num = __builtin_ffs(intr_mask) - 1;
*(uint8_t volatile *)(BYTE_CLIC_INT_IE_REG(intr_num + CLIC_EXT_INTR_NUM_OFFSET)) = 0;
intr_mask &= (intr_mask - 1);
}
RV_SET_CSR(mstatus, old_xstatus & MSTATUS_MIE);
}
FORCE_INLINE_ATTR void test_rv_utils_intr_set_type(int intr_num, enum intr_type type)
{
assert(intr_num >= 0 && intr_num < SOC_CPU_INTR_NUM);
/* TODO: CLIC supports both rising and falling edge triggered interrupts.
* Currently only rising edge is implemented.
*/
volatile uint8_t *attr_reg = (volatile uint8_t *)BYTE_CLIC_INT_ATTR_REG(intr_num + CLIC_EXT_INTR_NUM_OFFSET);
uint8_t attr = *attr_reg;
attr &= ~BYTE_CLIC_INT_ATTR_TRIG_M;
if (type == INTR_TYPE_EDGE) {
attr |= (INTR_TYPE_EDGE << BYTE_CLIC_INT_ATTR_TRIG_S);
}
*attr_reg = attr;
}
FORCE_INLINE_ATTR void test_rv_utils_intr_set_priority(int intr_num, int priority)
{
assert(intr_num >= 0 && intr_num < SOC_CPU_INTR_NUM);
*(uint8_t volatile *)(BYTE_CLIC_INT_CTL_REG(intr_num + CLIC_EXT_INTR_NUM_OFFSET)) = (priority << BYTE_CLIC_INT_CTL_S);
}
FORCE_INLINE_ATTR void test_rv_utils_intr_set_threshold(int priority_threshold, int mode)
{
uint32_t adj_threshold = ((priority_threshold << (8 - NLBITS)) | 0x1f);
if (mode == PRV_M) {
REG_SET_FIELD(CLIC_INT_THRESH_REG, CLIC_CPU_INT_THRESH, adj_threshold);
RV_WRITE_CSR(MINTTHRESH_CSR, adj_threshold);
} else {
RV_WRITE_CSR(UINTTHRESH_CSR, adj_threshold);
}
}
FORCE_INLINE_ATTR void test_rv_utils_intr_set_mode(int intr_num, int mode)
{
assert(intr_num >= 0 && intr_num < SOC_CPU_INTR_NUM);
REG_SET_FIELD(CLIC_INT_CTRL_REG(intr_num + CLIC_EXT_INTR_NUM_OFFSET), CLIC_INT_ATTR_MODE, mode);
}
FORCE_INLINE_ATTR void test_rv_utils_intr_set_vectored(int intr_num, bool vectored)
{
REG_SET_FIELD(CLIC_INT_CTRL_REG(intr_num + CLIC_EXT_INTR_NUM_OFFSET), CLIC_INT_ATTR_SHV, vectored ? 1 : 0);
}
FORCE_INLINE_ATTR void test_rv_utils_intr_enable_u_mode(bool enable)
{
REG_SET_FIELD(CLIC_INT_CONFIG_REG, CLIC_INT_CONFIG_NMBITS, enable ? 0x01 : 0x00);
REG_SET_FIELD(CLIC_INT_CONFIG_REG, CLIC_INT_CONFIG_UNLBITS, NLBITS);
REG_SET_FIELD(CLIC_INT_CONFIG_REG, CLIC_INT_CONFIG_MNLBITS, NLBITS);
}
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,16 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#define TEST_INTR_NUM_PASS_IN_SEC (31)
#ifndef __ASSEMBLER__
#include "soc/interrupts.h"
#define TG0_T0_INTR_SRC (ETS_TG0_T0_INTR_SOURCE)
#define CPU_FROM_CPU_N_INTR_SRC(n) (ETS_CPU_INTR_FROM_CPU_0_SOURCE + n)
#endif

View File

@@ -0,0 +1,152 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include <assert.h>
#include "esp_attr.h"
#include "soc/soc_caps.h"
#include "soc/clic_reg.h"
#include "hal/interrupt_clic_ll.h"
#include "riscv/csr.h"
#include "riscv/encoding.h"
#include "riscv/interrupt.h"
#include "esp_private/interrupt_clic.h"
#include "sdkconfig.h"
#define UTVT_CSR (0x007)
#define UINTSTATUS_CSR (0xCB1)
#define UINTTHRESH_CSR (0x047)
#ifdef __cplusplus
extern "C" {
#endif
FORCE_INLINE_ATTR void test_rv_utils_set_xtvec(uint32_t xtvec_val, int mode)
{
xtvec_val |= MTVEC_MODE_CSR;
if (mode == PRV_M) {
RV_WRITE_CSR(mtvec, xtvec_val);
} else {
RV_WRITE_CSR(utvec, xtvec_val);
}
}
FORCE_INLINE_ATTR void test_rv_utils_set_xtvt(uint32_t xtvt_val, int mode)
{
if (mode == PRV_M) {
RV_WRITE_CSR(MTVT_CSR, xtvt_val);
} else {
RV_WRITE_CSR(UTVT_CSR, xtvt_val);
}
}
FORCE_INLINE_ATTR void test_rv_utils_intr_global_enable(int mode)
{
if (mode == PRV_M) {
RV_SET_CSR(mstatus, MSTATUS_MIE);
RV_SET_CSR(mstatus, MSTATUS_UIE);
} else {
RV_SET_CSR(ustatus, USTATUS_UIE);
}
}
FORCE_INLINE_ATTR void test_rv_utils_intr_global_disable(int mode)
{
if (mode == PRV_M) {
RV_CLEAR_CSR(mstatus, MSTATUS_MIE);
RV_CLEAR_CSR(mstatus, MSTATUS_UIE);
} else {
RV_CLEAR_CSR(ustatus, USTATUS_UIE);
}
}
FORCE_INLINE_ATTR void test_rv_utils_intr_enable(uint32_t intr_mask)
{
// Machine mode
// Disable all interrupts to make updating of the interrupt mask atomic.
unsigned old_xstatus = RV_CLEAR_CSR(mstatus, MSTATUS_MIE);
while (intr_mask != 0) {
// __builtin_ffs returns one plus the index of the lsb 1-bit of x. If x is zero, returns zero
uint32_t intr_num = __builtin_ffs(intr_mask) - 1;
*(uint8_t volatile *)(BYTE_CLIC_INT_IE_REG(intr_num + CLIC_EXT_INTR_NUM_OFFSET)) = BYTE_CLIC_INT_IE;
intr_mask &= (intr_mask - 1);
}
RV_SET_CSR(mstatus, old_xstatus & MSTATUS_MIE);
}
FORCE_INLINE_ATTR void test_rv_utils_intr_disable(uint32_t intr_mask)
{
// Machine mode
// Disable all interrupts to make updating of the interrupt mask atomic.
unsigned old_xstatus = RV_CLEAR_CSR(mstatus, MSTATUS_MIE);
while (intr_mask != 0) {
// __builtin_ffs returns one plus the index of the lsb 1-bit of x. If x is zero, returns zero
uint32_t intr_num = __builtin_ffs(intr_mask) - 1;
*(uint8_t volatile *)(BYTE_CLIC_INT_IE_REG(intr_num + CLIC_EXT_INTR_NUM_OFFSET)) = 0;
intr_mask &= (intr_mask - 1);
}
RV_SET_CSR(mstatus, old_xstatus & MSTATUS_MIE);
}
FORCE_INLINE_ATTR void test_rv_utils_intr_set_type(int intr_num, enum intr_type type)
{
assert(intr_num >= 0 && intr_num < SOC_CPU_INTR_NUM);
/* TODO: CLIC supports both rising and falling edge triggered interrupts.
* Currently only rising edge is implemented.
*/
volatile uint8_t *attr_reg = (volatile uint8_t *)BYTE_CLIC_INT_ATTR_REG(intr_num + CLIC_EXT_INTR_NUM_OFFSET);
uint8_t attr = *attr_reg;
attr &= ~BYTE_CLIC_INT_ATTR_TRIG_M;
if (type == INTR_TYPE_EDGE) {
attr |= (INTR_TYPE_EDGE << BYTE_CLIC_INT_ATTR_TRIG_S);
}
*attr_reg = attr;
}
FORCE_INLINE_ATTR void test_rv_utils_intr_set_priority(int intr_num, int priority)
{
assert(intr_num >= 0 && intr_num < SOC_CPU_INTR_NUM);
*(uint8_t volatile *)(BYTE_CLIC_INT_CTL_REG(intr_num + CLIC_EXT_INTR_NUM_OFFSET)) = (priority << BYTE_CLIC_INT_CTL_S);
}
FORCE_INLINE_ATTR void test_rv_utils_intr_set_threshold(int priority_threshold, int mode)
{
uint32_t adj_threshold = ((priority_threshold << (8 - NLBITS)) | 0x1f);
if (mode == PRV_M) {
REG_SET_FIELD(CLIC_INT_THRESH_REG, CLIC_CPU_INT_THRESH, adj_threshold);
RV_WRITE_CSR(MINTTHRESH_CSR, adj_threshold);
} else {
RV_WRITE_CSR(UINTTHRESH_CSR, adj_threshold);
}
}
FORCE_INLINE_ATTR void test_rv_utils_intr_set_mode(int intr_num, int mode)
{
assert(intr_num >= 0 && intr_num < SOC_CPU_INTR_NUM);
REG_SET_FIELD(CLIC_INT_CTRL_REG(intr_num + CLIC_EXT_INTR_NUM_OFFSET), CLIC_INT_ATTR_MODE, mode);
}
FORCE_INLINE_ATTR void test_rv_utils_intr_set_vectored(int intr_num, bool vectored)
{
REG_SET_FIELD(CLIC_INT_CTRL_REG(intr_num + CLIC_EXT_INTR_NUM_OFFSET), CLIC_INT_ATTR_SHV, vectored ? 1 : 0);
}
FORCE_INLINE_ATTR void test_rv_utils_intr_enable_u_mode(bool enable)
{
REG_SET_FIELD(CLIC_INT_CONFIG_REG, CLIC_INT_CONFIG_NMBITS, enable ? 0x01 : 0x00);
REG_SET_FIELD(CLIC_INT_CONFIG_REG, CLIC_INT_CONFIG_UNLBITS, NLBITS);
REG_SET_FIELD(CLIC_INT_CONFIG_REG, CLIC_INT_CONFIG_MNLBITS, NLBITS);
}
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,17 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
/********************************** Utility *********************************/
void test_intr_utils_init(void);
void test_intr_utils_deinit(void);
void test_intr_alloc(int mode, int priority, int intr_src, intr_handler_t func, void *arg);
void test_intr_free_all(void);

View File

@@ -96,4 +96,4 @@ void test_gdma_deinit(void);
void test_gdma_m2m_transfer(uint8_t *src, uint8_t *dest, size_t size);
void test_gdma_wait_done(void);
void test_delay_us(uint32_t us);
void test_delay_ms(uint32_t ms);

View File

@@ -0,0 +1,251 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <assert.h>
#include "soc/soc_caps.h"
#include "soc/reg_base.h"
#include "soc/interrupts.h"
#include "soc/interrupt_matrix_reg.h"
#include "riscv/encoding.h"
#include "riscv/csr.h"
#include "riscv/rv_utils.h"
#include "esp_cpu.h"
#include "esp_rom_sys.h"
#if SOC_INT_PLIC_SUPPORTED
#include "esp_private/interrupt_plic.h"
#elif SOC_INT_CLIC_SUPPORTED
#include "esp_private/interrupt_clic.h"
#else
#include "esp_private/interrupt_intc.h"
#endif
#if CONFIG_SOC_SUPPORT_TEE_INTR_TEST
#include "test_rv_utils.h"
#include "test_cpu_intr_params.h"
#endif
/********************************** Vector Table Redirection *******************************/
void set_test_vector_table(void)
{
extern int _test_vector_table;
rv_utils_intr_global_disable();
rv_utils_set_mtvec((uintptr_t)&_test_vector_table);
rv_utils_intr_global_enable();
}
void restore_default_vector_table(void)
{
extern int _vector_table;
rv_utils_intr_global_disable();
rv_utils_set_mtvec((uintptr_t)&_vector_table);
rv_utils_intr_global_enable();
}
/********************************** Privilege Mode Switch *********************************/
void test_m2u_switch(void)
{
int mode = esp_cpu_get_curr_privilege_level();
assert(mode == PRV_M);
asm volatile("ecall\n");
mode = esp_cpu_get_curr_privilege_level();
assert(mode == PRV_U);
}
void test_u2m_switch(void)
{
int mode = esp_cpu_get_curr_privilege_level();
assert(mode == PRV_U);
asm volatile("ecall\n");
mode = esp_cpu_get_curr_privilege_level();
assert(mode == PRV_M);
}
/********************************** Interrupt Handler *************************************/
extern void _global_interrupt_handler(intptr_t sp, int mcause);
/* called from test_tee_vectors.S */
void _test_global_interrupt_handler(intptr_t sp, int mcause)
{
_global_interrupt_handler(sp, mcause);
}
#if CONFIG_SOC_SUPPORT_TEE_INTR_TEST
extern int _vector_table;
extern int _mtvt_table;
extern int _test_mtvec_table;
extern int _test_mtvt_table;
extern int _test_utvec_table;
extern int _test_utvt_table;
static void test_redirect_vector_table(bool redirect)
{
if (redirect) {
test_rv_utils_set_xtvec((uintptr_t)&_test_mtvec_table, PRV_M);
test_rv_utils_set_xtvt((uintptr_t)&_test_mtvt_table, PRV_M);
test_rv_utils_set_xtvec((uintptr_t)&_test_utvec_table, PRV_U);
test_rv_utils_set_xtvt((uintptr_t)&_test_utvt_table, PRV_U);
#if !SOC_CPU_HAS_CSR_PC
RV_SET_CSR(mcounteren, 0x07);
#endif
} else {
test_rv_utils_set_xtvt((uintptr_t)&_mtvt_table, PRV_M);
test_rv_utils_set_xtvec((uintptr_t)&_vector_table, PRV_M);
}
}
/***************************** Miscellaneous: Interrupts (CLIC configuration save/restore) *****************************/
#define INTMTX_REG(i) (DR_REG_INTMTX_BASE + ((i) * 4))
static uint32_t s_intr_src_map[ETS_MAX_INTR_SOURCE];
static uint32_t s_intr_clic_ctrl_reg[SOC_CPU_INTR_NUM];
static bool is_intr_ctx_saved = false;
static void test_save_intr_ctx(void)
{
// Save and unroute all interrupt matrix sources
for (int i = 0; i < ETS_MAX_INTR_SOURCE; i++) {
uint32_t offs = INTMTX_REG(i);
s_intr_src_map[i] = REG_READ(offs);
REG_WRITE(offs, 0x00U);
}
// Save CLIC configuration registers
for (int i = 0; i < SOC_CPU_INTR_NUM; i++) {
s_intr_clic_ctrl_reg[i] = REG_READ(CLIC_INT_CTRL_REG(CLIC_EXT_INTR_NUM_OFFSET + i));
REG_WRITE(CLIC_INT_CTRL_REG(i), 0x00U);
}
is_intr_ctx_saved = true;
}
static void test_restore_intr_ctx(void)
{
if (!is_intr_ctx_saved) {
return;
}
// Restore interrupt matrix sources
for (int i = 0; i < ETS_MAX_INTR_SOURCE; i++) {
REG_WRITE(INTMTX_REG(i), s_intr_src_map[i]);
}
// Restore CLIC configuration registers
for (int i = 0; i < SOC_CPU_INTR_NUM; i++) {
REG_WRITE(CLIC_INT_CTRL_REG(CLIC_EXT_INTR_NUM_OFFSET + i), s_intr_clic_ctrl_reg[i]);
}
is_intr_ctx_saved = false;
}
void test_intr_utils_init(void)
{
test_rv_utils_intr_global_disable(PRV_M);
test_save_intr_ctx();
test_redirect_vector_table(true);
test_rv_utils_intr_set_threshold(RVHAL_INTR_ENABLE_THRESH, PRV_M);
test_rv_utils_intr_set_threshold(RVHAL_INTR_ENABLE_THRESH, PRV_U);
test_rv_utils_intr_enable_u_mode(true);
test_rv_utils_intr_global_enable(PRV_M);
}
void test_intr_utils_deinit(void)
{
test_rv_utils_intr_global_disable(PRV_M);
test_rv_utils_intr_enable_u_mode(false);
test_redirect_vector_table(false);
test_restore_intr_ctx();
test_rv_utils_intr_global_enable(PRV_M);
}
/***************************** Miscellaneous: Interrupts (test allocation/freeing) *****************************/
#define TEST_INTR_NUM_MIN (12)
#define TEST_INTR_NUM_MAX (16)
#define TEST_INTR_NUM_COUNT (TEST_INTR_NUM_MAX - TEST_INTR_NUM_MIN)
#define TEST_INTR_ALLOC_MASK (((1U << TEST_INTR_NUM_COUNT) - 1) << TEST_INTR_NUM_MIN)
#define TEST_INTR_IDX(num) ((num) - TEST_INTR_NUM_MIN)
typedef struct {
int mode;
int intr_src;
} test_intr_entry_t;
static uint32_t s_intr_status = 0x00U;
static test_intr_entry_t s_intr_table[TEST_INTR_NUM_COUNT];
void test_intr_alloc(int mode, int priority, int intr_src, intr_handler_t func, void *arg)
{
int intr_num = -1;
if (intr_src == ETS_MAX_INTR_SOURCE) {
intr_num = TEST_INTR_NUM_PASS_IN_SEC;
} else {
intr_num = __builtin_ctz(~s_intr_status & TEST_INTR_ALLOC_MASK);
if (intr_num < TEST_INTR_NUM_MIN || intr_num > TEST_INTR_NUM_MAX) {
return; // No free interrupts
}
intr_handler_set(intr_num, func, arg);
esp_rom_route_intr_matrix(0, intr_src, intr_num);
}
s_intr_status |= BIT(intr_num);
s_intr_table[TEST_INTR_IDX(intr_num)] = (test_intr_entry_t){
.mode = mode,
.intr_src = intr_src,
};
test_rv_utils_intr_set_type(intr_num, INTR_TYPE_LEVEL);
test_rv_utils_intr_set_priority(intr_num, priority);
test_rv_utils_intr_set_vectored(intr_num, true);
test_rv_utils_intr_set_mode(intr_num, mode);
if (mode == PRV_U) {
REG_SET_BIT(DR_REG_INTMTX_BASE + 4 * intr_src, BIT(8));
}
if (mode == PRV_M && intr_src == ETS_MAX_INTR_SOURCE) {
REG_WRITE(INTERRUPT_CORE0_SIG_IDX_ASSERT_IN_SEC_REG, intr_num + CLIC_EXT_INTR_NUM_OFFSET);
}
test_rv_utils_intr_enable(BIT(intr_num));
}
void test_intr_free_all(void)
{
for (int intr_num = TEST_INTR_NUM_MIN; intr_num <= TEST_INTR_NUM_MAX; intr_num++) {
if (!(s_intr_status & BIT(intr_num))) {
continue;
}
test_intr_entry_t *entry = &s_intr_table[TEST_INTR_IDX(intr_num)];
intr_handler_set(intr_num, NULL, NULL);
interrupt_clic_ll_route(0, entry->intr_src, ETS_INVALID_INUM);
if (entry->mode == PRV_U) {
REG_CLR_BIT(DR_REG_INTMTX_BASE + 4 * entry->intr_src, BIT(8));
}
if (entry->mode == PRV_M && entry->intr_src == ETS_MAX_INTR_SOURCE) {
REG_WRITE(INTERRUPT_CORE0_SIG_IDX_ASSERT_IN_SEC_REG, ETS_INVALID_INUM);
}
test_rv_utils_intr_disable(BIT(intr_num));
s_intr_status &= ~BIT(intr_num);
}
}
#endif

View File

@@ -51,10 +51,16 @@ void test_panicHandler(RvExcFrame *frame, int exccause)
}
if (mcause == MCAUSE_ILLEGAL_INSTRUCTION) {
frame->mepc = frame->ra;
uint32_t mscratch = RV_READ_CSR(mscratch);
/* Check if panic is from the interrupt or PMS related test-cases */
if (mscratch == 0xDEADC0DE) {
frame->mepc += 0x04U;
RV_WRITE_CSR(mscratch, 0x00U);
} else {
frame->mepc = frame->ra;
}
return;
}
/* PERI_APM access fault */
#if SOC_APM_SUPPORT_TEE_PERI_ACCESS_CTRL
if (regs->mtval >= SOC_PERIPHERAL_LOW && regs->mtval < SOC_PERIPHERAL_HIGH) {

View File

@@ -38,8 +38,8 @@
/***************************** Utility - LP_CPU *****************************/
#if CONFIG_ULP_COPROC_ENABLED
extern const uint8_t ulp_lp_core_main_bin_start[] asm("_binary_ulp_lp_core_pms_bin_start");
extern const uint8_t ulp_lp_core_main_bin_end[] asm("_binary_ulp_lp_core_pms_bin_end");
extern const uint8_t ulp_lp_core_main_bin_start[] asm("_binary_ulp_lp_core_pms_and_cpu_intr_bin_start");
extern const uint8_t ulp_lp_core_main_bin_end[] asm("_binary_ulp_lp_core_pms_and_cpu_intr_bin_end");
void test_boot_lp_cpu(void)
{
@@ -226,8 +226,9 @@ void test_gdma_wait_done(void)
/***************************** Miscellaneous *****************************/
IRAM_ATTR void test_delay_us(uint32_t us)
IRAM_ATTR void test_delay_ms(uint32_t ms)
{
uint32_t us = ms * 1000U;
#if SOC_CPU_HAS_CSR_PC
esp_rom_delay_us(us);
#else

View File

@@ -0,0 +1,278 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include "soc/soc.h"
#include "soc/soc_caps.h"
#include "soc/intpri_reg.h"
#include "soc/pcr_reg.h"
#include "hal/timer_ll.h"
#include "esp_cpu.h"
#include "esp_attr.h"
#include "esp_rom_sys.h"
#include "test_rv_utils.h"
#include "unity.h"
#include "sdkconfig.h"
#include "test_pms_priv.h"
#include "test_cpu_intr_priv.h"
#include "test_cpu_intr_params.h"
#define TEST_INTR_MAX_COUNT (8)
/* ---------------------------------------------------- Utility - TIMER ---------------------------------------------------- */
#define TEST_TIMER_GROUP (0)
#define TEST_TIMER_ID (0)
#define TEST_TIMER_DIVIDER (80)
#define TEST_TIMER_RESOLUTION_HZ (1000000ULL) // 1MHz, 1 tick = 1us
#define TEST_TIMER_PERIOD_S (0.01f) // 10ms @ resolution 1MHz
#define TEST_INTPRI_PERIOD_S (0.10f) // 100ms (intpri trigger interval)
#define TEST_TIMER_EXP_COUNT (TEST_INTR_MAX_COUNT * (TEST_INTPRI_PERIOD_S / TEST_TIMER_PERIOD_S))
static timg_dev_t *timg_dev = TIMER_LL_GET_HW(TEST_TIMER_GROUP);
IRAM_ATTR static void test_timer_isr(void *arg)
{
uint32_t *intr_count = (uint32_t *)arg;
*intr_count = *intr_count + 1;
esp_rom_printf("[mode: %d] TIMER-%d interrupt triggered (%d)\n", esp_cpu_get_curr_privilege_level(), TEST_TIMER_GROUP, *intr_count);
/* Clear interrupt and re-enable the alarm */
timer_ll_clear_intr_status(timg_dev, TIMER_LL_EVENT_ALARM(TEST_TIMER_ID));
timer_ll_enable_alarm(timg_dev, TEST_TIMER_ID, true);
}
static void test_timer_deinit(void)
{
const uint32_t timer_id = TEST_TIMER_ID;
// Disable and clear timer state
timer_ll_enable_counter(timg_dev, timer_id, false);
timer_ll_enable_auto_reload(timg_dev, timer_id, false);
timer_ll_enable_alarm(timg_dev, timer_id, false);
timer_ll_enable_intr(timg_dev, TIMER_LL_EVENT_ALARM(timer_id), false);
timer_ll_clear_intr_status(timg_dev, TIMER_LL_EVENT_ALARM(timer_id));
// Reset the counter
uint64_t prev_val = timer_ll_get_reload_value(timg_dev, timer_id);
timer_ll_set_reload_value(timg_dev, timer_id, 0);
timer_ll_trigger_soft_reload(timg_dev, timer_id);
timer_ll_set_reload_value(timg_dev, timer_id, prev_val);
timer_ll_enable_clock(TEST_TIMER_GROUP, timer_id, false);
}
static void test_timer_init(int mode, volatile uint32_t *arg)
{
const uint32_t group_id = TEST_TIMER_GROUP;
const uint32_t timer_id = TEST_TIMER_ID;
test_timer_deinit();
// Enable peripheral clock and reset hardware
_timer_ll_enable_bus_clock(group_id, true);
_timer_ll_reset_register(group_id);
// Select clock source and enable module clock
// Enable the default clock source PLL_F80M
REG_SET_BIT(PCR_PLL_DIV_CLK_EN_REG, PCR_PLL_80M_CLK_EN);
timer_ll_set_clock_source(group_id, timer_id, GPTIMER_CLK_SRC_DEFAULT);
timer_ll_enable_clock(group_id, timer_id, true);
timer_ll_set_clock_prescale(timg_dev, timer_id, TEST_TIMER_DIVIDER);
timer_ll_set_count_direction(timg_dev, timer_id, GPTIMER_COUNT_UP);
// Register ISR
test_intr_alloc(mode, 2, TG0_T0_INTR_SRC, &test_timer_isr, (void *)arg);
timer_ll_enable_intr(timg_dev, TIMER_LL_EVENT_ALARM(timer_id), true);
// Configure and enable timer alarm
timer_ll_set_alarm_value(timg_dev, timer_id, TEST_TIMER_PERIOD_S * TEST_TIMER_RESOLUTION_HZ);
timer_ll_enable_auto_reload(timg_dev, timer_id, true);
timer_ll_enable_alarm(timg_dev, timer_id, true);
timer_ll_enable_counter(timg_dev, timer_id, true);
}
/* ---------------------------------------------------- Utility - INTPRI ---------------------------------------------------- */
#define TEST_INTR_TK_INIT 0x8BADF00D
#define TEST_INTR_TK_DONE 0xDEFEC8ED
#define CPU_INTR_FROM_CPU_N_REG(n) (INTPRI_CPU_INTR_FROM_CPU_0_REG + 4 * (n))
typedef struct {
volatile int id;
volatile int curr_count;
volatile int max_count;
volatile int token;
} test_intr_args_ctx_t;
FORCE_INLINE_ATTR void intpri_cpu_clr_intr(uint32_t n)
{
WRITE_PERI_REG(CPU_INTR_FROM_CPU_N_REG(n), 0);
}
FORCE_INLINE_ATTR void intpri_cpu_trig_intr(uint32_t n)
{
WRITE_PERI_REG(CPU_INTR_FROM_CPU_N_REG(n), 1);
}
FORCE_INLINE_ATTR uint32_t intpri_cpu_get_intr_state(uint32_t n)
{
return READ_PERI_REG(CPU_INTR_FROM_CPU_N_REG(n));
}
IRAM_ATTR static void test_intpri_isr(void *arg)
{
test_intr_args_ctx_t *ctx = (test_intr_args_ctx_t *)arg;
/* Clear interrupt */
intpri_cpu_clr_intr(ctx->id);
ctx->curr_count++;
if (ctx->curr_count >= ctx->max_count) {
ctx->token = TEST_INTR_TK_DONE;
}
esp_rom_printf("[mode: %d] INTPRI-%d interrupt triggered (%d)\n", esp_cpu_get_curr_privilege_level(), ctx->id, ctx->curr_count);
}
/* ---------------------------------------------------- Test-cases ---------------------------------------------------- */
void test_m_mode_intr_in_m_mode(void)
{
test_apm_ctrl_reset_all();
test_intr_utils_init();
test_intr_args_ctx_t ctx = {
.id = 0,
.curr_count = 0,
.max_count = TEST_INTR_MAX_COUNT,
.token = TEST_INTR_TK_INIT,
};
test_intr_alloc(PRV_M, 1, CPU_FROM_CPU_N_INTR_SRC(ctx.id), &test_intpri_isr, &ctx);
volatile uint32_t intr_count = 0;
test_timer_init(PRV_M, &intr_count);
while (ctx.token != TEST_INTR_TK_DONE) {
intpri_cpu_trig_intr(ctx.id);
test_delay_ms(TEST_INTPRI_PERIOD_S * 1000U);
}
TEST_ASSERT(ctx.curr_count == TEST_INTR_MAX_COUNT);
test_timer_deinit();
TEST_ASSERT(intr_count >= TEST_TIMER_EXP_COUNT);
test_intr_free_all();
test_intr_utils_deinit();
}
void test_u_mode_intr_in_u_mode(void)
{
test_apm_ctrl_reset_all();
test_intr_utils_init();
test_intr_args_ctx_t ctx = {
.id = 0,
.curr_count = 0,
.max_count = TEST_INTR_MAX_COUNT,
.token = TEST_INTR_TK_INIT,
};
test_intr_alloc(PRV_U, 1, CPU_FROM_CPU_N_INTR_SRC(ctx.id), &test_intpri_isr, &ctx);
volatile uint32_t intr_count = 0;
test_timer_init(PRV_U, &intr_count);
test_m2u_switch();
while (ctx.token != TEST_INTR_TK_DONE) {
intpri_cpu_trig_intr(ctx.id);
test_delay_ms(TEST_INTPRI_PERIOD_S * 1000U);
}
test_u2m_switch();
TEST_ASSERT(ctx.curr_count == TEST_INTR_MAX_COUNT);
test_timer_deinit();
TEST_ASSERT(intr_count >= TEST_TIMER_EXP_COUNT);
test_intr_free_all();
test_intr_utils_deinit();
}
void test_m_mode_intr_in_u_mode(void)
{
test_apm_ctrl_reset_all();
test_intr_utils_init();
test_intr_args_ctx_t ctx = {
.id = 0,
.curr_count = 0,
.max_count = TEST_INTR_MAX_COUNT,
.token = TEST_INTR_TK_INIT,
};
test_intr_alloc(PRV_M, 1, CPU_FROM_CPU_N_INTR_SRC(ctx.id), &test_intpri_isr, &ctx);
test_intr_alloc(PRV_M, 1, ETS_MAX_INTR_SOURCE, NULL, NULL);
volatile uint32_t intr_count = 0;
test_timer_init(PRV_U, &intr_count);
test_m2u_switch();
while (ctx.token != TEST_INTR_TK_DONE) {
intpri_cpu_trig_intr(ctx.id);
test_delay_ms(TEST_INTPRI_PERIOD_S * 1000U);
}
test_u2m_switch();
TEST_ASSERT(ctx.curr_count == TEST_INTR_MAX_COUNT);
test_timer_deinit();
TEST_ASSERT(intr_count >= TEST_TIMER_EXP_COUNT);
test_intr_free_all();
test_intr_utils_deinit();
}
void test_u_mode_intr_in_m_mode(void)
{
test_apm_ctrl_reset_all();
test_intr_utils_init();
test_intr_alloc(PRV_M, 1, ETS_MAX_INTR_SOURCE, NULL, NULL);
test_intr_args_ctx_t ctx = {
.id = 0,
.curr_count = 0,
.max_count = TEST_INTR_MAX_COUNT,
.token = TEST_INTR_TK_INIT,
};
test_intr_alloc(PRV_U, 1, CPU_FROM_CPU_N_INTR_SRC(ctx.id), &test_intpri_isr, &ctx);
volatile uint32_t intr_count = 0;
test_timer_init(PRV_M, &intr_count);
while (ctx.token != TEST_INTR_TK_DONE) {
intpri_cpu_trig_intr(ctx.id);
test_delay_ms(TEST_INTPRI_PERIOD_S * 1000U);
}
TEST_ASSERT(ctx.curr_count == TEST_INTR_MAX_COUNT);
test_timer_deinit();
TEST_ASSERT(intr_count >= TEST_TIMER_EXP_COUNT);
test_intr_free_all();
test_intr_utils_deinit();
}

View File

@@ -0,0 +1,467 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "soc/soc.h"
#include "soc/soc_caps.h"
#include "soc/interrupt_reg.h"
#include "soc/interrupt_matrix_reg.h"
#include "riscv/encoding.h"
#include "riscv/rvruntime-frames.h"
#include "esp_private/panic_reason.h"
#include "esp_private/vectors_const.h"
#include "test_cpu_intr_params.h"
#include "sdkconfig.h"
.equ SAVE_REGS, 32
.equ CONTEXT_SIZE, (SAVE_REGS * 4)
.equ ECALL_U_MODE, 0x8
.equ ECALL_M_MODE, 0xb
.equ INTR_M2U_MAGIC, 0x1f
.equ INTR_U2M_RTNVAL, 0xc0de
.equ INTMTX_SEC_STATUS_REG, INTERRUPT_CORE0_SECURE_STATUS_REG
.equ INTMTX_SIG_IDX_ASSERT_IN_SEC_REG, INTERRUPT_CORE0_SIG_IDX_ASSERT_IN_SEC_REG
.equ VECTORS_XCAUSE_XPIE_MASK, 0x08000000
.equ panic_from_excp, test_panicHandler
.global _test_uintr_handler
.section .data
.align 4
.global _m_sp
_m_sp:
.word 0
/* 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
* can be overridden. */
.macro save_general_regs cxt_size=CONTEXT_SIZE
addi sp, sp, -\cxt_size
sw ra, RV_STK_RA(sp)
sw tp, RV_STK_TP(sp)
sw t0, RV_STK_T0(sp)
sw t1, RV_STK_T1(sp)
sw t2, RV_STK_T2(sp)
sw s0, RV_STK_S0(sp)
sw s1, RV_STK_S1(sp)
sw a0, RV_STK_A0(sp)
sw a1, RV_STK_A1(sp)
sw a2, RV_STK_A2(sp)
sw a3, RV_STK_A3(sp)
sw a4, RV_STK_A4(sp)
sw a5, RV_STK_A5(sp)
sw a6, RV_STK_A6(sp)
sw a7, RV_STK_A7(sp)
sw s2, RV_STK_S2(sp)
sw s3, RV_STK_S3(sp)
sw s4, RV_STK_S4(sp)
sw s5, RV_STK_S5(sp)
sw s6, RV_STK_S6(sp)
sw s7, RV_STK_S7(sp)
sw s8, RV_STK_S8(sp)
sw s9, RV_STK_S9(sp)
sw s10, RV_STK_S10(sp)
sw s11, RV_STK_S11(sp)
sw t3, RV_STK_T3(sp)
sw t4, RV_STK_T4(sp)
sw t5, RV_STK_T5(sp)
sw t6, RV_STK_T6(sp)
.endm
.macro save_mepc
csrr t0, mepc
sw t0, RV_STK_MEPC(sp)
.endm
.macro save_mcsr
csrr t0, mstatus
sw t0, RV_STK_MSTATUS(sp)
csrr t0, mtvec
sw t0, RV_STK_MTVEC(sp)
csrr t0, mcause
sw t0, RV_STK_MCAUSE(sp)
csrr t0, mtval
sw t0, RV_STK_MTVAL(sp)
csrr t0, mhartid
sw t0, RV_STK_MHARTID(sp)
.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. */
.macro restore_general_regs cxt_size=CONTEXT_SIZE
lw ra, RV_STK_RA(sp)
lw tp, RV_STK_TP(sp)
lw t0, RV_STK_T0(sp)
lw t1, RV_STK_T1(sp)
lw t2, RV_STK_T2(sp)
lw s0, RV_STK_S0(sp)
lw s1, RV_STK_S1(sp)
lw a0, RV_STK_A0(sp)
lw a1, RV_STK_A1(sp)
lw a2, RV_STK_A2(sp)
lw a3, RV_STK_A3(sp)
lw a4, RV_STK_A4(sp)
lw a5, RV_STK_A5(sp)
lw a6, RV_STK_A6(sp)
lw a7, RV_STK_A7(sp)
lw s2, RV_STK_S2(sp)
lw s3, RV_STK_S3(sp)
lw s4, RV_STK_S4(sp)
lw s5, RV_STK_S5(sp)
lw s6, RV_STK_S6(sp)
lw s7, RV_STK_S7(sp)
lw s8, RV_STK_S8(sp)
lw s9, RV_STK_S9(sp)
lw s10, RV_STK_S10(sp)
lw s11, RV_STK_S11(sp)
lw t3, RV_STK_T3(sp)
lw t4, RV_STK_T4(sp)
lw t5, RV_STK_T5(sp)
lw t6, RV_STK_T6(sp)
addi sp,sp, \cxt_size
.endm
.macro restore_mepc
lw t0, RV_STK_MEPC(sp)
csrw mepc, t0
.endm
.macro restore_mcsr
lw t0, RV_STK_MSTATUS(sp)
csrw mstatus, t0
lw t0, RV_STK_MTVEC(sp)
csrw mtvec, t0
lw t0, RV_STK_MCAUSE(sp)
csrw mcause, t0
lw t0, RV_STK_MTVAL(sp)
csrw mtval, t0
lw t0, RV_STK_MHARTID(sp)
csrw mhartid, t0
.endm
.macro store_magic_general_regs
lui ra, INTR_M2U_MAGIC
lui tp, INTR_M2U_MAGIC
lui t0, INTR_M2U_MAGIC
lui t1, INTR_M2U_MAGIC
lui t2, INTR_M2U_MAGIC
lui s0, INTR_M2U_MAGIC
lui s1, INTR_M2U_MAGIC
lui a0, INTR_M2U_MAGIC
lui a1, INTR_M2U_MAGIC
lui a2, INTR_M2U_MAGIC
lui a3, INTR_M2U_MAGIC
lui a4, INTR_M2U_MAGIC
lui a5, INTR_M2U_MAGIC
lui a6, INTR_M2U_MAGIC
lui a7, INTR_M2U_MAGIC
lui s2, INTR_M2U_MAGIC
lui s3, INTR_M2U_MAGIC
lui s4, INTR_M2U_MAGIC
lui s5, INTR_M2U_MAGIC
lui s6, INTR_M2U_MAGIC
lui s7, INTR_M2U_MAGIC
lui s8, INTR_M2U_MAGIC
lui s9, INTR_M2U_MAGIC
lui s10, INTR_M2U_MAGIC
lui s11, INTR_M2U_MAGIC
lui t3, INTR_M2U_MAGIC
lui t4, INTR_M2U_MAGIC
lui t5, INTR_M2U_MAGIC
lui t6, INTR_M2U_MAGIC
.endm
.section .iram1, "ax"
/* Prevent the compiler from generating 2-byte instruction in the vector tables */
.option push
.option norvc
/**
* Vectored interrupt table. MTVT CSR points here.
*
* If an interrupt occurs and is configured as (hardware) vectored, the CPU will jump to
* MTVT[31:0] + 4 * interrupt_id
*
*/
.balign 0x40
.global _test_mtvt_table
.type _test_mtvt_table, @function
_test_mtvt_table:
/* First 16 (CLIC_EXT_INTR_NUM_OFFSET) entries are system interrupts */
.rept CLIC_EXT_INTR_NUM_OFFSET
.word _test_panic_handler
.endr
/* Next 31 entries are free interrupts (indices 16 to 46) */
.rept 31
.word _test_mintr_handler
.endr
/* Entry 47 is special - used for U-mode interrupt delegation */
.word _test_uintr_deleg
.size _test_mtvt_table, .-_test_mtvt_table
/*
* Non-hardware vectored interrupt entry. MTVEC CSR points here.
*
* On targets that use CLIC as their interrupt controller, when an exception occurs, the CPU
* jumps to the address stored in MTVEC[31:6] << 6. The CPU will also jump to this location
* if an interrupt is configured as non-vectored (CLIC_INT_ATTR.shv = 0).
*
*/
.balign 0x40
.global _test_mtvec_table
.type _test_mtvec_table, @function
_test_mtvec_table:
/* Backup t0, t1 on the stack before using it */
addi sp, sp, -16
sw t0, 0(sp)
sw t1, 4(sp)
/* Check whether the exception is an M-mode ecall */
csrr t0, mcause
li t1, VECTORS_MCAUSE_REASON_MASK
and t0, t0, t1
li t1, ECALL_M_MODE
beq t0, t1, _test_machine_ecall
/* Check whether the exception is an U-mode ecall */
li t1, ECALL_U_MODE
beq t0, t1, _test_user_ecall
/* Restore t0, t1 from the stack */
lw t1, 4(sp)
lw t0, 0(sp)
addi sp, sp, 16
/* Not an exception raised by the ecall instruction */
_test_panic_handler:
/* Save the register and CSR context */
save_general_regs RV_STK_FRMSZ
sw gp, RV_STK_GP(sp)
addi t0, sp, RV_STK_FRMSZ
sw t0, RV_STK_SP(sp)
save_mepc
save_mcsr
/* Executing the panic handler */
li t0, 0xDEADC0DE
csrr t0, mscratch
mv a0, sp
csrr a1, mcause
li t0, VECTORS_MCAUSE_REASON_MASK
and a1, a1, t0
jal panic_from_excp
/* We arrive here if the exception handler has returned. */
restore_mepc
restore_general_regs RV_STK_FRMSZ
mret
.size _test_mtvec_table, .-_test_mtvec_table
/* ECALL handler. */
.type _test_ecall_handler, @function
_test_ecall_handler:
/* M-mode ecall handler */
/* Currently in M-mode and switch to U-mode */
_test_machine_ecall:
csrr t0, mepc
addi t0, t0, 4
csrw mepc, t0
li t1, MSTATUS_MPP
csrc mstatus, t1
/* Restore t0, t1 from the stack */
lw t1, 4(sp)
lw t0, 0(sp)
addi sp, sp, 16
mret
/* U-mode ecall handler */
/* Currently in U-mode and switch to M-mode */
_test_user_ecall:
/* Check whether we are returning after servicing an U-mode interrupt */
lui t0, INTR_U2M_RTNVAL
csrrw t1, mscratch, zero
beq t0, t1, _rtn_from_u_intr
csrr t0, mepc
addi t0, t0, 4
csrw mepc, t0
li t1, MSTATUS_MPP
csrs mstatus, t1
/* Restore t0, t1 from the stack */
lw t1, 4(sp)
lw t0, 0(sp)
addi sp, sp, 16
mret
/* This point is reached after servicing a U-mode interrupt occurred in M-mode */
_rtn_from_u_intr:
/* Disable the U-mode delegation of all interrupts */
li t0, INTMTX_SIG_IDX_ASSERT_IN_SEC_REG
li t1, TEST_INTR_NUM_PASS_IN_SEC + CLIC_EXT_INTR_NUM_OFFSET
sw t1, 0(t0)
fence
/* Verify the interrupt remap */
_1:
lw t2, 0(t0)
bne t2, t1, _1
/* Restore the secure stack pointer */
la t0, _m_sp
lw sp, 0(t0)
/* Set the privilege mode to transition to after mret to M-mode */
li t0, MSTATUS_MPP
csrs mstatus, t0
/* Restore register context and resume the secure service */
restore_mepc
restore_general_regs
mret
.size _test_ecall_handler, .-_test_ecall_handler
/* Interrupt handler */
.type _test_mintr_handler, @function
_test_mintr_handler:
/* Start by saving the general purpose registers and the PC value before
* the interrupt happened. */
save_general_regs
save_mepc
/* Save GP and SP here. */
sw gp, RV_STK_GP(sp)
addi a0, sp, CONTEXT_SIZE
sw a0, RV_STK_SP(sp)
/* Save mcause */
csrr s1, mcause
csrr s2, mstatus
/* Enable nested interrupts */
csrsi mstatus, MSTATUS_MIE
/* call the C dispatcher */
mv a0, sp /* argument 1, stack pointer */
mv a1, s1 /* argument 2, interrupt number (mcause) */
/* mask off the interrupt flag of mcause */
li t0, VECTORS_MCAUSE_REASON_MASK
and a1, a1, t0
jal _test_global_interrupt_handler
/* Disable nested interrupts */
csrci mstatus, MSTATUS_MIE
/* Restore the rest of the registers. */
csrw mcause, s1
csrw mstatus, s2
restore_mepc
restore_general_regs
/* exit, this will also re-enable the interrupts */
mret
.size _test_mintr_handler, .-_test_mintr_handler
/* U-mode to M-mode switch */
.balign 4
.global _test_u2m_switch
.type _test_u2m_switch, @function
_test_u2m_switch:
ecall
fence
ret
.size _test_u2m_switch, .-_test_u2m_switch
/* U-mode interrupt delegation */
.global _test_uintr_deleg
.type _test_uintr_deleg, @function
_test_uintr_deleg:
/* Start by saving the general purpose registers and the PC value before
* the interrupt happened. */
save_general_regs
save_mepc
/* Save GP and SP here. */
sw gp, RV_STK_GP(sp)
addi a0, sp, CONTEXT_SIZE
sw a0, RV_STK_SP(sp)
/* Pass the interrupt ID to be serviced to U-mode */
li t2, INTMTX_SEC_STATUS_REG
lw t0, 0(t2)
li t1, 0
_find_intr_loop:
and t2, t0, 1
bnez t2, _found_intr
srai t0, t0, 1
addi t1, t1, 1
bnez t0, _find_intr_loop
/* should not reach here */
li t1, -1
unimp
_found_intr:
addi t0, t1, CLIC_EXT_INTR_NUM_OFFSET
csrr t1, ucause
or t1, t1, t0
li t2, (VECTORS_MCAUSE_INTBIT_MASK | VECTORS_XCAUSE_XPIE_MASK)
or t1, t1, t2
csrw ucause, t1
/* Enable the U-mode interrupt delegation */
li t0, INTMTX_SIG_IDX_ASSERT_IN_SEC_REG
li t1, 0x00
sw t1, 0(t0)
fence
/* Verify the interrupt remap */
_2:
lw t2, 0(t0)
bne t2, t1, _2
/* For U-mode interrupts, we use mret to switch to U-mode after executing the below steps - */
/* Disable the U-mode global interrupts */
csrci ustatus, USTATUS_UIE
/* Configure `uepc` with the U-mode ecall handler (see _test_u2m_switch) so that we can
* return to M-mode after handling the interrupt */
la t0, _test_u2m_switch
csrw uepc, t0
/* Set the program counter to the U-mode global interrupt handler (see _test_uintr_handler) */
la t0, _test_uintr_handler
csrw mepc, t0
/* Set the privilege mode to transition to after mret to U-mode */
li t1, MSTATUS_MPP
csrc mstatus, t1
/* Save the current secure stack pointer */
la t0, _m_sp
sw sp, 0(t0)
/* Set a flag to identify the next U2M switch would be after handling a U-mode interrupt */
lui t0, INTR_U2M_RTNVAL
csrw mscratch, t0
/* Place magic bytes in all the general registers */
store_magic_general_regs
mret
.size _test_uintr_deleg, .-_test_uintr_deleg
.option pop

View File

@@ -0,0 +1,208 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "soc/soc.h"
#include "soc/soc_caps.h"
#include "soc/clic_reg.h"
#include "soc/interrupt_reg.h"
#include "riscv/encoding.h"
#include "riscv/rvruntime-frames.h"
#include "esp_private/panic_reason.h"
#include "esp_private/vectors_const.h"
#include "sdkconfig.h"
.equ SAVE_REGS, 32
.equ CONTEXT_SIZE, (SAVE_REGS * 4)
.equ UINTTHRESH_CSR, 0x047
.equ RV_INTR_NUM, (CLIC_EXT_INTR_NUM_OFFSET + SOC_CPU_INTR_NUM)
/* 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
* can be overridden. */
.macro save_general_regs cxt_size=CONTEXT_SIZE
addi sp, sp, -\cxt_size
sw ra, RV_STK_RA(sp)
sw tp, RV_STK_TP(sp)
sw t0, RV_STK_T0(sp)
sw t1, RV_STK_T1(sp)
sw t2, RV_STK_T2(sp)
sw s0, RV_STK_S0(sp)
sw s1, RV_STK_S1(sp)
sw a0, RV_STK_A0(sp)
sw a1, RV_STK_A1(sp)
sw a2, RV_STK_A2(sp)
sw a3, RV_STK_A3(sp)
sw a4, RV_STK_A4(sp)
sw a5, RV_STK_A5(sp)
sw a6, RV_STK_A6(sp)
sw a7, RV_STK_A7(sp)
sw s2, RV_STK_S2(sp)
sw s3, RV_STK_S3(sp)
sw s4, RV_STK_S4(sp)
sw s5, RV_STK_S5(sp)
sw s6, RV_STK_S6(sp)
sw s7, RV_STK_S7(sp)
sw s8, RV_STK_S8(sp)
sw s9, RV_STK_S9(sp)
sw s10, RV_STK_S10(sp)
sw s11, RV_STK_S11(sp)
sw t3, RV_STK_T3(sp)
sw t4, RV_STK_T4(sp)
sw t5, RV_STK_T5(sp)
sw t6, RV_STK_T6(sp)
.endm
.macro save_uepc
csrr t0, uepc
sw t0, RV_STK_MEPC(sp)
.endm
.macro save_ucsr
csrr t0, ustatus
sw t0, RV_STK_MSTATUS(sp)
csrr t0, utvec
sw t0, RV_STK_MTVEC(sp)
csrr t0, ucause
sw t0, RV_STK_MCAUSE(sp)
.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. */
.macro restore_general_regs cxt_size=CONTEXT_SIZE
lw ra, RV_STK_RA(sp)
lw tp, RV_STK_TP(sp)
lw t0, RV_STK_T0(sp)
lw t1, RV_STK_T1(sp)
lw t2, RV_STK_T2(sp)
lw s0, RV_STK_S0(sp)
lw s1, RV_STK_S1(sp)
lw a0, RV_STK_A0(sp)
lw a1, RV_STK_A1(sp)
lw a2, RV_STK_A2(sp)
lw a3, RV_STK_A3(sp)
lw a4, RV_STK_A4(sp)
lw a5, RV_STK_A5(sp)
lw a6, RV_STK_A6(sp)
lw a7, RV_STK_A7(sp)
lw s2, RV_STK_S2(sp)
lw s3, RV_STK_S3(sp)
lw s4, RV_STK_S4(sp)
lw s5, RV_STK_S5(sp)
lw s6, RV_STK_S6(sp)
lw s7, RV_STK_S7(sp)
lw s8, RV_STK_S8(sp)
lw s9, RV_STK_S9(sp)
lw s10, RV_STK_S10(sp)
lw s11, RV_STK_S11(sp)
lw t3, RV_STK_T3(sp)
lw t4, RV_STK_T4(sp)
lw t5, RV_STK_T5(sp)
lw t6, RV_STK_T6(sp)
addi sp,sp, \cxt_size
.endm
.macro restore_uepc
lw t0, RV_STK_MEPC(sp)
csrw uepc, t0
.endm
.macro restore_ucsr
lw t0, RV_STK_MSTATUS(sp)
csrw ustatus, t0
lw t0, RV_STK_MTVEC(sp)
csrw utvec, t0
lw t0, RV_STK_MCAUSE(sp)
csrw ucause, t0
.endm
.section .iram1, "ax"
/* Prevent the compiler from generating 2-byte instruction in the vector tables */
.option push
.option norvc
/**
* Vectored interrupt table. MTVT CSR points here.
*
* If an interrupt occurs and is configured as (hardware) vectored, the CPU will jump to
* MTVT[31:0] + 4 * interrupt_id
*
*/
.balign 0x40
.global _test_utvt_table
.type _test_utvt_table, @function
_test_utvt_table:
.rept RV_INTR_NUM
.word _test_uintr_handler
.endr
.size _test_utvt_table, .-_test_utvt_table
/*
* Non-hardware vectored interrupt entry. MTVEC CSR points here.
*
* On targets that use CLIC as their interrupt controller, when an exception occurs, the CPU
* jumps to the address stored in MTVEC[31:6] << 6. The CPU will also jump to this location
* if an interrupt is configured as non-vectored (CLIC_INT_ATTR.shv = 0).
*
*/
.balign 0x40
.global _test_utvec_table
.type _test_utvec_table, @function
_test_utvec_table:
/* Do nothing */
mret
.size _test_utvec_table, .-_test_utvec_table
/* Interrupt handler */
.global _test_uintr_handler
.type _test_uintr_handler, @function
_test_uintr_handler:
/* Start by saving the general purpose registers and the PC value before
* the interrupt happened. */
save_general_regs
save_uepc
/* Save GP and SP here. */
sw gp, RV_STK_GP(sp)
addi a0, sp, CONTEXT_SIZE
sw a0, RV_STK_SP(sp)
/* Save ucause */
csrr s1, ucause
csrr s2, ustatus
/* Enable nested interrupts */
csrsi ustatus, USTATUS_UIE
/* call the C dispatcher */
mv a0, sp /* argument 1, stack pointer */
mv a1, s1 /* argument 2, interrupt number (mcause) */
/* mask off the interrupt flag of mcause */
li t0, VECTORS_MCAUSE_REASON_MASK
and a1, a1, t0
jal _test_global_interrupt_handler
/* Disable nested interrupts */
csrci ustatus, USTATUS_UIE
/* Restore the rest of the registers. */
csrw ucause, s1
csrw ustatus, s2
restore_uepc
restore_general_regs
/* exit, this will also re-enable the interrupts */
uret
.size _test_uintr_handler, .-_test_uintr_handler
.option pop

View File

@@ -20,7 +20,6 @@
#include "test_pms_priv.h"
#include "test_pms_params.h"
#include "test_peri_apm_reg.h"
#include "unity.h"
@@ -114,7 +113,7 @@ IRAM_ATTR static void hp_cpu_peri_addr_rw(uint32_t peri_addr, uint32_t attr)
RV_WRITE_CSR(uscratch, 0x00);
reg_write(peri_addr, TEST_VAL);
test_delay_us(10000);
test_delay_ms(10);
bool can_write = (attr & APM_PERM_W);
if (!can_write) {
@@ -125,7 +124,7 @@ IRAM_ATTR static void hp_cpu_peri_addr_rw(uint32_t peri_addr, uint32_t attr)
volatile uint32_t val = reg_read(peri_addr);
(void)val;
test_delay_us(10000);
test_delay_ms(10);
bool can_read = (attr & APM_PERM_R);
if (!can_read) {
@@ -142,7 +141,7 @@ IRAM_ATTR static void lp_cpu_peri_addr_rw(uint32_t peri_addr, uint32_t attr)
SEND_EXCP(0);
SEND_MSG(MSG_SLAVE_WRITE);
SEND_ADDR(peri_addr);
test_delay_us(10000);
test_delay_ms(10);
bool can_write = (attr & APM_PERM_W);
if (!can_write) {
@@ -154,7 +153,7 @@ IRAM_ATTR static void lp_cpu_peri_addr_rw(uint32_t peri_addr, uint32_t attr)
SEND_EXCP(0);
SEND_MSG(MSG_SLAVE_READ);
SEND_ADDR(peri_addr);
test_delay_us(10000);
test_delay_ms(10);
bool can_read = (attr & APM_PERM_R);
if (!can_read) {

View File

@@ -84,12 +84,12 @@ static void gdma_xmem_addr_rw(uint8_t *src, uint8_t *dest, size_t size, uint32_t
#endif
if (!test_attr) {
test_delay_us(10000);
test_delay_ms(10);
TEST_ASSERT(apm_master_excp_flag[TEST_GDMA_APM_MASTER_ID]);
test_gdma_wait_done();
} else {
test_gdma_wait_done();
test_delay_us(10000);
test_delay_ms(10);
TEST_ASSERT_FALSE(apm_master_excp_flag[TEST_GDMA_APM_MASTER_ID]);
TEST_ASSERT_EQUAL_HEX8_ARRAY(src, dest, size);
}
@@ -202,7 +202,7 @@ static void hp_cpu_peri_addr_rw(uint32_t peri_addr, uint32_t attr)
apm_master_excp_flag[APM_MASTER_HPCORE] = false;
REG_WRITE(peri_addr, TEST_VAL);
test_delay_us(10000);
test_delay_ms(10);
bool can_write = (attr & APM_PERM_W);
TEST_ASSERT(apm_master_excp_flag[APM_MASTER_HPCORE] != can_write);
@@ -211,7 +211,7 @@ static void hp_cpu_peri_addr_rw(uint32_t peri_addr, uint32_t attr)
volatile uint32_t val = REG_READ(peri_addr);
(void)val;
test_delay_us(10000);
test_delay_ms(10);
bool can_read = (attr & APM_PERM_R);
TEST_ASSERT(apm_master_excp_flag[APM_MASTER_HPCORE] != can_read);
@@ -227,7 +227,7 @@ static void lp_cpu_peri_addr_rw(uint32_t peri_addr, uint32_t attr)
SEND_MSG(MSG_SLAVE_WRITE);
SEND_ADDR(peri_addr);
test_delay_us(10000);
test_delay_ms(10);
bool can_write = (attr & APM_PERM_W);
TEST_ASSERT(apm_master_excp_flag[APM_MASTER_LPCORE] != can_write);
@@ -236,7 +236,7 @@ static void lp_cpu_peri_addr_rw(uint32_t peri_addr, uint32_t attr)
SEND_MSG(MSG_SLAVE_READ);
SEND_ADDR(peri_addr);
test_delay_us(10000);
test_delay_ms(10);
bool can_read = (attr & APM_PERM_R);
TEST_ASSERT(apm_master_excp_flag[APM_MASTER_LPCORE] != can_read);
@@ -490,7 +490,7 @@ static void hp_cpu_xmem_addr_rw(uint32_t mem_addr, size_t size, uint32_t attr)
for (uint32_t offs = 0; offs < num_words; offs += step) {
addr[offs] = TEST_VAL;
asm volatile ("nop");
test_delay_us(10000);
test_delay_ms(10);
bool can_write = (attr & APM_PERM_W);
TEST_ASSERT(apm_master_excp_flag[APM_MASTER_HPCORE] != can_write);
@@ -500,7 +500,7 @@ static void hp_cpu_xmem_addr_rw(uint32_t mem_addr, size_t size, uint32_t attr)
uint32_t val = addr[offs];
(void)val;
asm volatile ("fence");
test_delay_us(10000);
test_delay_ms(10);
bool can_read = (attr & APM_PERM_R);
TEST_ASSERT(apm_master_excp_flag[APM_MASTER_HPCORE] != can_read);
@@ -520,7 +520,7 @@ static void hp_cpu_xmem_addr_x(uint32_t mem_addr, size_t size, uint32_t attr)
void (*func_ptr)(void);
func_ptr = (void(*)(void))(mem_addr);
func_ptr();
test_delay_us(10000);
test_delay_ms(10);
bool can_execute = attr & APM_PERM_X;
TEST_ASSERT(apm_master_excp_flag[APM_MASTER_HPCORE] != can_execute);
@@ -538,7 +538,7 @@ static void lp_cpu_xmem_addr_rw(uint32_t mem_addr, size_t size, uint32_t attr)
SEND_MSG(MSG_SLAVE_WRITE);
SEND_ADDR(mem_addr);
SEND_SIZE(sizeof(uint32_t));
test_delay_us(10000);
test_delay_ms(10);
bool can_write = (attr & APM_PERM_W);
TEST_ASSERT(apm_master_excp_flag[APM_MASTER_LPCORE] != can_write);
@@ -547,7 +547,7 @@ static void lp_cpu_xmem_addr_rw(uint32_t mem_addr, size_t size, uint32_t attr)
SEND_MSG(MSG_SLAVE_READ);
SEND_ADDR(mem_addr);
test_delay_us(10000);
test_delay_ms(10);
bool can_read = (attr & APM_PERM_R);
TEST_ASSERT(apm_master_excp_flag[APM_MASTER_LPCORE] != can_read);
@@ -564,7 +564,7 @@ static void lp_cpu_xmem_addr_x(uint32_t mem_addr, size_t size, uint32_t attr)
SEND_MSG(MSG_SLAVE_EXEC);
SEND_ADDR(mem_addr);
test_delay_us(10000);
test_delay_ms(10);
bool can_execute = (attr & APM_PERM_X);
TEST_ASSERT(apm_master_excp_flag[APM_MASTER_LPCORE] != can_execute);

View File

@@ -0,0 +1,9 @@
set(srcs "test_main.c" "test_pms.c")
if(CONFIG_SOC_SUPPORT_TEE_INTR_TEST)
list(APPEND srcs "test_cpu_intr.c")
endif()
idf_component_register(SRCS "${srcs}"
INCLUDE_DIRS ""
PRIV_REQUIRES pms_and_cpu_intr esp_psram
WHOLE_ARCHIVE)

View File

@@ -0,0 +1,66 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "test_cpu_intr.h"
#include "unity.h"
#include "sdkconfig.h"
/**
* @brief Test M-mode interrupts in M-mode
*
* This test verifies interrupt handling behavior in M-mode:
* - Sets up a timer interrupt firing every 10ms (priority 2) in M-mode
* - Triggers INTPRI interrupt every 100ms (priority 1) in M-mode
* - Verifies that both interrupts are handled correctly
* - Checks interrupt counts match expected values
*/
TEST_CASE("Test M-mode interrupts in M-mode", "[CPU]")
{
test_m_mode_intr_in_m_mode();
}
/**
* @brief Test U-mode interrupts in U-mode
*
* This test verifies interrupt handling behavior in U-mode:
* - Sets up a timer interrupt firing every 10ms (priority 2) in U-mode
* - Triggers INTPRI interrupt every 100ms (priority 1) in U-mode
* - Verifies that both interrupts are handled correctly
* - Checks interrupt counts match expected values
*/
TEST_CASE("Test U-mode interrupts in U-mode", "[CPU]")
{
test_u_mode_intr_in_u_mode();
}
/**
* @brief Test M-mode interrupts in U-mode
*
* This test verifies interrupt handling behavior in U-mode:
* - Sets up a timer interrupt firing every 10ms (priority 2) in U-mode
* - Triggers INTPRI interrupt every 100ms (priority 1) in M-mode
* - Verifies that both interrupts are handled correctly
* - Checks interrupt counts match expected values
*/
TEST_CASE("Test M-mode interrupts in U-mode", "[CPU]")
{
test_m_mode_intr_in_u_mode();
}
/**
* @brief Test U-mode interrupts in M-mode
*
* This test verifies interrupt handling behavior in M-mode:
* - Sets up a timer interrupt firing every 10ms (priority 2) in M-mode
* - Triggers INTPRI interrupt every 100ms (priority 1) in U-mode
* - Verifies that both interrupts are handled correctly
* - Checks interrupt counts match expected values
*/
TEST_CASE("Test U-mode interrupts in M-mode", "[CPU]")
{
test_u_mode_intr_in_m_mode();
}

View File

@@ -6,22 +6,29 @@ from pytest_embedded_idf.utils import idf_parametrize
# ---------------- Pytest build parameters ----------------
SOC_SYS_APM_SUPPORTED = ['esp32c6', 'esp32h2', 'esp32c5', 'esp32c61']
SOC_PERI_APM_SUPPORTED = ['esp32c5']
SOC_SUPPORT_SYS_APM_TEST = ['esp32c6', 'esp32h2', 'esp32c5', 'esp32c61']
SOC_SUPPORT_PERI_APM_TEST = ['esp32c5']
SOC_SUPPORT_INTR_TEST = ['esp32c5', 'esp32c61']
CONFIG_SYS_APM = [
# 'config, target, markers',
('default', target, (pytest.mark.generic,))
for target in SOC_SYS_APM_SUPPORTED
for target in SOC_SUPPORT_SYS_APM_TEST
]
CONFIG_PERI_APM = [
# 'config, target, markers',
('default', target, (pytest.mark.generic,))
for target in SOC_PERI_APM_SUPPORTED
for target in SOC_SUPPORT_PERI_APM_TEST
]
# ---------------- TEE default tests ----------------
CONFIG_INTR_TEST = [
# 'config, target, markers',
('default', target, (pytest.mark.generic,))
for target in SOC_SUPPORT_INTR_TEST
]
# ---------------- TEE APM tests ----------------
@idf_parametrize(
@@ -40,3 +47,15 @@ def test_tee_sys_apm(dut: IdfDut) -> None:
)
def test_tee_peri_apm(dut: IdfDut) -> None:
dut.run_all_single_board_cases(group='PERI_APM')
# ---------------- TEE Interrupt tests ----------------
@idf_parametrize(
'config, target, markers',
CONFIG_INTR_TEST,
indirect=['config', 'target'],
)
def test_tee_interrupts(dut: IdfDut) -> None:
dut.run_all_single_board_cases()

View File

@@ -1,47 +0,0 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "soc/reg_base.h"
/********* For PERI_APM *********/
#include "soc/uart_reg.h"
#include "soc/i2c_reg.h"
#include "soc/i2s_reg.h"
#include "soc/mcpwm_reg.h"
#include "soc/twaifd_reg.h"
#include "soc/ahb_dma_reg.h"
#include "soc/pau_reg.h"
#include "soc/interrupt_matrix_reg.h"
#include "soc/apb_saradc_reg.h"
#include "soc/timer_group_reg.h"
#include "soc/pcnt_reg.h"
#include "soc/io_mux_reg.h"
#include "soc/hp_system_reg.h"
#include "soc/pcr_reg.h"
#include "soc/spi_mem_reg.h"
#include "soc/hp_apm_reg.h"
#include "soc/cpu_apm_reg.h"
#include "soc/sha_reg.h"
#include "soc/cache_reg.h"
#include "soc/spi_reg.h"
#include "soc/bitscrambler_reg.h"
#include "soc/keymng_reg.h"
#include "soc/sdio_slc_host_reg.h"
#include "soc/efuse_reg.h"
#include "soc/pmu_reg.h"
#include "soc/lp_clkrst_reg.h"
#include "soc/lp_aon_reg.h"
#include "soc/lp_wdt_reg.h"
#include "soc/lpperi_reg.h"
#include "soc/lp_analog_peri_reg.h"
#include "soc/lp_uart_reg.h"
#include "soc/lp_i2c_reg.h"
#include "soc/lp_i2c_ana_mst_reg.h"
#include "soc/huk_reg.h"
#include "soc/lp_apm_reg.h"

View File

@@ -1,77 +0,0 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <assert.h>
#include "soc/soc_caps.h"
#include "esp_cpu.h"
#include "riscv/encoding.h"
#include "riscv/csr.h"
#include "riscv/rv_utils.h"
#include "esp_rom_sys.h"
#if SOC_INT_PLIC_SUPPORTED
#include "esp_private/interrupt_plic.h"
#elif SOC_INT_CLIC_SUPPORTED
#include "esp_private/interrupt_clic.h"
#else
#include "esp_private/interrupt_intc.h"
#endif
/********************************** Vector Table Redirection *******************************/
extern int _vector_table;
extern int _test_vector_table;
void set_test_vector_table(void)
{
rv_utils_intr_global_disable();
rv_utils_set_mtvec((uintptr_t)&_test_vector_table);
rv_utils_intr_global_enable();
}
void restore_default_vector_table(void)
{
rv_utils_intr_global_disable();
rv_utils_set_mtvec((uintptr_t)&_vector_table);
rv_utils_intr_global_enable();
}
/********************************** Privilege Mode Switch *********************************/
void test_m2u_switch(void)
{
int mode = esp_cpu_get_curr_privilege_level();
assert(mode == PRV_M);
asm volatile("ecall\n");
mode = esp_cpu_get_curr_privilege_level();
assert(mode == PRV_U);
}
void test_u2m_switch(void)
{
int mode = esp_cpu_get_curr_privilege_level();
assert(mode == PRV_U);
asm volatile("ecall\n");
mode = esp_cpu_get_curr_privilege_level();
assert(mode == PRV_M);
}
/********************************** Interrupt Handler *************************************/
extern void _global_interrupt_handler(intptr_t sp, int mcause);
/* called from test_tee_vectors.S */
void _test_global_interrupt_handler(intptr_t sp, int mcause)
{
_global_interrupt_handler(sp, mcause);
}

View File

@@ -1,4 +0,0 @@
idf_component_register(SRCS "test_main.c" "test_pms.c"
INCLUDE_DIRS ""
PRIV_REQUIRES pms esp_psram
WHOLE_ARCHIVE)

View File

@@ -5,7 +5,6 @@
*/
#pragma once
#include <stdint.h>
#include "soc/soc.h"
#ifdef __cplusplus
extern "C" {

View File

@@ -5,7 +5,6 @@
*/
#pragma once
#include <stdint.h>
#include "soc/soc.h"
#ifdef __cplusplus
extern "C" {