diff --git a/tools/test_apps/system/panic/CMakeLists.txt b/tools/test_apps/system/panic/CMakeLists.txt index 2ac666b1e9..b11cdb11ea 100644 --- a/tools/test_apps/system/panic/CMakeLists.txt +++ b/tools/test_apps/system/panic/CMakeLists.txt @@ -7,22 +7,34 @@ include($ENV{IDF_PATH}/tools/cmake/project.cmake) project(test_panic) -# Enable UBSAN checks -# -# shift-base sanitizer is disabled due to the following pattern found in register header files: -# #define SOME_FIELD 0xFFFF -# #define SOME_FIELD_M ((SOME_FIELD_V)<<(SOME_FIELD_S)) -# #define SOME_FIELD_V 0xFFFF -# #define SOME_FIELD_S 16 -# here SOME_FIELD_V doesn't have an unsigned (U) prefix, so the compiler flags -# SOME_FIELD_M expansion (0xFFFF << 16) as generating integer overflow. -# -set(ubsan_options "-fsanitize=undefined" "-fno-sanitize=shift-base") +if(CONFIG_TEST_MEMPROT) + # TODO: IDF-6821 - Refactor this to make it easy to add any new targets + if(CONFIG_SOC_MEMPROT_SUPPORTED) + target_link_libraries(${project_elf} PRIVATE "-Wl,--wrap=esp_panic_handler") + if(CONFIG_IDF_TARGET_ESP32C3) + target_link_libraries(${project_elf} PRIVATE "-Wl,--wrap=panic_arch_fill_info") + endif() + endif() +endif() -# Only enable UBSAN for a few components related to the panic test, -# due to RAM size limitations. -foreach(component main espcoredump esp_system spi_flash - esp_common esp_hw_support soc hal freertos) - idf_component_get_property(lib ${component} COMPONENT_LIB) - target_compile_options(${lib} PRIVATE ${ubsan_options}) -endforeach() +if(NOT CONFIG_TEST_MEMPROT) + # Enable UBSAN checks + # + # shift-base sanitizer is disabled due to the following pattern found in register header files: + # #define SOME_FIELD 0xFFFF + # #define SOME_FIELD_M ((SOME_FIELD_V)<<(SOME_FIELD_S)) + # #define SOME_FIELD_V 0xFFFF + # #define SOME_FIELD_S 16 + # here SOME_FIELD_V doesn't have an unsigned (U) prefix, so the compiler flags + # SOME_FIELD_M expansion (0xFFFF << 16) as generating integer overflow. + # + set(ubsan_options "-fsanitize=undefined" "-fno-sanitize=shift-base") + + # Only enable UBSAN for a few components related to the panic test, + # due to RAM size limitations. + foreach(component main espcoredump esp_system spi_flash + esp_common esp_hw_support soc hal freertos) + idf_component_get_property(lib ${component} COMPONENT_LIB) + target_compile_options(${lib} PRIVATE ${ubsan_options}) + endforeach() +endif() diff --git a/tools/test_apps/system/panic/main/CMakeLists.txt b/tools/test_apps/system/panic/main/CMakeLists.txt index b71be1d492..fbc916d8f3 100644 --- a/tools/test_apps/system/panic/main/CMakeLists.txt +++ b/tools/test_apps/system/panic/main/CMakeLists.txt @@ -1,3 +1,16 @@ -idf_component_register(SRCS "test_app_main.c" "test_panic.c" - INCLUDE_DIRS "include" - REQUIRES spi_flash esp_psram esp_system esp_partition) +set(srcs "test_app_main.c" "test_panic.c") + +if(CONFIG_TEST_MEMPROT) + list(APPEND srcs "test_memprot.c") + if(CONFIG_SOC_MEMPROT_SUPPORTED) + if(CONFIG_IDF_TARGET_ARCH_XTENSA) + list(APPEND srcs "panic_utils/memprot_panic_utils_xtensa.c") + elseif(CONFIG_IDF_TARGET_ARCH_RISCV) + list(APPEND srcs "panic_utils/memprot_panic_utils_riscv.c") + endif() + endif() +endif() + +idf_component_register(SRCS "${srcs}" + INCLUDE_DIRS "include" + REQUIRES spi_flash esp_psram esp_system esp_partition) diff --git a/tools/test_apps/system/panic/main/Kconfig.projbuild b/tools/test_apps/system/panic/main/Kconfig.projbuild new file mode 100644 index 0000000000..a0f65299f5 --- /dev/null +++ b/tools/test_apps/system/panic/main/Kconfig.projbuild @@ -0,0 +1,6 @@ +menu "Test-app Configuration" + + config TEST_MEMPROT + bool "Enable memprot test" + +endmenu diff --git a/tools/test_apps/system/panic/main/include/test_memprot.h b/tools/test_apps/system/panic/main/include/test_memprot.h new file mode 100644 index 0000000000..c4b229a8b9 --- /dev/null +++ b/tools/test_apps/system/panic/main/include/test_memprot.h @@ -0,0 +1,48 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 +#define SOC_DCACHE_SUPPORTED (1) +#endif + +/* Functions testing memprot peripheral: memory regions and illegal operations on them */ + +void test_dcache_read_violation(void); + +void test_dcache_write_violation(void); + +void test_iram_reg1_write_violation(void); + +void test_iram_reg2_write_violation(void); + +void test_iram_reg3_write_violation(void); + +void test_iram_reg4_write_violation(void); + +void test_dram_reg1_execute_violation(void); + +void test_dram_reg2_execute_violation(void); + +void test_rtc_fast_reg1_execute_violation(void); + +void test_rtc_fast_reg2_execute_violation(void); + +void test_rtc_fast_reg3_execute_violation(void); + +void test_rtc_slow_reg1_execute_violation(void); + +void test_rtc_slow_reg2_execute_violation(void); + + +#ifdef __cplusplus +} +#endif diff --git a/tools/test_apps/system/panic/main/panic_utils/memprot_panic_utils_riscv.c b/tools/test_apps/system/panic/main/panic_utils/memprot_panic_utils_riscv.c new file mode 100644 index 0000000000..5fa0c34523 --- /dev/null +++ b/tools/test_apps/system/panic/main/panic_utils/memprot_panic_utils_riscv.c @@ -0,0 +1,59 @@ +/* + * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "riscv/rvruntime-frames.h" +#include "esp_private/panic_internal.h" + +#include "esp_rom_sys.h" +#include "soc/periph_defs.h" +#include "hal/memprot_ll.h" + +#define MCAUSE_ILLEGAL_INSTRUCTION (2) + +extern void panic_arch_fill_info(void *frame, panic_info_t *info); + +extern void panic_soc_fill_info(void *frame, panic_info_t *info); + +extern void esp_panic_handler(panic_info_t *info); + +void __real_esp_panic_handler(panic_info_t *info); + +void __real_panic_arch_fill_info(void *frame, panic_info_t *info); + +static void dump_memprot_status_register(void) +{ + esp_rom_printf("violation intr status ->\n"); + esp_rom_printf(" core 0 dram0: %x\n", memprot_ll_dram0_get_monitor_status_intr()); + esp_rom_printf(" core 0 iram0: %x\n", memprot_ll_iram0_get_monitor_status_intr()); + esp_rom_printf(" core 0 peribus: %x\n", memprot_ll_rtcfast_get_monitor_status_intr()); +} + +void __wrap_panic_arch_fill_info(void *frame, panic_info_t *info) +{ + RvExcFrame *regs = (RvExcFrame *) frame; + + if (regs->mcause == MCAUSE_ILLEGAL_INSTRUCTION) { + + uint32_t pms_violate_intr = memprot_ll_dram0_get_monitor_status_intr() | + memprot_ll_iram0_get_monitor_status_intr() | + memprot_ll_rtcfast_get_monitor_status_intr(); + + if (pms_violate_intr) { + regs->mcause = ETS_MEMPROT_ERR_INUM; + panic_soc_fill_info(frame, info); + return; + } + } + + __real_panic_arch_fill_info(frame, info); +} + +void __wrap_esp_panic_handler(panic_info_t *info) +{ + dump_memprot_status_register(); + + __real_esp_panic_handler(info); +} diff --git a/tools/test_apps/system/panic/main/panic_utils/memprot_panic_utils_xtensa.c b/tools/test_apps/system/panic/main/panic_utils/memprot_panic_utils_xtensa.c new file mode 100644 index 0000000000..f67e81aed7 --- /dev/null +++ b/tools/test_apps/system/panic/main/panic_utils/memprot_panic_utils_xtensa.c @@ -0,0 +1,52 @@ +/* + * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "freertos/xtensa_context.h" +#include "esp_private/panic_internal.h" + +#include "esp_rom_sys.h" +#include "soc/sensitive_reg.h" +#include "soc/periph_defs.h" +#include "hal/memprot_ll.h" + +extern void esp_panic_handler(panic_info_t *info); + +void __real_esp_panic_handler(panic_info_t *info); + +static void dump_memprot_status_register(void) +{ + esp_rom_printf("violation intr status ->\n"); +#if CONFIG_IDF_TARGET_ESP32S2 + esp_rom_printf(" core 0 dram0: %x\n", memprot_ll_dram0_get_intr_on_bit()); + esp_rom_printf(" core 0 iram0: %x\n", memprot_ll_iram0_get_intr_on_bit()); + esp_rom_printf(" core 0 peribus1: %x\n", REG_GET_FIELD(DPORT_PMS_PRO_DPORT_6_REG, DPORT_PMS_PRO_DPORT_ILG_INTR)); + esp_rom_printf(" core 0 peribus2: %x\n", REG_GET_FIELD(DPORT_PMS_PRO_AHB_3_REG, DPORT_PMS_PRO_AHB_ILG_INTR)); +#elif CONFIG_IDF_TARGET_ESP32S3 + uint32_t reg_val0 = 0, reg_val1 = 0; + + memprot_ll_dram0_get_monitor_status_intr(PRO_CPU_NUM, ®_val0); + memprot_ll_dram0_get_monitor_status_intr(APP_CPU_NUM, ®_val1); + esp_rom_printf(" core 0 dram0: %x\n", reg_val0); + esp_rom_printf(" core 1 dram0: %x\n", reg_val1); + + memprot_ll_iram0_get_monitor_status_intr(PRO_CPU_NUM, ®_val0); + memprot_ll_iram0_get_monitor_status_intr(APP_CPU_NUM, ®_val1); + esp_rom_printf(" core 0 iram0: %x\n", reg_val0); + esp_rom_printf(" core 1 iram0: %x\n", reg_val1); + + memprot_ll_rtcfast_get_monitor_status_intr(PRO_CPU_NUM, ®_val0); + memprot_ll_rtcfast_get_monitor_status_intr(APP_CPU_NUM, ®_val1); + esp_rom_printf(" core 0 peribus: %x\n", reg_val0); + esp_rom_printf(" core 1 peribus: %x\n", reg_val1); +#endif +} + +void __wrap_esp_panic_handler(panic_info_t *info) +{ + dump_memprot_status_register(); + + __real_esp_panic_handler(info); +} diff --git a/tools/test_apps/system/panic/main/test_app_main.c b/tools/test_apps/system/panic/main/test_app_main.c index f576a804b9..f5edc684fe 100644 --- a/tools/test_apps/system/panic/main/test_app_main.c +++ b/tools/test_apps/system/panic/main/test_app_main.c @@ -16,6 +16,7 @@ #include "freertos/task.h" #include "test_panic.h" +#include "test_memprot.h" /* Test Utility Functions */ @@ -99,5 +100,41 @@ void app_main(void) HANDLE_TEST(test_name, test_assert); HANDLE_TEST(test_name, test_assert_cache_disabled); +#if CONFIG_TEST_MEMPROT + + HANDLE_TEST(test_name, test_iram_reg1_write_violation); + HANDLE_TEST(test_name, test_iram_reg2_write_violation); + HANDLE_TEST(test_name, test_iram_reg3_write_violation); + + /* TODO: IDF-6820: ESP32-S2 -> Fix incorrect panic reason: Unhandled debug exception */ + HANDLE_TEST(test_name, test_iram_reg4_write_violation); + + /* TODO: IDF-6820: ESP32-S2-> Fix multiple panic reasons in different runs */ + HANDLE_TEST(test_name, test_dram_reg1_execute_violation); + + HANDLE_TEST(test_name, test_dram_reg2_execute_violation); + +#if CONFIG_SOC_RTC_FAST_MEM_SUPPORTED + HANDLE_TEST(test_name, test_rtc_fast_reg1_execute_violation); + HANDLE_TEST(test_name, test_rtc_fast_reg2_execute_violation); + + /* TODO: IDF-6820: ESP32-S2-> Fix multiple panic reasons in different runs */ + HANDLE_TEST(test_name, test_rtc_fast_reg3_execute_violation); +#endif + +#if SOC_DCACHE_SUPPORTED + HANDLE_TEST(test_name, test_dcache_read_violation); + + /* TODO: IDF-6820: ESP32-S2-> Fix multiple panic reasons in different runs */ + HANDLE_TEST(test_name, test_dcache_write_violation); +#endif + +#if CONFIG_SOC_RTC_SLOW_MEM_SUPPORTED + HANDLE_TEST(test_name, test_rtc_slow_reg1_execute_violation); + HANDLE_TEST(test_name, test_rtc_slow_reg2_execute_violation); +#endif + +#endif + die("Unknown test name"); } diff --git a/tools/test_apps/system/panic/main/test_memprot.c b/tools/test_apps/system/panic/main/test_memprot.c new file mode 100644 index 0000000000..3c559f1fc1 --- /dev/null +++ b/tools/test_apps/system/panic/main/test_memprot.c @@ -0,0 +1,215 @@ +/* + * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +#include "esp_err.h" +#include "esp_system.h" +#include "esp_log.h" +#include "soc/soc.h" + +#include "test_memprot.h" + + +#define RND_VAL (0xA5A5A5A5) +#define SPIN_ITER (16) + +extern int _iram_start; +extern int _iram_text_start; +extern int _iram_text_end; + +/* NOTE: Naming conventions for RTC_FAST_MEM are + * different for ESP32-C3 and other RISC-V targets + */ +#if CONFIG_SOC_RTC_FAST_MEM_SUPPORTED +#if CONFIG_IDF_TARGET_ARCH_RISCV +extern int _rtc_fast_start; +#else +extern int _rtc_text_start; +#endif +extern int _rtc_text_end; +extern int _rtc_force_fast_start; +#endif + +#if CONFIG_SOC_RTC_SLOW_MEM_SUPPORTED +extern int _rtc_force_slow_start; +extern int _rtc_data_start; +#endif + +/* ---------------------------------------------------- DCACHE Violation Checks ---------------------------------------------------- */ + +#if SOC_DCACHE_SUPPORTED +/* DCACHE: Internal cache memory accessed via DBUS */ +static uint32_t* get_test_dcache_addr(void) +{ + uint32_t *dcache_addr = NULL; +#if !CONFIG_ESP32S2_DATA_CACHE_0KB + dcache_addr = (uint32_t *)MAP_IRAM_TO_DRAM((uint32_t)&_iram_start - 0x04); +#endif + return dcache_addr; +} + +void test_dcache_read_violation(void) +{ + uint32_t *test_addr = get_test_dcache_addr(); + printf("DCACHE: Read operation | Address: %p\n", test_addr); + printf("Value : 0x%" PRIx32 "\n", *test_addr); +} + +void test_dcache_write_violation(void) +{ + uint32_t *test_addr = get_test_dcache_addr(); + printf("DCACHE: Write operation | Address: %p\n", test_addr); + *test_addr = RND_VAL; +} +#endif + +/* ---------------------------------------------------- IRAM Violation Checks ---------------------------------------------------- */ + +/* IRAM: I/DCACHE boundary region */ +void test_iram_reg1_write_violation(void) +{ + uint32_t *test_addr = (uint32_t *)((uint32_t)(&_iram_start) - 0x04); + printf("IRAM: Write operation | Address: %p\n", test_addr); + *test_addr = RND_VAL; +} + +/* IRAM: Interrupt vector table region */ +void test_iram_reg2_write_violation(void) +{ + uint32_t *test_addr = (uint32_t *)((uint32_t)(&_iram_text_start) - 0x04); + printf("IRAM: Write operation | Address: %p\n", test_addr); + *test_addr = RND_VAL; +} + +/* IRAM: Text (and data) region */ +void test_iram_reg3_write_violation(void) +{ + uint32_t *test_addr = (uint32_t *)((uint32_t)(&_iram_text_end) - 0x04); + printf("IRAM: Write operation | Address: %p\n", test_addr); + *test_addr = RND_VAL; +} + +/* IRAM: Through the data bus */ +void test_iram_reg4_write_violation(void) +{ + uint32_t *test_addr = (uint32_t *)MAP_IRAM_TO_DRAM((uint32_t)&_iram_text_end - 0x04); + printf("IRAM: Write operation | Address: %p\n", test_addr); + *test_addr = RND_VAL; +} + +/* ---------------------------------------------------- DRAM Violation Checks ---------------------------------------------------- */ + +static void foo_d(void) +{ + for(int i = 0; i < SPIN_ITER; i++) + __asm__ __volatile__("NOP"); +} + +static DRAM_ATTR uint8_t s_dram_buf[1024]; + +/* DRAM: Data region (DRAM_ATTR tagged) */ +void test_dram_reg1_execute_violation(void) +{ + memcpy(&s_dram_buf, &foo_d, sizeof(s_dram_buf)); + void (*func_ptr)(void); + func_ptr = (void(*)(void))&s_dram_buf; + printf("DRAM: Execute operation | Address: %p\n", &s_dram_buf); + func_ptr(); +} + +/* DRAM: Heap region */ +void test_dram_reg2_execute_violation(void) +{ + uint8_t *instr = calloc(1024, sizeof(uint8_t)); + assert(instr != NULL); + + printf("DRAM: Execute operation | Address: %p\n", instr); + + memcpy(instr, &foo_d, 1024); + void (*func_ptr)(void); + func_ptr = (void(*)(void))instr; + func_ptr(); +} + +/* ---------------------------------------------------- RTC Violation Checks ---------------------------------------------------- */ + +#if CONFIG_SOC_RTC_FAST_MEM_SUPPORTED +static RTC_FAST_ATTR uint32_t var_f = RND_VAL; + +static RTC_IRAM_ATTR void foo_f(void) +{ + for(int i = 0; i < SPIN_ITER; i++) + __asm__ __volatile__("NOP"); +} + +/* RTC_FAST_MEM: .text section start */ +void test_rtc_fast_reg1_execute_violation(void) +{ +#if CONFIG_IDF_TARGET_ARCH_RISCV + void (*test_addr)(void) = (void(*)(void))((uint32_t)&_rtc_fast_start); +#else + void (*test_addr)(void) = (void(*)(void))((uint32_t)&_rtc_text_start); +#endif + printf("RTC_MEM (Fast): Execute operation | Address: %p\n", test_addr); + test_addr(); +} + +/* RTC_FAST_MEM: .text section boundary */ +void test_rtc_fast_reg2_execute_violation(void) +{ + void (*test_addr)(void) = (void(*)(void))((uint32_t)&_rtc_text_end - 0x04); + printf("RTC_MEM (Fast): Execute operation | Address: %p\n", test_addr); + test_addr(); +} + +/* RTC_FAST_MEM: .data section */ +void test_rtc_fast_reg3_execute_violation(void) +{ + void (*test_addr)(void) = (void(*)(void))((uint32_t)&_rtc_force_fast_start + 0x04); + printf("RTC_MEM (Fast): Execute operation | Address: %p\n", test_addr); + test_addr(); +} +#endif + +#if CONFIG_SOC_RTC_SLOW_MEM_SUPPORTED +static RTC_SLOW_ATTR uint32_t var_s = RND_VAL; + +static RTC_SLOW_ATTR void foo_s(void) +{ + for(int i = 0; i < SPIN_ITER; i++) + __asm__ __volatile__("NOP"); +} + +/* RTC_SLOW_MEM: Data tagged with RTC_SLOW_ATTR */ +void test_rtc_slow_reg1_execute_violation(void) +{ + void (*test_addr)(void) = (void(*)(void))((uint32_t)&_rtc_force_slow_start); + printf("RTC_MEM (Slow): Execute operation | Address: %p\n", test_addr); + test_addr(); +} + +/* RTC_SLOW_MEM: Region start */ +void test_rtc_slow_reg2_execute_violation(void) +{ + void (*test_addr)(void) = (void(*)(void))((uint32_t)&_rtc_data_start); + printf("RTC_MEM (Slow): Execute operation | Address: %p\n", test_addr); + test_addr(); +} +#endif + +static void __attribute__((constructor)) test_print_rtc_var_func(void) +{ +#if CONFIG_SOC_RTC_FAST_MEM_SUPPORTED + printf("foo_f: %p | var_f: %p\n", &foo_f, &var_f); +#endif +#if CONFIG_SOC_RTC_SLOW_MEM_SUPPORTED + printf("foo_s: %p | var_s: %p\n", &foo_s, &var_s); +#endif +} diff --git a/tools/test_apps/system/panic/pytest_panic.py b/tools/test_apps/system/panic/pytest_panic.py index bcc8343658..64ad0319a7 100644 --- a/tools/test_apps/system/panic/pytest_panic.py +++ b/tools/test_apps/system/panic/pytest_panic.py @@ -158,7 +158,7 @@ def test_task_wdt_both_cpus(dut: PanicTestDut, config: str, test_func_name: str) @pytest.mark.parametrize('config', CONFIGS_EXTRAM_STACK, indirect=True) def test_panic_extram_stack(dut: PanicTestDut, config: str, test_func_name: str) -> None: - dut.expect_test_func_name(test_func_name) + dut.run_test_func(test_func_name) dut.expect_none('Allocated stack is not in external RAM') dut.expect_none('Guru Meditation') dut.expect_backtrace() @@ -473,3 +473,235 @@ def test_panic_delay(dut: PanicTestDut) -> None: dut.expect_exact('Rebooting...', timeout=3) dut.expect_exact('rst:0xc (SW_CPU_RESET)') + + +######################### +# for memprot test only # +######################### + +# Memprot-related tests are supported only on targets with PMS/PMA peripheral; +# currently ESP32-S2, ESP32-C3 and ESP32-C2 are supported +CONFIGS_MEMPROT_IDRAM = [ + pytest.param('memprot_esp32s2', marks=[pytest.mark.esp32s2]), + pytest.param('memprot_esp32c3', marks=[pytest.mark.esp32c3]), + pytest.param('memprot_esp32c2', marks=[pytest.mark.esp32c2]) +] + +CONFIGS_MEMPROT_DCACHE = [ + pytest.param('memprot_esp32s2', marks=pytest.mark.esp32s2), +] + +CONFIGS_MEMPROT_RTC_FAST_MEM = [ + pytest.param('memprot_esp32s2', marks=[pytest.mark.esp32s2]), + pytest.param('memprot_esp32c3', marks=[pytest.mark.esp32c3]), +] + +CONFIGS_MEMPROT_RTC_SLOW_MEM = [ + pytest.param('memprot_esp32s2', marks=[pytest.mark.esp32s2]), +] + + +@pytest.mark.parametrize('config', CONFIGS_MEMPROT_DCACHE, indirect=True) +@pytest.mark.generic +def test_dcache_read_violation(dut: PanicTestDut, test_func_name: str) -> None: + dut.run_test_func(test_func_name) + dut.expect_exact(r'Test error: Test function has returned') + + +# TODO: IDF-6820: ESP32-S2 -> Fix multiple panic reasons in different runs +@pytest.mark.parametrize('config', CONFIGS_MEMPROT_DCACHE, indirect=True) +@pytest.mark.generic +@pytest.mark.xfail('config.getvalue("target") == "esp32s2"', reason='Incorrect panic reason may be observed', run=False) +def test_dcache_write_violation(dut: PanicTestDut, test_func_name: str) -> None: + dut.run_test_func(test_func_name) + dut.expect_gme('Memory protection fault') + dut.expect(r'Write operation at address [0-9xa-f]+ not permitted \((\S+)\)') + dut.expect_reg_dump(0) + dut.expect_backtrace() + + +@pytest.mark.parametrize('config', CONFIGS_MEMPROT_IDRAM, indirect=True) +@pytest.mark.generic +def test_iram_reg1_write_violation(dut: PanicTestDut, test_func_name: str) -> None: + dut.run_test_func(test_func_name) + + if dut.target == 'esp32s2': + dut.expect_gme('Memory protection fault') + dut.expect(r'Write operation at address [0-9xa-f]+ not permitted \((\S+)\)') + dut.expect_reg_dump(0) + dut.expect_backtrace() + elif dut.target == 'esp32c3': + dut.expect_exact(r'Test error: Test function has returned') + elif dut.target == 'esp32c2': + dut.expect_gme('Store access fault') + dut.expect_reg_dump(0) + dut.expect_stack_dump() + + +@pytest.mark.parametrize('config', CONFIGS_MEMPROT_IDRAM, indirect=True) +@pytest.mark.generic +def test_iram_reg2_write_violation(dut: PanicTestDut, test_func_name: str) -> None: + dut.run_test_func(test_func_name) + + if dut.target == 'esp32s2': + dut.expect_gme('Memory protection fault') + dut.expect(r'Write operation at address [0-9xa-f]+ not permitted \((\S+)\)') + dut.expect_reg_dump(0) + dut.expect_backtrace() + elif dut.target == 'esp32c3': + dut.expect_gme('Memory protection fault') + dut.expect(r' memory type: (\S+)') + dut.expect(r' faulting address: [0-9xa-f]+') + dut.expect(r' operation type: (\S+)') + dut.expect_reg_dump(0) + dut.expect_stack_dump() + elif dut.target == 'esp32c2': + dut.expect_gme('Store access fault') + dut.expect_reg_dump(0) + dut.expect_stack_dump() + + +@pytest.mark.parametrize('config', CONFIGS_MEMPROT_IDRAM, indirect=True) +@pytest.mark.generic +def test_iram_reg3_write_violation(dut: PanicTestDut, test_func_name: str) -> None: + dut.run_test_func(test_func_name) + + if dut.target == 'esp32s2': + dut.expect_gme('Memory protection fault') + dut.expect(r'Write operation at address [0-9xa-f]+ not permitted \((\S+)\)') + dut.expect_reg_dump(0) + dut.expect_backtrace() + elif dut.target == 'esp32c3': + dut.expect_gme('Memory protection fault') + dut.expect(r' memory type: (\S+)') + dut.expect(r' faulting address: [0-9xa-f]+') + dut.expect(r' operation type: (\S+)') + dut.expect_reg_dump(0) + dut.expect_stack_dump() + elif dut.target == 'esp32c2': + dut.expect_gme('Store access fault') + dut.expect_reg_dump(0) + dut.expect_stack_dump() + + +# TODO: IDF-6820: ESP32-S2 -> Fix incorrect panic reason: Unhandled debug exception +@pytest.mark.parametrize('config', CONFIGS_MEMPROT_IDRAM, indirect=True) +@pytest.mark.generic +@pytest.mark.xfail('config.getvalue("target") == "esp32s2"', reason='Incorrect panic reason may be observed', run=False) +def test_iram_reg4_write_violation(dut: PanicTestDut, test_func_name: str) -> None: + dut.run_test_func(test_func_name) + + if dut.target == 'esp32s2': + dut.expect_gme('Memory protection fault') + dut.expect(r'Write operation at address [0-9xa-f]+ not permitted \((\S+)\)') + dut.expect_reg_dump(0) + dut.expect_backtrace() + elif dut.target == 'esp32c3': + dut.expect_gme('Memory protection fault') + dut.expect(r' memory type: (\S+)') + dut.expect(r' faulting address: [0-9xa-f]+') + dut.expect(r' operation type: (\S+)') + dut.expect_reg_dump(0) + dut.expect_stack_dump() + elif dut.target == 'esp32c2': + dut.expect_gme('Store access fault') + dut.expect_reg_dump(0) + dut.expect_stack_dump() + + +# TODO: IDF-6820: ESP32-S2 -> Fix multiple panic reasons in different runs +@pytest.mark.parametrize('config', CONFIGS_MEMPROT_IDRAM, indirect=True) +@pytest.mark.generic +@pytest.mark.xfail('config.getvalue("target") == "esp32s2"', reason='Multiple panic reasons for the same test may surface', run=False) +def test_dram_reg1_execute_violation(dut: PanicTestDut, test_func_name: str) -> None: + dut.run_test_func(test_func_name) + + if dut.target == 'esp32s2': + dut.expect_gme('Memory protection fault') + dut.expect(r'Unknown operation at address [0-9xa-f]+ not permitted \((\S+)\)') + dut.expect_reg_dump(0) + dut.expect_corrupted_backtrace() + elif dut.target in ['esp32c3', 'esp32c2']: + dut.expect_gme('Instruction access fault') + dut.expect_reg_dump(0) + dut.expect_stack_dump() + + +@pytest.mark.parametrize('config', CONFIGS_MEMPROT_IDRAM, indirect=True) +@pytest.mark.generic +def test_dram_reg2_execute_violation(dut: PanicTestDut, test_func_name: str) -> None: + dut.run_test_func(test_func_name) + + if dut.target == 'esp32s2': + dut.expect_gme('InstructionFetchError') + dut.expect_reg_dump(0) + dut.expect_corrupted_backtrace() + elif dut.target in ['esp32c3', 'esp32c2']: + dut.expect_gme('Instruction access fault') + dut.expect_reg_dump(0) + dut.expect_stack_dump() + + +@pytest.mark.parametrize('config', CONFIGS_MEMPROT_RTC_FAST_MEM, indirect=True) +@pytest.mark.generic +def test_rtc_fast_reg1_execute_violation(dut: PanicTestDut, test_func_name: str) -> None: + dut.run_test_func(test_func_name) + dut.expect_exact(r'Test error: Test function has returned') + + +@pytest.mark.parametrize('config', CONFIGS_MEMPROT_RTC_FAST_MEM, indirect=True) +@pytest.mark.generic +def test_rtc_fast_reg2_execute_violation(dut: PanicTestDut, test_func_name: str) -> None: + dut.run_test_func(test_func_name) + dut.expect_gme('Memory protection fault') + + if dut.target == 'esp32s2': + dut.expect(r'Read operation at address [0-9xa-f]+ not permitted \((\S+)\)') + dut.expect_reg_dump(0) + dut.expect_backtrace() + elif dut.target == 'esp32c3': + dut.expect(r' memory type: (\S+)') + dut.expect(r' faulting address: [0-9xa-f]+') + dut.expect(r' operation type: (\S+)') + dut.expect_reg_dump(0) + dut.expect_stack_dump() + + +# TODO: IDF-6820: ESP32-S2 -> Fix multiple panic reasons in different runs +@pytest.mark.parametrize('config', CONFIGS_MEMPROT_RTC_FAST_MEM, indirect=True) +@pytest.mark.generic +@pytest.mark.xfail('config.getvalue("target") == "esp32s2"', reason='Multiple panic reasons for the same test may surface', run=False) +def test_rtc_fast_reg3_execute_violation(dut: PanicTestDut, test_func_name: str) -> None: + dut.run_test_func(test_func_name) + dut.expect_gme('Memory protection fault') + + if dut.target == 'esp32s2': + dut.expect(r'Unknown operation at address [0-9xa-f]+ not permitted \((\S+)\)') + dut.expect_reg_dump(0) + dut.expect_backtrace() + elif dut.target == 'esp32c3': + dut.expect(r' memory type: (\S+)') + dut.expect(r' faulting address: [0-9xa-f]+') + dut.expect(r' operation type: (\S+)') + dut.expect_reg_dump(0) + dut.expect_stack_dump() + + +@pytest.mark.parametrize('config', CONFIGS_MEMPROT_RTC_SLOW_MEM, indirect=True) +@pytest.mark.generic +def test_rtc_slow_reg1_execute_violation(dut: PanicTestDut, test_func_name: str) -> None: + dut.run_test_func(test_func_name) + dut.expect_gme('Memory protection fault') + dut.expect(r'Read operation at address [0-9xa-f]+ not permitted \((\S+)\)') + dut.expect_reg_dump(0) + dut.expect_corrupted_backtrace() + + +@pytest.mark.parametrize('config', CONFIGS_MEMPROT_RTC_SLOW_MEM, indirect=True) +@pytest.mark.generic +def test_rtc_slow_reg2_execute_violation(dut: PanicTestDut, test_func_name: str) -> None: + dut.run_test_func(test_func_name) + dut.expect_gme('Memory protection fault') + dut.expect(r'Read operation at address [0-9xa-f]+ not permitted \((\S+)\)') + dut.expect_reg_dump(0) + dut.expect_corrupted_backtrace() diff --git a/tools/test_apps/system/panic/sdkconfig.ci.memprot_esp32c2 b/tools/test_apps/system/panic/sdkconfig.ci.memprot_esp32c2 new file mode 100644 index 0000000000..cae7f78260 --- /dev/null +++ b/tools/test_apps/system/panic/sdkconfig.ci.memprot_esp32c2 @@ -0,0 +1,8 @@ +# Restricting to ESP32C2 +CONFIG_IDF_TARGET="esp32c2" + +# Enabling memory protection +CONFIG_ESP_SYSTEM_PMP_IDRAM_SPLIT=y + +# Enable memprot test +CONFIG_TEST_MEMPROT=y diff --git a/tools/test_apps/system/panic/sdkconfig.ci.memprot_esp32c3 b/tools/test_apps/system/panic/sdkconfig.ci.memprot_esp32c3 new file mode 100644 index 0000000000..2b7dede7b0 --- /dev/null +++ b/tools/test_apps/system/panic/sdkconfig.ci.memprot_esp32c3 @@ -0,0 +1,9 @@ +# Restricting to ESP32C3 +CONFIG_IDF_TARGET="esp32c3" + +# Enabling memory protection +CONFIG_ESP_SYSTEM_MEMPROT_FEATURE=y +CONFIG_ESP_SYSTEM_MEMPROT_FEATURE_LOCK=y + +# Enable memprot test +CONFIG_TEST_MEMPROT=y diff --git a/tools/test_apps/system/panic/sdkconfig.ci.memprot_esp32s2 b/tools/test_apps/system/panic/sdkconfig.ci.memprot_esp32s2 new file mode 100644 index 0000000000..05e1f9e87d --- /dev/null +++ b/tools/test_apps/system/panic/sdkconfig.ci.memprot_esp32s2 @@ -0,0 +1,12 @@ +# Restricting to ESP32S2 +CONFIG_IDF_TARGET="esp32s2" + +# Enabling memory protection +CONFIG_ESP_SYSTEM_MEMPROT_FEATURE=y +CONFIG_ESP_SYSTEM_MEMPROT_FEATURE_LOCK=y + +# Enabling DCACHE +CONFIG_ESP32S2_DATA_CACHE_8KB=y + +# Enable memprot test +CONFIG_TEST_MEMPROT=y diff --git a/tools/test_apps/system/panic/sdkconfig.ci.memprot_esp32s3 b/tools/test_apps/system/panic/sdkconfig.ci.memprot_esp32s3 new file mode 100644 index 0000000000..3e1fc69f27 --- /dev/null +++ b/tools/test_apps/system/panic/sdkconfig.ci.memprot_esp32s3 @@ -0,0 +1,12 @@ +# Restricting to ESP32S3 +CONFIG_IDF_TARGET="esp32s3" + +# Enabling memory protection +CONFIG_ESP_SYSTEM_MEMPROT_FEATURE=y +CONFIG_ESP_SYSTEM_MEMPROT_FEATURE_LOCK=y + +# Enabling DCACHE +CONFIG_ESP32S3_DATA_CACHE_16KB=y + +# Enable memprot test +CONFIG_TEST_MEMPROT=y diff --git a/tools/test_apps/system/panic/test_panic_util/panic_dut.py b/tools/test_apps/system/panic/test_panic_util/panic_dut.py index da63a5ec91..4afa79855e 100644 --- a/tools/test_apps/system/panic/test_panic_util/panic_dut.py +++ b/tools/test_apps/system/panic/test_panic_util/panic_dut.py @@ -71,6 +71,11 @@ class PanicTestDut(IdfDut): match = self.expect(r'Backtrace:( 0x[0-9a-fA-F]{8}:0x[0-9a-fA-F]{8})+(?P \|<-CORRUPTED)?') assert not match.group('corrupted') + def expect_corrupted_backtrace(self) -> None: + assert self.is_xtensa, 'Backtrace can be printed only on Xtensa' + self.expect_exact('Backtrace:') + self.expect_exact('CORRUPTED') + def expect_stack_dump(self) -> None: assert not self.is_xtensa, 'Stack memory dump is only printed on RISC-V' self.expect_exact('Stack memory:')