Merge branch 'feat/psram_enc_exempt_v5.5' into 'release/v5.5'

feat(esp_psram): add option to carve unencrypted PSRAM region (v5.5)

See merge request espressif/esp-idf!49931
This commit is contained in:
Jiang Jiang Jian
2026-07-16 17:04:28 +08:00
11 changed files with 312 additions and 9 deletions
@@ -156,3 +156,42 @@ 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 && SOC_PSRAM_ENCRYPTION_PAGE_CONFIGURABLE
help
!!! SECURITY WARNING !!!
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.
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 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.
+18
View File
@@ -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
+108 -4
View File
@@ -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 (upper end of PSRAM, highest physical addresses)
*/
#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
@@ -277,6 +283,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
@@ -365,6 +381,36 @@ 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);
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);
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!
*----------------------------------------------------------------------------*/
@@ -489,6 +535,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
@@ -533,6 +595,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;
@@ -548,6 +617,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) {
@@ -665,6 +749,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;
}
@@ -711,7 +807,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);
@@ -749,7 +849,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) {
+18 -1
View File
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -227,6 +227,23 @@ __attribute__((always_inline)) static inline void mmu_ll_write_entry(uint32_t mm
REG_WRITE(SPI_MEM_MMU_ITEM_CONTENT_REG(0), mmu_raw_value);
}
#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
*
+19 -1
View File
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2024 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"
@@ -229,6 +230,23 @@ __attribute__((always_inline)) static inline void mmu_ll_write_entry(uint32_t mm
REG_WRITE(SPI_MEM_MMU_ITEM_CONTENT_REG(0), mmu_raw_value);
}
#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
*
+22 -1
View File
@@ -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"
@@ -291,6 +292,26 @@ __attribute__((always_inline)) static inline void mmu_ll_write_entry(uint32_t mm
REG_WRITE(content_reg, mmu_val);
}
#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
*
+14 -1
View File
@@ -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
*/
@@ -64,6 +64,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
*
+25 -1
View File
@@ -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
*/
@@ -102,6 +102,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);
+1
View File
@@ -48,6 +48,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