From 21bd94f1165add75f44583cf51025559d68ab6f5 Mon Sep 17 00:00:00 2001 From: Armando Date: Thu, 12 Sep 2024 09:52:52 +0800 Subject: [PATCH 1/5] fix(linker): fixed extern linker symbol type from int to char --- components/esp_psram/mmu_psram_flash.c | 16 ++++++++++++---- components/esp_psram/mmu_psram_flash_v2.c | 16 ++++++++++++---- components/esp_system/port/cpu_start.c | 17 +++++++++++++---- components/spi_flash/flash_mmap.c | 8 ++++---- 4 files changed, 41 insertions(+), 16 deletions(-) diff --git a/components/esp_psram/mmu_psram_flash.c b/components/esp_psram/mmu_psram_flash.c index 64baaaa36c..bb9288410a 100644 --- a/components/esp_psram/mmu_psram_flash.c +++ b/components/esp_psram/mmu_psram_flash.c @@ -136,10 +136,18 @@ esp_err_t mmu_config_psram_rodata_segment(uint32_t start_page, uint32_t psram_si /*---------------------------------------------------------------------------- Part 2 APIs (See @Backgrounds on top of this file) -------------------------------------------------------------------------------*/ -extern int _instruction_reserved_start; -extern int _instruction_reserved_end; -extern int _rodata_reserved_start; -extern int _rodata_reserved_end; +/** + * If using `int`, then for CLANG, with enabled optimization when inlined function is provided with the address of external symbol, the two least bits of the constant used inside that function get cleared. + * Optimizer assumes that address of external symbol should be aligned to 4-bytes and therefore aligns constant value used for bitwise AND operation with that address. + * + * This means `extern int _instruction_reserved_start;` can be unaligned to 4 bytes, whereas using `char` can solve this issue. + * + * As we only use these symbol address, we declare them as `char` here + */ +extern char _instruction_reserved_start; +extern char _instruction_reserved_end; +extern char _rodata_reserved_start; +extern char _rodata_reserved_end; //------------------------------------Copy Flash .text to PSRAM-------------------------------------// #if CONFIG_SPIRAM_FETCH_INSTRUCTIONS diff --git a/components/esp_psram/mmu_psram_flash_v2.c b/components/esp_psram/mmu_psram_flash_v2.c index d29a873bb2..fe9a8407b2 100644 --- a/components/esp_psram/mmu_psram_flash_v2.c +++ b/components/esp_psram/mmu_psram_flash_v2.c @@ -28,10 +28,18 @@ #define ALIGN_UP_BY(num, align) (((num) + ((align) - 1)) & ~((align) - 1)) #define ALIGN_DOWN_BY(num, align) ((num) & (~((align) - 1))) -extern int _instruction_reserved_start; -extern int _instruction_reserved_end; -extern int _rodata_reserved_start; -extern int _rodata_reserved_end; +/** + * If using `int`, then for CLANG, with enabled optimization when inlined function is provided with the address of external symbol, the two least bits of the constant used inside that function get cleared. + * Optimizer assumes that address of external symbol should be aligned to 4-bytes and therefore aligns constant value used for bitwise AND operation with that address. + * + * This means `extern int _instruction_reserved_start;` can be unaligned to 4 bytes, whereas using `char` can solve this issue. + * + * As we only use these symbol address, we declare them as `char` here + */ +extern char _instruction_reserved_start; +extern char _instruction_reserved_end; +extern char _rodata_reserved_start; +extern char _rodata_reserved_end; const static char *TAG = "mmu_psram"; static uint32_t s_irom_vaddr_start; diff --git a/components/esp_system/port/cpu_start.c b/components/esp_system/port/cpu_start.c index ea24e95de5..9e7947bf90 100644 --- a/components/esp_system/port/cpu_start.c +++ b/components/esp_system/port/cpu_start.c @@ -138,10 +138,19 @@ extern int _rtc_bss_end; extern int _bss_bt_start; extern int _bss_bt_end; #endif // CONFIG_BT_LE_RELEASE_IRAM_SUPPORTED -extern int _instruction_reserved_start; -extern int _instruction_reserved_end; -extern int _rodata_reserved_start; -extern int _rodata_reserved_end; + +/** + * If using `int`, then for CLANG, with enabled optimization when inlined function is provided with the address of external symbol, the two least bits of the constant used inside that function get cleared. + * Optimizer assumes that address of external symbol should be aligned to 4-bytes and therefore aligns constant value used for bitwise AND operation with that address. + * + * This means `extern int _instruction_reserved_start;` can be unaligned to 4 bytes, whereas using `char` can solve this issue. + * + * As we only use these symbol address, we declare them as `char` here + */ +extern char _instruction_reserved_start; +extern char _instruction_reserved_end; +extern char _rodata_reserved_start; +extern char _rodata_reserved_end; extern int _vector_table; #if SOC_INT_CLIC_SUPPORTED diff --git a/components/spi_flash/flash_mmap.c b/components/spi_flash/flash_mmap.c index d82db7f811..007324e8df 100644 --- a/components/spi_flash/flash_mmap.c +++ b/components/spi_flash/flash_mmap.c @@ -36,13 +36,13 @@ #include "spi_flash_mmap.h" #if CONFIG_SPIRAM_FETCH_INSTRUCTIONS -extern int _instruction_reserved_start; -extern int _instruction_reserved_end; +extern char _instruction_reserved_start; +extern char _instruction_reserved_end; #endif #if CONFIG_SPIRAM_RODATA -extern int _rodata_reserved_start; -extern int _rodata_reserved_end; +extern char _rodata_reserved_start; +extern char _rodata_reserved_end; #endif #if !CONFIG_SPI_FLASH_ROM_IMPL From 2cb93cd926097d3eb8cad620802ccfac5435ebd7 Mon Sep 17 00:00:00 2001 From: "harshal.patil" Date: Thu, 20 Feb 2025 16:18:20 +0530 Subject: [PATCH 2/5] feat(esp_psram): Add some helper APIs to get usable PSRAM memory size --- .../include/esp_private/mmu_psram_flash.h | 18 ++++++++- components/esp_psram/mmu_psram_flash.c | 35 ++++++++++++------ components/esp_psram/mmu_psram_flash_v2.c | 37 ++++++++++++------- 3 files changed, 64 insertions(+), 26 deletions(-) diff --git a/components/esp_psram/include/esp_private/mmu_psram_flash.h b/components/esp_psram/include/esp_private/mmu_psram_flash.h index 554ab77d16..e70205be53 100644 --- a/components/esp_psram/include/esp_private/mmu_psram_flash.h +++ b/components/esp_psram/include/esp_private/mmu_psram_flash.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -39,6 +39,14 @@ extern "C" { Part 1 APIs (See @Backgrounds on top of this file) -------------------------------------------------------------------------------*/ #if CONFIG_SPIRAM_FETCH_INSTRUCTIONS + +/** + * @brief Calculates the size of memory that would be used for copying flash texts into PSRAM (in bytes) + * + * @return size_t size of memory that would be used for copying flash texts into PSRAM (in bytes) + */ +size_t mmu_psram_get_text_segment_length(void); + /** * @brief Copy Flash texts to PSRAM * @@ -50,6 +58,14 @@ esp_err_t mmu_config_psram_text_segment(uint32_t start_page, uint32_t psram_size #endif //#if CONFIG_SPIRAM_FETCH_INSTRUCTIONS #if CONFIG_SPIRAM_RODATA + +/** + * @brief Calculates the size of memory that would be used for copying flash rodata into PSRAM (in bytes) + * + * @return size_t size of memory that would be used for copying flash rodata into PSRAM (in bytes) + */ +size_t mmu_psram_get_rodata_segment_length(void); + /** * @brief Copy Flash rodata to PSRAM * diff --git a/components/esp_psram/mmu_psram_flash.c b/components/esp_psram/mmu_psram_flash.c index bb9288410a..faa0510307 100644 --- a/components/esp_psram/mmu_psram_flash.c +++ b/components/esp_psram/mmu_psram_flash.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -44,10 +44,8 @@ static uint32_t page0_page = INVALID_PHY_PAGE; #endif //#if CONFIG_SPIRAM_FETCH_INSTRUCTIONS || CONFIG_SPIRAM_RODATA #if CONFIG_SPIRAM_FETCH_INSTRUCTIONS -esp_err_t mmu_config_psram_text_segment(uint32_t start_page, uint32_t psram_size, uint32_t *out_page) +size_t mmu_psram_get_text_segment_length(void) { - uint32_t page_id = start_page; - uint32_t flash_pages = 0; #if CONFIG_IDF_TARGET_ESP32S2 flash_pages += Cache_Count_Flash_Pages(PRO_CACHE_IBUS0, &page0_mapped); @@ -55,9 +53,17 @@ esp_err_t mmu_config_psram_text_segment(uint32_t start_page, uint32_t psram_size #elif CONFIG_IDF_TARGET_ESP32S3 flash_pages += Cache_Count_Flash_Pages(CACHE_IBUS, &page0_mapped); #endif - if ((flash_pages + page_id) > BYTES_TO_MMU_PAGE(psram_size)) { + return MMU_PAGE_TO_BYTES(flash_pages); +} + +esp_err_t mmu_config_psram_text_segment(uint32_t start_page, uint32_t psram_size, uint32_t *out_page) +{ + uint32_t page_id = start_page; + + uint32_t flash_bytes = mmu_psram_get_text_segment_length(); + if ((flash_bytes + MMU_PAGE_TO_BYTES(page_id)) > psram_size) { ESP_EARLY_LOGE(TAG, "PSRAM space not enough for the Flash instructions, need %" PRIu32 " B, from %" PRIu32 " B to %" PRIu32 " B", - MMU_PAGE_TO_BYTES(flash_pages), MMU_PAGE_TO_BYTES(start_page), MMU_PAGE_TO_BYTES(flash_pages + page_id)); + flash_bytes, MMU_PAGE_TO_BYTES(start_page), flash_bytes + MMU_PAGE_TO_BYTES(page_id)); return ESP_FAIL; } @@ -87,10 +93,8 @@ esp_err_t mmu_config_psram_text_segment(uint32_t start_page, uint32_t psram_size #endif //#if CONFIG_SPIRAM_FETCH_INSTRUCTIONS #if CONFIG_SPIRAM_RODATA -esp_err_t mmu_config_psram_rodata_segment(uint32_t start_page, uint32_t psram_size, uint32_t *out_page) +size_t mmu_psram_get_rodata_segment_length(void) { - uint32_t page_id = start_page; - uint32_t flash_pages = 0; #if CONFIG_IDF_TARGET_ESP32S2 flash_pages += Cache_Count_Flash_Pages(PRO_CACHE_IBUS2, &page0_mapped); @@ -100,8 +104,17 @@ esp_err_t mmu_config_psram_rodata_segment(uint32_t start_page, uint32_t psram_si #elif CONFIG_IDF_TARGET_ESP32S3 flash_pages += Cache_Count_Flash_Pages(CACHE_DBUS, &page0_mapped); #endif - if ((flash_pages + page_id) > BYTES_TO_MMU_PAGE(psram_size)) { - ESP_EARLY_LOGE(TAG, "SPI RAM space not enough for the instructions, need to copy to %" PRIu32 " B.", MMU_PAGE_TO_BYTES(flash_pages + page_id)); + return MMU_PAGE_TO_BYTES(flash_pages); +} + +esp_err_t mmu_config_psram_rodata_segment(uint32_t start_page, uint32_t psram_size, uint32_t *out_page) +{ + uint32_t page_id = start_page; + + uint32_t flash_bytes = mmu_psram_get_rodata_segment_length(); + + if ((flash_bytes + MMU_PAGE_TO_BYTES(page_id)) > psram_size) { + ESP_EARLY_LOGE(TAG, "SPI RAM space not enough for the instructions, need to copy to %" PRIu32 " B.", flash_bytes + MMU_PAGE_TO_BYTES(page_id)); return ESP_FAIL; } diff --git a/components/esp_psram/mmu_psram_flash_v2.c b/components/esp_psram/mmu_psram_flash_v2.c index fe9a8407b2..c8d45ce112 100644 --- a/components/esp_psram/mmu_psram_flash_v2.c +++ b/components/esp_psram/mmu_psram_flash_v2.c @@ -84,10 +84,14 @@ static uint32_t s_do_load_from_flash(uint32_t flash_paddr_start, uint32_t size, #endif //#if CONFIG_SPIRAM_FETCH_INSTRUCTIONS || CONFIG_SPIRAM_RODATA #if CONFIG_SPIRAM_FETCH_INSTRUCTIONS +size_t mmu_psram_get_text_segment_length(void) +{ + return ALIGN_UP_BY((uint32_t)&_instruction_reserved_end, CONFIG_MMU_PAGE_SIZE) - ALIGN_DOWN_BY((uint32_t)&_instruction_reserved_start, CONFIG_MMU_PAGE_SIZE); +} + esp_err_t mmu_config_psram_text_segment(uint32_t start_page, uint32_t psram_size, uint32_t *out_page) { - size_t irom_size = ALIGN_UP_BY((uint32_t)&_instruction_reserved_end, CONFIG_MMU_PAGE_SIZE) - ALIGN_DOWN_BY((uint32_t)&_instruction_reserved_start, CONFIG_MMU_PAGE_SIZE); - s_irom_size = irom_size; + s_irom_size = mmu_psram_get_text_segment_length(); uint32_t flash_drom_paddr_start = 0; uint32_t flash_irom_paddr_start = 0; @@ -95,8 +99,8 @@ esp_err_t mmu_config_psram_text_segment(uint32_t start_page, uint32_t psram_size flash_irom_paddr_start = ALIGN_DOWN_BY(flash_irom_paddr_start, CONFIG_MMU_PAGE_SIZE); ESP_EARLY_LOGI(TAG, "flash_irom_paddr_start: 0x%x", flash_irom_paddr_start); - if ((MMU_PAGE_TO_BYTES(start_page) + irom_size) > psram_size) { - ESP_EARLY_LOGE(TAG, "PSRAM space not enough for the Flash instructions, need %"PRId32" B, from %"PRId32" B to %"PRId32" B", irom_size, MMU_PAGE_TO_BYTES(start_page), MMU_PAGE_TO_BYTES(start_page) + irom_size); + if ((MMU_PAGE_TO_BYTES(start_page) + s_irom_size) > psram_size) { + ESP_EARLY_LOGE(TAG, "PSRAM space not enough for the Flash instructions, need %"PRId32" B, from %"PRId32" B to %"PRId32" B", s_irom_size, MMU_PAGE_TO_BYTES(start_page), MMU_PAGE_TO_BYTES(start_page) + s_irom_size); return ESP_ERR_NO_MEM; } @@ -106,22 +110,27 @@ esp_err_t mmu_config_psram_text_segment(uint32_t start_page, uint32_t psram_size ESP_EARLY_LOGV(TAG, "flash_irom_paddr_start: 0x%"PRIx32", MMU_PAGE_TO_BYTES(start_page): 0x%"PRIx32", s_irom_paddr_offset: 0x%"PRIx32", s_irom_vaddr_start: 0x%"PRIx32, flash_irom_paddr_start, MMU_PAGE_TO_BYTES(start_page), s_irom_paddr_offset, s_irom_vaddr_start); uint32_t mapped_size = 0; - mapped_size = s_do_load_from_flash(flash_irom_paddr_start, irom_size, irom_load_addr_aligned, MMU_PAGE_TO_BYTES(start_page)); - cache_hal_writeback_addr(irom_load_addr_aligned, irom_size); + mapped_size = s_do_load_from_flash(flash_irom_paddr_start, s_irom_size, irom_load_addr_aligned, MMU_PAGE_TO_BYTES(start_page)); + cache_hal_writeback_addr(irom_load_addr_aligned, s_irom_size); ESP_EARLY_LOGV(TAG, "after mapping text, starting from paddr=0x%08"PRIx32" and vaddr=0x%08"PRIx32", 0x%"PRIx32" bytes are mapped", MMU_PAGE_TO_BYTES(start_page), irom_load_addr_aligned, mapped_size); - *out_page = BYTES_TO_MMU_PAGE(irom_size); + *out_page = BYTES_TO_MMU_PAGE(s_irom_size); return ESP_OK; } #endif //#if CONFIG_SPIRAM_FETCH_INSTRUCTIONS #if CONFIG_SPIRAM_RODATA + +size_t mmu_psram_get_rodata_segment_length(void) +{ + return ALIGN_UP_BY((uint32_t)&_rodata_reserved_end, CONFIG_MMU_PAGE_SIZE) - ALIGN_DOWN_BY((uint32_t)&_rodata_reserved_start, CONFIG_MMU_PAGE_SIZE); +} + esp_err_t mmu_config_psram_rodata_segment(uint32_t start_page, uint32_t psram_size, uint32_t *out_page) { - size_t drom_size = ALIGN_UP_BY((uint32_t)&_rodata_reserved_end, CONFIG_MMU_PAGE_SIZE) - ALIGN_DOWN_BY((uint32_t)&_rodata_reserved_start, CONFIG_MMU_PAGE_SIZE); - s_drom_size = drom_size; + s_drom_size = mmu_psram_get_rodata_segment_length(); uint32_t flash_drom_paddr_start = 0; uint32_t flash_irom_paddr_start = 0; @@ -129,8 +138,8 @@ esp_err_t mmu_config_psram_rodata_segment(uint32_t start_page, uint32_t psram_si flash_drom_paddr_start = ALIGN_DOWN_BY(flash_drom_paddr_start, CONFIG_MMU_PAGE_SIZE); ESP_EARLY_LOGI(TAG, "flash_drom_paddr_start: 0x%x", flash_drom_paddr_start); - if ((MMU_PAGE_TO_BYTES(start_page) + drom_size) > psram_size) { - ESP_EARLY_LOGE(TAG, "PSRAM space not enough for the Flash rodata, need %"PRId32" B, from %"PRId32" B to %"PRId32" B", drom_size, MMU_PAGE_TO_BYTES(start_page), MMU_PAGE_TO_BYTES(start_page) + drom_size); + if ((MMU_PAGE_TO_BYTES(start_page) + s_drom_size) > psram_size) { + ESP_EARLY_LOGE(TAG, "PSRAM space not enough for the Flash rodata, need %"PRId32" B, from %"PRId32" B to %"PRId32" B", s_drom_size, MMU_PAGE_TO_BYTES(start_page), MMU_PAGE_TO_BYTES(start_page) + s_drom_size); return ESP_ERR_NO_MEM; } @@ -140,12 +149,12 @@ esp_err_t mmu_config_psram_rodata_segment(uint32_t start_page, uint32_t psram_si ESP_EARLY_LOGV(TAG, "flash_drom_paddr_start: 0x%"PRIx32", MMU_PAGE_TO_BYTES(start_page): 0x%"PRIx32", s_drom_paddr_offset: 0x%"PRIx32", s_drom_vaddr_start: 0x%"PRIx32, flash_drom_paddr_start, MMU_PAGE_TO_BYTES(start_page), s_drom_paddr_offset, s_drom_vaddr_start); uint32_t mapped_size = 0; - mapped_size = s_do_load_from_flash(flash_drom_paddr_start, drom_size, drom_load_addr_aligned, MMU_PAGE_TO_BYTES(start_page)); - cache_hal_writeback_addr(drom_load_addr_aligned, drom_size); + mapped_size = s_do_load_from_flash(flash_drom_paddr_start, s_drom_size, drom_load_addr_aligned, MMU_PAGE_TO_BYTES(start_page)); + cache_hal_writeback_addr(drom_load_addr_aligned, s_drom_size); ESP_EARLY_LOGV(TAG, "after mapping rodata, starting from paddr=0x%08"PRIx32" and vaddr=0x%08"PRIx32", 0x%"PRIx32" bytes are mapped", MMU_PAGE_TO_BYTES(start_page), drom_load_addr_aligned, mapped_size); - *out_page = BYTES_TO_MMU_PAGE(drom_size); + *out_page = BYTES_TO_MMU_PAGE(s_drom_size); return ESP_OK; } From 8d42a711c4f6ffd73212730aadc6f9b9addc68fc Mon Sep 17 00:00:00 2001 From: "harshal.patil" Date: Fri, 14 Feb 2025 12:29:24 +0530 Subject: [PATCH 3/5] feat(esp_psram): Add the gap created due to alignment of XIP segments in heap --- components/esp_psram/esp_psram.c | 55 ++++++++++++++++++- .../include/esp_private/mmu_psram_flash.h | 34 ++++++++++++ components/esp_psram/mmu_psram_flash.c | 43 +++++++++++++++ components/esp_psram/mmu_psram_flash_v2.c | 43 +++++++++++++++ 4 files changed, 173 insertions(+), 2 deletions(-) diff --git a/components/esp_psram/esp_psram.c b/components/esp_psram/esp_psram.c index 52592ab443..96207e0228 100644 --- a/components/esp_psram/esp_psram.c +++ b/components/esp_psram/esp_psram.c @@ -51,6 +51,14 @@ #define PSRAM_EARLY_LOGI ESP_EARLY_LOGI #endif +#if CONFIG_SPIRAM_RODATA +extern uint8_t _rodata_reserved_end; +#endif /* CONFIG_SPIRAM_RODATA */ + +#if CONFIG_SPIRAM_FETCH_INSTRUCTIONS +extern uint8_t _instruction_reserved_end; +#endif /* CONFIG_SPIRAM_FETCH_INSTRUCTIONS */ + #if CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY extern uint8_t _ext_ram_bss_start; extern uint8_t _ext_ram_bss_end; @@ -61,6 +69,8 @@ extern uint8_t _ext_ram_noinit_start; extern uint8_t _ext_ram_noinit_end; #endif //#if CONFIG_SPIRAM_ALLOW_NOINIT_SEG_EXTERNAL_MEMORY +#define ALIGN_UP_BY(num, align) (((num) + ((align) - 1)) & ~((align) - 1)) + typedef struct { intptr_t vaddr_start; intptr_t vaddr_end; @@ -401,6 +411,31 @@ 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); + // Here, SOC_MMU_DI_VADDR_SHARED is necessary because, for the targets that have separate data and instruction virtual address spaces, + // the SPIRAM gap created due to the alignment needed while placing the instruction segment in the instruction virtual address space + // cannot be added in heap because the region cannot be configured with write permissions. +#if CONFIG_SPIRAM_FETCH_INSTRUCTIONS && SOC_MMU_DI_VADDR_SHARED + if ((uint32_t)&_instruction_reserved_end & (CONFIG_MMU_PAGE_SIZE - 1)) { + uint32_t instruction_alignment_gap_heap_start, instruction_alignment_gap_heap_end; + mmu_psram_get_instruction_alignment_gap_info(&instruction_alignment_gap_heap_start, &instruction_alignment_gap_heap_end); + ret = heap_caps_add_region_with_caps(byte_aligned_caps, instruction_alignment_gap_heap_start, instruction_alignment_gap_heap_end); + if (ret == ESP_OK) { + ESP_EARLY_LOGI(TAG, "Adding pool of %dK of PSRAM memory gap generated due to end address alignment of irom to the heap allocator", (instruction_alignment_gap_heap_end - instruction_alignment_gap_heap_start) / 1024); + } + } +#endif /* CONFIG_SPIRAM_FETCH_INSTRUCTIONS */ + + // In the case of ESP32S2, the rodata is mapped to a read-only region (SOC_DROM0_ADDRESS_LOW - SOC_DROM0_ADDRESS_HIGH), thus we cannot add this region to the heap. +#if CONFIG_SPIRAM_RODATA && !CONFIG_IDF_TARGET_ESP32S2 + if ((uint32_t)&_rodata_reserved_end & (CONFIG_MMU_PAGE_SIZE - 1)) { + uint32_t rodata_alignment_gap_heap_start, rodata_alignment_gap_heap_end; + mmu_psram_get_rodata_alignment_gap_info(&rodata_alignment_gap_heap_start, &rodata_alignment_gap_heap_end); + ret = heap_caps_add_region_with_caps(byte_aligned_caps, rodata_alignment_gap_heap_start, rodata_alignment_gap_heap_end); + if (ret == ESP_OK) { + ESP_EARLY_LOGI(TAG, "Adding pool of %dK of PSRAM memory gap generated due to end address alignment of drom to the heap allocator", (rodata_alignment_gap_heap_end - rodata_alignment_gap_heap_start) / 1024); + } + } +#endif /* CONFIG_SPIRAM_RODATA */ return ESP_OK; } @@ -410,8 +445,24 @@ bool IRAM_ATTR esp_psram_check_ptr_addr(const void *p) return false; } - return ((intptr_t)p >= s_psram_ctx.mapped_regions[PSRAM_MEM_8BIT_ALIGNED].vaddr_start && (intptr_t)p < s_psram_ctx.mapped_regions[PSRAM_MEM_8BIT_ALIGNED].vaddr_end) || - ((intptr_t)p >= s_psram_ctx.mapped_regions[PSRAM_MEM_32BIT_ALIGNED].vaddr_start && (intptr_t)p < s_psram_ctx.mapped_regions[PSRAM_MEM_32BIT_ALIGNED].vaddr_end); + if (((intptr_t)p >= s_psram_ctx.mapped_regions[PSRAM_MEM_8BIT_ALIGNED].vaddr_start && (intptr_t)p < s_psram_ctx.mapped_regions[PSRAM_MEM_8BIT_ALIGNED].vaddr_end) || + ((intptr_t)p >= s_psram_ctx.mapped_regions[PSRAM_MEM_32BIT_ALIGNED].vaddr_start && (intptr_t)p < s_psram_ctx.mapped_regions[PSRAM_MEM_32BIT_ALIGNED].vaddr_end)) { + return true; + } + +#if CONFIG_SPIRAM_RODATA && !CONFIG_IDF_TARGET_ESP32S2 + if (mmu_psram_check_ptr_addr_in_rodata_alignment_gap(p)) { + return true; + } +#endif /* CONFIG_SPIRAM_RODATA && !CONFIG_IDF_TARGET_ESP32S2 */ + +#if CONFIG_SPIRAM_FETCH_INSTRUCTIONS && SOC_MMU_DI_VADDR_SHARED + if (mmu_psram_check_ptr_addr_in_instruction_alignment_gap(p)) { + return true; + } +#endif /* CONFIG_SPIRAM_FETCH_INSTRUCTIONS && SOC_MMU_DI_VADDR_SHARED */ + + return false; } esp_err_t esp_psram_extram_reserve_dma_pool(size_t size) diff --git a/components/esp_psram/include/esp_private/mmu_psram_flash.h b/components/esp_psram/include/esp_private/mmu_psram_flash.h index e70205be53..f129888a10 100644 --- a/components/esp_psram/include/esp_private/mmu_psram_flash.h +++ b/components/esp_psram/include/esp_private/mmu_psram_flash.h @@ -47,6 +47,23 @@ extern "C" { */ size_t mmu_psram_get_text_segment_length(void); +/** + * @brief Get the start and size of the instruction segment alignment gap + * + * @param[out] gap_start Start of the gap + * @param[out] gap_size Size of the gap + */ +void mmu_psram_get_instruction_alignment_gap_info(uint32_t *gap_start, uint32_t *gap_size); + +/** + * @brief Check if the pointer is in the instruction alignment gap + * + * @param[in] p Pointer to check + * + * @return true if the pointer is in the instruction alignment gap, false otherwise + */ +bool mmu_psram_check_ptr_addr_in_instruction_alignment_gap(const void *p); + /** * @brief Copy Flash texts to PSRAM * @@ -59,6 +76,14 @@ esp_err_t mmu_config_psram_text_segment(uint32_t start_page, uint32_t psram_size #if CONFIG_SPIRAM_RODATA +/** + * @brief Get the start and size of the rodata segment alignment gap + * + * @param[out] gap_start Start of the gap + * @param[out] gap_size Size of the gap + */ +void mmu_psram_get_rodata_alignment_gap_info(uint32_t *gap_start, uint32_t *gap_size); + /** * @brief Calculates the size of memory that would be used for copying flash rodata into PSRAM (in bytes) * @@ -74,6 +99,15 @@ size_t mmu_psram_get_rodata_segment_length(void); * @param[out] out_page Used pages */ esp_err_t mmu_config_psram_rodata_segment(uint32_t start_page, uint32_t psram_size, uint32_t *out_page); + +/** + * @brief Check if the pointer is in the rodata alignment gap + * + * @param[in] p Pointer to check + * + * @return true if the pointer is in the rodata alignment gap, false otherwise + */ +bool mmu_psram_check_ptr_addr_in_rodata_alignment_gap(const void *p); #endif //#if CONFIG_SPIRAM_RODATA /*---------------------------------------------------------------------------- diff --git a/components/esp_psram/mmu_psram_flash.c b/components/esp_psram/mmu_psram_flash.c index faa0510307..c33c9f26e0 100644 --- a/components/esp_psram/mmu_psram_flash.c +++ b/components/esp_psram/mmu_psram_flash.c @@ -16,6 +16,7 @@ * APIs in 2 will be refactored when MMU driver is ready */ +#include #include #include "sdkconfig.h" #include "esp_log.h" @@ -31,6 +32,8 @@ #include "esp32s3/rom/cache.h" #endif +#define ALIGN_UP_BY(num, align) (((num) + ((align) - 1)) & ~((align) - 1)) + /*---------------------------------------------------------------------------- Part 1 APIs (See @Backgrounds on top of this file) -------------------------------------------------------------------------------*/ @@ -44,6 +47,10 @@ static uint32_t page0_page = INVALID_PHY_PAGE; #endif //#if CONFIG_SPIRAM_FETCH_INSTRUCTIONS || CONFIG_SPIRAM_RODATA #if CONFIG_SPIRAM_FETCH_INSTRUCTIONS +extern char _instruction_reserved_end; +#define INSTRUCTION_ALIGNMENT_GAP_START ALIGN_UP_BY((uint32_t)&_instruction_reserved_end, 4) +#define INSTRUCTION_ALIGNMENT_GAP_END ALIGN_UP_BY((uint32_t)&_instruction_reserved_end, CONFIG_MMU_PAGE_SIZE) + size_t mmu_psram_get_text_segment_length(void) { uint32_t flash_pages = 0; @@ -56,6 +63,22 @@ size_t mmu_psram_get_text_segment_length(void) return MMU_PAGE_TO_BYTES(flash_pages); } +void mmu_psram_get_instruction_alignment_gap_info(uint32_t *gap_start, uint32_t *gap_end) +{ + // As we need the memory to start with word aligned address, max virtual space that could be wasted = 3 bytes + // Or create a new region from (uint32_t)&_instruction_reserved_end to ALIGN_UP_BY((uint32_t)&_instruction_reserved_end, 4) as only byte-accessible + *gap_start = INSTRUCTION_ALIGNMENT_GAP_START; + *gap_end = INSTRUCTION_ALIGNMENT_GAP_END; +} + +bool IRAM_ATTR mmu_psram_check_ptr_addr_in_instruction_alignment_gap(const void *p) +{ + if ((intptr_t)p >= INSTRUCTION_ALIGNMENT_GAP_START && (intptr_t)p < INSTRUCTION_ALIGNMENT_GAP_END) { + return true; + } + return false; +} + esp_err_t mmu_config_psram_text_segment(uint32_t start_page, uint32_t psram_size, uint32_t *out_page) { uint32_t page_id = start_page; @@ -93,6 +116,10 @@ esp_err_t mmu_config_psram_text_segment(uint32_t start_page, uint32_t psram_size #endif //#if CONFIG_SPIRAM_FETCH_INSTRUCTIONS #if CONFIG_SPIRAM_RODATA +extern char _rodata_reserved_end; +#define RODATA_ALIGNMENT_GAP_START ALIGN_UP_BY((uint32_t)&_rodata_reserved_end, 4) +#define RODATA_ALIGNMENT_GAP_END ALIGN_UP_BY((uint32_t)&_rodata_reserved_end, CONFIG_MMU_PAGE_SIZE) + size_t mmu_psram_get_rodata_segment_length(void) { uint32_t flash_pages = 0; @@ -107,6 +134,22 @@ size_t mmu_psram_get_rodata_segment_length(void) return MMU_PAGE_TO_BYTES(flash_pages); } +void mmu_psram_get_rodata_alignment_gap_info(uint32_t *gap_start, uint32_t *gap_end) +{ + // As we need the memory to start with word aligned address, max virtual space that could be wasted = 3 bytes + // Or create a new region from (uint32_t)&_rodata_reserved_end to ALIGN_UP_BY((uint32_t)&_rodata_reserved_end, 4) as only byte-accessible + *gap_start = RODATA_ALIGNMENT_GAP_START; + *gap_end = RODATA_ALIGNMENT_GAP_END; +} + +bool IRAM_ATTR mmu_psram_check_ptr_addr_in_rodata_alignment_gap(const void *p) +{ + if ((intptr_t)p >= RODATA_ALIGNMENT_GAP_START && (intptr_t)p < RODATA_ALIGNMENT_GAP_END) { + return true; + } + return false; +} + esp_err_t mmu_config_psram_rodata_segment(uint32_t start_page, uint32_t psram_size, uint32_t *out_page) { uint32_t page_id = start_page; diff --git a/components/esp_psram/mmu_psram_flash_v2.c b/components/esp_psram/mmu_psram_flash_v2.c index c8d45ce112..379b549c8c 100644 --- a/components/esp_psram/mmu_psram_flash_v2.c +++ b/components/esp_psram/mmu_psram_flash_v2.c @@ -10,6 +10,7 @@ * The XIP PSRAM is done by CPU copy, v1(see mmu_psram_flash.c) is done by Cache copy */ +#include #include #include #include "sdkconfig.h" @@ -84,11 +85,32 @@ static uint32_t s_do_load_from_flash(uint32_t flash_paddr_start, uint32_t size, #endif //#if CONFIG_SPIRAM_FETCH_INSTRUCTIONS || CONFIG_SPIRAM_RODATA #if CONFIG_SPIRAM_FETCH_INSTRUCTIONS +/* As heap memory is allocated in 4-byte aligned manner, we need to align the instruction to 4-byte boundary */ +#define INSTRUCTION_ALIGNMENT_GAP_START ALIGN_UP_BY((uint32_t)&_instruction_reserved_end, 4) +/* The end of the instruction is aligned to CONFIG_MMU_PAGE_SIZE boundary as the flash instruction is mapped to PSRAM */ +#define INSTRUCTION_ALIGNMENT_GAP_END ALIGN_UP_BY((uint32_t)&_instruction_reserved_end, CONFIG_MMU_PAGE_SIZE) + size_t mmu_psram_get_text_segment_length(void) { return ALIGN_UP_BY((uint32_t)&_instruction_reserved_end, CONFIG_MMU_PAGE_SIZE) - ALIGN_DOWN_BY((uint32_t)&_instruction_reserved_start, CONFIG_MMU_PAGE_SIZE); } +void mmu_psram_get_instruction_alignment_gap_info(uint32_t *gap_start, uint32_t *gap_end) +{ + // As we need the memory to start with word aligned address, max virtual space that could be wasted = 3 bytes + // Or create a new region from (uint32_t)&_instruction_reserved_end to ALIGN_UP_BY((uint32_t)&_instruction_reserved_end, 4) as only byte-accessible + *gap_start = INSTRUCTION_ALIGNMENT_GAP_START; + *gap_end = INSTRUCTION_ALIGNMENT_GAP_END; +} + +bool IRAM_ATTR mmu_psram_check_ptr_addr_in_instruction_alignment_gap(const void *p) +{ + if ((intptr_t)p >= INSTRUCTION_ALIGNMENT_GAP_START && (intptr_t)p < INSTRUCTION_ALIGNMENT_GAP_END) { + return true; + } + return false; +} + esp_err_t mmu_config_psram_text_segment(uint32_t start_page, uint32_t psram_size, uint32_t *out_page) { s_irom_size = mmu_psram_get_text_segment_length(); @@ -128,6 +150,27 @@ size_t mmu_psram_get_rodata_segment_length(void) return ALIGN_UP_BY((uint32_t)&_rodata_reserved_end, CONFIG_MMU_PAGE_SIZE) - ALIGN_DOWN_BY((uint32_t)&_rodata_reserved_start, CONFIG_MMU_PAGE_SIZE); } +/* As heap memory is allocated in 4-byte aligned manner, we need to align the rodata to 4-byte boundary */ +#define RODATA_ALIGNMENT_GAP_START ALIGN_UP_BY((uint32_t)&_rodata_reserved_end, 4) +/* The end of the rodata is aligned to CONFIG_MMU_PAGE_SIZE boundary as the flash rodata is mapped to PSRAM */ +#define RODATA_ALIGNMENT_GAP_END ALIGN_UP_BY((uint32_t)&_rodata_reserved_end, CONFIG_MMU_PAGE_SIZE) + +void mmu_psram_get_rodata_alignment_gap_info(uint32_t *gap_start, uint32_t *gap_end) +{ + // As we need the memory to start with word aligned address, max virtual space that could be wasted = 3 bytes + // Or create a new region from (uint32_t)&_rodata_reserved_end to ALIGN_UP_BY((uint32_t)&_rodata_reserved_end, 4) as only byte-accessible + *gap_start = RODATA_ALIGNMENT_GAP_START; + *gap_end = RODATA_ALIGNMENT_GAP_END; +} + +bool IRAM_ATTR mmu_psram_check_ptr_addr_in_rodata_alignment_gap(const void *p) +{ + if ((intptr_t)p >= RODATA_ALIGNMENT_GAP_START && (intptr_t)p < RODATA_ALIGNMENT_GAP_END) { + return true; + } + return false; +} + esp_err_t mmu_config_psram_rodata_segment(uint32_t start_page, uint32_t psram_size, uint32_t *out_page) { s_drom_size = mmu_psram_get_rodata_segment_length(); From 29f7654c2bdaac2358d3fa377cd25b8dce6f50e9 Mon Sep 17 00:00:00 2001 From: "harshal.patil" Date: Tue, 18 Feb 2025 23:28:26 +0530 Subject: [PATCH 4/5] feat(cpu_region_protect): Enable basic memory protection for SPIRAM --- .../port/esp32p4/cpu_region_protect.c | 52 ++++++++---- components/esp_psram/Kconfig.spiram.common | 24 ++++++ components/esp_psram/esp_psram.c | 81 +++++++++++++++++++ .../include/esp_private/esp_psram_extram.h | 14 +++- .../esp_system/ld/esp32p4/sections.ld.in | 11 +++ .../system/panic/main/include/test_memprot.h | 4 + .../system/panic/main/test_app_main.c | 12 ++- .../system/panic/main/test_memprot.c | 34 +++++++- tools/test_apps/system/panic/pytest_panic.py | 41 +++++++++- .../system/panic/sdkconfig.ci.memprot_esp32p4 | 4 + .../sdkconfig.ci.memprot_spiram_xip_esp32p4 | 13 +++ .../sdkconfig.ci.memprot_spiram_xip_esp32s3 | 17 ++++ 12 files changed, 287 insertions(+), 20 deletions(-) create mode 100644 tools/test_apps/system/panic/sdkconfig.ci.memprot_spiram_xip_esp32p4 create mode 100644 tools/test_apps/system/panic/sdkconfig.ci.memprot_spiram_xip_esp32s3 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 f733d63521..b8a785f835 100644 --- a/components/esp_hw_support/port/esp32p4/cpu_region_protect.c +++ b/components/esp_hw_support/port/esp32p4/cpu_region_protect.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -11,6 +11,9 @@ #include "esp_fault.h" #include "hal/cache_ll.h" #include "riscv/csr.h" +#if CONFIG_SPIRAM +#include "esp_private/esp_psram_extram.h" +#endif /* CONFIG_SPIRAM */ #ifdef BOOTLOADER_BUILD // Without L bit set @@ -30,6 +33,7 @@ #define ALIGN_UP_TO_MMU_PAGE_SIZE(addr) (((addr) + (SOC_MMU_PAGE_SIZE) - 1) & ~((SOC_MMU_PAGE_SIZE) - 1)) #define ALIGN_DOWN_TO_MMU_PAGE_SIZE(addr) ((addr) & ~((SOC_MMU_PAGE_SIZE) - 1)) +#define ALIGN_UP(addr, align) ((addr) & ~((align) - 1)) static void esp_cpu_configure_invalid_regions(void) { @@ -191,16 +195,34 @@ void esp_cpu_configure_region_protection(void) 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)); + 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, 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, irom_resv_end, PMP_TOR | RX); - PMP_ENTRY_SET(8, drom_resv_end, PMP_TOR | R); + 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); @@ -215,24 +237,24 @@ void esp_cpu_configure_region_protection(void) /* 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); + 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 - PMP_ENTRY_SET(10, (int)&_rtc_text_start, PMP_TOR | RW); - PMP_ENTRY_SET(11, (int)&_rtc_text_end, PMP_TOR | RX); - PMP_ENTRY_SET(12, SOC_RTC_IRAM_HIGH, PMP_TOR | RW); + PMP_ENTRY_SET(12, (int)&_rtc_text_start, PMP_TOR | RW); + 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 pmpaddr9 = PMPADDR_NAPOT(SOC_RTC_IRAM_LOW, SOC_RTC_IRAM_HIGH); - PMP_ENTRY_SET(9, pmpaddr9, PMP_NAPOT | CONDITIONAL_RWX); + 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 pmpaddr13 = PMPADDR_NAPOT(SOC_PERIPHERAL_LOW, SOC_PERIPHERAL_HIGH); - PMP_ENTRY_SET(13, pmpaddr13, PMP_NAPOT | RW); + 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_psram/Kconfig.spiram.common b/components/esp_psram/Kconfig.spiram.common index 2832f1e677..2e41bef18d 100644 --- a/components/esp_psram/Kconfig.spiram.common +++ b/components/esp_psram/Kconfig.spiram.common @@ -10,6 +10,30 @@ config SPIRAM_BOOT_INIT have specific requirements, you'll want to leave this enabled so memory allocated during boot-up can also be placed in SPI RAM. +config SPIRAM_PRE_CONFIGURE_MEMORY_PROTECTION + bool "Pre-configure memory protection for PSRAM" + default y if SPIRAM_BOOT_INIT + default n + depends on SPIRAM + help + If this is enabled, the PSRAM will be pre-configured for memory protection during initial boot. + This configuration takes into consideration the PSRAM memory configurations that are performed + by ESP-IDF's default PSRAM initialization function, esp_psram_init(). + Thus, the config is enabled by default when SPIRAM_BOOT_INIT is enabled, + because the function esp_psram_init() would be called in the startup code. + + In case you wish to disable SPIRAM_BOOT_INIT just for delaying the PSRAM initialization and plan + to use the ESP-IDF's default PSRAM initialization function, esp_psram_init() in the application code, + you should still enable this config to enable memory protection for the PSRAM. + + Note that enabling this config also considers that the rest of the PSRAM memory that is left after + the memory configurations are performed by esp_psram_init(), can be allocated to the heap using the function + esp_psram_extram_add_to_heap_allocator(), thus configures this region with heap memory protection (RW). + + As an advanced usage, if you plan to initialize the PSRAM memory regions manually by yourself without + using the function esp_psram_init(), you should disable this config to avoid any memory protection and + usage conflicts. + config SPIRAM_IGNORE_NOTFOUND bool "Ignore PSRAM when not found" default "n" diff --git a/components/esp_psram/esp_psram.c b/components/esp_psram/esp_psram.c index 96207e0228..7a1a7948e5 100644 --- a/components/esp_psram/esp_psram.c +++ b/components/esp_psram/esp_psram.c @@ -592,3 +592,84 @@ void esp_psram_bss_init(void) memset(&_ext_ram_bss_start, 0, size); #endif } + +#if CONFIG_SPIRAM_PRE_CONFIGURE_MEMORY_PROTECTION +static inline uint32_t s_get_ext_bss_size(void) +{ +#if CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY + return ((intptr_t)&_ext_ram_bss_end - (intptr_t)&_ext_ram_bss_start); +#else + return 0; +#endif /* CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY */ +} + +static inline uint32_t s_get_ext_noinit_size(void) +{ +#if CONFIG_SPIRAM_ALLOW_NOINIT_SEG_EXTERNAL_MEMORY + return ((intptr_t)&_ext_ram_noinit_end - (intptr_t)&_ext_ram_noinit_start); +#else + return 0; +#endif /* CONFIG_SPIRAM_ALLOW_NOINIT_SEG_EXTERNAL_MEMORY */ +} + +/** + * @brief Calculates the effective PSRAM memory that would be / is mapped. + * + * @return The size of PSRAM memory that would be / is mapped in bytes, or 0 if PSRAM isn't successfully initialized + */ +static size_t esp_psram_get_effective_mapped_size(void) +{ + size_t byte_aligned_size = 0; + size_t total_mapped_size = 0; + + 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; + } else { + uint32_t psram_available_size = 0; + esp_err_t ret = esp_psram_impl_get_available_size(&psram_available_size); + assert(ret == ESP_OK); + +#if CONFIG_SPIRAM_RODATA + psram_available_size -= mmu_psram_get_rodata_segment_length(); +#endif /* CONFIG_SPIRAM_RODATA */ + +#if CONFIG_SPIRAM_FETCH_INSTRUCTIONS + psram_available_size -= mmu_psram_get_text_segment_length(); +#endif /* CONFIG_SPIRAM_FETCH_INSTRUCTIONS */ + + ret = esp_mmu_map_get_max_consecutive_free_block_size(MMU_MEM_CAP_READ | MMU_MEM_CAP_WRITE | MMU_MEM_CAP_8BIT | MMU_MEM_CAP_32BIT, MMU_TARGET_PSRAM0, &byte_aligned_size); + assert(ret == ESP_OK); + total_mapped_size += MIN(byte_aligned_size, psram_available_size - total_mapped_size); + +#if CONFIG_IDF_TARGET_ESP32S2 + if (total_mapped_size < psram_available_size) { + size_t word_aligned_size = 0; + ret = esp_mmu_map_get_max_consecutive_free_block_size(MMU_MEM_CAP_READ | MMU_MEM_CAP_WRITE | MMU_MEM_CAP_32BIT, MMU_TARGET_PSRAM0, &word_aligned_size); + assert(ret == ESP_OK); + total_mapped_size += MIN(word_aligned_size, psram_available_size - total_mapped_size); + } +#endif + return total_mapped_size; + } +} + +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; + } else { + size_t effective_mapped_size = esp_psram_get_effective_mapped_size(); + if (effective_mapped_size == 0) { + return 0; + } + + effective_mapped_size -= s_get_ext_bss_size(); + effective_mapped_size -= s_get_ext_noinit_size(); + +#if CONFIG_IDF_TARGET_ESP32 + effective_mapped_size -= esp_himem_reserved_area_size() - 1; +#endif + return effective_mapped_size; + } +} +#endif /* CONFIG_SPIRAM_PRE_CONFIGURE_MEMORY_PROTECTION */ diff --git a/components/esp_psram/include/esp_private/esp_psram_extram.h b/components/esp_psram/include/esp_private/esp_psram_extram.h index 0b44e1bfd8..f0e25041b6 100644 --- a/components/esp_psram/include/esp_private/esp_psram_extram.h +++ b/components/esp_psram/include/esp_private/esp_psram_extram.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -60,6 +60,18 @@ bool esp_psram_extram_test(void); */ void esp_psram_bss_init(void); +/** + * @brief Calculates the effective PSRAM memory that would be / is added into the heap. + * + * @return The size of PSRAM memory that would be / is added into the heap in bytes, or 0 if PSRAM hardware isn't successfully initialized + * @note The function pre-calculates the effective size of the PSRAM memory that would be added into the heap after performing the XIP or + * ext bss and ext noinit considerations, thus, even if the function is called before esp_psram_init(), it will return the final + * effective size of the PSRAM memory that would have been added into the heap after esp_psram_init() is performed + * instead of the vanilla size of the PSRAM memory. + * This function is only available if CONFIG_SPIRAM_PRE_CONFIGURE_MEMORY_PROTECTION is enabled. + */ +size_t esp_psram_get_heap_size_to_protect(void); + #if CONFIG_IDF_TARGET_ESP32 /** * @brief Force a writeback of the data in the PSRAM cache. This is to be called whenever diff --git a/components/esp_system/ld/esp32p4/sections.ld.in b/components/esp_system/ld/esp32p4/sections.ld.in index 8be9ec01e4..9a9bbc437c 100644 --- a/components/esp_system/ld/esp32p4/sections.ld.in +++ b/components/esp_system/ld/esp32p4/sections.ld.in @@ -295,6 +295,11 @@ SECTIONS */ . += _esp_flash_mmap_prefetch_pad_size; +#if CONFIG_SPIRAM_FETCH_INSTRUCTIONS + /* Align the end of flash text region as per PMP granularity */ + . = ALIGN(_esp_pmp_align_size); +#endif // CONFIG_SPIRAM_FETCH_INSTRUCTIONS + _text_end = ABSOLUTE(.); /** * Mark the flash.text end. @@ -444,6 +449,12 @@ SECTIONS *(.tdata .tdata.* .gnu.linkonce.td.*) . = ALIGN(ALIGNOF(.flash.tbss)); + +#if CONFIG_SPIRAM_RODATA + /* Align the end of flash rodata region as per PMP granularity */ + . = ALIGN(_esp_pmp_align_size); +#endif // CONFIG_SPIRAM_RODATA + _thread_local_data_end = ABSOLUTE(.); } > rodata_seg_low ASSERT_SECTIONS_GAP(.flash.tdata, .flash.tbss) diff --git a/tools/test_apps/system/panic/main/include/test_memprot.h b/tools/test_apps/system/panic/main/include/test_memprot.h index 6b8cfde2df..5dde7fc83c 100644 --- a/tools/test_apps/system/panic/main/include/test_memprot.h +++ b/tools/test_apps/system/panic/main/include/test_memprot.h @@ -44,6 +44,10 @@ void test_rtc_slow_reg2_execute_violation(void); void test_irom_reg_write_violation(void); +void test_spiram_xip_irom_alignment_reg_execute_violation(void); + +void test_spiram_xip_drom_alignment_reg_execute_violation(void); + void test_drom_reg_write_violation(void); void test_drom_reg_execute_violation(void); diff --git a/tools/test_apps/system/panic/main/test_app_main.c b/tools/test_apps/system/panic/main/test_app_main.c index a8f7f7b7c3..47b2328cf0 100644 --- a/tools/test_apps/system/panic/main/test_app_main.c +++ b/tools/test_apps/system/panic/main/test_app_main.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -18,6 +18,9 @@ #include "test_panic.h" #include "test_memprot.h" +#include "sdkconfig.h" +#include "soc/soc_caps.h" + /* Test Utility Functions */ #define BOOT_CMD_MAX_LEN (128) @@ -170,6 +173,13 @@ void app_main(void) HANDLE_TEST(test_name, test_irom_reg_write_violation); HANDLE_TEST(test_name, test_drom_reg_write_violation); HANDLE_TEST(test_name, test_drom_reg_execute_violation); +#if CONFIG_SPIRAM_FETCH_INSTRUCTIONS && SOC_MMU_DI_VADDR_SHARED + HANDLE_TEST(test_name, test_spiram_xip_irom_alignment_reg_execute_violation); +#endif +#endif + +#if CONFIG_SPIRAM_RODATA && !CONFIG_IDF_TARGET_ESP32S2 + HANDLE_TEST(test_name, test_spiram_xip_drom_alignment_reg_execute_violation); #endif #ifdef CONFIG_SOC_CPU_HAS_PMA diff --git a/tools/test_apps/system/panic/main/test_memprot.c b/tools/test_apps/system/panic/main/test_memprot.c index 73885f1e0c..46116949cb 100644 --- a/tools/test_apps/system/panic/main/test_memprot.c +++ b/tools/test_apps/system/panic/main/test_memprot.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -13,6 +13,7 @@ #include "esp_system.h" #include "esp_log.h" #include "soc/soc.h" +#include "soc/soc_caps.h" #include "test_memprot.h" #include "sdkconfig.h" @@ -24,6 +25,8 @@ extern int _iram_start; extern int _iram_text_start; extern int _iram_text_end; +#define ALIGN_UP_TO_MMU_PAGE_SIZE(addr) (((addr) + (SOC_MMU_PAGE_SIZE) - 1) & ~((SOC_MMU_PAGE_SIZE) - 1)) + /* NOTE: Naming conventions for RTC_FAST_MEM are * different for ESP32-C3 and other RISC-V targets */ @@ -245,8 +248,37 @@ void test_drom_reg_execute_violation(void) func_ptr = (void(*)(void))foo_buf; func_ptr(); } + +// Check if the memory alignment gaps added to the heap are correctly configured +#if CONFIG_SPIRAM_FETCH_INSTRUCTIONS && SOC_MMU_DI_VADDR_SHARED +void test_spiram_xip_irom_alignment_reg_execute_violation(void) +{ + extern int _instruction_reserved_end; + if (ALIGN_UP_TO_MMU_PAGE_SIZE((uint32_t)(&_instruction_reserved_end)) - (uint32_t)(&_instruction_reserved_end) >= 4) { + void (*test_addr)(void) = (void(*)(void))((uint32_t)(&_instruction_reserved_end + 0x4)); + printf("SPIRAM (IROM): Execute operation | Address: %p\n", test_addr); + test_addr(); + } else { + printf("SPIRAM (IROM): IROM alignment gap not added into heap\n"); + } +} +#endif /* CONFIG_SPIRAM_FETCH_INSTRUCTIONS && SOC_MMU_DI_VADDR_SHARED */ #endif +#if CONFIG_SPIRAM_RODATA && !CONFIG_IDF_TARGET_ESP32S2 +void test_spiram_xip_drom_alignment_reg_execute_violation(void) +{ + extern int _rodata_reserved_end; + if (ALIGN_UP_TO_MMU_PAGE_SIZE((uint32_t)(&_rodata_reserved_end)) - (uint32_t)(&_rodata_reserved_end) >= 4) { + void (*test_addr)(void) = (void(*)(void))((uint32_t)(&_rodata_reserved_end + 0x4)); + printf("SPIRAM (DROM): Execute operation | Address: %p\n", test_addr); + test_addr(); + } else { + printf("SPIRAM (DROM): DROM alignment gap not added into heap\n"); + } +} +#endif /* CONFIG_SPIRAM_RODATA && !CONFIG_IDF_TARGET_ESP32S2 */ + #ifdef CONFIG_SOC_CPU_HAS_PMA void test_invalid_memory_region_write_violation(void) { diff --git a/tools/test_apps/system/panic/pytest_panic.py b/tools/test_apps/system/panic/pytest_panic.py index 9e61b0b1ab..5fd78e2bbc 100644 --- a/tools/test_apps/system/panic/pytest_panic.py +++ b/tools/test_apps/system/panic/pytest_panic.py @@ -157,7 +157,8 @@ def common_test(dut: PanicTestDut, config: str, expected_backtrace: Optional[Lis dut.revert_log_level() return # don't expect "Rebooting" output below - # We will only perform comparisons for ELF files, as we are not introducing any new fields to the binary file format. + # We will only perform comparisons for ELF files, + # as we are not introducing any new fields to the binary file format. if 'bin' in config: expected_coredump = None @@ -751,6 +752,15 @@ CONFIGS_MEMPROT_FLASH_IDROM = [ pytest.param('memprot_esp32p4', marks=[pytest.mark.esp32p4]) ] +CONFIGS_MEMPROT_SPIRAM_XIP_IROM_ALIGNMENT_HEAP = [ + pytest.param('memprot_spiram_xip_esp32p4', marks=[pytest.mark.esp32p4]) +] + +CONFIGS_MEMPROT_SPIRAM_XIP_DROM_ALIGNMENT_HEAP = [ + pytest.param('memprot_spiram_xip_esp32s3', marks=[pytest.mark.esp32s3]), + pytest.param('memprot_spiram_xip_esp32p4', marks=[pytest.mark.esp32p4]) +] + CONFIGS_MEMPROT_INVALID_REGION_PROTECTION_USING_PMA = [ pytest.param('memprot_esp32c6', marks=[pytest.mark.esp32c6]), pytest.param('memprot_esp32h2', marks=[pytest.mark.esp32h2]), @@ -1020,8 +1030,35 @@ def test_drom_reg_execute_violation(dut: PanicTestDut, test_func_name: str) -> N dut.expect_cpu_reset() -@pytest.mark.parametrize('config', CONFIGS_MEMPROT_INVALID_REGION_PROTECTION_USING_PMA, indirect=True) +@pytest.mark.parametrize('config', CONFIGS_MEMPROT_SPIRAM_XIP_IROM_ALIGNMENT_HEAP, indirect=True) @pytest.mark.generic +def test_spiram_xip_irom_alignment_reg_execute_violation(dut: PanicTestDut, test_func_name: str) -> None: + dut.run_test_func(test_func_name) + try: + dut.expect_gme('Instruction access fault') + except Exception: + dut.expect_exact('SPIRAM (IROM): IROM alignment gap not added into heap') + dut.expect_reg_dump(0) + dut.expect_cpu_reset() + + +@pytest.mark.parametrize('config', CONFIGS_MEMPROT_SPIRAM_XIP_DROM_ALIGNMENT_HEAP, indirect=True) +@pytest.mark.generic +def test_spiram_xip_drom_alignment_reg_execute_violation(dut: PanicTestDut, test_func_name: str) -> None: + dut.run_test_func(test_func_name) + try: + if dut.target == 'esp32s3': + dut.expect_gme('InstructionFetchError') + else: + dut.expect_gme('Instruction access fault') + except Exception: + dut.expect_exact('SPIRAM (DROM): DROM alignment gap not added into heap') + dut.expect_reg_dump(0) + dut.expect_cpu_reset() + + +@pytest.mark.generic +@pytest.mark.parametrize('config', CONFIGS_MEMPROT_INVALID_REGION_PROTECTION_USING_PMA, indirect=True) def test_invalid_memory_region_write_violation(dut: PanicTestDut, test_func_name: str) -> None: dut.run_test_func(test_func_name) dut.expect_gme('Store access fault') diff --git a/tools/test_apps/system/panic/sdkconfig.ci.memprot_esp32p4 b/tools/test_apps/system/panic/sdkconfig.ci.memprot_esp32p4 index bfe815e677..eeee9f8d62 100644 --- a/tools/test_apps/system/panic/sdkconfig.ci.memprot_esp32p4 +++ b/tools/test_apps/system/panic/sdkconfig.ci.memprot_esp32p4 @@ -6,3 +6,7 @@ CONFIG_ESP_SYSTEM_PMP_IDRAM_SPLIT=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/sdkconfig.ci.memprot_spiram_xip_esp32p4 b/tools/test_apps/system/panic/sdkconfig.ci.memprot_spiram_xip_esp32p4 new file mode 100644 index 0000000000..0f614e2a42 --- /dev/null +++ b/tools/test_apps/system/panic/sdkconfig.ci.memprot_spiram_xip_esp32p4 @@ -0,0 +1,13 @@ +# Restricting to ESP32P4 +CONFIG_IDF_TARGET="esp32p4" + +# Enabling memory protection +CONFIG_ESP_SYSTEM_PMP_IDRAM_SPLIT=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/sdkconfig.ci.memprot_spiram_xip_esp32s3 b/tools/test_apps/system/panic/sdkconfig.ci.memprot_spiram_xip_esp32s3 new file mode 100644 index 0000000000..56dede974c --- /dev/null +++ b/tools/test_apps/system/panic/sdkconfig.ci.memprot_spiram_xip_esp32s3 @@ -0,0 +1,17 @@ +# Restricting to ESP32S3 +CONFIG_IDF_TARGET="esp32s3" + +# Enabling memory protection +CONFIG_ESP_SYSTEM_MEMPROT_FEATURE=y +CONFIG_ESP_SYSTEM_MEMPROT_FEATURE_LOCK=y + +# Enabling DCACHE +CONFIG_ESP32S3_DATA_CACHE_16KB=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 From 5e15adfae9603187162b563ad6f803f993f3fa98 Mon Sep 17 00:00:00 2001 From: "harshal.patil" Date: Mon, 5 May 2025 10:49:04 +0530 Subject: [PATCH 5/5] fix(esp_psram): Add XIP PSRAM alignment gaps in heap only if PSRAM protection is enabled --- components/esp_psram/esp_psram.c | 5 +++++ components/esp_system/ld/esp32p4/sections.ld.in | 16 ++++++++++------ 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/components/esp_psram/esp_psram.c b/components/esp_psram/esp_psram.c index 7a1a7948e5..3a55c8da28 100644 --- a/components/esp_psram/esp_psram.c +++ b/components/esp_psram/esp_psram.c @@ -411,6 +411,9 @@ 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); + // 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 // Here, SOC_MMU_DI_VADDR_SHARED is necessary because, for the targets that have separate data and instruction virtual address spaces, // the SPIRAM gap created due to the alignment needed while placing the instruction segment in the instruction virtual address space // cannot be added in heap because the region cannot be configured with write permissions. @@ -436,6 +439,8 @@ esp_err_t esp_psram_extram_add_to_heap_allocator(void) } } #endif /* CONFIG_SPIRAM_RODATA */ +#endif /* CONFIG_SPIRAM_PRE_CONFIGURE_MEMORY_PROTECTION */ + return ESP_OK; } diff --git a/components/esp_system/ld/esp32p4/sections.ld.in b/components/esp_system/ld/esp32p4/sections.ld.in index 9a9bbc437c..79962be575 100644 --- a/components/esp_system/ld/esp32p4/sections.ld.in +++ b/components/esp_system/ld/esp32p4/sections.ld.in @@ -295,10 +295,12 @@ SECTIONS */ . += _esp_flash_mmap_prefetch_pad_size; -#if CONFIG_SPIRAM_FETCH_INSTRUCTIONS - /* Align the end of flash text region as per PMP granularity */ +#if CONFIG_SPIRAM_FETCH_INSTRUCTIONS && CONFIG_SPIRAM_PRE_CONFIGURE_MEMORY_PROTECTION + /* Align the end of flash text region as per PMP granularity to allow using the + * page alignment gap created while mapping the flash region into the PSRAM memory. + */ . = ALIGN(_esp_pmp_align_size); -#endif // CONFIG_SPIRAM_FETCH_INSTRUCTIONS +#endif // CONFIG_SPIRAM_FETCH_INSTRUCTIONS && CONFIG_SPIRAM_PRE_CONFIGURE_MEMORY_PROTECTION _text_end = ABSOLUTE(.); /** @@ -450,10 +452,12 @@ SECTIONS . = ALIGN(ALIGNOF(.flash.tbss)); -#if CONFIG_SPIRAM_RODATA - /* Align the end of flash rodata region as per PMP granularity */ +#if CONFIG_SPIRAM_RODATA && CONFIG_SPIRAM_PRE_CONFIGURE_MEMORY_PROTECTION + /* Align the end of flash rodata region as per PMP granularity to allow using the + * page alignment gap created while mapping the flash region into the PSRAM memory. + */ . = ALIGN(_esp_pmp_align_size); -#endif // CONFIG_SPIRAM_RODATA +#endif // CONFIG_SPIRAM_RODATA && CONFIG_SPIRAM_PRE_CONFIGURE_MEMORY_PROTECTION _thread_local_data_end = ABSOLUTE(.); } > rodata_seg_low