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..93433940489 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,35 +225,152 @@ 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 +// 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; // 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 +382,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,85 +396,36 @@ 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 - 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 | CONDITIONAL_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 && !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 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 | CONDITIONAL_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); - _Static_assert(SOC_PERIPHERAL_LOW < SOC_PERIPHERAL_HIGH, "Invalid peripheral region"); + esp_cpu_configure_region_protection_rev_less_than_v3_default(); } #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 +455,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..505061787e5 --- /dev/null +++ b/components/esp_hw_support/port/esp32p4/private_include/pmp_layout.h @@ -0,0 +1,60 @@ +/* + * 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 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 + * 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 == 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"); + +#ifdef __cplusplus +} +#endif 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 配置。 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)