Merge branch 'feat/pma_pmp_lock_policy' into 'master'

Stop locking PMP in the bootloader and freeze the entry layout

See merge request espressif/esp-idf!50326
This commit is contained in:
Mahavir Jain
2026-08-06 11:31:39 +05:30
20 changed files with 901 additions and 681 deletions

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -14,19 +14,7 @@
#include "esp_private/esp_psram_extram.h"
#endif /* !BOOTLOADER_BUILD && CONFIG_SPIRAM */
#ifdef BOOTLOADER_BUILD
// Without L bit set
#define CONDITIONAL_NONE 0x0
#define CONDITIONAL_RX PMP_R | PMP_X
#define CONDITIONAL_RW PMP_R | PMP_W
#define CONDITIONAL_RWX PMP_R | PMP_W | PMP_X
#else
// With L bit set
#define CONDITIONAL_NONE NONE
#define CONDITIONAL_RX RX
#define CONDITIONAL_RW RW
#define CONDITIONAL_RWX RWX
#endif
#include "pmp_layout.h"
#define ALIGN_UP_TO_MMU_PAGE_SIZE(addr) ESP_ALIGN_UP(addr, SOC_MMU_PAGE_SIZE)
#define ALIGN_DOWN_TO_MMU_PAGE_SIZE(addr) ESP_ALIGN_DOWN(addr, SOC_MMU_PAGE_SIZE)
@@ -92,39 +80,22 @@ static void esp_cpu_configure_invalid_regions(void)
#endif
}
void esp_cpu_configure_region_protection(void)
#ifndef BOOTLOADER_BUILD
static void esp_cpu_configure_valid_regions(void)
{
/* Notes on implementation:
/* There are 3 configuration scenarios for SRAM in the application
*
* 1) Note: ESP32-C5 CPU support overlapping PMP regions
*
* 2) ESP32-C5 supports 16 PMA regions so we use this feature to block all the invalid address ranges
*
* 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.
*/
/* 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
* 1. 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 && CONFIG_ESP_SYSTEM_MEMPROT_PMP disabled
* 2. 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
* 3. 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
@@ -135,43 +106,33 @@ void esp_cpu_configure_region_protection(void)
__attribute__((unused)) const unsigned RX = PMP_L | PMP_R | PMP_X;
__attribute__((unused)) const unsigned RWX = PMP_L | PMP_R | PMP_W | PMP_X;
//
// Configure all the invalid address regions using PMA
//
esp_cpu_configure_invalid_regions();
/* NOTE: When ESP-TEE is active, only configure invalid memory regions in bootloader
* to prevent errors before TEE initialization. TEE will handle all other
* memory protection.
*/
#if CONFIG_SECURE_ENABLE_TEE && BOOTLOADER_BUILD
return;
#endif
//
// Configure all the valid address regions using PMP
//
// 1. CPU Subsystem region - contains interrupt config registers
const uint32_t pmpaddr0 = PMPADDR_NAPOT(SOC_CPU_SUBSYSTEM_LOW, SOC_CPU_SUBSYSTEM_HIGH);
PMP_ENTRY_SET(0, pmpaddr0, PMP_NAPOT | RWX);
PMP_RESET_AND_ENTRY_SET(PMP_ENTRY_CPU_SUBSYSTEM, pmpaddr0, PMP_NAPOT | RWX);
_Static_assert(SOC_CPU_SUBSYSTEM_LOW < SOC_CPU_SUBSYSTEM_HIGH, "Invalid CPU subsystem region");
// 2. I/D-ROM
#if CONFIG_ESP_SYSTEM_MEMPROT && CONFIG_ESP_SYSTEM_MEMPROT_PMP && !BOOTLOADER_BUILD
#if CONFIG_ESP_SYSTEM_MEMPROT && CONFIG_ESP_SYSTEM_MEMPROT_PMP
const uint32_t drom_start = (uint32_t) (ets_rom_layout_p->drom_start);
if ((drom_start & (SOC_CPU_PMP_REGION_GRANULARITY - 1)) == 0) {
// We can skip configuring the PMP entry for the [SOC_IROM_MASK_LOW - drom_start]
// region as RX, as we already have configured a PMA entry with RX permissions for the
// [SOC_IROM_MASK_LOW - SOC_DROM_MASK_HIGH] region that helps us to also configure
// the region as cacheable. Thus, we save on one PMP entry.
PMP_ENTRY_SET(1, drom_start, NONE);
PMP_ENTRY_SET(2, SOC_DROM_MASK_HIGH, PMP_TOR | R);
/* No cfg reset on the two ROM entries, deliberately: v6.0/v6.1
* bootloaders lock PMP_ENTRY_ROM_LOW at SOC_IROM_MASK_LOW, so on such
* devices the TOR region below spans the ROM text and must keep the X
* bit those bootloaders left in PMP_ENTRY_ROM_HIGH. PMP_ENTRY_SET only
* ORs cfg bits and cannot clear it; on newer bootloaders (no PMP
* writes) the entries are clean and ROM data gets the intended R.
*/
PMP_ENTRY_SET(PMP_ENTRY_ROM_LOW, drom_start, NONE);
PMP_ENTRY_SET(PMP_ENTRY_ROM_HIGH, SOC_DROM_MASK_HIGH, PMP_TOR | R);
} else
#endif
{
PMP_ENTRY_SET(1, SOC_IROM_MASK_LOW, NONE);
PMP_ENTRY_SET(2, SOC_IROM_MASK_HIGH, PMP_TOR | CONDITIONAL_RX);
PMP_RESET_AND_ENTRY_SET(PMP_ENTRY_ROM_LOW, SOC_IROM_MASK_LOW, NONE);
PMP_RESET_AND_ENTRY_SET(PMP_ENTRY_ROM_HIGH, SOC_IROM_MASK_HIGH, PMP_TOR | RX);
_Static_assert(SOC_IROM_MASK_LOW < SOC_IROM_MASK_HIGH, "Invalid I/D-ROM region");
}
@@ -180,38 +141,31 @@ void esp_cpu_configure_region_protection(void)
// Anti-FI check that cpu is really in ocd mode
ESP_FAULT_ASSERT(esp_cpu_dbgr_is_attached());
PMP_ENTRY_SET(3, SOC_IRAM_LOW, NONE);
PMP_ENTRY_SET(4, SOC_IRAM_HIGH, PMP_TOR | RWX);
PMP_RESET_AND_ENTRY_SET(3, SOC_IRAM_LOW, NONE);
PMP_RESET_AND_ENTRY_SET(4, 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
#if CONFIG_ESP_SYSTEM_MEMPROT && CONFIG_ESP_SYSTEM_MEMPROT_PMP
extern int _iram_text_end;
/* Reset the corresponding PMP config because PMP_ENTRY_SET only sets the given bits
* Bootloader might have given extra permissions and those won't be cleared
*/
PMP_ENTRY_CFG_RESET(3);
PMP_ENTRY_CFG_RESET(4);
PMP_ENTRY_CFG_RESET(5);
PMP_ENTRY_SET(3, SOC_IRAM_LOW, NONE);
PMP_ENTRY_SET(4, (int)&_iram_text_end, PMP_TOR | RX);
PMP_ENTRY_SET(5, SOC_DRAM_HIGH, PMP_TOR | RW);
PMP_RESET_AND_ENTRY_SET(3, SOC_IRAM_LOW, NONE);
PMP_RESET_AND_ENTRY_SET(4, (int)&_iram_text_end, PMP_TOR | RX);
PMP_RESET_AND_ENTRY_SET(5, SOC_DRAM_HIGH, PMP_TOR | RW);
#else
PMP_ENTRY_SET(3, SOC_IRAM_LOW, CONDITIONAL_NONE);
PMP_ENTRY_SET(4, SOC_IRAM_HIGH, PMP_TOR | CONDITIONAL_RWX);
PMP_RESET_AND_ENTRY_SET(3, SOC_IRAM_LOW, NONE);
PMP_RESET_AND_ENTRY_SET(4, SOC_IRAM_HIGH, PMP_TOR | RWX);
_Static_assert(SOC_IRAM_LOW < SOC_IRAM_HIGH, "Invalid RAM region");
#endif
}
// 4. I_Cache / D_Cache (flash)
#if CONFIG_ESP_SYSTEM_MEMPROT && CONFIG_ESP_SYSTEM_MEMPROT_PMP && !BOOTLOADER_BUILD
#if CONFIG_ESP_SYSTEM_MEMPROT && CONFIG_ESP_SYSTEM_MEMPROT_PMP
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));
PMP_ENTRY_CFG_RESET(6);
PMP_ENTRY_SET(6, SOC_IROM_LOW, NONE);
PMP_RESET_AND_ENTRY_SET(6, SOC_IROM_LOW, NONE);
/**
Virtual space layout:
@@ -238,68 +192,85 @@ void esp_cpu_configure_region_protection(void)
if CONFIG_SPIRAM: [_rodata_reserved_end, page_aligned_drom_resv_end + available_psram_heap] in heap / reserved for mapping (RW)
*/
PMP_ENTRY_CFG_RESET(7);
PMP_ENTRY_CFG_RESET(8);
PMP_ENTRY_CFG_RESET(9);
#if CONFIG_SPIRAM_FETCH_INSTRUCTIONS && CONFIG_SPIRAM_PRE_CONFIGURE_MEMORY_PROTECTION
PMP_ENTRY_SET(7, (uint32_t)(&_instruction_reserved_end), PMP_TOR | RX);
PMP_ENTRY_SET(8, page_aligned_irom_resv_end, PMP_TOR | RW);
PMP_RESET_AND_ENTRY_SET(7, (uint32_t)(&_instruction_reserved_end), PMP_TOR | RX);
PMP_RESET_AND_ENTRY_SET(8, page_aligned_irom_resv_end, PMP_TOR | RW);
#else
PMP_ENTRY_SET(7, page_aligned_irom_resv_end, PMP_TOR | RX);
PMP_ENTRY_SET(8, page_aligned_irom_resv_end, NONE);
PMP_RESET_AND_ENTRY_SET(7, page_aligned_irom_resv_end, PMP_TOR | RX);
PMP_RESET_AND_ENTRY_SET(8, page_aligned_irom_resv_end, NONE);
#endif /* CONFIG_SPIRAM_FETCH_INSTRUCTIONS && CONFIG_SPIRAM_PRE_CONFIGURE_MEMORY_PROTECTION */
#if CONFIG_SPIRAM_RODATA && CONFIG_SPIRAM_PRE_CONFIGURE_MEMORY_PROTECTION
PMP_ENTRY_SET(9, (uint32_t)(&_rodata_reserved_end), PMP_TOR | R);
PMP_RESET_AND_ENTRY_SET(9, (uint32_t)(&_rodata_reserved_end), PMP_TOR | R);
#else
PMP_ENTRY_SET(9, page_aligned_drom_resv_end, PMP_TOR | R);
PMP_RESET_AND_ENTRY_SET(9, page_aligned_drom_resv_end, PMP_TOR | R);
#endif /* CONFIG_SPIRAM_RODATA && CONFIG_SPIRAM_PRE_CONFIGURE_MEMORY_PROTECTION */
#if CONFIG_SPIRAM_PRE_CONFIGURE_MEMORY_PROTECTION
size_t available_psram_heap = esp_psram_get_heap_size_to_protect();
PMP_ENTRY_CFG_RESET(10);
PMP_ENTRY_SET(10, ESP_ALIGN_UP(page_aligned_drom_resv_end + available_psram_heap, SOC_CPU_PMP_REGION_GRANULARITY), PMP_TOR | RW);
PMP_RESET_AND_ENTRY_SET(10, ESP_ALIGN_UP(page_aligned_drom_resv_end + available_psram_heap, SOC_CPU_PMP_REGION_GRANULARITY), PMP_TOR | RW);
#endif /* CONFIG_SPIRAM_PRE_CONFIGURE_MEMORY_PROTECTION */
#else
const uint32_t pmpaddr6 = PMPADDR_NAPOT(SOC_IROM_LOW, SOC_IROM_HIGH);
// Add the W attribute in the case of PSRAM
PMP_ENTRY_SET(6, pmpaddr6, PMP_NAPOT | CONDITIONAL_RWX);
PMP_RESET_AND_ENTRY_SET(6, pmpaddr6, PMP_NAPOT | RWX);
_Static_assert(SOC_IROM_LOW < SOC_IROM_HIGH, "Invalid I/D_Cache region");
#endif
// 5. LP memory
#if CONFIG_ESP_SYSTEM_MEMPROT && CONFIG_ESP_SYSTEM_MEMPROT_PMP && !BOOTLOADER_BUILD
#if CONFIG_ESP_SYSTEM_MEMPROT && CONFIG_ESP_SYSTEM_MEMPROT_PMP
extern int _rtc_text_start;
extern int _rtc_text_end;
/* Reset the corresponding PMP config because PMP_ENTRY_SET only sets the given bits
* Bootloader might have given extra permissions and those won't be cleared
*/
PMP_ENTRY_CFG_RESET(11);
PMP_ENTRY_CFG_RESET(12);
PMP_ENTRY_CFG_RESET(13);
PMP_ENTRY_CFG_RESET(14);
PMP_ENTRY_SET(11, SOC_RTC_IRAM_LOW, NONE);
PMP_RESET_AND_ENTRY_SET(11, SOC_RTC_IRAM_LOW, NONE);
// First part of LP mem is reserved for ULP coprocessor
#if CONFIG_ESP_SYSTEM_MEMPROT_PMP_LP_CORE_RESERVE_MEM_EXEC
PMP_ENTRY_SET(12, (int)&_rtc_text_start, PMP_TOR | RWX);
PMP_RESET_AND_ENTRY_SET(12, (int)&_rtc_text_start, PMP_TOR | RWX);
#else
PMP_ENTRY_SET(12, (int)&_rtc_text_start, PMP_TOR | RW);
PMP_RESET_AND_ENTRY_SET(12, (int)&_rtc_text_start, PMP_TOR | RW);
#endif
PMP_ENTRY_SET(13, (int)&_rtc_text_end, PMP_TOR | RX);
PMP_ENTRY_SET(14, SOC_RTC_IRAM_HIGH, PMP_TOR | RW);
PMP_RESET_AND_ENTRY_SET(13, (int)&_rtc_text_end, PMP_TOR | RX);
PMP_RESET_AND_ENTRY_SET(14, SOC_RTC_IRAM_HIGH, PMP_TOR | RW);
#else
const uint32_t pmpaddr11 = PMPADDR_NAPOT(SOC_RTC_IRAM_LOW, SOC_RTC_IRAM_HIGH);
PMP_ENTRY_SET(11, pmpaddr11, PMP_NAPOT | CONDITIONAL_RWX);
PMP_RESET_AND_ENTRY_SET(11, pmpaddr11, PMP_NAPOT | RWX);
_Static_assert(SOC_RTC_IRAM_LOW < SOC_RTC_IRAM_HIGH, "Invalid RTC IRAM region");
#endif
// 6. Peripheral addresses
PMP_ENTRY_CFG_RESET(15);
const uint32_t pmpaddr15 = PMPADDR_NAPOT(SOC_PERIPHERAL_LOW, SOC_PERIPHERAL_HIGH);
PMP_ENTRY_SET(15, pmpaddr15, PMP_NAPOT | RW);
PMP_RESET_AND_ENTRY_SET(PMP_ENTRY_PERIPHERAL, pmpaddr15, PMP_NAPOT | RW);
_Static_assert(SOC_PERIPHERAL_LOW < SOC_PERIPHERAL_HIGH, "Invalid peripheral region");
}
#endif // BOOTLOADER_BUILD
void esp_cpu_configure_region_protection(void)
{
/* Notes on implementation:
*
* 1) Note: ESP32-C5 CPU support overlapping PMP regions
*
* 2) ESP32-C5 supports 16 PMA regions so we use this feature to block all the invalid address ranges
*
* 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.
*/
/* The invalid (PMA) regions are configured in both the bootloader and the
* application; the valid (PMP) regions are configured and locked by the
* application only (an unlocked PMP entry does not apply to M-mode, and a
* locked one would survive into the application with no way to reconfigure
* it until the next CPU reset). */
esp_cpu_configure_invalid_regions();
#ifndef BOOTLOADER_BUILD
esp_cpu_configure_valid_regions();
#endif
}

