feat(cpu_region_protect): Extend PMP memprot for ESP32-P4 V3

This commit is contained in:
harshal.patil
2025-11-11 17:54:17 +05:30
parent 5d40b0d252
commit 3e7602a2e4
7 changed files with 618 additions and 152 deletions
@@ -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
}
+16 -4
View File
@@ -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); \
@@ -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);
@@ -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
@@ -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
+185 -41
View File
@@ -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'])
@@ -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)]