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" #include "esp_private/esp_psram_extram.h"
#endif /* CONFIG_SPIRAM */ #endif /* CONFIG_SPIRAM */
#include "soc/chip_revision.h"
#include "hal/config.h"
#ifdef BOOTLOADER_BUILD #ifdef BOOTLOADER_BUILD
// Without L bit set // Without L bit set
#define CONDITIONAL_NONE 0x0 #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); PMA_RESET_AND_ENTRY_SET_TOR(15, UINT32_MAX, PMA_TOR | PMA_NONE);
} }
void esp_cpu_configure_region_protection(void) #if HAL_CONFIG(CHIP_SUPPORT_MIN_REV) >= 300
{ // Helper macro to set both cached and non-cached PMP entries with the same permissions
/* Notes on implementation: #define PMP_ENTRY_SET_CACHED_AND_UNCACHED(cached_entry, non_cached_entry, addr, perm) \
* do { \
* 1) Note: ESP32-P4 CPU support overlapping PMP regions, configuration is based on static priority PMP_RESET_AND_ENTRY_SET(cached_entry, addr, perm); \
* feature (lowest numbered entry has highest priority). PMP_RESET_AND_ENTRY_SET(non_cached_entry, CACHE_LL_L2MEM_NON_CACHE_ADDR(addr), perm); \
* } while(0)
* 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)
*/
/* There are 4 configuration scenarios for SRAM static void esp_cpu_configure_region_protection_rev_v3(void)
* {
* 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
*/
const unsigned NONE = PMP_L; const unsigned NONE = PMP_L;
__attribute__((unused)) const unsigned R = PMP_L | PMP_R; __attribute__((unused)) const unsigned R = PMP_L | PMP_R;
const unsigned RW = PMP_L | PMP_R | PMP_W; const unsigned RW = PMP_L | PMP_R | PMP_W;
const unsigned RX = PMP_L | PMP_R | PMP_X; const unsigned RX = PMP_L | PMP_R | PMP_X;
const unsigned RWX = PMP_L | PMP_R | PMP_W | 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
// Configure all the invalid address regions using PMA const uint32_t pmpaddr0 = PMPADDR_NAPOT(SOC_CPU_SUBSYSTEM_LOW, SOC_CPU_SUBSYSTEM_HIGH);
// PMP_RESET_AND_ENTRY_SET(0, pmpaddr0, PMP_NAPOT | RW);
esp_cpu_configure_invalid_regions(); _Static_assert(SOC_CPU_SUBSYSTEM_LOW < SOC_CPU_SUBSYSTEM_HIGH, "Invalid CPU subsystem region");
// // 2. HP-CPU TCM
// Configure all the valid address regions using PMP // 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 // 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); 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); PMP_ENTRY_SET(15, pmpaddr15, PMP_NAPOT | RW);
_Static_assert(SOC_PERIPHERAL_LOW < SOC_PERIPHERAL_HIGH, "Invalid peripheral region"); _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 { \ #define PMP_ENTRY_SET(ENTRY, ADDR, CFG) do { \
RV_WRITE_CSR((CSR_PMPADDR0) + (ENTRY), (ADDR) >> (PMP_SHIFT)); \ 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) } while(0)
/*Only set PMPCFG entries*/ /*Only set PMPCFG entries*/
#define PMP_ENTRY_CFG_SET(ENTRY, CFG) do {\ #define PMP_ENTRY_CFG_SET(ENTRY, CFG) \
RV_SET_CSR((CSR_PMPCFG0) + (ENTRY)/4, ((CFG)&0xFF) << (ENTRY%4)*8); \ RV_SET_CSR((CSR_PMPCFG0) + (ENTRY)/4, ((CFG)&0xFF) << (ENTRY%4)*8)
} while(0)
/*Reset all permissions of a particular PMPCFG entry*/ /*Reset all permissions of a particular PMPCFG entry*/
#define PMP_ENTRY_CFG_RESET(ENTRY) do {\ #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); \ RV_CLEAR_CSR((CSR_PMPCFG0) + (ENTRY)/4, (0xFF) << (ENTRY%4)*8); \
} while(0) } 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*/ /*Reset all permissions of a particular PMACFG entry*/
#define PMA_ENTRY_CFG_RESET(ENTRY) do {\ #define PMA_ENTRY_CFG_RESET(ENTRY) do {\
RV_WRITE_CSR((CSR_PMACFG0) + (ENTRY) , 0); \ 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 * SPDX-License-Identifier: Apache-2.0
*/ */
#pragma once #pragma once
#include "sdkconfig.h"
#include "soc/soc_caps.h"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
@@ -26,12 +28,25 @@ void test_iram_reg2_write_violation(void);
void test_iram_reg3_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_iram_reg4_write_violation(void);
void test_dram_reg1_execute_violation(void); void test_dram_reg1_execute_violation(void);
void test_dram_reg2_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_reg1_execute_violation(void);
void test_rtc_fast_reg2_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); 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_write_violation(void);
void test_invalid_memory_region_execute_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_reg2_write_violation);
HANDLE_TEST(test_name, test_iram_reg3_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 */ /* TODO: IDF-6820: ESP32-S2 -> Fix incorrect panic reason: Unhandled debug exception */
HANDLE_TEST(test_name, test_iram_reg4_write_violation); 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); 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 #if CONFIG_SOC_RTC_FAST_MEM_SUPPORTED
HANDLE_TEST(test_name, test_rtc_fast_reg1_execute_violation); HANDLE_TEST(test_name, test_rtc_fast_reg1_execute_violation);
HANDLE_TEST(test_name, test_rtc_fast_reg2_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_irom_reg_write_violation);
HANDLE_TEST(test_name, test_drom_reg_write_violation); HANDLE_TEST(test_name, test_drom_reg_write_violation);
HANDLE_TEST(test_name, test_drom_reg_execute_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 #if CONFIG_SPIRAM_FETCH_INSTRUCTIONS && SOC_MMU_DI_VADDR_SHARED
HANDLE_TEST(test_name, test_spiram_xip_irom_alignment_reg_execute_violation); 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
#endif #endif
#if CONFIG_SPIRAM_RODATA && !CONFIG_IDF_TARGET_ESP32S2 #if CONFIG_SPIRAM_RODATA && !CONFIG_IDF_TARGET_ESP32S2
HANDLE_TEST(test_name, test_spiram_xip_drom_alignment_reg_execute_violation); 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 #endif
#ifdef CONFIG_SOC_CPU_HAS_PMA #ifdef CONFIG_SOC_CPU_HAS_PMA
@@ -18,6 +18,10 @@
#include "test_memprot.h" #include "test_memprot.h"
#include "sdkconfig.h" #include "sdkconfig.h"
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
#include "hal/cache_ll.h"
#endif
#define RND_VAL (0xA5A5A5A5) #define RND_VAL (0xA5A5A5A5)
#define SPIN_ITER (16) #define SPIN_ITER (16)
@@ -112,6 +116,40 @@ void test_iram_reg4_write_violation(void)
*test_addr = RND_VAL; *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 ---------------------------------------------------- */ /* ---------------------------------------------------- DRAM Violation Checks ---------------------------------------------------- */
static void foo_d(void) static void foo_d(void)
@@ -144,6 +182,30 @@ void test_dram_reg2_execute_violation(void)
run_function(test_addr); 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 ---------------------------------------------------- */ /* ---------------------------------------------------- RTC Violation Checks ---------------------------------------------------- */
#if CONFIG_SOC_RTC_FAST_MEM_SUPPORTED #if CONFIG_SOC_RTC_FAST_MEM_SUPPORTED
@@ -251,6 +313,30 @@ void test_drom_reg_execute_violation(void)
run_function(test_addr); 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 // Check if the memory alignment gaps added to the heap are correctly configured
#if CONFIG_SPIRAM_FETCH_INSTRUCTIONS && SOC_MMU_DI_VADDR_SHARED #if CONFIG_SPIRAM_FETCH_INSTRUCTIONS && SOC_MMU_DI_VADDR_SHARED
void test_spiram_xip_irom_alignment_reg_execute_violation(void) 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"); 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 /* CONFIG_SPIRAM_FETCH_INSTRUCTIONS && SOC_MMU_DI_VADDR_SHARED */
#endif #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"); 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 */ #endif /* CONFIG_SPIRAM_RODATA && !CONFIG_IDF_TARGET_ESP32S2 */
#ifdef CONFIG_SOC_CPU_HAS_PMA #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_DCACHE = list(zip(['memprot_esp32s2'], ['esp32s2']))
CONFIGS_MEMPROT_RTC_FAST_MEM = list( 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( CONFIGS_MEMPROT_SPIRAM_XIP_IROM_ALIGNMENT_HEAP = list(
zip( zip(
['memprot_spiram_xip_esp32c5', 'memprot_spiram_xip_esp32c61', 'memprot_spiram_xip_esp32p4'], ['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( CONFIGS_MEMPROT_SPIRAM_XIP_DROM_ALIGNMENT_HEAP = list(
zip( 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( CONFIGS_MEMPROT_INVALID_REGION_PROTECTION_USING_PMA = list(
zip( zip(
[ [
@@ -769,10 +788,7 @@ def test_dcache_write_violation(dut: PanicTestDut, test_func_name: str) -> None:
dut.expect_cpu_reset() dut.expect_cpu_reset()
@pytest.mark.generic def iram_reg1_write_violation(dut: PanicTestDut, test_func_name: str) -> None:
@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:
dut.run_test_func(test_func_name) dut.run_test_func(test_func_name)
if dut.target == 'esp32s2': 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() 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.generic
@pytest.mark.temp_skip_ci(targets=['esp32h21'], reason='lack of runners') @pytest.mark.temp_skip_ci(targets=['esp32h21'], reason='lack of runners')
@idf_parametrize('config, target', CONFIGS_MEMPROT_IDRAM, indirect=['config', 'target']) @idf_parametrize('config, target', CONFIGS_MEMPROT_IDRAM, indirect=['config', 'target'])
def test_iram_reg2_write_violation(dut: PanicTestDut, test_func_name: str) -> None: 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) dut.run_test_func(test_func_name)
if dut.target == 'esp32s2': 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') @pytest.mark.temp_skip_ci(targets=['esp32h21'], reason='lack of runners')
@idf_parametrize('config, target', CONFIGS_MEMPROT_IDRAM, indirect=['config', 'target']) @idf_parametrize('config, target', CONFIGS_MEMPROT_IDRAM, indirect=['config', 'target'])
def test_iram_reg3_write_violation(dut: PanicTestDut, test_func_name: str) -> None: 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) dut.run_test_func(test_func_name)
if dut.target == 'esp32s2': 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') @pytest.mark.temp_skip_ci(targets=['esp32h21'], reason='lack of runners')
@idf_parametrize('config, target', CONFIGS_MEMPROT_IDRAM, indirect=['config', 'target']) @idf_parametrize('config, target', CONFIGS_MEMPROT_IDRAM, indirect=['config', 'target'])
def test_iram_reg4_write_violation(dut: PanicTestDut, test_func_name: str) -> None: def test_iram_reg4_write_violation(dut: PanicTestDut, test_func_name: str) -> None:
dut.run_test_func(test_func_name) iram_reg_write_violation(dut, 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()
# TODO: IDF-6820: ESP32-S2 -> Fix multiple panic reasons in different runs
@pytest.mark.generic @pytest.mark.generic
@pytest.mark.xfail( @idf_parametrize('config, target', CONFIGS_MEMPROT_IDRAM_L2_MEM_NON_CACHE, indirect=['config', 'target'])
'config.getvalue("target") == "esp32s2"', reason='Multiple panic reasons for the same test may surface', run=False 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'):
@pytest.mark.temp_skip_ci(targets=['esp32h21'], reason='lack of runners') iram_reg_write_violation(dut, test_func_name)
@idf_parametrize('config, target', CONFIGS_MEMPROT_IDRAM, indirect=['config', 'target'])
def test_dram_reg1_execute_violation(dut: PanicTestDut, test_func_name: str) -> None:
def dram_reg1_execute_violation(dut: PanicTestDut, test_func_name: str) -> None:
dut.run_test_func(test_func_name) dut.run_test_func(test_func_name)
if dut.target == 'esp32s2': 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') @pytest.mark.temp_skip_ci(targets=['esp32h21'], reason='lack of runners')
@idf_parametrize('config, target', CONFIGS_MEMPROT_IDRAM, indirect=['config', 'target']) @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) dut.run_test_func(test_func_name)
if dut.target == 'esp32s2': if dut.target == 'esp32s2':
@@ -915,6 +982,24 @@ def test_dram_reg2_execute_violation(dut: PanicTestDut, test_func_name: str) ->
dut.expect_cpu_reset() 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.generic
@pytest.mark.temp_skip_ci(targets=['esp32h21'], reason='lack of runners') @pytest.mark.temp_skip_ci(targets=['esp32h21'], reason='lack of runners')
@idf_parametrize('config, target', CONFIGS_MEMPROT_RTC_FAST_MEM, indirect=['config', 'target']) @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() 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.generic
@pytest.mark.temp_skip_ci(targets=['esp32h21'], reason='lack of runners') @pytest.mark.temp_skip_ci(targets=['esp32h21'], reason='lack of runners')
@idf_parametrize('config, target', CONFIGS_MEMPROT_FLASH_IDROM, indirect=['config', 'target']) @idf_parametrize('config, target', CONFIGS_MEMPROT_FLASH_IDROM, indirect=['config', 'target'])
def test_irom_reg_write_violation(dut: PanicTestDut, test_func_name: str) -> None: 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.run_test_func(test_func_name)
dut.expect_gme('Store access fault') dut.expect_gme('Store access fault')
dut.expect_reg_dump(0) 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') @pytest.mark.temp_skip_ci(targets=['esp32h21'], reason='lack of runners')
@idf_parametrize('config, target', CONFIGS_MEMPROT_FLASH_IDROM, indirect=['config', 'target']) @idf_parametrize('config, target', CONFIGS_MEMPROT_FLASH_IDROM, indirect=['config', 'target'])
def test_drom_reg_write_violation(dut: PanicTestDut, test_func_name: str) -> None: 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.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_reg_dump(0)
dut.expect_cpu_reset() 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') @pytest.mark.temp_skip_ci(targets=['esp32h21'], reason='lack of runners')
@idf_parametrize('config, target', CONFIGS_MEMPROT_FLASH_IDROM, indirect=['config', 'target']) @idf_parametrize('config, target', CONFIGS_MEMPROT_FLASH_IDROM, indirect=['config', 'target'])
def test_drom_reg_execute_violation(dut: PanicTestDut, test_func_name: str) -> None: def test_drom_reg_execute_violation(dut: PanicTestDut, test_func_name: str) -> None:
dut.run_test_func(test_func_name) drom_reg_execute_violation(dut, test_func_name)
dut.expect_gme('Instruction access fault')
dut.expect_reg_dump(0)
dut.expect_cpu_reset()
@pytest.mark.generic @pytest.mark.generic
@idf_parametrize('config, target', CONFIGS_MEMPROT_SPIRAM_XIP_IROM_ALIGNMENT_HEAP, indirect=['config', 'target']) @idf_parametrize('config, target', CONFIGS_MEMPROT_FLASH_IDROM_L2_NON_CACHE, indirect=['config', 'target'])
def test_spiram_xip_irom_alignment_reg_execute_violation(dut: PanicTestDut, test_func_name: str) -> None: 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) dut.run_test_func(test_func_name)
try: try:
dut.expect_gme('Instruction access fault') 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 @pytest.mark.generic
@idf_parametrize('config, target', CONFIGS_MEMPROT_SPIRAM_XIP_DROM_ALIGNMENT_HEAP, indirect=['config', 'target']) @idf_parametrize('config, target', CONFIGS_MEMPROT_SPIRAM_XIP_IROM_ALIGNMENT_HEAP, indirect=['config', 'target'])
def test_spiram_xip_drom_alignment_reg_execute_violation(dut: PanicTestDut, test_func_name: str) -> None: 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) dut.run_test_func(test_func_name)
try: try:
if dut.target == 'esp32s3': if dut.target == 'esp32s3':
@@ -1057,6 +1186,21 @@ def test_spiram_xip_drom_alignment_reg_execute_violation(dut: PanicTestDut, test
dut.expect_cpu_reset() 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.generic
@pytest.mark.temp_skip_ci(targets=['esp32h21'], reason='lack of runners') @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']) @idf_parametrize('config, target', CONFIGS_MEMPROT_INVALID_REGION_PROTECTION_USING_PMA, indirect=['config', 'target'])
@@ -6,15 +6,11 @@ import re
import subprocess import subprocess
import sys import sys
from typing import Any from typing import Any
from typing import Dict
from typing import List
from typing import Optional
from typing import TextIO from typing import TextIO
from typing import Union
import pexpect import pexpect
from panic_utils import attach_logger
from panic_utils import NoGdbProcessError from panic_utils import NoGdbProcessError
from panic_utils import attach_logger
from panic_utils import quote_string from panic_utils import quote_string
from panic_utils import sha256 from panic_utils import sha256
from panic_utils import verify_valid_gdb_subprocess from panic_utils import verify_valid_gdb_subprocess
@@ -32,7 +28,10 @@ class PanicTestDut(IdfDut):
COREDUMP_UART_END = r'================= CORE DUMP END =================' COREDUMP_UART_END = r'================= CORE DUMP END ================='
COREDUMP_CHECKSUM = r"Coredump checksum='([a-fA-F0-9]+)'" COREDUMP_CHECKSUM = r"Coredump checksum='([a-fA-F0-9]+)'"
REBOOT = r'.*Rebooting\.\.\.' 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 app: IdfApp
serial: IdfSerial serial: IdfSerial
@@ -40,14 +39,14 @@ class PanicTestDut(IdfDut):
def __init__(self, *args: Any, **kwargs: Any) -> None: def __init__(self, *args: Any, **kwargs: Any) -> None:
super().__init__(*args, **kwargs) 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 # record this since pygdbmi is using logging.debug to generate some single character mess
self.log_level = logging.getLogger().level self.log_level = logging.getLogger().level
# pygdbmi is using logging.debug to generate some single character mess # pygdbmi is using logging.debug to generate some single character mess
if self.log_level <= logging.DEBUG: if self.log_level <= logging.DEBUG:
logging.getLogger().setLevel(logging.INFO) logging.getLogger().setLevel(logging.INFO)
self.coredump_output: Optional[TextIO] = None self.coredump_output: TextIO | None = None
def close(self) -> None: def close(self) -> None:
if self.gdbmi: if self.gdbmi:
@@ -68,6 +67,8 @@ class PanicTestDut(IdfDut):
return self.target in ['esp32', 'esp32s3', 'esp32p4'] return self.target in ['esp32', 'esp32s3', 'esp32p4']
def run_test_func(self, test_func_name: str) -> None: 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.expect_exact('Enter test name:')
self.write(test_func_name) self.write(test_func_name)
self.expect_exact('Got test name: ' + 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""" """Expect method for Guru Meditation Errors"""
self.expect_exact(f"Guru Meditation Error: Core 0 panic'ed ({reason})") 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: if core is None:
# Match any core num # Match any core num
self.expect(r'Core\s+\d+\s+register dump:') self.expect(r'Core\s+\d+\s+register dump:')
else: else:
# Match the exact core num provided # 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: def expect_cpu_reset(self) -> None:
# no digital system reset for panic handling restarts (see IDF-7255) # 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: def expect_elf_sha256(self, caption: str = 'ELF file SHA256: ') -> None:
"""Expect method for ELF SHA256 line""" """Expect method for ELF SHA256 line"""
elf_sha256 = sha256(self.app.elf_file) elf_sha256 = sha256(self.app.elf_file)
elf_sha256_len = int( elf_sha256_len = int(self.app.sdkconfig.get('CONFIG_APP_RETRIEVE_LEN_ELF_SHA', '9'))
self.app.sdkconfig.get('CONFIG_APP_RETRIEVE_LEN_ELF_SHA', '9')
)
self.expect_exact(caption + elf_sha256[0:elf_sha256_len]) 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: def expect_coredump(self, output_file_name: str, patterns: list[str | re.Pattern]) -> None:
with open(output_file_name, 'r') as file: with open(output_file_name) as file:
coredump = file.read() coredump = file.read()
for pattern in patterns: for pattern in patterns:
if isinstance(pattern, str): if isinstance(pattern, str):
@@ -131,16 +130,12 @@ class PanicTestDut(IdfDut):
else: else:
raise ValueError(f'Unsupported input type: {type(pattern).__name__}') raise ValueError(f'Unsupported input type: {type(pattern).__name__}')
def _call_espcoredump( def _call_espcoredump(self, extra_args: list[str], output_file_name: str) -> None:
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 # no "with" here, since we need the file to be open for later inspection by the test case
if not self.coredump_output: if not self.coredump_output:
self.coredump_output = open(output_file_name, 'w') self.coredump_output = open(output_file_name, 'w')
espcoredump_script = os.path.join( espcoredump_script = os.path.join(os.environ['IDF_PATH'], 'components', 'espcoredump', 'espcoredump.py')
os.environ['IDF_PATH'], 'components', 'espcoredump', 'espcoredump.py'
)
espcoredump_args = [ espcoredump_args = [
sys.executable, sys.executable,
espcoredump_script, espcoredump_script,
@@ -157,14 +152,16 @@ class PanicTestDut(IdfDut):
subprocess.check_call(espcoredump_args, stdout=self.coredump_output, stderr=self.coredump_output) subprocess.check_call(espcoredump_args, stdout=self.coredump_output, stderr=self.coredump_output)
except subprocess.CalledProcessError: except subprocess.CalledProcessError:
self.coredump_output.flush() 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()) logging.error('espcoredump failed with output: %s', file.read())
raise raise
finally: finally:
self.coredump_output.seek(0) self.coredump_output.seek(0)
def process_coredump_uart( 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: ) -> Any:
with open(os.path.join(self.logdir, 'coredump_data.b64'), 'w') as coredump_file: 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) 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) self.expect_coredump(output_file_name, expected)
return coredump_elf_file 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') coredump_file_name = os.path.join(self.logdir, 'coredump_data.bin')
logging.info('Writing flash binary core dump to %s', coredump_file_name) logging.info('Writing flash binary core dump to %s', coredump_file_name)
output_file_name = os.path.join(self.logdir, 'coredump_flash_result.txt') output_file_name = os.path.join(self.logdir, 'coredump_flash_result.txt')
self._call_espcoredump( self._call_espcoredump(['--core-format', 'raw', '--save-core', coredump_file_name], output_file_name)
['--core-format', 'raw', '--save-core', coredump_file_name], output_file_name
)
if expected: if expected:
self.expect_coredump(output_file_name, expected) self.expect_coredump(output_file_name, expected)
return coredump_file_name return coredump_file_name
@@ -210,12 +205,14 @@ class PanicTestDut(IdfDut):
gdb_path = 'riscv32-esp-elf-gdb' gdb_path = 'riscv32-esp-elf-gdb'
try: try:
from pygdbmi.constants import GdbTimeoutError from pygdbmi.constants import GdbTimeoutError
gdb_command = [gdb_path] + gdb_args gdb_command = [gdb_path] + gdb_args
self.gdbmi = GdbController(command=gdb_command) self.gdbmi = GdbController(command=gdb_command)
pygdbmi_logger = attach_logger() pygdbmi_logger = attach_logger()
except ImportError: except ImportError:
# fallback for pygdbmi<0.10.0.0. # fallback for pygdbmi<0.10.0.0.
from pygdbmi.gdbcontroller import GdbTimeoutError from pygdbmi.gdbcontroller import GdbTimeoutError
self.gdbmi = GdbController(gdb_path=gdb_path, gdb_args=gdb_args) self.gdbmi = GdbController(gdb_path=gdb_path, gdb_args=gdb_args)
pygdbmi_logger = self.gdbmi.logger pygdbmi_logger = self.gdbmi.logger
@@ -225,9 +222,7 @@ class PanicTestDut(IdfDut):
while pygdbmi_logger.hasHandlers(): while pygdbmi_logger.hasHandlers():
pygdbmi_logger.removeHandler(pygdbmi_logger.handlers[0]) pygdbmi_logger.removeHandler(pygdbmi_logger.handlers[0])
log_handler = logging.FileHandler(pygdbmi_log_file_name) log_handler = logging.FileHandler(pygdbmi_log_file_name)
log_handler.setFormatter( log_handler.setFormatter(logging.Formatter('%(asctime)s %(levelname)s: %(message)s'))
logging.Formatter('%(asctime)s %(levelname)s: %(message)s')
)
logging.info(f'Saving pygdbmi logs to {pygdbmi_log_file_name}') logging.info(f'Saving pygdbmi logs to {pygdbmi_log_file_name}')
pygdbmi_logger.addHandler(log_handler) pygdbmi_logger.addHandler(log_handler)
try: try:
@@ -251,27 +246,22 @@ class PanicTestDut(IdfDut):
logging.info('GDB response: %s', resp) logging.info('GDB response: %s', resp)
break # success break # success
except GdbTimeoutError: except GdbTimeoutError:
logging.warning( logging.warning('GDB internal error: cannot get response from the subprocess')
'GDB internal error: cannot get response from the subprocess'
)
except NoGdbProcessError: except NoGdbProcessError:
logging.error('GDB internal error: process is not running') logging.error('GDB internal error: process is not running')
break # failure - TODO: create another GdbController break # failure - TODO: create another GdbController
except ValueError: except ValueError:
logging.error( logging.error('GDB internal error: select() returned an unexpected file number')
'GDB internal error: select() returned an unexpected file number'
)
# Set up logging for GDB remote protocol # Set up logging for GDB remote protocol
gdb_remotelog_file_name = os.path.join(self.logdir, 'gdb_remote_log.txt') gdb_remotelog_file_name = os.path.join(self.logdir, 'gdb_remote_log.txt')
self.gdb_write('-gdb-set remotelogfile ' + gdb_remotelog_file_name) self.gdb_write('-gdb-set remotelogfile ' + gdb_remotelog_file_name)
# Load the ELF file # 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 # Prepare gdb for the gdb stub
def start_gdb_for_gdbstub(self) -> None: def start_gdb_for_gdbstub(self) -> None:
self.run_gdb() self.run_gdb()
# Connect GDB to UART # Connect GDB to UART
@@ -280,8 +270,9 @@ class PanicTestDut(IdfDut):
self.gdb_write('-gdb-set serial baud 115200') self.gdb_write('-gdb-set serial baud 115200')
if sys.platform == 'darwin': if sys.platform == 'darwin':
assert '/dev/tty.' not in self.serial.port, \ assert '/dev/tty.' not in self.serial.port, (
'/dev/tty.* ports can\'t be used with GDB on macOS. Use with /dev/cu.* instead.' "/dev/tty.* ports can't be used with GDB on macOS. Use with /dev/cu.* instead."
)
# Make sure we get the 'stopped' notification # Make sure we get the 'stopped' notification
responses = self.gdb_write('-target-select remote ' + self.serial.port) responses = self.gdb_write('-target-select remote ' + self.serial.port)
@@ -307,9 +298,8 @@ class PanicTestDut(IdfDut):
# Prepare gdb to debug coredump file # Prepare gdb to debug coredump file
def start_gdb_for_coredump(self, elf_file: str) -> None: def start_gdb_for_coredump(self, elf_file: str) -> None:
self.run_gdb() self.run_gdb()
self.gdb_write('core {}'.format(elf_file)) self.gdb_write(f'core {elf_file}')
def gdb_backtrace(self) -> Any: def gdb_backtrace(self) -> Any:
""" """
@@ -322,13 +312,11 @@ class PanicTestDut(IdfDut):
return self.find_gdb_response('done', 'result', responses)['payload']['stack'] return self.find_gdb_response('done', 'result', responses)['payload']['stack']
def gdb_data_eval_expr(self, expr: str) -> Any: 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'] return self.find_gdb_response('done', 'result', responses)['payload']['value']
@staticmethod @staticmethod
def verify_gdb_backtrace( def verify_gdb_backtrace(gdb_backtrace: list[Any], expected_functions_list: list[Any]) -> None:
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 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() 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' assert False, 'Got unexpected backtrace'
@staticmethod @staticmethod
def find_gdb_response( def find_gdb_response(message: str, response_type: str, responses: list[Any]) -> Any:
message: str, response_type: str, responses: List[Any]
) -> Any:
""" """
Helper function which extracts one response from an array of GDB responses, filtering 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. 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 return response['message'] == message and response['type'] == response_type # type: ignore
filtered_responses = [r for r in responses if match_response(r)] filtered_responses = [r for r in responses if match_response(r)]