mirror of
https://github.com/espressif/esp-idf.git
synced 2026-09-22 13:01:16 +03:00
feat: enable cpu region protection for esp32s31
This commit is contained in:
committed by
Mahavir Jain
parent
d60155a174
commit
b5891edb9b
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user