From 80535b24a0eb145b4b108dd306c54a79005c6964 Mon Sep 17 00:00:00 2001 From: "harshal.patil" Date: Mon, 6 Jul 2026 16:35:13 +0530 Subject: [PATCH 1/4] change(esp_hw_support): configure PMP only in the application and freeze its layout A PMP entry the (non-OTA-updatable) bootloader locks cannot be reconfigured by the application until CPU reset, so the layout of the entries a shipped bootloader locks is a bootloader<->application ABI that renumbering would silently break on deployed devices. On C5, C6, C61, H2 and P4 the bootloader now configures only the PMA invalid regions and leaves PMP to the application. On C5 the application programs the two ROM entries without a cfg reset: v6.0/v6.1 bootloaders lock the TOR base entry at SOC_IROM_MASK_LOW, so on such devices the TOR region above it spans the ROM text and must keep the X bit those bootloaders left in the following unlocked entry, which OR-only writes can never clear. With newer bootloaders the application receives clean entries and the ROM data region gets the intended strict R permission. --- .../port/esp32c5/cpu_region_protect.c | 181 +++++------ .../port/esp32c5/private_include/pmp_layout.h | 40 +++ .../port/esp32c6/cpu_region_protect.c | 298 ++++++++---------- .../port/esp32c6/private_include/pmp_layout.h | 46 +++ .../port/esp32c61/cpu_region_protect.c | 152 ++++----- .../esp32c61/private_include/pmp_layout.h | 42 +++ .../port/esp32h2/cpu_region_protect.c | 284 +++++++---------- .../port/esp32h2/private_include/pmp_layout.h | 46 +++ .../port/esp32p4/cpu_region_protect.c | 126 +++----- .../port/esp32p4/private_include/pmp_layout.h | 51 +++ 10 files changed, 658 insertions(+), 608 deletions(-) create mode 100644 components/esp_hw_support/port/esp32c5/private_include/pmp_layout.h create mode 100644 components/esp_hw_support/port/esp32c6/private_include/pmp_layout.h create mode 100644 components/esp_hw_support/port/esp32c61/private_include/pmp_layout.h create mode 100644 components/esp_hw_support/port/esp32h2/private_include/pmp_layout.h create mode 100644 components/esp_hw_support/port/esp32p4/private_include/pmp_layout.h diff --git a/components/esp_hw_support/port/esp32c5/cpu_region_protect.c b/components/esp_hw_support/port/esp32c5/cpu_region_protect.c index 59f1565897a..7099d4f6a24 100644 --- a/components/esp_hw_support/port/esp32c5/cpu_region_protect.c +++ b/components/esp_hw_support/port/esp32c5/cpu_region_protect.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -14,19 +14,7 @@ #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 -#else -// With L bit set -#define CONDITIONAL_NONE NONE -#define CONDITIONAL_RX RX -#define CONDITIONAL_RW RW -#define CONDITIONAL_RWX RWX -#endif +#include "pmp_layout.h" #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) @@ -92,39 +80,22 @@ static void esp_cpu_configure_invalid_regions(void) #endif } -void esp_cpu_configure_region_protection(void) +#ifndef BOOTLOADER_BUILD +static void esp_cpu_configure_valid_regions(void) { - /* Notes on implementation: + /* There are 3 configuration scenarios for SRAM in the application * - * 1) Note: ESP32-C5 CPU support overlapping PMP regions - * - * 2) ESP32-C5 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_text_end and _data_start markers to set the boundaries. * 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 && CONFIG_ESP_SYSTEM_MEMPROT_PMP 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 @@ -135,43 +106,33 @@ void esp_cpu_configure_region_protection(void) __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(); - - /* NOTE: When ESP-TEE is active, only configure invalid memory regions in bootloader - * to prevent errors before TEE initialization. TEE will handle all other - * memory protection. - */ -#if CONFIG_SECURE_ENABLE_TEE && BOOTLOADER_BUILD - return; -#endif - - // - // Configure all the valid address regions using PMP - // - // 1. CPU Subsystem region - contains 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); + PMP_RESET_AND_ENTRY_SET(PMP_ENTRY_CPU_SUBSYSTEM, 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 && !BOOTLOADER_BUILD +#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 helps us to also configure // the region as cacheable. Thus, we save on one PMP entry. - PMP_ENTRY_SET(1, drom_start, NONE); - PMP_ENTRY_SET(2, SOC_DROM_MASK_HIGH, PMP_TOR | R); + /* No cfg reset on the two ROM entries, deliberately: v6.0/v6.1 + * bootloaders lock PMP_ENTRY_ROM_LOW at SOC_IROM_MASK_LOW, so on such + * devices the TOR region below spans the ROM text and must keep the X + * bit those bootloaders left in PMP_ENTRY_ROM_HIGH. PMP_ENTRY_SET only + * ORs cfg bits and cannot clear it; on newer bootloaders (no PMP + * writes) the entries are clean and ROM data gets the intended R. + */ + 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(1, SOC_IROM_MASK_LOW, NONE); - PMP_ENTRY_SET(2, SOC_IROM_MASK_HIGH, PMP_TOR | CONDITIONAL_RX); + PMP_RESET_AND_ENTRY_SET(PMP_ENTRY_ROM_LOW, SOC_IROM_MASK_LOW, NONE); + PMP_RESET_AND_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"); } @@ -180,38 +141,31 @@ void esp_cpu_configure_region_protection(void) // 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); + PMP_RESET_AND_ENTRY_SET(3, SOC_IRAM_LOW, NONE); + PMP_RESET_AND_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 && !BOOTLOADER_BUILD +#if CONFIG_ESP_SYSTEM_MEMPROT && CONFIG_ESP_SYSTEM_MEMPROT_PMP extern int _iram_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 - */ - PMP_ENTRY_CFG_RESET(3); - PMP_ENTRY_CFG_RESET(4); - PMP_ENTRY_CFG_RESET(5); - 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); + PMP_RESET_AND_ENTRY_SET(3, SOC_IRAM_LOW, NONE); + PMP_RESET_AND_ENTRY_SET(4, (int)&_iram_text_end, PMP_TOR | RX); + PMP_RESET_AND_ENTRY_SET(5, SOC_DRAM_HIGH, PMP_TOR | RW); #else - PMP_ENTRY_SET(3, SOC_IRAM_LOW, CONDITIONAL_NONE); - PMP_ENTRY_SET(4, SOC_IRAM_HIGH, PMP_TOR | CONDITIONAL_RWX); + PMP_RESET_AND_ENTRY_SET(3, SOC_IRAM_LOW, NONE); + PMP_RESET_AND_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 (flash) -#if CONFIG_ESP_SYSTEM_MEMPROT && CONFIG_ESP_SYSTEM_MEMPROT_PMP && !BOOTLOADER_BUILD +#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_CFG_RESET(6); - PMP_ENTRY_SET(6, SOC_IROM_LOW, NONE); + PMP_RESET_AND_ENTRY_SET(6, SOC_IROM_LOW, NONE); /** Virtual space layout: @@ -238,68 +192,85 @@ void esp_cpu_configure_region_protection(void) if CONFIG_SPIRAM: [_rodata_reserved_end, page_aligned_drom_resv_end + available_psram_heap] in heap / reserved for mapping (RW) */ - PMP_ENTRY_CFG_RESET(7); - PMP_ENTRY_CFG_RESET(8); - PMP_ENTRY_CFG_RESET(9); #if CONFIG_SPIRAM_FETCH_INSTRUCTIONS && CONFIG_SPIRAM_PRE_CONFIGURE_MEMORY_PROTECTION - PMP_ENTRY_SET(7, (uint32_t)(&_instruction_reserved_end), PMP_TOR | RX); - PMP_ENTRY_SET(8, page_aligned_irom_resv_end, PMP_TOR | RW); + PMP_RESET_AND_ENTRY_SET(7, (uint32_t)(&_instruction_reserved_end), PMP_TOR | RX); + PMP_RESET_AND_ENTRY_SET(8, page_aligned_irom_resv_end, PMP_TOR | RW); #else - PMP_ENTRY_SET(7, page_aligned_irom_resv_end, PMP_TOR | RX); - PMP_ENTRY_SET(8, page_aligned_irom_resv_end, NONE); + PMP_RESET_AND_ENTRY_SET(7, page_aligned_irom_resv_end, PMP_TOR | RX); + PMP_RESET_AND_ENTRY_SET(8, 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(9, (uint32_t)(&_rodata_reserved_end), PMP_TOR | R); + PMP_RESET_AND_ENTRY_SET(9, (uint32_t)(&_rodata_reserved_end), PMP_TOR | R); #else - PMP_ENTRY_SET(9, page_aligned_drom_resv_end, PMP_TOR | R); + PMP_RESET_AND_ENTRY_SET(9, 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 size_t available_psram_heap = esp_psram_get_heap_size_to_protect(); - PMP_ENTRY_CFG_RESET(10); - PMP_ENTRY_SET(10, ESP_ALIGN_UP(page_aligned_drom_resv_end + available_psram_heap, SOC_CPU_PMP_REGION_GRANULARITY), PMP_TOR | RW); + PMP_RESET_AND_ENTRY_SET(10, 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 pmpaddr6 = PMPADDR_NAPOT(SOC_IROM_LOW, SOC_IROM_HIGH); // Add the W attribute in the case of PSRAM - PMP_ENTRY_SET(6, pmpaddr6, PMP_NAPOT | CONDITIONAL_RWX); + PMP_RESET_AND_ENTRY_SET(6, pmpaddr6, PMP_NAPOT | RWX); _Static_assert(SOC_IROM_LOW < SOC_IROM_HIGH, "Invalid I/D_Cache region"); #endif // 5. LP memory -#if CONFIG_ESP_SYSTEM_MEMPROT && CONFIG_ESP_SYSTEM_MEMPROT_PMP && !BOOTLOADER_BUILD +#if CONFIG_ESP_SYSTEM_MEMPROT && CONFIG_ESP_SYSTEM_MEMPROT_PMP extern int _rtc_text_start; 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 - */ - PMP_ENTRY_CFG_RESET(11); - PMP_ENTRY_CFG_RESET(12); - PMP_ENTRY_CFG_RESET(13); - PMP_ENTRY_CFG_RESET(14); - PMP_ENTRY_SET(11, SOC_RTC_IRAM_LOW, NONE); + PMP_RESET_AND_ENTRY_SET(11, SOC_RTC_IRAM_LOW, NONE); // First part of LP mem is reserved for ULP coprocessor #if CONFIG_ESP_SYSTEM_MEMPROT_PMP_LP_CORE_RESERVE_MEM_EXEC - PMP_ENTRY_SET(12, (int)&_rtc_text_start, PMP_TOR | RWX); + PMP_RESET_AND_ENTRY_SET(12, (int)&_rtc_text_start, PMP_TOR | RWX); #else - PMP_ENTRY_SET(12, (int)&_rtc_text_start, PMP_TOR | RW); + PMP_RESET_AND_ENTRY_SET(12, (int)&_rtc_text_start, PMP_TOR | RW); #endif - PMP_ENTRY_SET(13, (int)&_rtc_text_end, PMP_TOR | RX); - PMP_ENTRY_SET(14, SOC_RTC_IRAM_HIGH, PMP_TOR | RW); + PMP_RESET_AND_ENTRY_SET(13, (int)&_rtc_text_end, PMP_TOR | RX); + PMP_RESET_AND_ENTRY_SET(14, SOC_RTC_IRAM_HIGH, PMP_TOR | RW); #else const uint32_t pmpaddr11 = PMPADDR_NAPOT(SOC_RTC_IRAM_LOW, SOC_RTC_IRAM_HIGH); - PMP_ENTRY_SET(11, pmpaddr11, PMP_NAPOT | CONDITIONAL_RWX); + PMP_RESET_AND_ENTRY_SET(11, pmpaddr11, PMP_NAPOT | RWX); _Static_assert(SOC_RTC_IRAM_LOW < SOC_RTC_IRAM_HIGH, "Invalid RTC IRAM region"); #endif // 6. Peripheral addresses - PMP_ENTRY_CFG_RESET(15); const uint32_t pmpaddr15 = PMPADDR_NAPOT(SOC_PERIPHERAL_LOW, SOC_PERIPHERAL_HIGH); - PMP_ENTRY_SET(15, pmpaddr15, PMP_NAPOT | RW); + PMP_RESET_AND_ENTRY_SET(PMP_ENTRY_PERIPHERAL, 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) Note: ESP32-C5 CPU support overlapping PMP regions + * + * 2) ESP32-C5 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. + */ + + /* 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 apply to M-mode, and a + * locked one would survive into the application with no way to reconfigure + * it until the next CPU reset). */ + esp_cpu_configure_invalid_regions(); + +#ifndef BOOTLOADER_BUILD + esp_cpu_configure_valid_regions(); +#endif +} diff --git a/components/esp_hw_support/port/esp32c5/private_include/pmp_layout.h b/components/esp_hw_support/port/esp32c5/private_include/pmp_layout.h new file mode 100644 index 00000000000..89ac208c5d6 --- /dev/null +++ b/components/esp_hw_support/port/esp32c5/private_include/pmp_layout.h @@ -0,0 +1,40 @@ +/* + * 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-C5 PMP entry layout: a bootloader<->application ABI. An entry the + * (non-updatable) bootloader locks cannot be reconfigured until CPU reset, so + * the index of any entry a shipped bootloader locks is frozen. + * + * Entries locked by shipped bootloader generations (MP baseline v5.5): + * v5.5 : 0, 1-2 (ROM), 15 (peripherals) + * v6.0 / v6.1 : 0, 1 (ROM), 15 (peripherals) + * >= v6.2 : none + */ +#define PMP_ENTRY_CPU_SUBSYSTEM 0 /* NAPOT RWX */ +#define PMP_ENTRY_ROM_LOW 1 /* TOR base */ +#define PMP_ENTRY_ROM_HIGH 2 /* TOR R or RX */ +/* 3..14: application-owned SRAM/flash/LP-RAM split, programmed by plain index + * in cpu_region_protect.c; not part of the ABI, not frozen here. */ +#define PMP_ENTRY_PERIPHERAL 15 /* NAPOT RW */ + +ESP_STATIC_ASSERT(PMP_ENTRY_CPU_SUBSYSTEM == 0 + && PMP_ENTRY_ROM_LOW == 1 && PMP_ENTRY_ROM_HIGH == 2 + && PMP_ENTRY_PERIPHERAL == 15, + "Entries locked by shipped bootloaders are a frozen ABI and must never move"); + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_hw_support/port/esp32c6/cpu_region_protect.c b/components/esp_hw_support/port/esp32c6/cpu_region_protect.c index cea14dc5d2c..bdcf5c334a9 100644 --- a/components/esp_hw_support/port/esp32c6/cpu_region_protect.c +++ b/components/esp_hw_support/port/esp32c6/cpu_region_protect.c @@ -10,22 +10,7 @@ #include "esp_cpu.h" #include "esp_fault.h" #include "esp_macros.h" - -#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 -#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 -#endif +#include "pmp_layout.h" #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) @@ -69,6 +54,125 @@ static void esp_cpu_configure_invalid_regions(void) PMA_ENTRY_CFG_RESET(15); } +#ifndef BOOTLOADER_BUILD +static void esp_cpu_configure_valid_regions(void) +{ + /* There are 3 configuration scenarios for SRAM in the application + * + * 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_text_end and _data_start markers to set the boundaries. + * 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; + 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; + + // 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_RESET_AND_ENTRY_SET(PMP_ENTRY_CPU_SUBSYSTEM, pmpaddr0, PMP_NAPOT | RWX); + _Static_assert(SOC_CPU_SUBSYSTEM_LOW < SOC_CPU_SUBSYSTEM_HIGH, "Invalid CPU subsystem region"); + + // 2.1 I-ROM + PMP_RESET_AND_ENTRY_SET(PMP_ENTRY_IROM_LOW, SOC_IROM_MASK_LOW, NONE); + PMP_RESET_AND_ENTRY_SET(PMP_ENTRY_IROM_HIGH, 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 - redundant with the I-ROM entry above (same range, already + * locked RX); kept as a separate pair only to pin the app's IRAM/DRAM split + * to entries 5-7. + * + * C6's ROM mask is not a power of two, so ROM needs a TOR pair on entries + * 1-2. Commit d4167fea60c (v6.0) dropped this D-ROM pair and moved the split + * down onto entries 3-4 - which bootloaders up to v5.2.1 lock as D-ROM. A + * locked PMP entry can't be reconfigured until CPU reset, so such an app + * can't gain IRAM execute permission and resets before app_main(). + * v5.2.2 freed 3-4, so only pre-v5.2.2 C6 bootloaders break. + */ + PMP_RESET_AND_ENTRY_SET(PMP_ENTRY_DROM_LOW, SOC_DROM_MASK_LOW, NONE); + PMP_RESET_AND_ENTRY_SET(PMP_ENTRY_DROM_HIGH, 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()); + + // 3. IRAM and DRAM + const uint32_t pmpaddr5 = PMPADDR_NAPOT(SOC_IRAM_LOW, SOC_IRAM_HIGH); + PMP_RESET_AND_ENTRY_SET(5, pmpaddr5, PMP_NAPOT | 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; + // 3. IRAM and DRAM + PMP_RESET_AND_ENTRY_SET(5, SOC_IRAM_LOW, NONE); + PMP_RESET_AND_ENTRY_SET(6, (int)&_iram_text_end, PMP_TOR | RX); + PMP_RESET_AND_ENTRY_SET(7, SOC_DRAM_HIGH, PMP_TOR | RW); +#else + // 3. IRAM and DRAM + const uint32_t pmpaddr5 = PMPADDR_NAPOT(SOC_IRAM_LOW, SOC_IRAM_HIGH); + PMP_RESET_AND_ENTRY_SET(5, pmpaddr5, PMP_NAPOT | RWX); + _Static_assert(SOC_IRAM_LOW < SOC_IRAM_HIGH, "Invalid RAM region"); +#endif + } + +#if CONFIG_ESP_SYSTEM_MEMPROT && CONFIG_ESP_SYSTEM_MEMPROT_PMP + extern int _instruction_reserved_end; + extern int _rodata_reserved_end; + + const uint32_t irom_resv_end = ALIGN_UP_TO_MMU_PAGE_SIZE((uint32_t)(&_instruction_reserved_end)); + const uint32_t drom_resv_end = ALIGN_UP_TO_MMU_PAGE_SIZE((uint32_t)(&_rodata_reserved_end)); + + // 4. I_Cache / D_Cache (flash) + PMP_RESET_AND_ENTRY_SET(8, SOC_IROM_LOW, NONE); + PMP_RESET_AND_ENTRY_SET(9, irom_resv_end, PMP_TOR | RX); + PMP_RESET_AND_ENTRY_SET(10, drom_resv_end, PMP_TOR | R); +#else + // 4. I_Cache / D_Cache (flash) + const uint32_t pmpaddr8 = PMPADDR_NAPOT(SOC_IROM_LOW, SOC_IROM_HIGH); + PMP_RESET_AND_ENTRY_SET(8, pmpaddr8, PMP_NAPOT | RX); + _Static_assert(SOC_IROM_LOW < SOC_IROM_HIGH, "Invalid I/D_Cache region"); +#endif + + // 5. LP memory +#if CONFIG_ESP_SYSTEM_MEMPROT && CONFIG_ESP_SYSTEM_MEMPROT_PMP + extern int _rtc_text_start; + extern int _rtc_text_end; + PMP_RESET_AND_ENTRY_SET(11, SOC_RTC_IRAM_LOW, NONE); + + // First part of LP mem is reserved for ULP coprocessor +#if CONFIG_ESP_SYSTEM_MEMPROT_PMP_LP_CORE_RESERVE_MEM_EXEC + PMP_RESET_AND_ENTRY_SET(12, (int)&_rtc_text_start, PMP_TOR | RWX); +#else + PMP_RESET_AND_ENTRY_SET(12, (int)&_rtc_text_start, PMP_TOR | RW); +#endif + PMP_RESET_AND_ENTRY_SET(13, (int)&_rtc_text_end, PMP_TOR | RX); + PMP_RESET_AND_ENTRY_SET(14, SOC_RTC_IRAM_HIGH, PMP_TOR | RW); +#else + const uint32_t pmpaddr11 = PMPADDR_NAPOT(SOC_RTC_IRAM_LOW, SOC_RTC_IRAM_HIGH); + PMP_RESET_AND_ENTRY_SET(11, pmpaddr11, PMP_NAPOT | RWX); + _Static_assert(SOC_RTC_IRAM_LOW < SOC_RTC_IRAM_HIGH, "Invalid RTC IRAM region"); +#endif + + // 6. Peripheral addresses + const uint32_t pmpaddr15 = PMPADDR_NAPOT(SOC_PERIPHERAL_LOW, SOC_PERIPHERAL_HIGH); + PMP_RESET_AND_ENTRY_SET(PMP_ENTRY_PERIPHERAL, 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: @@ -85,162 +189,14 @@ void esp_cpu_configure_region_protection(void) * 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 - * - 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_text_end and _data_start markers to set the boundaries. - * 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 - * - 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 - * - 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; - 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; - - // - // Configure all the invalid address regions using PMA - // + /* 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 apply to M-mode, and a + * locked one would survive into the application with no way to reconfigure + * it until the next CPU reset). */ esp_cpu_configure_invalid_regions(); - /* NOTE: When ESP-TEE is active, only configure invalid memory regions in bootloader - * to prevent errors before TEE initialization. TEE will handle all other - * memory protection. - */ -#if CONFIG_SECURE_ENABLE_TEE && BOOTLOADER_BUILD - return; +#ifndef BOOTLOADER_BUILD + esp_cpu_configure_valid_regions(); #endif - - // - // Configure all the valid address regions using PMP - // - - // 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.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 - redundant with the I-ROM entry above (same range, already - * locked RX); kept as a separate pair only to pin the app's IRAM/DRAM split - * to entries 5-7. - * - * C6's ROM mask is not a power of two, so ROM needs a TOR pair on entries - * 1-2. Commit d4167fea60c (v6.0) dropped this D-ROM pair and moved the split - * down onto entries 3-4 - which bootloaders up to v5.2.1 lock as D-ROM. A - * locked PMP entry can't be reconfigured until CPU reset, so such an app - * can't gain IRAM execute permission and resets before app_main() (GitHub - * #18769). v5.2.2 freed 3-4, so only pre-v5.2.2 C6 bootloaders break. - * - * CFG_RESET clears stale config a v6.0/v6.0.1 bootloader left here - * (PMP_ENTRY_SET only ORs bits); no-op once 3-4 are locked. CONDITIONAL_* - * keeps 3-4 locked in the app but unlocked in the bootloader (redundant, so - * no protection lost), letting a future app reclaim them once pre-v5.2.2 - * bootloaders are out of support. - */ - PMP_ENTRY_CFG_RESET(3); - PMP_ENTRY_CFG_RESET(4); - PMP_ENTRY_SET(3, SOC_DROM_MASK_LOW, CONDITIONAL_NONE); - PMP_ENTRY_SET(4, SOC_DROM_MASK_HIGH, PMP_TOR | CONDITIONAL_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()); - - // 3. 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"); - } else { -#if CONFIG_ESP_SYSTEM_MEMPROT && CONFIG_ESP_SYSTEM_MEMPROT_PMP && !BOOTLOADER_BUILD - extern int _iram_text_end; - // 3. 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_text_end, PMP_TOR | RX); - PMP_ENTRY_SET(7, SOC_DRAM_HIGH, PMP_TOR | RW); -#else - // 3. 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"); -#endif - } - -#if CONFIG_ESP_SYSTEM_MEMPROT && CONFIG_ESP_SYSTEM_MEMPROT_PMP && !BOOTLOADER_BUILD - extern int _instruction_reserved_end; - extern int _rodata_reserved_end; - - const uint32_t irom_resv_end = ALIGN_UP_TO_MMU_PAGE_SIZE((uint32_t)(&_instruction_reserved_end)); - const uint32_t drom_resv_end = ALIGN_UP_TO_MMU_PAGE_SIZE((uint32_t)(&_rodata_reserved_end)); - - // 4. I_Cache / D_Cache (flash) - PMP_ENTRY_CFG_RESET(8); - PMP_ENTRY_CFG_RESET(9); - PMP_ENTRY_CFG_RESET(10); - PMP_ENTRY_SET(8, SOC_IROM_LOW, NONE); - PMP_ENTRY_SET(9, irom_resv_end, PMP_TOR | RX); - PMP_ENTRY_SET(10, drom_resv_end, PMP_TOR | R); -#else - // 4. I_Cache / D_Cache (flash) - const uint32_t pmpaddr8 = PMPADDR_NAPOT(SOC_IROM_LOW, SOC_IROM_HIGH); - PMP_ENTRY_SET(8, pmpaddr8, PMP_NAPOT | CONDITIONAL_RX); - _Static_assert(SOC_IROM_LOW < SOC_IROM_HIGH, "Invalid I/D_Cache region"); -#endif - - // 5. LP memory -#if CONFIG_ESP_SYSTEM_MEMPROT && CONFIG_ESP_SYSTEM_MEMPROT_PMP && !BOOTLOADER_BUILD - extern int _rtc_text_start; - 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 - */ - PMP_ENTRY_CFG_RESET(11); - PMP_ENTRY_CFG_RESET(12); - PMP_ENTRY_CFG_RESET(13); - PMP_ENTRY_CFG_RESET(14); - PMP_ENTRY_SET(11, SOC_RTC_IRAM_LOW, NONE); - - // First part of LP mem is reserved for ULP coprocessor -#if CONFIG_ESP_SYSTEM_MEMPROT_PMP_LP_CORE_RESERVE_MEM_EXEC - PMP_ENTRY_SET(12, (int)&_rtc_text_start, PMP_TOR | RWX); -#else - PMP_ENTRY_SET(12, (int)&_rtc_text_start, PMP_TOR | RW); -#endif - PMP_ENTRY_SET(13, (int)&_rtc_text_end, PMP_TOR | RX); - PMP_ENTRY_SET(14, SOC_RTC_IRAM_HIGH, PMP_TOR | RW); -#else - const uint32_t pmpaddr11 = PMPADDR_NAPOT(SOC_RTC_IRAM_LOW, SOC_RTC_IRAM_HIGH); - PMP_ENTRY_SET(11, pmpaddr11, PMP_NAPOT | CONDITIONAL_RWX); - _Static_assert(SOC_RTC_IRAM_LOW < SOC_RTC_IRAM_HIGH, "Invalid RTC IRAM region"); -#endif - - // 6. 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"); } diff --git a/components/esp_hw_support/port/esp32c6/private_include/pmp_layout.h b/components/esp_hw_support/port/esp32c6/private_include/pmp_layout.h new file mode 100644 index 00000000000..a3b36337b4d --- /dev/null +++ b/components/esp_hw_support/port/esp32c6/private_include/pmp_layout.h @@ -0,0 +1,46 @@ +/* + * 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-C6 PMP entry layout: a bootloader<->application ABI. An entry the + * (non-updatable) bootloader locks cannot be reconfigured until CPU reset, so + * the index of any entry a shipped bootloader locks is frozen. + * The layout below matches v5.2.2..v5.5; older generations that locked a + * different value on a shared index cannot be helped (their lock wins). + * + * Entries locked by shipped bootloader generations: + * <= v5.2.1 : 0, 1-2 (I-ROM), 3-4 (D-ROM), 8-9 (cache), 14 (periph) + * v5.2.2 .. v5.5 : 0, 1-2 (I-ROM), 15 (peripherals) + * v6.0 / v6.1 : 0, 1-2 (I-ROM), 13 (peripherals) + * >= v6.2 : none + */ +#define PMP_ENTRY_CPU_SUBSYSTEM 0 /* NAPOT RWX */ +#define PMP_ENTRY_IROM_LOW 1 /* TOR base */ +#define PMP_ENTRY_IROM_HIGH 2 /* TOR RX */ +#define PMP_ENTRY_DROM_LOW 3 /* TOR base (locked by <= v5.2.1) */ +#define PMP_ENTRY_DROM_HIGH 4 /* TOR R (locked by <= v5.2.1) */ +/* 5..14: application-owned SRAM/flash/LP-RAM split, programmed by plain index + * in cpu_region_protect.c; not part of the ABI, not frozen here. */ +#define PMP_ENTRY_PERIPHERAL 15 /* NAPOT RW */ + +ESP_STATIC_ASSERT(PMP_ENTRY_CPU_SUBSYSTEM == 0 + && PMP_ENTRY_IROM_LOW == 1 && PMP_ENTRY_IROM_HIGH == 2 + && PMP_ENTRY_DROM_LOW == 3 && PMP_ENTRY_DROM_HIGH == 4 + && PMP_ENTRY_PERIPHERAL == 15, + "Entries locked by shipped bootloaders are a frozen ABI and must never move"); + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_hw_support/port/esp32c61/cpu_region_protect.c b/components/esp_hw_support/port/esp32c61/cpu_region_protect.c index 816813b2ba3..eccc24ba5db 100644 --- a/components/esp_hw_support/port/esp32c61/cpu_region_protect.c +++ b/components/esp_hw_support/port/esp32c61/cpu_region_protect.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -15,19 +15,7 @@ #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 -#else -// With L bit set -#define CONDITIONAL_NONE NONE -#define CONDITIONAL_RX RX -#define CONDITIONAL_RW RW -#define CONDITIONAL_RWX RWX -#endif +#include "pmp_layout.h" #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) @@ -76,39 +64,22 @@ static void esp_cpu_configure_invalid_regions(void) PMA_ENTRY_CFG_RESET(15); } -void esp_cpu_configure_region_protection(void) +#ifndef BOOTLOADER_BUILD +static void esp_cpu_configure_valid_regions(void) { - /* Notes on implementation: + /* There are 3 configuration scenarios for SRAM in the application * - * 1) Note: ESP32-C61 CPU supports overlapping PMP regions - * - * 2) ESP32-C61 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_text_end and _data_start markers to set the boundaries. * 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 @@ -119,40 +90,23 @@ void esp_cpu_configure_region_protection(void) __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(); - - /* NOTE: When ESP-TEE is active, only configure invalid memory regions in bootloader - * to prevent errors before TEE initialization. TEE will handle all other - * memory protection. - */ -#if CONFIG_SECURE_ENABLE_TEE && BOOTLOADER_BUILD - return; -#endif - - // - // Configure all the valid address regions using PMP - // - // 1. CPU Subsystem region - contains 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); + PMP_RESET_AND_ENTRY_SET(PMP_ENTRY_CPU_SUBSYSTEM, 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 && !BOOTLOADER_BUILD +#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) { - 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); + PMP_RESET_AND_ENTRY_SET(PMP_ENTRY_ROM_LOW, SOC_IROM_MASK_LOW, NONE); + PMP_RESET_AND_ENTRY_SET(PMP_ENTRY_ROM_TEXT_HIGH, drom_start, PMP_TOR | RX); + PMP_RESET_AND_ENTRY_SET(PMP_ENTRY_ROM_HIGH, SOC_DROM_MASK_HIGH, PMP_TOR | R); } else #endif { const uint32_t pmpaddr1 = PMPADDR_NAPOT(SOC_IROM_MASK_LOW, SOC_IROM_MASK_HIGH); - PMP_ENTRY_SET(1, pmpaddr1, PMP_NAPOT | CONDITIONAL_RX); + PMP_RESET_AND_ENTRY_SET(PMP_ENTRY_ROM_LOW, pmpaddr1, PMP_NAPOT | RX); _Static_assert(SOC_IROM_MASK_LOW < SOC_IROM_MASK_HIGH, "Invalid I/D-ROM region"); } @@ -161,38 +115,31 @@ void esp_cpu_configure_region_protection(void) // 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_IRAM_HIGH, PMP_TOR | RWX); + PMP_RESET_AND_ENTRY_SET(4, SOC_IRAM_LOW, NONE); + PMP_RESET_AND_ENTRY_SET(5, 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 +#if CONFIG_ESP_SYSTEM_MEMPROT && CONFIG_ESP_SYSTEM_MEMPROT_PMP extern int _iram_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 - */ - PMP_ENTRY_CFG_RESET(4); - PMP_ENTRY_CFG_RESET(5); - PMP_ENTRY_CFG_RESET(6); - PMP_ENTRY_SET(4, SOC_IRAM_LOW, NONE); - PMP_ENTRY_SET(5, (int)&_iram_text_end, PMP_TOR | RX); - PMP_ENTRY_SET(6, SOC_DRAM_HIGH, PMP_TOR | RW); + PMP_RESET_AND_ENTRY_SET(4, SOC_IRAM_LOW, NONE); + PMP_RESET_AND_ENTRY_SET(5, (int)&_iram_text_end, PMP_TOR | RX); + PMP_RESET_AND_ENTRY_SET(6, SOC_DRAM_HIGH, PMP_TOR | RW); #else - PMP_ENTRY_SET(4, SOC_IRAM_LOW, CONDITIONAL_NONE); - PMP_ENTRY_SET(5, SOC_IRAM_HIGH, PMP_TOR | CONDITIONAL_RWX); + PMP_RESET_AND_ENTRY_SET(4, SOC_IRAM_LOW, NONE); + PMP_RESET_AND_ENTRY_SET(5, SOC_IRAM_HIGH, PMP_TOR | RWX); _Static_assert(SOC_IRAM_LOW < SOC_IRAM_HIGH, "Invalid RAM region"); #endif } // 4. I_Cache / D_Cache (flash) -#if CONFIG_ESP_SYSTEM_MEMPROT && CONFIG_ESP_SYSTEM_MEMPROT_PMP && !BOOTLOADER_BUILD +#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_CFG_RESET(7); - PMP_ENTRY_SET(7, SOC_IROM_LOW, NONE); + PMP_RESET_AND_ENTRY_SET(7, SOC_IROM_LOW, NONE); /** Virtual space layout: @@ -219,38 +166,63 @@ void esp_cpu_configure_region_protection(void) if CONFIG_SPIRAM: [_rodata_reserved_end, page_aligned_drom_resv_end + available_psram_heap] in heap / reserved for mapping (RW) */ - PMP_ENTRY_CFG_RESET(8); - PMP_ENTRY_CFG_RESET(9); - PMP_ENTRY_CFG_RESET(10); #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); + PMP_RESET_AND_ENTRY_SET(8, (uint32_t)(&_instruction_reserved_end), PMP_TOR | RX); + PMP_RESET_AND_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); + PMP_RESET_AND_ENTRY_SET(8, page_aligned_irom_resv_end, PMP_TOR | RX); + PMP_RESET_AND_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); + PMP_RESET_AND_ENTRY_SET(10, (uint32_t)(&_rodata_reserved_end), PMP_TOR | R); #else - PMP_ENTRY_SET(10, page_aligned_drom_resv_end, PMP_TOR | R); + PMP_RESET_AND_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 size_t available_psram_heap = esp_psram_get_heap_size_to_protect(); - PMP_ENTRY_CFG_RESET(11); - PMP_ENTRY_SET(11, ESP_ALIGN_UP(page_aligned_drom_resv_end + available_psram_heap, SOC_CPU_PMP_REGION_GRANULARITY), PMP_TOR | RW); + PMP_RESET_AND_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); // Add the W attribute in the case of PSRAM - PMP_ENTRY_SET(7, pmpaddr7, PMP_NAPOT | CONDITIONAL_RWX); + PMP_RESET_AND_ENTRY_SET(7, pmpaddr7, PMP_NAPOT | RWX); _Static_assert(SOC_IROM_LOW < SOC_IROM_HIGH, "Invalid I/D_Cache region"); #endif // 5. Peripheral addresses const uint32_t pmpaddr12 = PMPADDR_NAPOT(SOC_PERIPHERAL_LOW, SOC_PERIPHERAL_HIGH); - PMP_ENTRY_SET(12, pmpaddr12, PMP_NAPOT | RW); + PMP_RESET_AND_ENTRY_SET(PMP_ENTRY_PERIPHERAL, pmpaddr12, 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) Note: ESP32-C61 CPU supports overlapping PMP regions + * + * 2) ESP32-C61 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. + */ + + /* 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 apply to M-mode, and a + * locked one would survive into the application with no way to reconfigure + * it until the next CPU reset). */ + esp_cpu_configure_invalid_regions(); + +#ifndef BOOTLOADER_BUILD + esp_cpu_configure_valid_regions(); +#endif +} diff --git a/components/esp_hw_support/port/esp32c61/private_include/pmp_layout.h b/components/esp_hw_support/port/esp32c61/private_include/pmp_layout.h new file mode 100644 index 00000000000..5b6c056a605 --- /dev/null +++ b/components/esp_hw_support/port/esp32c61/private_include/pmp_layout.h @@ -0,0 +1,42 @@ +/* + * 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-C61 PMP entry layout: a bootloader<->application ABI. An entry the + * (non-updatable) bootloader locks cannot be reconfigured until CPU reset, so + * the index of any entry a shipped bootloader locks is frozen. + * + * Entries locked by shipped bootloader generations (MP baseline v5.5): + * v5.5 : 0, 1-3 (ROM), 12 (peripherals) + * v6.0 / v6.1 : 0, 12 (peripherals) + * >= v6.2 : none + */ +#define PMP_ENTRY_CPU_SUBSYSTEM 0 /* NAPOT RWX */ +#define PMP_ENTRY_ROM_LOW 1 /* TOR base, or NAPOT RX (unaligned branch) */ +#define PMP_ENTRY_ROM_TEXT_HIGH 2 /* TOR RX (aligned branch) */ +#define PMP_ENTRY_ROM_HIGH 3 /* TOR R (aligned branch) */ +/* 4..11: application-owned SRAM/flash split, programmed by plain index in + * cpu_region_protect.c; not part of the ABI, not frozen here. */ +#define PMP_ENTRY_PERIPHERAL 12 /* NAPOT RW */ + +ESP_STATIC_ASSERT(PMP_ENTRY_CPU_SUBSYSTEM == 0 + && PMP_ENTRY_ROM_LOW == 1 && PMP_ENTRY_ROM_TEXT_HIGH == 2 + && PMP_ENTRY_ROM_HIGH == 3 + && PMP_ENTRY_PERIPHERAL == 12, + "Entries locked by shipped bootloaders are a frozen ABI and must never move"); + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_hw_support/port/esp32h2/cpu_region_protect.c b/components/esp_hw_support/port/esp32h2/cpu_region_protect.c index b0fa5acda32..1bc766510a1 100644 --- a/components/esp_hw_support/port/esp32h2/cpu_region_protect.c +++ b/components/esp_hw_support/port/esp32h2/cpu_region_protect.c @@ -10,22 +10,7 @@ #include "esp_cpu.h" #include "esp_fault.h" #include "esp_macros.h" - -#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 -#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 -#endif +#include "pmp_layout.h" #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) @@ -69,6 +54,118 @@ static void esp_cpu_configure_invalid_regions(void) PMA_ENTRY_CFG_RESET(15); } +#ifndef BOOTLOADER_BUILD +static void esp_cpu_configure_valid_regions(void) +{ + /* There are 3 configuration scenarios for SRAM in the application + * + * 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_text_end and _data_start markers to set the boundaries. + * 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; + 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; + + // 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_RESET_AND_ENTRY_SET(PMP_ENTRY_CPU_SUBSYSTEM, pmpaddr0, PMP_NAPOT | RWX); + _Static_assert(SOC_CPU_SUBSYSTEM_LOW < SOC_CPU_SUBSYSTEM_HIGH, "Invalid CPU subsystem region"); + + // 2.1 I-ROM + PMP_RESET_AND_ENTRY_SET(PMP_ENTRY_IROM_LOW, SOC_IROM_MASK_LOW, NONE); + PMP_RESET_AND_ENTRY_SET(PMP_ENTRY_IROM_HIGH, 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 - redundant with the I-ROM entry above (same range, already + * locked RX); kept as a separate pair only to pin the app's IRAM/DRAM split + * to entries 5-7. + * + * H2's ROM mask is a power of two, so commit d4167fea60c (v6.0) folded ROM + * into a single NAPOT entry (1) and moved the split down onto entry 2 - + * which every pre-v6.0 bootloader locks as the I-ROM TOR top. A locked PMP + * entry can't be reconfigured until CPU reset, so such an app can't gain + * IRAM execute permission and resets before app_main(). + * ROM was never a single NAPOT before v6.0, so ALL pre-v6.0 bootloaders + * break (unlike C6, where only pre-v5.2.2 do). + */ + PMP_RESET_AND_ENTRY_SET(PMP_ENTRY_DROM_LOW, SOC_DROM_MASK_LOW, NONE); + PMP_RESET_AND_ENTRY_SET(PMP_ENTRY_DROM_HIGH, 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()); + + // 3. IRAM and DRAM + PMP_RESET_AND_ENTRY_SET(5, SOC_IRAM_LOW, NONE); + PMP_RESET_AND_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 + extern int _iram_text_end; + // 3. IRAM and DRAM + PMP_RESET_AND_ENTRY_SET(5, SOC_IRAM_LOW, NONE); + PMP_RESET_AND_ENTRY_SET(6, (int)&_iram_text_end, PMP_TOR | RX); + PMP_RESET_AND_ENTRY_SET(7, SOC_DRAM_HIGH, PMP_TOR | RW); +#else + // 3. IRAM and DRAM + PMP_RESET_AND_ENTRY_SET(5, SOC_IRAM_LOW, NONE); + PMP_RESET_AND_ENTRY_SET(6, SOC_IRAM_HIGH, PMP_TOR | RWX); + _Static_assert(SOC_IRAM_LOW < SOC_IRAM_HIGH, "Invalid RAM region"); +#endif + } + +#if CONFIG_ESP_SYSTEM_MEMPROT && CONFIG_ESP_SYSTEM_MEMPROT_PMP + extern int _instruction_reserved_end; + extern int _rodata_reserved_end; + + const uint32_t irom_resv_end = ALIGN_UP_TO_MMU_PAGE_SIZE((uint32_t)(&_instruction_reserved_end)); + const uint32_t drom_resv_end = ALIGN_UP_TO_MMU_PAGE_SIZE((uint32_t)(&_rodata_reserved_end)); + + // 4. I_Cache / D_Cache (flash) + PMP_RESET_AND_ENTRY_SET(8, SOC_IROM_LOW, NONE); + PMP_RESET_AND_ENTRY_SET(9, irom_resv_end, PMP_TOR | RX); + PMP_RESET_AND_ENTRY_SET(10, drom_resv_end, PMP_TOR | R); +#else + // 4. I_Cache / D_Cache (flash) + const uint32_t pmpaddr8 = PMPADDR_NAPOT(SOC_IROM_LOW, SOC_IROM_HIGH); + PMP_RESET_AND_ENTRY_SET(8, pmpaddr8, PMP_NAPOT | RX); + _Static_assert(SOC_IROM_LOW < SOC_IROM_HIGH, "Invalid I/D_Cache region"); +#endif + + // 5. LP memory +#if CONFIG_ESP_SYSTEM_MEMPROT && CONFIG_ESP_SYSTEM_MEMPROT_PMP + extern int _rtc_text_end; + PMP_RESET_AND_ENTRY_SET(11, SOC_RTC_IRAM_LOW, NONE); + PMP_RESET_AND_ENTRY_SET(12, (int)&_rtc_text_end, PMP_TOR | RX); + PMP_RESET_AND_ENTRY_SET(13, SOC_RTC_IRAM_HIGH, PMP_TOR | RW); +#else + const uint32_t pmpaddr11 = PMPADDR_NAPOT(SOC_RTC_IRAM_LOW, SOC_RTC_IRAM_HIGH); + PMP_RESET_AND_ENTRY_SET(11, pmpaddr11, PMP_NAPOT | RWX); + _Static_assert(SOC_RTC_IRAM_LOW < SOC_RTC_IRAM_HIGH, "Invalid RTC IRAM region"); +#endif + + // 6. Peripheral addresses + const uint32_t pmpaddr14 = PMPADDR_NAPOT(SOC_PERIPHERAL_LOW, SOC_PERIPHERAL_HIGH); + PMP_RESET_AND_ENTRY_SET(PMP_ENTRY_PERIPHERAL, pmpaddr14, 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: @@ -85,155 +182,14 @@ void esp_cpu_configure_region_protection(void) * 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 - * - 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_text_end and _data_start markers to set the boundaries. - * 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 - * - 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 - * - 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; - 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; - - // - // Configure all the invalid address regions using PMA - // + /* 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 apply to M-mode, and a + * locked one would survive into the application with no way to reconfigure + * it until the next CPU reset). */ esp_cpu_configure_invalid_regions(); - /* NOTE: When ESP-TEE is active, only configure invalid memory regions in bootloader - * to prevent errors before TEE initialization. TEE will handle all other - * memory protection. - */ -#if CONFIG_SECURE_ENABLE_TEE && BOOTLOADER_BUILD - return; +#ifndef BOOTLOADER_BUILD + esp_cpu_configure_valid_regions(); #endif - - // - // Configure all the valid address regions using PMP - // - - // 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.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 - redundant with the I-ROM entry above (same range, already - * locked RX); kept as a separate pair only to pin the app's IRAM/DRAM split - * to entries 5-7. - * - * H2's ROM mask is a power of two, so commit d4167fea60c (v6.0) folded ROM - * into a single NAPOT entry (1) and moved the split down onto entry 2 - - * which every pre-v6.0 bootloader locks as the I-ROM TOR top. A locked PMP - * entry can't be reconfigured until CPU reset, so such an app can't gain - * IRAM execute permission and resets before app_main() (GitHub #18769). - * ROM was never a single NAPOT before v6.0, so ALL pre-v6.0 bootloaders - * break (unlike C6, where only pre-v5.2.2 do). - * - * CFG_RESET clears stale config a v6.0/v6.0.1 bootloader left here - * (PMP_ENTRY_SET only ORs bits); no-op once these are locked. CONDITIONAL_* - * keeps 3-4 locked in the app but unlocked in the bootloader (redundant, so - * no protection lost), letting a future app reclaim them once pre-v5.2.3 - * bootloaders are out of support. - */ - PMP_ENTRY_CFG_RESET(3); - PMP_ENTRY_CFG_RESET(4); - PMP_ENTRY_SET(3, SOC_DROM_MASK_LOW, CONDITIONAL_NONE); - PMP_ENTRY_SET(4, SOC_DROM_MASK_HIGH, PMP_TOR | CONDITIONAL_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()); - - // 3. IRAM and DRAM - 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_text_end; - // 3. 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_text_end, PMP_TOR | RX); - PMP_ENTRY_SET(7, SOC_DRAM_HIGH, PMP_TOR | RW); -#else - // 3. IRAM and DRAM - PMP_ENTRY_SET(5, SOC_IRAM_LOW, CONDITIONAL_NONE); - PMP_ENTRY_SET(6, SOC_IRAM_HIGH, PMP_TOR | CONDITIONAL_RWX); - _Static_assert(SOC_IRAM_LOW < SOC_IRAM_HIGH, "Invalid RAM region"); -#endif - } - -#if CONFIG_ESP_SYSTEM_MEMPROT && CONFIG_ESP_SYSTEM_MEMPROT_PMP && !BOOTLOADER_BUILD - extern int _instruction_reserved_end; - extern int _rodata_reserved_end; - - const uint32_t irom_resv_end = ALIGN_UP_TO_MMU_PAGE_SIZE((uint32_t)(&_instruction_reserved_end)); - const uint32_t drom_resv_end = ALIGN_UP_TO_MMU_PAGE_SIZE((uint32_t)(&_rodata_reserved_end)); - - // 4. I_Cache / D_Cache (flash) - PMP_ENTRY_CFG_RESET(8); - PMP_ENTRY_CFG_RESET(9); - PMP_ENTRY_CFG_RESET(10); - PMP_ENTRY_SET(8, SOC_IROM_LOW, NONE); - PMP_ENTRY_SET(9, irom_resv_end, PMP_TOR | RX); - PMP_ENTRY_SET(10, drom_resv_end, PMP_TOR | R); -#else - // 4. I_Cache / D_Cache (flash) - const uint32_t pmpaddr8 = PMPADDR_NAPOT(SOC_IROM_LOW, SOC_IROM_HIGH); - PMP_ENTRY_SET(8, pmpaddr8, PMP_NAPOT | CONDITIONAL_RX); - _Static_assert(SOC_IROM_LOW < SOC_IROM_HIGH, "Invalid I/D_Cache region"); -#endif - - // 5. 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 - */ - PMP_ENTRY_CFG_RESET(11); - PMP_ENTRY_CFG_RESET(12); - PMP_ENTRY_CFG_RESET(13); - 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); -#else - const uint32_t pmpaddr11 = PMPADDR_NAPOT(SOC_RTC_IRAM_LOW, SOC_RTC_IRAM_HIGH); - PMP_ENTRY_SET(11, pmpaddr11, PMP_NAPOT | CONDITIONAL_RWX); - _Static_assert(SOC_RTC_IRAM_LOW < SOC_RTC_IRAM_HIGH, "Invalid RTC IRAM region"); -#endif - - // 6. Peripheral addresses - PMP_ENTRY_CFG_RESET(14); - const uint32_t pmpaddr14 = PMPADDR_NAPOT(SOC_PERIPHERAL_LOW, SOC_PERIPHERAL_HIGH); - PMP_ENTRY_SET(14, pmpaddr14, PMP_NAPOT | RW); - _Static_assert(SOC_PERIPHERAL_LOW < SOC_PERIPHERAL_HIGH, "Invalid peripheral region"); } diff --git a/components/esp_hw_support/port/esp32h2/private_include/pmp_layout.h b/components/esp_hw_support/port/esp32h2/private_include/pmp_layout.h new file mode 100644 index 00000000000..08ac07c9f5a --- /dev/null +++ b/components/esp_hw_support/port/esp32h2/private_include/pmp_layout.h @@ -0,0 +1,46 @@ +/* + * 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-H2 PMP entry layout: a bootloader<->application ABI. An entry the + * (non-updatable) bootloader locks cannot be reconfigured until CPU reset, so + * the index of any entry a shipped bootloader locks is frozen. + * The layout below matches v5.2.2..v5.5; older generations that locked a + * different value on a shared index cannot be helped (their lock wins). + * + * Entries locked by shipped bootloader generations: + * <= v5.2.1 : 0, 1-2 (I-ROM), 3-4 (D-ROM), 8-9 (cache), 13 (periph) + * v5.2.2 .. v5.5 : 0, 1-2 (I-ROM), 14 (peripherals) + * v6.0 / v6.1 : 0, 1 (ROM), 11 (peripherals) + * >= v6.2 : none + */ +#define PMP_ENTRY_CPU_SUBSYSTEM 0 /* NAPOT RWX */ +#define PMP_ENTRY_IROM_LOW 1 /* TOR base */ +#define PMP_ENTRY_IROM_HIGH 2 /* TOR RX */ +#define PMP_ENTRY_DROM_LOW 3 /* TOR base (locked by <= v5.2.1) */ +#define PMP_ENTRY_DROM_HIGH 4 /* TOR R (locked by <= v5.2.1) */ +/* 5..13: application-owned SRAM/flash/LP-RAM split, programmed by plain index + * in cpu_region_protect.c; not part of the ABI, not frozen here. */ +#define PMP_ENTRY_PERIPHERAL 14 /* NAPOT RW */ + +ESP_STATIC_ASSERT(PMP_ENTRY_CPU_SUBSYSTEM == 0 + && PMP_ENTRY_IROM_LOW == 1 && PMP_ENTRY_IROM_HIGH == 2 + && PMP_ENTRY_DROM_LOW == 3 && PMP_ENTRY_DROM_HIGH == 4 + && PMP_ENTRY_PERIPHERAL == 14, + "Entries locked by shipped bootloaders are a frozen ABI and must never move"); + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_hw_support/port/esp32p4/cpu_region_protect.c b/components/esp_hw_support/port/esp32p4/cpu_region_protect.c index 37dc596fb29..860e1ff1418 100644 --- a/components/esp_hw_support/port/esp32p4/cpu_region_protect.c +++ b/components/esp_hw_support/port/esp32p4/cpu_region_protect.c @@ -18,22 +18,7 @@ #include "soc/chip_revision.h" #include "hal/config.h" - -#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 -#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 -#endif +#include "pmp_layout.h" #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) @@ -85,6 +70,7 @@ static void esp_cpu_configure_invalid_regions(void) PMA_RESET_AND_ENTRY_SET_TOR(15, UINT32_MAX, PMA_TOR | PMA_NONE); } +#ifndef BOOTLOADER_BUILD #if HAL_CONFIG(CHIP_SUPPORT_MIN_REV) >= 300 // Helper macro to set both cached and non-cached PMP entries with the same permissions #define PMP_ENTRY_SET_CACHED_AND_UNCACHED(cached_entry, non_cached_entry, addr, perm) \ @@ -103,7 +89,7 @@ static void esp_cpu_configure_region_protection_rev_v3(void) // 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_RESET_AND_ENTRY_SET(0, pmpaddr0, PMP_NAPOT | RW); + PMP_RESET_AND_ENTRY_SET(PMP_ENTRY_CPU_SUBSYSTEM, pmpaddr0, PMP_NAPOT | RW); _Static_assert(SOC_CPU_SUBSYSTEM_LOW < SOC_CPU_SUBSYSTEM_HIGH, "Invalid CPU subsystem region"); // 2. HP-CPU SPM @@ -111,15 +97,15 @@ static void esp_cpu_configure_region_protection_rev_v3(void) // 3. CPU Peripherals const uint32_t pmpaddr1 = PMPADDR_NAPOT(CPU_PERIPH_LOW, CPU_PERIPH_HIGH); - PMP_RESET_AND_ENTRY_SET(1, pmpaddr1, PMP_NAPOT | RW); + PMP_RESET_AND_ENTRY_SET(PMP_ENTRY_CPU_PERIPHERAL, pmpaddr1, PMP_NAPOT | RW); _Static_assert(CPU_PERIPH_LOW < CPU_PERIPH_HIGH, "Invalid CPU peripheral region"); // 4. I/D-ROM const uint32_t pmpaddr2 = PMPADDR_NAPOT(SOC_IROM_MASK_LOW, SOC_IROM_MASK_HIGH); - PMP_RESET_AND_ENTRY_SET(2, pmpaddr2, PMP_NAPOT | RX); + PMP_RESET_AND_ENTRY_SET(PMP_ENTRY_ROM, pmpaddr2, PMP_NAPOT | RX); const uint32_t pmpaddr3 = PMPADDR_NAPOT(CACHE_LL_L2MEM_NON_CACHE_ADDR(SOC_IROM_MASK_LOW), CACHE_LL_L2MEM_NON_CACHE_ADDR(SOC_IROM_MASK_HIGH)); - PMP_RESET_AND_ENTRY_SET(3, pmpaddr3, PMP_NAPOT | RX); + PMP_RESET_AND_ENTRY_SET(PMP_ENTRY_ROM_UNCACHED_REV_V3, pmpaddr3, PMP_NAPOT | RX); _Static_assert(SOC_IROM_MASK_LOW < SOC_IROM_MASK_HIGH, "Invalid I/D-ROM region"); @@ -133,20 +119,20 @@ static void esp_cpu_configure_region_protection_rev_v3(void) _Static_assert(SOC_IRAM_LOW < SOC_IRAM_HIGH, "Invalid RAM region"); } else { -#if CONFIG_ESP_SYSTEM_MEMPROT && CONFIG_ESP_SYSTEM_MEMPROT_PMP && !BOOTLOADER_BUILD +#if CONFIG_ESP_SYSTEM_MEMPROT && CONFIG_ESP_SYSTEM_MEMPROT_PMP extern int _iram_text_end; PMP_ENTRY_SET_CACHED_AND_UNCACHED(4, 7, SOC_IRAM_LOW, NONE); PMP_ENTRY_SET_CACHED_AND_UNCACHED(5, 8, (int)&_iram_text_end, PMP_TOR | RX); PMP_ENTRY_SET_CACHED_AND_UNCACHED(6, 9, SOC_DRAM_HIGH, PMP_TOR | RW); #else - PMP_ENTRY_SET_CACHED_AND_UNCACHED(4, 6, SOC_IRAM_LOW, CONDITIONAL_NONE); - PMP_ENTRY_SET_CACHED_AND_UNCACHED(5, 7, SOC_IRAM_HIGH, PMP_TOR | CONDITIONAL_RWX); + PMP_ENTRY_SET_CACHED_AND_UNCACHED(4, 6, SOC_IRAM_LOW, NONE); + PMP_ENTRY_SET_CACHED_AND_UNCACHED(5, 7, SOC_IRAM_HIGH, PMP_TOR | RWX); _Static_assert(SOC_IRAM_LOW < SOC_IRAM_HIGH, "Invalid RAM region"); #endif } -#if CONFIG_ESP_SYSTEM_MEMPROT && CONFIG_ESP_SYSTEM_MEMPROT_PMP && !BOOTLOADER_BUILD +#if CONFIG_ESP_SYSTEM_MEMPROT && CONFIG_ESP_SYSTEM_MEMPROT_PMP extern int _instruction_reserved_end; extern int _rodata_reserved_end; @@ -198,30 +184,30 @@ static void esp_cpu_configure_region_protection_rev_v3(void) PMP_ENTRY_SET_CACHED_AND_UNCACHED(22, 26, page_aligned_drom_resv_end, PMP_TOR | R); #else -#if !BOOTLOADER_BUILD && CONFIG_SPIRAM +#if CONFIG_SPIRAM const uint32_t pmpaddr10 = PMPADDR_NAPOT(SOC_EXTRAM_LOW, SOC_EXTRAM_HIGH); - PMP_RESET_AND_ENTRY_SET(10, pmpaddr10, PMP_NAPOT | CONDITIONAL_RWX); + PMP_RESET_AND_ENTRY_SET(10, pmpaddr10, PMP_NAPOT | RWX); const uint32_t pmpaddr11 = PMPADDR_NAPOT(CACHE_LL_L2MEM_NON_CACHE_ADDR(SOC_EXTRAM_LOW), CACHE_LL_L2MEM_NON_CACHE_ADDR(SOC_EXTRAM_HIGH)); - PMP_RESET_AND_ENTRY_SET(11, pmpaddr11, PMP_NAPOT | CONDITIONAL_RWX); + PMP_RESET_AND_ENTRY_SET(11, pmpaddr11, PMP_NAPOT | RWX); _Static_assert(SOC_EXTRAM_LOW < SOC_EXTRAM_HIGH, "Invalid I/D_EXTRAM region"); -#endif /* !BOOTLOADER_BUILD && CONFIG_SPIRAM */ +#endif /* CONFIG_SPIRAM */ const uint32_t pmpaddr12 = PMPADDR_NAPOT(SOC_IROM_LOW, SOC_IROM_HIGH); - PMP_RESET_AND_ENTRY_SET(12, pmpaddr12, PMP_NAPOT | CONDITIONAL_RX); + PMP_RESET_AND_ENTRY_SET(12, pmpaddr12, PMP_NAPOT | RX); const uint32_t pmpaddr13 = PMPADDR_NAPOT(CACHE_LL_L2MEM_NON_CACHE_ADDR(SOC_IROM_LOW), CACHE_LL_L2MEM_NON_CACHE_ADDR(SOC_IROM_HIGH)); - PMP_RESET_AND_ENTRY_SET(13, pmpaddr13, PMP_NAPOT | CONDITIONAL_RX); + PMP_RESET_AND_ENTRY_SET(13, pmpaddr13, PMP_NAPOT | RX); _Static_assert(SOC_IROM_LOW < SOC_IROM_HIGH, "Invalid I/D_Cache region"); #endif // 8. Peripheral addresses const uint32_t pmpaddr27 = PMPADDR_NAPOT(SOC_PERIPHERAL_LOW, SOC_PERIPHERAL_HIGH); - PMP_RESET_AND_ENTRY_SET(27, pmpaddr27, PMP_NAPOT | RW); + PMP_RESET_AND_ENTRY_SET(PMP_ENTRY_PERIPHERAL_REV_V3, pmpaddr27, PMP_NAPOT | RW); _Static_assert(SOC_PERIPHERAL_LOW < SOC_PERIPHERAL_HIGH, "Invalid peripheral region"); // 9. LP memory and LP peripherals -#if CONFIG_ESP_SYSTEM_MEMPROT && CONFIG_ESP_SYSTEM_MEMPROT_PMP && !BOOTLOADER_BUILD +#if CONFIG_ESP_SYSTEM_MEMPROT && CONFIG_ESP_SYSTEM_MEMPROT_PMP extern int _rtc_text_start; extern int _rtc_text_end; @@ -239,11 +225,11 @@ static void esp_cpu_configure_region_protection_rev_v3(void) PMP_RESET_AND_ENTRY_SET(31, SOC_LP_PERIPH_HIGH, PMP_TOR | RW); #else const uint32_t pmpaddr28 = PMPADDR_NAPOT(SOC_RTC_IRAM_LOW, SOC_RTC_IRAM_HIGH); - PMP_RESET_AND_ENTRY_SET(28, pmpaddr28, PMP_NAPOT | CONDITIONAL_RWX); + PMP_RESET_AND_ENTRY_SET(28, pmpaddr28, PMP_NAPOT | RWX); _Static_assert(SOC_RTC_IRAM_LOW < SOC_RTC_IRAM_HIGH, "Invalid RTC IRAM region"); PMP_RESET_AND_ENTRY_SET(29, SOC_LP_PERIPH_LOW, NONE); - PMP_RESET_AND_ENTRY_SET(30, SOC_LP_PERIPH_HIGH, PMP_TOR | CONDITIONAL_RW); + PMP_RESET_AND_ENTRY_SET(30, SOC_LP_PERIPH_HIGH, PMP_TOR | RW); #endif } #else @@ -257,17 +243,17 @@ static void esp_cpu_configure_region_protection_rev_less_than_v3(void) // 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 | RW); + PMP_RESET_AND_ENTRY_SET(PMP_ENTRY_CPU_SUBSYSTEM, pmpaddr0, PMP_NAPOT | RW); _Static_assert(SOC_CPU_SUBSYSTEM_LOW < SOC_CPU_SUBSYSTEM_HIGH, "Invalid CPU subsystem region"); // 2. CPU Peripherals const uint32_t pmpaddr1 = PMPADDR_NAPOT(CPU_PERIPH_LOW, CPU_PERIPH_HIGH); - PMP_ENTRY_SET(1, pmpaddr1, PMP_NAPOT | RW); + PMP_RESET_AND_ENTRY_SET(PMP_ENTRY_CPU_PERIPHERAL, pmpaddr1, PMP_NAPOT | RW); _Static_assert(CPU_PERIPH_LOW < CPU_PERIPH_HIGH, "Invalid CPU peripheral region"); // 3. I/D-ROM const uint32_t pmpaddr2 = PMPADDR_NAPOT(SOC_IROM_MASK_LOW, SOC_IROM_MASK_HIGH); - PMP_ENTRY_SET(2, pmpaddr2, PMP_NAPOT | RX); + PMP_RESET_AND_ENTRY_SET(PMP_ENTRY_ROM, pmpaddr2, PMP_NAPOT | RX); _Static_assert(SOC_IROM_MASK_LOW < SOC_IROM_MASK_HIGH, "Invalid I/D-ROM region"); if (esp_cpu_dbgr_is_attached()) { @@ -279,7 +265,7 @@ static void esp_cpu_configure_region_protection_rev_less_than_v3(void) 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 && !BOOTLOADER_BUILD +#if CONFIG_ESP_SYSTEM_MEMPROT && CONFIG_ESP_SYSTEM_MEMPROT_PMP extern int _iram_text_end; // 4. IRAM and DRAM /* Reset the corresponding PMP config because PMP_ENTRY_SET only sets the given bits @@ -293,13 +279,13 @@ static void esp_cpu_configure_region_protection_rev_less_than_v3(void) PMP_ENTRY_SET(5, SOC_DRAM_HIGH, PMP_TOR | RW); #else // 4. IRAM and DRAM - PMP_ENTRY_SET(3, SOC_IRAM_LOW, CONDITIONAL_NONE); - PMP_ENTRY_SET(4, SOC_IRAM_HIGH, PMP_TOR | CONDITIONAL_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 } -#if CONFIG_ESP_SYSTEM_MEMPROT && CONFIG_ESP_SYSTEM_MEMPROT_PMP && !BOOTLOADER_BUILD +#if CONFIG_ESP_SYSTEM_MEMPROT && CONFIG_ESP_SYSTEM_MEMPROT_PMP extern int _instruction_reserved_end; extern int _rodata_reserved_end; @@ -334,12 +320,12 @@ static void esp_cpu_configure_region_protection_rev_less_than_v3(void) #else // 5. I_Cache / D_Cache (flash) const uint32_t pmpaddr6 = PMPADDR_NAPOT(SOC_IROM_LOW, SOC_IROM_HIGH); - PMP_ENTRY_SET(6, pmpaddr6, PMP_NAPOT | CONDITIONAL_RX); + PMP_ENTRY_SET(6, pmpaddr6, PMP_NAPOT | RX); _Static_assert(SOC_IROM_LOW < SOC_IROM_HIGH, "Invalid I/D_Cache region"); #endif // 6. LP memory -#if CONFIG_ESP_SYSTEM_MEMPROT && CONFIG_ESP_SYSTEM_MEMPROT_PMP && !BOOTLOADER_BUILD +#if CONFIG_ESP_SYSTEM_MEMPROT && CONFIG_ESP_SYSTEM_MEMPROT_PMP extern int _rtc_text_start; extern int _rtc_text_end; /* Reset the corresponding PMP config because PMP_ENTRY_SET only sets the given bits @@ -361,17 +347,27 @@ static void esp_cpu_configure_region_protection_rev_less_than_v3(void) PMP_ENTRY_SET(14, SOC_RTC_IRAM_HIGH, PMP_TOR | RW); #else const uint32_t pmpaddr11 = PMPADDR_NAPOT(SOC_RTC_IRAM_LOW, SOC_RTC_IRAM_HIGH); - PMP_ENTRY_SET(11, pmpaddr11, PMP_NAPOT | CONDITIONAL_RWX); + PMP_ENTRY_SET(11, pmpaddr11, PMP_NAPOT | RWX); _Static_assert(SOC_RTC_IRAM_LOW < SOC_RTC_IRAM_HIGH, "Invalid RTC IRAM region"); #endif // 7. Peripheral addresses const uint32_t pmpaddr15 = PMPADDR_NAPOT(SOC_PERIPHERAL_LOW, SOC_PERIPHERAL_HIGH); - PMP_ENTRY_SET(15, pmpaddr15, PMP_NAPOT | RW); + PMP_RESET_AND_ENTRY_SET(PMP_ENTRY_PERIPHERAL_REV_LESS_THAN_V3, pmpaddr15, PMP_NAPOT | RW); _Static_assert(SOC_PERIPHERAL_LOW < SOC_PERIPHERAL_HIGH, "Invalid peripheral region"); } #endif +static void esp_cpu_configure_valid_regions(void) +{ +#if HAL_CONFIG(CHIP_SUPPORT_MIN_REV) >= 300 + esp_cpu_configure_region_protection_rev_v3(); +#else + esp_cpu_configure_region_protection_rev_less_than_v3(); +#endif +} +#endif // BOOTLOADER_BUILD + void esp_cpu_configure_region_protection(void) { /* Notes on implementation: @@ -401,40 +397,14 @@ void esp_cpu_configure_region_protection(void) * c. External flash, External RAM, HP ROM, HP L2MEM (direct access) */ - /* 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 - * - 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_text_end and _data_start markers to set the boundaries. - * 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 - * - 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 - * - 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 - */ - // - // Configure all the invalid address regions using PMA - // + /* 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 apply to M-mode, and a + * locked one would survive into the application with no way to reconfigure + * it until the next CPU reset). */ esp_cpu_configure_invalid_regions(); - // - // Configure all the valid address regions using PMP - // - -#if HAL_CONFIG(CHIP_SUPPORT_MIN_REV) >= 300 - esp_cpu_configure_region_protection_rev_v3(); -#else - esp_cpu_configure_region_protection_rev_less_than_v3(); +#ifndef BOOTLOADER_BUILD + esp_cpu_configure_valid_regions(); #endif - } diff --git a/components/esp_hw_support/port/esp32p4/private_include/pmp_layout.h b/components/esp_hw_support/port/esp32p4/private_include/pmp_layout.h new file mode 100644 index 00000000000..004543956e7 --- /dev/null +++ b/components/esp_hw_support/port/esp32p4/private_include/pmp_layout.h @@ -0,0 +1,51 @@ +/* + * 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-P4 PMP entry layout: a bootloader<->application ABI. An entry the + * (non-updatable) bootloader locks cannot be reconfigured until CPU reset, so + * the index of any entry a shipped bootloader locks is frozen. + * Two incompatible layouts selected by chip revision (>= 3.0); binaries do not + * mix, so each has its own index namespace. Entries 0-2 are shared. + * + * Locked by shipped bootloader generations: + * rev < 3.0 v5.3/v5.4 : 0-2 (ROM), 13 (periph); v5.5..v6.1 : 0-2, 15 + * rev >= 3.0 v6.0/v6.1 : 0-3 (ROM cached+uncached), 27 (periph) + * >= v6.2 : none + */ +#define PMP_ENTRY_CPU_SUBSYSTEM 0 /* NAPOT RW, both layouts */ +#define PMP_ENTRY_CPU_PERIPHERAL 1 /* NAPOT RW, both layouts */ +#define PMP_ENTRY_ROM 2 /* NAPOT RX, both layouts */ + +/* rev < 3.0: 3..14 are application-owned (SRAM/flash/LP-RAM split). */ +#define PMP_ENTRY_PERIPHERAL_REV_LESS_THAN_V3 15 /* NAPOT RW */ + +/* rev >= 3.0: 4..26 (SRAM/ext-RAM/flash cached+uncached aliases) and 28..31 + * (LP) are application-owned; their indices shift with the memprot config, so + * they are programmed by plain index in cpu_region_protect.c, not named here. */ +#define PMP_ENTRY_ROM_UNCACHED_REV_V3 3 /* NAPOT RX */ +#define PMP_ENTRY_PERIPHERAL_REV_V3 27 /* NAPOT RW */ + +ESP_STATIC_ASSERT(PMP_ENTRY_CPU_SUBSYSTEM == 0 && PMP_ENTRY_CPU_PERIPHERAL == 1 + && PMP_ENTRY_ROM == 2, + "Shared entries are a frozen ABI and must never move"); +ESP_STATIC_ASSERT(PMP_ENTRY_PERIPHERAL_REV_LESS_THAN_V3 == 15, + "rev < 3.0 peripheral entry is locked by shipped bootloaders and must never move"); +ESP_STATIC_ASSERT(PMP_ENTRY_ROM_UNCACHED_REV_V3 == 3 && PMP_ENTRY_PERIPHERAL_REV_V3 == 27, + "rev >= 3.0 entries locked by shipped bootloaders are a frozen ABI and must never move"); + +#ifdef __cplusplus +} +#endif From 1d6c44a8896303e8c38847eb29bfac0d271dc906 Mon Sep 17 00:00:00 2001 From: "harshal.patil" Date: Thu, 16 Jul 2026 14:45:52 +0530 Subject: [PATCH 2/4] change(esp_hw_support): make ESP32-P4 rev < 3.0 PMP layout forward-compatible Commit ab229a34 added PSRAM memory protection for ESP32-P4 rev < 3.0. It consumed all 16 PMP entries and shifted the fixed LP-RAM (9-12 -> 11-14) and peripheral (13 -> 15) entries to make room. Older bootloaders lock the peripheral entry at its original index 13, so on a v5.3/v5.4 bootloader running a newer application the LP-RAM entry that landed on the locked index 13 was silently ignored, weakening LP-RAM protection. Select the rev < 3.0 layout at runtime by probing whether the bootloader locked the peripheral at entry 13: - locked (v5.3/v5.4) -> default layout (LP-RAM 9-12, peripheral 13), no PSRAM protection; these devices never had it (it was introduced in v5.5). - free (v5.5+) -> PSRAM layout (flash/ext-RAM 6-10, LP-RAM 11-14, peripheral 15) with full external-RAM protection; the peripheral entry at 15 matches the bootloader's lock. This restores forward compatibility across all shipped rev < 3.0 bootloaders without regressing PSRAM protection on the v5.5+ devices that already have it. Chip revision >= 3.0 (32 PMP entries) is unaffected. --- .../port/esp32p4/cpu_region_protect.c | 198 +++++++++++------- .../port/esp32p4/private_include/pmp_layout.h | 17 +- 2 files changed, 141 insertions(+), 74 deletions(-) diff --git a/components/esp_hw_support/port/esp32p4/cpu_region_protect.c b/components/esp_hw_support/port/esp32p4/cpu_region_protect.c index 860e1ff1418..93433940489 100644 --- a/components/esp_hw_support/port/esp32p4/cpu_region_protect.c +++ b/components/esp_hw_support/port/esp32p4/cpu_region_protect.c @@ -233,10 +233,127 @@ static void esp_cpu_configure_region_protection_rev_v3(void) #endif } #else +// Default rev < 3.0 layout: flash 6-8, LP-RAM 9-12, peripheral 13. Used on a +// v5.3/v5.4 bootloader (peripheral locked at 13) or when PSRAM protection is off. +static void esp_cpu_configure_region_protection_rev_less_than_v3_default(void) +{ + __attribute__((unused)) const unsigned NONE = PMP_L; + __attribute__((unused)) 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; + __attribute__((unused)) const unsigned RWX = PMP_L | PMP_R | PMP_W | PMP_X; + +#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)); + const uint32_t page_aligned_drom_resv_end = ALIGN_UP_TO_MMU_PAGE_SIZE((uint32_t)(&_rodata_reserved_end)); + + // 5. I_Cache / D_Cache (flash) + PMP_ENTRY_CFG_RESET(6); + PMP_ENTRY_CFG_RESET(7); + PMP_ENTRY_CFG_RESET(8); + 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 + // 5. I_Cache / D_Cache (flash) + const uint32_t pmpaddr6 = PMPADDR_NAPOT(SOC_IROM_LOW, SOC_IROM_HIGH); + PMP_ENTRY_SET(6, pmpaddr6, PMP_NAPOT | RX); + _Static_assert(SOC_IROM_LOW < SOC_IROM_HIGH, "Invalid I/D_Cache region"); +#endif + + // 6. LP memory +#if CONFIG_ESP_SYSTEM_MEMPROT && CONFIG_ESP_SYSTEM_MEMPROT_PMP + extern int _rtc_text_start; + 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 + */ + PMP_ENTRY_CFG_RESET(9); + PMP_ENTRY_CFG_RESET(10); + PMP_ENTRY_CFG_RESET(11); + PMP_ENTRY_CFG_RESET(12); + PMP_ENTRY_SET(9, SOC_RTC_IRAM_LOW, NONE); + // First part of LP mem is reserved for RTC reserved mem (shared between bootloader and app) + // as well as memory for ULP coprocessor +#if CONFIG_ESP_SYSTEM_MEMPROT_PMP_LP_CORE_RESERVE_MEM_EXEC + PMP_ENTRY_SET(10, (int)&_rtc_text_start, PMP_TOR | RWX); +#else + PMP_ENTRY_SET(10, (int)&_rtc_text_start, PMP_TOR | RW); +#endif + PMP_ENTRY_SET(11, (int)&_rtc_text_end, PMP_TOR | RX); + PMP_ENTRY_SET(12, SOC_RTC_IRAM_HIGH, PMP_TOR | RW); +#else + const uint32_t pmpaddr9 = PMPADDR_NAPOT(SOC_RTC_IRAM_LOW, SOC_RTC_IRAM_HIGH); + PMP_ENTRY_SET(9, pmpaddr9, PMP_NAPOT | RWX); + _Static_assert(SOC_RTC_IRAM_LOW < SOC_RTC_IRAM_HIGH, "Invalid RTC IRAM region"); +#endif + + // 7. Peripheral addresses + const uint32_t pmpaddr13 = PMPADDR_NAPOT(SOC_PERIPHERAL_LOW, SOC_PERIPHERAL_HIGH); + PMP_RESET_AND_ENTRY_SET(PMP_ENTRY_PERIPHERAL_REV_LESS_THAN_V3, pmpaddr13, PMP_NAPOT | RW); + _Static_assert(SOC_PERIPHERAL_LOW < SOC_PERIPHERAL_HIGH, "Invalid peripheral region"); +} + +#if CONFIG_ESP_SYSTEM_MEMPROT && CONFIG_ESP_SYSTEM_MEMPROT_PMP && CONFIG_SPIRAM_XIP_FROM_PSRAM && CONFIG_SPIRAM_PRE_CONFIGURE_MEMORY_PROTECTION +// PSRAM-protected rev < 3.0 layout: flash/ext-RAM 6-10, LP-RAM 11-14, peripheral +// 15. Fits only when the bootloader left entry 13 free (v5.5+). +static void esp_cpu_configure_region_protection_rev_less_than_v3_psram(void) +{ + 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; + __attribute__((unused)) const unsigned RWX = PMP_L | PMP_R | PMP_W | PMP_X; + + extern int _instruction_reserved_end; + extern int _rodata_reserved_end; + extern int _rtc_text_start; + extern int _rtc_text_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)); + + // 5. I_Cache / D_Cache (flash) and external RAM + PMP_ENTRY_CFG_RESET(6); + PMP_ENTRY_CFG_RESET(7); + PMP_ENTRY_CFG_RESET(8); + PMP_ENTRY_CFG_RESET(9); + PMP_ENTRY_SET(6, SOC_EXTRAM_LOW, NONE); + PMP_ENTRY_SET(7, (uint32_t)(&_instruction_reserved_end), PMP_TOR | RX); + PMP_ENTRY_SET(8, page_aligned_irom_resv_end, PMP_TOR | RW); + PMP_ENTRY_SET(9, (uint32_t)(&_rodata_reserved_end), PMP_TOR | R); + + const size_t available_psram_heap = esp_psram_get_heap_size_to_protect(); + PMP_ENTRY_CFG_RESET(10); + PMP_ENTRY_SET(10, ESP_ALIGN_UP(page_aligned_drom_resv_end + available_psram_heap, SOC_CPU_PMP_REGION_GRANULARITY), PMP_TOR | RW); + + // 6. LP memory + PMP_ENTRY_CFG_RESET(11); + PMP_ENTRY_CFG_RESET(12); + PMP_ENTRY_CFG_RESET(13); + PMP_ENTRY_CFG_RESET(14); + PMP_ENTRY_SET(11, SOC_RTC_IRAM_LOW, NONE); +#if CONFIG_ESP_SYSTEM_MEMPROT_PMP_LP_CORE_RESERVE_MEM_EXEC + PMP_ENTRY_SET(12, (int)&_rtc_text_start, PMP_TOR | RWX); +#else + PMP_ENTRY_SET(12, (int)&_rtc_text_start, PMP_TOR | RW); +#endif + PMP_ENTRY_SET(13, (int)&_rtc_text_end, PMP_TOR | RX); + PMP_ENTRY_SET(14, SOC_RTC_IRAM_HIGH, PMP_TOR | RW); + + // 7. Peripheral addresses + const uint32_t pmpaddr15 = PMPADDR_NAPOT(SOC_PERIPHERAL_LOW, SOC_PERIPHERAL_HIGH); + PMP_RESET_AND_ENTRY_SET(PMP_ENTRY_PERIPHERAL_REV_LESS_THAN_V3_SPIRAM_MEMPROT_EN, pmpaddr15, PMP_NAPOT | RW); + _Static_assert(SOC_PERIPHERAL_LOW < SOC_PERIPHERAL_HIGH, "Invalid peripheral region"); +} +#endif + static void esp_cpu_configure_region_protection_rev_less_than_v3(void) { const unsigned NONE = PMP_L; - __attribute__((unused)) 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; @@ -285,76 +402,17 @@ static void esp_cpu_configure_region_protection_rev_less_than_v3(void) #endif } -#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)); - - // 5. I_Cache / D_Cache (flash) -#if CONFIG_SPIRAM_XIP_FROM_PSRAM && CONFIG_SPIRAM_PRE_CONFIGURE_MEMORY_PROTECTION - // We could have split CONFIG_SPIRAM_XIP_FROM_PSRAM into CONFIG_SPIRAM_FETCH_INSTRUCTIONS and CONFIG_SPIRAM_RODATA - // but we don't have enough PMP entries to do so thus not allowing us finer control over the memory regions - PMP_ENTRY_CFG_RESET(6); - PMP_ENTRY_CFG_RESET(7); - PMP_ENTRY_CFG_RESET(8); - PMP_ENTRY_CFG_RESET(9); - - PMP_ENTRY_SET(6, SOC_EXTRAM_LOW, NONE); - PMP_ENTRY_SET(7, (uint32_t)(&_instruction_reserved_end), PMP_TOR | RX); - PMP_ENTRY_SET(8, page_aligned_irom_resv_end, PMP_TOR | RW); - PMP_ENTRY_SET(9, (uint32_t)(&_rodata_reserved_end), PMP_TOR | R); - - size_t available_psram_heap = esp_psram_get_heap_size_to_protect(); - PMP_ENTRY_CFG_RESET(10); - PMP_ENTRY_SET(10, ESP_ALIGN_UP(page_aligned_drom_resv_end + available_psram_heap, SOC_CPU_PMP_REGION_GRANULARITY), PMP_TOR | RW); -#else - PMP_ENTRY_CFG_RESET(6); - PMP_ENTRY_CFG_RESET(7); - PMP_ENTRY_CFG_RESET(8); - 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); -#endif /* CONFIG_SPIRAM_XIP_FROM_PSRAM && CONFIG_SPIRAM_PRE_CONFIGURE_MEMORY_PROTECTION */ -#else - // 5. I_Cache / D_Cache (flash) - const uint32_t pmpaddr6 = PMPADDR_NAPOT(SOC_IROM_LOW, SOC_IROM_HIGH); - PMP_ENTRY_SET(6, pmpaddr6, PMP_NAPOT | RX); - _Static_assert(SOC_IROM_LOW < SOC_IROM_HIGH, "Invalid I/D_Cache region"); + /* The flash, LP memory and peripheral entries have two layouts. The PSRAM + * layout needs all 16 entries and only fits when the bootloader left entry 13 + * free; a v5.3/v5.4 bootloader locks the peripheral there, so fall back to the + * default layout (no PSRAM protection) on those devices. */ +#if CONFIG_ESP_SYSTEM_MEMPROT && CONFIG_ESP_SYSTEM_MEMPROT_PMP && CONFIG_SPIRAM_XIP_FROM_PSRAM && CONFIG_SPIRAM_PRE_CONFIGURE_MEMORY_PROTECTION + if (!(PMP_ENTRY_CFG_READ(PMP_ENTRY_PERIPHERAL_REV_LESS_THAN_V3) & PMP_L)) { + esp_cpu_configure_region_protection_rev_less_than_v3_psram(); + return; + } #endif - - // 6. LP memory -#if CONFIG_ESP_SYSTEM_MEMPROT && CONFIG_ESP_SYSTEM_MEMPROT_PMP - extern int _rtc_text_start; - 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 - */ - PMP_ENTRY_CFG_RESET(11); - PMP_ENTRY_CFG_RESET(12); - PMP_ENTRY_CFG_RESET(13); - PMP_ENTRY_CFG_RESET(14); - PMP_ENTRY_SET(11, SOC_RTC_IRAM_LOW, NONE); - // First part of LP mem is reserved for RTC reserved mem (shared between bootloader and app) - // as well as memory for ULP coprocessor -#if CONFIG_ESP_SYSTEM_MEMPROT_PMP_LP_CORE_RESERVE_MEM_EXEC - PMP_ENTRY_SET(12, (int)&_rtc_text_start, PMP_TOR | RWX); -#else - PMP_ENTRY_SET(12, (int)&_rtc_text_start, PMP_TOR | RW); -#endif - PMP_ENTRY_SET(13, (int)&_rtc_text_end, PMP_TOR | RX); - PMP_ENTRY_SET(14, SOC_RTC_IRAM_HIGH, PMP_TOR | RW); -#else - const uint32_t pmpaddr11 = PMPADDR_NAPOT(SOC_RTC_IRAM_LOW, SOC_RTC_IRAM_HIGH); - PMP_ENTRY_SET(11, pmpaddr11, PMP_NAPOT | RWX); - _Static_assert(SOC_RTC_IRAM_LOW < SOC_RTC_IRAM_HIGH, "Invalid RTC IRAM region"); -#endif - - // 7. Peripheral addresses - const uint32_t pmpaddr15 = PMPADDR_NAPOT(SOC_PERIPHERAL_LOW, SOC_PERIPHERAL_HIGH); - PMP_RESET_AND_ENTRY_SET(PMP_ENTRY_PERIPHERAL_REV_LESS_THAN_V3, pmpaddr15, PMP_NAPOT | RW); - _Static_assert(SOC_PERIPHERAL_LOW < SOC_PERIPHERAL_HIGH, "Invalid peripheral region"); + esp_cpu_configure_region_protection_rev_less_than_v3_default(); } #endif diff --git a/components/esp_hw_support/port/esp32p4/private_include/pmp_layout.h b/components/esp_hw_support/port/esp32p4/private_include/pmp_layout.h index 004543956e7..505061787e5 100644 --- a/components/esp_hw_support/port/esp32p4/private_include/pmp_layout.h +++ b/components/esp_hw_support/port/esp32p4/private_include/pmp_layout.h @@ -29,8 +29,16 @@ extern "C" { #define PMP_ENTRY_CPU_PERIPHERAL 1 /* NAPOT RW, both layouts */ #define PMP_ENTRY_ROM 2 /* NAPOT RX, both layouts */ -/* rev < 3.0: 3..14 are application-owned (SRAM/flash/LP-RAM split). */ -#define PMP_ENTRY_PERIPHERAL_REV_LESS_THAN_V3 15 /* NAPOT RW */ +/* rev < 3.0 has two application layouts, chosen at runtime by probing which + * peripheral entry the bootloader locked (see cpu_region_protect.c): + * - default : SRAM/flash 3-8, LP-RAM 9-12, peripheral 13; 14-15 free. + * - PSRAM-protected : SRAM/flash/ext-RAM 3-10, LP-RAM 11-14, peripheral 15. + * The PSRAM layout is taken only on v5.5+ bootloaders (which leave 13 free and + * lock the peripheral at 15); a v5.3/v5.4 bootloader locks the peripheral at 13, + * forcing the default layout. Entries 13 and 15 are both peripheral homes locked + * by shipped bootloaders and must never be repurposed for a non-peripheral region. */ +#define PMP_ENTRY_PERIPHERAL_REV_LESS_THAN_V3 13 /* NAPOT RW, default layout */ +#define PMP_ENTRY_PERIPHERAL_REV_LESS_THAN_V3_SPIRAM_MEMPROT_EN 15 /* NAPOT RW, PSRAM layout */ /* rev >= 3.0: 4..26 (SRAM/ext-RAM/flash cached+uncached aliases) and 28..31 * (LP) are application-owned; their indices shift with the memprot config, so @@ -41,8 +49,9 @@ extern "C" { ESP_STATIC_ASSERT(PMP_ENTRY_CPU_SUBSYSTEM == 0 && PMP_ENTRY_CPU_PERIPHERAL == 1 && PMP_ENTRY_ROM == 2, "Shared entries are a frozen ABI and must never move"); -ESP_STATIC_ASSERT(PMP_ENTRY_PERIPHERAL_REV_LESS_THAN_V3 == 15, - "rev < 3.0 peripheral entry is locked by shipped bootloaders and must never move"); +ESP_STATIC_ASSERT(PMP_ENTRY_PERIPHERAL_REV_LESS_THAN_V3 == 13 + && PMP_ENTRY_PERIPHERAL_REV_LESS_THAN_V3_SPIRAM_MEMPROT_EN == 15, + "rev < 3.0 peripheral entries are locked by shipped bootloaders and must never move"); ESP_STATIC_ASSERT(PMP_ENTRY_ROM_UNCACHED_REV_V3 == 3 && PMP_ENTRY_PERIPHERAL_REV_V3 == 27, "rev >= 3.0 entries locked by shipped bootloaders are a frozen ABI and must never move"); From dc6939e58369d2d84919a15d4d34563614d7bab2 Mon Sep 17 00:00:00 2001 From: "harshal.patil" Date: Thu, 16 Jul 2026 16:41:13 +0530 Subject: [PATCH 3/4] test(esp_system): run memory protection panic tests on ESP32-P4 rev < 3.0 --- .../system/panic/panic_base/pytest_panic.py | 34 +++++++++++++++---- ...config.ci.memprot_esp32p4_rev_less_than_v3 | 16 +++++++++ ...emprot_spiram_xip_esp32p4_rev_less_than_v3 | 17 ++++++++++ .../panic_base/test_panic_util/panic_dut.py | 2 +- 4 files changed, 61 insertions(+), 8 deletions(-) create mode 100644 tools/test_apps/system/panic/panic_base/sdkconfig.ci.memprot_esp32p4_rev_less_than_v3 create mode 100644 tools/test_apps/system/panic/panic_base/sdkconfig.ci.memprot_spiram_xip_esp32p4_rev_less_than_v3 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 13ffe68c0ae..adfdbb438af 100644 --- a/tools/test_apps/system/panic/panic_base/pytest_panic.py +++ b/tools/test_apps/system/panic/panic_base/pytest_panic.py @@ -610,6 +610,10 @@ def test_panic_handler_crash1(dut: PanicTestDut, config: str, test_func_name: st # Memprot-related tests are supported only on targets with PMS/PMA peripheral; # currently ESP32-S2, ESP32-C3, ESP32-C2, ESP32-H2, ESP32-H21, ESP32-C6, ESP32-P4, ESP32-C5 and ESP32-C61 are supported +# ESP32-P4 rev < 3.0 runs on a dedicated rev 1.x runner (its binary is built for +# and only boots on rev < 3.0 silicon), so its configs carry the esp32p4_rev1 marker. +P4_REV_LESS_THAN_V3_MARKER = pytest.mark.esp32p4_rev1 + CONFIGS_MEMPROT_IDRAM = list( zip( [ @@ -624,7 +628,9 @@ CONFIGS_MEMPROT_IDRAM = list( ], ['esp32s2', 'esp32c3', 'esp32c2', 'esp32c5', 'esp32c61', 'esp32h2', 'esp32p4', 'esp32h21'], ) -) +) + [ + ('memprot_esp32p4_rev_less_than_v3', 'esp32p4', P4_REV_LESS_THAN_V3_MARKER), +] CONFIGS_MEMPROT_IDRAM_L2_MEM_NON_CACHE = list(zip(['memprot_esp32p4'], ['esp32p4'])) @@ -643,7 +649,9 @@ CONFIGS_MEMPROT_RTC_FAST_MEM = list( ], ['esp32s2', 'esp32c3', 'esp32c5', 'esp32c6', 'esp32h2', 'esp32p4', 'esp32h21'], ) -) +) + [ + ('memprot_esp32p4_rev_less_than_v3', 'esp32p4', P4_REV_LESS_THAN_V3_MARKER), +] CONFIGS_MEMPROT_RTC_SLOW_MEM = list(zip(['memprot_esp32s2'], ['esp32s2'])) @@ -659,16 +667,24 @@ CONFIGS_MEMPROT_FLASH_IDROM = list( ], ['esp32c5', 'esp32c6', 'esp32c61', 'esp32h2', 'esp32p4', 'esp32h21'], ) -) +) + [ + ('memprot_esp32p4_rev_less_than_v3', 'esp32p4', P4_REV_LESS_THAN_V3_MARKER), +] CONFIGS_MEMPROT_FLASH_IDROM_L2_NON_CACHE = list(zip(['memprot_esp32p4'], ['esp32p4'])) CONFIGS_MEMPROT_SPIRAM_XIP_IROM_ALIGNMENT_HEAP = list( zip( - ['memprot_spiram_xip_esp32c5', 'memprot_spiram_xip_esp32c61', 'memprot_spiram_xip_esp32p4'], + [ + 'memprot_spiram_xip_esp32c5', + 'memprot_spiram_xip_esp32c61', + 'memprot_spiram_xip_esp32p4', + ], ['esp32c5', 'esp32c61', 'esp32p4'], ) -) +) + [ + ('memprot_spiram_xip_esp32p4_rev_less_than_v3', 'esp32p4', P4_REV_LESS_THAN_V3_MARKER), +] CONFIGS_MEMPROT_SPIRAM_XIP_IROM_ALIGNMENT_HEAP_L2_NON_CACHE = list( zip( @@ -688,7 +704,9 @@ CONFIGS_MEMPROT_SPIRAM_XIP_DROM_ALIGNMENT_HEAP = list( ], ['esp32s3', 'esp32c5', 'esp32c61', 'esp32p4'], ) -) +) + [ + ('memprot_spiram_xip_esp32p4_rev_less_than_v3', 'esp32p4', P4_REV_LESS_THAN_V3_MARKER), +] CONFIGS_MEMPROT_SPIRAM_XIP_DROM_ALIGNMENT_HEAP_L2_NON_CACHE = list( zip( @@ -709,7 +727,9 @@ CONFIGS_MEMPROT_INVALID_REGION_PROTECTION_USING_PMA = list( ], ['esp32c5', 'esp32c6', 'esp32c61', 'esp32h2', 'esp32p4', 'esp32h21'], ) -) +) + [ + ('memprot_esp32p4_rev_less_than_v3', 'esp32p4', P4_REV_LESS_THAN_V3_MARKER), +] @pytest.mark.generic diff --git a/tools/test_apps/system/panic/panic_base/sdkconfig.ci.memprot_esp32p4_rev_less_than_v3 b/tools/test_apps/system/panic/panic_base/sdkconfig.ci.memprot_esp32p4_rev_less_than_v3 new file mode 100644 index 00000000000..12a8dd40f88 --- /dev/null +++ b/tools/test_apps/system/panic/panic_base/sdkconfig.ci.memprot_esp32p4_rev_less_than_v3 @@ -0,0 +1,16 @@ +# Restricting to ESP32P4 +CONFIG_IDF_TARGET="esp32p4" + +# Target the rev < 3.0 PMP layout (16 entries) +CONFIG_ESP32P4_SELECTS_REV_LESS_V3=y +CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ=360 + +# 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 diff --git a/tools/test_apps/system/panic/panic_base/sdkconfig.ci.memprot_spiram_xip_esp32p4_rev_less_than_v3 b/tools/test_apps/system/panic/panic_base/sdkconfig.ci.memprot_spiram_xip_esp32p4_rev_less_than_v3 new file mode 100644 index 00000000000..eb4ea8c37f2 --- /dev/null +++ b/tools/test_apps/system/panic/panic_base/sdkconfig.ci.memprot_spiram_xip_esp32p4_rev_less_than_v3 @@ -0,0 +1,17 @@ +# Restricting to ESP32P4 +CONFIG_IDF_TARGET="esp32p4" + +# Target the rev < 3.0 PMP layout (16 entries) +CONFIG_ESP32P4_SELECTS_REV_LESS_V3=y +CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ=360 + +# 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 diff --git a/tools/test_apps/system/panic/panic_base/test_panic_util/panic_dut.py b/tools/test_apps/system/panic/panic_base/test_panic_util/panic_dut.py index 68070f5e737..fac02e09fe6 100644 --- a/tools/test_apps/system/panic/panic_base/test_panic_util/panic_dut.py +++ b/tools/test_apps/system/panic/panic_base/test_panic_util/panic_dut.py @@ -68,7 +68,7 @@ class PanicTestDut(IdfDut): return self.target in ['esp32', 'esp32s3', 'esp32p4', 'esp32h4', 'esp32s31'] def run_test_func(self, test_func_name: str) -> None: - if self.target == 'esp32p4' and not self.app.sdkconfig.get('ESP32P4_SELECTS_REV_LESS_V3'): + if self.target == 'esp32p4': self.write('\n') self.expect_exact('Enter test name:') self.write(test_func_name) From 29d67905f016aec0004be1ac159f17e63ae17cb8 Mon Sep 17 00:00:00 2001 From: "harshal.patil" Date: Tue, 21 Jul 2026 15:22:56 +0530 Subject: [PATCH 4/4] docs: add v6.2 migration guide entry for bootloader PMP ownership change The second stage bootloader no longer configures or locks PMP entries on C5, C6, C61, H2 and P4; document the impact on non-ESP-IDF applications launched by the ESP-IDF bootloader that relied on the bootloader-provided PMP configuration. --- docs/en/migration-guides/index.rst | 1 + .../en/migration-guides/release-6.x/6.2/index.rst | 9 +++++++++ .../migration-guides/release-6.x/6.2/security.rst | 15 +++++++++++++++ docs/zh_CN/migration-guides/index.rst | 1 + .../migration-guides/release-6.x/6.2/index.rst | 9 +++++++++ .../migration-guides/release-6.x/6.2/security.rst | 15 +++++++++++++++ 6 files changed, 50 insertions(+) create mode 100644 docs/en/migration-guides/release-6.x/6.2/index.rst create mode 100644 docs/en/migration-guides/release-6.x/6.2/security.rst create mode 100644 docs/zh_CN/migration-guides/release-6.x/6.2/index.rst create mode 100644 docs/zh_CN/migration-guides/release-6.x/6.2/security.rst diff --git a/docs/en/migration-guides/index.rst b/docs/en/migration-guides/index.rst index 25b6666115e..a847d471c76 100644 --- a/docs/en/migration-guides/index.rst +++ b/docs/en/migration-guides/index.rst @@ -24,3 +24,4 @@ ESP-IDF 6.x Migration Guide release-6.x/6.0/index release-6.x/6.1/index + release-6.x/6.2/index diff --git a/docs/en/migration-guides/release-6.x/6.2/index.rst b/docs/en/migration-guides/release-6.x/6.2/index.rst new file mode 100644 index 00000000000..c19f7650634 --- /dev/null +++ b/docs/en/migration-guides/release-6.x/6.2/index.rst @@ -0,0 +1,9 @@ +Migration from 6.1 to 6.2 +-------------------------- + +:link_to_translation:`zh_CN:[中文]` + +.. toctree:: + :maxdepth: 1 + + security diff --git a/docs/en/migration-guides/release-6.x/6.2/security.rst b/docs/en/migration-guides/release-6.x/6.2/security.rst new file mode 100644 index 00000000000..130a67f8d0c --- /dev/null +++ b/docs/en/migration-guides/release-6.x/6.2/security.rst @@ -0,0 +1,15 @@ +Security +======== + +:link_to_translation:`zh_CN:[中文]` + +Memory Protection +----------------- + +Starting from **ESP-IDF v6.2**, on ESP32-C5, ESP32-C6, ESP32-C61, ESP32-H2, and ESP32-P4 the second stage bootloader no longer configures or locks any RISC-V PMP entries. The complete PMP configuration is now programmed and locked by the application during early startup. + +A PMP entry locked by the bootloader cannot be reconfigured until the next CPU reset. Since the bootloader on a deployed device is generally never updated, entries locked by an older bootloader could conflict with the PMP layout expected by an application built with a newer ESP-IDF version, leading to boot failures or to memory protection being silently inactive. + +Applications built with ESP-IDF are not affected, as they program and lock the full PMP configuration themselves during startup, before any application code runs. + +Custom (non-ESP-IDF) applications launched by the ESP-IDF second stage bootloader must not assume that any PMP entries are pre-configured or locked at handoff. Previously, the bootloader configured and locked entries covering, e.g., the ROM and the peripheral address spaces; such applications must now program their own PMP configuration. diff --git a/docs/zh_CN/migration-guides/index.rst b/docs/zh_CN/migration-guides/index.rst index f8447add4b3..2d49c919563 100644 --- a/docs/zh_CN/migration-guides/index.rst +++ b/docs/zh_CN/migration-guides/index.rst @@ -24,3 +24,4 @@ release-6.x/6.0/index release-6.x/6.1/index + release-6.x/6.2/index diff --git a/docs/zh_CN/migration-guides/release-6.x/6.2/index.rst b/docs/zh_CN/migration-guides/release-6.x/6.2/index.rst new file mode 100644 index 00000000000..c4f35f62498 --- /dev/null +++ b/docs/zh_CN/migration-guides/release-6.x/6.2/index.rst @@ -0,0 +1,9 @@ +从 6.1 迁移到 6.2 +------------------ + +:link_to_translation:`en:[English]` + +.. toctree:: + :maxdepth: 1 + + security diff --git a/docs/zh_CN/migration-guides/release-6.x/6.2/security.rst b/docs/zh_CN/migration-guides/release-6.x/6.2/security.rst new file mode 100644 index 00000000000..70bd2c3d733 --- /dev/null +++ b/docs/zh_CN/migration-guides/release-6.x/6.2/security.rst @@ -0,0 +1,15 @@ +安全性 +====== + +:link_to_translation:`en:[English]` + +内存保护 +-------- + +从 **ESP-IDF v6.2** 开始,在 ESP32-C5、ESP32-C6、ESP32-C61、ESP32-H2 和 ESP32-P4 上,二级引导加载程序不再配置或锁定任何 RISC-V PMP 表项。完整的 PMP 配置改由应用程序在启动早期编程并锁定。 + +被引导加载程序锁定的 PMP 表项在下一次 CPU 复位前无法重新配置。由于已部署设备上的引导加载程序通常不会更新,旧版引导加载程序锁定的表项可能与使用较新 ESP-IDF 版本构建的应用程序所期望的 PMP 布局发生冲突,导致启动失败,或使内存保护在没有任何提示的情况下失效。 + +使用 ESP-IDF 构建的应用程序不受影响,因为应用程序会在启动阶段、任何应用代码运行之前,自行编程并锁定完整的 PMP 配置。 + +由 ESP-IDF 二级引导加载程序启动的自定义(非 ESP-IDF)应用程序不得假定交接时任何 PMP 表项已被预先配置或锁定。此前,引导加载程序会配置并锁定覆盖 ROM 和外设地址空间等区域的表项;此类应用程序现在必须自行完成 PMP 配置。