diff --git a/components/esp_hw_support/port/esp32p4/cpu_region_protect.c b/components/esp_hw_support/port/esp32p4/cpu_region_protect.c index 52037adba0a..1d7e804a4d8 100644 --- a/components/esp_hw_support/port/esp32p4/cpu_region_protect.c +++ b/components/esp_hw_support/port/esp32p4/cpu_region_protect.c @@ -15,6 +15,9 @@ #include "esp_private/esp_psram_extram.h" #endif /* CONFIG_SPIRAM */ +#include "soc/chip_revision.h" +#include "hal/config.h" + #ifdef BOOTLOADER_BUILD // Without L bit set #define CONDITIONAL_NONE 0x0 @@ -82,70 +85,160 @@ static void esp_cpu_configure_invalid_regions(void) PMA_RESET_AND_ENTRY_SET_TOR(15, UINT32_MAX, PMA_TOR | PMA_NONE); } -void esp_cpu_configure_region_protection(void) -{ - /* Notes on implementation: - * - * 1) Note: ESP32-P4 CPU support overlapping PMP regions, configuration is based on static priority - * feature (lowest numbered entry has highest priority). - * - * 2) ESP32-P4 supports 16 PMA regions so we use this feature to block the invalid address ranges. - * However the entries are not sufficient to block all reserved memory ranges and the excluded sections are: - * a. Region between LP ROM and LP SRAM - * b. Region between LP peripherals and External flash (direct access) - * c. Region between External flash (direct access) and External RAM (direct access) - * d. Region between External RAM (direct access) and HP ROM (direct access) - * e. Region between HP ROM (direct access) and HP L2MEM (direct access) - * - * 3) We use combination of NAPOT (Naturally Aligned Power Of Two) and TOR (top of range) - * entries to map all the valid address space, bottom to top. This leaves us with some extra PMP entries - * which can be used to provide more granular access - * - * 4) Entries are grouped in order with some static asserts to try and verify everything is - * correct. - * - * 5) No explicit permission specified in PMP (default all permissions) for following regions due to - * limited entries: - * a. External RAM - * b. LP ROM, LP Peripherals, LP SRAM - * c. External flash, External RAM, HP ROM, HP L2MEM (direct access) - */ +#if HAL_CONFIG(CHIP_SUPPORT_MIN_REV) >= 300 +// Helper macro to set both cached and non-cached PMP entries with the same permissions +#define PMP_ENTRY_SET_CACHED_AND_UNCACHED(cached_entry, non_cached_entry, addr, perm) \ + do { \ + PMP_RESET_AND_ENTRY_SET(cached_entry, addr, perm); \ + PMP_RESET_AND_ENTRY_SET(non_cached_entry, CACHE_LL_L2MEM_NON_CACHE_ADDR(addr), perm); \ + } while(0) - /* There are 4 configuration scenarios for SRAM - * - * 1. Bootloader build: - * - We cannot set the lock bit as we need to reconfigure it again for the application. - * We configure PMP to cover entire valid IRAM and DRAM range. - * - * 2. Application build with CONFIG_ESP_SYSTEM_MEMPROT enabled - * - We split the SRAM into IRAM and DRAM such that IRAM region cannot be written to - * and DRAM region cannot be executed. We use _iram_text_end and _data_start markers to set the boundaries. - * We also lock these entries so the R/W/X permissions are enforced even for machine mode - * - * 3. Application build with CONFIG_ESP_SYSTEM_MEMPROT disabled - * - The IRAM-DRAM split is not enabled so we just need to ensure that access to only valid address ranges are successful - * so for that we set PMP to cover entire valid IRAM and DRAM region. - * We also lock these entries so the R/W/X permissions are enforced even for machine mode - * - * 4. CPU is in OCD debug mode - * - The IRAM-DRAM split is not enabled so that OpenOCD can write and execute from IRAM. - * We set PMP to cover entire valid IRAM and DRAM region. - * We also lock these entries so the R/W/X permissions are enforced even for machine mode - */ +static void esp_cpu_configure_region_protection_rev_v3(void) +{ const unsigned NONE = PMP_L; __attribute__((unused)) const unsigned R = PMP_L | PMP_R; const unsigned RW = PMP_L | PMP_R | PMP_W; const unsigned RX = PMP_L | PMP_R | PMP_X; const unsigned RWX = PMP_L | PMP_R | PMP_W | PMP_X; - // - // Configure all the invalid address regions using PMA - // - esp_cpu_configure_invalid_regions(); + // 1. CPU Subsystem region - contains debug mode code and interrupt config registers + const uint32_t pmpaddr0 = PMPADDR_NAPOT(SOC_CPU_SUBSYSTEM_LOW, SOC_CPU_SUBSYSTEM_HIGH); + PMP_RESET_AND_ENTRY_SET(0, pmpaddr0, PMP_NAPOT | RW); + _Static_assert(SOC_CPU_SUBSYSTEM_LOW < SOC_CPU_SUBSYSTEM_HIGH, "Invalid CPU subsystem region"); - // - // Configure all the valid address regions using PMP - // + // 2. HP-CPU TCM + // The default memory permissions are RWX and TCM should be RWX, so we can skip configuring it + + // 3. CPU Peripherals + const uint32_t pmpaddr1 = PMPADDR_NAPOT(CPU_PERIPH_LOW, CPU_PERIPH_HIGH); + PMP_RESET_AND_ENTRY_SET(1, pmpaddr1, PMP_NAPOT | RW); + _Static_assert(CPU_PERIPH_LOW < CPU_PERIPH_HIGH, "Invalid CPU peripheral region"); + + // 4. I/D-ROM + const uint32_t pmpaddr2 = PMPADDR_NAPOT(SOC_IROM_MASK_LOW, SOC_IROM_MASK_HIGH); + PMP_RESET_AND_ENTRY_SET(2, pmpaddr2, PMP_NAPOT | RX); + + const uint32_t pmpaddr3 = PMPADDR_NAPOT(CACHE_LL_L2MEM_NON_CACHE_ADDR(SOC_IROM_MASK_LOW), CACHE_LL_L2MEM_NON_CACHE_ADDR(SOC_IROM_MASK_HIGH)); + PMP_RESET_AND_ENTRY_SET(3, pmpaddr3, PMP_NAPOT | RX); + + _Static_assert(SOC_IROM_MASK_LOW < SOC_IROM_MASK_HIGH, "Invalid I/D-ROM region"); + + // 5. IRAM and DRAM + if (esp_cpu_dbgr_is_attached()) { + // Anti-FI check that cpu is really in ocd mode + ESP_FAULT_ASSERT(esp_cpu_dbgr_is_attached()); + + PMP_ENTRY_SET_CACHED_AND_UNCACHED(4, 6, SOC_IRAM_LOW, NONE); + PMP_ENTRY_SET_CACHED_AND_UNCACHED(5, 7, SOC_IRAM_HIGH, PMP_TOR | RWX); + + _Static_assert(SOC_IRAM_LOW < SOC_IRAM_HIGH, "Invalid RAM region"); + } else { +#if CONFIG_ESP_SYSTEM_MEMPROT && CONFIG_ESP_SYSTEM_MEMPROT_PMP && !BOOTLOADER_BUILD + extern int _iram_text_end; + + PMP_ENTRY_SET_CACHED_AND_UNCACHED(4, 7, SOC_IRAM_LOW, NONE); + PMP_ENTRY_SET_CACHED_AND_UNCACHED(5, 8, (int)&_iram_text_end, PMP_TOR | RX); + PMP_ENTRY_SET_CACHED_AND_UNCACHED(6, 9, SOC_DRAM_HIGH, PMP_TOR | RW); +#else + PMP_ENTRY_SET_CACHED_AND_UNCACHED(4, 6, SOC_IRAM_LOW, CONDITIONAL_NONE); + PMP_ENTRY_SET_CACHED_AND_UNCACHED(5, 7, SOC_IRAM_HIGH, PMP_TOR | CONDITIONAL_RWX); + _Static_assert(SOC_IRAM_LOW < SOC_IRAM_HIGH, "Invalid RAM region"); +#endif + } + +#if CONFIG_ESP_SYSTEM_MEMPROT && CONFIG_ESP_SYSTEM_MEMPROT_PMP && !BOOTLOADER_BUILD + extern int _instruction_reserved_end; + extern int _rodata_reserved_end; + + const uint32_t page_aligned_irom_resv_end = ALIGN_UP_TO_MMU_PAGE_SIZE((uint32_t)(&_instruction_reserved_end)); + __attribute__((unused)) const uint32_t page_aligned_drom_resv_end = ALIGN_UP_TO_MMU_PAGE_SIZE((uint32_t)(&_rodata_reserved_end)); + + // 6. I_EXTRAM / D_EXTRAM (SPIRAM) +#if CONFIG_SPIRAM && CONFIG_SPIRAM_PRE_CONFIGURE_MEMORY_PROTECTION + + const size_t available_psram_heap = esp_psram_get_heap_size_to_protect(); + + PMP_ENTRY_SET_CACHED_AND_UNCACHED(10, 15, SOC_EXTRAM_LOW, NONE); + +#if CONFIG_SPIRAM_FETCH_INSTRUCTIONS && CONFIG_SPIRAM_RODATA + PMP_ENTRY_SET_CACHED_AND_UNCACHED(11, 16, (uint32_t)(&_instruction_reserved_end), PMP_TOR | RX); + PMP_ENTRY_SET_CACHED_AND_UNCACHED(12, 17, page_aligned_irom_resv_end, PMP_TOR | RW); + PMP_ENTRY_SET_CACHED_AND_UNCACHED(13, 18, (uint32_t)(&_rodata_reserved_end), PMP_TOR | R); + PMP_ENTRY_SET_CACHED_AND_UNCACHED(14, 19, ALIGN_UP((uint32_t)(&_rodata_reserved_end) + available_psram_heap, SOC_CPU_PMP_REGION_GRANULARITY), PMP_TOR | RW); + +#elif CONFIG_SPIRAM_FETCH_INSTRUCTIONS + PMP_ENTRY_SET_CACHED_AND_UNCACHED(11, 16, (uint32_t)(&_instruction_reserved_end), PMP_TOR | RX); + PMP_ENTRY_SET_CACHED_AND_UNCACHED(12, 17, page_aligned_irom_resv_end, PMP_TOR | RW); + PMP_ENTRY_SET_CACHED_AND_UNCACHED(13, 18, ALIGN_UP(page_aligned_irom_resv_end + available_psram_heap, SOC_CPU_PMP_REGION_GRANULARITY), PMP_TOR | RW); + +#elif CONFIG_SPIRAM_RODATA + PMP_ENTRY_SET_CACHED_AND_UNCACHED(11, 16, (uint32_t)(&_rodata_reserved_end), PMP_TOR | R); + PMP_ENTRY_SET_CACHED_AND_UNCACHED(12, 17, ALIGN_UP((uint32_t)(&_rodata_reserved_end) + available_psram_heap, SOC_CPU_PMP_REGION_GRANULARITY), PMP_TOR | RW); + +#else + PMP_ENTRY_SET_CACHED_AND_UNCACHED(11, 16, ALIGN_UP(SOC_EXTRAM_LOW + available_psram_heap, SOC_CPU_PMP_REGION_GRANULARITY), PMP_TOR | RW); +#endif +#endif /* CONFIG_SPIRAM && CONFIG_SPIRAM_PRE_CONFIGURE_MEMORY_PROTECTION */ + + // ESP32-P4 V3's 24th PMP entry cannot be used as a TOR entry // DIG-752 + // 7. I_Cache / D_Cache (flash) + PMP_ENTRY_SET_CACHED_AND_UNCACHED(20, 24, SOC_IROM_LOW, NONE); + PMP_ENTRY_SET_CACHED_AND_UNCACHED(21, 25, page_aligned_irom_resv_end, PMP_TOR | RX); + PMP_ENTRY_SET_CACHED_AND_UNCACHED(22, 26, page_aligned_drom_resv_end, PMP_TOR | R); + +#else +#if CONFIG_SPIRAM + const uint32_t pmpaddr10 = PMPADDR_NAPOT(SOC_EXTRAM_LOW, SOC_EXTRAM_HIGH); + PMP_RESET_AND_ENTRY_SET(10, pmpaddr10, PMP_NAPOT | CONDITIONAL_RWX); + + const uint32_t pmpaddr11 = PMPADDR_NAPOT(CACHE_LL_L2MEM_NON_CACHE_ADDR(SOC_EXTRAM_LOW), CACHE_LL_L2MEM_NON_CACHE_ADDR(SOC_EXTRAM_HIGH)); + PMP_RESET_AND_ENTRY_SET(11, pmpaddr11, PMP_NAPOT | CONDITIONAL_RWX); + _Static_assert(SOC_EXTRAM_LOW < SOC_EXTRAM_HIGH, "Invalid I/D_EXTRAM region"); +#endif /* CONFIG_SPIRAM */ + + const uint32_t pmpaddr12 = PMPADDR_NAPOT(SOC_IROM_LOW, SOC_IROM_HIGH); + PMP_RESET_AND_ENTRY_SET(12, pmpaddr12, PMP_NAPOT | CONDITIONAL_RX); + + const uint32_t pmpaddr13 = PMPADDR_NAPOT(CACHE_LL_L2MEM_NON_CACHE_ADDR(SOC_IROM_LOW), CACHE_LL_L2MEM_NON_CACHE_ADDR(SOC_IROM_HIGH)); + PMP_RESET_AND_ENTRY_SET(13, pmpaddr13, PMP_NAPOT | CONDITIONAL_RX); + _Static_assert(SOC_IROM_LOW < SOC_IROM_HIGH, "Invalid I/D_Cache region"); +#endif + + // 8. Peripheral addresses + const uint32_t pmpaddr27 = PMPADDR_NAPOT(SOC_PERIPHERAL_LOW, SOC_PERIPHERAL_HIGH); + PMP_RESET_AND_ENTRY_SET(27, pmpaddr27, PMP_NAPOT | RW); + _Static_assert(SOC_PERIPHERAL_LOW < SOC_PERIPHERAL_HIGH, "Invalid peripheral region"); + + // 9. LP memory +#if CONFIG_ESP_SYSTEM_MEMPROT && CONFIG_ESP_SYSTEM_MEMPROT_PMP && !BOOTLOADER_BUILD + extern int _rtc_text_start; + extern int _rtc_text_end; + + // ESP32-P4 V3's 28th PMP entry cannot be used as a TOR entry // DIG-752 + PMP_RESET_AND_ENTRY_SET(28, SOC_RTC_IRAM_LOW, NONE); + // First part of LP mem is reserved for RTC reserved mem (shared between bootloader and app) + // as well as memory for ULP coprocessor +#if CONFIG_ESP_SYSTEM_MEMPROT_PMP_LP_CORE_RESERVE_MEM_EXEC + PMP_RESET_AND_ENTRY_SET(29, (int)&_rtc_text_start, PMP_TOR | RWX); +#else + PMP_RESET_AND_ENTRY_SET(29, (int)&_rtc_text_start, PMP_TOR | RW); +#endif + PMP_RESET_AND_ENTRY_SET(30, (int)&_rtc_text_end, PMP_TOR | RX); + PMP_RESET_AND_ENTRY_SET(31, SOC_RTC_IRAM_HIGH, PMP_TOR | RW); +#else + const uint32_t pmpaddr28 = PMPADDR_NAPOT(SOC_RTC_IRAM_LOW, SOC_RTC_IRAM_HIGH); + PMP_RESET_AND_ENTRY_SET(28, pmpaddr28, PMP_NAPOT | CONDITIONAL_RWX); + _Static_assert(SOC_RTC_IRAM_LOW < SOC_RTC_IRAM_HIGH, "Invalid RTC IRAM region"); +#endif +} +#else +static void esp_cpu_configure_region_protection_rev_less_than_v3(void) +{ + const unsigned NONE = PMP_L; + __attribute__((unused)) const unsigned R = PMP_L | PMP_R; + const unsigned RW = PMP_L | PMP_R | PMP_W; + const unsigned RX = PMP_L | PMP_R | PMP_X; + const unsigned RWX = PMP_L | PMP_R | PMP_W | PMP_X; // 1. CPU Subsystem region - contains debug mode code and interrupt config registers const uint32_t pmpaddr0 = PMPADDR_NAPOT(SOC_CPU_SUBSYSTEM_LOW, SOC_CPU_SUBSYSTEM_HIGH); @@ -262,3 +355,71 @@ void esp_cpu_configure_region_protection(void) PMP_ENTRY_SET(15, pmpaddr15, PMP_NAPOT | RW); _Static_assert(SOC_PERIPHERAL_LOW < SOC_PERIPHERAL_HIGH, "Invalid peripheral region"); } +#endif + +void esp_cpu_configure_region_protection(void) +{ + /* Notes on implementation: + * + * 1) Note: ESP32-P4 CPU support overlapping PMP regions, configuration is based on static priority + * feature (lowest numbered entry has highest priority). + * + * 2) ESP32-P4 supports 16 PMA regions so we use this feature to block the invalid address ranges. + * However the entries are not sufficient to block all reserved memory ranges and the excluded sections are: + * a. Region between LP ROM and LP SRAM + * b. Region between LP peripherals and External flash (direct access) + * c. Region between External flash (direct access) and External RAM (direct access) + * d. Region between External RAM (direct access) and HP ROM (direct access) + * e. Region between HP ROM (direct access) and HP L2MEM (direct access) + * + * 3) We use combination of NAPOT (Naturally Aligned Power Of Two) and TOR (top of range) + * entries to map all the valid address space, bottom to top. This leaves us with some extra PMP entries + * which can be used to provide more granular access + * + * 4) Entries are grouped in order with some static asserts to try and verify everything is + * correct. + * + * 5) For ESP32-P4's versions less than v3, no explicit permissions are specified in PMP (default all permissions) for following regions: + * limited entries: + * a. External RAM + * b. LP ROM, LP Peripherals, LP SRAM + * c. External flash, External RAM, HP ROM, HP L2MEM (direct access) + */ + + /* There are 4 configuration scenarios for SRAM + * + * 1. Bootloader build: + * - We cannot set the lock bit as we need to reconfigure it again for the application. + * We configure PMP to cover entire valid IRAM and DRAM range. + * + * 2. Application build with CONFIG_ESP_SYSTEM_MEMPROT enabled + * - We split the SRAM into IRAM and DRAM such that IRAM region cannot be written to + * and DRAM region cannot be executed. We use _iram_text_end and _data_start markers to set the boundaries. + * We also lock these entries so the R/W/X permissions are enforced even for machine mode + * + * 3. Application build with CONFIG_ESP_SYSTEM_MEMPROT disabled + * - The IRAM-DRAM split is not enabled so we just need to ensure that access to only valid address ranges are successful + * so for that we set PMP to cover entire valid IRAM and DRAM region. + * We also lock these entries so the R/W/X permissions are enforced even for machine mode + * + * 4. CPU is in OCD debug mode + * - The IRAM-DRAM split is not enabled so that OpenOCD can write and execute from IRAM. + * We set PMP to cover entire valid IRAM and DRAM region. + * We also lock these entries so the R/W/X permissions are enforced even for machine mode + */ + // + // Configure all the invalid address regions using PMA + // + esp_cpu_configure_invalid_regions(); + + // + // Configure all the valid address regions using PMP + // + +#if HAL_CONFIG(CHIP_SUPPORT_MIN_REV) >= 300 + esp_cpu_configure_region_protection_rev_v3(); +#else + esp_cpu_configure_region_protection_rev_less_than_v3(); +#endif + +} diff --git a/components/riscv/include/riscv/csr.h b/components/riscv/include/riscv/csr.h index 1eb2c3fe11b..cbb8773da14 100644 --- a/components/riscv/include/riscv/csr.h +++ b/components/riscv/include/riscv/csr.h @@ -126,19 +126,31 @@ extern "C" { */ #define PMP_ENTRY_SET(ENTRY, ADDR, CFG) do { \ RV_WRITE_CSR((CSR_PMPADDR0) + (ENTRY), (ADDR) >> (PMP_SHIFT)); \ - RV_SET_CSR((CSR_PMPCFG0) + (ENTRY)/4, ((CFG)&0xFF) << (ENTRY%4)*8); \ + PMP_ENTRY_CFG_SET(ENTRY, CFG); \ } while(0) /*Only set PMPCFG entries*/ -#define PMP_ENTRY_CFG_SET(ENTRY, CFG) do {\ - RV_SET_CSR((CSR_PMPCFG0) + (ENTRY)/4, ((CFG)&0xFF) << (ENTRY%4)*8); \ - } while(0) +#define PMP_ENTRY_CFG_SET(ENTRY, CFG) \ + RV_SET_CSR((CSR_PMPCFG0) + (ENTRY)/4, ((CFG)&0xFF) << (ENTRY%4)*8) /*Reset all permissions of a particular PMPCFG entry*/ #define PMP_ENTRY_CFG_RESET(ENTRY) do {\ + RV_WRITE_CSR((CSR_PMPADDR0) + (ENTRY), 0); \ RV_CLEAR_CSR((CSR_PMPCFG0) + (ENTRY)/4, (0xFF) << (ENTRY%4)*8); \ } while(0) +/*Read configuration of a particular PMPCFG entry*/ +#define PMP_ENTRY_CFG_READ(ENTRY) \ + ((RV_READ_CSR((CSR_PMPCFG0) + (ENTRY)/4) >> ((ENTRY%4)*8)) & 0xFF) + +#define PMP_ENTRY_ADDR_READ(ENTRY) \ + (RV_READ_CSR((CSR_PMPADDR0) + (ENTRY)) << (PMP_SHIFT)) + +#define PMP_RESET_AND_ENTRY_SET(ENTRY, ADDR, CFG) do {\ + PMP_ENTRY_CFG_RESET(ENTRY); \ + PMP_ENTRY_SET(ENTRY, ADDR, CFG); \ + } while(0) + /*Reset all permissions of a particular PMACFG entry*/ #define PMA_ENTRY_CFG_RESET(ENTRY) do {\ RV_WRITE_CSR((CSR_PMACFG0) + (ENTRY) , 0); \ diff --git a/tools/test_apps/system/panic/main/include/test_memprot.h b/tools/test_apps/system/panic/main/include/test_memprot.h index 5dde7fc83c5..e3e0dc40af6 100644 --- a/tools/test_apps/system/panic/main/include/test_memprot.h +++ b/tools/test_apps/system/panic/main/include/test_memprot.h @@ -1,10 +1,12 @@ /* - * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #pragma once +#include "sdkconfig.h" +#include "soc/soc_caps.h" #ifdef __cplusplus extern "C" { @@ -26,12 +28,25 @@ void test_iram_reg2_write_violation(void); void test_iram_reg3_write_violation(void); + +#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE +void test_non_cache_iram_reg1_write_violation(void); +void test_non_cache_iram_reg2_write_violation(void); +void test_non_cache_iram_reg3_write_violation(void); +void test_non_cache_iram_reg4_write_violation(void); +#endif + void test_iram_reg4_write_violation(void); void test_dram_reg1_execute_violation(void); void test_dram_reg2_execute_violation(void); +#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE +void test_non_cache_dram_reg1_execute_violation(void); +void test_non_cache_dram_reg2_execute_violation(void); +#endif + void test_rtc_fast_reg1_execute_violation(void); void test_rtc_fast_reg2_execute_violation(void); @@ -52,6 +67,15 @@ void test_drom_reg_write_violation(void); void test_drom_reg_execute_violation(void); +#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE +void test_non_cache_irom_reg_write_violation(void); +void test_non_cache_drom_reg_write_violation(void); +void test_non_cache_drom_reg_execute_violation(void); + +void test_non_cache_spiram_xip_irom_alignment_reg_execute_violation(void); +void test_non_cache_spiram_xip_drom_alignment_reg_execute_violation(void); +#endif + void test_invalid_memory_region_write_violation(void); void test_invalid_memory_region_execute_violation(void); 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 34e637483ce..8413507b16b 100644 --- a/tools/test_apps/system/panic/main/test_app_main.c +++ b/tools/test_apps/system/panic/main/test_app_main.c @@ -146,6 +146,13 @@ void app_main(void) HANDLE_TEST(test_name, test_iram_reg2_write_violation); HANDLE_TEST(test_name, test_iram_reg3_write_violation); +#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE + HANDLE_TEST(test_name, test_non_cache_iram_reg1_write_violation); + HANDLE_TEST(test_name, test_non_cache_iram_reg2_write_violation); + HANDLE_TEST(test_name, test_non_cache_iram_reg3_write_violation); + HANDLE_TEST(test_name, test_non_cache_iram_reg4_write_violation); +#endif + /* TODO: IDF-6820: ESP32-S2 -> Fix incorrect panic reason: Unhandled debug exception */ HANDLE_TEST(test_name, test_iram_reg4_write_violation); @@ -154,6 +161,11 @@ void app_main(void) HANDLE_TEST(test_name, test_dram_reg2_execute_violation); +#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE + HANDLE_TEST(test_name, test_non_cache_dram_reg1_execute_violation); + HANDLE_TEST(test_name, test_non_cache_dram_reg2_execute_violation); +#endif + #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); @@ -178,13 +190,26 @@ void app_main(void) HANDLE_TEST(test_name, test_irom_reg_write_violation); HANDLE_TEST(test_name, test_drom_reg_write_violation); HANDLE_TEST(test_name, test_drom_reg_execute_violation); + +#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE + HANDLE_TEST(test_name, test_non_cache_irom_reg_write_violation); + HANDLE_TEST(test_name, test_non_cache_drom_reg_write_violation); + HANDLE_TEST(test_name, test_non_cache_drom_reg_execute_violation); +#endif + #if CONFIG_SPIRAM_FETCH_INSTRUCTIONS && SOC_MMU_DI_VADDR_SHARED HANDLE_TEST(test_name, test_spiram_xip_irom_alignment_reg_execute_violation); +#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE + HANDLE_TEST(test_name, test_non_cache_spiram_xip_irom_alignment_reg_execute_violation); +#endif #endif #endif #if CONFIG_SPIRAM_RODATA && !CONFIG_IDF_TARGET_ESP32S2 HANDLE_TEST(test_name, test_spiram_xip_drom_alignment_reg_execute_violation); +#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE + HANDLE_TEST(test_name, test_non_cache_spiram_xip_drom_alignment_reg_execute_violation); +#endif #endif #ifdef CONFIG_SOC_CPU_HAS_PMA diff --git a/tools/test_apps/system/panic/main/test_memprot.c b/tools/test_apps/system/panic/main/test_memprot.c index 15cd3476266..209809127a2 100644 --- a/tools/test_apps/system/panic/main/test_memprot.c +++ b/tools/test_apps/system/panic/main/test_memprot.c @@ -18,6 +18,10 @@ #include "test_memprot.h" #include "sdkconfig.h" +#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE +#include "hal/cache_ll.h" +#endif + #define RND_VAL (0xA5A5A5A5) #define SPIN_ITER (16) @@ -112,6 +116,40 @@ void test_iram_reg4_write_violation(void) *test_addr = RND_VAL; } +#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE +/* IRAM: I/DCACHE boundary region */ +void test_non_cache_iram_reg1_write_violation(void) +{ + uint32_t *test_addr = (uint32_t *)(CACHE_LL_L2MEM_NON_CACHE_ADDR((uint32_t)(&_iram_start) - 0x04)); + printf("IRAM: Non-cacheable Write operation | Address: %p\n", test_addr); + *test_addr = RND_VAL; +} + +/* IRAM: Interrupt vector table region */ +void test_non_cache_iram_reg2_write_violation(void) +{ + uint32_t *test_addr = (uint32_t *)(CACHE_LL_L2MEM_NON_CACHE_ADDR((uint32_t)(&_iram_text_start) - 0x04)); + printf("IRAM: Non-cacheable Write operation | Address: %p\n", test_addr); + *test_addr = RND_VAL; +} + +/* IRAM: Text (and data) region */ +void test_non_cache_iram_reg3_write_violation(void) +{ + uint32_t *test_addr = (uint32_t *)(CACHE_LL_L2MEM_NON_CACHE_ADDR((uint32_t)(&_iram_text_end) - 0x04)); + printf("IRAM: Non-cacheable Write operation | Address: %p\n", test_addr); + *test_addr = RND_VAL; +} + +/* IRAM: Through the data bus */ +void test_non_cache_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; +} +#endif /* SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE */ + /* ---------------------------------------------------- DRAM Violation Checks ---------------------------------------------------- */ static void foo_d(void) @@ -144,6 +182,30 @@ void test_dram_reg2_execute_violation(void) run_function(test_addr); } +#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE +/* DRAM: Data region (DRAM_ATTR tagged) */ +void test_non_cache_dram_reg1_execute_violation(void) +{ + memcpy(&s_dram_buf, &foo_d, sizeof(s_dram_buf)); + void *test_addr = (void *)(CACHE_LL_L2MEM_NON_CACHE_ADDR(&s_dram_buf)); + printf("DRAM: Non-cacheable Execute operation | Address: %p\n", &s_dram_buf); + run_function(test_addr); +} + +/* DRAM: Heap region */ +void test_non_cache_dram_reg2_execute_violation(void) +{ + uint8_t *instr = calloc(1024, sizeof(uint8_t)); + assert(instr != NULL); + + printf("DRAM: Non-cacheable Execute operation | Address: %p\n", instr); + + memcpy(instr, &foo_d, 1024); + void *test_addr = (void *)(CACHE_LL_L2MEM_NON_CACHE_ADDR(instr)); + run_function(test_addr); +} +#endif /* SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE */ + /* ---------------------------------------------------- RTC Violation Checks ---------------------------------------------------- */ #if CONFIG_SOC_RTC_FAST_MEM_SUPPORTED @@ -251,6 +313,30 @@ void test_drom_reg_execute_violation(void) run_function(test_addr); } +#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE +void test_non_cache_irom_reg_write_violation(void) +{ + extern int _instruction_reserved_end; + uint32_t *test_addr = (uint32_t *)(CACHE_LL_L2MEM_NON_CACHE_ADDR((uint32_t)(&_instruction_reserved_end - 0x100))); + printf("Flash (IROM): Non-cacheable Write operation | Address: %p\n", test_addr); + *test_addr = RND_VAL; +} + +void test_non_cache_drom_reg_write_violation(void) +{ + uint32_t *test_addr = (uint32_t *)CACHE_LL_L2MEM_NON_CACHE_ADDR(((uint32_t)(foo_buf))); + printf("Flash (DROM): Non-cacheable Write operation | Address: %p\n", test_addr); + *test_addr = RND_VAL; +} + +void test_non_cache_drom_reg_execute_violation(void) +{ + void *test_addr = (void *)CACHE_LL_L2MEM_NON_CACHE_ADDR((void *)foo_buf); + printf("Flash (DROM): Non-cacheable Execute operation | Address: %p\n", test_addr); + run_function(test_addr); +} +#endif + // Check if the memory alignment gaps added to the heap are correctly configured #if CONFIG_SPIRAM_FETCH_INSTRUCTIONS && SOC_MMU_DI_VADDR_SHARED void test_spiram_xip_irom_alignment_reg_execute_violation(void) @@ -264,6 +350,20 @@ void test_spiram_xip_irom_alignment_reg_execute_violation(void) printf("SPIRAM (IROM): IROM alignment gap not added into heap\n"); } } + +#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE +void test_non_cache_spiram_xip_irom_alignment_reg_execute_violation(void) +{ + extern int _instruction_reserved_end; + if (ALIGN_UP_TO_MMU_PAGE_SIZE((uint32_t)(&_instruction_reserved_end)) - (uint32_t)(&_instruction_reserved_end) >= 4) { + void *test_addr = (void *)CACHE_LL_L2MEM_NON_CACHE_ADDR(&_instruction_reserved_end + 1); + printf("SPIRAM (IROM): Non-cacheable Execute operation | Address: %p\n", test_addr); + run_function(test_addr); + } else { + printf("SPIRAM (IROM): Non-cacheable IROM alignment gap not added into heap\n"); + } +} +#endif #endif /* CONFIG_SPIRAM_FETCH_INSTRUCTIONS && SOC_MMU_DI_VADDR_SHARED */ #endif @@ -279,6 +379,20 @@ void test_spiram_xip_drom_alignment_reg_execute_violation(void) printf("SPIRAM (DROM): DROM alignment gap not added into heap\n"); } } + +#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE +void test_non_cache_spiram_xip_drom_alignment_reg_execute_violation(void) +{ + extern int _rodata_reserved_end; + if (ALIGN_UP_TO_MMU_PAGE_SIZE((uint32_t)(&_rodata_reserved_end)) - (uint32_t)(&_rodata_reserved_end) >= 4) { + void *test_addr = (void *)CACHE_LL_L2MEM_NON_CACHE_ADDR(&_rodata_reserved_end + 0x4); + printf("SPIRAM (DROM): Non-cacheable Execute operation | Address: %p\n", test_addr); + run_function(test_addr); + } else { + printf("SPIRAM (DROM): Non-cacheable DROM alignment gap not added into heap\n"); + } +} +#endif #endif /* CONFIG_SPIRAM_RODATA && !CONFIG_IDF_TARGET_ESP32S2 */ #ifdef CONFIG_SOC_CPU_HAS_PMA diff --git a/tools/test_apps/system/panic/pytest_panic.py b/tools/test_apps/system/panic/pytest_panic.py index 1df2f495833..7cb7c3d5e50 100644 --- a/tools/test_apps/system/panic/pytest_panic.py +++ b/tools/test_apps/system/panic/pytest_panic.py @@ -681,6 +681,8 @@ CONFIGS_MEMPROT_IDRAM = list( ) ) +CONFIGS_MEMPROT_IDRAM_L2_MEM_NON_CACHE = list(zip(['memprot_esp32p4'], ['esp32p4'])) + CONFIGS_MEMPROT_DCACHE = list(zip(['memprot_esp32s2'], ['esp32s2'])) CONFIGS_MEMPROT_RTC_FAST_MEM = list( @@ -714,6 +716,8 @@ CONFIGS_MEMPROT_FLASH_IDROM = list( ) ) +CONFIGS_MEMPROT_FLASH_IDROM_L2_NON_CACHE = list(zip(['memprot_esp32p4'], ['esp32p4'])) + CONFIGS_MEMPROT_SPIRAM_XIP_IROM_ALIGNMENT_HEAP = list( zip( ['memprot_spiram_xip_esp32c5', 'memprot_spiram_xip_esp32c61', 'memprot_spiram_xip_esp32p4'], @@ -721,6 +725,14 @@ CONFIGS_MEMPROT_SPIRAM_XIP_IROM_ALIGNMENT_HEAP = list( ) ) +CONFIGS_MEMPROT_SPIRAM_XIP_IROM_ALIGNMENT_HEAP_L2_NON_CACHE = list( + zip( + ['memprot_spiram_xip_esp32p4'], + ['esp32p4'], + ) +) + + CONFIGS_MEMPROT_SPIRAM_XIP_DROM_ALIGNMENT_HEAP = list( zip( [ @@ -733,6 +745,13 @@ CONFIGS_MEMPROT_SPIRAM_XIP_DROM_ALIGNMENT_HEAP = list( ) ) +CONFIGS_MEMPROT_SPIRAM_XIP_DROM_ALIGNMENT_HEAP_L2_NON_CACHE = list( + zip( + ['memprot_spiram_xip_esp32p4'], + ['esp32p4'], + ) +) + CONFIGS_MEMPROT_INVALID_REGION_PROTECTION_USING_PMA = list( zip( [ @@ -769,10 +788,7 @@ def test_dcache_write_violation(dut: PanicTestDut, test_func_name: str) -> None: dut.expect_cpu_reset() -@pytest.mark.generic -@pytest.mark.temp_skip_ci(targets=['esp32h21'], reason='lack of runners') -@idf_parametrize('config, target', CONFIGS_MEMPROT_IDRAM, indirect=['config', 'target']) -def test_iram_reg1_write_violation(dut: PanicTestDut, test_func_name: str) -> None: +def iram_reg1_write_violation(dut: PanicTestDut, test_func_name: str) -> None: dut.run_test_func(test_func_name) if dut.target == 'esp32s2': @@ -790,10 +806,58 @@ def test_iram_reg1_write_violation(dut: PanicTestDut, test_func_name: str) -> No dut.expect_cpu_reset() +@pytest.mark.generic +@pytest.mark.temp_skip_ci(targets=['esp32h21'], reason='lack of runners') +@idf_parametrize('config, target', CONFIGS_MEMPROT_IDRAM, indirect=['config', 'target']) +def test_iram_reg1_write_violation(dut: PanicTestDut, test_func_name: str) -> None: + iram_reg1_write_violation(dut, test_func_name) + + +@pytest.mark.generic +@idf_parametrize('config, target', CONFIGS_MEMPROT_IDRAM_L2_MEM_NON_CACHE, indirect=['config', 'target']) +def test_non_cache_iram_reg1_write_violation(dut: PanicTestDut, test_func_name: str) -> None: + if dut.target == 'esp32p4' and not dut.app.sdkconfig.get('ESP32P4_SELECTS_REV_LESS_V3'): + iram_reg1_write_violation(dut, test_func_name) + + +def iram_reg_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() + else: + dut.expect_gme('Store access fault') + dut.expect_reg_dump(0) + dut.expect_stack_dump() + + dut.expect_cpu_reset() + + @pytest.mark.generic @pytest.mark.temp_skip_ci(targets=['esp32h21'], reason='lack of runners') @idf_parametrize('config, target', CONFIGS_MEMPROT_IDRAM, indirect=['config', 'target']) def test_iram_reg2_write_violation(dut: PanicTestDut, test_func_name: str) -> None: + iram_reg_write_violation(dut, test_func_name) + + +@pytest.mark.generic +@idf_parametrize('config, target', CONFIGS_MEMPROT_IDRAM_L2_MEM_NON_CACHE, indirect=['config', 'target']) +def test_non_cache_iram_reg2_write_violation(dut: PanicTestDut, test_func_name: str) -> None: + if dut.target == 'esp32p4' and not dut.app.sdkconfig.get('ESP32P4_SELECTS_REV_LESS_V3'): + iram_reg_write_violation(dut, test_func_name) + + +def iram_reg3_write_violation(dut: PanicTestDut, test_func_name: str) -> None: dut.run_test_func(test_func_name) if dut.target == 'esp32s2': @@ -820,6 +884,17 @@ def test_iram_reg2_write_violation(dut: PanicTestDut, test_func_name: str) -> No @pytest.mark.temp_skip_ci(targets=['esp32h21'], reason='lack of runners') @idf_parametrize('config, target', CONFIGS_MEMPROT_IDRAM, indirect=['config', 'target']) def test_iram_reg3_write_violation(dut: PanicTestDut, test_func_name: str) -> None: + iram_reg_write_violation(dut, test_func_name) + + +@pytest.mark.generic +@idf_parametrize('config, target', CONFIGS_MEMPROT_IDRAM_L2_MEM_NON_CACHE, indirect=['config', 'target']) +def test_non_cache_iram_reg3_write_violation(dut: PanicTestDut, test_func_name: str) -> None: + if dut.target == 'esp32p4' and not dut.app.sdkconfig.get('ESP32P4_SELECTS_REV_LESS_V3'): + iram_reg_write_violation(dut, test_func_name) + + +def iram_reg4_write_violation(dut: PanicTestDut, test_func_name: str) -> None: dut.run_test_func(test_func_name) if dut.target == 'esp32s2': @@ -848,36 +923,17 @@ def test_iram_reg3_write_violation(dut: PanicTestDut, test_func_name: str) -> No @pytest.mark.temp_skip_ci(targets=['esp32h21'], reason='lack of runners') @idf_parametrize('config, target', CONFIGS_MEMPROT_IDRAM, indirect=['config', 'target']) 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() - else: - dut.expect_gme('Store access fault') - dut.expect_reg_dump(0) - dut.expect_stack_dump() - - dut.expect_cpu_reset() + iram_reg_write_violation(dut, test_func_name) -# TODO: IDF-6820: ESP32-S2 -> Fix multiple panic reasons in different runs @pytest.mark.generic -@pytest.mark.xfail( - 'config.getvalue("target") == "esp32s2"', reason='Multiple panic reasons for the same test may surface', run=False -) -@pytest.mark.temp_skip_ci(targets=['esp32h21'], reason='lack of runners') -@idf_parametrize('config, target', CONFIGS_MEMPROT_IDRAM, indirect=['config', 'target']) -def test_dram_reg1_execute_violation(dut: PanicTestDut, test_func_name: str) -> None: +@idf_parametrize('config, target', CONFIGS_MEMPROT_IDRAM_L2_MEM_NON_CACHE, indirect=['config', 'target']) +def test_non_cache_iram_reg4_write_violation(dut: PanicTestDut, test_func_name: str) -> None: + if dut.target == 'esp32p4' and not dut.app.sdkconfig.get('ESP32P4_SELECTS_REV_LESS_V3'): + iram_reg_write_violation(dut, test_func_name) + + +def dram_reg1_execute_violation(dut: PanicTestDut, test_func_name: str) -> None: dut.run_test_func(test_func_name) if dut.target == 'esp32s2': @@ -900,7 +956,18 @@ def test_dram_reg1_execute_violation(dut: PanicTestDut, test_func_name: str) -> ) @pytest.mark.temp_skip_ci(targets=['esp32h21'], reason='lack of runners') @idf_parametrize('config, target', CONFIGS_MEMPROT_IDRAM, indirect=['config', 'target']) -def test_dram_reg2_execute_violation(dut: PanicTestDut, test_func_name: str) -> None: +def test_dram_reg1_execute_violation(dut: PanicTestDut, test_func_name: str) -> None: + dram_reg1_execute_violation(dut, test_func_name) + + +@pytest.mark.generic +@idf_parametrize('config, target', CONFIGS_MEMPROT_IDRAM_L2_MEM_NON_CACHE, indirect=['config', 'target']) +def test_non_cache_dram_reg1_execute_violation(dut: PanicTestDut, test_func_name: str) -> None: + if dut.target == 'esp32p4' and not dut.app.sdkconfig.get('ESP32P4_SELECTS_REV_LESS_V3'): + dram_reg1_execute_violation(dut, test_func_name) + + +def dram_reg2_execute_violation(dut: PanicTestDut, test_func_name: str) -> None: dut.run_test_func(test_func_name) if dut.target == 'esp32s2': @@ -915,6 +982,24 @@ def test_dram_reg2_execute_violation(dut: PanicTestDut, test_func_name: str) -> dut.expect_cpu_reset() +# TODO: IDF-6820: ESP32-S2 -> Fix multiple panic reasons in different runs +@pytest.mark.generic +@pytest.mark.xfail( + 'config.getvalue("target") == "esp32s2"', reason='Multiple panic reasons for the same test may surface', run=False +) +@pytest.mark.temp_skip_ci(targets=['esp32h21'], reason='lack of runners') +@idf_parametrize('config, target', CONFIGS_MEMPROT_IDRAM, indirect=['config', 'target']) +def test_dram_reg2_execute_violation(dut: PanicTestDut, test_func_name: str) -> None: + dram_reg2_execute_violation(dut, test_func_name) + + +@pytest.mark.generic +@idf_parametrize('config, target', CONFIGS_MEMPROT_IDRAM_L2_MEM_NON_CACHE, indirect=['config', 'target']) +def test_non_cache_dram_reg2_execute_violation(dut: PanicTestDut, test_func_name: str) -> None: + if dut.target == 'esp32p4' and not dut.app.sdkconfig.get('ESP32P4_SELECTS_REV_LESS_V3'): + dram_reg2_execute_violation(dut, test_func_name) + + @pytest.mark.generic @pytest.mark.temp_skip_ci(targets=['esp32h21'], reason='lack of runners') @idf_parametrize('config, target', CONFIGS_MEMPROT_RTC_FAST_MEM, indirect=['config', 'target']) @@ -1000,10 +1085,28 @@ def test_rtc_slow_reg2_execute_violation(dut: PanicTestDut, test_func_name: str) dut.expect_cpu_reset() +def irom_reg_write_violation(dut: PanicTestDut, test_func_name: str) -> None: + dut.run_test_func(test_func_name) + dut.expect_gme('Store access fault') + dut.expect_reg_dump(0) + dut.expect_cpu_reset() + + @pytest.mark.generic @pytest.mark.temp_skip_ci(targets=['esp32h21'], reason='lack of runners') @idf_parametrize('config, target', CONFIGS_MEMPROT_FLASH_IDROM, indirect=['config', 'target']) def test_irom_reg_write_violation(dut: PanicTestDut, test_func_name: str) -> None: + irom_reg_write_violation(dut, test_func_name) + + +@pytest.mark.generic +@idf_parametrize('config, target', CONFIGS_MEMPROT_FLASH_IDROM_L2_NON_CACHE, indirect=['config', 'target']) +def test_non_cache_irom_reg_write_violation(dut: PanicTestDut, test_func_name: str) -> None: + if dut.target == 'esp32p4' and not dut.app.sdkconfig.get('ESP32P4_SELECTS_REV_LESS_V3'): + irom_reg_write_violation(dut, test_func_name) + + +def drom_reg_write_violation(dut: PanicTestDut, test_func_name: str) -> None: dut.run_test_func(test_func_name) dut.expect_gme('Store access fault') dut.expect_reg_dump(0) @@ -1014,8 +1117,19 @@ def test_irom_reg_write_violation(dut: PanicTestDut, test_func_name: str) -> Non @pytest.mark.temp_skip_ci(targets=['esp32h21'], reason='lack of runners') @idf_parametrize('config, target', CONFIGS_MEMPROT_FLASH_IDROM, indirect=['config', 'target']) def test_drom_reg_write_violation(dut: PanicTestDut, test_func_name: str) -> None: + drom_reg_write_violation(dut, test_func_name) + + +@pytest.mark.generic +@idf_parametrize('config, target', CONFIGS_MEMPROT_FLASH_IDROM_L2_NON_CACHE, indirect=['config', 'target']) +def test_non_cache_drom_reg_write_violation(dut: PanicTestDut, test_func_name: str) -> None: + if dut.target == 'esp32p4' and not dut.app.sdkconfig.get('ESP32P4_SELECTS_REV_LESS_V3'): + drom_reg_write_violation(dut, test_func_name) + + +def drom_reg_execute_violation(dut: PanicTestDut, test_func_name: str) -> None: dut.run_test_func(test_func_name) - dut.expect_gme('Store access fault') + dut.expect_gme('Instruction access fault') dut.expect_reg_dump(0) dut.expect_cpu_reset() @@ -1024,15 +1138,17 @@ def test_drom_reg_write_violation(dut: PanicTestDut, test_func_name: str) -> Non @pytest.mark.temp_skip_ci(targets=['esp32h21'], reason='lack of runners') @idf_parametrize('config, target', CONFIGS_MEMPROT_FLASH_IDROM, indirect=['config', 'target']) def test_drom_reg_execute_violation(dut: PanicTestDut, test_func_name: str) -> None: - dut.run_test_func(test_func_name) - dut.expect_gme('Instruction access fault') - dut.expect_reg_dump(0) - dut.expect_cpu_reset() + drom_reg_execute_violation(dut, test_func_name) @pytest.mark.generic -@idf_parametrize('config, target', CONFIGS_MEMPROT_SPIRAM_XIP_IROM_ALIGNMENT_HEAP, indirect=['config', 'target']) -def test_spiram_xip_irom_alignment_reg_execute_violation(dut: PanicTestDut, test_func_name: str) -> None: +@idf_parametrize('config, target', CONFIGS_MEMPROT_FLASH_IDROM_L2_NON_CACHE, indirect=['config', 'target']) +def test_non_cache_drom_reg_execute_violation(dut: PanicTestDut, test_func_name: str) -> None: + if dut.target == 'esp32p4' and not dut.app.sdkconfig.get('ESP32P4_SELECTS_REV_LESS_V3'): + drom_reg_execute_violation(dut, test_func_name) + + +def spiram_xip_irom_alignment_reg_execute_violation(dut: PanicTestDut, test_func_name: str) -> None: dut.run_test_func(test_func_name) try: dut.expect_gme('Instruction access fault') @@ -1043,8 +1159,21 @@ def test_spiram_xip_irom_alignment_reg_execute_violation(dut: PanicTestDut, test @pytest.mark.generic -@idf_parametrize('config, target', CONFIGS_MEMPROT_SPIRAM_XIP_DROM_ALIGNMENT_HEAP, indirect=['config', 'target']) -def test_spiram_xip_drom_alignment_reg_execute_violation(dut: PanicTestDut, test_func_name: str) -> None: +@idf_parametrize('config, target', CONFIGS_MEMPROT_SPIRAM_XIP_IROM_ALIGNMENT_HEAP, indirect=['config', 'target']) +def test_spiram_xip_irom_alignment_reg_execute_violation(dut: PanicTestDut, test_func_name: str) -> None: + spiram_xip_irom_alignment_reg_execute_violation(dut, test_func_name) + + +@pytest.mark.generic +@idf_parametrize( + 'config, target', CONFIGS_MEMPROT_SPIRAM_XIP_IROM_ALIGNMENT_HEAP_L2_NON_CACHE, indirect=['config', 'target'] +) +def test_non_cache_spiram_xip_irom_alignment_reg_execute_violation(dut: PanicTestDut, test_func_name: str) -> None: + if dut.target == 'esp32p4' and not dut.app.sdkconfig.get('ESP32P4_SELECTS_REV_LESS_V3'): + spiram_xip_irom_alignment_reg_execute_violation(dut, test_func_name) + + +def spiram_xip_drom_alignment_reg_execute_violation(dut: PanicTestDut, test_func_name: str) -> None: dut.run_test_func(test_func_name) try: if dut.target == 'esp32s3': @@ -1057,6 +1186,21 @@ def test_spiram_xip_drom_alignment_reg_execute_violation(dut: PanicTestDut, test dut.expect_cpu_reset() +@pytest.mark.generic +@idf_parametrize('config, target', CONFIGS_MEMPROT_SPIRAM_XIP_DROM_ALIGNMENT_HEAP, indirect=['config', 'target']) +def test_spiram_xip_drom_alignment_reg_execute_violation(dut: PanicTestDut, test_func_name: str) -> None: + spiram_xip_drom_alignment_reg_execute_violation(dut, test_func_name) + + +@pytest.mark.generic +@idf_parametrize( + 'config, target', CONFIGS_MEMPROT_SPIRAM_XIP_DROM_ALIGNMENT_HEAP_L2_NON_CACHE, indirect=['config', 'target'] +) +def test_non_cache_spiram_xip_drom_alignment_reg_execute_violation(dut: PanicTestDut, test_func_name: str) -> None: + if dut.target == 'esp32p4' and not dut.app.sdkconfig.get('ESP32P4_SELECTS_REV_LESS_V3'): + spiram_xip_drom_alignment_reg_execute_violation(dut, test_func_name) + + @pytest.mark.generic @pytest.mark.temp_skip_ci(targets=['esp32h21'], reason='lack of runners') @idf_parametrize('config, target', CONFIGS_MEMPROT_INVALID_REGION_PROTECTION_USING_PMA, indirect=['config', 'target']) 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 0e03d201ae8..db425883044 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 @@ -6,15 +6,11 @@ import re import subprocess import sys from typing import Any -from typing import Dict -from typing import List -from typing import Optional from typing import TextIO -from typing import Union import pexpect -from panic_utils import attach_logger from panic_utils import NoGdbProcessError +from panic_utils import attach_logger from panic_utils import quote_string from panic_utils import sha256 from panic_utils import verify_valid_gdb_subprocess @@ -32,7 +28,10 @@ class PanicTestDut(IdfDut): COREDUMP_UART_END = r'================= CORE DUMP END =================' COREDUMP_CHECKSUM = r"Coredump checksum='([a-fA-F0-9]+)'" REBOOT = r'.*Rebooting\.\.\.' - CPU_RESET = r'.*rst:.*(RTC_SW_CPU_RST|SW_CPU_RESET|SW_CPU|RTCWDT_RTC_RESET|LP_WDT_SYS|RTCWDT_RTC_RST|CHIP_LP_WDT_RESET|RTC_WDT_SYS)\b' + CPU_RESET = ( + r'.*rst:.*(RTC_SW_CPU_RST|SW_CPU_RESET|SW_CPU|RTCWDT_RTC_RESET|LP_WDT_SYS|' + r'RTCWDT_RTC_RST|CHIP_LP_WDT_RESET|RTC_WDT_SYS)\b' + ) app: IdfApp serial: IdfSerial @@ -40,14 +39,14 @@ class PanicTestDut(IdfDut): def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) - self.gdbmi: Optional[GdbController] = None + self.gdbmi: GdbController | None = None # record this since pygdbmi is using logging.debug to generate some single character mess self.log_level = logging.getLogger().level # pygdbmi is using logging.debug to generate some single character mess if self.log_level <= logging.DEBUG: logging.getLogger().setLevel(logging.INFO) - self.coredump_output: Optional[TextIO] = None + self.coredump_output: TextIO | None = None def close(self) -> None: if self.gdbmi: @@ -68,6 +67,8 @@ class PanicTestDut(IdfDut): return self.target in ['esp32', 'esp32s3', 'esp32p4'] def run_test_func(self, test_func_name: str) -> None: + if self.target == 'esp32p4' and not self.app.sdkconfig.get('ESP32P4_SELECTS_REV_LESS_V3'): + self.write('\n') self.expect_exact('Enter test name:') self.write(test_func_name) self.expect_exact('Got test name: ' + test_func_name) @@ -98,13 +99,13 @@ class PanicTestDut(IdfDut): """Expect method for Guru Meditation Errors""" self.expect_exact(f"Guru Meditation Error: Core 0 panic'ed ({reason})") - def expect_reg_dump(self, core: Optional[int] = None) -> None: + def expect_reg_dump(self, core: int | None = None) -> None: if core is None: # Match any core num self.expect(r'Core\s+\d+\s+register dump:') else: # Match the exact core num provided - self.expect(r'Core\s+%d\s+register dump:' % core) + self.expect(rf'Core\s+{core}\s+register dump:') def expect_cpu_reset(self) -> None: # no digital system reset for panic handling restarts (see IDF-7255) @@ -113,13 +114,11 @@ class PanicTestDut(IdfDut): def expect_elf_sha256(self, caption: str = 'ELF file SHA256: ') -> None: """Expect method for ELF SHA256 line""" elf_sha256 = sha256(self.app.elf_file) - elf_sha256_len = int( - self.app.sdkconfig.get('CONFIG_APP_RETRIEVE_LEN_ELF_SHA', '9') - ) + elf_sha256_len = int(self.app.sdkconfig.get('CONFIG_APP_RETRIEVE_LEN_ELF_SHA', '9')) self.expect_exact(caption + elf_sha256[0:elf_sha256_len]) - def expect_coredump(self, output_file_name: str, patterns: List[Union[str, re.Pattern]]) -> None: - with open(output_file_name, 'r') as file: + def expect_coredump(self, output_file_name: str, patterns: list[str | re.Pattern]) -> None: + with open(output_file_name) as file: coredump = file.read() for pattern in patterns: if isinstance(pattern, str): @@ -131,16 +130,12 @@ class PanicTestDut(IdfDut): else: raise ValueError(f'Unsupported input type: {type(pattern).__name__}') - def _call_espcoredump( - self, extra_args: List[str], output_file_name: str - ) -> None: + def _call_espcoredump(self, extra_args: list[str], output_file_name: str) -> None: # no "with" here, since we need the file to be open for later inspection by the test case if not self.coredump_output: self.coredump_output = open(output_file_name, 'w') - espcoredump_script = os.path.join( - os.environ['IDF_PATH'], 'components', 'espcoredump', 'espcoredump.py' - ) + espcoredump_script = os.path.join(os.environ['IDF_PATH'], 'components', 'espcoredump', 'espcoredump.py') espcoredump_args = [ sys.executable, espcoredump_script, @@ -157,14 +152,16 @@ class PanicTestDut(IdfDut): subprocess.check_call(espcoredump_args, stdout=self.coredump_output, stderr=self.coredump_output) except subprocess.CalledProcessError: self.coredump_output.flush() - with open(output_file_name, 'r') as file: + with open(output_file_name) as file: logging.error('espcoredump failed with output: %s', file.read()) raise finally: self.coredump_output.seek(0) def process_coredump_uart( - self, coredump_base64: Any, expected: Optional[List[Union[str, re.Pattern]]] = None, + self, + coredump_base64: Any, + expected: list[str | re.Pattern] | None = None, ) -> Any: with open(os.path.join(self.logdir, 'coredump_data.b64'), 'w') as coredump_file: logging.info('Writing UART base64 core dump to %s', coredump_file.name) @@ -179,13 +176,11 @@ class PanicTestDut(IdfDut): self.expect_coredump(output_file_name, expected) return coredump_elf_file - def process_coredump_flash(self, expected: Optional[List[Union[str, re.Pattern]]] = None) -> Any: + def process_coredump_flash(self, expected: list[str | re.Pattern] | None = None) -> Any: coredump_file_name = os.path.join(self.logdir, 'coredump_data.bin') logging.info('Writing flash binary core dump to %s', coredump_file_name) output_file_name = os.path.join(self.logdir, 'coredump_flash_result.txt') - self._call_espcoredump( - ['--core-format', 'raw', '--save-core', coredump_file_name], output_file_name - ) + self._call_espcoredump(['--core-format', 'raw', '--save-core', coredump_file_name], output_file_name) if expected: self.expect_coredump(output_file_name, expected) return coredump_file_name @@ -210,12 +205,14 @@ class PanicTestDut(IdfDut): gdb_path = 'riscv32-esp-elf-gdb' try: from pygdbmi.constants import GdbTimeoutError + gdb_command = [gdb_path] + gdb_args self.gdbmi = GdbController(command=gdb_command) pygdbmi_logger = attach_logger() except ImportError: # fallback for pygdbmi<0.10.0.0. from pygdbmi.gdbcontroller import GdbTimeoutError + self.gdbmi = GdbController(gdb_path=gdb_path, gdb_args=gdb_args) pygdbmi_logger = self.gdbmi.logger @@ -225,9 +222,7 @@ class PanicTestDut(IdfDut): while pygdbmi_logger.hasHandlers(): pygdbmi_logger.removeHandler(pygdbmi_logger.handlers[0]) log_handler = logging.FileHandler(pygdbmi_log_file_name) - log_handler.setFormatter( - logging.Formatter('%(asctime)s %(levelname)s: %(message)s') - ) + log_handler.setFormatter(logging.Formatter('%(asctime)s %(levelname)s: %(message)s')) logging.info(f'Saving pygdbmi logs to {pygdbmi_log_file_name}') pygdbmi_logger.addHandler(log_handler) try: @@ -251,27 +246,22 @@ class PanicTestDut(IdfDut): logging.info('GDB response: %s', resp) break # success except GdbTimeoutError: - logging.warning( - 'GDB internal error: cannot get response from the subprocess' - ) + logging.warning('GDB internal error: cannot get response from the subprocess') except NoGdbProcessError: logging.error('GDB internal error: process is not running') break # failure - TODO: create another GdbController except ValueError: - logging.error( - 'GDB internal error: select() returned an unexpected file number' - ) + logging.error('GDB internal error: select() returned an unexpected file number') # Set up logging for GDB remote protocol gdb_remotelog_file_name = os.path.join(self.logdir, 'gdb_remote_log.txt') self.gdb_write('-gdb-set remotelogfile ' + gdb_remotelog_file_name) # Load the ELF file - self.gdb_write('-file-exec-and-symbols {}'.format(self.app.elf_file)) + self.gdb_write(f'-file-exec-and-symbols {self.app.elf_file}') # Prepare gdb for the gdb stub def start_gdb_for_gdbstub(self) -> None: - self.run_gdb() # Connect GDB to UART @@ -280,8 +270,9 @@ class PanicTestDut(IdfDut): self.gdb_write('-gdb-set serial baud 115200') if sys.platform == 'darwin': - assert '/dev/tty.' not in self.serial.port, \ - '/dev/tty.* ports can\'t be used with GDB on macOS. Use with /dev/cu.* instead.' + assert '/dev/tty.' not in self.serial.port, ( + "/dev/tty.* ports can't be used with GDB on macOS. Use with /dev/cu.* instead." + ) # Make sure we get the 'stopped' notification responses = self.gdb_write('-target-select remote ' + self.serial.port) @@ -307,9 +298,8 @@ class PanicTestDut(IdfDut): # Prepare gdb to debug coredump file def start_gdb_for_coredump(self, elf_file: str) -> None: - self.run_gdb() - self.gdb_write('core {}'.format(elf_file)) + self.gdb_write(f'core {elf_file}') def gdb_backtrace(self) -> Any: """ @@ -322,13 +312,11 @@ class PanicTestDut(IdfDut): return self.find_gdb_response('done', 'result', responses)['payload']['stack'] def gdb_data_eval_expr(self, expr: str) -> Any: - responses = self.gdb_write('-data-evaluate-expression "%s"' % expr) + responses = self.gdb_write(f'-data-evaluate-expression "{expr}"') return self.find_gdb_response('done', 'result', responses)['payload']['value'] @staticmethod - def verify_gdb_backtrace( - gdb_backtrace: List[Any], expected_functions_list: List[Any] - ) -> None: + def verify_gdb_backtrace(gdb_backtrace: list[Any], expected_functions_list: list[Any]) -> None: """ Raises an assert if the function names listed in expected_functions_list do not match the backtrace given by gdb_backtrace argument. The latter is in the same format as returned by gdb_backtrace() @@ -341,15 +329,13 @@ class PanicTestDut(IdfDut): assert False, 'Got unexpected backtrace' @staticmethod - def find_gdb_response( - message: str, response_type: str, responses: List[Any] - ) -> Any: + def find_gdb_response(message: str, response_type: str, responses: list[Any]) -> Any: """ Helper function which extracts one response from an array of GDB responses, filtering by message and type. Returned message is a dictionary, refer to pygdbmi docs for the format. """ - def match_response(response: Dict[str, Any]) -> bool: + def match_response(response: dict[str, Any]) -> bool: return response['message'] == message and response['type'] == response_type # type: ignore filtered_responses = [r for r in responses if match_response(r)]