From b5891edb9b2c1734e9f131134e37c4b4553c8cd6 Mon Sep 17 00:00:00 2001 From: "nilesh.kale" Date: Thu, 4 Jun 2026 14:49:01 +0530 Subject: [PATCH] feat: enable cpu region protection for esp32s31 --- .../port/esp32s31/cpu_region_protect.c | 301 ++++++++++++++++-- .../esp32s31/private_include/pmp_layout.h | 56 ++++ components/soc/esp32s31/include/soc/soc.h | 9 +- .../system/panic/panic_base/pytest_panic.py | 22 +- .../panic_base/sdkconfig.ci.memprot_esp32s31 | 8 + .../sdkconfig.ci.memprot_spiram_xip_esp32s31 | 13 + 6 files changed, 371 insertions(+), 38 deletions(-) create mode 100644 components/esp_hw_support/port/esp32s31/private_include/pmp_layout.h create mode 100644 tools/test_apps/system/panic/panic_base/sdkconfig.ci.memprot_esp32s31 create mode 100644 tools/test_apps/system/panic/panic_base/sdkconfig.ci.memprot_spiram_xip_esp32s31 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 17506593ae5..bc9be39ba87 100644 --- a/components/esp_hw_support/port/esp32s31/cpu_region_protect.c +++ b/components/esp_hw_support/port/esp32s31/cpu_region_protect.c @@ -9,42 +9,287 @@ #include "soc/soc.h" #include "esp_cpu.h" #include "esp_fault.h" -#include "hal/cache_ll.h" -#include "riscv/csr.h" -#include "esp_macros.h" -#if !BOOTLOADER_BUILD && CONFIG_SPIRAM -#include "esp_private/esp_psram_extram.h" -#endif /* !BOOTLOADER_BUILD && CONFIG_SPIRAM */ +#include "esp32s31/rom/rom_layout.h" +#include "pmp_layout.h" -/* TODO: [ESP32S31] IDF-15238 */ +#define ALIGN_UP_TO_MMU_PAGE_SIZE(addr) (((addr) + (SOC_MMU_PAGE_SIZE) - 1) & ~((SOC_MMU_PAGE_SIZE) - 1)) -#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 +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; + + // 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 + // overlapping *PMP* regions: PMA configures memory attributes (not locked protection), so + // the transient pmpaddr=0 inside the macro does not raise a fault here (same as ESP32-H4). + + // 0. Gap at bottom of address space + PMA_RESET_AND_ENTRY_SET_NAPOT(0, 0, SOC_CPU_SUBSYSTEM_LOW, PMA_NAPOT | PMA_NONE); + + // [SOC_CPU_SUBSYSTEM_LOW .. SOC_RTC_IRAM_HIGH) is all valid (CPU subsystem, on-chip + // peripherals and the LP TCM) and is configured using PMP below, so no PMA entry is needed. + + // 1. Gap between LP TCM (RTC SRAM) and HP TCM (internal SRAM) + PMA_RESET_AND_ENTRY_SET_TOR(1, SOC_RTC_IRAM_HIGH, PMA_NONE); + PMA_RESET_AND_ENTRY_SET_TOR(2, SOC_IRAM_LOW, PMA_TOR | PMA_NONE); + + // 2. Gap between HP TCM (internal SRAM) and ROM-Cache + 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. + // 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); + PMA_RESET_AND_ENTRY_SET_TOR(6, SOC_DROM_MASK_HIGH, PMA_TOR | PMA_RX); + + // 4. Gap between ROM-Cache and External flash (I/D-Cache) + 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 + // 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); + + // 6. Gap between External flash (I/D-Cache) and External PSRAM + PMA_RESET_AND_ENTRY_SET_TOR(10, SOC_IROM_HIGH, PMA_NONE); + 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). + 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, + // used by GDMA to reach PSRAM-resident descriptors/buffers coherently. Marked RW at a higher + // priority (lower index) than the catch-all below so it stays a valid region. + PMA_RESET_AND_ENTRY_SET_NAPOT(13, (uint32_t)SOC_EXTRAM_LOW + (uint32_t)SOC_NON_CACHEABLE_OFFSET_PSRAM, (SOC_EXTRAM_HIGH - SOC_EXTRAM_LOW), PMA_NAPOT | PMA_RW); + + // 9. End of address space (everything above External PSRAM except the non-cacheable alias above) + PMA_RESET_AND_ENTRY_SET_TOR(14, SOC_EXTRAM_HIGH, PMA_NONE); + PMA_RESET_AND_ENTRY_SET_TOR(15, UINT32_MAX, PMA_TOR | PMA_NONE); +} + +#ifndef BOOTLOADER_BUILD +static void esp_cpu_configure_valid_regions(void) +{ + /* There are 3 configuration scenarios for SRAM + * + * 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 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 + * + * 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; + __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; + + /* Reset every PMP entry before programming it. + * + * The bootloader no longer configures any PMP entry, so on entry to the application each PMP + * register still holds whatever the ROM left in it. PMP_ENTRY_SET only ORs configuration bits, + * so stale permission bits must be cleared first (e.g. an entry that must end up as NONE would + * otherwise keep any R/W/X bit the ROM set). + * + * 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. + */ + 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_CFG_RESET(14); + PMP_ENTRY_CFG_RESET(15); + + // 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); + _Static_assert(SOC_CPU_SUBSYSTEM_LOW < SOC_CPU_SUBSYSTEM_HIGH, "Invalid CPU subsystem region"); + + // 2. I/D-ROM (ROM-Cache) +#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 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); + } 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); + _Static_assert(SOC_IROM_MASK_LOW < SOC_IROM_MASK_HIGH, "Invalid I/D-ROM region"); + } + + // 3. IRAM and DRAM (HP TCM) + 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(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"); + } else { +#if CONFIG_ESP_SYSTEM_MEMPROT && CONFIG_ESP_SYSTEM_MEMPROT_PMP + extern int _iram_text_end; + 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); #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 + 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 + } + + // 4. I_Cache / D_Cache (external flash) + // The marker-based split uses _instruction_reserved_end / _rodata_reserved_end, which lie in + // the flash aperture only when .text/.rodata are flash-resident. Under XIP-from-PSRAM they + // move into PSRAM, so this higher-priority flash entry would shadow the PSRAM RWX entry + // (section 5) and make the heap-reused page-alignment gaps read-only. So split only when + // flash-resident; otherwise map the flash aperture as a single RX window and let section 5 + // govern the XIP code/rodata/gaps. +#if CONFIG_ESP_SYSTEM_MEMPROT && CONFIG_ESP_SYSTEM_MEMPROT_PMP && !CONFIG_SPIRAM_FETCH_INSTRUCTIONS && !CONFIG_SPIRAM_RODATA + 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)); + + 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 + const uint32_t pmpaddr_flash = PMPADDR_NAPOT(SOC_IROM_LOW, SOC_IROM_HIGH); + PMP_ENTRY_SET(6, pmpaddr_flash, PMP_NAPOT | RX); + _Static_assert(SOC_IROM_LOW < SOC_IROM_HIGH, "Invalid I/D_Cache region"); #endif -#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) + // 5. I_EXTRAM / D_EXTRAM (external PSRAM). The PMA entry configured above already makes this + // region cacheable; the PSRAM cache-bypass alias is kept valid by PMA (an address unmatched + // by PMP is allowed by PMP in M-mode, so it needs no extra PMP entry). +#if CONFIG_SPIRAM +#if CONFIG_ESP_SYSTEM_MEMPROT && CONFIG_ESP_SYSTEM_MEMPROT_PMP && (CONFIG_SPIRAM_FETCH_INSTRUCTIONS || CONFIG_SPIRAM_RODATA) + /* XIP-from-PSRAM: .text is executed directly out of PSRAM, so enforce W^X instead of a single + * RWX window. Map [EXTRAM_LOW, _instruction_reserved_end) as RX (code) and the remainder + * (rodata, the page-alignment gaps and the reclaimed heap) as RW - i.e. non-executable - so an + * execute access into the rodata / alignment-gap region faults (Instruction access fault), as + * the memory-protection tests expect. A full P4-style per-section split (separate R rodata and + * heap-only entries) does not fit the 16-entry PMP budget once the LP split takes 4 entries, but + * this two-region split already enforces W^X. It uses entries 7-8 (left free by the flash + * aperture's single-entry mapping under XIP) plus entry 9, programmed in ascending order. */ + extern int _instruction_reserved_end; + PMP_ENTRY_SET(7, SOC_EXTRAM_LOW, NONE); + PMP_ENTRY_SET(8, (uint32_t)(&_instruction_reserved_end), PMP_TOR | RX); + PMP_ENTRY_SET(9, SOC_EXTRAM_HIGH, PMP_TOR | RW); + _Static_assert(SOC_EXTRAM_LOW < SOC_EXTRAM_HIGH, "Invalid I/D_EXTRAM region"); +#else + /* PSRAM used as data only (no XIP): a single RWX NAPOT window. */ + const uint32_t pmpaddr_extram = PMPADDR_NAPOT(SOC_EXTRAM_LOW, SOC_EXTRAM_HIGH); + PMP_ENTRY_SET(9, pmpaddr_extram, PMP_NAPOT | RWX); + _Static_assert(SOC_EXTRAM_LOW < SOC_EXTRAM_HIGH, "Invalid I/D_EXTRAM region"); +#endif +#endif /* CONFIG_SPIRAM */ + + // 6. LP memory (LP TCM / RTC SRAM) +#if CONFIG_ESP_SYSTEM_MEMPROT && CONFIG_ESP_SYSTEM_MEMPROT_PMP + extern int _rtc_text_start; + extern int _rtc_text_end; + /* LP TCM split into three regions (mirroring ESP32-P4 / ESP32-C6): + * [LOW, _rtc_text_start) RW : RTC-reserved mem + ULP/LP-core image (the HP core writes + * the program here, so it must stay writable). + * [_rtc_text_start, _rtc_text_end) RX : HP core rtc.text (e.g. deep-sleep wake stub). + * [_rtc_text_end, HIGH) RW : RTC data. + * Programmed in ascending-address order (S31 faults on overlapping PMP entries); every entry was + * already reset to a disabled state at the top of this function. */ + PMP_ENTRY_SET(10, SOC_RTC_IRAM_LOW, NONE); +#if CONFIG_ESP_SYSTEM_MEMPROT_PMP_LP_CORE_RESERVE_MEM_EXEC + PMP_ENTRY_SET(11, (int)&_rtc_text_start, PMP_TOR | RWX); +#else + PMP_ENTRY_SET(11, (int)&_rtc_text_start, PMP_TOR | RW); +#endif + 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 pmpaddr_rtc = PMPADDR_NAPOT(SOC_RTC_IRAM_LOW, SOC_RTC_IRAM_HIGH); + PMP_ENTRY_SET(10, pmpaddr_rtc, PMP_NAPOT | RWX); + _Static_assert(SOC_RTC_IRAM_LOW < SOC_RTC_IRAM_HIGH, "Invalid RTC IRAM region"); +#endif + + // 7. Peripheral addresses (on-chip peripherals, CPU peripheral, cache-data memory and the + // 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); + _Static_assert(SOC_PERIPHERAL_LOW < SOC_PERIPHERAL_HIGH, "Invalid peripheral region"); +} +#endif // BOOTLOADER_BUILD void esp_cpu_configure_region_protection(void) { - /* TODO: [ESP32S31] IDF-15238 */ + /* Notes on implementation: + * + * 1) Note: ESP32-S31 CPU doesn't support overlapping PMP regions. Hence, like ESP32-H4, we + * must avoid the reset-and-set PMP macros for TOR entries (the transient pmpaddr=0 inside + * them creates a fleeting wrapping overlap that faults on this CPU). We use the non-reset + * PMP_ENTRY_SET and reset the relevant entries up front using PMP_ENTRY_CFG_RESET. + * + * 2) ESP32-S31 supports 16 PMA regions so we use this feature to block all the invalid address + * ranges and to mark the external flash/PSRAM and ROM regions as cacheable. + * + * 3) ESP32-S31 supports 16 PMP regions (writing pmpaddr>=16 raises an illegal-instruction fault, + * confirmed on rev v0.0 silicon). We use a combination of NAPOT (Naturally Aligned Power Of Two) + * and TOR (top of range) entries to map all the valid address space, bottom to top. The whole + * region set must therefore fit within entries 0-15, with the peripherals using the last entry. + * + * 4) Unlike ESP32-P4, the internal memories on ESP32-S31 are not aliased into a cached/uncached + * address pair, so each region needs only a single PMP entry (no CACHED_AND_UNCACHED helper). + */ - __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; + /* 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). */ + esp_cpu_configure_invalid_regions(); - //without setting this, psram cannot be reached - PMA_RESET_AND_ENTRY_SET_NAPOT(7, SOC_EXTRAM_LOW, (SOC_EXTRAM_HIGH - SOC_EXTRAM_LOW), PMA_NAPOT | PMA_RWX); +#ifndef BOOTLOADER_BUILD + esp_cpu_configure_valid_regions(); +#endif } 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 new file mode 100644 index 00000000000..08f644cf1ea --- /dev/null +++ b/components/esp_hw_support/port/esp32s31/private_include/pmp_layout.h @@ -0,0 +1,56 @@ +/* + * 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 diff --git a/components/soc/esp32s31/include/soc/soc.h b/components/soc/esp32s31/include/soc/soc.h index e32604a5f4c..dc47bc6003d 100644 --- a/components/soc/esp32s31/include/soc/soc.h +++ b/components/soc/esp32s31/include/soc/soc.h @@ -193,9 +193,12 @@ #define CPU_PERIPH_LOW 0x2C000000 #define CPU_PERIPH_HIGH 0x2C020000 -// Region of address space that holds peripherals, HP APB peripherals -#define SOC_PERIPHERAL_LOW 0x50000000 //TODO need update -#define SOC_PERIPHERAL_HIGH 0x50100000 //TODO need update +// Region of address space that holds the on-chip peripherals (MODEM/HP/LP APB +// peripherals, security peripherals, CPU peripheral, cache-data memory and the +// debug address space). Per the S31 bus address map this spans from the start of +// the "On-Chip Peripherals" aperture up to the base of the LP TCM (SOC_RTC_IRAM_LOW). +#define SOC_PERIPHERAL_LOW 0x20000000 +#define SOC_PERIPHERAL_HIGH 0x2E000000 /** LP subsystem from ``LP_SYS`` through ``LP_DAC``*/ #define SOC_LP_PERIPH_LOW DR_REG_LP_SYS_BASE diff --git a/tools/test_apps/system/panic/panic_base/pytest_panic.py b/tools/test_apps/system/panic/panic_base/pytest_panic.py index adfdbb438af..52f4ca51c30 100644 --- a/tools/test_apps/system/panic/panic_base/pytest_panic.py +++ b/tools/test_apps/system/panic/panic_base/pytest_panic.py @@ -625,8 +625,9 @@ CONFIGS_MEMPROT_IDRAM = list( 'memprot_esp32h2', 'memprot_esp32p4', 'memprot_esp32h21', + 'memprot_esp32s31', ], - ['esp32s2', 'esp32c3', 'esp32c2', 'esp32c5', 'esp32c61', 'esp32h2', 'esp32p4', 'esp32h21'], + ['esp32s2', 'esp32c3', 'esp32c2', 'esp32c5', 'esp32c61', 'esp32h2', 'esp32p4', 'esp32h21', 'esp32s31'], ) ) + [ ('memprot_esp32p4_rev_less_than_v3', 'esp32p4', P4_REV_LESS_THAN_V3_MARKER), @@ -646,8 +647,9 @@ CONFIGS_MEMPROT_RTC_FAST_MEM = list( 'memprot_esp32h2', 'memprot_esp32p4', 'memprot_esp32h21', + 'memprot_esp32s31', ], - ['esp32s2', 'esp32c3', 'esp32c5', 'esp32c6', 'esp32h2', 'esp32p4', 'esp32h21'], + ['esp32s2', 'esp32c3', 'esp32c5', 'esp32c6', 'esp32h2', 'esp32p4', 'esp32h21', 'esp32s31'], ) ) + [ ('memprot_esp32p4_rev_less_than_v3', 'esp32p4', P4_REV_LESS_THAN_V3_MARKER), @@ -664,8 +666,9 @@ CONFIGS_MEMPROT_FLASH_IDROM = list( 'memprot_esp32h2', 'memprot_esp32p4', 'memprot_esp32h21', + 'memprot_esp32s31', ], - ['esp32c5', 'esp32c6', 'esp32c61', 'esp32h2', 'esp32p4', 'esp32h21'], + ['esp32c5', 'esp32c6', 'esp32c61', 'esp32h2', 'esp32p4', 'esp32h21', 'esp32s31'], ) ) + [ ('memprot_esp32p4_rev_less_than_v3', 'esp32p4', P4_REV_LESS_THAN_V3_MARKER), @@ -679,8 +682,9 @@ CONFIGS_MEMPROT_SPIRAM_XIP_IROM_ALIGNMENT_HEAP = list( 'memprot_spiram_xip_esp32c5', 'memprot_spiram_xip_esp32c61', 'memprot_spiram_xip_esp32p4', + 'memprot_spiram_xip_esp32s31', ], - ['esp32c5', 'esp32c61', 'esp32p4'], + ['esp32c5', 'esp32c61', 'esp32p4', 'esp32s31'], ) ) + [ ('memprot_spiram_xip_esp32p4_rev_less_than_v3', 'esp32p4', P4_REV_LESS_THAN_V3_MARKER), @@ -701,8 +705,9 @@ CONFIGS_MEMPROT_SPIRAM_XIP_DROM_ALIGNMENT_HEAP = list( 'memprot_spiram_xip_esp32c5', 'memprot_spiram_xip_esp32c61', 'memprot_spiram_xip_esp32p4', + 'memprot_spiram_xip_esp32s31', ], - ['esp32s3', 'esp32c5', 'esp32c61', 'esp32p4'], + ['esp32s3', 'esp32c5', 'esp32c61', 'esp32p4', 'esp32s31'], ) ) + [ ('memprot_spiram_xip_esp32p4_rev_less_than_v3', 'esp32p4', P4_REV_LESS_THAN_V3_MARKER), @@ -724,8 +729,9 @@ CONFIGS_MEMPROT_INVALID_REGION_PROTECTION_USING_PMA = list( 'memprot_esp32h2', 'memprot_esp32p4', 'memprot_esp32h21', + 'memprot_esp32s31', ], - ['esp32c5', 'esp32c6', 'esp32c61', 'esp32h2', 'esp32p4', 'esp32h21'], + ['esp32c5', 'esp32c6', 'esp32c61', 'esp32h2', 'esp32p4', 'esp32h21', 'esp32s31'], ) ) + [ ('memprot_esp32p4_rev_less_than_v3', 'esp32p4', P4_REV_LESS_THAN_V3_MARKER), @@ -972,7 +978,7 @@ def test_rtc_fast_reg1_execute_violation(dut: PanicTestDut, test_func_name: str) @pytest.mark.generic @pytest.mark.temp_skip( - targets=['esp32c5', 'esp32c6', 'esp32h2', 'esp32p4', 'esp32h21'], + targets=['esp32c5', 'esp32c6', 'esp32h2', 'esp32p4', 'esp32h21', 'esp32s31'], reason='Not a violation condition, no PMS peripheral cases', ) @idf_parametrize('config, target', CONFIGS_MEMPROT_RTC_FAST_MEM, indirect=['config', 'target']) @@ -1144,6 +1150,7 @@ def spiram_xip_irom_alignment_reg_execute_violation(dut: PanicTestDut, test_func @pytest.mark.generic +@pytest.mark.temp_skip_ci(targets=['esp32s31'], reason='no PSRAM-equipped runner for v0.0 silicon yet') @idf_parametrize('config, target', CONFIGS_MEMPROT_SPIRAM_XIP_IROM_ALIGNMENT_HEAP, indirect=['config', 'target']) def test_spiram_xip_irom_alignment_reg_execute_violation(dut: PanicTestDut, test_func_name: str) -> None: spiram_xip_irom_alignment_reg_execute_violation(dut, test_func_name) @@ -1168,6 +1175,7 @@ def spiram_xip_drom_alignment_reg_execute_violation(dut: PanicTestDut, test_func @pytest.mark.generic +@pytest.mark.temp_skip_ci(targets=['esp32s31'], reason='no PSRAM-equipped runner for v0.0 silicon yet') @idf_parametrize('config, target', CONFIGS_MEMPROT_SPIRAM_XIP_DROM_ALIGNMENT_HEAP, indirect=['config', 'target']) def test_spiram_xip_drom_alignment_reg_execute_violation(dut: PanicTestDut, test_func_name: str) -> None: spiram_xip_drom_alignment_reg_execute_violation(dut, test_func_name) diff --git a/tools/test_apps/system/panic/panic_base/sdkconfig.ci.memprot_esp32s31 b/tools/test_apps/system/panic/panic_base/sdkconfig.ci.memprot_esp32s31 new file mode 100644 index 00000000000..5eb52cface4 --- /dev/null +++ b/tools/test_apps/system/panic/panic_base/sdkconfig.ci.memprot_esp32s31 @@ -0,0 +1,8 @@ +# Restricting to ESP32S31 +CONFIG_IDF_TARGET="esp32s31" + +# Enabling memory protection +CONFIG_ESP_SYSTEM_MEMPROT=y + +# Enable memprot test +CONFIG_TEST_MEMPROT=y diff --git a/tools/test_apps/system/panic/panic_base/sdkconfig.ci.memprot_spiram_xip_esp32s31 b/tools/test_apps/system/panic/panic_base/sdkconfig.ci.memprot_spiram_xip_esp32s31 new file mode 100644 index 00000000000..5a746fb49c8 --- /dev/null +++ b/tools/test_apps/system/panic/panic_base/sdkconfig.ci.memprot_spiram_xip_esp32s31 @@ -0,0 +1,13 @@ +# Restricting to ESP32S31 +CONFIG_IDF_TARGET="esp32s31" + +# 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