From 2ff7df235a0895a572f2c03fb7c91a590e51f3de Mon Sep 17 00:00:00 2001 From: Alexey Lapshin Date: Tue, 6 May 2025 16:02:08 +0700 Subject: [PATCH 1/5] fix(panic_test): avoid linker check on dangerous relocations xtensa linker throws warning dangerous relocation: windowed longcall crosses 1GB boundary; return may fail create separate function to call function by pointer to avoid warnings --- .../system/panic/main/test_memprot.c | 59 ++++++++++--------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/tools/test_apps/system/panic/main/test_memprot.c b/tools/test_apps/system/panic/main/test_memprot.c index 46116949cb7..47c3a1b4345 100644 --- a/tools/test_apps/system/panic/main/test_memprot.c +++ b/tools/test_apps/system/panic/main/test_memprot.c @@ -27,6 +27,11 @@ extern int _iram_text_end; #define ALIGN_UP_TO_MMU_PAGE_SIZE(addr) (((addr) + (SOC_MMU_PAGE_SIZE) - 1) & ~((SOC_MMU_PAGE_SIZE) - 1)) +__attribute__((noinline)) +static void run_function(void (*test_addr)(void)) { + test_addr(); +} + /* NOTE: Naming conventions for RTC_FAST_MEM are * different for ESP32-C3 and other RISC-V targets */ @@ -121,10 +126,9 @@ static DRAM_ATTR uint8_t s_dram_buf[1024]; 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; + void *test_addr = &s_dram_buf; printf("DRAM: Execute operation | Address: %p\n", &s_dram_buf); - func_ptr(); + run_function(test_addr); } /* DRAM: Heap region */ @@ -136,9 +140,8 @@ void test_dram_reg2_execute_violation(void) printf("DRAM: Execute operation | Address: %p\n", instr); memcpy(instr, &foo_d, 1024); - void (*func_ptr)(void); - func_ptr = (void(*)(void))instr; - func_ptr(); + void *test_addr = instr; + run_function(test_addr); } /* ---------------------------------------------------- RTC Violation Checks ---------------------------------------------------- */ @@ -156,28 +159,28 @@ static RTC_IRAM_ATTR void foo_f(void) 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); + void *test_addr = &_rtc_fast_start; #else - void (*test_addr)(void) = (void(*)(void))((uint32_t)&_rtc_text_start); + void *test_addr = &_rtc_text_start; #endif printf("RTC_MEM (Fast): Execute operation | Address: %p\n", test_addr); - test_addr(); + run_function(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); + void *test_addr = &_rtc_text_end - 1; printf("RTC_MEM (Fast): Execute operation | Address: %p\n", test_addr); - test_addr(); + run_function(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); + void *test_addr = &_rtc_force_fast_start + 1; printf("RTC_MEM (Fast): Execute operation | Address: %p\n", test_addr); - test_addr(); + run_function(test_addr); } #endif @@ -193,17 +196,17 @@ static RTC_SLOW_ATTR void foo_s(void) /* 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); + void *test_addr = &_rtc_force_slow_start; printf("RTC_MEM (Slow): Execute operation | Address: %p\n", test_addr); - test_addr(); + run_function(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); + void *test_addr = &_rtc_data_start; printf("RTC_MEM (Slow): Execute operation | Address: %p\n", test_addr); - test_addr(); + run_function(test_addr); } #endif @@ -243,10 +246,9 @@ void test_drom_reg_write_violation(void) void test_drom_reg_execute_violation(void) { - printf("Flash (DROM): Execute operation | Address: %p\n", foo_buf); - void (*func_ptr)(void); - func_ptr = (void(*)(void))foo_buf; - func_ptr(); + void *test_addr = (void *)foo_buf; + printf("Flash (DROM): Execute operation | Address: %p\n", test_addr); + run_function(test_addr); } // Check if the memory alignment gaps added to the heap are correctly configured @@ -255,9 +257,9 @@ void test_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) = (void(*)(void))((uint32_t)(&_instruction_reserved_end + 0x4)); + void *test_addr = &_instruction_reserved_end + 1; printf("SPIRAM (IROM): Execute operation | Address: %p\n", test_addr); - test_addr(); + run_function(test_addr); } else { printf("SPIRAM (IROM): IROM alignment gap not added into heap\n"); } @@ -270,9 +272,9 @@ void test_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) = (void(*)(void))((uint32_t)(&_rodata_reserved_end + 0x4)); + void *test_addr = &_rodata_reserved_end + 0x4; printf("SPIRAM (DROM): Execute operation | Address: %p\n", test_addr); - test_addr(); + run_function(test_addr); } else { printf("SPIRAM (DROM): DROM alignment gap not added into heap\n"); } @@ -290,9 +292,8 @@ void test_invalid_memory_region_write_violation(void) void test_invalid_memory_region_execute_violation(void) { - void (*func_ptr)(void); - func_ptr = (void(*)(void))(SOC_DRAM_HIGH + 0x40); - printf("Execute operation | Address: %p\n", func_ptr); - func_ptr(); + void *test_addr = (void *)(SOC_DRAM_HIGH + 0x40); + printf("Execute operation | Address: %p\n", test_addr); + run_function(test_addr); } #endif From 8121f66ec32b24c4dd0793baf184736b7f69e607 Mon Sep 17 00:00:00 2001 From: "harshal.patil" Date: Thu, 30 Apr 2026 11:09:37 +0530 Subject: [PATCH 2/5] feat(cpu_region_protect): Extend PMP memprot for ESP32-P4 V3 --- .../port/esp32p4/cpu_region_protect.c | 273 ++++++++++++++---- .../test_apps/tee_test_fw/sdkconfig.defaults | 2 +- components/riscv/include/riscv/csr.h | 20 +- .../custom_flash_driver/sdkconfig.ci.default | 2 +- .../system/panic/main/include/test_memprot.h | 26 +- .../system/panic/main/test_app_main.c | 27 +- .../system/panic/main/test_memprot.c | 116 +++++++- tools/test_apps/system/panic/pytest_panic.py | 227 ++++++++++++--- .../system/panic/test_panic_util/panic_dut.py | 2 + 9 files changed, 590 insertions(+), 105 deletions(-) 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 99ba73b924e..87310e548ae 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_PMP_IDRAM_SPLIT 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_PMP_IDRAM_SPLIT 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_PMP_IDRAM_SPLIT && !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_PMP_IDRAM_SPLIT && !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_PMP_IDRAM_SPLIT && !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_PMP_LP_CORE_RESERVE_MEM_EXECUTABLE + 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_PMP_IDRAM_SPLIT 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_PMP_IDRAM_SPLIT 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/esp_tee/test_apps/tee_test_fw/sdkconfig.defaults b/components/esp_tee/test_apps/tee_test_fw/sdkconfig.defaults index 193e004f851..badc06f5296 100644 --- a/components/esp_tee/test_apps/tee_test_fw/sdkconfig.defaults +++ b/components/esp_tee/test_apps/tee_test_fw/sdkconfig.defaults @@ -16,4 +16,4 @@ CONFIG_PARTITION_TABLE_OFFSET=0xF000 # Increasing TEE I/DRAM size CONFIG_SECURE_TEE_IRAM_SIZE=0x8800 -CONFIG_SECURE_TEE_DRAM_SIZE=0x4c00 +CONFIG_SECURE_TEE_DRAM_SIZE=0x5800 diff --git a/components/riscv/include/riscv/csr.h b/components/riscv/include/riscv/csr.h index 0e9d71b4be1..38b2d443b49 100644 --- a/components/riscv/include/riscv/csr.h +++ b/components/riscv/include/riscv/csr.h @@ -123,19 +123,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/examples/storage/custom_flash_driver/sdkconfig.ci.default b/examples/storage/custom_flash_driver/sdkconfig.ci.default index 47265c65637..f4b81b98e61 100644 --- a/examples/storage/custom_flash_driver/sdkconfig.ci.default +++ b/examples/storage/custom_flash_driver/sdkconfig.ci.default @@ -2,4 +2,4 @@ CONFIG_BOOTLOADER_LOG_LEVEL_DEBUG=y CONFIG_BOOTLOADER_LOG_LEVEL=4 CONFIG_ESPTOOLPY_FLASHMODE_QIO=y # Bootloader overlapped with partition table after we enabled LOGD for bootloader. Move the partition table a bit to fix the issue on CI. -CONFIG_PARTITION_TABLE_OFFSET=0x9000 +CONFIG_PARTITION_TABLE_OFFSET=0xA000 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..54a5871b36e 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-2026 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 880999cbfbc..02de44f53dd 100644 --- a/tools/test_apps/system/panic/main/test_app_main.c +++ b/tools/test_apps/system/panic/main/test_app_main.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -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 47c3a1b4345..d30ceccf2cc 100644 --- a/tools/test_apps/system/panic/main/test_memprot.c +++ b/tools/test_apps/system/panic/main/test_memprot.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -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 1b6e0294a14..c6c110bdb29 100644 --- a/tools/test_apps/system/panic/pytest_panic.py +++ b/tools/test_apps/system/panic/pytest_panic.py @@ -695,6 +695,8 @@ CONFIGS_MEMPROT_IDRAM = list( ) ) +CONFIGS_MEMPROT_IDRAM_L2_MEM_NON_CACHE = list(itertools.chain(itertools.product(['memprot_esp32p4'], ['esp32p4']))) + CONFIGS_MEMPROT_DCACHE = list(itertools.chain(itertools.product(['memprot_esp32s2'], ['esp32s2']))) CONFIGS_MEMPROT_RTC_FAST_MEM = list( @@ -725,6 +727,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( itertools.chain( zip( @@ -734,6 +738,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( itertools.chain( zip( @@ -748,6 +760,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( itertools.chain( zip( @@ -779,9 +798,7 @@ def test_dcache_write_violation(dut: PanicTestDut, test_func_name: str) -> None: dut.expect_cpu_reset() -@pytest.mark.generic -@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': @@ -801,7 +818,55 @@ def test_iram_reg1_write_violation(dut: PanicTestDut, test_func_name: str) -> No @pytest.mark.generic @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': @@ -827,6 +892,17 @@ def test_iram_reg2_write_violation(dut: PanicTestDut, test_func_name: str) -> No @pytest.mark.generic @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': @@ -854,35 +930,17 @@ def test_iram_reg3_write_violation(dut: PanicTestDut, test_func_name: str) -> No @pytest.mark.xfail('config.getvalue("target") == "esp32s2"', reason='Incorrect panic reason may be observed', run=False) @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 -) -@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': @@ -904,7 +962,18 @@ def test_dram_reg1_execute_violation(dut: PanicTestDut, test_func_name: str) -> 'config.getvalue("target") == "esp32s2"', reason='Multiple panic reasons for the same test may surface', run=False ) @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': @@ -919,6 +988,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 @idf_parametrize('config, target', CONFIGS_MEMPROT_RTC_FAST_MEM, indirect=['config', 'target']) def test_rtc_fast_reg1_execute_violation(dut: PanicTestDut, test_func_name: str) -> None: @@ -1000,9 +1087,27 @@ 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 @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) @@ -1012,15 +1117,17 @@ def test_irom_reg_write_violation(dut: PanicTestDut, test_func_name: str) -> Non @pytest.mark.generic @idf_parametrize('config, target', CONFIGS_MEMPROT_FLASH_IDROM, indirect=['config', 'target']) def test_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) - dut.expect_cpu_reset() + drom_reg_write_violation(dut, test_func_name) @pytest.mark.generic -@idf_parametrize('config, target', CONFIGS_MEMPROT_FLASH_IDROM, indirect=['config', 'target']) -def test_drom_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_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('Instruction access fault') dut.expect_reg_dump(0) @@ -1028,8 +1135,19 @@ def test_drom_reg_execute_violation(dut: PanicTestDut, test_func_name: str) -> N @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, indirect=['config', 'target']) +def test_drom_reg_execute_violation(dut: PanicTestDut, test_func_name: str) -> None: + drom_reg_execute_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_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') @@ -1040,8 +1158,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': @@ -1055,6 +1186,22 @@ def test_spiram_xip_drom_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: + 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']) def test_invalid_memory_region_write_violation(dut: PanicTestDut, test_func_name: str) -> None: dut.run_test_func(test_func_name) 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 04c8e0dd466..0a26c66b3df 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 @@ -72,6 +72,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) From fe83bf0011811d952f8e7824a5548387360c37ef Mon Sep 17 00:00:00 2001 From: "harshal.patil" Date: Tue, 28 Apr 2026 18:12:11 +0530 Subject: [PATCH 3/5] fix(esp_hw_support): reset stale PMP gap entries on P4 v3 before app memprot setup --- .../esp_hw_support/port/esp32p4/cpu_region_protect.c | 11 +++++++++++ 1 file changed, 11 insertions(+) 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 87310e548ae..ab7cebf2a02 100644 --- a/components/esp_hw_support/port/esp32p4/cpu_region_protect.c +++ b/components/esp_hw_support/port/esp32p4/cpu_region_protect.c @@ -153,6 +153,17 @@ static void esp_cpu_configure_region_protection_rev_v3(void) 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)); + PMP_ENTRY_CFG_RESET(11); + PMP_ENTRY_CFG_RESET(12); + PMP_ENTRY_CFG_RESET(13); + PMP_ENTRY_CFG_RESET(14); + PMP_ENTRY_CFG_RESET(15); + PMP_ENTRY_CFG_RESET(16); + PMP_ENTRY_CFG_RESET(17); + PMP_ENTRY_CFG_RESET(18); + PMP_ENTRY_CFG_RESET(19); + PMP_ENTRY_CFG_RESET(23); + // 6. I_EXTRAM / D_EXTRAM (SPIRAM) #if CONFIG_SPIRAM && CONFIG_SPIRAM_PRE_CONFIGURE_MEMORY_PROTECTION From 55657f5f22591fe4abe63060cbbbfda22e22aae5 Mon Sep 17 00:00:00 2001 From: "harshal.patil" Date: Fri, 27 Mar 2026 20:30:10 +0530 Subject: [PATCH 4/5] fix(cpu_region_protect): Fix incorrect definition of ALIGN_UP macro --- components/esp_hw_support/port/esp32c5/cpu_region_protect.c | 2 +- components/esp_hw_support/port/esp32c61/cpu_region_protect.c | 2 +- components/esp_hw_support/port/esp32p4/cpu_region_protect.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/components/esp_hw_support/port/esp32c5/cpu_region_protect.c b/components/esp_hw_support/port/esp32c5/cpu_region_protect.c index 5d2ccd14bff..d4e7dbe5b7f 100644 --- a/components/esp_hw_support/port/esp32c5/cpu_region_protect.c +++ b/components/esp_hw_support/port/esp32c5/cpu_region_protect.c @@ -29,7 +29,7 @@ #define ALIGN_UP_TO_MMU_PAGE_SIZE(addr) (((addr) + (SOC_MMU_PAGE_SIZE) - 1) & ~((SOC_MMU_PAGE_SIZE) - 1)) #define ALIGN_DOWN_TO_MMU_PAGE_SIZE(addr) ((addr) & ~((SOC_MMU_PAGE_SIZE) - 1)) -#define ALIGN_UP(addr, align) ((addr) & ~((align) - 1)) +#define ALIGN_UP(addr, align) (((addr) + (align) - 1) & ~((align) - 1)) static void esp_cpu_configure_invalid_regions(void) { diff --git a/components/esp_hw_support/port/esp32c61/cpu_region_protect.c b/components/esp_hw_support/port/esp32c61/cpu_region_protect.c index 080a56a24fb..0369f214071 100644 --- a/components/esp_hw_support/port/esp32c61/cpu_region_protect.c +++ b/components/esp_hw_support/port/esp32c61/cpu_region_protect.c @@ -30,7 +30,7 @@ #define ALIGN_UP_TO_MMU_PAGE_SIZE(addr) (((addr) + (SOC_MMU_PAGE_SIZE) - 1) & ~((SOC_MMU_PAGE_SIZE) - 1)) #define ALIGN_DOWN_TO_MMU_PAGE_SIZE(addr) ((addr) & ~((SOC_MMU_PAGE_SIZE) - 1)) -#define ALIGN_UP(addr, align) ((addr) & ~((align) - 1)) +#define ALIGN_UP(addr, align) (((addr) + (align) - 1) & ~((align) - 1)) static void esp_cpu_configure_invalid_regions(void) { 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 ab7cebf2a02..e2cbbb4b008 100644 --- a/components/esp_hw_support/port/esp32p4/cpu_region_protect.c +++ b/components/esp_hw_support/port/esp32p4/cpu_region_protect.c @@ -36,7 +36,7 @@ #define ALIGN_UP_TO_MMU_PAGE_SIZE(addr) (((addr) + (SOC_MMU_PAGE_SIZE) - 1) & ~((SOC_MMU_PAGE_SIZE) - 1)) #define ALIGN_DOWN_TO_MMU_PAGE_SIZE(addr) ((addr) & ~((SOC_MMU_PAGE_SIZE) - 1)) -#define ALIGN_UP(addr, align) ((addr) & ~((align) - 1)) +#define ALIGN_UP(addr, align) (((addr) + (align) - 1) & ~((align) - 1)) static void esp_cpu_configure_invalid_regions(void) { From 1df6b857ac46367b8f4dad5eea1cd70831e02123 Mon Sep 17 00:00:00 2001 From: Laukik Hase Date: Fri, 6 Mar 2026 15:44:34 +0530 Subject: [PATCH 5/5] fix(esp_hw_support): Add PMP entry for LP peripherals region on P4 v3+ --- .../esp_hw_support/port/esp32p4/cpu_region_protect.c | 8 ++++++-- .../esp_tee/test_apps/tee_test_fw/sdkconfig.defaults | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) 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 e2cbbb4b008..319349fde09 100644 --- a/components/esp_hw_support/port/esp32p4/cpu_region_protect.c +++ b/components/esp_hw_support/port/esp32p4/cpu_region_protect.c @@ -220,7 +220,7 @@ static void esp_cpu_configure_region_protection_rev_v3(void) PMP_RESET_AND_ENTRY_SET(27, pmpaddr27, PMP_NAPOT | RW); _Static_assert(SOC_PERIPHERAL_LOW < SOC_PERIPHERAL_HIGH, "Invalid peripheral region"); - // 9. LP memory + // 9. LP memory and LP peripherals #if CONFIG_ESP_SYSTEM_PMP_IDRAM_SPLIT && !BOOTLOADER_BUILD extern int _rtc_text_start; extern int _rtc_text_end; @@ -235,11 +235,15 @@ static void esp_cpu_configure_region_protection_rev_v3(void) 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); + // LP peripherals are contiguous with LP memory; this entry covers both with R/W + PMP_RESET_AND_ENTRY_SET(31, SOC_LP_PERIPH_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"); + + PMP_RESET_AND_ENTRY_SET(29, SOC_LP_PERIPH_LOW, NONE); + PMP_RESET_AND_ENTRY_SET(30, SOC_LP_PERIPH_HIGH, PMP_TOR | CONDITIONAL_RW); #endif } #else diff --git a/components/esp_tee/test_apps/tee_test_fw/sdkconfig.defaults b/components/esp_tee/test_apps/tee_test_fw/sdkconfig.defaults index badc06f5296..37effd8eb5c 100644 --- a/components/esp_tee/test_apps/tee_test_fw/sdkconfig.defaults +++ b/components/esp_tee/test_apps/tee_test_fw/sdkconfig.defaults @@ -16,4 +16,4 @@ CONFIG_PARTITION_TABLE_OFFSET=0xF000 # Increasing TEE I/DRAM size CONFIG_SECURE_TEE_IRAM_SIZE=0x8800 -CONFIG_SECURE_TEE_DRAM_SIZE=0x5800 +CONFIG_SECURE_TEE_DRAM_SIZE=0x5000