Merge branch 'feat/cpu_region_protection_h4' into 'master'

Memory region protection for ESP32-H4

Closes IDF-12588

See merge request espressif/esp-idf!47070
This commit is contained in:
Mahavir Jain
2026-08-31 17:41:52 +05:30
8 changed files with 309 additions and 152 deletions

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -9,168 +9,284 @@
#include "soc/soc.h"
#include "esp_cpu.h"
#include "esp_fault.h"
#include "esp32h4/rom/rom_layout.h"
#include "esp_macros.h"
#include "esp_attr.h"
#if !BOOTLOADER_BUILD && CONFIG_SPIRAM
#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
#define ALIGN_UP_TO_MMU_PAGE_SIZE(addr) ESP_ALIGN_UP(addr, SOC_MMU_PAGE_SIZE)
/* In single core mode, the ICache1 SRAM block is reclaimed as heap RAM (contiguous with D/IRAM,
* see components/heap/port/esp32h4/memory_layout.c), so the valid RAM window must extend over it.
* In multi-core mode it is used as actual I-Cache and must stay outside the window.
*/
#if CONFIG_ESP_SYSTEM_SINGLE_CORE_MODE
#define SOC_HP_RAM_HIGH SOC_RAM_ICACHE1_HIGH
_Static_assert(SOC_IRAM_HIGH == SOC_RAM_ICACHE1_LOW, "ICache1 RAM must be contiguous with D/IRAM");
#else
// With L bit set
#define CONDITIONAL_NONE NONE
#define CONDITIONAL_RX RX
#define CONDITIONAL_RW RW
#define CONDITIONAL_RWX RWX
#define SOC_HP_RAM_HIGH SOC_IRAM_HIGH
#endif
//TODO: [ESP32H4] IDF-12588
static void esp_cpu_configure_invalid_regions(void)
NOINLINE_ATTR IRAM_ATTR static void esp_cpu_configure_invalid_regions(void)
{
abort();
#ifdef BOOTLOADER_BUILD
/* Don't lock PMA entries in the bootloader: an enabled entry constrains M-mode even without
* the lock bit (unlike PMP), and a locked entry would be frozen until CPU reset, turning the
* PMA layout into a bootloader<->application ABI. Only the application locks. */
const unsigned PMA_LOCK = 0;
#else
const unsigned PMA_LOCK = PMA_L;
#endif
const unsigned PMA_NONE = PMA_LOCK | PMA_EN;
__attribute__((unused)) const unsigned PMA_RW = PMA_LOCK | PMA_EN | PMA_R | PMA_W;
__attribute__((unused)) const unsigned PMA_RX = PMA_LOCK | PMA_EN | PMA_R | PMA_X;
__attribute__((unused)) const unsigned PMA_RWX = PMA_LOCK | PMA_EN | PMA_R | PMA_W | PMA_X;
/* Disable every PMA entry before programming.
* Descending order helps to correctly reset the TOR settings (if any).
*/
PMA_ENTRY_CFG_RESET(15);
PMA_ENTRY_CFG_RESET(14);
PMA_ENTRY_CFG_RESET(13);
PMA_ENTRY_CFG_RESET(12);
PMA_ENTRY_CFG_RESET(11);
PMA_ENTRY_CFG_RESET(10);
PMA_ENTRY_CFG_RESET(9);
PMA_ENTRY_CFG_RESET(8);
PMA_ENTRY_CFG_RESET(7);
PMA_ENTRY_CFG_RESET(6);
PMA_ENTRY_CFG_RESET(5);
PMA_ENTRY_CFG_RESET(4);
PMA_ENTRY_CFG_RESET(3);
PMA_ENTRY_CFG_RESET(2);
PMA_ENTRY_CFG_RESET(1);
PMA_ENTRY_CFG_RESET(0);
// 0. Gap at bottom of address space
PMA_ENTRY_SET_NAPOT(0, 0, SOC_CPU_SUBSYSTEM_LOW, PMA_NAPOT | PMA_NONE);
// 1. Gap between CPU subsystem region (valid, configured using PMP below) and ROM
PMA_ENTRY_SET_TOR(1, SOC_CPU_SUBSYSTEM_HIGH, PMA_NONE);
PMA_ENTRY_SET_TOR(2, SOC_IROM_MASK_LOW, PMA_TOR | PMA_NONE);
// 2. ROM region, kept valid as RX so the deny chain does not swallow it; also the only no-W
// enforcement on the ROM in the bootloader stage, where no PMP entry exists.
PMA_ENTRY_SET_TOR(3, SOC_DROM_MASK_HIGH, PMA_TOR | PMA_RX);
// 3. Gap between ROM and the D/IRAM region, including the SRAM0 block (DCache data memory),
// which ESP-IDF never assigns to the CPU.
PMA_ENTRY_SET_TOR(4, SOC_IRAM_LOW, PMA_TOR | PMA_NONE);
// 4. Gap between the valid RAM window (configured using PMP below) and the external memory
// window. In multi-core mode this gap starts at the ICache1 block.
PMA_ENTRY_SET_TOR(5, SOC_HP_RAM_HIGH, PMA_NONE);
PMA_ENTRY_SET_TOR(6, SOC_IROM_LOW, PMA_TOR | PMA_NONE);
// 5. External memory window (unified for flash and PSRAM): the cacheable attribute can only
// be set through PMA. W is only needed when PSRAM is in use, but the bootloader cannot see
// CONFIG_SPIRAM and the application's early load-to-PSRAM copy runs under this (still
// bootloader-programmed) entry - so the bootloader always grants W, the application narrows.
#if defined(BOOTLOADER_BUILD) || CONFIG_SPIRAM
PMA_ENTRY_SET_TOR(7, SOC_IROM_HIGH, PMA_TOR | PMA_RWX);
#else
PMA_ENTRY_SET_TOR(7, SOC_IROM_HIGH, PMA_TOR | PMA_RX);
#endif
// 6. Gap between the external memory window and the peripherals (no LP/RTC RAM on this target)
PMA_ENTRY_SET_TOR(8, SOC_PERIPHERAL_LOW, PMA_TOR | PMA_NONE);
// 7. End of address space; the peripherals below are valid, configured using PMP
PMA_ENTRY_SET_TOR(9, SOC_PERIPHERAL_HIGH, PMA_NONE);
PMA_ENTRY_SET_TOR(10, UINT32_MAX, PMA_TOR | PMA_NONE);
}
void esp_cpu_configure_region_protection(void)
#ifndef BOOTLOADER_BUILD
static void esp_cpu_configure_valid_regions(void)
{
// ROM has configured the MSPI region with RX permission, we should add W attribute for psram
PMA_ENTRY_SET_NAPOT(0, SOC_IROM_LOW, (SOC_IROM_HIGH - SOC_IROM_LOW), PMA_NAPOT | PMA_L | PMA_EN | PMA_R | PMA_W | PMA_X);
return;
/* Notes on implementation:
/* There are 3 configuration scenarios for SRAM
*
* 1) Note: ESP32-H4 CPU doesn't support overlapping PMP regions
*
* 2) ESP32-H4 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_end and _data_start markers to set the boundaries.
* and DRAM region cannot be executed. We use the _iram_text_end marker to set the boundary.
* 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
*/
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;
const unsigned RWX = PMP_L | PMP_R | PMP_W | PMP_X;
__attribute__((unused)) const unsigned R = PMP_L | PMP_R;
__attribute__((unused)) const unsigned RW = PMP_L | PMP_R | PMP_W;
__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();
//
// Configure all the valid address regions using PMP
//
// 1. Debug region
const uint32_t pmpaddr0 = PMPADDR_NAPOT(SOC_DEBUG_LOW, SOC_DEBUG_HIGH);
PMP_ENTRY_SET(0, pmpaddr0, PMP_NAPOT | RWX);
_Static_assert(SOC_DEBUG_LOW < SOC_DEBUG_HIGH, "Invalid CPU debug 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
PMP_ENTRY_SET(3, SOC_DROM_MASK_LOW, NONE);
PMP_ENTRY_SET(4, 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());
// 5. 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");
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_end;
// 5. 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_end, PMP_TOR | RX);
PMP_ENTRY_SET(7, SOC_DRAM_HIGH, PMP_TOR | RW);
#else
// 5. 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");
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");
#endif
}
// 4. I_Cache (flash)
const uint32_t pmpaddr8 = PMPADDR_NAPOT(SOC_IROM_LOW, SOC_IROM_HIGH);
PMP_ENTRY_SET(8, pmpaddr8, PMP_NAPOT | RX);
_Static_assert(SOC_IROM_LOW < SOC_IROM_HIGH, "Invalid I_Cache region");
// 5. D_Cache (flash)
const uint32_t pmpaddr9 = PMPADDR_NAPOT(SOC_DROM_LOW, SOC_DROM_HIGH);
PMP_ENTRY_SET(9, pmpaddr9, PMP_NAPOT | R);
_Static_assert(SOC_DROM_LOW < SOC_DROM_HIGH, "Invalid D_Cache region");
// 6. 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
/* Reset every PMP entry before programming: the bootloader configures no PMP, so the
* registers hold whatever the ROM left, and PMP_ENTRY_SET only ORs configuration bits.
* ESP32-H4 is not known to support overlapping PMP regions, so the reset-and-set macros
* cannot be used for TOR entries (their transient pmpaddr=0 creates a wrapping overlap);
* instead all entries are disabled up front and programmed in ascending index order, so no
* overlapping configuration - transient or final - is ever visible.
*/
PMP_ENTRY_CFG_RESET(0);
PMP_ENTRY_CFG_RESET(1);
PMP_ENTRY_CFG_RESET(2);
PMP_ENTRY_CFG_RESET(3);
PMP_ENTRY_CFG_RESET(4);
PMP_ENTRY_CFG_RESET(5);
PMP_ENTRY_CFG_RESET(6);
PMP_ENTRY_CFG_RESET(7);
PMP_ENTRY_CFG_RESET(8);
PMP_ENTRY_CFG_RESET(9);
PMP_ENTRY_CFG_RESET(10);
PMP_ENTRY_CFG_RESET(11);
PMP_ENTRY_CFG_RESET(12);
PMP_ENTRY_CFG_RESET(13);
PMP_ENTRY_SET(10, SOC_RTC_IRAM_LOW, NONE);
PMP_ENTRY_CFG_RESET(14);
PMP_ENTRY_CFG_RESET(15);
// Repeat same previous entry, to ensure next entry has correct base address (TOR)
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);
// 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. I/D-ROM
#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)
&& (drom_start > SOC_IROM_MASK_LOW) && (drom_start < SOC_DROM_MASK_HIGH)) {
// Split the ROM into text (RX) and data (R), fully PMP-matched (this target has spare
// entries, so the ROM text is not left PMP-unmatched as on ESP32-C5/S31).
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);
} else
#endif
{
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/D-ROM region");
}
// 3. 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(4, SOC_IRAM_LOW, NONE);
PMP_ENTRY_SET(5, SOC_HP_RAM_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;
PMP_ENTRY_SET(4, SOC_IRAM_LOW, NONE);
PMP_ENTRY_SET(5, (int)&_iram_text_end, PMP_TOR | RX);
PMP_ENTRY_SET(6, SOC_HP_RAM_HIGH, PMP_TOR | RW);
#else
const uint32_t pmpaddr10 = PMPADDR_NAPOT(SOC_RTC_IRAM_LOW, SOC_RTC_IRAM_HIGH);
PMP_ENTRY_SET(10, pmpaddr10, PMP_NAPOT | CONDITIONAL_RWX);
_Static_assert(SOC_RTC_IRAM_LOW < SOC_RTC_IRAM_HIGH, "Invalid RTC IRAM region");
PMP_ENTRY_SET(4, SOC_IRAM_LOW, NONE);
PMP_ENTRY_SET(5, SOC_HP_RAM_HIGH, PMP_TOR | RWX);
_Static_assert(SOC_IRAM_LOW < SOC_IRAM_HIGH, "Invalid RAM region");
#endif
}
// 4. External memory window (unified I/D-Cache aperture for flash and PSRAM)
#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_SET(7, SOC_IROM_LOW, NONE);
/**
Virtual space layout (single window shared by flash and PSRAM mappings):
_________ <- SOC_IROM_LOW
| |
|_______| <- _instruction_reserved_end
|_______| <- page_aligned_irom_resv_end
| |
|_______| <- _rodata_reserved_end
|_______| <- page_aligned_drom_resv_end
| |
| |
| |
|_______| <- page_aligned_drom_resv_end + available_psram_heap
| |
| |
| |
| |
|_______| <- SOC_DROM_HIGH
if CONFIG_SPIRAM_FETCH_INSTRUCTIONS: [_instruction_reserved_end, page_aligned_irom_resv_end) in heap (RW)
if CONFIG_SPIRAM_RODATA: [_rodata_reserved_end, page_aligned_drom_resv_end) in heap (RW)
if CONFIG_SPIRAM: [_rodata_reserved_end, page_aligned_drom_resv_end + available_psram_heap] in heap / reserved for mapping (RW)
*/
#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);
#else
PMP_ENTRY_SET(8, page_aligned_irom_resv_end, PMP_TOR | RX);
PMP_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);
#else
PMP_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
const size_t available_psram_heap = esp_psram_get_heap_size_to_protect();
PMP_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);
#if CONFIG_SPIRAM
// The W permission is needed since PSRAM data is mapped into this window as well
PMP_ENTRY_SET(7, pmpaddr7, PMP_NAPOT | RWX);
#else
PMP_ENTRY_SET(7, pmpaddr7, PMP_NAPOT | RX);
#endif
_Static_assert(SOC_IROM_LOW < SOC_IROM_HIGH, "Invalid I/D_Cache region");
#endif
// 7. Peripheral addresses
const uint32_t pmpaddr14 = PMPADDR_NAPOT(SOC_PERIPHERAL_LOW, SOC_PERIPHERAL_HIGH);
PMP_ENTRY_SET(14, pmpaddr14, PMP_NAPOT | RW);
// 5. 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");
}
#endif // BOOTLOADER_BUILD
void esp_cpu_configure_region_protection(void)
{
/* Notes on implementation:
*
* 1) ESP32-H4 supports 16 PMA and 16 PMP regions. PMA blocks the invalid address ranges and
* marks the external memory window as cacheable; PMP maps the valid address space bottom
* to top with NAPOT and TOR entries, the peripherals using the last entry.
*
* 2) Unlike ESP32-S31/P4, external flash and PSRAM share a single 32 MB window
* ([SOC_IROM_LOW, SOC_IROM_HIGH)), so the PSRAM sections are carved out of that window.
* There is no LP/RTC RAM on this target, so no LP memory entries are needed.
*/
/* The invalid (PMA) regions are configured by both the bootloader and the application; the valid
* (PMP) regions by the application only, since an unlocked PMP entry does not constrain M-mode.
* Only the application locks either, so the bootloader freezes nothing and the whole PMA/PMP
* layout stays application-owned - an implementation detail, not an ABI. */
esp_cpu_configure_invalid_regions();
#ifndef BOOTLOADER_BUILD
esp_cpu_configure_valid_regions();
#endif
}

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -85,16 +85,15 @@ typedef struct {
#if SUPPORT_USB_DWCOTG
void *dram_start_usb_dwcotg_rom;
void *dram_end_usb_dwcotg_rom;
#else
//Two reserved members are defined here, so the structure will not be broken,
//please keep in mind that there is no memory can be released between
//dram_start_usb_reserved_rom ~ dram_end_usb_reserved_rom.
void *dram_start_usb_reserved_rom;
void *dram_end_usb_reserved_rom;
#endif
void *dram_start_uart_rom;
void *dram_end_uart_rom;
void *eh_frame_vaddr_rom;
void *eh_frame_hdr_vaddr_rom;
void *drom_start;
} ets_rom_layout_t;
extern const ets_rom_layout_t *const ets_rom_layout_p;