View File

@@ -0,0 +1,40 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "soc/soc.h"
#include "esp_assert.h"
#ifdef __cplusplus
extern "C" {
#endif
/*
* ESP32-C5 PMP entry layout: a bootloader<->application ABI. An entry the
* (non-updatable) bootloader locks cannot be reconfigured until CPU reset, so
* the index of any entry a shipped bootloader locks is frozen.
*
* Entries locked by shipped bootloader generations (MP baseline v5.5):
* v5.5 : 0, 1-2 (ROM), 15 (peripherals)
* v6.0 / v6.1 : 0, 1 (ROM), 15 (peripherals)
* >= v6.2 : none
*/
#define PMP_ENTRY_CPU_SUBSYSTEM 0 /* NAPOT RWX */
#define PMP_ENTRY_ROM_LOW 1 /* TOR base */
#define PMP_ENTRY_ROM_HIGH 2 /* TOR R or RX */
/* 3..14: application-owned SRAM/flash/LP-RAM split, programmed by plain index
* in cpu_region_protect.c; not part of the ABI, not frozen here. */
#define PMP_ENTRY_PERIPHERAL 15 /* NAPOT RW */
ESP_STATIC_ASSERT(PMP_ENTRY_CPU_SUBSYSTEM == 0
&& PMP_ENTRY_ROM_LOW == 1 && PMP_ENTRY_ROM_HIGH == 2
&& PMP_ENTRY_PERIPHERAL == 15,
"Entries locked by shipped bootloaders are a frozen ABI and must never move");
#ifdef __cplusplus
}
#endif

View File

