mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-01 18:50:34 +03:00
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.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user