From 46225a4026f23f453da52145345c39c676c2e74d Mon Sep 17 00:00:00 2001 From: "harshal.patil" Date: Thu, 20 Feb 2025 16:16:43 +0530 Subject: [PATCH 1/7] feat(esp_psram): Add a new API to just detect and enable the PSRAM - esp_psram_chip_init() just detects and does basic initialisations of PSRAM - esp_psram_init() initialises and maps/loads the PSRAM pages --- components/esp_psram/Kconfig.spiram.common | 23 ++++++++---- .../include/esp_private/esp_psram_extram.h | 13 ++++++- components/esp_psram/include/esp_psram.h | 3 +- components/esp_psram/linker.lf | 1 + components/esp_psram/system_layer/esp_psram.c | 36 +++++++++++-------- components/esp_system/port/cpu_start.c | 22 +++++++----- components/esp_system/port/soc/esp32p4/clk.c | 2 +- 7 files changed, 67 insertions(+), 33 deletions(-) diff --git a/components/esp_psram/Kconfig.spiram.common b/components/esp_psram/Kconfig.spiram.common index 2832f1e677..f215f1d8d1 100644 --- a/components/esp_psram/Kconfig.spiram.common +++ b/components/esp_psram/Kconfig.spiram.common @@ -2,18 +2,29 @@ # # sourced into the "SPIRAM config" submenu for a specific chip. -config SPIRAM_BOOT_INIT - bool "Initialize SPI RAM during startup" - default "y" +config SPIRAM_BOOT_HW_INIT + bool "Initialise the PSRAM related hardware" + depends on SPIRAM + default "y" if SPIRAM help - If this is enabled, the SPI RAM will be enabled during initial boot. Unless you + If this is enabled, the PSRAM hardware will be initialized during startup. + Enabling this config does not ensure make sure the availability of the PSRAM for usage, + but just initialises the PSRAM hardware. + This is necessary to configure PSRAM memory protection during the boot up. + +config SPIRAM_BOOT_INIT + bool "Initialize PSRAM during startup, including the hardware and memory related configurations" + default "y" + depends on SPIRAM_BOOT_HW_INIT + help + If this is enabled, the PSRAM will be enabled during initial boot. Unless you have specific requirements, you'll want to leave this enabled so memory allocated - during boot-up can also be placed in SPI RAM. + during boot-up can also be placed in PSRAM. config SPIRAM_IGNORE_NOTFOUND bool "Ignore PSRAM when not found" default "n" - depends on SPIRAM_BOOT_INIT && !SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY && !SPIRAM_ALLOW_NOINIT_SEG_EXTERNAL_MEMORY + depends on SPIRAM_BOOT_HW_INIT && !SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY && !SPIRAM_ALLOW_NOINIT_SEG_EXTERNAL_MEMORY help Normally, if psram initialization is enabled during compile time but not found at runtime, it is seen as an error making the CPU panic. If this is enabled, booting will complete 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..3066d8c281 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,17 @@ bool esp_psram_extram_test(void); */ void esp_psram_bss_init(void); +/** + * @brief Initialize the PSRAM hardware. + * Just initialize the PSRAM hardware, does not load the XIP segments or map the PSRAM memory + * + * @return + * - ESP_OK: On success + * - ESP_FAIL: PSRAM isn't initialized successfully, potential reason would be: wrong VDDSDIO, invalid chip ID, etc. + * - ESP_ERR_INVALID_STATE: PSRAM is initialized already + */ +esp_err_t esp_psram_chip_init(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/include/esp_psram.h b/components/esp_psram/include/esp_psram.h index 8692aef7cf..27c7f7b4a5 100644 --- a/components/esp_psram/include/esp_psram.h +++ b/components/esp_psram/include/esp_psram.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -17,6 +17,7 @@ extern "C" { /** * @brief Initialize PSRAM interface/hardware. + * Initializes the PSRAM hardware and load the XIP segments or maps the PSRAM memory * * @return * - ESP_OK: On success diff --git a/components/esp_psram/linker.lf b/components/esp_psram/linker.lf index 1e11799f15..65f737830e 100644 --- a/components/esp_psram/linker.lf +++ b/components/esp_psram/linker.lf @@ -20,6 +20,7 @@ entries: if SPIRAM_FLASH_LOAD_TO_PSRAM = y: esp_psram_impl_ap_hex (noflash) mmu_psram_flash_v2 (noflash) + esp_psram: esp_psram_chip_init (noflash) esp_psram: esp_psram_init (noflash) esp_psram: s_psram_chip_init (noflash) esp_psram: s_xip_psram_placement (noflash) diff --git a/components/esp_psram/system_layer/esp_psram.c b/components/esp_psram/system_layer/esp_psram.c index 003e365754..ad1b76a963 100644 --- a/components/esp_psram/system_layer/esp_psram.c +++ b/components/esp_psram/system_layer/esp_psram.c @@ -78,7 +78,8 @@ typedef struct { } psram_mem_t; typedef struct { - bool is_initialised; + bool is_chip_initialised; // if psram hardware is initialised or not + bool is_initialised; // if psram initialised with memory mapping or not and is ready to use /** * @note 1 * As we can't use heap allocator during this stage, we need to statically declare these regions. @@ -138,9 +139,9 @@ static void IRAM_ATTR s_mapping(int v_start, int size) } #endif //CONFIG_IDF_TARGET_ESP32 -static esp_err_t s_psram_chip_init(uint32_t *out_available_size) +static esp_err_t s_psram_chip_init(void) { - if (s_psram_ctx.is_initialised) { + if (s_psram_ctx.is_chip_initialised) { return ESP_ERR_INVALID_STATE; } @@ -152,7 +153,7 @@ static esp_err_t s_psram_chip_init(uint32_t *out_available_size) #endif return ret; } - s_psram_ctx.is_initialised = true; + s_psram_ctx.is_chip_initialised = true; uint32_t psram_physical_size = 0; ret = esp_psram_impl_get_physical_size(&psram_physical_size); @@ -167,13 +168,6 @@ static esp_err_t s_psram_chip_init(uint32_t *out_available_size) ESP_EARLY_LOGI(TAG, "PSRAM initialized, cache is in low/high (2-core) mode."); #endif #endif - - uint32_t psram_available_size = 0; - ret = esp_psram_impl_get_available_size(&psram_available_size); - assert(ret == ESP_OK); - - *out_available_size = psram_available_size; - return ESP_OK; } @@ -349,15 +343,26 @@ static void s_psram_mapping(uint32_t psram_available_size, uint32_t start_page) #endif } +esp_err_t esp_psram_chip_init(void) +{ + return s_psram_chip_init(); +} + esp_err_t esp_psram_init(void) { esp_err_t ret = ESP_FAIL; - uint32_t psram_available_size = 0; - ret = s_psram_chip_init(&psram_available_size); - if (ret != ESP_OK) { - return ret; + + if (!s_psram_ctx.is_chip_initialised) { + ret = esp_psram_chip_init(); + if (ret != ESP_OK) { + return ret; + } } + uint32_t psram_available_size = 0; + ret = esp_psram_impl_get_available_size(&psram_available_size); + assert(ret == ESP_OK); + /** * `start_page` is the psram physical address in MMU page size. * MMU page size on ESP32S2 is 64KB @@ -382,6 +387,7 @@ esp_err_t esp_psram_init(void) cache_register_writeback(&drv); #endif + s_psram_ctx.is_initialised = true; return ESP_OK; } diff --git a/components/esp_system/port/cpu_start.c b/components/esp_system/port/cpu_start.c index c1b66a0930..5af3d7bbbc 100644 --- a/components/esp_system/port/cpu_start.c +++ b/components/esp_system/port/cpu_start.c @@ -565,19 +565,23 @@ void IRAM_ATTR call_start_cpu0(void) #endif #endif +#if CONFIG_SPIRAM_BOOT_HW_INIT + if (esp_psram_chip_init() != ESP_OK) { +#if CONFIG_SPIRAM_IGNORE_NOTFOUND + ESP_DRAM_LOGE(TAG, "Failed to init external RAM; continuing without it."); +#else + ESP_DRAM_LOGE(TAG, "Failed to init external RAM!"); + abort(); +#endif + } +#endif + #if CONFIG_SPIRAM_BOOT_INIT if (esp_psram_init() != ESP_OK) { #if CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY ESP_DRAM_LOGE(TAG, "Failed to init external RAM, needed for external .bss segment"); abort(); #endif - -#if CONFIG_SPIRAM_IGNORE_NOTFOUND - ESP_EARLY_LOGI(TAG, "Failed to init external RAM; continuing without it."); -#else - ESP_DRAM_LOGE(TAG, "Failed to init external RAM!"); - abort(); -#endif } #endif @@ -792,13 +796,13 @@ void IRAM_ATTR call_start_cpu0(void) } #if CONFIG_IDF_TARGET_ESP32 -#if !CONFIG_SPIRAM_BOOT_INIT +#if !CONFIG_SPIRAM_BOOT_HW_INIT // If psram is uninitialized, we need to improve some flash configuration. bootloader_flash_clock_config(&fhdr); bootloader_flash_gpio_config(&fhdr); bootloader_flash_dummy_config(&fhdr); bootloader_flash_cs_timing_config(); -#endif //!CONFIG_SPIRAM_BOOT_INIT +#endif //!CONFIG_SPIRAM_BOOT_HW_INIT #endif //CONFIG_IDF_TARGET_ESP32 #if CONFIG_SPI_FLASH_SIZE_OVERRIDE diff --git a/components/esp_system/port/soc/esp32p4/clk.c b/components/esp_system/port/soc/esp32p4/clk.c index 2475aa00db..9ca7f49cbe 100644 --- a/components/esp_system/port/soc/esp32p4/clk.c +++ b/components/esp_system/port/soc/esp32p4/clk.c @@ -414,7 +414,7 @@ __attribute__((weak)) void esp_perip_clk_init(void) REG_CLR_BIT(LP_CLKRST_HP_CLK_CTRL_REG, LP_CLKRST_HP_SDIO_PLL2_CLK_EN); REG_CLR_BIT(LP_CLKRST_HP_CLK_CTRL_REG, LP_CLKRST_HP_SDIO_PLL1_CLK_EN); REG_CLR_BIT(LP_CLKRST_HP_CLK_CTRL_REG, LP_CLKRST_HP_SDIO_PLL0_CLK_EN); -#if !CONFIG_SPIRAM_BOOT_INIT +#if !CONFIG_SPIRAM_BOOT_HW_INIT REG_CLR_BIT(LP_CLKRST_HP_CLK_CTRL_REG, LP_CLKRST_HP_MPLL_500M_CLK_EN); #endif REG_CLR_BIT(LP_SYSTEM_REG_HP_ROOT_CLK_CTRL_REG, LP_SYSTEM_REG_CPU_CLK_EN); From 0f628496c86387e7250ffba28200d0402d5d2c1c Mon Sep 17 00:00:00 2001 From: "harshal.patil" Date: Thu, 20 Feb 2025 16:18:20 +0530 Subject: [PATCH 2/7] feat(esp_psram): Add some helper APIs to get usable PSRAM memory size --- .../include/esp_private/esp_psram_extram.h | 11 +++ components/esp_psram/system_layer/esp_psram.c | 89 +++++++++++++++++++ .../include/esp_private/mmu_psram_flash.h | 18 +++- .../esp_psram/xip_impl/mmu_psram_flash.c | 35 +++++--- .../esp_psram/xip_impl/mmu_psram_flash_v2.c | 37 +++++--- 5 files changed, 164 insertions(+), 26 deletions(-) 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; From 49f253361ef528ebbfede2f94f87415d28113226 Mon Sep 17 00:00:00 2001 From: "harshal.patil" Date: Fri, 14 Feb 2025 12:29:24 +0530 Subject: [PATCH 3/7] feat(esp_psram): Add the gap created due to alignment of XIP segments in heap --- components/esp_psram/system_layer/esp_psram.c | 55 ++++++++++++++++++- .../include/esp_private/mmu_psram_flash.h | 34 ++++++++++++ .../esp_psram/xip_impl/mmu_psram_flash.c | 43 +++++++++++++++ .../esp_psram/xip_impl/mmu_psram_flash_v2.c | 43 +++++++++++++++ 4 files changed, 173 insertions(+), 2 deletions(-) diff --git a/components/esp_psram/system_layer/esp_psram.c b/components/esp_psram/system_layer/esp_psram.c index 109b4aabc7..471408b0b8 100644 --- a/components/esp_psram/system_layer/esp_psram.c +++ b/components/esp_psram/system_layer/esp_psram.c @@ -61,6 +61,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; @@ -71,6 +79,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; @@ -506,6 +516,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; } @@ -515,8 +550,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/xip_impl/include/esp_private/mmu_psram_flash.h b/components/esp_psram/xip_impl/include/esp_private/mmu_psram_flash.h index 416f7bba85..a1fbcf0f4b 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 @@ -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/xip_impl/mmu_psram_flash.c b/components/esp_psram/xip_impl/mmu_psram_flash.c index faa0510307..c33c9f26e0 100644 --- a/components/esp_psram/xip_impl/mmu_psram_flash.c +++ b/components/esp_psram/xip_impl/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/xip_impl/mmu_psram_flash_v2.c b/components/esp_psram/xip_impl/mmu_psram_flash_v2.c index 7166167afe..7611ac33af 100644 --- a/components/esp_psram/xip_impl/mmu_psram_flash_v2.c +++ b/components/esp_psram/xip_impl/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(); @@ -129,6 +151,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 ab229a34b39ab8fe65e3b8275e8e8834c97bf111 Mon Sep 17 00:00:00 2001 From: "harshal.patil" Date: Tue, 18 Feb 2025 23:28:26 +0530 Subject: [PATCH 4/7] feat(cpu_region_protect): Enable basic memory protection for SPIRAM --- .../port/esp32c5/cpu_region_protect.c | 85 +++++++-- .../port/esp32c61/cpu_region_protect.c | 64 ++++++- .../port/esp32p4/cpu_region_protect.c | 54 ++++-- components/esp_psram/Kconfig.spiram.common | 24 +++ .../include/esp_private/esp_psram_extram.h | 1 + components/esp_psram/system_layer/esp_psram.c | 180 +++++++++--------- .../esp_system/ld/esp32c5/sections.ld.in | 11 ++ .../esp_system/ld/esp32c61/sections.ld.in | 11 ++ .../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 | 53 +++++- .../system/panic/sdkconfig.ci.memprot_esp32c5 | 4 + .../panic/sdkconfig.ci.memprot_esp32c61 | 4 + .../system/panic/sdkconfig.ci.memprot_esp32p4 | 4 + .../sdkconfig.ci.memprot_spiram_xip_esp32c5 | 13 ++ .../sdkconfig.ci.memprot_spiram_xip_esp32c61 | 13 ++ .../sdkconfig.ci.memprot_spiram_xip_esp32p4 | 13 ++ .../sdkconfig.ci.memprot_spiram_xip_esp32s3 | 17 ++ 20 files changed, 479 insertions(+), 133 deletions(-) create mode 100644 tools/test_apps/system/panic/sdkconfig.ci.memprot_spiram_xip_esp32c5 create mode 100644 tools/test_apps/system/panic/sdkconfig.ci.memprot_spiram_xip_esp32c61 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/esp32c5/cpu_region_protect.c b/components/esp_hw_support/port/esp32c5/cpu_region_protect.c index 339ae20f34..5de2dc4cf5 100644 --- a/components/esp_hw_support/port/esp32c5/cpu_region_protect.c +++ b/components/esp_hw_support/port/esp32c5/cpu_region_protect.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -9,6 +9,9 @@ #include "esp_cpu.h" #include "esp_fault.h" #include "esp32c5/rom/rom_layout.h" +#if CONFIG_SPIRAM +#include "esp_private/esp_psram_extram.h" +#endif /* CONFIG_SPIRAM */ #ifdef BOOTLOADER_BUILD // Without L bit set @@ -26,6 +29,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) { @@ -178,15 +182,60 @@ 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)); PMP_ENTRY_CFG_RESET(6); + PMP_ENTRY_SET(6, SOC_IROM_LOW, NONE); + +/** + Virtual space layout: + + _________ <- SOC_IROM_LOW + | | + |_______| <- _instruction_reserved_end + |_______| <- page_aligned_irom_resv_end + | | + |_______| <- _rodata_reserved_end + |_______| <- page_aligned_drom_resv_end + | | + | | + | | + |_______| <- page_aligned_drom_resv_end + available_psram_heap + | | + | | + | | + | | + |_______| <- SOC_DROM_HIGH + + if CONFIG_SPIRAM_FETCH_INSTRUCTIONS: [_instruction_reserved_end, page_aligned_irom_resv_end) in heap (RW) + if CONFIG_SPIRAM_RODATA: [_rodata_reserved_end, page_aligned_drom_resv_end) in heap (RW) + if CONFIG_SPIRAM: [_rodata_reserved_end, page_aligned_drom_resv_end + available_psram_heap] in heap / reserved for mapping (RW) +*/ + PMP_ENTRY_CFG_RESET(7); PMP_ENTRY_CFG_RESET(8); - PMP_ENTRY_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_CFG_RESET(9); + +#if CONFIG_SPIRAM_FETCH_INSTRUCTIONS && CONFIG_SPIRAM_PRE_CONFIGURE_MEMORY_PROTECTION + PMP_ENTRY_SET(7, (uint32_t)(&_instruction_reserved_end), PMP_TOR | RX); + PMP_ENTRY_SET(8, page_aligned_irom_resv_end, PMP_TOR | RW); +#else + PMP_ENTRY_SET(7, page_aligned_irom_resv_end, PMP_TOR | RX); + PMP_ENTRY_SET(8, page_aligned_irom_resv_end, NONE); +#endif /* CONFIG_SPIRAM_FETCH_INSTRUCTIONS && CONFIG_SPIRAM_PRE_CONFIGURE_MEMORY_PROTECTION */ + +#if CONFIG_SPIRAM_RODATA && CONFIG_SPIRAM_PRE_CONFIGURE_MEMORY_PROTECTION + PMP_ENTRY_SET(9, (uint32_t)(&_rodata_reserved_end), PMP_TOR | R); +#else + PMP_ENTRY_SET(9, page_aligned_drom_resv_end, PMP_TOR | R); +#endif /* CONFIG_SPIRAM_RODATA && CONFIG_SPIRAM_PRE_CONFIGURE_MEMORY_PROTECTION */ + +#if CONFIG_SPIRAM_PRE_CONFIGURE_MEMORY_PROTECTION + size_t available_psram_heap = esp_psram_get_heap_size_to_protect(); + PMP_ENTRY_CFG_RESET(10); + PMP_ENTRY_SET(10, ALIGN_UP(page_aligned_drom_resv_end + available_psram_heap, SOC_CPU_PMP_REGION_GRANULARITY), PMP_TOR | RW); +#endif /* CONFIG_SPIRAM_PRE_CONFIGURE_MEMORY_PROTECTION */ #else const uint32_t pmpaddr6 = PMPADDR_NAPOT(SOC_IROM_LOW, SOC_IROM_HIGH); // Add the W attribute in the case of PSRAM @@ -201,29 +250,31 @@ 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 ULP coprocessor #if CONFIG_ESP_SYSTEM_PMP_LP_CORE_RESERVE_MEM_EXECUTABLE - PMP_ENTRY_SET(10, (int)&_rtc_text_start, PMP_TOR | RWX); + PMP_ENTRY_SET(12, (int)&_rtc_text_start, PMP_TOR | RWX); #else - PMP_ENTRY_SET(10, (int)&_rtc_text_start, PMP_TOR | RW); + PMP_ENTRY_SET(12, (int)&_rtc_text_start, PMP_TOR | RW); #endif - PMP_ENTRY_SET(11, (int)&_rtc_text_end, PMP_TOR | RX); - PMP_ENTRY_SET(12, SOC_RTC_IRAM_HIGH, PMP_TOR | RW); + 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 // 6. Peripheral addresses - const uint32_t pmpaddr13 = PMPADDR_NAPOT(SOC_PERIPHERAL_LOW, SOC_PERIPHERAL_HIGH); - PMP_ENTRY_SET(13, pmpaddr13, PMP_NAPOT | RW); + PMP_ENTRY_CFG_RESET(15); + const uint32_t pmpaddr15 = PMPADDR_NAPOT(SOC_PERIPHERAL_LOW, SOC_PERIPHERAL_HIGH); + PMP_ENTRY_SET(15, pmpaddr15, PMP_NAPOT | RW); _Static_assert(SOC_PERIPHERAL_LOW < SOC_PERIPHERAL_HIGH, "Invalid peripheral region"); } diff --git a/components/esp_hw_support/port/esp32c61/cpu_region_protect.c b/components/esp_hw_support/port/esp32c61/cpu_region_protect.c index b0866ac5f0..080a56a24f 100644 --- a/components/esp_hw_support/port/esp32c61/cpu_region_protect.c +++ b/components/esp_hw_support/port/esp32c61/cpu_region_protect.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -10,6 +10,9 @@ #include "esp_cpu.h" #include "esp_fault.h" #include "esp32c61/rom/rom_layout.h" +#if CONFIG_SPIRAM +#include "esp_private/esp_psram_extram.h" +#endif /* CONFIG_SPIRAM */ #ifdef BOOTLOADER_BUILD // Without L bit set @@ -27,6 +30,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) { @@ -172,16 +176,60 @@ 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)); PMP_ENTRY_CFG_RESET(7); + PMP_ENTRY_SET(7, SOC_IROM_LOW, NONE); + +/** + Virtual space layout: + + _________ <- SOC_IROM_LOW + | | + |_______| <- _instruction_reserved_end + |_______| <- page_aligned_irom_resv_end + | | + |_______| <- _rodata_reserved_end + |_______| <- page_aligned_drom_resv_end + | | + | | + | | + |_______| <- page_aligned_drom_resv_end + available_psram_heap + | | + | | + | | + | | + |_______| <- SOC_DROM_HIGH + + if CONFIG_SPIRAM_FETCH_INSTRUCTIONS: [_instruction_reserved_end, page_aligned_irom_resv_end) in heap (RW) + if CONFIG_SPIRAM_RODATA: [_rodata_reserved_end, page_aligned_drom_resv_end) in heap (RW) + if CONFIG_SPIRAM: [_rodata_reserved_end, page_aligned_drom_resv_end + available_psram_heap] in heap / reserved for mapping (RW) +*/ + PMP_ENTRY_CFG_RESET(8); PMP_ENTRY_CFG_RESET(9); PMP_ENTRY_CFG_RESET(10); - PMP_ENTRY_SET(7, SOC_IROM_LOW, NONE); - PMP_ENTRY_SET(8, irom_resv_end, PMP_TOR | RX); - PMP_ENTRY_SET(9, drom_resv_end, PMP_TOR | R); + +#if CONFIG_SPIRAM_FETCH_INSTRUCTIONS && CONFIG_SPIRAM_PRE_CONFIGURE_MEMORY_PROTECTION + PMP_ENTRY_SET(8, (uint32_t)(&_instruction_reserved_end), PMP_TOR | RX); + PMP_ENTRY_SET(9, page_aligned_irom_resv_end, PMP_TOR | RW); +#else + PMP_ENTRY_SET(8, page_aligned_irom_resv_end, PMP_TOR | RX); + PMP_ENTRY_SET(9, page_aligned_irom_resv_end, NONE); +#endif /* CONFIG_SPIRAM_FETCH_INSTRUCTIONS && CONFIG_SPIRAM_PRE_CONFIGURE_MEMORY_PROTECTION */ + +#if CONFIG_SPIRAM_RODATA && CONFIG_SPIRAM_PRE_CONFIGURE_MEMORY_PROTECTION + PMP_ENTRY_SET(10, (uint32_t)(&_rodata_reserved_end), PMP_TOR | R); +#else + PMP_ENTRY_SET(10, page_aligned_drom_resv_end, PMP_TOR | R); +#endif /* CONFIG_SPIRAM_RODATA && CONFIG_SPIRAM_PRE_CONFIGURE_MEMORY_PROTECTION*/ + +#if CONFIG_SPIRAM_PRE_CONFIGURE_MEMORY_PROTECTION + size_t available_psram_heap = esp_psram_get_heap_size_to_protect(); + PMP_ENTRY_CFG_RESET(11); + PMP_ENTRY_SET(11, ALIGN_UP(page_aligned_drom_resv_end + available_psram_heap, SOC_CPU_PMP_REGION_GRANULARITY), PMP_TOR | RW); +#endif /* CONFIG_SPIRAM_PRE_CONFIGURE_MEMORY_PROTECTION */ #else const uint32_t pmpaddr7 = PMPADDR_NAPOT(SOC_IROM_LOW, SOC_IROM_HIGH); // Add the W attribute in the case of PSRAM @@ -190,7 +238,7 @@ void esp_cpu_configure_region_protection(void) #endif // 5. Peripheral addresses - const uint32_t pmpaddr10 = PMPADDR_NAPOT(SOC_PERIPHERAL_LOW, SOC_PERIPHERAL_HIGH); - PMP_ENTRY_SET(10, pmpaddr10, PMP_NAPOT | RW); + const uint32_t pmpaddr12 = PMPADDR_NAPOT(SOC_PERIPHERAL_LOW, SOC_PERIPHERAL_HIGH); + PMP_ENTRY_SET(12, pmpaddr12, PMP_NAPOT | RW); _Static_assert(SOC_PERIPHERAL_LOW < SOC_PERIPHERAL_HIGH, "Invalid peripheral region"); } 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 6f56e265b3..b46f338ed8 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,28 +237,28 @@ 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 #if CONFIG_ESP_SYSTEM_PMP_LP_CORE_RESERVE_MEM_EXECUTABLE - PMP_ENTRY_SET(10, (int)&_rtc_text_start, PMP_TOR | RWX); + PMP_ENTRY_SET(12, (int)&_rtc_text_start, PMP_TOR | RWX); #else - PMP_ENTRY_SET(10, (int)&_rtc_text_start, PMP_TOR | RW); + PMP_ENTRY_SET(12, (int)&_rtc_text_start, PMP_TOR | RW); #endif - PMP_ENTRY_SET(11, (int)&_rtc_text_end, PMP_TOR | RX); - PMP_ENTRY_SET(12, SOC_RTC_IRAM_HIGH, PMP_TOR | RW); + 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 f215f1d8d1..ea16849a52 100644 --- a/components/esp_psram/Kconfig.spiram.common +++ b/components/esp_psram/Kconfig.spiram.common @@ -21,6 +21,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 PSRAM. +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/include/esp_private/esp_psram_extram.h b/components/esp_psram/include/esp_private/esp_psram_extram.h index 29bcca30d4..61fffd4ae7 100644 --- a/components/esp_psram/include/esp_private/esp_psram_extram.h +++ b/components/esp_psram/include/esp_private/esp_psram_extram.h @@ -79,6 +79,7 @@ esp_err_t esp_psram_chip_init(void); * 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); diff --git a/components/esp_psram/system_layer/esp_psram.c b/components/esp_psram/system_layer/esp_psram.c index 471408b0b8..d80d8498fb 100644 --- a/components/esp_psram/system_layer/esp_psram.c +++ b/components/esp_psram/system_layer/esp_psram.c @@ -226,24 +226,6 @@ 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; @@ -376,77 +358,6 @@ 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; @@ -697,3 +608,94 @@ 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; + + // 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; + } +} +#endif /* CONFIG_SPIRAM_PRE_CONFIGURE_MEMORY_PROTECTION */ diff --git a/components/esp_system/ld/esp32c5/sections.ld.in b/components/esp_system/ld/esp32c5/sections.ld.in index 1bac043fb7..cdf188cb92 100644 --- a/components/esp_system/ld/esp32c5/sections.ld.in +++ b/components/esp_system/ld/esp32c5/sections.ld.in @@ -278,6 +278,11 @@ SECTIONS */ . += _esp_flash_mmap_prefetch_pad_size; +#if CONFIG_SPIRAM_FETCH_INSTRUCTIONS && CONFIG_SPIRAM_PRE_CONFIGURE_MEMORY_PROTECTION + /* Align the end of flash text region as per PMP granularity as PSRAM memory protection is enabled */ + . = ALIGN(_esp_pmp_align_size); +#endif // CONFIG_SPIRAM_FETCH_INSTRUCTIONS && CONFIG_SPIRAM_PRE_CONFIGURE_MEMORY_PROTECTION + _text_end = ABSOLUTE(.); /** * Mark the flash.text end. @@ -418,6 +423,12 @@ SECTIONS *(.tdata .tdata.* .gnu.linkonce.td.*) . = ALIGN(ALIGNOF(.flash.tbss)); + +#if CONFIG_SPIRAM_RODATA && CONFIG_SPIRAM_PRE_CONFIGURE_MEMORY_PROTECTION + /* Align the end of flash rodata region as per PMP granularity as PSRAM memory protection is enabled */ + . = ALIGN(_esp_pmp_align_size); +#endif // CONFIG_SPIRAM_RODATA && CONFIG_SPIRAM_PRE_CONFIGURE_MEMORY_PROTECTION + _thread_local_data_end = ABSOLUTE(.); } > default_rodata_seg ASSERT_SECTIONS_GAP(.flash.tdata, .flash.tbss) diff --git a/components/esp_system/ld/esp32c61/sections.ld.in b/components/esp_system/ld/esp32c61/sections.ld.in index 1403fcd041..0c7d1babf1 100644 --- a/components/esp_system/ld/esp32c61/sections.ld.in +++ b/components/esp_system/ld/esp32c61/sections.ld.in @@ -136,6 +136,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. @@ -276,6 +281,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(.); } > default_rodata_seg ASSERT_SECTIONS_GAP(.flash.tdata, .flash.tbss) diff --git a/components/esp_system/ld/esp32p4/sections.ld.in b/components/esp_system/ld/esp32p4/sections.ld.in index 9eca91d073..02b961d97f 100644 --- a/components/esp_system/ld/esp32p4/sections.ld.in +++ b/components/esp_system/ld/esp32p4/sections.ld.in @@ -293,6 +293,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. @@ -440,6 +445,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 2c2b103933..9b883d0e00 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) @@ -172,6 +175,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 1774b29155..85237ccff8 100644 --- a/tools/test_apps/system/panic/pytest_panic.py +++ b/tools/test_apps/system/panic/pytest_panic.py @@ -163,7 +163,8 @@ def common_test( 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 @@ -719,6 +720,29 @@ CONFIGS_MEMPROT_FLASH_IDROM = list( ) ) +CONFIGS_MEMPROT_SPIRAM_XIP_IROM_ALIGNMENT_HEAP = list( + itertools.chain( + itertools.product( + ['memprot_spiram_xip_esp32c5', 'memprot_spiram_xip_esp32c61', 'memprot_spiram_xip_esp32p4'], + ['esp32c5', 'esp32c61', 'esp32p4'], + ) + ) +) + +CONFIGS_MEMPROT_SPIRAM_XIP_DROM_ALIGNMENT_HEAP = list( + itertools.chain( + itertools.product( + [ + 'memprot_spiram_xip_esp32s3', + 'memprot_spiram_xip_esp32c5', + 'memprot_spiram_xip_esp32c61', + 'memprot_spiram_xip_esp32p4', + ], + ['esp32s3', 'esp32c5', 'esp32c61', 'esp32p4'], + ) + ) +) + CONFIGS_MEMPROT_INVALID_REGION_PROTECTION_USING_PMA = list( itertools.chain( itertools.product( @@ -1000,6 +1024,33 @@ def test_drom_reg_execute_violation(dut: PanicTestDut, test_func_name: str) -> N dut.expect_cpu_reset() +@pytest.mark.generic +@idf_parametrize('config, target', CONFIGS_MEMPROT_SPIRAM_XIP_IROM_ALIGNMENT_HEAP, indirect=['config', 'target']) +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.generic +@idf_parametrize('config, target', CONFIGS_MEMPROT_SPIRAM_XIP_DROM_ALIGNMENT_HEAP, indirect=['config', 'target']) +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 @idf_parametrize('config, target', CONFIGS_MEMPROT_INVALID_REGION_PROTECTION_USING_PMA, indirect=['config', 'target']) def test_invalid_memory_region_write_violation(dut: PanicTestDut, test_func_name: str) -> None: diff --git a/tools/test_apps/system/panic/sdkconfig.ci.memprot_esp32c5 b/tools/test_apps/system/panic/sdkconfig.ci.memprot_esp32c5 index a2a280afbe..003cec1475 100644 --- a/tools/test_apps/system/panic/sdkconfig.ci.memprot_esp32c5 +++ b/tools/test_apps/system/panic/sdkconfig.ci.memprot_esp32c5 @@ -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_esp32c61 b/tools/test_apps/system/panic/sdkconfig.ci.memprot_esp32c61 index 97235c12a2..cd96600170 100644 --- a/tools/test_apps/system/panic/sdkconfig.ci.memprot_esp32c61 +++ b/tools/test_apps/system/panic/sdkconfig.ci.memprot_esp32c61 @@ -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_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_esp32c5 b/tools/test_apps/system/panic/sdkconfig.ci.memprot_spiram_xip_esp32c5 new file mode 100644 index 0000000000..252ea1e0a9 --- /dev/null +++ b/tools/test_apps/system/panic/sdkconfig.ci.memprot_spiram_xip_esp32c5 @@ -0,0 +1,13 @@ +# Restricting to ESP32C5 +CONFIG_IDF_TARGET="esp32c5" + +# 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_esp32c61 b/tools/test_apps/system/panic/sdkconfig.ci.memprot_spiram_xip_esp32c61 new file mode 100644 index 0000000000..ce02425ae7 --- /dev/null +++ b/tools/test_apps/system/panic/sdkconfig.ci.memprot_spiram_xip_esp32c61 @@ -0,0 +1,13 @@ +# Restricting to ESP32C61 +CONFIG_IDF_TARGET="esp32c61" + +# 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_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 f58c78b6449ceb804736a554542cd112d0fa9af3 Mon Sep 17 00:00:00 2001 From: "harshal.patil" Date: Wed, 19 Feb 2025 11:43:43 +0530 Subject: [PATCH 5/7] fix(esp_psram): Fix loading PSRAM XIP segments after PMP split has been configured --- .../esp_psram/xip_impl/mmu_psram_flash_v2.c | 30 +++++++++++++++++-- 1 file changed, 27 insertions(+), 3 deletions(-) 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 7611ac33af..3f9f583254 100644 --- a/components/esp_psram/xip_impl/mmu_psram_flash_v2.c +++ b/components/esp_psram/xip_impl/mmu_psram_flash_v2.c @@ -54,6 +54,17 @@ static int s_drom_paddr_offset; static uint32_t s_do_load_from_flash(uint32_t flash_paddr_start, uint32_t size, uint32_t target_vaddr_start, uint32_t target_paddr_start) { uint32_t flash_end_page_vaddr = SOC_DRAM_FLASH_ADDRESS_HIGH - CONFIG_MMU_PAGE_SIZE; + uint32_t psram_vaddr_start; + +#if !CONFIG_SPIRAM_BOOT_INIT + // Once PMP sets up the IROM/DROM split, the target_vaddr_start would not be configured to write (W) access. Thus, performing s_do_load_from_flash() post the PMP split + // is configured, copying the flash contents to the PSRAM would generate a store access fault. + // Thus, we need to choose a different PSRAM virtual address (that would have the PMP write (W) access) to map and copy the flash contents into the PSRAM. + // Choosing the second last PSRAM page instead of the last one, to avoid overlap with flash_end_page_vaddr for targets that share the same flash and psram virtual space. + uint32_t psram_second_last_page_vaddr = SOC_DRAM_PSRAM_ADDRESS_HIGH - 2 * CONFIG_MMU_PAGE_SIZE; + assert((psram_second_last_page_vaddr % CONFIG_MMU_PAGE_SIZE) == 0); +#endif + ESP_EARLY_LOGV(TAG, "flash_paddr_start: 0x%"PRIx32", flash_end_page_vaddr: 0x%"PRIx32", size: 0x%"PRIx32", target_vaddr_start: 0x%"PRIx32, flash_paddr_start, flash_end_page_vaddr, size, target_vaddr_start); assert((flash_paddr_start % CONFIG_MMU_PAGE_SIZE) == 0); assert((flash_end_page_vaddr % CONFIG_MMU_PAGE_SIZE) == 0); @@ -62,15 +73,28 @@ static uint32_t s_do_load_from_flash(uint32_t flash_paddr_start, uint32_t size, uint32_t mapped_size = 0; while (mapped_size < size) { uint32_t actual_mapped_len = 0; - mmu_hal_map_region(MMU_LL_PSRAM_MMU_ID, MMU_TARGET_PSRAM0, target_vaddr_start, target_paddr_start + mapped_size, CONFIG_MMU_PAGE_SIZE, &actual_mapped_len); +#if !CONFIG_SPIRAM_BOOT_INIT + psram_vaddr_start = psram_second_last_page_vaddr; +#else + psram_vaddr_start = target_vaddr_start; +#endif /* !CONFIG_SPIRAM_BOOT_INIT*/ + + mmu_hal_map_region(MMU_LL_PSRAM_MMU_ID, MMU_TARGET_PSRAM0, psram_vaddr_start, target_paddr_start + mapped_size, CONFIG_MMU_PAGE_SIZE, &actual_mapped_len); assert(actual_mapped_len == CONFIG_MMU_PAGE_SIZE); mmu_hal_map_region(MMU_LL_FLASH_MMU_ID, MMU_TARGET_FLASH0, flash_end_page_vaddr, flash_paddr_start + mapped_size, CONFIG_MMU_PAGE_SIZE, &actual_mapped_len); assert(actual_mapped_len == CONFIG_MMU_PAGE_SIZE); - cache_hal_invalidate_addr(target_vaddr_start, CONFIG_MMU_PAGE_SIZE); + cache_hal_invalidate_addr(psram_vaddr_start, CONFIG_MMU_PAGE_SIZE); cache_hal_invalidate_addr(flash_end_page_vaddr, CONFIG_MMU_PAGE_SIZE); - memcpy((void *)target_vaddr_start, (void *)flash_end_page_vaddr, CONFIG_MMU_PAGE_SIZE); + memcpy((void *)psram_vaddr_start, (void *)flash_end_page_vaddr, CONFIG_MMU_PAGE_SIZE); + +#if !CONFIG_SPIRAM_BOOT_INIT + cache_hal_writeback_addr(psram_vaddr_start, CONFIG_MMU_PAGE_SIZE); + mmu_hal_map_region(MMU_LL_PSRAM_MMU_ID, MMU_TARGET_PSRAM0, target_vaddr_start, target_paddr_start + mapped_size, CONFIG_MMU_PAGE_SIZE, &actual_mapped_len); + assert(actual_mapped_len == CONFIG_MMU_PAGE_SIZE); + cache_hal_invalidate_addr(target_vaddr_start, CONFIG_MMU_PAGE_SIZE); +#endif /* !CONFIG_SPIRAM_BOOT_INIT */ ESP_EARLY_LOGV(TAG, "target_vaddr_start: 0x%"PRIx32, target_vaddr_start); mapped_size += CONFIG_MMU_PAGE_SIZE; From c67901fc07c201d3db5c47936d1f857c77f80007 Mon Sep 17 00:00:00 2001 From: "harshal.patil" Date: Thu, 13 Mar 2025 10:34:26 +0530 Subject: [PATCH 6/7] ci(esp_psram): Add a test to validate non-bootup PSRAM initialisation - Also adds a test for the newly added PSRAM helper APIs: esp_psram_get_heap_pool_size() and esp_psram_get_effective_mapped_size() --- .../esp_psram/test_apps/.build-test-rules.yml | 3 +++ .../test_apps/psram/main/CMakeLists.txt | 13 +++++++--- .../psram/main/test_psram_no_boot_init.c | 25 ++++++++++++++++++ .../esp_psram/test_apps/psram/pytest_psram.py | 26 +++++++++++++++++++ .../psram/sdkconfig.ci.psram_no_boot_init | 4 +++ .../psram/sdkconfig.ci.xip_psram_no_boot_init | 7 +++++ 6 files changed, 74 insertions(+), 4 deletions(-) create mode 100644 components/esp_psram/test_apps/psram/main/test_psram_no_boot_init.c create mode 100644 components/esp_psram/test_apps/psram/sdkconfig.ci.psram_no_boot_init create mode 100644 components/esp_psram/test_apps/psram/sdkconfig.ci.xip_psram_no_boot_init diff --git a/components/esp_psram/test_apps/.build-test-rules.yml b/components/esp_psram/test_apps/.build-test-rules.yml index c9b865f301..14b2fd4070 100644 --- a/components/esp_psram/test_apps/.build-test-rules.yml +++ b/components/esp_psram/test_apps/.build-test-rules.yml @@ -1,8 +1,11 @@ # Documentation: .gitlab/ci/README.md#manifest-file-to-control-the-buildtest-apps components/esp_psram/test_apps/psram: + enable: + - if: CONFIG_NAME == "psram_no_boot_init" and SOC_SPIRAM_SUPPORTED == 1 disable: - if: SOC_SPIRAM_SUPPORTED != 1 + - if: CONFIG_NAME == "xip_psram_no_boot_init" and SOC_SPIRAM_XIP_SUPPORTED != 1 depends_components: - esp_psram - esp_mm diff --git a/components/esp_psram/test_apps/psram/main/CMakeLists.txt b/components/esp_psram/test_apps/psram/main/CMakeLists.txt index d19067a385..4bf51e380b 100644 --- a/components/esp_psram/test_apps/psram/main/CMakeLists.txt +++ b/components/esp_psram/test_apps/psram/main/CMakeLists.txt @@ -1,10 +1,15 @@ idf_build_get_property(target IDF_TARGET) -set(srcs "test_app_main.c" - "test_psram.c") +set(srcs "test_app_main.c") -if(${target} STREQUAL "esp32") - list(APPEND srcs "test_himem.c" "test_4mpsram.c") +if(CONFIG_SPIRAM_BOOT_INIT) + list(APPEND srcs "test_psram.c") + + if(${target} STREQUAL "esp32") + list(APPEND srcs "test_himem.c" "test_4mpsram.c") + endif() +else() + list(APPEND srcs "test_psram_no_boot_init.c") endif() # In order for the cases defined by `TEST_CASE` to be linked into the final elf, diff --git a/components/esp_psram/test_apps/psram/main/test_psram_no_boot_init.c b/components/esp_psram/test_apps/psram/main/test_psram_no_boot_init.c new file mode 100644 index 0000000000..f1026863bd --- /dev/null +++ b/components/esp_psram/test_apps/psram/main/test_psram_no_boot_init.c @@ -0,0 +1,25 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ +#include "sdkconfig.h" +#include "unity.h" +#include "esp_psram.h" +#include "esp_private/esp_psram_extram.h" + +TEST_CASE("test psram no boot init", "[psram_no_boot_init]") +{ +#if CONFIG_SPIRAM_PRE_CONFIGURE_MEMORY_PROTECTION + // As PSRAM is just enabled and not initialised during the boot up, the API + // esp_psram_get_heap_size_to_protect() manually calculates the size of the PSRAM heap + size_t manually_calculated_psram_heap = esp_psram_get_heap_size_to_protect(); +#endif /* CONFIG_SPIRAM_PRE_CONFIGURE_MEMORY_PROTECTION */ + + TEST_ESP_OK(esp_psram_init()); + +#if CONFIG_SPIRAM_PRE_CONFIGURE_MEMORY_PROTECTION + size_t final_psram_heap = esp_psram_get_heap_size_to_protect(); + TEST_ASSERT_EQUAL(final_psram_heap, manually_calculated_psram_heap); +#endif /* CONFIG_SPIRAM_PRE_CONFIGURE_MEMORY_PROTECTION */ +} diff --git a/components/esp_psram/test_apps/psram/pytest_psram.py b/components/esp_psram/test_apps/psram/pytest_psram.py index cf5d41b7d4..38b56d77fd 100644 --- a/components/esp_psram/test_apps/psram/pytest_psram.py +++ b/components/esp_psram/test_apps/psram/pytest_psram.py @@ -112,3 +112,29 @@ def test_psram_esp32c5(dut: Dut) -> None: @idf_parametrize('target', ['esp32c61'], indirect=['target']) def test_psram_esp32c61(dut: Dut) -> None: dut.run_all_single_board_cases() + + +@pytest.mark.generic +@pytest.mark.parametrize( + 'config', + [ + 'xip_psram_no_boot_init', + ], + indirect=True, +) +@idf_parametrize('target', ['esp32s2', 'esp32s3', 'esp32c5', 'esp32c61'], indirect=['target']) +def test_xip_psram_no_boot_init(dut: Dut) -> None: + dut.run_all_single_board_cases() + + +@pytest.mark.generic +@pytest.mark.parametrize( + 'config', + [ + 'psram_no_boot_init', + ], + indirect=True, +) +@idf_parametrize('target', ['supported_targets'], indirect=['target']) +def test_psram_no_boot_init(dut: Dut) -> None: + dut.run_all_single_board_cases() diff --git a/components/esp_psram/test_apps/psram/sdkconfig.ci.psram_no_boot_init b/components/esp_psram/test_apps/psram/sdkconfig.ci.psram_no_boot_init new file mode 100644 index 0000000000..eec27d115c --- /dev/null +++ b/components/esp_psram/test_apps/psram/sdkconfig.ci.psram_no_boot_init @@ -0,0 +1,4 @@ +CONFIG_SPIRAM=y +CONFIG_SPIRAM_BOOT_HW_INIT=y +CONFIG_SPIRAM_BOOT_INIT=n +CONFIG_SPIRAM_PRE_CONFIGURE_MEMORY_PROTECTION=y diff --git a/components/esp_psram/test_apps/psram/sdkconfig.ci.xip_psram_no_boot_init b/components/esp_psram/test_apps/psram/sdkconfig.ci.xip_psram_no_boot_init new file mode 100644 index 0000000000..a9658128bd --- /dev/null +++ b/components/esp_psram/test_apps/psram/sdkconfig.ci.xip_psram_no_boot_init @@ -0,0 +1,7 @@ +CONFIG_SPIRAM=y +CONFIG_SPIRAM_XIP_FROM_PSRAM=y +CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY=y +CONFIG_SPIRAM_ALLOW_NOINIT_SEG_EXTERNAL_MEMORY=y +CONFIG_SPIRAM_BOOT_HW_INIT=y +CONFIG_SPIRAM_BOOT_INIT=n +CONFIG_SPIRAM_PRE_CONFIGURE_MEMORY_PROTECTION=y From 003b54ebfebe5a3d723bfc580db82777e1c3140e Mon Sep 17 00:00:00 2001 From: "harshal.patil" Date: Thu, 24 Apr 2025 15:45:51 +0530 Subject: [PATCH 7/7] feat(esp_psram): XIP from PSRAM can be used only when SPIRAM_BOOT_INIT=y for ESP32P4 In case of ESP32-P4, if SPIRAM memory initialisations are not performed during boot-up, XIP PSRAM can be setup because of different virtual range of PSRAM and Flash --- components/esp_psram/esp32p4/Kconfig.spiram | 1 + 1 file changed, 1 insertion(+) diff --git a/components/esp_psram/esp32p4/Kconfig.spiram b/components/esp_psram/esp32p4/Kconfig.spiram index 85335b3050..b8e741fa24 100644 --- a/components/esp_psram/esp32p4/Kconfig.spiram +++ b/components/esp_psram/esp32p4/Kconfig.spiram @@ -63,6 +63,7 @@ menu "PSRAM config" config SPIRAM_XIP_FROM_PSRAM bool "Enable Executable in place from (XiP) from PSRAM feature (READ HELP)" default n + depends on SPIRAM_BOOT_INIT select SPIRAM_FETCH_INSTRUCTIONS select SPIRAM_RODATA select SPIRAM_FLASH_LOAD_TO_PSRAM