@@ -10,22 +10,7 @@
#include "esp_cpu.h"
#include "esp_fault.h"
#include "esp_macros.h"
#ifdef BOOTLOADER_BUILD
// Without L bit set
#define CONDITIONAL_NONE 0x0
#define CONDITIONAL_R PMP_R
#define CONDITIONAL_RX PMP_R | PMP_X
#define CONDITIONAL_RW PMP_R | PMP_W
#define CONDITIONAL_RWX PMP_R | PMP_W | PMP_X
#else
// With L bit set
#define CONDITIONAL_NONE NONE
#define CONDITIONAL_R R
#define CONDITIONAL_RX RX
#define CONDITIONAL_RW RW
#define CONDITIONAL_RWX RWX
#endif
#include "pmp_layout.h"
#define ALIGN_UP_TO_MMU_PAGE_SIZE(addr) ESP_ALIGN_UP(addr, SOC_MMU_PAGE_SIZE)
#define ALIGN_DOWN_TO_MMU_PAGE_SIZE(addr) ESP_ALIGN_DOWN(addr, SOC_MMU_PAGE_SIZE)
@@ -69,6 +54,125 @@ static void esp_cpu_configure_invalid_regions(void)
PMA_ENTRY_CFG_RESET(15);
}
#ifndef BOOTLOADER_BUILD
static void esp_cpu_configure_valid_regions(void)
{
/* There are 3 configuration scenarios for SRAM in the application
*
* 1. 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
*
* 2. 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
*
* 3. 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;
__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);
PMP_RESET_AND_ENTRY_SET(PMP_ENTRY_CPU_SUBSYSTEM, pmpaddr0, PMP_NAPOT | RWX);
_Static_assert(SOC_CPU_SUBSYSTEM_LOW < SOC_CPU_SUBSYSTEM_HIGH, "Invalid CPU subsystem region");
// 2.1 I-ROM
PMP_RESET_AND_ENTRY_SET(PMP_ENTRY_IROM_LOW, SOC_IROM_MASK_LOW, NONE);
PMP_RESET_AND_ENTRY_SET(PMP_ENTRY_IROM_HIGH, SOC_IROM_MASK_HIGH, PMP_TOR | RX);
_Static_assert(SOC_IROM_MASK_LOW < SOC_IROM_MASK_HIGH, "Invalid I-ROM region");
/* 2.2 D-ROM - redundant with the I-ROM entry above (same range, already
* locked RX); kept as a separate pair only to pin the app's IRAM/DRAM split
* to entries 5-7.
*
* C6's ROM mask is not a power of two, so ROM needs a TOR pair on entries
* 1-2. Commit d4167fea60c (v6.0) dropped this D-ROM pair and moved the split
* down onto entries 3-4 - which bootloaders up to v5.2.1 lock as D-ROM. A
* locked PMP entry can't be reconfigured until CPU reset, so such an app
* can't gain IRAM execute permission and resets before app_main().
* v5.2.2 freed 3-4, so only pre-v5.2.2 C6 bootloaders break.
*/
PMP_RESET_AND_ENTRY_SET(PMP_ENTRY_DROM_LOW, SOC_DROM_MASK_LOW, NONE);
PMP_RESET_AND_ENTRY_SET(PMP_ENTRY_DROM_HIGH, SOC_DROM_MASK_HIGH, PMP_TOR | R);
_Static_assert(SOC_DROM_MASK_LOW < SOC_DROM_MASK_HIGH, "Invalid D-ROM region");
if (esp_cpu_dbgr_is_attached()) {
// Anti-FI check that cpu is really in ocd mode
ESP_FAULT_ASSERT(esp_cpu_dbgr_is_attached());
// 3. IRAM and DRAM
const uint32_t pmpaddr5 = PMPADDR_NAPOT(SOC_IRAM_LOW, SOC_IRAM_HIGH);
PMP_RESET_AND_ENTRY_SET(5, pmpaddr5, PMP_NAPOT | RWX);
_Static_assert(SOC_IRAM_LOW < SOC_IRAM_HIGH, "Invalid RAM region");
} else {
#if CONFIG_ESP_SYSTEM_MEMPROT && CONFIG_ESP_SYSTEM_MEMPROT_PMP
extern int _iram_text_end;
// 3. IRAM and DRAM
PMP_RESET_AND_ENTRY_SET(5, SOC_IRAM_LOW, NONE);
PMP_RESET_AND_ENTRY_SET(6, (int)&_iram_text_end, PMP_TOR | RX);
PMP_RESET_AND_ENTRY_SET(7, SOC_DRAM_HIGH, PMP_TOR | RW);
#else
// 3. IRAM and DRAM
const uint32_t pmpaddr5 = PMPADDR_NAPOT(SOC_IRAM_LOW, SOC_IRAM_HIGH);
PMP_RESET_AND_ENTRY_SET(5, pmpaddr5, PMP_NAPOT | RWX);
_Static_assert(SOC_IRAM_LOW < SOC_IRAM_HIGH, "Invalid RAM region");
#endif
}
#if CONFIG_ESP_SYSTEM_MEMPROT && CONFIG_ESP_SYSTEM_MEMPROT_PMP
extern int _instruction_reserved_end;
extern int _rodata_reserved_end;
const uint32_t irom_resv_end = ALIGN_UP_TO_MMU_PAGE_SIZE((uint32_t)(&_instruction_reserved_end));
const uint32_t drom_resv_end = ALIGN_UP_TO_MMU_PAGE_SIZE((uint32_t)(&_rodata_reserved_end));
// 4. I_Cache / D_Cache (flash)
PMP_RESET_AND_ENTRY_SET(8, SOC_IROM_LOW, NONE);
PMP_RESET_AND_ENTRY_SET(9, irom_resv_end, PMP_TOR | RX);
PMP_RESET_AND_ENTRY_SET(10, drom_resv_end, PMP_TOR | R);
#else
// 4. I_Cache / D_Cache (flash)
const uint32_t pmpaddr8 = PMPADDR_NAPOT(SOC_IROM_LOW, SOC_IROM_HIGH);
PMP_RESET_AND_ENTRY_SET(8, pmpaddr8, PMP_NAPOT | RX);
_Static_assert(SOC_IROM_LOW < SOC_IROM_HIGH, "Invalid I/D_Cache region");
#endif
// 5. LP memory
#if CONFIG_ESP_SYSTEM_MEMPROT && CONFIG_ESP_SYSTEM_MEMPROT_PMP
extern int _rtc_text_start;
extern int _rtc_text_end;
PMP_RESET_AND_ENTRY_SET(11, SOC_RTC_IRAM_LOW, NONE);
// First part of LP mem is reserved for ULP coprocessor
#if CONFIG_ESP_SYSTEM_MEMPROT_PMP_LP_CORE_RESERVE_MEM_EXEC
PMP_RESET_AND_ENTRY_SET(12, (int)&_rtc_text_start, PMP_TOR | RWX);
#else
PMP_RESET_AND_ENTRY_SET(12, (int)&_rtc_text_start, PMP_TOR | RW);
#endif
PMP_RESET_AND_ENTRY_SET(13, (int)&_rtc_text_end, PMP_TOR | RX);
PMP_RESET_AND_ENTRY_SET(14, SOC_RTC_IRAM_HIGH, PMP_TOR | RW);
#else
const uint32_t pmpaddr11 = PMPADDR_NAPOT(SOC_RTC_IRAM_LOW, SOC_RTC_IRAM_HIGH);
PMP_RESET_AND_ENTRY_SET(11, pmpaddr11, PMP_NAPOT | RWX);
_Static_assert(SOC_RTC_IRAM_LOW < SOC_RTC_IRAM_HIGH, "Invalid RTC IRAM region");
#endif
// 6. Peripheral addresses
const uint32_t pmpaddr15 = PMPADDR_NAPOT(SOC_PERIPHERAL_LOW, SOC_PERIPHERAL_HIGH);
PMP_RESET_AND_ENTRY_SET(PMP_ENTRY_PERIPHERAL, pmpaddr15, PMP_NAPOT | RW);
_Static_assert(SOC_PERIPHERAL_LOW < SOC_PERIPHERAL_HIGH, "Invalid peripheral region");
}
#endif // BOOTLOADER_BUILD
void esp_cpu_configure_region_protection(void)
{
/* Notes on implementation:
@@ -85,162 +189,14 @@ void esp_cpu_configure_region_protection(void)
* correct.
*/
/* 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
*/
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
//
/* The invalid (PMA) regions are configured in both the bootloader and the
* application; the valid (PMP) regions are configured and locked by the
* application only (an unlocked PMP entry does not apply to M-mode, and a
* locked one would survive into the application with no way to reconfigure
* it until the next CPU reset). */
esp_cpu_configure_invalid_regions();
/* NOTE: When ESP-TEE is active, only configure invalid memory regions in bootloader
* to prevent errors before TEE initialization. TEE will handle all other
* memory protection.
*/
#if CONFIG_SECURE_ENABLE_TEE && BOOTLOADER_BUILD
return;
#ifndef BOOTLOADER_BUILD
esp_cpu_configure_valid_regions();
#endif
//
// Configure all the valid address regions using PMP
//
// 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_ENTRY_SET(0, pmpaddr0, PMP_NAPOT | RWX);
_Static_assert(SOC_CPU_SUBSYSTEM_LOW < SOC_CPU_SUBSYSTEM_HIGH, "Invalid CPU subsystem region");
// 2.1 I-ROM
PMP_ENTRY_SET(1, SOC_IROM_MASK_LOW, NONE);
PMP_ENTRY_SET(2, SOC_IROM_MASK_HIGH, PMP_TOR | RX);
_Static_assert(SOC_IROM_MASK_LOW < SOC_IROM_MASK_HIGH, "Invalid I-ROM region");
/* 2.2 D-ROM - redundant with the I-ROM entry above (same range, already
* locked RX); kept as a separate pair only to pin the app's IRAM/DRAM split
* to entries 5-7.
*
* C6's ROM mask is not a power of two, so ROM needs a TOR pair on entries
* 1-2. Commit d4167fea60c (v6.0) dropped this D-ROM pair and moved the split
* down onto entries 3-4 - which bootloaders up to v5.2.1 lock as D-ROM. A
* locked PMP entry can't be reconfigured until CPU reset, so such an app
* can't gain IRAM execute permission and resets before app_main() (GitHub
* #18769). v5.2.2 freed 3-4, so only pre-v5.2.2 C6 bootloaders break.
*
* CFG_RESET clears stale config a v6.0/v6.0.1 bootloader left here
* (PMP_ENTRY_SET only ORs bits); no-op once 3-4 are locked. CONDITIONAL_*
* keeps 3-4 locked in the app but unlocked in the bootloader (redundant, so
* no protection lost), letting a future app reclaim them once pre-v5.2.2
* bootloaders are out of support.
*/
PMP_ENTRY_CFG_RESET(3);
PMP_ENTRY_CFG_RESET(4);
PMP_ENTRY_SET(3, SOC_DROM_MASK_LOW, CONDITIONAL_NONE);
PMP_ENTRY_SET(4, SOC_DROM_MASK_HIGH, PMP_TOR | CONDITIONAL_R);
_Static_assert(SOC_DROM_MASK_LOW < SOC_DROM_MASK_HIGH, "Invalid D-ROM region");
if (esp_cpu_dbgr_is_attached()) {
// Anti-FI check that cpu is really in ocd mode
ESP_FAULT_ASSERT(esp_cpu_dbgr_is_attached());
// 3. IRAM and DRAM
const uint32_t pmpaddr5 = PMPADDR_NAPOT(SOC_IRAM_LOW, SOC_IRAM_HIGH);
PMP_ENTRY_SET(5, pmpaddr5, PMP_NAPOT | 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;
// 3. IRAM and DRAM
/* Reset the corresponding PMP config because PMP_ENTRY_SET only sets the given bits
* Bootloader might have given extra permissions and those won't be cleared
*/
PMP_ENTRY_CFG_RESET(5);
PMP_ENTRY_CFG_RESET(6);
PMP_ENTRY_CFG_RESET(7);
PMP_ENTRY_SET(5, SOC_IRAM_LOW, NONE);
PMP_ENTRY_SET(6, (int)&_iram_text_end, PMP_TOR | RX);
PMP_ENTRY_SET(7, SOC_DRAM_HIGH, PMP_TOR | RW);
#else
// 3. IRAM and DRAM
const uint32_t pmpaddr5 = PMPADDR_NAPOT(SOC_IRAM_LOW, SOC_IRAM_HIGH);
PMP_ENTRY_SET(5, pmpaddr5, PMP_NAPOT | 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 irom_resv_end = ALIGN_UP_TO_MMU_PAGE_SIZE((uint32_t)(&_instruction_reserved_end));
const uint32_t drom_resv_end = ALIGN_UP_TO_MMU_PAGE_SIZE((uint32_t)(&_rodata_reserved_end));
// 4. I_Cache / D_Cache (flash)
PMP_ENTRY_CFG_RESET(8);
PMP_ENTRY_CFG_RESET(9);
PMP_ENTRY_CFG_RESET(10);
PMP_ENTRY_SET(8, SOC_IROM_LOW, NONE);
PMP_ENTRY_SET(9, irom_resv_end, PMP_TOR | RX);
PMP_ENTRY_SET(10, drom_resv_end, PMP_TOR | R);
#else
// 4. I_Cache / D_Cache (flash)
const uint32_t pmpaddr8 = PMPADDR_NAPOT(SOC_IROM_LOW, SOC_IROM_HIGH);
PMP_ENTRY_SET(8, pmpaddr8, PMP_NAPOT | CONDITIONAL_RX);
_Static_assert(SOC_IROM_LOW < SOC_IROM_HIGH, "Invalid I/D_Cache region");
#endif
// 5. LP memory
#if CONFIG_ESP_SYSTEM_MEMPROT && CONFIG_ESP_SYSTEM_MEMPROT_PMP && !BOOTLOADER_BUILD
extern int _rtc_text_start;
extern int _rtc_text_end;
/* Reset the corresponding PMP config because PMP_ENTRY_SET only sets the given bits
* Bootloader might have given extra permissions and those won't be cleared
*/
PMP_ENTRY_CFG_RESET(11);
PMP_ENTRY_CFG_RESET(12);
PMP_ENTRY_CFG_RESET(13);
PMP_ENTRY_CFG_RESET(14);
PMP_ENTRY_SET(11, SOC_RTC_IRAM_LOW, NONE);
// First part of LP mem is reserved for ULP coprocessor
#if CONFIG_ESP_SYSTEM_MEMPROT_PMP_LP_CORE_RESERVE_MEM_EXEC
PMP_ENTRY_SET(12, (int)&_rtc_text_start, PMP_TOR | RWX);
#else
PMP_ENTRY_SET(12, (int)&_rtc_text_start, PMP_TOR | RW);
#endif
PMP_ENTRY_SET(13, (int)&_rtc_text_end, PMP_TOR | RX);
PMP_ENTRY_SET(14, SOC_RTC_IRAM_HIGH, PMP_TOR | RW);
#else
const uint32_t pmpaddr11 = PMPADDR_NAPOT(SOC_RTC_IRAM_LOW, SOC_RTC_IRAM_HIGH);
PMP_ENTRY_SET(11, pmpaddr11, PMP_NAPOT | CONDITIONAL_RWX);
_Static_assert(SOC_RTC_IRAM_LOW < SOC_RTC_IRAM_HIGH, "Invalid RTC IRAM region");
#endif
// 6. Peripheral addresses
const uint32_t pmpaddr15 = PMPADDR_NAPOT(SOC_PERIPHERAL_LOW, SOC_PERIPHERAL_HIGH);
PMP_ENTRY_SET(15, pmpaddr15, PMP_NAPOT | RW);
_Static_assert(SOC_PERIPHERAL_LOW < SOC_PERIPHERAL_HIGH, "Invalid peripheral region");
}

View File

@@ -0,0 +1,46 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "soc/soc.h"
#include "esp_assert.h"
#ifdef __cplusplus
extern "C" {
#endif
/*
* ESP32-C6 PMP entry layout: a bootloader<->application ABI. An entry the
* (non-updatable) bootloader locks cannot be reconfigured until CPU reset, so
* the index of any entry a shipped bootloader locks is frozen.
* The layout below matches v5.2.2..v5.5; older generations that locked a
* different value on a shared index cannot be helped (their lock wins).
*
* Entries locked by shipped bootloader generations:
* <= v5.2.1 : 0, 1-2 (I-ROM), 3-4 (D-ROM), 8-9 (cache), 14 (periph)
* v5.2.2 .. v5.5 : 0, 1-2 (I-ROM), 15 (peripherals)
* v6.0 / v6.1 : 0, 1-2 (I-ROM), 13 (peripherals)
* >= v6.2 : none
*/
#define PMP_ENTRY_CPU_SUBSYSTEM 0 /* NAPOT RWX */
#define PMP_ENTRY_IROM_LOW 1 /* TOR base */
#define PMP_ENTRY_IROM_HIGH 2 /* TOR RX */
#define PMP_ENTRY_DROM_LOW 3 /* TOR base (locked by <= v5.2.1) */
#define PMP_ENTRY_DROM_HIGH 4 /* TOR R (locked by <= v5.2.1) */
/* 5..14: application-owned SRAM/flash/LP-RAM split, programmed by plain index
* in cpu_region_protect.c; not part of the ABI, not frozen here. */
#define PMP_ENTRY_PERIPHERAL 15 /* NAPOT RW */
ESP_STATIC_ASSERT(PMP_ENTRY_CPU_SUBSYSTEM == 0
&& PMP_ENTRY_IROM_LOW == 1 && PMP_ENTRY_IROM_HIGH == 2
&& PMP_ENTRY_DROM_LOW == 3 && PMP_ENTRY_DROM_HIGH == 4
&& PMP_ENTRY_PERIPHERAL == 15,
"Entries locked by shipped bootloaders are a frozen ABI and must never move");
#ifdef __cplusplus
}
#endif

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -15,19 +15,7 @@
#include "esp_private/esp_psram_extram.h"
#endif /* !BOOTLOADER_BUILD && CONFIG_SPIRAM */
#ifdef BOOTLOADER_BUILD
// Without L bit set
#define CONDITIONAL_NONE 0x0
#define CONDITIONAL_RX PMP_R | PMP_X
#define CONDITIONAL_RW PMP_R | PMP_W
#define CONDITIONAL_RWX PMP_R | PMP_W | PMP_X
#else
// With L bit set
#define CONDITIONAL_NONE NONE
#define CONDITIONAL_RX RX
#define CONDITIONAL_RW RW
#define CONDITIONAL_RWX RWX
#endif
#include "pmp_layout.h"
#define ALIGN_UP_TO_MMU_PAGE_SIZE(addr) ESP_ALIGN_UP(addr, SOC_MMU_PAGE_SIZE)
#define ALIGN_DOWN_TO_MMU_PAGE_SIZE(addr) ESP_ALIGN_DOWN(addr, SOC_MMU_PAGE_SIZE)
@@ -76,39 +64,22 @@ static void esp_cpu_configure_invalid_regions(void)
PMA_ENTRY_CFG_RESET(15);
}
void esp_cpu_configure_region_protection(void)
#ifndef BOOTLOADER_BUILD
static void esp_cpu_configure_valid_regions(void)
{
/* Notes on implementation:
/* There are 3 configuration scenarios for SRAM in the application
*
* 1) Note: ESP32-C61 CPU supports overlapping PMP regions
*
* 2) ESP32-C61 supports 16 PMA regions so we use this feature to block all the invalid address ranges
*
* 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.
*/
/* 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
* 1. 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
* 2. 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
* 3. 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
@@ -119,40 +90,23 @@ void esp_cpu_configure_region_protection(void)
__attribute__((unused)) const unsigned RX = PMP_L | PMP_R | PMP_X;
__attribute__((unused)) const unsigned RWX = PMP_L | PMP_R | PMP_W | PMP_X;
//
// Configure all the invalid address regions using PMA
//
esp_cpu_configure_invalid_regions();
/* NOTE: When ESP-TEE is active, only configure invalid memory regions in bootloader
* to prevent errors before TEE initialization. TEE will handle all other
* memory protection.
*/
#if CONFIG_SECURE_ENABLE_TEE && BOOTLOADER_BUILD
return;
#endif
//
// Configure all the valid address regions using PMP
//
// 1. CPU Subsystem region - contains interrupt config registers
const uint32_t pmpaddr0 = PMPADDR_NAPOT(SOC_CPU_SUBSYSTEM_LOW, SOC_CPU_SUBSYSTEM_HIGH);
PMP_ENTRY_SET(0, pmpaddr0, PMP_NAPOT | RWX);
PMP_RESET_AND_ENTRY_SET(PMP_ENTRY_CPU_SUBSYSTEM, pmpaddr0, PMP_NAPOT | RWX);
_Static_assert(SOC_CPU_SUBSYSTEM_LOW < SOC_CPU_SUBSYSTEM_HIGH, "Invalid CPU subsystem region");
// 2. I/D-ROM
#if CONFIG_ESP_SYSTEM_MEMPROT && CONFIG_ESP_SYSTEM_MEMPROT_PMP && !BOOTLOADER_BUILD
#if CONFIG_ESP_SYSTEM_MEMPROT && CONFIG_ESP_SYSTEM_MEMPROT_PMP
const uint32_t drom_start = (uint32_t) (ets_rom_layout_p->drom_start);
if ((drom_start & (SOC_CPU_PMP_REGION_GRANULARITY - 1)) == 0) {
PMP_ENTRY_SET(1, SOC_IROM_MASK_LOW, NONE);
PMP_ENTRY_SET(2, drom_start, PMP_TOR | RX);
PMP_ENTRY_SET(3, SOC_DROM_MASK_HIGH, PMP_TOR | R);
PMP_RESET_AND_ENTRY_SET(PMP_ENTRY_ROM_LOW, SOC_IROM_MASK_LOW, NONE);
PMP_RESET_AND_ENTRY_SET(PMP_ENTRY_ROM_TEXT_HIGH, drom_start, PMP_TOR | RX);
PMP_RESET_AND_ENTRY_SET(PMP_ENTRY_ROM_HIGH, SOC_DROM_MASK_HIGH, PMP_TOR | R);
} else
#endif
{
const uint32_t pmpaddr1 = PMPADDR_NAPOT(SOC_IROM_MASK_LOW, SOC_IROM_MASK_HIGH);
PMP_ENTRY_SET(1, pmpaddr1, PMP_NAPOT | CONDITIONAL_RX);
PMP_RESET_AND_ENTRY_SET(PMP_ENTRY_ROM_LOW, pmpaddr1, PMP_NAPOT | RX);
_Static_assert(SOC_IROM_MASK_LOW < SOC_IROM_MASK_HIGH, "Invalid I/D-ROM region");
}
@@ -161,38 +115,31 @@ void esp_cpu_configure_region_protection(void)
// Anti-FI check that cpu is really in ocd mode
ESP_FAULT_ASSERT(esp_cpu_dbgr_is_attached());
PMP_ENTRY_SET(4, SOC_IRAM_LOW, NONE);
PMP_ENTRY_SET(5, SOC_IRAM_HIGH, PMP_TOR | RWX);
PMP_RESET_AND_ENTRY_SET(4, SOC_IRAM_LOW, NONE);
PMP_RESET_AND_ENTRY_SET(5, 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
#if CONFIG_ESP_SYSTEM_MEMPROT && CONFIG_ESP_SYSTEM_MEMPROT_PMP
extern int _iram_text_end;
/* Reset the corresponding PMP config because PMP_ENTRY_SET only sets the given bits
* Bootloader might have given extra permissions and those won't be cleared
*/
PMP_ENTRY_CFG_RESET(4);
PMP_ENTRY_CFG_RESET(5);
PMP_ENTRY_CFG_RESET(6);
PMP_ENTRY_SET(4, SOC_IRAM_LOW, NONE);
PMP_ENTRY_SET(5, (int)&_iram_text_end, PMP_TOR | RX);
PMP_ENTRY_SET(6, SOC_DRAM_HIGH, PMP_TOR | RW);
PMP_RESET_AND_ENTRY_SET(4, SOC_IRAM_LOW, NONE);
PMP_RESET_AND_ENTRY_SET(5, (int)&_iram_text_end, PMP_TOR | RX);
PMP_RESET_AND_ENTRY_SET(6, SOC_DRAM_HIGH, PMP_TOR | RW);
#else
PMP_ENTRY_SET(4, SOC_IRAM_LOW, CONDITIONAL_NONE);
PMP_ENTRY_SET(5, SOC_IRAM_HIGH, PMP_TOR | CONDITIONAL_RWX);
PMP_RESET_AND_ENTRY_SET(4, SOC_IRAM_LOW, NONE);
PMP_RESET_AND_ENTRY_SET(5, SOC_IRAM_HIGH, PMP_TOR | RWX);
_Static_assert(SOC_IRAM_LOW < SOC_IRAM_HIGH, "Invalid RAM region");
#endif
}
// 4. I_Cache / D_Cache (flash)
#if CONFIG_ESP_SYSTEM_MEMPROT && CONFIG_ESP_SYSTEM_MEMPROT_PMP && !BOOTLOADER_BUILD
#if CONFIG_ESP_SYSTEM_MEMPROT && CONFIG_ESP_SYSTEM_MEMPROT_PMP
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));
PMP_ENTRY_CFG_RESET(7);
PMP_ENTRY_SET(7, SOC_IROM_LOW, NONE);
PMP_RESET_AND_ENTRY_SET(7, SOC_IROM_LOW, NONE);
/**
Virtual space layout:
@@ -219,38 +166,63 @@ void esp_cpu_configure_region_protection(void)
if CONFIG_SPIRAM: [_rodata_reserved_end, page_aligned_drom_resv_end + available_psram_heap] in heap / reserved for mapping (RW)
*/
PMP_ENTRY_CFG_RESET(8);
PMP_ENTRY_CFG_RESET(9);
PMP_ENTRY_CFG_RESET(10);
#if CONFIG_SPIRAM_FETCH_INSTRUCTIONS && CONFIG_SPIRAM_PRE_CONFIGURE_MEMORY_PROTECTION
PMP_ENTRY_SET(8, (uint32_t)(&_instruction_reserved_end), PMP_TOR | RX);
PMP_ENTRY_SET(9, page_aligned_irom_resv_end, PMP_TOR | RW);
PMP_RESET_AND_ENTRY_SET(8, (uint32_t)(&_instruction_reserved_end), PMP_TOR | RX);
PMP_RESET_AND_ENTRY_SET(9, page_aligned_irom_resv_end, PMP_TOR | RW);
#else
PMP_ENTRY_SET(8, page_aligned_irom_resv_end, PMP_TOR | RX);
PMP_ENTRY_SET(9, page_aligned_irom_resv_end, NONE);
PMP_RESET_AND_ENTRY_SET(8, page_aligned_irom_resv_end, PMP_TOR | RX);
PMP_RESET_AND_ENTRY_SET(9, page_aligned_irom_resv_end, NONE);
#endif /* CONFIG_SPIRAM_FETCH_INSTRUCTIONS && CONFIG_SPIRAM_PRE_CONFIGURE_MEMORY_PROTECTION */
#if CONFIG_SPIRAM_RODATA && CONFIG_SPIRAM_PRE_CONFIGURE_MEMORY_PROTECTION
PMP_ENTRY_SET(10, (uint32_t)(&_rodata_reserved_end), PMP_TOR | R);
PMP_RESET_AND_ENTRY_SET(10, (uint32_t)(&_rodata_reserved_end), PMP_TOR | R);
#else
PMP_ENTRY_SET(10, page_aligned_drom_resv_end, PMP_TOR | R);
PMP_RESET_AND_ENTRY_SET(10, page_aligned_drom_resv_end, PMP_TOR | R);
#endif /* CONFIG_SPIRAM_RODATA && CONFIG_SPIRAM_PRE_CONFIGURE_MEMORY_PROTECTION*/
#if CONFIG_SPIRAM_PRE_CONFIGURE_MEMORY_PROTECTION
size_t available_psram_heap = esp_psram_get_heap_size_to_protect();
PMP_ENTRY_CFG_RESET(11);
PMP_ENTRY_SET(11, ESP_ALIGN_UP(page_aligned_drom_resv_end + available_psram_heap, SOC_CPU_PMP_REGION_GRANULARITY), PMP_TOR | RW);
PMP_RESET_AND_ENTRY_SET(11, ESP_ALIGN_UP(page_aligned_drom_resv_end + available_psram_heap, SOC_CPU_PMP_REGION_GRANULARITY), PMP_TOR | RW);
#endif /* CONFIG_SPIRAM_PRE_CONFIGURE_MEMORY_PROTECTION */
#else
const uint32_t pmpaddr7 = PMPADDR_NAPOT(SOC_IROM_LOW, SOC_IROM_HIGH);
// Add the W attribute in the case of PSRAM
PMP_ENTRY_SET(7, pmpaddr7, PMP_NAPOT | CONDITIONAL_RWX);
PMP_RESET_AND_ENTRY_SET(7, pmpaddr7, PMP_NAPOT | RWX);
_Static_assert(SOC_IROM_LOW < SOC_IROM_HIGH, "Invalid I/D_Cache region");
#endif
// 5. Peripheral addresses
const uint32_t pmpaddr12 = PMPADDR_NAPOT(SOC_PERIPHERAL_LOW, SOC_PERIPHERAL_HIGH);
PMP_ENTRY_SET(12, pmpaddr12, PMP_NAPOT | RW);
PMP_RESET_AND_ENTRY_SET(PMP_ENTRY_PERIPHERAL, pmpaddr12, PMP_NAPOT | RW);
_Static_assert(SOC_PERIPHERAL_LOW < SOC_PERIPHERAL_HIGH, "Invalid peripheral region");
}
#endif // BOOTLOADER_BUILD
void esp_cpu_configure_region_protection(void)
{
/* Notes on implementation:
*
* 1) Note: ESP32-C61 CPU supports overlapping PMP regions
*
* 2) ESP32-C61 supports 16 PMA regions so we use this feature to block all the invalid address ranges
*
* 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.
*/
/* The invalid (PMA) regions are configured in both the bootloader and the
* application; the valid (PMP) regions are configured and locked by the
* application only (an unlocked PMP entry does not apply to M-mode, and a
* locked one would survive into the application with no way to reconfigure
* it until the next CPU reset). */
esp_cpu_configure_invalid_regions();
#ifndef BOOTLOADER_BUILD
esp_cpu_configure_valid_regions();
#endif
}

