From 23aba459f52848b0fccd0fac5902cc17a5f113ba Mon Sep 17 00:00:00 2001 From: Mahavir Jain Date: Wed, 6 May 2026 19:53:47 +0530 Subject: [PATCH 1/7] feat(esp_psram): add option to carve unencrypted PSRAM region Adds CONFIG_SPIRAM_ENC_EXEMPT, available on chips that support per-page PSRAM encryption configuration (esp32c5, esp32c61, esp32p4). When enabled, esp_psram carves CONFIG_SPIRAM_ENC_EXEMPT_SIZE off the top of PSRAM and maps it via the new mmu_hal_map_region_no_enc() helper, which writes MMU entries without the SENSITIVE bit. The region is registered as a separate heap pool reachable only through the new MALLOC_CAP_SPIRAM_NO_ENC capability bit, so default SPIRAM allocations cannot accidentally land there. PSRAM encryption imposes alignment constraints that some DMA engines (e.g. 2D-DMA) cannot satisfy. This option lets such workloads place their buffers in unencrypted PSRAM while keeping the rest of PSRAM (and flash) encrypted. Default disabled; security implications are documented in the Kconfig help text. --- components/esp_psram/Kconfig.spiram.common | 37 ++++++++++ components/esp_psram/system_layer/esp_psram.c | 70 ++++++++++++++++++- components/hal/esp32c5/include/hal/mmu_ll.h | 19 ++++- components/hal/esp32c61/include/hal/mmu_ll.h | 20 +++++- components/hal/esp32p4/include/hal/mmu_ll.h | 23 +++++- components/hal/include/hal/mmu_hal.h | 15 +++- components/hal/mmu_hal.c | 26 ++++++- components/heap/include/esp_heap_caps.h | 1 + 8 files changed, 204 insertions(+), 7 deletions(-) diff --git a/components/esp_psram/Kconfig.spiram.common b/components/esp_psram/Kconfig.spiram.common index 2d6c36831c7..e492ec659fc 100644 --- a/components/esp_psram/Kconfig.spiram.common +++ b/components/esp_psram/Kconfig.spiram.common @@ -156,3 +156,40 @@ config SPIRAM_ALLOW_NOINIT_SEG_EXTERNAL_MEMORY Note the values placed into this section will not be initialized at startup and should keep its value after software restart. + +config SPIRAM_ENC_EXEMPT + bool "Reserve a PSRAM region exempt from encryption" + default n + depends on SPIRAM && SECURE_FLASH_ENC_ENABLED && SOC_PSRAM_ENCRYPTION_PAGE_CONFIGURABLE + help + !!! SECURITY WARNING !!! + + Enabling this option carves out a region at the top of PSRAM that is mapped + WITHOUT encryption, even when flash encryption is enabled. Memory allocated + from this region (via MALLOC_CAP_SPIRAM_NO_ENC) is stored in plaintext in + PSRAM and can be observed by an attacker with physical access to the PSRAM + interface. + + DO NOT enable this unless you have weighed the trade-off: + - You accept that any data placed in this region is unprotected at rest. + - You will only allocate workloads that are not security-sensitive (e.g. + video frame buffers, intermediate codec scratch buffers) into this region. + - You understand that PSRAM-resident TLS state, keys, or other secrets + MUST NOT be allocated with MALLOC_CAP_SPIRAM_NO_ENC. + + Use case: PSRAM encryption imposes alignment constraints on buffers that + cross PSRAM. Some DMA engines (e.g. 2D-DMA) cannot satisfy these alignment + requirements, so DMA into encrypted PSRAM fails. This option lets such + workloads place their buffers in an unencrypted PSRAM region while keeping + the rest of PSRAM (and flash) encrypted. + +config SPIRAM_ENC_EXEMPT_SIZE + int "Size of the unencrypted PSRAM region (KB)" + default 256 + range 64 65536 + depends on SPIRAM_ENC_EXEMPT + help + Size of the PSRAM region carved out at the top of PSRAM and mapped without + encryption. Rounded up to a multiple of the MMU page size (typically 64 KB). + The region is registered as a separate heap pool, accessible only via + MALLOC_CAP_SPIRAM_NO_ENC. diff --git a/components/esp_psram/system_layer/esp_psram.c b/components/esp_psram/system_layer/esp_psram.c index 285b0dfec6d..594c6d4c9d8 100644 --- a/components/esp_psram/system_layer/esp_psram.c +++ b/components/esp_psram/system_layer/esp_psram.c @@ -49,13 +49,19 @@ #define BYTES_TO_MMU_PAGE(bytes) ((bytes) / MMU_PAGE_SIZE) /** - * Two types of PSRAM memory regions for now: + * PSRAM memory regions: * - 8bit aligned * - 32bit aligned + * - Optional: encryption-exempt carve-out (top of PSRAM) */ -#define PSRAM_MEM_TYPE_NUM 2 #define PSRAM_MEM_8BIT_ALIGNED 0 #define PSRAM_MEM_32BIT_ALIGNED 1 +#if CONFIG_SPIRAM_ENC_EXEMPT +#define PSRAM_MEM_ENC_EXEMPT 2 +#define PSRAM_MEM_TYPE_NUM 3 +#else +#define PSRAM_MEM_TYPE_NUM 2 +#endif #if CONFIG_SPIRAM_FLASH_LOAD_TO_PSRAM #define PSRAM_EARLY_LOGI ESP_DRAM_LOGI @@ -283,6 +289,16 @@ static void s_xip_psram_placement(uint32_t *psram_available_size, uint32_t *out_ static void s_psram_mapping(uint32_t psram_available_size, uint32_t start_page) { esp_err_t ret = ESP_FAIL; +#if CONFIG_SPIRAM_ENC_EXEMPT + size_t enc_exempt_size = ALIGN_UP_BY((size_t)CONFIG_SPIRAM_ENC_EXEMPT_SIZE * 1024, MMU_PAGE_SIZE); + if (enc_exempt_size >= psram_available_size) { + ESP_EARLY_LOGE(TAG, "SPIRAM_ENC_EXEMPT_SIZE (%dKB) >= available PSRAM (%dKB); disabling carve-out", + (int)(enc_exempt_size / 1024), (int)(psram_available_size / 1024)); + enc_exempt_size = 0; + } else { + psram_available_size -= enc_exempt_size; + } +#endif //----------------------------------Map the PSRAM physical range to MMU-----------------------------// /** * @note 2 @@ -372,6 +388,33 @@ static void s_psram_mapping(uint32_t psram_available_size, uint32_t start_page) ESP_EARLY_LOGW(TAG, "Virtual address not enough for PSRAM, map as much as we can. %dMB is mapped", total_mapped_size / 1024 / 1024); } +#if CONFIG_SPIRAM_ENC_EXEMPT + if (enc_exempt_size) { + const void *v_start_no_enc = NULL; + ret = esp_mmu_map_reserve_block_with_caps(enc_exempt_size, + MMU_MEM_CAP_READ | MMU_MEM_CAP_WRITE | MMU_MEM_CAP_8BIT | MMU_MEM_CAP_32BIT, + MMU_TARGET_PSRAM0, &v_start_no_enc); + assert(ret == ESP_OK); + + mmu_hal_map_region_no_enc((uint32_t)v_start_no_enc, MMU_PAGE_TO_BYTES(start_page), enc_exempt_size); + + cache_bus_mask_t bus_mask = cache_ll_l1_get_bus(0, (uint32_t)v_start_no_enc, enc_exempt_size); + cache_ll_l1_enable_bus(0, bus_mask); +#if !CONFIG_ESP_SYSTEM_SINGLE_CORE_MODE + bus_mask = cache_ll_l1_get_bus(1, (uint32_t)v_start_no_enc, enc_exempt_size); + cache_ll_l1_enable_bus(1, bus_mask); +#endif + + s_psram_ctx.mapped_regions[PSRAM_MEM_ENC_EXEMPT].size = enc_exempt_size; + s_psram_ctx.mapped_regions[PSRAM_MEM_ENC_EXEMPT].vaddr_start = (intptr_t)v_start_no_enc; + s_psram_ctx.mapped_regions[PSRAM_MEM_ENC_EXEMPT].vaddr_end = (intptr_t)v_start_no_enc + enc_exempt_size; + s_psram_ctx.regions_to_heap[PSRAM_MEM_ENC_EXEMPT].size = enc_exempt_size; + s_psram_ctx.regions_to_heap[PSRAM_MEM_ENC_EXEMPT].vaddr_start = (intptr_t)v_start_no_enc; + s_psram_ctx.regions_to_heap[PSRAM_MEM_ENC_EXEMPT].vaddr_end = (intptr_t)v_start_no_enc + enc_exempt_size; + ESP_EARLY_LOGI(TAG, "PSRAM unencrypted region: 0x%x B at %p", (unsigned)enc_exempt_size, v_start_no_enc); + } +#endif /* CONFIG_SPIRAM_ENC_EXEMPT */ + /*------------------------------------------------------------------------------ * After mapping, we DON'T care about the PSRAM PHYSICAL ADDRESS ANYMORE! *----------------------------------------------------------------------------*/ @@ -500,6 +543,22 @@ esp_err_t esp_psram_extram_add_to_heap_allocator(void) ESP_EARLY_LOGI(TAG, "Adding pool of %dK of PSRAM memory to heap allocator", (s_psram_ctx.regions_to_heap[PSRAM_MEM_8BIT_ALIGNED].size + s_psram_ctx.regions_to_heap[PSRAM_MEM_32BIT_ALIGNED].size) / 1024); +#if CONFIG_SPIRAM_ENC_EXEMPT + if (s_psram_ctx.regions_to_heap[PSRAM_MEM_ENC_EXEMPT].size) { + // Only MALLOC_CAP_SPIRAM_NO_ENC: any other bit here would let generic 8BIT/32BIT + // allocations fall through to this region as a low-priority match. + uint32_t no_enc_caps[] = {MALLOC_CAP_SPIRAM_NO_ENC, 0, 0}; + ret = heap_caps_add_region_with_caps(no_enc_caps, + s_psram_ctx.regions_to_heap[PSRAM_MEM_ENC_EXEMPT].vaddr_start, + s_psram_ctx.regions_to_heap[PSRAM_MEM_ENC_EXEMPT].vaddr_end); + if (ret != ESP_OK) { + return ret; + } + ESP_EARLY_LOGI(TAG, "Adding pool of %dK of unencrypted PSRAM memory to heap allocator", + (int)(s_psram_ctx.regions_to_heap[PSRAM_MEM_ENC_EXEMPT].size / 1024)); + } +#endif + // To allow using the page alignment gaps created while mapping the flash segments, // the alignment gaps must be configured with correct memory protection configurations. #if CONFIG_SPIRAM_PRE_CONFIGURE_MEMORY_PROTECTION @@ -544,6 +603,13 @@ bool IRAM_ATTR esp_psram_check_ptr_addr(const void *p) return true; } +#if CONFIG_SPIRAM_ENC_EXEMPT + if ((intptr_t)p >= s_psram_ctx.mapped_regions[PSRAM_MEM_ENC_EXEMPT].vaddr_start && + (intptr_t)p < s_psram_ctx.mapped_regions[PSRAM_MEM_ENC_EXEMPT].vaddr_end) { + return true; + } +#endif + #if CONFIG_SPIRAM_RODATA if (mmu_psram_check_ptr_addr_in_xip_psram_rodata_region(p)) { return true; diff --git a/components/hal/esp32c5/include/hal/mmu_ll.h b/components/hal/esp32c5/include/hal/mmu_ll.h index 79df1a70676..ccbd9c3ab9d 100644 --- a/components/hal/esp32c5/include/hal/mmu_ll.h +++ b/components/hal/esp32c5/include/hal/mmu_ll.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -237,6 +237,23 @@ __attribute__((always_inline)) static inline void mmu_ll_write_entry(uint32_t mm } } +#if SOC_PSRAM_ENCRYPTION_PAGE_CONFIGURABLE +/** + * Write a PSRAM MMU entry without the SENSITIVE bit, used only for the + * carved-out unencrypted region (see CONFIG_SPIRAM_ENC_EXEMPT). + * + * No anti-FI check: the SENSITIVE bit is intentionally clear, and an FI flip + * that sets it would force decryption of plaintext data (garbage, fails safe). + */ +__attribute__((always_inline)) static inline void mmu_ll_write_entry_no_enc(uint32_t mmu_id, uint32_t entry_id, uint32_t mmu_val) +{ + (void)mmu_id; + uint32_t mmu_raw_value = mmu_val | SOC_MMU_ACCESS_SPIRAM | SOC_MMU_VALID; + REG_WRITE(SPI_MEM_MMU_ITEM_INDEX_REG(0), entry_id); + REG_WRITE(SPI_MEM_MMU_ITEM_CONTENT_REG(0), mmu_raw_value); +} +#endif + /** * Read the raw value from MMU table * diff --git a/components/hal/esp32c61/include/hal/mmu_ll.h b/components/hal/esp32c61/include/hal/mmu_ll.h index b2a2c893f3c..551baa6395c 100644 --- a/components/hal/esp32c61/include/hal/mmu_ll.h +++ b/components/hal/esp32c61/include/hal/mmu_ll.h @@ -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 */ @@ -10,6 +10,7 @@ #include "soc/spi_mem_reg.h" #include "soc/ext_mem_defs.h" +#include "soc/soc_caps.h" #include "hal/assert.h" #include "hal/mmu_types.h" #include "hal/efuse_ll.h" @@ -237,6 +238,23 @@ __attribute__((always_inline)) static inline void mmu_ll_write_entry(uint32_t mm } } +#if SOC_PSRAM_ENCRYPTION_PAGE_CONFIGURABLE +/** + * Write a PSRAM MMU entry without the SENSITIVE bit, used only for the + * carved-out unencrypted region (see CONFIG_SPIRAM_ENC_EXEMPT). + * + * No anti-FI check: the SENSITIVE bit is intentionally clear, and an FI flip + * that sets it would force decryption of plaintext data (garbage, fails safe). + */ +__attribute__((always_inline)) static inline void mmu_ll_write_entry_no_enc(uint32_t mmu_id, uint32_t entry_id, uint32_t mmu_val) +{ + (void)mmu_id; + uint32_t mmu_raw_value = mmu_val | SOC_MMU_ACCESS_SPIRAM | SOC_MMU_VALID; + REG_WRITE(SPI_MEM_MMU_ITEM_INDEX_REG(0), entry_id); + REG_WRITE(SPI_MEM_MMU_ITEM_CONTENT_REG(0), mmu_raw_value); +} +#endif + /** * Read the raw value from MMU table * diff --git a/components/hal/esp32p4/include/hal/mmu_ll.h b/components/hal/esp32p4/include/hal/mmu_ll.h index e86d1a18fbd..aeebe6eed86 100644 --- a/components/hal/esp32p4/include/hal/mmu_ll.h +++ b/components/hal/esp32p4/include/hal/mmu_ll.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -11,6 +11,7 @@ #include "soc/spi_mem_c_reg.h" #include "soc/spi_mem_s_reg.h" #include "soc/ext_mem_defs.h" +#include "soc/soc_caps.h" #include "hal/assert.h" #include "hal/mmu_types.h" #include "hal/efuse_ll.h" @@ -301,6 +302,26 @@ __attribute__((always_inline)) static inline void mmu_ll_write_entry(uint32_t mm } } +#if SOC_PSRAM_ENCRYPTION_PAGE_CONFIGURABLE +/** + * Write a PSRAM MMU entry without the SENSITIVE bit, used only for the + * carved-out unencrypted region (see CONFIG_SPIRAM_ENC_EXEMPT). + * + * No anti-FI check: the SENSITIVE bit is intentionally clear, and an FI flip + * that sets it would force decryption of plaintext data (garbage, fails safe). + */ +__attribute__((always_inline)) static inline void mmu_ll_write_entry_no_enc(uint32_t mmu_id, uint32_t entry_id, uint32_t mmu_val) +{ + HAL_ASSERT(mmu_id == MMU_LL_PSRAM_MMU_ID); + + mmu_val |= SOC_MMU_PSRAM_VALID; + mmu_val |= SOC_MMU_ACCESS_PSRAM; + + REG_WRITE(SPI_MEM_S_MMU_ITEM_INDEX_REG, entry_id); + REG_WRITE(SPI_MEM_S_MMU_ITEM_CONTENT_REG, mmu_val); +} +#endif + /** * Read the raw value from MMU table * diff --git a/components/hal/include/hal/mmu_hal.h b/components/hal/include/hal/mmu_hal.h index 6d8563a1fef..779af2d068e 100644 --- a/components/hal/include/hal/mmu_hal.h +++ b/components/hal/include/hal/mmu_hal.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2010-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2010-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -81,6 +81,19 @@ uint32_t mmu_hal_bytes_to_pages(uint32_t mmu_id, uint32_t bytes); */ void mmu_hal_map_region(uint32_t mmu_id, mmu_target_t mem_type, uint32_t vaddr, uint32_t paddr, uint32_t len, uint32_t *out_len); +#if SOC_PSRAM_ENCRYPTION_PAGE_CONFIGURABLE +/** + * Map a PSRAM physical range to virtual memory without setting the encryption + * SENSITIVE bit on each MMU entry. Used only for the explicitly carved-out + * unencrypted PSRAM region (see CONFIG_SPIRAM_ENC_EXEMPT). + * + * @param vaddr start virtual address (MMU-page-aligned) + * @param paddr start physical address (MMU-page-aligned) + * @param len length in bytes + */ +void mmu_hal_map_region_no_enc(uint32_t vaddr, uint32_t paddr, uint32_t len); +#endif + /** * To unmap a virtual address block that is mapped to a physical memory block previously * diff --git a/components/hal/mmu_hal.c b/components/hal/mmu_hal.c index 95441f7463e..5c83a9681fd 100644 --- a/components/hal/mmu_hal.c +++ b/components/hal/mmu_hal.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -113,6 +113,30 @@ void mmu_hal_map_region(uint32_t mmu_id, mmu_target_t mem_type, uint32_t vaddr, } } +#if SOC_PSRAM_ENCRYPTION_PAGE_CONFIGURABLE +void mmu_hal_map_region_no_enc(uint32_t vaddr, uint32_t paddr, uint32_t len) +{ + uint32_t mmu_id = MMU_LL_PSRAM_MMU_ID; + uint32_t page_size_in_bytes = mmu_hal_pages_to_bytes(mmu_id, 1); + HAL_ASSERT(vaddr % page_size_in_bytes == 0); + HAL_ASSERT(paddr % page_size_in_bytes == 0); + HAL_ASSERT(mmu_ll_check_valid_paddr_region(mmu_id, paddr, len)); + // Restrict to data vaddr space — unencrypted PSRAM must never back code/rodata. + HAL_ASSERT(mmu_hal_check_valid_ext_vaddr_region(mmu_id, vaddr, len, MMU_VADDR_DATA)); + + uint32_t page_num = (len + page_size_in_bytes - 1) / page_size_in_bytes; + uint32_t mmu_val = mmu_ll_format_paddr(mmu_id, paddr, MMU_TARGET_PSRAM0); + + while (page_num) { + uint32_t entry_id = mmu_ll_get_entry_id(mmu_id, vaddr); + mmu_ll_write_entry_no_enc(mmu_id, entry_id, mmu_val); + vaddr += page_size_in_bytes; + mmu_val++; + page_num--; + } +} +#endif + void mmu_hal_unmap_region(uint32_t mmu_id, uint32_t vaddr, uint32_t len) { uint32_t page_size_in_bytes = mmu_hal_pages_to_bytes(mmu_id, 1); diff --git a/components/heap/include/esp_heap_caps.h b/components/heap/include/esp_heap_caps.h index 08e87426446..f753d72c7e7 100644 --- a/components/heap/include/esp_heap_caps.h +++ b/components/heap/include/esp_heap_caps.h @@ -50,6 +50,7 @@ extern "C" { #define MALLOC_CAP_DMA_DESC_AXI (1<<18) ///< Memory must be capable of containing AXI DMA descriptors #define MALLOC_CAP_CACHE_ALIGNED (1<<19) ///< Memory must be aligned to the cache line size of any intermediate caches #define MALLOC_CAP_SIMD (1<<20) ///< Memory must be capable of being used for SIMD instructions (i.e. allow for SIMD-specific-bit data accesses) +#define MALLOC_CAP_SPIRAM_NO_ENC (1<<21) ///< Memory must be in the PSRAM region exempt from flash encryption (plaintext in PSRAM; see CONFIG_SPIRAM_ENC_EXEMPT) #define MALLOC_CAP_INVALID (1<<31) ///< Memory can't be used / list end marker From ac3de1ded73c072ed8797a0887db3dec4f169103 Mon Sep 17 00:00:00 2001 From: Mahavir Jain Date: Wed, 6 May 2026 19:54:00 +0530 Subject: [PATCH 2/7] docs(esp_psram): document MALLOC_CAP_SPIRAM_NO_ENC carve-out Extends the External RAM encryption section to describe CONFIG_SPIRAM_ENC_EXEMPT and the MALLOC_CAP_SPIRAM_NO_ENC heap capability, including a security warning and a typical DMA-alignment use case. Mirrors the change in zh_CN. --- components/esp_psram/Kconfig.spiram.common | 2 +- docs/en/api-guides/external-ram.rst | 22 ++++++++++++++++++++++ docs/zh_CN/api-guides/external-ram.rst | 22 ++++++++++++++++++++++ 3 files changed, 45 insertions(+), 1 deletion(-) diff --git a/components/esp_psram/Kconfig.spiram.common b/components/esp_psram/Kconfig.spiram.common index e492ec659fc..7273e090efc 100644 --- a/components/esp_psram/Kconfig.spiram.common +++ b/components/esp_psram/Kconfig.spiram.common @@ -160,7 +160,7 @@ config SPIRAM_ALLOW_NOINIT_SEG_EXTERNAL_MEMORY config SPIRAM_ENC_EXEMPT bool "Reserve a PSRAM region exempt from encryption" default n - depends on SPIRAM && SECURE_FLASH_ENC_ENABLED && SOC_PSRAM_ENCRYPTION_PAGE_CONFIGURABLE + depends on SPIRAM && SOC_PSRAM_ENCRYPTION_PAGE_CONFIGURABLE help !!! SECURITY WARNING !!! diff --git a/docs/en/api-guides/external-ram.rst b/docs/en/api-guides/external-ram.rst index d23182760b2..55030aba2d0 100644 --- a/docs/en/api-guides/external-ram.rst +++ b/docs/en/api-guides/external-ram.rst @@ -249,6 +249,28 @@ By default, failure to initialize external RAM will cause the ESP-IDF startup to On {IDF_TARGET_NAME}, PSRAM encryption can be controlled on a per-MMU-page basis, allowing individual PSRAM pages to be selectively encrypted or left unencrypted. However, in the default configuration, all PSRAM pages are encrypted when flash encryption is enabled. + Reserving an Unencrypted PSRAM Region + ------------------------------------- + + Enabling :ref:`CONFIG_SPIRAM_ENC_EXEMPT` reserves a region at the top of PSRAM (sized by :ref:`CONFIG_SPIRAM_ENC_EXEMPT_SIZE`, in KB, rounded up to the MMU page size) that is mapped without encryption. This region is registered as a separate heap pool reachable only via the ``MALLOC_CAP_SPIRAM_NO_ENC`` capability. The rest of PSRAM (and flash) remains encrypted. + + .. warning:: + + Memory allocated with ``MALLOC_CAP_SPIRAM_NO_ENC`` is stored as plaintext in PSRAM and can be observed by an attacker with physical access to the PSRAM interface. Never place TLS state, keys, or other secrets in this region. + + Typical use case: PSRAM encryption imposes alignment constraints on buffers that some DMA engines (for example, 2D-DMA) cannot satisfy. Buffers that need to be DMA-accessed from such engines can be allocated from this unencrypted region: + + .. code-block:: c + + #if CONFIG_SPIRAM_ENC_EXEMPT + uint32_t caps = MALLOC_CAP_SPIRAM_NO_ENC; + #else + uint32_t caps = MALLOC_CAP_SPIRAM; + #endif + uint8_t *buf = heap_caps_malloc(buf_size, caps); + + ``MALLOC_CAP_SPIRAM_NO_ENC`` must be requested explicitly. It is intentionally not combined with ``MALLOC_CAP_SPIRAM`` or ``MALLOC_CAP_DEFAULT``, so ordinary SPIRAM/heap allocations cannot accidentally land in the unencrypted region. + .. only:: SOC_PSRAM_ENCRYPTION_SEPARATE_KEY On {IDF_TARGET_NAME}, PSRAM encryption can use an independent encryption key. If the PSRAM encryption key is not programmed, the flash encryption key will be used as the PSRAM encryption key. diff --git a/docs/zh_CN/api-guides/external-ram.rst b/docs/zh_CN/api-guides/external-ram.rst index 4271abd043f..6c3637dd9b0 100644 --- a/docs/zh_CN/api-guides/external-ram.rst +++ b/docs/zh_CN/api-guides/external-ram.rst @@ -249,6 +249,28 @@ ESP-IDF 启动过程中,片外 RAM 被映射到数据虚拟地址空间,该 在 {IDF_TARGET_NAME} 上,PSRAM 加密可以按 MMU 页面粒度进行控制,允许对单个 PSRAM 页面选择性地加密或不加密。但在默认配置下,启用 flash 加密时所有 PSRAM 页面都会被加密。 + 预留未加密的 PSRAM 区域 + ----------------------- + + 启用 :ref:`CONFIG_SPIRAM_ENC_EXEMPT` 会在 PSRAM 顶部预留一段区域(大小由 :ref:`CONFIG_SPIRAM_ENC_EXEMPT_SIZE` 指定,单位为 KB,向上取整到 MMU 页面大小),该区域在映射时不启用加密。此区域被注册为一个独立的堆池,仅可通过 ``MALLOC_CAP_SPIRAM_NO_ENC`` 能力位访问。其余 PSRAM(以及 flash)仍然保持加密。 + + .. warning:: + + 通过 ``MALLOC_CAP_SPIRAM_NO_ENC`` 分配的内存以明文形式存储在 PSRAM 中,攻击者若能物理接触 PSRAM 接口即可读取其内容。切勿将 TLS 状态、密钥或其他敏感数据放入该区域。 + + 典型使用场景:PSRAM 加密会对缓冲区施加对齐约束,部分 DMA 引擎(如 2D-DMA)无法满足这些约束。需要被此类引擎进行 DMA 访问的缓冲区可以从该未加密区域分配: + + .. code-block:: c + + #if CONFIG_SPIRAM_ENC_EXEMPT + uint32_t caps = MALLOC_CAP_SPIRAM_NO_ENC; + #else + uint32_t caps = MALLOC_CAP_SPIRAM; + #endif + uint8_t *buf = heap_caps_malloc(buf_size, caps); + + 必须显式请求 ``MALLOC_CAP_SPIRAM_NO_ENC``。该能力位有意未与 ``MALLOC_CAP_SPIRAM`` 或 ``MALLOC_CAP_DEFAULT`` 组合,因此普通 SPIRAM/堆分配不会意外落入该未加密区域。 + .. only:: SOC_PSRAM_ENCRYPTION_SEPARATE_KEY 在 {IDF_TARGET_NAME} 上,PSRAM 加密可以使用独立的加密密钥。如果未烧录 PSRAM 加密密钥,则会使用 flash 加密密钥作为 PSRAM 加密密钥。 From b4517542ae65b2d8ebadae920b0d04437a9d8d1d Mon Sep 17 00:00:00 2001 From: "harshal.patil" Date: Tue, 19 May 2026 21:28:36 +0530 Subject: [PATCH 3/7] change(esp_psram): Consider all PSRAM regions in PMP protection --- components/esp_psram/system_layer/esp_psram.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/components/esp_psram/system_layer/esp_psram.c b/components/esp_psram/system_layer/esp_psram.c index 594c6d4c9d8..3d3b4071d9b 100644 --- a/components/esp_psram/system_layer/esp_psram.c +++ b/components/esp_psram/system_layer/esp_psram.c @@ -788,7 +788,11 @@ static size_t esp_psram_get_effective_mapped_size(void) } if (s_psram_ctx.is_initialised) { - return s_psram_ctx.mapped_regions[PSRAM_MEM_8BIT_ALIGNED].size + s_psram_ctx.mapped_regions[PSRAM_MEM_32BIT_ALIGNED].size; + size_t mapped = 0; + for (int i = 0; i < PSRAM_MEM_TYPE_NUM; i++) { + mapped += s_psram_ctx.mapped_regions[i].size; + } + return mapped; } else { uint32_t psram_available_size = 0; esp_err_t ret = esp_psram_impl_get_available_size(&psram_available_size); @@ -827,7 +831,11 @@ size_t esp_psram_get_heap_size_to_protect(void) } if (s_psram_ctx.is_initialised) { - return s_psram_ctx.regions_to_heap[PSRAM_MEM_8BIT_ALIGNED].size + s_psram_ctx.regions_to_heap[PSRAM_MEM_32BIT_ALIGNED].size; + size_t heap = 0; + for (int i = 0; i < PSRAM_MEM_TYPE_NUM; i++) { + heap += s_psram_ctx.regions_to_heap[i].size; + } + return heap; } else { size_t effective_mapped_size = esp_psram_get_effective_mapped_size(); if (effective_mapped_size == 0) { From 99fe60f404c31ce556ebbf4fe18e3a35cd98862e Mon Sep 17 00:00:00 2001 From: Mahavir Jain Date: Wed, 20 May 2026 10:24:28 +0530 Subject: [PATCH 4/7] fix(esp_psram): handle carve-out vaddr exhaustion and add region to self-test If the virtual-address pool is exhausted when reserving the unencrypted PSRAM carve-out (the warning at L388 may already have fired for the main mapping), esp_mmu_map_reserve_block_with_caps() returns an error rather than aborting. Convert the previous assert() into a logged fallback that disables the carve-out for this boot, mirroring the SPIRAM_ENC_EXEMPT_SIZE >= psram_available_size path. Also extend esp_psram_extram_test() to run the standard memory test on the carve-out region when CONFIG_SPIRAM_ENC_EXEMPT is enabled, so the unencrypted mapping is exercised on startup like the other PSRAM regions. --- components/esp_psram/system_layer/esp_psram.c | 43 +++++++++++++------ 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/components/esp_psram/system_layer/esp_psram.c b/components/esp_psram/system_layer/esp_psram.c index 3d3b4071d9b..86c7b4d56dc 100644 --- a/components/esp_psram/system_layer/esp_psram.c +++ b/components/esp_psram/system_layer/esp_psram.c @@ -394,24 +394,27 @@ static void s_psram_mapping(uint32_t psram_available_size, uint32_t start_page) ret = esp_mmu_map_reserve_block_with_caps(enc_exempt_size, MMU_MEM_CAP_READ | MMU_MEM_CAP_WRITE | MMU_MEM_CAP_8BIT | MMU_MEM_CAP_32BIT, MMU_TARGET_PSRAM0, &v_start_no_enc); - assert(ret == ESP_OK); + if (ret != ESP_OK) { + ESP_EARLY_LOGE(TAG, "Virtual address pool exhausted; disabling SPIRAM_ENC_EXEMPT carve-out (%dKB)", + (int)(enc_exempt_size / 1024)); + } else { + mmu_hal_map_region_no_enc((uint32_t)v_start_no_enc, MMU_PAGE_TO_BYTES(start_page), enc_exempt_size); - mmu_hal_map_region_no_enc((uint32_t)v_start_no_enc, MMU_PAGE_TO_BYTES(start_page), enc_exempt_size); - - cache_bus_mask_t bus_mask = cache_ll_l1_get_bus(0, (uint32_t)v_start_no_enc, enc_exempt_size); - cache_ll_l1_enable_bus(0, bus_mask); + cache_bus_mask_t bus_mask = cache_ll_l1_get_bus(0, (uint32_t)v_start_no_enc, enc_exempt_size); + cache_ll_l1_enable_bus(0, bus_mask); #if !CONFIG_ESP_SYSTEM_SINGLE_CORE_MODE - bus_mask = cache_ll_l1_get_bus(1, (uint32_t)v_start_no_enc, enc_exempt_size); - cache_ll_l1_enable_bus(1, bus_mask); + bus_mask = cache_ll_l1_get_bus(1, (uint32_t)v_start_no_enc, enc_exempt_size); + cache_ll_l1_enable_bus(1, bus_mask); #endif - s_psram_ctx.mapped_regions[PSRAM_MEM_ENC_EXEMPT].size = enc_exempt_size; - s_psram_ctx.mapped_regions[PSRAM_MEM_ENC_EXEMPT].vaddr_start = (intptr_t)v_start_no_enc; - s_psram_ctx.mapped_regions[PSRAM_MEM_ENC_EXEMPT].vaddr_end = (intptr_t)v_start_no_enc + enc_exempt_size; - s_psram_ctx.regions_to_heap[PSRAM_MEM_ENC_EXEMPT].size = enc_exempt_size; - s_psram_ctx.regions_to_heap[PSRAM_MEM_ENC_EXEMPT].vaddr_start = (intptr_t)v_start_no_enc; - s_psram_ctx.regions_to_heap[PSRAM_MEM_ENC_EXEMPT].vaddr_end = (intptr_t)v_start_no_enc + enc_exempt_size; - ESP_EARLY_LOGI(TAG, "PSRAM unencrypted region: 0x%x B at %p", (unsigned)enc_exempt_size, v_start_no_enc); + s_psram_ctx.mapped_regions[PSRAM_MEM_ENC_EXEMPT].size = enc_exempt_size; + s_psram_ctx.mapped_regions[PSRAM_MEM_ENC_EXEMPT].vaddr_start = (intptr_t)v_start_no_enc; + s_psram_ctx.mapped_regions[PSRAM_MEM_ENC_EXEMPT].vaddr_end = (intptr_t)v_start_no_enc + enc_exempt_size; + s_psram_ctx.regions_to_heap[PSRAM_MEM_ENC_EXEMPT].size = enc_exempt_size; + s_psram_ctx.regions_to_heap[PSRAM_MEM_ENC_EXEMPT].vaddr_start = (intptr_t)v_start_no_enc; + s_psram_ctx.regions_to_heap[PSRAM_MEM_ENC_EXEMPT].vaddr_end = (intptr_t)v_start_no_enc + enc_exempt_size; + ESP_EARLY_LOGI(TAG, "PSRAM unencrypted region: 0x%x B at %p", (unsigned)enc_exempt_size, v_start_no_enc); + } } #endif /* CONFIG_SPIRAM_ENC_EXEMPT */ @@ -742,6 +745,18 @@ bool esp_psram_extram_test(void) return false; } +#if CONFIG_SPIRAM_ENC_EXEMPT + if (s_psram_ctx.mapped_regions[PSRAM_MEM_ENC_EXEMPT].size) { + test_success = s_test_psram(s_psram_ctx.mapped_regions[PSRAM_MEM_ENC_EXEMPT].vaddr_start, + s_psram_ctx.mapped_regions[PSRAM_MEM_ENC_EXEMPT].size, + 0, + 0); + } + if (!test_success) { + return false; + } +#endif + return true; } From 231dc0e8845e680ab4ff636b51e6f7f8582fa97c Mon Sep 17 00:00:00 2001 From: Mahavir Jain Date: Wed, 20 May 2026 10:28:06 +0530 Subject: [PATCH 5/7] feat(esp_psram): add esp_psram_ptr_is_no_enc() helper Drivers that allocate from the unencrypted PSRAM carve-out via MALLOC_CAP_SPIRAM_NO_ENC currently have no way to verify after the fact which pool a buffer came from. This is particularly relevant for callers using heap_caps_malloc_prefer(MALLOC_CAP_SPIRAM_NO_ENC, MALLOC_CAP_SPIRAM), where a silent fallback to encrypted PSRAM would still pass the typical esp_ptr_external_ram() check. Expose esp_psram_ptr_is_no_enc() in the public esp_psram.h header. It performs a range check against the carve-out's virtual-address window and returns false when PSRAM is not initialized or CONFIG_SPIRAM_ENC_EXEMPT is disabled, so callers do not need to guard the call site with #if. Also reference the helper from the External RAM documentation alongside the heap_caps_malloc(MALLOC_CAP_SPIRAM_NO_ENC) usage example. --- components/esp_psram/include/esp_psram.h | 18 ++++++++++++++++++ components/esp_psram/system_layer/esp_psram.c | 15 +++++++++++++++ docs/en/api-guides/external-ram.rst | 2 ++ 3 files changed, 35 insertions(+) diff --git a/components/esp_psram/include/esp_psram.h b/components/esp_psram/include/esp_psram.h index 27c7f7b4a55..562e851d1e9 100644 --- a/components/esp_psram/include/esp_psram.h +++ b/components/esp_psram/include/esp_psram.h @@ -42,6 +42,24 @@ bool esp_psram_is_initialized(void); */ size_t esp_psram_get_size(void); +/** + * @brief Check if the pointer falls inside the unencrypted PSRAM carve-out region + * + * When @c CONFIG_SPIRAM_ENC_EXEMPT is enabled, esp_psram reserves a region of PSRAM + * that is mapped without encryption and exposed through the @c MALLOC_CAP_SPIRAM_NO_ENC + * heap capability. This function lets drivers verify whether a buffer returned by the + * heap allocator actually lives in that unencrypted region — useful for example after + * a @c heap_caps_malloc_prefer() call that may have fallen back to encrypted PSRAM. + * + * @param[in] p The pointer to check + * + * @return + * - true: the pointer is within the unencrypted PSRAM carve-out + * - false: the pointer is not in the carve-out, PSRAM is not initialized, + * or @c CONFIG_SPIRAM_ENC_EXEMPT is disabled + */ +bool esp_psram_ptr_is_no_enc(const void *p); + #ifdef __cplusplus } #endif diff --git a/components/esp_psram/system_layer/esp_psram.c b/components/esp_psram/system_layer/esp_psram.c index 86c7b4d56dc..c58ffe41219 100644 --- a/components/esp_psram/system_layer/esp_psram.c +++ b/components/esp_psram/system_layer/esp_psram.c @@ -628,6 +628,21 @@ bool IRAM_ATTR esp_psram_check_ptr_addr(const void *p) return false; } +bool IRAM_ATTR esp_psram_ptr_is_no_enc(const void *p) +{ +#if CONFIG_SPIRAM_ENC_EXEMPT + if (!s_psram_ctx.is_initialised) { + return false; + } + + return ((intptr_t)p >= s_psram_ctx.mapped_regions[PSRAM_MEM_ENC_EXEMPT].vaddr_start && + (intptr_t)p < s_psram_ctx.mapped_regions[PSRAM_MEM_ENC_EXEMPT].vaddr_end); +#else + (void)p; + return false; +#endif +} + esp_err_t esp_psram_extram_reserve_dma_pool(size_t size) { if (size == 0) { diff --git a/docs/en/api-guides/external-ram.rst b/docs/en/api-guides/external-ram.rst index 55030aba2d0..f68ab4721f1 100644 --- a/docs/en/api-guides/external-ram.rst +++ b/docs/en/api-guides/external-ram.rst @@ -271,6 +271,8 @@ By default, failure to initialize external RAM will cause the ESP-IDF startup to ``MALLOC_CAP_SPIRAM_NO_ENC`` must be requested explicitly. It is intentionally not combined with ``MALLOC_CAP_SPIRAM`` or ``MALLOC_CAP_DEFAULT``, so ordinary SPIRAM/heap allocations cannot accidentally land in the unencrypted region. + To verify after the fact that a buffer was allocated from the unencrypted carve-out (for example after a ``heap_caps_malloc_prefer()`` call that may have fallen back to encrypted PSRAM), use :cpp:func:`esp_psram_ptr_is_no_enc`. + .. only:: SOC_PSRAM_ENCRYPTION_SEPARATE_KEY On {IDF_TARGET_NAME}, PSRAM encryption can use an independent encryption key. If the PSRAM encryption key is not programmed, the flash encryption key will be used as the PSRAM encryption key. From 9dff0c23b707e042421751ea712f706be394e7cd Mon Sep 17 00:00:00 2001 From: Mahavir Jain Date: Wed, 20 May 2026 10:28:17 +0530 Subject: [PATCH 6/7] docs(esp_psram): clarify carve-out location is at upper end of PSRAM The previous wording "top of PSRAM" was ambiguous: the carve-out is actually mapped at the highest physical addresses of PSRAM (after the rodata, text, and main heap mappings). Update the Kconfig help text for SPIRAM_ENC_EXEMPT and SPIRAM_ENC_EXEMPT_SIZE, the External RAM documentation, and the internal layout comment to say "upper end of PSRAM (highest physical addresses)" instead. --- components/esp_psram/Kconfig.spiram.common | 10 ++++++---- components/esp_psram/system_layer/esp_psram.c | 2 +- docs/en/api-guides/external-ram.rst | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/components/esp_psram/Kconfig.spiram.common b/components/esp_psram/Kconfig.spiram.common index 7273e090efc..dad17a34447 100644 --- a/components/esp_psram/Kconfig.spiram.common +++ b/components/esp_psram/Kconfig.spiram.common @@ -164,8 +164,9 @@ config SPIRAM_ENC_EXEMPT help !!! SECURITY WARNING !!! - Enabling this option carves out a region at the top of PSRAM that is mapped - WITHOUT encryption, even when flash encryption is enabled. Memory allocated + Enabling this option carves out a region at the upper end of PSRAM (highest + physical addresses) that is mapped WITHOUT encryption, even when flash + encryption is enabled. Memory allocated from this region (via MALLOC_CAP_SPIRAM_NO_ENC) is stored in plaintext in PSRAM and can be observed by an attacker with physical access to the PSRAM interface. @@ -189,7 +190,8 @@ config SPIRAM_ENC_EXEMPT_SIZE range 64 65536 depends on SPIRAM_ENC_EXEMPT help - Size of the PSRAM region carved out at the top of PSRAM and mapped without - encryption. Rounded up to a multiple of the MMU page size (typically 64 KB). + Size of the PSRAM region carved out at the upper end of PSRAM (highest + physical addresses) and mapped without encryption. Rounded up to a multiple + of the MMU page size (typically 64 KB). The region is registered as a separate heap pool, accessible only via MALLOC_CAP_SPIRAM_NO_ENC. diff --git a/components/esp_psram/system_layer/esp_psram.c b/components/esp_psram/system_layer/esp_psram.c index c58ffe41219..7c34e6aea44 100644 --- a/components/esp_psram/system_layer/esp_psram.c +++ b/components/esp_psram/system_layer/esp_psram.c @@ -52,7 +52,7 @@ * PSRAM memory regions: * - 8bit aligned * - 32bit aligned - * - Optional: encryption-exempt carve-out (top of PSRAM) + * - Optional: encryption-exempt carve-out (upper end of PSRAM, highest physical addresses) */ #define PSRAM_MEM_8BIT_ALIGNED 0 #define PSRAM_MEM_32BIT_ALIGNED 1 diff --git a/docs/en/api-guides/external-ram.rst b/docs/en/api-guides/external-ram.rst index f68ab4721f1..f25c461b1c8 100644 --- a/docs/en/api-guides/external-ram.rst +++ b/docs/en/api-guides/external-ram.rst @@ -252,7 +252,7 @@ By default, failure to initialize external RAM will cause the ESP-IDF startup to Reserving an Unencrypted PSRAM Region ------------------------------------- - Enabling :ref:`CONFIG_SPIRAM_ENC_EXEMPT` reserves a region at the top of PSRAM (sized by :ref:`CONFIG_SPIRAM_ENC_EXEMPT_SIZE`, in KB, rounded up to the MMU page size) that is mapped without encryption. This region is registered as a separate heap pool reachable only via the ``MALLOC_CAP_SPIRAM_NO_ENC`` capability. The rest of PSRAM (and flash) remains encrypted. + Enabling :ref:`CONFIG_SPIRAM_ENC_EXEMPT` reserves a region at the upper end of PSRAM (highest physical addresses; sized by :ref:`CONFIG_SPIRAM_ENC_EXEMPT_SIZE`, in KB, rounded up to the MMU page size) that is mapped without encryption. This region is registered as a separate heap pool reachable only via the ``MALLOC_CAP_SPIRAM_NO_ENC`` capability. The rest of PSRAM (and flash) remains encrypted. .. warning:: From 945f43cba18983b7971c07fd90813be953dcadfe Mon Sep 17 00:00:00 2001 From: Mahavir Jain Date: Wed, 20 May 2026 11:19:42 +0530 Subject: [PATCH 7/7] docs(esp_psram): mirror carve-out review-feedback updates in zh_CN MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mirror the recent EN-side updates in the Chinese External RAM guide: - Replace "PSRAM 顶部" with "PSRAM 上端(最高物理地址区)" to match the clarified wording on the EN side. - Add the verification-helper pointer next to the heap_caps_malloc example so Chinese readers also learn about esp_psram_ptr_is_no_enc(). Also drop the :cpp:func: cross-reference for esp_psram_ptr_is_no_enc on the EN side: esp_psram.h is not in any chip-specific Doxyfile, so Sphinx/Breathe cannot resolve the reference. Use a plain inline code literal instead, matching the existing reference style for esp_psram_get_size and other esp_psram functions in the docs. --- docs/en/api-guides/external-ram.rst | 2 +- docs/zh_CN/api-guides/external-ram.rst | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/en/api-guides/external-ram.rst b/docs/en/api-guides/external-ram.rst index f25c461b1c8..358a08ab0a6 100644 --- a/docs/en/api-guides/external-ram.rst +++ b/docs/en/api-guides/external-ram.rst @@ -271,7 +271,7 @@ By default, failure to initialize external RAM will cause the ESP-IDF startup to ``MALLOC_CAP_SPIRAM_NO_ENC`` must be requested explicitly. It is intentionally not combined with ``MALLOC_CAP_SPIRAM`` or ``MALLOC_CAP_DEFAULT``, so ordinary SPIRAM/heap allocations cannot accidentally land in the unencrypted region. - To verify after the fact that a buffer was allocated from the unencrypted carve-out (for example after a ``heap_caps_malloc_prefer()`` call that may have fallen back to encrypted PSRAM), use :cpp:func:`esp_psram_ptr_is_no_enc`. + To verify after the fact that a buffer was allocated from the unencrypted carve-out (for example after a ``heap_caps_malloc_prefer()`` call that may have fallen back to encrypted PSRAM), use ``esp_psram_ptr_is_no_enc()``. .. only:: SOC_PSRAM_ENCRYPTION_SEPARATE_KEY diff --git a/docs/zh_CN/api-guides/external-ram.rst b/docs/zh_CN/api-guides/external-ram.rst index 6c3637dd9b0..71e7e5e39c8 100644 --- a/docs/zh_CN/api-guides/external-ram.rst +++ b/docs/zh_CN/api-guides/external-ram.rst @@ -252,7 +252,7 @@ ESP-IDF 启动过程中,片外 RAM 被映射到数据虚拟地址空间,该 预留未加密的 PSRAM 区域 ----------------------- - 启用 :ref:`CONFIG_SPIRAM_ENC_EXEMPT` 会在 PSRAM 顶部预留一段区域(大小由 :ref:`CONFIG_SPIRAM_ENC_EXEMPT_SIZE` 指定,单位为 KB,向上取整到 MMU 页面大小),该区域在映射时不启用加密。此区域被注册为一个独立的堆池,仅可通过 ``MALLOC_CAP_SPIRAM_NO_ENC`` 能力位访问。其余 PSRAM(以及 flash)仍然保持加密。 + 启用 :ref:`CONFIG_SPIRAM_ENC_EXEMPT` 会在 PSRAM 上端(最高物理地址区,大小由 :ref:`CONFIG_SPIRAM_ENC_EXEMPT_SIZE` 指定,单位为 KB,向上取整到 MMU 页面大小)预留一段区域,该区域在映射时不启用加密。此区域被注册为一个独立的堆池,仅可通过 ``MALLOC_CAP_SPIRAM_NO_ENC`` 能力位访问。其余 PSRAM(以及 flash)仍然保持加密。 .. warning:: @@ -271,6 +271,8 @@ ESP-IDF 启动过程中,片外 RAM 被映射到数据虚拟地址空间,该 必须显式请求 ``MALLOC_CAP_SPIRAM_NO_ENC``。该能力位有意未与 ``MALLOC_CAP_SPIRAM`` 或 ``MALLOC_CAP_DEFAULT`` 组合,因此普通 SPIRAM/堆分配不会意外落入该未加密区域。 + 如需在分配后验证缓冲区是否确实位于未加密的预留区域(例如调用 ``heap_caps_malloc_prefer()`` 后可能回退到加密的 PSRAM),可使用 ``esp_psram_ptr_is_no_enc()``。 + .. only:: SOC_PSRAM_ENCRYPTION_SEPARATE_KEY 在 {IDF_TARGET_NAME} 上,PSRAM 加密可以使用独立的加密密钥。如果未烧录 PSRAM 加密密钥,则会使用 flash 加密密钥作为 PSRAM 加密密钥。