View File

@@ -419,6 +419,10 @@ config SOC_CPU_IDRAM_SPLIT_USING_PMP
bool
default y
config SOC_CPU_PMP_REGION_GRANULARITY
int
default 128
config SOC_HP_CPU_HAS_MULTIPLE_CORES
bool
default y

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*/
@@ -142,9 +142,9 @@
#define SOC_DROM_LOW SOC_IROM_LOW
#define SOC_DROM_HIGH SOC_IROM_HIGH
#define SOC_IROM_MASK_LOW 0x40000000
#define SOC_IROM_MASK_HIGH 0x40050000
#define SOC_IROM_MASK_HIGH 0x40020000
#define SOC_DROM_MASK_LOW 0x40000000
#define SOC_DROM_MASK_HIGH 0x40050000
#define SOC_DROM_MASK_HIGH 0x40020000
#define SOC_IRAM_LOW 0x40810000
#define SOC_IRAM_HIGH 0x40860000
#define SOC_DRAM_LOW 0x40810000
@@ -186,9 +186,9 @@
#define SOC_PERIPHERAL_LOW 0x60000000
#define SOC_PERIPHERAL_HIGH 0x60100000
// Debug region, not used by software
#define SOC_DEBUG_LOW 0x20000000
#define SOC_DEBUG_HIGH 0x28000000
// CPU subsystem region (debug mode code and interrupt config)
#define SOC_CPU_SUBSYSTEM_LOW 0x20000000
#define SOC_CPU_SUBSYSTEM_HIGH 0x30000000
// Start (highest address) of ROM boot stack, only relevant during early boot
#define SOC_ROM_STACK_START 0x4085d350
@@ -196,7 +196,7 @@
//ICache1 region
#define SOC_RAM_ICACHE1_LOW 0x40860000
#define SOC_RAM_ICACHE1_HIGH 0x40867fff
#define SOC_RAM_ICACHE1_HIGH 0x40868000
//On RISC-V CPUs, the interrupt sources are all external interrupts, whose type, source and priority are configured by SW.
//There is no HW NMI conception. SW should controlled the masked levels through INT_THRESH_REG.