View File

@@ -0,0 +1,42 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "soc/soc.h"
#include "esp_assert.h"
#ifdef __cplusplus
extern "C" {
#endif
/*
* ESP32-C61 PMP entry layout: a bootloader<->application ABI. An entry the
* (non-updatable) bootloader locks cannot be reconfigured until CPU reset, so
* the index of any entry a shipped bootloader locks is frozen.
*
* Entries locked by shipped bootloader generations (MP baseline v5.5):
* v5.5 : 0, 1-3 (ROM), 12 (peripherals)
* v6.0 / v6.1 : 0, 12 (peripherals)
* >= v6.2 : none
*/
#define PMP_ENTRY_CPU_SUBSYSTEM 0 /* NAPOT RWX */
#define PMP_ENTRY_ROM_LOW 1 /* TOR base, or NAPOT RX (unaligned branch) */
#define PMP_ENTRY_ROM_TEXT_HIGH 2 /* TOR RX (aligned branch) */
#define PMP_ENTRY_ROM_HIGH 3 /* TOR R (aligned branch) */
/* 4..11: application-owned SRAM/flash split, programmed by plain index in
* cpu_region_protect.c; not part of the ABI, not frozen here. */
#define PMP_ENTRY_PERIPHERAL 12 /* NAPOT RW */
ESP_STATIC_ASSERT(PMP_ENTRY_CPU_SUBSYSTEM == 0
&& PMP_ENTRY_ROM_LOW == 1 && PMP_ENTRY_ROM_TEXT_HIGH == 2
&& PMP_ENTRY_ROM_HIGH == 3
&& PMP_ENTRY_PERIPHERAL == 12,
"Entries locked by shipped bootloaders are a frozen ABI and must never move");
#ifdef __cplusplus
}
#endif

View File

