From 3ee34cdac543ab5fe34809d416e363180f93add8 Mon Sep 17 00:00:00 2001 From: Mahavir Jain Date: Thu, 6 Aug 2026 12:27:35 +0530 Subject: [PATCH] change(esp_hw_support): drop the frozen PMP entry layout for ESP32-S31 Freezing PMP entry indices as a bootloader<->application ABI (pmp_layout.h) is only needed on targets where an already-shipped bootloader locks PMP entries (C5, C6, C61, H2, P4). No such bootloader has ever shipped for ESP32-S31 and, per the v6.2 PMP ownership policy, the bootloader never configures or locks any PMP entry on this target - so there is no ABI to freeze. Program all entries by plain index (as on ESP32-H4/H21) and state that the application-owned layout is not an ABI. --- .../port/esp32s31/cpu_region_protect.c | 60 ++++++++++--------- .../esp32s31/private_include/pmp_layout.h | 56 ----------------- 2 files changed, 33 insertions(+), 83 deletions(-) delete mode 100644 components/esp_hw_support/port/esp32s31/private_include/pmp_layout.h diff --git a/components/esp_hw_support/port/esp32s31/cpu_region_protect.c b/components/esp_hw_support/port/esp32s31/cpu_region_protect.c index bc9be39ba87..9ac7d1bf824 100644 --- a/components/esp_hw_support/port/esp32s31/cpu_region_protect.c +++ b/components/esp_hw_support/port/esp32s31/cpu_region_protect.c @@ -10,16 +10,24 @@ #include "esp_cpu.h" #include "esp_fault.h" #include "esp32s31/rom/rom_layout.h" -#include "pmp_layout.h" +#include "esp_macros.h" -#define ALIGN_UP_TO_MMU_PAGE_SIZE(addr) (((addr) + (SOC_MMU_PAGE_SIZE) - 1) & ~((SOC_MMU_PAGE_SIZE) - 1)) +#define ALIGN_UP_TO_MMU_PAGE_SIZE(addr) ESP_ALIGN_UP(addr, SOC_MMU_PAGE_SIZE) static void esp_cpu_configure_invalid_regions(void) { - const unsigned PMA_NONE = PMA_L | PMA_EN; - __attribute__((unused)) const unsigned PMA_RW = PMA_L | PMA_EN | PMA_R | PMA_W; - __attribute__((unused)) const unsigned PMA_RX = PMA_L | PMA_EN | PMA_R | PMA_X; - __attribute__((unused)) const unsigned PMA_RWX = PMA_L | PMA_EN | PMA_R | PMA_W | PMA_X; +#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; // ROM uses some PMA entries, so we need to clear them before using them in ESP-IDF. // The reset-and-set PMA macros are safe to use even though ESP32-S31 does not tolerate @@ -40,7 +48,7 @@ static void esp_cpu_configure_invalid_regions(void) PMA_RESET_AND_ENTRY_SET_TOR(3, SOC_IRAM_HIGH, PMA_NONE); PMA_RESET_AND_ENTRY_SET_TOR(4, SOC_IROM_MASK_LOW, PMA_TOR | PMA_NONE); - // 3. ROM has configured the ROM region to be cacheable, lock the configuration as RX. + // 3. ROM has configured the ROM region to be cacheable, keep the configuration as RX. // Configuring it here (as a valid RX region) lets the I/D-ROM PMP entries below use the // DROM-split optimization and save a PMP entry. PMA_RESET_AND_ENTRY_SET_TOR(5, SOC_IROM_MASK_LOW, PMA_NONE); @@ -50,7 +58,7 @@ static void esp_cpu_configure_invalid_regions(void) PMA_RESET_AND_ENTRY_SET_TOR(7, SOC_DROM_MASK_HIGH, PMA_NONE); PMA_RESET_AND_ENTRY_SET_TOR(8, SOC_IROM_LOW, PMA_TOR | PMA_NONE); - // 5. ROM has configured the external flash MSPI region with RX permission; lock it as RX and + // 5. ROM has configured the external flash MSPI region with RX permission; keep it RX and // make it cacheable. This is a valid region but is configured using PMA (instead of only // PMP) because the cacheable attribute can only be set through PMA. PMA_RESET_AND_ENTRY_SET_NAPOT(9, SOC_IROM_LOW, (SOC_IROM_HIGH - SOC_IROM_LOW), PMA_NAPOT | PMA_RX); @@ -60,8 +68,8 @@ static void esp_cpu_configure_invalid_regions(void) PMA_RESET_AND_ENTRY_SET_TOR(11, SOC_EXTRAM_LOW, PMA_TOR | PMA_NONE); // 7. ROM has configured the external PSRAM MSPI region with RX permission; add the W attribute - // and lock it, and make it cacheable. As above, this valid region is configured using PMA - // so that it can be marked cacheable (RWX so that PSRAM data and XIP-from-PSRAM both work). + // and make it cacheable. As above, this valid region is configured using PMA so that it can + // be marked cacheable (RWX so that PSRAM data and XIP-from-PSRAM both work). PMA_RESET_AND_ENTRY_SET_NAPOT(12, SOC_EXTRAM_LOW, (SOC_EXTRAM_HIGH - SOC_EXTRAM_LOW), PMA_NAPOT | PMA_RWX); // 8. Non-cacheable (cache-bypass) alias of External PSRAM at +SOC_NON_CACHEABLE_OFFSET_PSRAM, @@ -110,9 +118,10 @@ static void esp_cpu_configure_valid_regions(void) * ESP32-S31 does not support overlapping PMP regions, so the per-entry PMP_RESET_AND_ENTRY_SET * macro cannot be used for TOR entries (the transient pmpaddr=0 it writes creates a fleeting * wrapping overlap that faults on this CPU). Instead every entry is reset to a disabled state - * up front and the entries are then programmed strictly in ascending-address order, so each TOR - * base is already in place before the entry that uses it is enabled, and no overlapping - * intermediate configuration is ever visible. + * up front (so no region is active while programming) and the entries are then programmed in + * ascending index order, so each TOR base address is already in place before the entry that + * uses it is enabled. The final regions themselves are disjoint, so no overlapping + * configuration - transient or final - is ever visible. */ PMP_ENTRY_CFG_RESET(0); PMP_ENTRY_CFG_RESET(1); @@ -133,7 +142,7 @@ static void esp_cpu_configure_valid_regions(void) // 1. CPU Subsystem region - contains debug mode code and interrupt config registers const uint32_t pmpaddr_cpu_subsystem = PMPADDR_NAPOT(SOC_CPU_SUBSYSTEM_LOW, SOC_CPU_SUBSYSTEM_HIGH); - PMP_ENTRY_SET(PMP_ENTRY_CPU_SUBSYSTEM, pmpaddr_cpu_subsystem, PMP_NAPOT | RWX); + PMP_ENTRY_SET(0, pmpaddr_cpu_subsystem, PMP_NAPOT | RWX); _Static_assert(SOC_CPU_SUBSYSTEM_LOW < SOC_CPU_SUBSYSTEM_HIGH, "Invalid CPU subsystem region"); // 2. I/D-ROM (ROM-Cache) @@ -144,13 +153,13 @@ static void esp_cpu_configure_valid_regions(void) // 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 also makes it cacheable. Thus, we // save on one PMP entry. - PMP_ENTRY_SET(PMP_ENTRY_ROM_LOW, drom_start, NONE); - PMP_ENTRY_SET(PMP_ENTRY_ROM_HIGH, SOC_DROM_MASK_HIGH, PMP_TOR | R); + PMP_ENTRY_SET(1, drom_start, NONE); + PMP_ENTRY_SET(2, SOC_DROM_MASK_HIGH, PMP_TOR | R); } else #endif { - PMP_ENTRY_SET(PMP_ENTRY_ROM_LOW, SOC_IROM_MASK_LOW, NONE); - PMP_ENTRY_SET(PMP_ENTRY_ROM_HIGH, SOC_IROM_MASK_HIGH, PMP_TOR | RX); + 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"); } @@ -253,9 +262,8 @@ static void esp_cpu_configure_valid_regions(void) // debug address space). The LP peripherals are contiguous with the rest and are covered by // this single window as well. Peripherals use the last PMP entries (14-15) so that the entry // count used by the regions above (which varies with the build configuration) never collides. - // Both indices are frozen by pmp_layout.h. - PMP_ENTRY_SET(PMP_ENTRY_PERIPHERAL_LOW, SOC_PERIPHERAL_LOW, NONE); - PMP_ENTRY_SET(PMP_ENTRY_PERIPHERAL_HIGH, SOC_PERIPHERAL_HIGH, PMP_TOR | RW); + PMP_ENTRY_SET(14, SOC_PERIPHERAL_LOW, NONE); + PMP_ENTRY_SET(15, SOC_PERIPHERAL_HIGH, PMP_TOR | RW); _Static_assert(SOC_PERIPHERAL_LOW < SOC_PERIPHERAL_HIGH, "Invalid peripheral region"); } #endif // BOOTLOADER_BUILD @@ -281,12 +289,10 @@ void esp_cpu_configure_region_protection(void) * address pair, so each region needs only a single PMP entry (no CACHED_AND_UNCACHED helper). */ - /* 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 - * constrain machine mode anyway, and a bootloader-locked entry could not be reconfigured until the - * next CPU reset - which would freeze the entry layout into a bootloader <-> application ABI. The - * bootloader therefore programs no PMP entry and the application owns and freezes the whole PMP - * layout (see pmp_layout.h). */ + /* 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 diff --git a/components/esp_hw_support/port/esp32s31/private_include/pmp_layout.h b/components/esp_hw_support/port/esp32s31/private_include/pmp_layout.h deleted file mode 100644 index 08f644cf1ea..00000000000 --- a/components/esp_hw_support/port/esp32s31/private_include/pmp_layout.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * 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-S31 PMP entry layout: a bootloader <-> application ABI. - * - * A PMP entry that the (non-updatable) second stage bootloader locks cannot be reconfigured until - * the next CPU reset, so the index of any entry a shipped bootloader locks would be frozen for the - * lifetime of that device. To avoid such drift, the ESP-IDF bootloader for ESP32-S31 configures no - * PMP entry at all (it configures only PMA); the application programs and locks the entire PMP - * layout during early startup (see cpu_region_protect.c). - * - * No bootloader that locks a PMP entry has ever shipped for this target, so today no index is - * constrained by a deployed device. The boundary entries below are nonetheless frozen as a - * forward-compatibility contract and pinned with ESP_STATIC_ASSERTs, so a locked entry a future - * bootloader or a custom (non-ESP-IDF) application might rely on can never be silently renumbered. - * - * Frozen entries: - * 0 : CPU subsystem (NAPOT RWX) - * 1 - 2 : I/D-ROM (ROM-Cache) TOR pair - * 14 - 15 : peripherals TOR pair (on-chip peripherals, CPU peripheral, cache-data memory, - * LP peripherals and the debug address space) - * - * Entries 3 - 13 are application-owned (the SRAM IRAM/DRAM split, the flash I/D-cache split, the - * external PSRAM region and the LP-TCM split). Their count varies with the build configuration, - * they are programmed by plain index in cpu_region_protect.c, and they are NOT part of the ABI - - * they may change between versions. - */ -#define PMP_ENTRY_CPU_SUBSYSTEM 0 -#define PMP_ENTRY_ROM_LOW 1 -#define PMP_ENTRY_ROM_HIGH 2 -#define PMP_ENTRY_PERIPHERAL_LOW 14 -#define PMP_ENTRY_PERIPHERAL_HIGH 15 - -ESP_STATIC_ASSERT(PMP_ENTRY_CPU_SUBSYSTEM == 0 - && PMP_ENTRY_ROM_LOW == 1 - && PMP_ENTRY_ROM_HIGH == 2 - && PMP_ENTRY_PERIPHERAL_LOW == 14 - && PMP_ENTRY_PERIPHERAL_HIGH == 15, - "ESP32-S31 PMP entries that a bootloader may lock are a frozen ABI and must never move"); - -#ifdef __cplusplus -} -#endif