From 80535b24a0eb145b4b108dd306c54a79005c6964 Mon Sep 17 00:00:00 2001 From: "harshal.patil" Date: Mon, 6 Jul 2026 16:35:13 +0530 Subject: [PATCH] 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