diff --git a/components/hal/esp32c3/include/hal/memprot_ll.h b/components/hal/esp32c3/include/hal/memprot_ll.h index 77227351ee..61a20d0434 100644 --- a/components/hal/esp32c3/include/hal/memprot_ll.h +++ b/components/hal/esp32c3/include/hal/memprot_ll.h @@ -300,7 +300,7 @@ static inline uint32_t memprot_ll_iram0_get_monitor_status_fault_world(void) static inline intptr_t memprot_ll_iram0_get_monitor_status_fault_addr(void) { uint32_t addr = REG_GET_FIELD(SENSITIVE_CORE_0_IRAM0_PMS_MONITOR_2_REG, SENSITIVE_CORE_0_IRAM0_PMS_MONITOR_VIOLATE_STATUS_ADDR); - return (intptr_t)(addr > 0 ? (addr << I_D_FAULT_ADDR_SHIFT) + IRAM0_ADDRESS_LOW : 0); + return (intptr_t)(addr > 0 ? (addr << I_D_FAULT_ADDR_SHIFT) + IRAM0_VIOLATE_STATUS_ADDR_OFFSET : 0); } static inline uint32_t memprot_ll_iram0_get_monitor_status_register(void) @@ -815,7 +815,7 @@ static inline uint32_t memprot_ll_dram0_get_monitor_status_fault_world(void) static inline uint32_t memprot_ll_dram0_get_monitor_status_fault_addr(void) { uint32_t addr = REG_GET_FIELD(SENSITIVE_CORE_0_DRAM0_PMS_MONITOR_2_REG, SENSITIVE_CORE_0_DRAM0_PMS_MONITOR_VIOLATE_STATUS_ADDR); - return addr > 0 ? (addr << I_D_FAULT_ADDR_SHIFT) + DRAM0_ADDRESS_LOW : 0; + return addr > 0 ? (addr << I_D_FAULT_ADDR_SHIFT) + DRAM0_VIOLATE_STATUS_ADDR_OFFSET : 0; } static inline uint32_t memprot_ll_dram0_get_monitor_status_fault_wr(void) diff --git a/components/hal/esp32s3/include/hal/memprot_ll.h b/components/hal/esp32s3/include/hal/memprot_ll.h index f4528c5357..ccb99c77bd 100644 --- a/components/hal/esp32s3/include/hal/memprot_ll.h +++ b/components/hal/esp32s3/include/hal/memprot_ll.h @@ -627,7 +627,7 @@ static inline memprot_hal_err_t memprot_ll_iram0_get_monitor_status_fault_addr(c return MEMP_HAL_ERR_CORE_INVALID; } - *addr = (void*)(reg_off > 0 ? (reg_off << I_FAULT_ADDR_SHIFT) + IRAM0_ADDRESS_LOW : 0); + *addr = (void*)(reg_off > 0 ? (reg_off << I_FAULT_ADDR_SHIFT) + IRAM0_VIOLATE_STATUS_ADDR_OFFSET : 0); return MEMP_HAL_OK; } @@ -1646,7 +1646,7 @@ static inline memprot_hal_err_t memprot_ll_dram0_get_monitor_status_fault_addr(c return MEMP_HAL_ERR_CORE_INVALID; } - *addr = (void*)(reg_off > 0 ? (reg_off << D_FAULT_ADDR_SHIFT) + DRAM0_ADDRESS_LOW : 0); + *addr = (void*)(reg_off > 0 ? (reg_off << D_FAULT_ADDR_SHIFT) + DRAM0_VIOLATE_STATUS_ADDR_OFFSET : 0); return MEMP_HAL_OK; } diff --git a/components/soc/esp32c3/include/soc/memprot_defs.h b/components/soc/esp32c3/include/soc/memprot_defs.h index 78bf5cdb0e..59d04a6eb4 100644 --- a/components/soc/esp32c3/include/soc/memprot_defs.h +++ b/components/soc/esp32c3/include/soc/memprot_defs.h @@ -35,6 +35,9 @@ typedef union { #define DRAM_SRAM_START 0x3FC7C000 +#define IRAM0_VIOLATE_STATUS_ADDR_OFFSET 0x40000000 +#define DRAM0_VIOLATE_STATUS_ADDR_OFFSET 0x3C000000 + //IRAM0 //16kB (ICACHE) diff --git a/components/soc/esp32s3/include/soc/memprot_defs.h b/components/soc/esp32s3/include/soc/memprot_defs.h index 86dbf1a753..f1c02bae57 100644 --- a/components/soc/esp32s3/include/soc/memprot_defs.h +++ b/components/soc/esp32s3/include/soc/memprot_defs.h @@ -42,6 +42,9 @@ typedef union { #define I_FAULT_ADDR_SHIFT 0x2 #define D_FAULT_ADDR_SHIFT 0x4 +#define IRAM0_VIOLATE_STATUS_ADDR_OFFSET 0x40000000 +#define DRAM0_VIOLATE_STATUS_ADDR_OFFSET 0x3C000000 + //Icache #define SENSITIVE_CORE_X_ICACHE_PMS_CONSTRAIN_SRAM_WORLD_X_R 0x1 #define SENSITIVE_CORE_X_ICACHE_PMS_CONSTRAIN_SRAM_WORLD_X_W 0x2 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 92dc346341..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_panic_main.c" - INCLUDE_DIRS "." - 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/include/test_panic.h b/tools/test_apps/system/panic/main/include/test_panic.h new file mode 100644 index 0000000000..c4be49af10 --- /dev/null +++ b/tools/test_apps/system/panic/main/include/test_panic.h @@ -0,0 +1,56 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +/* Utility functions */ + +void die(const char* msg) __attribute__ ((noreturn)); + +/* Functions causing an exception/panic in different ways */ + +void test_abort(void); + +void test_abort_cache_disabled(void); + +void test_int_wdt(void); + +void test_task_wdt_cpu0(void); + +#if CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH && CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY +void test_panic_extram_stack(void); +#endif + +#if !CONFIG_FREERTOS_UNICORE +void test_task_wdt_cpu1(void); +void test_task_wdt_both_cpus(void); +#endif + +void test_storeprohibited(void); + +void test_cache_error(void); + +void test_int_wdt_cache_disabled(void); + +void test_stack_overflow(void); + +void test_illegal_instruction(void); + +void test_instr_fetch_prohibited(void); + +void test_ub(void); + +void test_assert(void); + +void test_assert_cache_disabled(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 new file mode 100644 index 0000000000..f5edc684fe --- /dev/null +++ b/tools/test_apps/system/panic/main/test_app_main.c @@ -0,0 +1,140 @@ +/* + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +#include "esp_err.h" +#include "esp_system.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +#include "test_panic.h" +#include "test_memprot.h" + +/* Test Utility Functions */ + +#define BOOT_CMD_MAX_LEN (128) + +#define HANDLE_TEST(test_name, name_) \ + if (strcmp(test_name, #name_) == 0) { \ + name_(); \ + die("Test function has returned"); \ + } + +static const char* get_test_name(void) +{ + static char test_name_str[BOOT_CMD_MAX_LEN] = {0}; + + printf("Enter test name: "); + fflush(stdout); + + /* Not using blocking fgets(stdin) here, as QEMU doesn't yet implement RX timeout interrupt, + * which is required for the UART driver and blocking stdio to work. + */ + int c = EOF; + char *p = test_name_str; + const char *end = test_name_str + sizeof(test_name_str) - 1; + while (p < end) { + c = getchar(); + if (c == EOF) { + vTaskDelay(pdMS_TO_TICKS(10)); + } else if ((c == '\r' || c == '\n') && p != test_name_str) { + /* terminate the line */ + puts("\n\r"); + fflush(stdout); + *p = '\0'; + break; + } else { + /* echo the received character */ + putchar(c); + fflush(stdout); + /* and save it */ + *p = c; + ++p; + } + } + + return test_name_str; +} + +/* app_main */ + +void app_main(void) +{ + /* Needed to allow the tick hook to set correct INT WDT timeouts */ + vTaskDelay(2); + + /* Test script sends to command over UART. Read it and determine how to proceed. */ + const char* test_name = get_test_name(); + if (test_name == NULL) { + /* Nothing to do */ + return; + } + printf("Got test name: %s\n", test_name); + + HANDLE_TEST(test_name, test_abort); + HANDLE_TEST(test_name, test_abort_cache_disabled); + HANDLE_TEST(test_name, test_int_wdt); + HANDLE_TEST(test_name, test_task_wdt_cpu0); +#if CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH && CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY + HANDLE_TEST(test_name, test_panic_extram_stack); +#endif +#if !CONFIG_FREERTOS_UNICORE + HANDLE_TEST(test_name, test_task_wdt_cpu1); + HANDLE_TEST(test_name, test_task_wdt_both_cpus); +#endif + HANDLE_TEST(test_name, test_storeprohibited); + HANDLE_TEST(test_name, test_cache_error); + HANDLE_TEST(test_name, test_int_wdt_cache_disabled); + HANDLE_TEST(test_name, test_stack_overflow); + HANDLE_TEST(test_name, test_illegal_instruction); + HANDLE_TEST(test_name, test_instr_fetch_prohibited); + HANDLE_TEST(test_name, test_ub); + 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/main/test_panic.c b/tools/test_apps/system/panic/main/test_panic.c new file mode 100644 index 0000000000..10d9f9e5a8 --- /dev/null +++ b/tools/test_apps/system/panic/main/test_panic.c @@ -0,0 +1,196 @@ +/* + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ +#include +#include +#include +#include + +#include "esp_partition.h" +#include "esp_flash.h" +#include "esp_system.h" + +#include "esp_private/cache_utils.h" +#include "esp_memory_utils.h" +#include "esp_heap_caps.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +/* Test utility function */ + +extern void esp_restart_noos(void) __attribute__ ((noreturn)); + +void die(const char* msg) +{ + printf("Test error: %s\n\n", msg); + fflush(stdout); + usleep(1000); + /* Don't use abort here as it would enter the panic handler */ + esp_restart_noos(); +} + +/* implementations of the test functions */ + +void test_abort(void) +{ + abort(); +} + +void IRAM_ATTR test_abort_cache_disabled(void) +{ + spi_flash_disable_interrupts_caches_and_other_cpu(); + abort(); +} + +void test_int_wdt(void) +{ + portDISABLE_INTERRUPTS(); + while (true) { + ; + } +} + +void test_task_wdt_cpu0(void) +{ + while (true) { + ; + } +} + +#if CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH && CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY + +static void stack_in_extram(void* arg) { + (void) arg; + /* Abort instead of using a load/store prohibited to prevent a sanitize error */ + abort(); +} + +void test_panic_extram_stack(void) { + /* Start by initializing a Task which has a stack in external RAM */ + StaticTask_t handle; + const uint32_t stack_size = 8192; + void* stack = heap_caps_malloc(stack_size, MALLOC_CAP_SPIRAM); + + /* Make sure the stack is in external RAM */ + if (!esp_ptr_external_ram(stack)) { + die("Allocated stack is not in external RAM!\n"); + } + + xTaskCreateStatic(stack_in_extram, "Task_stack_extram", stack_size, NULL, 4, (StackType_t*) stack, &handle); + + vTaskDelay(1000); +} + + +#endif // ESP_COREDUMP_ENABLE_TO_FLASH && SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY + + +#if !CONFIG_FREERTOS_UNICORE +static void infinite_loop(void* arg) { + (void) arg; + while(1) { + ; + } +} + +void test_task_wdt_cpu1(void) +{ + xTaskCreatePinnedToCore(infinite_loop, "Infinite loop", 1024, NULL, 1, NULL, 1); + while (true) { + vTaskDelay(1); + } +} + +void test_task_wdt_both_cpus(void) +{ + xTaskCreatePinnedToCore(infinite_loop, "Infinite loop", 1024, NULL, 4, NULL, 1); + /* Give some time to the task on CPU 1 to be scheduled */ + vTaskDelay(1); + xTaskCreatePinnedToCore(infinite_loop, "Infinite loop", 1024, NULL, 4, NULL, 0); + while (true) { + ; + } +} +#endif + +void __attribute__((no_sanitize_undefined)) test_storeprohibited(void) +{ + *(int*) 0x1 = 0; +} + +void IRAM_ATTR test_cache_error(void) +{ + spi_flash_disable_interrupts_caches_and_other_cpu(); + die("this should not be printed"); +} + +void IRAM_ATTR test_int_wdt_cache_disabled(void) +{ + spi_flash_disable_interrupts_caches_and_other_cpu(); + portDISABLE_INTERRUPTS(); + while (true) { + ; + } +} + +void test_assert(void) +{ + assert(0); +} + +void IRAM_ATTR test_assert_cache_disabled(void) +{ + spi_flash_disable_interrupts_caches_and_other_cpu(); + assert(0); +} + +/** + * This function overwrites the stack beginning from the valid area continuously towards and beyond + * the end of the stack (stack base) of the current task. + * This is to test stack protection measures like a watchpoint at the end of the stack. + * + * @note: This test DOES NOT write beyond the stack limit. It only writes up to exactly the limit itself. + * The FreeRTOS stack protection mechanisms all trigger shortly before the end of the stack. + */ +void test_stack_overflow(void) +{ + register uint32_t* sp asm("sp"); + TaskStatus_t pxTaskStatus; + vTaskGetInfo(NULL, &pxTaskStatus, pdFALSE, pdFALSE); + uint32_t *end = (uint32_t*) pxTaskStatus.pxStackBase; + + // offset - 20 bytes from SP in order to not corrupt the current frame. + // Need to write from higher to lower addresses since the stack grows downwards and the watchpoint/canary is near + // the end of the stack (lowest address). + for (uint32_t* ptr = sp - 5; ptr != end; --ptr) { + *ptr = 0; + } + + // trigger a context switch to initiate checking the FreeRTOS stack canary + vTaskDelay(pdMS_TO_TICKS(0)); +} + +void test_illegal_instruction(void) +{ +#if __XTENSA__ + __asm__ __volatile__("ill"); +#elif __riscv + __asm__ __volatile__("unimp"); +#endif +} + +void test_instr_fetch_prohibited(void) +{ + typedef void (*fptr_t)(void); + volatile fptr_t fptr = (fptr_t) 0x4; + fptr(); +} + +void test_ub(void) +{ + uint8_t stuff[1] = {rand()}; + printf("%d\n", stuff[rand()]); +} diff --git a/tools/test_apps/system/panic/main/test_panic_main.c b/tools/test_apps/system/panic/main/test_panic_main.c deleted file mode 100644 index 5d9211e52f..0000000000 --- a/tools/test_apps/system/panic/main/test_panic_main.c +++ /dev/null @@ -1,295 +0,0 @@ -#include -#include -#include -#include -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "esp_partition.h" -#include "esp_flash.h" -#include "esp_system.h" - -/* utility functions */ -static void die(const char* msg) __attribute__ ((noreturn)); -static const char* get_test_name(void); - -/* functions which cause an exception/panic in different ways */ -static void test_abort(void); -static void test_abort_cache_disabled(void); -static void test_int_wdt(void); -static void test_task_wdt_cpu0(void); -#if CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH && CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY -static void test_panic_extram_stack(void); -#endif -#if !CONFIG_FREERTOS_UNICORE -static void test_task_wdt_cpu1(void); -static void test_task_wdt_both_cpus(void); -#endif -static void test_storeprohibited(void); -static void test_cache_error(void); -static void test_int_wdt_cache_disabled(void); -static void test_stack_overflow(void); -static void test_illegal_instruction(void); -static void test_instr_fetch_prohibited(void); -static void test_ub(void); -static void test_assert(void); -static void test_assert_cache_disabled(void); - - -void app_main(void) -{ - /* Needed to allow the tick hook to set correct INT WDT timeouts */ - vTaskDelay(2); - - /* Test script sends to command over UART. Read it and determine how to proceed. */ - const char* test_name = get_test_name(); - if (test_name == NULL) { - /* Nothing to do */ - return; - } - printf("Got test name: %s\n", test_name); - - #define HANDLE_TEST(name_) \ - if (strcmp(test_name, #name_) == 0) { \ - name_(); \ - die("Test function has returned"); \ - } - - HANDLE_TEST(test_abort); - HANDLE_TEST(test_abort_cache_disabled); - HANDLE_TEST(test_int_wdt); - HANDLE_TEST(test_task_wdt_cpu0); -#if CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH && CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY - HANDLE_TEST(test_panic_extram_stack); -#endif -#if !CONFIG_FREERTOS_UNICORE - HANDLE_TEST(test_task_wdt_cpu1); - HANDLE_TEST(test_task_wdt_both_cpus); -#endif - HANDLE_TEST(test_storeprohibited); - HANDLE_TEST(test_cache_error); - HANDLE_TEST(test_int_wdt_cache_disabled); - HANDLE_TEST(test_stack_overflow); - HANDLE_TEST(test_illegal_instruction); - HANDLE_TEST(test_instr_fetch_prohibited); - HANDLE_TEST(test_ub); - HANDLE_TEST(test_assert); - HANDLE_TEST(test_assert_cache_disabled); - - #undef HANDLE_TEST - - die("Unknown test name"); -} - -/* implementations of the test functions */ - -static void test_abort(void) -{ - abort(); -} - -static void IRAM_ATTR test_abort_cache_disabled(void) -{ - esp_flash_default_chip->os_func->start(esp_flash_default_chip->os_func_data); - abort(); -} - -static void test_int_wdt(void) -{ - portDISABLE_INTERRUPTS(); - while (true) { - ; - } -} - -static void test_task_wdt_cpu0(void) -{ - while (true) { - ; - } -} - -#if CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH && CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY - -static void stack_in_extram(void* arg) { - (void) arg; - /* Abort instead of using a load/store prohibited to prevent a sanitize error */ - abort(); -} - -static void test_panic_extram_stack(void) { - /* Start by initializing a Task which has a stack in external RAM */ - StaticTask_t handle; - const uint32_t stack_size = 8192; - void* stack = heap_caps_malloc(stack_size, MALLOC_CAP_SPIRAM); - - /* Make sure the stack is in external RAM */ - if (!esp_ptr_external_ram(stack)) { - die("Allocated stack is not in external RAM!\n"); - } - - xTaskCreateStatic(stack_in_extram, "Task_stack_extram", stack_size, NULL, 4, (StackType_t*) stack, &handle); - - vTaskDelay(1000); -} - - -#endif // ESP_COREDUMP_ENABLE_TO_FLASH && SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY - - -#if !CONFIG_FREERTOS_UNICORE -static void infinite_loop(void* arg) { - (void) arg; - while(1) { - ; - } -} - -static void test_task_wdt_cpu1(void) -{ - xTaskCreatePinnedToCore(infinite_loop, "Infinite loop", 1024, NULL, 1, NULL, 1); - while (true) { - vTaskDelay(1); - } -} - -static void test_task_wdt_both_cpus(void) -{ - xTaskCreatePinnedToCore(infinite_loop, "Infinite loop", 1024, NULL, 4, NULL, 1); - /* Give some time to the task on CPU 1 to be scheduled */ - vTaskDelay(1); - xTaskCreatePinnedToCore(infinite_loop, "Infinite loop", 1024, NULL, 4, NULL, 0); - while (true) { - ; - } -} -#endif - -static void __attribute__((no_sanitize_undefined)) test_storeprohibited(void) -{ - *(int*) 0x1 = 0; -} - -static IRAM_ATTR void test_cache_error(void) -{ - esp_flash_default_chip->os_func->start(esp_flash_default_chip->os_func_data); - die("this should not be printed"); -} - -static void IRAM_ATTR test_int_wdt_cache_disabled(void) -{ - esp_flash_default_chip->os_func->start(esp_flash_default_chip->os_func_data); - portDISABLE_INTERRUPTS(); - while (true) { - ; - } -} - -static void test_assert(void) -{ - assert(0); -} - -static void IRAM_ATTR test_assert_cache_disabled(void) -{ - esp_flash_default_chip->os_func->start(esp_flash_default_chip->os_func_data); - assert(0); -} - -/** - * This function overwrites the stack beginning from the valid area continuously towards and beyond - * the end of the stack (stack base) of the current task. - * This is to test stack protection measures like a watchpoint at the end of the stack. - * - * @note: This test DOES NOT write beyond the stack limit. It only writes up to exactly the limit itself. - * The FreeRTOS stack protection mechanisms all trigger shortly before the end of the stack. - */ -static void test_stack_overflow(void) -{ - register uint32_t* sp asm("sp"); - TaskStatus_t pxTaskStatus; - vTaskGetInfo(NULL, &pxTaskStatus, pdFALSE, pdFALSE); - uint32_t *end = (uint32_t*) pxTaskStatus.pxStackBase; - - // offset - 20 bytes from SP in order to not corrupt the current frame. - // Need to write from higher to lower addresses since the stack grows downwards and the watchpoint/canary is near - // the end of the stack (lowest address). - for (uint32_t* ptr = sp - 5; ptr != end; --ptr) { - *ptr = 0; - } - - // trigger a context switch to initiate checking the FreeRTOS stack canary - vTaskDelay(pdMS_TO_TICKS(0)); -} - -static void test_illegal_instruction(void) -{ -#if __XTENSA__ - __asm__ __volatile__("ill"); -#elif __riscv - __asm__ __volatile__("unimp"); -#endif -} - -static void test_instr_fetch_prohibited(void) -{ - typedef void (*fptr_t)(void); - volatile fptr_t fptr = (fptr_t) 0x4; - fptr(); -} - -static void test_ub(void) -{ - uint8_t stuff[1] = {rand()}; - printf("%d\n", stuff[rand()]); -} - -/* implementations of the utility functions */ - -#define BOOT_CMD_MAX_LEN (128) - -static const char* get_test_name(void) -{ - static char test_name_str[BOOT_CMD_MAX_LEN] = {0}; - - printf("Enter test name: "); - fflush(stdout); - - /* Not using blocking fgets(stdin) here, as QEMU doesn't yet implement RX timeout interrupt, - * which is required for the UART driver and blocking stdio to work. - */ - int c = EOF; - char *p = test_name_str; - const char *end = test_name_str + sizeof(test_name_str) - 1; - while (p < end) { - c = getchar(); - if (c == EOF) { - vTaskDelay(pdMS_TO_TICKS(10)); - } else if ((c == '\r' || c == '\n') && p != test_name_str) { - /* terminate the line */ - puts("\n\r"); - fflush(stdout); - *p = '\0'; - break; - } else { - /* echo the received character */ - putchar(c); - fflush(stdout); - /* and save it */ - *p = c; - ++p; - } - } - - return test_name_str; -} - -extern void esp_restart_noos(void) __attribute__ ((noreturn)); - -static void die(const char* msg) -{ - printf("Test error: %s\n\n", msg); - fflush(stdout); - usleep(1000); - /* Don't use abort here as it would enter the panic handler */ - esp_restart_noos(); -} 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:')