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 3066d8c281..29bcca30d4 100644 --- a/components/esp_psram/include/esp_private/esp_psram_extram.h +++ b/components/esp_psram/include/esp_private/esp_psram_extram.h @@ -71,6 +71,17 @@ void esp_psram_bss_init(void); */ esp_err_t esp_psram_chip_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. + */ +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_psram/system_layer/esp_psram.c b/components/esp_psram/system_layer/esp_psram.c index ad1b76a963..109b4aabc7 100644 --- a/components/esp_psram/system_layer/esp_psram.c +++ b/components/esp_psram/system_layer/esp_psram.c @@ -216,6 +216,24 @@ static void s_xip_psram_placement(uint32_t *psram_available_size, uint32_t *out_ } #endif //#if CONFIG_SPIRAM_FETCH_INSTRUCTIONS || CONFIG_SPIRAM_RODATA +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 */ +} + static void s_psram_mapping(uint32_t psram_available_size, uint32_t start_page) { esp_err_t ret = ESP_FAIL; @@ -348,6 +366,77 @@ esp_err_t esp_psram_chip_init(void) return s_psram_chip_init(); } +/** + * @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; + + // return if the PSRAM is not enabled + if (!s_psram_ctx.is_chip_initialised) { + return 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) +{ + // return if the PSRAM is not enabled + if (!s_psram_ctx.is_chip_initialised) { + return 0; + } + + 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; + } +} + esp_err_t esp_psram_init(void) { esp_err_t ret = ESP_FAIL; diff --git a/components/esp_psram/xip_impl/include/esp_private/mmu_psram_flash.h b/components/esp_psram/xip_impl/include/esp_private/mmu_psram_flash.h index e95845a0f4..416f7bba85 100644 --- a/components/esp_psram/xip_impl/include/esp_private/mmu_psram_flash.h +++ b/components/esp_psram/xip_impl/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/xip_impl/mmu_psram_flash.c b/components/esp_psram/xip_impl/mmu_psram_flash.c index bb9288410a..faa0510307 100644 --- a/components/esp_psram/xip_impl/mmu_psram_flash.c +++ b/components/esp_psram/xip_impl/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/xip_impl/mmu_psram_flash_v2.c b/components/esp_psram/xip_impl/mmu_psram_flash_v2.c index 72c662c4e3..7166167afe 100644 --- a/components/esp_psram/xip_impl/mmu_psram_flash_v2.c +++ b/components/esp_psram/xip_impl/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_LOGV(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,12 +110,12 @@ 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); ESP_EARLY_LOGI(TAG, ".text xip on psram"); return ESP_OK; @@ -119,10 +123,15 @@ 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 + +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; @@ -130,8 +139,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_LOGV(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; } @@ -141,12 +150,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); ESP_EARLY_LOGI(TAG, ".rodata xip on psram"); return ESP_OK;