@@ -10,22 +10,7 @@
#include "esp_cpu.h"
#include "esp_fault.h"
#include "esp_macros.h"
#ifdef BOOTLOADER_BUILD
// Without L bit set
#define CONDITIONAL_NONE 0x0
#define CONDITIONAL_R PMP_R
#define CONDITIONAL_RX PMP_R | PMP_X
#define CONDITIONAL_RW PMP_R | PMP_W
#define CONDITIONAL_RWX PMP_R | PMP_W | PMP_X
#else
// With L bit set
#define CONDITIONAL_NONE NONE
#define CONDITIONAL_R R
#define CONDITIONAL_RX RX
#define CONDITIONAL_RW RW
#define CONDITIONAL_RWX RWX
#endif
#include "pmp_layout.h"
#define ALIGN_UP_TO_MMU_PAGE_SIZE(addr) ESP_ALIGN_UP(addr, SOC_MMU_PAGE_SIZE)
#define ALIGN_DOWN_TO_MMU_PAGE_SIZE(addr) ESP_ALIGN_DOWN(addr, SOC_MMU_PAGE_SIZE)
@@ -69,6 +54,118 @@ static void esp_cpu_configure_invalid_regions(void)
PMA_ENTRY_CFG_RESET(15);
}
#ifndef BOOTLOADER_BUILD
static void esp_cpu_configure_valid_regions(void)
{
/* There are 3 configuration scenarios for SRAM in the application
*
* 1. 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
*
* 2. 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
*
* 3. 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;
__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);
PMP_RESET_AND_ENTRY_SET(PMP_ENTRY_CPU_SUBSYSTEM, pmpaddr0, PMP_NAPOT | RWX);
_Static_assert(SOC_CPU_SUBSYSTEM_LOW < SOC_CPU_SUBSYSTEM_HIGH, "Invalid CPU subsystem region");
// 2.1 I-ROM
PMP_RESET_AND_ENTRY_SET(PMP_ENTRY_IROM_LOW, SOC_IROM_MASK_LOW, NONE);
PMP_RESET_AND_ENTRY_SET(PMP_ENTRY_IROM_HIGH, SOC_IROM_MASK_HIGH, PMP_TOR | RX);
_Static_assert(SOC_IROM_MASK_LOW < SOC_IROM_MASK_HIGH, "Invalid I-ROM region");
/* 2.2 D-ROM - redundant with the I-ROM entry above (same range, already
* locked RX); kept as a separate pair only to pin the app's IRAM/DRAM split
* to entries 5-7.
*
* H2's ROM mask is a power of two, so commit d4167fea60c (v6.0) folded ROM
* into a single NAPOT entry (1) and moved the split down onto entry 2 -
* which every pre-v6.0 bootloader locks as the I-ROM TOR top. A locked PMP
* entry can't be reconfigured until CPU reset, so such an app can't gain
* IRAM execute permission and resets before app_main().
* ROM was never a single NAPOT before v6.0, so ALL pre-v6.0 bootloaders
* break (unlike C6, where only pre-v5.2.2 do).
*/
PMP_RESET_AND_ENTRY_SET(PMP_ENTRY_DROM_LOW, SOC_DROM_MASK_LOW, NONE);
PMP_RESET_AND_ENTRY_SET(PMP_ENTRY_DROM_HIGH, SOC_DROM_MASK_HIGH, PMP_TOR | R);
_Static_assert(SOC_DROM_MASK_LOW < SOC_DROM_MASK_HIGH, "Invalid D-ROM region");
if (esp_cpu_dbgr_is_attached()) {
// Anti-FI check that cpu is really in ocd mode
ESP_FAULT_ASSERT(esp_cpu_dbgr_is_attached());
// 3. IRAM and DRAM
PMP_RESET_AND_ENTRY_SET(5, SOC_IRAM_LOW, NONE);
PMP_RESET_AND_ENTRY_SET(6, 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
extern int _iram_text_end;
// 3. IRAM and DRAM
PMP_RESET_AND_ENTRY_SET(5, SOC_IRAM_LOW, NONE);
PMP_RESET_AND_ENTRY_SET(6, (int)&_iram_text_end, PMP_TOR | RX);
PMP_RESET_AND_ENTRY_SET(7, SOC_DRAM_HIGH, PMP_TOR | RW);
#else
// 3. IRAM and DRAM
PMP_RESET_AND_ENTRY_SET(5, SOC_IRAM_LOW, NONE);
PMP_RESET_AND_ENTRY_SET(6, SOC_IRAM_HIGH, PMP_TOR | RWX);
_Static_assert(SOC_IRAM_LOW < SOC_IRAM_HIGH, "Invalid RAM region");
#endif
}
#if CONFIG_ESP_SYSTEM_MEMPROT && CONFIG_ESP_SYSTEM_MEMPROT_PMP
extern int _instruction_reserved_end;
extern int _rodata_reserved_end;
const uint32_t irom_resv_end = ALIGN_UP_TO_MMU_PAGE_SIZE((uint32_t)(&_instruction_reserved_end));
const uint32_t drom_resv_end = ALIGN_UP_TO_MMU_PAGE_SIZE((uint32_t)(&_rodata_reserved_end));
// 4. I_Cache / D_Cache (flash)
PMP_RESET_AND_ENTRY_SET(8, SOC_IROM_LOW, NONE);
PMP_RESET_AND_ENTRY_SET(9, irom_resv_end, PMP_TOR | RX);
PMP_RESET_AND_ENTRY_SET(10, drom_resv_end, PMP_TOR | R);
#else
// 4. I_Cache / D_Cache (flash)
const uint32_t pmpaddr8 = PMPADDR_NAPOT(SOC_IROM_LOW, SOC_IROM_HIGH);
PMP_RESET_AND_ENTRY_SET(8, pmpaddr8, PMP_NAPOT | RX);
_Static_assert(SOC_IROM_LOW < SOC_IROM_HIGH, "Invalid I/D_Cache region");
#endif
// 5. LP memory
#if CONFIG_ESP_SYSTEM_MEMPROT && CONFIG_ESP_SYSTEM_MEMPROT_PMP
extern int _rtc_text_end;
PMP_RESET_AND_ENTRY_SET(11, SOC_RTC_IRAM_LOW, NONE);
PMP_RESET_AND_ENTRY_SET(12, (int)&_rtc_text_end, PMP_TOR | RX);
PMP_RESET_AND_ENTRY_SET(13, SOC_RTC_IRAM_HIGH, PMP_TOR | RW);
#else
const uint32_t pmpaddr11 = PMPADDR_NAPOT(SOC_RTC_IRAM_LOW, SOC_RTC_IRAM_HIGH);
PMP_RESET_AND_ENTRY_SET(11, pmpaddr11, PMP_NAPOT | RWX);
_Static_assert(SOC_RTC_IRAM_LOW < SOC_RTC_IRAM_HIGH, "Invalid RTC IRAM region");
#endif
// 6. Peripheral addresses
const uint32_t pmpaddr14 = PMPADDR_NAPOT(SOC_PERIPHERAL_LOW, SOC_PERIPHERAL_HIGH);
PMP_RESET_AND_ENTRY_SET(PMP_ENTRY_PERIPHERAL, pmpaddr14, PMP_NAPOT | RW);
_Static_assert(SOC_PERIPHERAL_LOW < SOC_PERIPHERAL_HIGH, "Invalid peripheral region");
}
#endif // BOOTLOADER_BUILD
void esp_cpu_configure_region_protection(void)
{
/* Notes on implementation:
@@ -85,155 +182,14 @@ void esp_cpu_configure_region_protection(void)
* correct.
*/
/* 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
*/
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
//
/* The invalid (PMA) regions are configured in both the bootloader and the
* application; the valid (PMP) regions are configured and locked by the
* application only (an unlocked PMP entry does not apply to M-mode, and a
* locked one would survive into the application with no way to reconfigure
* it until the next CPU reset). */
esp_cpu_configure_invalid_regions();
/* NOTE: When ESP-TEE is active, only configure invalid memory regions in bootloader
* to prevent errors before TEE initialization. TEE will handle all other
* memory protection.
*/
#if CONFIG_SECURE_ENABLE_TEE && BOOTLOADER_BUILD
return;
#ifndef BOOTLOADER_BUILD
esp_cpu_configure_valid_regions();
#endif
//
// Configure all the valid address regions using PMP
//
// 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_ENTRY_SET(0, pmpaddr0, PMP_NAPOT | RWX);
_Static_assert(SOC_CPU_SUBSYSTEM_LOW < SOC_CPU_SUBSYSTEM_HIGH, "Invalid CPU subsystem region");
// 2.1 I-ROM
PMP_ENTRY_SET(1, SOC_IROM_MASK_LOW, NONE);
PMP_ENTRY_SET(2, SOC_IROM_MASK_HIGH, PMP_TOR | RX);
_Static_assert(SOC_IROM_MASK_LOW < SOC_IROM_MASK_HIGH, "Invalid I-ROM region");
/* 2.2 D-ROM - redundant with the I-ROM entry above (same range, already
* locked RX); kept as a separate pair only to pin the app's IRAM/DRAM split
* to entries 5-7.
*
* H2's ROM mask is a power of two, so commit d4167fea60c (v6.0) folded ROM
* into a single NAPOT entry (1) and moved the split down onto entry 2 -
* which every pre-v6.0 bootloader locks as the I-ROM TOR top. A locked PMP
* entry can't be reconfigured until CPU reset, so such an app can't gain
* IRAM execute permission and resets before app_main() (GitHub #18769).
* ROM was never a single NAPOT before v6.0, so ALL pre-v6.0 bootloaders
* break (unlike C6, where only pre-v5.2.2 do).
*
* CFG_RESET clears stale config a v6.0/v6.0.1 bootloader left here
* (PMP_ENTRY_SET only ORs bits); no-op once these are locked. CONDITIONAL_*
* keeps 3-4 locked in the app but unlocked in the bootloader (redundant, so
* no protection lost), letting a future app reclaim them once pre-v5.2.3
* bootloaders are out of support.
*/
PMP_ENTRY_CFG_RESET(3);
PMP_ENTRY_CFG_RESET(4);
PMP_ENTRY_SET(3, SOC_DROM_MASK_LOW, CONDITIONAL_NONE);
PMP_ENTRY_SET(4, SOC_DROM_MASK_HIGH, PMP_TOR | CONDITIONAL_R);
_Static_assert(SOC_DROM_MASK_LOW < SOC_DROM_MASK_HIGH, "Invalid D-ROM region");
if (esp_cpu_dbgr_is_attached()) {
// Anti-FI check that cpu is really in ocd mode
ESP_FAULT_ASSERT(esp_cpu_dbgr_is_attached());
// 3. IRAM and DRAM
PMP_ENTRY_SET(5, SOC_IRAM_LOW, NONE);
PMP_ENTRY_SET(6, 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;
// 3. IRAM and DRAM
/* Reset the corresponding PMP config because PMP_ENTRY_SET only sets the given bits
* Bootloader might have given extra permissions and those won't be cleared
*/
PMP_ENTRY_CFG_RESET(5);
PMP_ENTRY_CFG_RESET(6);
PMP_ENTRY_CFG_RESET(7);
PMP_ENTRY_SET(5, SOC_IRAM_LOW, NONE);
PMP_ENTRY_SET(6, (int)&_iram_text_end, PMP_TOR | RX);
PMP_ENTRY_SET(7, SOC_DRAM_HIGH, PMP_TOR | RW);
#else
// 3. IRAM and DRAM
PMP_ENTRY_SET(5, SOC_IRAM_LOW, CONDITIONAL_NONE);
PMP_ENTRY_SET(6, 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 irom_resv_end = ALIGN_UP_TO_MMU_PAGE_SIZE((uint32_t)(&_instruction_reserved_end));
const uint32_t drom_resv_end = ALIGN_UP_TO_MMU_PAGE_SIZE((uint32_t)(&_rodata_reserved_end));
// 4. I_Cache / D_Cache (flash)
PMP_ENTRY_CFG_RESET(8);
PMP_ENTRY_CFG_RESET(9);
PMP_ENTRY_CFG_RESET(10);
PMP_ENTRY_SET(8, SOC_IROM_LOW, NONE);
PMP_ENTRY_SET(9, irom_resv_end, PMP_TOR | RX);
PMP_ENTRY_SET(10, drom_resv_end, PMP_TOR | R);
#else
// 4. I_Cache / D_Cache (flash)
const uint32_t pmpaddr8 = PMPADDR_NAPOT(SOC_IROM_LOW, SOC_IROM_HIGH);
PMP_ENTRY_SET(8, pmpaddr8, PMP_NAPOT | CONDITIONAL_RX);
_Static_assert(SOC_IROM_LOW < SOC_IROM_HIGH, "Invalid I/D_Cache region");
#endif
// 5. LP memory
#if CONFIG_ESP_SYSTEM_MEMPROT && CONFIG_ESP_SYSTEM_MEMPROT_PMP && !BOOTLOADER_BUILD
extern int _rtc_text_end;
/* Reset the corresponding PMP config because PMP_ENTRY_SET only sets the given bits
* Bootloader might have given extra permissions and those won't be cleared
*/
PMP_ENTRY_CFG_RESET(11);
PMP_ENTRY_CFG_RESET(12);
PMP_ENTRY_CFG_RESET(13);
PMP_ENTRY_SET(11, SOC_RTC_IRAM_LOW, NONE);
PMP_ENTRY_SET(12, (int)&_rtc_text_end, PMP_TOR | RX);
PMP_ENTRY_SET(13, SOC_RTC_IRAM_HIGH, PMP_TOR | RW);
#else
const uint32_t pmpaddr11 = PMPADDR_NAPOT(SOC_RTC_IRAM_LOW, SOC_RTC_IRAM_HIGH);
PMP_ENTRY_SET(11, pmpaddr11, PMP_NAPOT | CONDITIONAL_RWX);
_Static_assert(SOC_RTC_IRAM_LOW < SOC_RTC_IRAM_HIGH, "Invalid RTC IRAM region");
#endif
// 6. Peripheral addresses
PMP_ENTRY_CFG_RESET(14);
const uint32_t pmpaddr14 = PMPADDR_NAPOT(SOC_PERIPHERAL_LOW, SOC_PERIPHERAL_HIGH);
PMP_ENTRY_SET(14, pmpaddr14, PMP_NAPOT | RW);
_Static_assert(SOC_PERIPHERAL_LOW < SOC_PERIPHERAL_HIGH, "Invalid peripheral region");
}

View File

@@ -0,0 +1,46 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "soc/soc.h"
#include "esp_assert.h"
#ifdef __cplusplus
extern "C" {
#endif
/*
* ESP32-H2 PMP entry layout: a bootloader<->application ABI. An entry the
* (non-updatable) bootloader locks cannot be reconfigured until CPU reset, so
* the index of any entry a shipped bootloader locks is frozen.
* The layout below matches v5.2.2..v5.5; older generations that locked a
* different value on a shared index cannot be helped (their lock wins).
*
* Entries locked by shipped bootloader generations:
* <= v5.2.1 : 0, 1-2 (I-ROM), 3-4 (D-ROM), 8-9 (cache), 13 (periph)
* v5.2.2 .. v5.5 : 0, 1-2 (I-ROM), 14 (peripherals)
* v6.0 / v6.1 : 0, 1 (ROM), 11 (peripherals)
* >= v6.2 : none
*/
#define PMP_ENTRY_CPU_SUBSYSTEM 0 /* NAPOT RWX */
#define PMP_ENTRY_IROM_LOW 1 /* TOR base */
#define PMP_ENTRY_IROM_HIGH 2 /* TOR RX */
#define PMP_ENTRY_DROM_LOW 3 /* TOR base (locked by <= v5.2.1) */
#define PMP_ENTRY_DROM_HIGH 4 /* TOR R (locked by <= v5.2.1) */
/* 5..13: application-owned SRAM/flash/LP-RAM split, programmed by plain index
* in cpu_region_protect.c; not part of the ABI, not frozen here. */
#define PMP_ENTRY_PERIPHERAL 14 /* NAPOT RW */
ESP_STATIC_ASSERT(PMP_ENTRY_CPU_SUBSYSTEM == 0
&& PMP_ENTRY_IROM_LOW == 1 && PMP_ENTRY_IROM_HIGH == 2
&& PMP_ENTRY_DROM_LOW == 3 && PMP_ENTRY_DROM_HIGH == 4
&& PMP_ENTRY_PERIPHERAL == 14,
"Entries locked by shipped bootloaders are a frozen ABI and must never move");
#ifdef __cplusplus
}
#endif

View File

@@ -18,22 +18,7 @@
#include "soc/chip_revision.h"
#include "hal/config.h"
#ifdef BOOTLOADER_BUILD
// Without L bit set
#define CONDITIONAL_NONE 0x0
#define CONDITIONAL_R PMP_R
#define CONDITIONAL_RX PMP_R | PMP_X
#define CONDITIONAL_RW PMP_R | PMP_W
#define CONDITIONAL_RWX PMP_R | PMP_W | PMP_X
#else
// With L bit set
#define CONDITIONAL_NONE NONE
#define CONDITIONAL_R R
#define CONDITIONAL_RX RX
#define CONDITIONAL_RW RW
#define CONDITIONAL_RWX RWX
#endif
#include "pmp_layout.h"
#define ALIGN_UP_TO_MMU_PAGE_SIZE(addr) ESP_ALIGN_UP(addr, SOC_MMU_PAGE_SIZE)
#define ALIGN_DOWN_TO_MMU_PAGE_SIZE(addr) ESP_ALIGN_DOWN(addr, SOC_MMU_PAGE_SIZE)
@@ -85,6 +70,7 @@ static void esp_cpu_configure_invalid_regions(void)
PMA_RESET_AND_ENTRY_SET_TOR(15, UINT32_MAX, PMA_TOR | PMA_NONE);
}
#ifndef BOOTLOADER_BUILD
#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) \
@@ -103,7 +89,7 @@ static void esp_cpu_configure_region_protection_rev_v3(void)
// 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);
PMP_RESET_AND_ENTRY_SET(PMP_ENTRY_CPU_SUBSYSTEM, pmpaddr0, PMP_NAPOT | RW);
_Static_assert(SOC_CPU_SUBSYSTEM_LOW < SOC_CPU_SUBSYSTEM_HIGH, "Invalid CPU subsystem region");
// 2. HP-CPU SPM
@@ -111,15 +97,15 @@ static void esp_cpu_configure_region_protection_rev_v3(void)
// 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);
PMP_RESET_AND_ENTRY_SET(PMP_ENTRY_CPU_PERIPHERAL, 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);
PMP_RESET_AND_ENTRY_SET(PMP_ENTRY_ROM, 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);
PMP_RESET_AND_ENTRY_SET(PMP_ENTRY_ROM_UNCACHED_REV_V3, pmpaddr3, PMP_NAPOT | RX);
_Static_assert(SOC_IROM_MASK_LOW < SOC_IROM_MASK_HIGH, "Invalid I/D-ROM region");
@@ -133,20 +119,20 @@ static void esp_cpu_configure_region_protection_rev_v3(void)
_Static_assert(SOC_IRAM_LOW < SOC_IRAM_HIGH, "Invalid RAM region");
} else {
#if CONFIG_ESP_SYSTEM_MEMPROT && CONFIG_ESP_SYSTEM_MEMPROT_PMP && !BOOTLOADER_BUILD
#if CONFIG_ESP_SYSTEM_MEMPROT && CONFIG_ESP_SYSTEM_MEMPROT_PMP
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);
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");
#endif
}
#if CONFIG_ESP_SYSTEM_MEMPROT && CONFIG_ESP_SYSTEM_MEMPROT_PMP && !BOOTLOADER_BUILD
#if CONFIG_ESP_SYSTEM_MEMPROT && CONFIG_ESP_SYSTEM_MEMPROT_PMP
extern int _instruction_reserved_end;
extern int _rodata_reserved_end;
@@ -198,30 +184,30 @@ static void esp_cpu_configure_region_protection_rev_v3(void)
PMP_ENTRY_SET_CACHED_AND_UNCACHED(22, 26, page_aligned_drom_resv_end, PMP_TOR | R);
#else
#if !BOOTLOADER_BUILD && CONFIG_SPIRAM
#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);
PMP_RESET_AND_ENTRY_SET(10, pmpaddr10, PMP_NAPOT | 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);
PMP_RESET_AND_ENTRY_SET(11, pmpaddr11, PMP_NAPOT | RWX);
_Static_assert(SOC_EXTRAM_LOW < SOC_EXTRAM_HIGH, "Invalid I/D_EXTRAM region");
#endif /* !BOOTLOADER_BUILD && CONFIG_SPIRAM */
#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);
PMP_RESET_AND_ENTRY_SET(12, pmpaddr12, PMP_NAPOT | 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);
PMP_RESET_AND_ENTRY_SET(13, pmpaddr13, PMP_NAPOT | 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);
PMP_RESET_AND_ENTRY_SET(PMP_ENTRY_PERIPHERAL_REV_V3, pmpaddr27, PMP_NAPOT | RW);
_Static_assert(SOC_PERIPHERAL_LOW < SOC_PERIPHERAL_HIGH, "Invalid peripheral region");
// 9. LP memory and LP peripherals
#if CONFIG_ESP_SYSTEM_MEMPROT && CONFIG_ESP_SYSTEM_MEMPROT_PMP && !BOOTLOADER_BUILD
#if CONFIG_ESP_SYSTEM_MEMPROT && CONFIG_ESP_SYSTEM_MEMPROT_PMP
extern int _rtc_text_start;
extern int _rtc_text_end;
@@ -239,35 +225,152 @@ static void esp_cpu_configure_region_protection_rev_v3(void)
PMP_RESET_AND_ENTRY_SET(31, SOC_LP_PERIPH_HIGH, PMP_TOR | RW);
#else
const uint32_t pmpaddr28 = PMPADDR_NAPOT(SOC_RTC_IRAM_LOW, SOC_RTC_IRAM_HIGH);
PMP_RESET_AND_ENTRY_SET(28, pmpaddr28, PMP_NAPOT | CONDITIONAL_RWX);
PMP_RESET_AND_ENTRY_SET(28, pmpaddr28, PMP_NAPOT | RWX);
_Static_assert(SOC_RTC_IRAM_LOW < SOC_RTC_IRAM_HIGH, "Invalid RTC IRAM region");
PMP_RESET_AND_ENTRY_SET(29, SOC_LP_PERIPH_LOW, NONE);
PMP_RESET_AND_ENTRY_SET(30, SOC_LP_PERIPH_HIGH, PMP_TOR | CONDITIONAL_RW);
PMP_RESET_AND_ENTRY_SET(30, SOC_LP_PERIPH_HIGH, PMP_TOR | RW);
#endif
}
#else
// Default rev < 3.0 layout: flash 6-8, LP-RAM 9-12, peripheral 13. Used on a
// v5.3/v5.4 bootloader (peripheral locked at 13) or when PSRAM protection is off.
static void esp_cpu_configure_region_protection_rev_less_than_v3_default(void)
{
__attribute__((unused)) 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;
__attribute__((unused)) const unsigned RWX = PMP_L | PMP_R | PMP_W | PMP_X;
#if CONFIG_ESP_SYSTEM_MEMPROT && CONFIG_ESP_SYSTEM_MEMPROT_PMP
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));
const uint32_t page_aligned_drom_resv_end = ALIGN_UP_TO_MMU_PAGE_SIZE((uint32_t)(&_rodata_reserved_end));
// 5. I_Cache / D_Cache (flash)
PMP_ENTRY_CFG_RESET(6);
PMP_ENTRY_CFG_RESET(7);
PMP_ENTRY_CFG_RESET(8);
PMP_ENTRY_SET(6, SOC_IROM_LOW, NONE);
PMP_ENTRY_SET(7, page_aligned_irom_resv_end, PMP_TOR | RX);
PMP_ENTRY_SET(8, page_aligned_drom_resv_end, PMP_TOR | R);
#else
// 5. I_Cache / D_Cache (flash)
const uint32_t pmpaddr6 = PMPADDR_NAPOT(SOC_IROM_LOW, SOC_IROM_HIGH);
PMP_ENTRY_SET(6, pmpaddr6, PMP_NAPOT | RX);
_Static_assert(SOC_IROM_LOW < SOC_IROM_HIGH, "Invalid I/D_Cache region");
#endif
// 6. LP memory
#if CONFIG_ESP_SYSTEM_MEMPROT && CONFIG_ESP_SYSTEM_MEMPROT_PMP
extern int _rtc_text_start;
extern int _rtc_text_end;
/* Reset the corresponding PMP config because PMP_ENTRY_SET only sets the given bits
* Bootloader might have given extra permissions and those won't be cleared
*/
PMP_ENTRY_CFG_RESET(9);
PMP_ENTRY_CFG_RESET(10);
PMP_ENTRY_CFG_RESET(11);
PMP_ENTRY_CFG_RESET(12);
PMP_ENTRY_SET(9, 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_ENTRY_SET(10, (int)&_rtc_text_start, PMP_TOR | RWX);
#else
PMP_ENTRY_SET(10, (int)&_rtc_text_start, PMP_TOR | RW);
#endif
PMP_ENTRY_SET(11, (int)&_rtc_text_end, PMP_TOR | RX);
PMP_ENTRY_SET(12, SOC_RTC_IRAM_HIGH, PMP_TOR | RW);
#else
const uint32_t pmpaddr9 = PMPADDR_NAPOT(SOC_RTC_IRAM_LOW, SOC_RTC_IRAM_HIGH);
PMP_ENTRY_SET(9, pmpaddr9, PMP_NAPOT | RWX);
_Static_assert(SOC_RTC_IRAM_LOW < SOC_RTC_IRAM_HIGH, "Invalid RTC IRAM region");
#endif
// 7. Peripheral addresses
const uint32_t pmpaddr13 = PMPADDR_NAPOT(SOC_PERIPHERAL_LOW, SOC_PERIPHERAL_HIGH);
PMP_RESET_AND_ENTRY_SET(PMP_ENTRY_PERIPHERAL_REV_LESS_THAN_V3, pmpaddr13, PMP_NAPOT | RW);
_Static_assert(SOC_PERIPHERAL_LOW < SOC_PERIPHERAL_HIGH, "Invalid peripheral region");
}
#if CONFIG_ESP_SYSTEM_MEMPROT && CONFIG_ESP_SYSTEM_MEMPROT_PMP && CONFIG_SPIRAM_XIP_FROM_PSRAM && CONFIG_SPIRAM_PRE_CONFIGURE_MEMORY_PROTECTION
// PSRAM-protected rev < 3.0 layout: flash/ext-RAM 6-10, LP-RAM 11-14, peripheral
// 15. Fits only when the bootloader left entry 13 free (v5.5+).
static void esp_cpu_configure_region_protection_rev_less_than_v3_psram(void)
{
const unsigned NONE = PMP_L;
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;
__attribute__((unused)) const unsigned RWX = PMP_L | PMP_R | PMP_W | PMP_X;
extern int _instruction_reserved_end;
extern int _rodata_reserved_end;
extern int _rtc_text_start;
extern int _rtc_text_end;
const uint32_t page_aligned_irom_resv_end = ALIGN_UP_TO_MMU_PAGE_SIZE((uint32_t)(&_instruction_reserved_end));
const uint32_t page_aligned_drom_resv_end = ALIGN_UP_TO_MMU_PAGE_SIZE((uint32_t)(&_rodata_reserved_end));
// 5. I_Cache / D_Cache (flash) and external RAM
PMP_ENTRY_CFG_RESET(6);
PMP_ENTRY_CFG_RESET(7);
PMP_ENTRY_CFG_RESET(8);
PMP_ENTRY_CFG_RESET(9);
PMP_ENTRY_SET(6, SOC_EXTRAM_LOW, NONE);
PMP_ENTRY_SET(7, (uint32_t)(&_instruction_reserved_end), PMP_TOR | RX);
PMP_ENTRY_SET(8, page_aligned_irom_resv_end, PMP_TOR | RW);
PMP_ENTRY_SET(9, (uint32_t)(&_rodata_reserved_end), PMP_TOR | R);
const size_t available_psram_heap = esp_psram_get_heap_size_to_protect();
PMP_ENTRY_CFG_RESET(10);
PMP_ENTRY_SET(10, ESP_ALIGN_UP(page_aligned_drom_resv_end + available_psram_heap, SOC_CPU_PMP_REGION_GRANULARITY), PMP_TOR | RW);
// 6. LP memory
PMP_ENTRY_CFG_RESET(11);
PMP_ENTRY_CFG_RESET(12);
PMP_ENTRY_CFG_RESET(13);
PMP_ENTRY_CFG_RESET(14);
PMP_ENTRY_SET(11, SOC_RTC_IRAM_LOW, NONE);
#if CONFIG_ESP_SYSTEM_MEMPROT_PMP_LP_CORE_RESERVE_MEM_EXEC
PMP_ENTRY_SET(12, (int)&_rtc_text_start, PMP_TOR | RWX);
#else
PMP_ENTRY_SET(12, (int)&_rtc_text_start, PMP_TOR | RW);
#endif
PMP_ENTRY_SET(13, (int)&_rtc_text_end, PMP_TOR | RX);
PMP_ENTRY_SET(14, SOC_RTC_IRAM_HIGH, PMP_TOR | RW);
// 7. Peripheral addresses
const uint32_t pmpaddr15 = PMPADDR_NAPOT(SOC_PERIPHERAL_LOW, SOC_PERIPHERAL_HIGH);
PMP_RESET_AND_ENTRY_SET(PMP_ENTRY_PERIPHERAL_REV_LESS_THAN_V3_SPIRAM_MEMPROT_EN, pmpaddr15, PMP_NAPOT | RW);
_Static_assert(SOC_PERIPHERAL_LOW < SOC_PERIPHERAL_HIGH, "Invalid peripheral region");
}
#endif
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);
PMP_ENTRY_SET(0, pmpaddr0, PMP_NAPOT | RW);
PMP_RESET_AND_ENTRY_SET(PMP_ENTRY_CPU_SUBSYSTEM, pmpaddr0, PMP_NAPOT | RW);
_Static_assert(SOC_CPU_SUBSYSTEM_LOW < SOC_CPU_SUBSYSTEM_HIGH, "Invalid CPU subsystem region");
// 2. CPU Peripherals
const uint32_t pmpaddr1 = PMPADDR_NAPOT(CPU_PERIPH_LOW, CPU_PERIPH_HIGH);
PMP_ENTRY_SET(1, pmpaddr1, PMP_NAPOT | RW);
PMP_RESET_AND_ENTRY_SET(PMP_ENTRY_CPU_PERIPHERAL, pmpaddr1, PMP_NAPOT | RW);
_Static_assert(CPU_PERIPH_LOW < CPU_PERIPH_HIGH, "Invalid CPU peripheral region");
// 3. I/D-ROM
const uint32_t pmpaddr2 = PMPADDR_NAPOT(SOC_IROM_MASK_LOW, SOC_IROM_MASK_HIGH);
PMP_ENTRY_SET(2, pmpaddr2, PMP_NAPOT | RX);
PMP_RESET_AND_ENTRY_SET(PMP_ENTRY_ROM, pmpaddr2, PMP_NAPOT | RX);
_Static_assert(SOC_IROM_MASK_LOW < SOC_IROM_MASK_HIGH, "Invalid I/D-ROM region");
if (esp_cpu_dbgr_is_attached()) {
@@ -279,7 +382,7 @@ static void esp_cpu_configure_region_protection_rev_less_than_v3(void)
PMP_ENTRY_SET(4, 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
#if CONFIG_ESP_SYSTEM_MEMPROT && CONFIG_ESP_SYSTEM_MEMPROT_PMP
extern int _iram_text_end;
// 4. IRAM and DRAM
/* Reset the corresponding PMP config because PMP_ENTRY_SET only sets the given bits
@@ -293,85 +396,36 @@ static void esp_cpu_configure_region_protection_rev_less_than_v3(void)
PMP_ENTRY_SET(5, SOC_DRAM_HIGH, PMP_TOR | RW);
#else
// 4. IRAM and DRAM
PMP_ENTRY_SET(3, SOC_IRAM_LOW, CONDITIONAL_NONE);
PMP_ENTRY_SET(4, SOC_IRAM_HIGH, PMP_TOR | CONDITIONAL_RWX);
PMP_ENTRY_SET(3, SOC_IRAM_LOW, NONE);
PMP_ENTRY_SET(4, SOC_IRAM_HIGH, PMP_TOR | 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));
// 5. I_Cache / D_Cache (flash)
#if CONFIG_SPIRAM_XIP_FROM_PSRAM && CONFIG_SPIRAM_PRE_CONFIGURE_MEMORY_PROTECTION
// We could have split CONFIG_SPIRAM_XIP_FROM_PSRAM into CONFIG_SPIRAM_FETCH_INSTRUCTIONS and CONFIG_SPIRAM_RODATA
// but we don't have enough PMP entries to do so thus not allowing us finer control over the memory regions
PMP_ENTRY_CFG_RESET(6);
PMP_ENTRY_CFG_RESET(7);
PMP_ENTRY_CFG_RESET(8);
PMP_ENTRY_CFG_RESET(9);
PMP_ENTRY_SET(6, SOC_EXTRAM_LOW, NONE);
PMP_ENTRY_SET(7, (uint32_t)(&_instruction_reserved_end), PMP_TOR | RX);
PMP_ENTRY_SET(8, page_aligned_irom_resv_end, PMP_TOR | RW);
PMP_ENTRY_SET(9, (uint32_t)(&_rodata_reserved_end), PMP_TOR | R);
size_t available_psram_heap = esp_psram_get_heap_size_to_protect();
PMP_ENTRY_CFG_RESET(10);
PMP_ENTRY_SET(10, ESP_ALIGN_UP(page_aligned_drom_resv_end + available_psram_heap, SOC_CPU_PMP_REGION_GRANULARITY), PMP_TOR | RW);
#else
PMP_ENTRY_CFG_RESET(6);
PMP_ENTRY_CFG_RESET(7);
PMP_ENTRY_CFG_RESET(8);
PMP_ENTRY_SET(6, SOC_IROM_LOW, NONE);
PMP_ENTRY_SET(7, page_aligned_irom_resv_end, PMP_TOR | RX);
PMP_ENTRY_SET(8, page_aligned_drom_resv_end, PMP_TOR | R);
#endif /* CONFIG_SPIRAM_XIP_FROM_PSRAM && CONFIG_SPIRAM_PRE_CONFIGURE_MEMORY_PROTECTION */
#else
// 5. I_Cache / D_Cache (flash)
const uint32_t pmpaddr6 = PMPADDR_NAPOT(SOC_IROM_LOW, SOC_IROM_HIGH);
PMP_ENTRY_SET(6, pmpaddr6, PMP_NAPOT | CONDITIONAL_RX);
_Static_assert(SOC_IROM_LOW < SOC_IROM_HIGH, "Invalid I/D_Cache region");
/* The flash, LP memory and peripheral entries have two layouts. The PSRAM
* layout needs all 16 entries and only fits when the bootloader left entry 13
* free; a v5.3/v5.4 bootloader locks the peripheral there, so fall back to the
* default layout (no PSRAM protection) on those devices. */
#if CONFIG_ESP_SYSTEM_MEMPROT && CONFIG_ESP_SYSTEM_MEMPROT_PMP && CONFIG_SPIRAM_XIP_FROM_PSRAM && CONFIG_SPIRAM_PRE_CONFIGURE_MEMORY_PROTECTION
if (!(PMP_ENTRY_CFG_READ(PMP_ENTRY_PERIPHERAL_REV_LESS_THAN_V3) & PMP_L)) {
esp_cpu_configure_region_protection_rev_less_than_v3_psram();
return;
}
#endif
// 6. LP memory
#if CONFIG_ESP_SYSTEM_MEMPROT && CONFIG_ESP_SYSTEM_MEMPROT_PMP && !BOOTLOADER_BUILD
extern int _rtc_text_start;
extern int _rtc_text_end;
/* Reset the corresponding PMP config because PMP_ENTRY_SET only sets the given bits
* Bootloader might have given extra permissions and those won't be cleared
*/
PMP_ENTRY_CFG_RESET(11);
PMP_ENTRY_CFG_RESET(12);
PMP_ENTRY_CFG_RESET(13);
PMP_ENTRY_CFG_RESET(14);
PMP_ENTRY_SET(11, 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_ENTRY_SET(12, (int)&_rtc_text_start, PMP_TOR | RWX);
#else
PMP_ENTRY_SET(12, (int)&_rtc_text_start, PMP_TOR | RW);
#endif
PMP_ENTRY_SET(13, (int)&_rtc_text_end, PMP_TOR | RX);
PMP_ENTRY_SET(14, SOC_RTC_IRAM_HIGH, PMP_TOR | RW);
#else
const uint32_t pmpaddr11 = PMPADDR_NAPOT(SOC_RTC_IRAM_LOW, SOC_RTC_IRAM_HIGH);
PMP_ENTRY_SET(11, pmpaddr11, PMP_NAPOT | CONDITIONAL_RWX);
_Static_assert(SOC_RTC_IRAM_LOW < SOC_RTC_IRAM_HIGH, "Invalid RTC IRAM region");
#endif
// 7. Peripheral addresses
const uint32_t pmpaddr15 = PMPADDR_NAPOT(SOC_PERIPHERAL_LOW, SOC_PERIPHERAL_HIGH);
PMP_ENTRY_SET(15, pmpaddr15, PMP_NAPOT | RW);
_Static_assert(SOC_PERIPHERAL_LOW < SOC_PERIPHERAL_HIGH, "Invalid peripheral region");
esp_cpu_configure_region_protection_rev_less_than_v3_default();
}
#endif
static void esp_cpu_configure_valid_regions(void)
{
#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
}
#endif // BOOTLOADER_BUILD
void esp_cpu_configure_region_protection(void)
{
/* Notes on implementation:
@@ -401,40 +455,14 @@ void esp_cpu_configure_region_protection(void)
* 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
//
/* The invalid (PMA) regions are configured in both the bootloader and the
* application; the valid (PMP) regions are configured and locked by the
* application only (an unlocked PMP entry does not apply to M-mode, and a
* locked one would survive into the application with no way to reconfigure
* it until the next CPU reset). */
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();
#ifndef BOOTLOADER_BUILD
esp_cpu_configure_valid_regions();
#endif
}

View File

@@ -0,0 +1,60 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "soc/soc.h"
#include "esp_assert.h"
#ifdef __cplusplus
extern "C" {
#endif
/*
* ESP32-P4 PMP entry layout: a bootloader<->application ABI. An entry the
* (non-updatable) bootloader locks cannot be reconfigured until CPU reset, so
* the index of any entry a shipped bootloader locks is frozen.
* Two incompatible layouts selected by chip revision (>= 3.0); binaries do not
* mix, so each has its own index namespace. Entries 0-2 are shared.
*
* Locked by shipped bootloader generations:
* rev < 3.0 v5.3/v5.4 : 0-2 (ROM), 13 (periph); v5.5..v6.1 : 0-2, 15
* rev >= 3.0 v6.0/v6.1 : 0-3 (ROM cached+uncached), 27 (periph)
* >= v6.2 : none
*/
#define PMP_ENTRY_CPU_SUBSYSTEM 0 /* NAPOT RW, both layouts */
#define PMP_ENTRY_CPU_PERIPHERAL 1 /* NAPOT RW, both layouts */
#define PMP_ENTRY_ROM 2 /* NAPOT RX, both layouts */
/* rev < 3.0 has two application layouts, chosen at runtime by probing which
* peripheral entry the bootloader locked (see cpu_region_protect.c):
* - default : SRAM/flash 3-8, LP-RAM 9-12, peripheral 13; 14-15 free.
* - PSRAM-protected : SRAM/flash/ext-RAM 3-10, LP-RAM 11-14, peripheral 15.
* The PSRAM layout is taken only on v5.5+ bootloaders (which leave 13 free and
* lock the peripheral at 15); a v5.3/v5.4 bootloader locks the peripheral at 13,
* forcing the default layout. Entries 13 and 15 are both peripheral homes locked
* by shipped bootloaders and must never be repurposed for a non-peripheral region. */
#define PMP_ENTRY_PERIPHERAL_REV_LESS_THAN_V3 13 /* NAPOT RW, default layout */
#define PMP_ENTRY_PERIPHERAL_REV_LESS_THAN_V3_SPIRAM_MEMPROT_EN 15 /* NAPOT RW, PSRAM layout */
/* rev >= 3.0: 4..26 (SRAM/ext-RAM/flash cached+uncached aliases) and 28..31
* (LP) are application-owned; their indices shift with the memprot config, so
* they are programmed by plain index in cpu_region_protect.c, not named here. */
#define PMP_ENTRY_ROM_UNCACHED_REV_V3 3 /* NAPOT RX */
#define PMP_ENTRY_PERIPHERAL_REV_V3 27 /* NAPOT RW */
ESP_STATIC_ASSERT(PMP_ENTRY_CPU_SUBSYSTEM == 0 && PMP_ENTRY_CPU_PERIPHERAL == 1
&& PMP_ENTRY_ROM == 2,
"Shared entries are a frozen ABI and must never move");
ESP_STATIC_ASSERT(PMP_ENTRY_PERIPHERAL_REV_LESS_THAN_V3 == 13
&& PMP_ENTRY_PERIPHERAL_REV_LESS_THAN_V3_SPIRAM_MEMPROT_EN == 15,
"rev < 3.0 peripheral entries are locked by shipped bootloaders and must never move");
ESP_STATIC_ASSERT(PMP_ENTRY_ROM_UNCACHED_REV_V3 == 3 && PMP_ENTRY_PERIPHERAL_REV_V3 == 27,
"rev >= 3.0 entries locked by shipped bootloaders are a frozen ABI and must never move");
#ifdef __cplusplus
}
#endif

View File

@@ -24,3 +24,4 @@ ESP-IDF 6.x Migration Guide
release-6.x/6.0/index
release-6.x/6.1/index
release-6.x/6.2/index

View File

@@ -0,0 +1,9 @@
Migration from 6.1 to 6.2
--------------------------
:link_to_translation:`zh_CN:[中文]`
.. toctree::
:maxdepth: 1
security

View File

@@ -0,0 +1,15 @@
Security
========
:link_to_translation:`zh_CN:[中文]`
Memory Protection
-----------------
Starting from **ESP-IDF v6.2**, on ESP32-C5, ESP32-C6, ESP32-C61, ESP32-H2, and ESP32-P4 the second stage bootloader no longer configures or locks any RISC-V PMP entries. The complete PMP configuration is now programmed and locked by the application during early startup.
A PMP entry locked by the bootloader cannot be reconfigured until the next CPU reset. Since the bootloader on a deployed device is generally never updated, entries locked by an older bootloader could conflict with the PMP layout expected by an application built with a newer ESP-IDF version, leading to boot failures or to memory protection being silently inactive.
Applications built with ESP-IDF are not affected, as they program and lock the full PMP configuration themselves during startup, before any application code runs.
Custom (non-ESP-IDF) applications launched by the ESP-IDF second stage bootloader must not assume that any PMP entries are pre-configured or locked at handoff. Previously, the bootloader configured and locked entries covering, e.g., the ROM and the peripheral address spaces; such applications must now program their own PMP configuration.

View File

@@ -24,3 +24,4 @@
release-6.x/6.0/index
release-6.x/6.1/index
release-6.x/6.2/index

View File

@@ -0,0 +1,9 @@
从 6.1 迁移到 6.2
------------------
:link_to_translation:`en:[English]`
.. toctree::
:maxdepth: 1
security

View File

@@ -0,0 +1,15 @@
安全性
======
:link_to_translation:`en:[English]`
内存保护
--------
**ESP-IDF v6.2** 开始,在 ESP32-C5、ESP32-C6、ESP32-C61、ESP32-H2 和 ESP32-P4 上,二级引导加载程序不再配置或锁定任何 RISC-V PMP 表项。完整的 PMP 配置改由应用程序在启动早期编程并锁定。
被引导加载程序锁定的 PMP 表项在下一次 CPU 复位前无法重新配置。由于已部署设备上的引导加载程序通常不会更新,旧版引导加载程序锁定的表项可能与使用较新 ESP-IDF 版本构建的应用程序所期望的 PMP 布局发生冲突,导致启动失败,或使内存保护在没有任何提示的情况下失效。
使用 ESP-IDF 构建的应用程序不受影响,因为应用程序会在启动阶段、任何应用代码运行之前,自行编程并锁定完整的 PMP 配置。
由 ESP-IDF 二级引导加载程序启动的自定义(非 ESP-IDF应用程序不得假定交接时任何 PMP 表项已被预先配置或锁定。此前,引导加载程序会配置并锁定覆盖 ROM 和外设地址空间等区域的表项;此类应用程序现在必须自行完成 PMP 配置。

View File

@@ -610,6 +610,10 @@ def test_panic_handler_crash1(dut: PanicTestDut, config: str, test_func_name: st
# Memprot-related tests are supported only on targets with PMS/PMA peripheral;
# currently ESP32-S2, ESP32-C3, ESP32-C2, ESP32-H2, ESP32-H21, ESP32-C6, ESP32-P4, ESP32-C5 and ESP32-C61 are supported
# ESP32-P4 rev < 3.0 runs on a dedicated rev 1.x runner (its binary is built for
# and only boots on rev < 3.0 silicon), so its configs carry the esp32p4_rev1 marker.
P4_REV_LESS_THAN_V3_MARKER = pytest.mark.esp32p4_rev1
CONFIGS_MEMPROT_IDRAM = list(
zip(
[
@@ -624,7 +628,9 @@ CONFIGS_MEMPROT_IDRAM = list(
],
['esp32s2', 'esp32c3', 'esp32c2', 'esp32c5', 'esp32c61', 'esp32h2', 'esp32p4', 'esp32h21'],
)
)
) + [
('memprot_esp32p4_rev_less_than_v3', 'esp32p4', P4_REV_LESS_THAN_V3_MARKER),
]
CONFIGS_MEMPROT_IDRAM_L2_MEM_NON_CACHE = list(zip(['memprot_esp32p4'], ['esp32p4']))
@@ -643,7 +649,9 @@ CONFIGS_MEMPROT_RTC_FAST_MEM = list(
],
['esp32s2', 'esp32c3', 'esp32c5', 'esp32c6', 'esp32h2', 'esp32p4', 'esp32h21'],
)
)
) + [
('memprot_esp32p4_rev_less_than_v3', 'esp32p4', P4_REV_LESS_THAN_V3_MARKER),
]
CONFIGS_MEMPROT_RTC_SLOW_MEM = list(zip(['memprot_esp32s2'], ['esp32s2']))
@@ -659,16 +667,24 @@ CONFIGS_MEMPROT_FLASH_IDROM = list(
],
['esp32c5', 'esp32c6', 'esp32c61', 'esp32h2', 'esp32p4', 'esp32h21'],
)
)
) + [
('memprot_esp32p4_rev_less_than_v3', 'esp32p4', P4_REV_LESS_THAN_V3_MARKER),
]
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'],
[
'memprot_spiram_xip_esp32c5',
'memprot_spiram_xip_esp32c61',
'memprot_spiram_xip_esp32p4',
],
['esp32c5', 'esp32c61', 'esp32p4'],
)
)
) + [
('memprot_spiram_xip_esp32p4_rev_less_than_v3', 'esp32p4', P4_REV_LESS_THAN_V3_MARKER),
]
CONFIGS_MEMPROT_SPIRAM_XIP_IROM_ALIGNMENT_HEAP_L2_NON_CACHE = list(
zip(
@@ -688,7 +704,9 @@ CONFIGS_MEMPROT_SPIRAM_XIP_DROM_ALIGNMENT_HEAP = list(
],
['esp32s3', 'esp32c5', 'esp32c61', 'esp32p4'],
)
)
) + [
('memprot_spiram_xip_esp32p4_rev_less_than_v3', 'esp32p4', P4_REV_LESS_THAN_V3_MARKER),
]
CONFIGS_MEMPROT_SPIRAM_XIP_DROM_ALIGNMENT_HEAP_L2_NON_CACHE = list(
zip(
@@ -709,7 +727,9 @@ CONFIGS_MEMPROT_INVALID_REGION_PROTECTION_USING_PMA = list(
],
['esp32c5', 'esp32c6', 'esp32c61', 'esp32h2', 'esp32p4', 'esp32h21'],
)
)
) + [
('memprot_esp32p4_rev_less_than_v3', 'esp32p4', P4_REV_LESS_THAN_V3_MARKER),
]
@pytest.mark.generic