View File

@@ -172,6 +172,7 @@
#define SOC_CPU_HAS_PMA 1
#define SOC_CPU_IDRAM_SPLIT_USING_PMP 1
#define SOC_CPU_PMP_REGION_GRANULARITY 128
#define SOC_HP_CPU_HAS_MULTIPLE_CORES 1 // Convenience boolean macro used to determine if a target has multiple cores.
#define SOC_CPU_HAS_LOCKUP_RESET 1

View File

@@ -635,8 +635,20 @@ CONFIGS_MEMPROT_IDRAM = list(
'memprot_esp32p4',
'memprot_esp32h21',
'memprot_esp32s31',
'memprot_esp32h4',
],
[
'esp32s2',
'esp32c3',
'esp32c2',
'esp32c5',
'esp32c61',
'esp32h2',
'esp32p4',
'esp32h21',
'esp32s31',
'esp32h4',
],
['esp32s2', 'esp32c3', 'esp32c2', 'esp32c5', 'esp32c61', 'esp32h2', 'esp32p4', 'esp32h21', 'esp32s31'],
)
) + [
('memprot_esp32p4_rev_less_than_v3', 'esp32p4', P4_REV_LESS_THAN_V3_MARKER),
@@ -676,8 +688,9 @@ CONFIGS_MEMPROT_FLASH_IDROM = list(
'memprot_esp32p4',
'memprot_esp32h21',
'memprot_esp32s31',
'memprot_esp32h4',
],
['esp32c5', 'esp32c6', 'esp32c61', 'esp32h2', 'esp32p4', 'esp32h21', 'esp32s31'],
['esp32c5', 'esp32c6', 'esp32c61', 'esp32h2', 'esp32p4', 'esp32h21', 'esp32s31', 'esp32h4'],
)
) + [
('memprot_esp32p4_rev_less_than_v3', 'esp32p4', P4_REV_LESS_THAN_V3_MARKER),
@@ -692,8 +705,9 @@ CONFIGS_MEMPROT_SPIRAM_XIP_IROM_ALIGNMENT_HEAP = list(
'memprot_spiram_xip_esp32c61',
'memprot_spiram_xip_esp32p4',
'memprot_spiram_xip_esp32s31',
'memprot_spiram_xip_esp32h4',
],
['esp32c5', 'esp32c61', 'esp32p4', 'esp32s31'],
['esp32c5', 'esp32c61', 'esp32p4', 'esp32s31', 'esp32h4'],
)
) + [
('memprot_spiram_xip_esp32p4_rev_less_than_v3', 'esp32p4', P4_REV_LESS_THAN_V3_MARKER),
@@ -715,8 +729,9 @@ CONFIGS_MEMPROT_SPIRAM_XIP_DROM_ALIGNMENT_HEAP = list(
'memprot_spiram_xip_esp32c61',
'memprot_spiram_xip_esp32p4',
'memprot_spiram_xip_esp32s31',
'memprot_spiram_xip_esp32h4',
],
['esp32s3', 'esp32c5', 'esp32c61', 'esp32p4', 'esp32s31'],
['esp32s3', 'esp32c5', 'esp32c61', 'esp32p4', 'esp32s31', 'esp32h4'],
)
) + [
('memprot_spiram_xip_esp32p4_rev_less_than_v3', 'esp32p4', P4_REV_LESS_THAN_V3_MARKER),
@@ -739,8 +754,9 @@ CONFIGS_MEMPROT_INVALID_REGION_PROTECTION_USING_PMA = list(
'memprot_esp32p4',
'memprot_esp32h21',
'memprot_esp32s31',
'memprot_esp32h4',
],
['esp32c5', 'esp32c6', 'esp32c61', 'esp32h2', 'esp32p4', 'esp32h21', 'esp32s31'],
['esp32c5', 'esp32c6', 'esp32c61', 'esp32h2', 'esp32p4', 'esp32h21', 'esp32s31', 'esp32h4'],
)
) + [
('memprot_esp32p4_rev_less_than_v3', 'esp32p4', P4_REV_LESS_THAN_V3_MARKER),

View File

@@ -0,0 +1,8 @@
# Restricting to ESP32H4
CONFIG_IDF_TARGET="esp32h4"
# Enabling memory protection
CONFIG_ESP_SYSTEM_MEMPROT=y
# Enable memprot test
CONFIG_TEST_MEMPROT=y

View File

@@ -0,0 +1,13 @@
# Restricting to ESP32H4
CONFIG_IDF_TARGET="esp32h4"
# 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