View File

@@ -0,0 +1,16 @@
# Restricting to ESP32P4
CONFIG_IDF_TARGET="esp32p4"
# Target the rev < 3.0 PMP layout (16 entries)
CONFIG_ESP32P4_SELECTS_REV_LESS_V3=y
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ=360
# Enabling memory protection
CONFIG_ESP_SYSTEM_MEMPROT=y
# Enable memprot test
CONFIG_TEST_MEMPROT=y
# Enable SPIRAM to check the alignment gap's memory protection
CONFIG_SPIRAM=y
CONFIG_SPIRAM_USE_CAPS_ALLOC=y

View File

@@ -0,0 +1,17 @@
# Restricting to ESP32P4
CONFIG_IDF_TARGET="esp32p4"
# Target the rev < 3.0 PMP layout (16 entries)
CONFIG_ESP32P4_SELECTS_REV_LESS_V3=y
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ=360
# Enabling memory protection
CONFIG_ESP_SYSTEM_MEMPROT=y
# Enable memprot test
CONFIG_TEST_MEMPROT=y
# Enable SPIRAM to check the alignment gap's memory protection
CONFIG_SPIRAM=y
CONFIG_SPIRAM_USE_CAPS_ALLOC=y
CONFIG_SPIRAM_XIP_FROM_PSRAM=y

View File

@@ -68,7 +68,7 @@ class PanicTestDut(IdfDut):
return self.target in ['esp32', 'esp32s3', 'esp32p4', 'esp32h4', 'esp32s31']
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'):
if self.target == 'esp32p4':
self.write('\n')
self.expect_exact('Enter test name:')
self.write(test_func_name)