/* * SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include #include #include "sdkconfig.h" #include "esp_bit_defs.h" #include "esp_attr.h" #include "esp_log.h" #include "esp_rom_caps.h" #include "hal/mmu_ll.h" #include "hal/mmu_hal.h" #include "hal/cache_hal.h" #include "soc/soc_caps.h" #if ESP_ROM_NEEDS_SET_CACHE_MMU_SIZE #include "soc/mmu.h" #endif #include "esp_private/esp_mmu_map_private.h" #include "esp_private/esp_cache_private.h" #include "esp_mmu_map.h" #include "esp_rom_spiflash.h" #if CONFIG_SPIRAM #include "esp_private/esp_psram_extram.h" #if SOC_SPIRAM_XIP_SUPPORTED #include "esp_private/mmu_psram_flash.h" #endif #endif #if CONFIG_IDF_TARGET_ESP32 #include "esp_private/esp_cache_esp32_private.h" #endif #include "esp_private/cache_utils.h" #include "spi_flash_mmap.h" #include "esp_private/flash_mmap.h" #if CONFIG_SPIRAM_FETCH_INSTRUCTIONS extern char _instruction_reserved_start; extern char _instruction_reserved_end; #endif #if CONFIG_SPIRAM_RODATA extern char _rodata_reserved_start; extern char _rodata_reserved_end; #endif /* 0x1000000, 16MB */ #define FLASH_MMAP_ADDR_24BIT_MAX (BIT(24)) #if !CONFIG_IDF_TARGET_ESP32 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Mmap lock implementation. // This lock allows external caller (flash driver) freezing the mmap flash pages when erasing. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// typedef struct { _lock_t outer_mux; bool frozen; int freezing_wait_count; int acquired_count; //minus value means number of waiting callers SemaphoreHandle_t semphr_freeze; SemaphoreHandle_t semphr_acq; } mmap_lock_t; static mmap_lock_t s_mmap_lock; static esp_err_t mmap_lock_init(void) { esp_err_t ret = ESP_OK; _lock_init(&s_mmap_lock.outer_mux); s_mmap_lock.frozen = false; s_mmap_lock.freezing_wait_count = 0; s_mmap_lock.acquired_count = 0; s_mmap_lock.semphr_freeze = xSemaphoreCreateBinary(); if (s_mmap_lock.semphr_freeze == NULL) { ret = ESP_ERR_NO_MEM; goto err; } s_mmap_lock.semphr_acq = xSemaphoreCreateBinary(); if (s_mmap_lock.semphr_acq == NULL) { ret = ESP_ERR_NO_MEM; goto err; } return ESP_OK; err: if (s_mmap_lock.semphr_freeze != NULL) { vSemaphoreDelete(s_mmap_lock.semphr_freeze); s_mmap_lock.semphr_freeze = NULL; } return ret; } static void mmap_lock_acquire(void) { mmap_lock_t* const lock = &s_mmap_lock; bool wait = false; _lock_acquire(&lock->outer_mux); if (!lock->frozen) { assert(lock->acquired_count >= 0); lock->acquired_count++; } else { //Register one event assert(lock->acquired_count <= 0); lock->acquired_count--; wait = true; } _lock_release(&lock->outer_mux); if (wait) { //Wait for event xSemaphoreTake(lock->semphr_acq, portMAX_DELAY); } } static void mmap_lock_release(void) { mmap_lock_t* const lock = &s_mmap_lock; bool wakeup_freeze = false; _lock_acquire(&lock->outer_mux); assert(lock->acquired_count > 0); assert(lock->frozen == false); lock->acquired_count--; if (lock->acquired_count == 0 && lock->freezing_wait_count > 0) { //All acquiring nodes have released, and there are waiting freezing requests //Go to the freezing state and wake up one freeze request lock->freezing_wait_count--; lock->frozen = true; wakeup_freeze = true; } _lock_release(&lock->outer_mux); if (wakeup_freeze) { //Wake up one freezing request xSemaphoreGive(lock->semphr_freeze); } } static void mmap_lock_freeze(void) { mmap_lock_t* const lock = &s_mmap_lock; bool wait = false; _lock_acquire(&lock->outer_mux); if (lock->acquired_count > 0 || lock->frozen) { //If frozen, or already acquired, register one event and wait for it lock->freezing_wait_count++; wait = true; } else { lock->frozen = true; } _lock_release(&lock->outer_mux); if (wait) { //Wait for event trigger xSemaphoreTake(lock->semphr_freeze, portMAX_DELAY); } } static void mmap_lock_unfreeze(void) { mmap_lock_t* const lock = &s_mmap_lock; bool wakeup_frozen = false; int wakeup_acq_count = 0; assert(lock->frozen); assert(lock->acquired_count <= 0); _lock_acquire(&lock->outer_mux); if (lock->acquired_count < 0) { //acquiring requests has higher priority than freezing request lock->frozen = false; lock->acquired_count = -lock->acquired_count; wakeup_acq_count = lock->acquired_count; } else if (lock->freezing_wait_count > 0) { lock->freezing_wait_count--; lock->frozen = true; wakeup_frozen = true; } else { //otherwise no one owns the lock lock->frozen = false; } _lock_release(&lock->outer_mux); if (wakeup_frozen) { //Wake one freezing request xSemaphoreGive(lock->semphr_freeze); } else { //Wake up all acquiring requests for (int i = 0; i < wakeup_acq_count; i++) { xSemaphoreGive(lock->semphr_acq); } } } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Interfaces for mmap API and external caller (flash driver). //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //the count and the mapping table (esp_mmu_map) can only be touched when the mmap lock is acquired static int s_mmap_remain_count; //number of mmap regions that are still in use #define MMAP_CNT_INCREASE() do { \ assert(s_mmap_remain_count >= 0); \ s_mmap_remain_count++; \ } while (0) #define MMAP_CNT_DECREASE() do { \ s_mmap_remain_count--; \ assert(s_mmap_remain_count >= 0); \ } while (0) esp_err_t flash_mmap_lock_init(void) { return mmap_lock_init(); } bool flash_mmap_remain(void) { return s_mmap_remain_count > 0; } void flash_mmap_lock_freeze(void) { mmap_lock_freeze(); } void flash_mmap_lock_unfreeze(void) { mmap_lock_unfreeze(); } #else //!CONFIG_IDF_TARGET_ESP32 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Empty interfaces for mmap APIs (ESP32 only). //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #define mmap_lock_acquire() do {} while (0) #define mmap_lock_release() do {} while (0) #define MMAP_CNT_INCREASE() do {} while (0) #define MMAP_CNT_DECREASE() do {} while (0) #endif //!CONFIG_IDF_TARGET_ESP32 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Mmap operations //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #if !MMAP_ROM_IMPL_ENABLED typedef struct mmap_block_t { uint32_t *vaddr_list; int list_num; uint32_t permanent; //When this flag is set, the mmap region will last for a very long time. Don't wait for the unmap and release the mmap lock immediately when exit mmap calls. } mmap_block_t; /* ROM and patch information * Latest: Add OS function to avoid concurrent access with erase/program when XIP from PSRAM * V1: added to ROM */ // Called from esp_flash_read_encrypted which is also a ROM function. esp_err_t spi_flash_mmap(size_t src_addr, size_t size, spi_flash_mmap_flag_t flags, const void** out_ptr, spi_flash_mmap_handle_t* out_handle) { #if !CONFIG_BOOTLOADER_CACHE_32BIT_ADDR_QUAD_FLASH && !CONFIG_BOOTLOADER_CACHE_32BIT_ADDR_OCTAL_FLASH if (src_addr >= FLASH_MMAP_ADDR_24BIT_MAX || size > FLASH_MMAP_ADDR_24BIT_MAX || src_addr > FLASH_MMAP_ADDR_24BIT_MAX - size) { ESP_LOGE("flash_mmap", "Address 0x%08x is out of range for 24bit flash mapping, see CONFIG_BOOTLOADER_CACHE_32BIT_ADDR_QUAD_FLASH and CONFIG_BOOTLOADER_CACHE_32BIT_ADDR_OCTAL_FLASH for more details", src_addr); return ESP_ERR_INVALID_ARG; } #endif esp_err_t ret = ESP_FAIL; mmu_mem_caps_t caps = 0; void *ptr = NULL; mmap_block_t *block = NULL; uint32_t *vaddr_list = NULL; block = heap_caps_calloc(1, sizeof(mmap_block_t), MALLOC_CAP_INTERNAL); if (!block) { ret = ESP_ERR_NO_MEM; goto err; } vaddr_list = heap_caps_calloc(1, 1 * sizeof(uint32_t), MALLOC_CAP_INTERNAL); if (!vaddr_list) { ret = ESP_ERR_NO_MEM; goto err; } block->vaddr_list = vaddr_list; block->permanent = !(flags & SPI_FLASH_MMAP_FLAG_BLOCKS_WRITE); if (flags & SPI_FLASH_MMAP_FLAG_INST) { caps = MMU_MEM_CAP_EXEC | MMU_MEM_CAP_32BIT; } else { caps = MMU_MEM_CAP_READ | MMU_MEM_CAP_8BIT; } mmap_lock_acquire(); ret = esp_mmu_map(src_addr, size, MMU_TARGET_FLASH0, caps, ESP_MMU_MMAP_FLAG_PADDR_SHARED, &ptr); if (ret == ESP_OK) { vaddr_list[0] = (uint32_t)ptr; block->list_num = 1; } else if (ret == ESP_ERR_INVALID_STATE) { /** * paddr region is mapped already, * to keep `flash_mmap.c` original behaviour, we consider this as a valid behaviour. * Set `list_num` to 0 so we don't need to call `esp_mmu_unmap` to this one, as `esp_mmu_map` * doesn't really create a new handle. */ block->list_num = 0; } else { mmap_lock_release(); goto err; } MMAP_CNT_INCREASE(); *out_ptr = ptr; *out_handle = (uint32_t)block; if (block->permanent) { //If the mmap is permanent, the lock is released without waiting for the unmap. mmap_lock_release(); } return ESP_OK; err: if (vaddr_list) { free(vaddr_list); } if (block) { free(block); } return ret; } static int s_find_non_contiguous_block_nums(const int *pages, int page_count) { int nums = 1; int last_end = pages[0] + 1; for (int i = 1; i < page_count; i++) { if (pages[i] != last_end) { nums++; } last_end = pages[i] + 1; } return nums; } static void s_merge_contiguous_pages(const int *pages, uint32_t page_count, int block_nums, int (*out_blocks)[2]) { uint32_t last_end = pages[0] + 1; int new_array_id = 0; out_blocks[new_array_id][0] = pages[0]; out_blocks[new_array_id][1] = 1; for (int i = 1; i < page_count; i++) { if (pages[i] != last_end) { new_array_id += 1; assert(new_array_id < block_nums); out_blocks[new_array_id][0] = pages[i]; out_blocks[new_array_id][1] = 1; } else { out_blocks[new_array_id][1] += 1; } last_end = pages[i] + 1; } } static void s_pages_to_bytes(int (*blocks)[2], int block_nums) { for (int i = 0; i < block_nums; i++) { blocks[i][0] = blocks[i][0] * CONFIG_MMU_PAGE_SIZE; blocks[i][1] = blocks[i][1] * CONFIG_MMU_PAGE_SIZE; } } /* ROM and patch information * Latest: Add OS function to avoid concurrent access with erase/program * V1: added to ROM */ // Called from esp_flash_read_encrypted which is also a ROM function. esp_err_t spi_flash_mmap_pages(const int *pages, size_t page_count, spi_flash_mmap_flag_t flags, const void** out_ptr, spi_flash_mmap_handle_t* out_handle) { #if !CONFIG_BOOTLOADER_CACHE_32BIT_ADDR_QUAD_FLASH && !CONFIG_BOOTLOADER_CACHE_32BIT_ADDR_OCTAL_FLASH for (size_t i = 0; i < page_count; i++) { uint32_t phys = (uint32_t)pages[i] * CONFIG_MMU_PAGE_SIZE; if (phys >= FLASH_MMAP_ADDR_24BIT_MAX) { ESP_LOGE("flash_mmap", "Page %d (addr 0x%08x) is out of range for 24bit flash mapping", pages[i], phys); return ESP_ERR_INVALID_ARG; } } #endif esp_err_t ret = ESP_FAIL; mmu_mem_caps_t caps = 0; mmap_block_t *block = NULL; uint32_t *vaddr_list = NULL; int successful_cnt = 0; bool mmap_lock_acquired = false; int block_num = s_find_non_contiguous_block_nums(pages, page_count); int paddr_blocks[block_num][2]; s_merge_contiguous_pages(pages, page_count, block_num, paddr_blocks); s_pages_to_bytes(paddr_blocks, block_num); block = heap_caps_calloc(1, sizeof(mmap_block_t), MALLOC_CAP_INTERNAL); if (!block) { ret = ESP_ERR_NO_MEM; goto err; } vaddr_list = heap_caps_calloc(1, block_num * sizeof(uint32_t), MALLOC_CAP_INTERNAL); if (!vaddr_list) { ret = ESP_ERR_NO_MEM; goto err; } if (flags & SPI_FLASH_MMAP_FLAG_INST) { caps = MMU_MEM_CAP_EXEC | MMU_MEM_CAP_32BIT; } else { caps = MMU_MEM_CAP_READ | MMU_MEM_CAP_8BIT; } mmap_lock_acquire(); mmap_lock_acquired = true; for (int i = 0; i < block_num; i++) { void *ptr = NULL; ret = esp_mmu_map(paddr_blocks[i][0], paddr_blocks[i][1], MMU_TARGET_FLASH0, caps, ESP_MMU_MMAP_FLAG_PADDR_SHARED, &ptr); if (ret == ESP_OK) { vaddr_list[i] = (uint32_t)ptr; successful_cnt++; } else { /** * A note for `ret == ESP_ERR_INVALID_STATE`: * If one of the `*pages` are mapped already, this means we can't find a * consecutive vaddr block for these `*pages` */ goto err; } vaddr_list[i] = (uint32_t)ptr; } block->permanent = !(flags & SPI_FLASH_MMAP_FLAG_BLOCKS_WRITE); block->vaddr_list = vaddr_list; block->list_num = successful_cnt; MMAP_CNT_INCREASE(); /** * We get a contiguous vaddr block, but may contain multiple esp_mmu handles. * The first handle vaddr is the start address of this contiguous vaddr block. */ *out_ptr = (void *)vaddr_list[0]; *out_handle = (uint32_t)block; if (block->permanent) { //If the mmap is permanent, the lock is released without waiting for the unmap. mmap_lock_release(); } return ESP_OK; err: for (int i = 0; i < successful_cnt; i++) { esp_mmu_unmap((void *)vaddr_list[i]); } if (vaddr_list) { free(vaddr_list); } if (mmap_lock_acquired) { mmap_lock_release(); } if (block) { free(block); } return ret; } /* ROM and patch information * Latest: Add OS function to avoid concurrent access with erase/program * V1: added to ROM */ // Called from esp_flash_read_encrypted which is also a ROM function. void spi_flash_munmap(spi_flash_mmap_handle_t handle) { esp_err_t ret = ESP_FAIL; mmap_block_t *block = (void *)handle; if (block->permanent) { mmap_lock_acquire(); } for (int i = 0; i < block->list_num; i++) { ret = esp_mmu_unmap((void *)block->vaddr_list[i]); if (ret == ESP_ERR_NOT_FOUND) { assert(0 && "invalid handle, or handle already unmapped"); } } MMAP_CNT_DECREASE(); mmap_lock_release(); free(block->vaddr_list); free(block); } #else //!MMAP_ROM_IMPL_ENABLED //Using ROM v1, which can't understand other flags like SPI_FLASH_MMAP_FLAG_BLOCKS_WRITE. //Handle the BLOCKS_WRITE flag and lock in the wrapper, then call ROM impl. // //The "permanent" state (i.e. no BLOCKS_WRITE, lock released immediately after mmap) is encoded //in BIT(31) of the returned handle, mirroring the mmap_block_t::permanent field in the IDF //implementation above. extern esp_err_t rom_spi_flash_mmap(size_t src_addr, size_t size, spi_flash_mmap_flag_t flags, const void** out_ptr, spi_flash_mmap_handle_t* out_handle); extern esp_err_t rom_spi_flash_mmap_pages(const int *pages, size_t page_count, spi_flash_mmap_flag_t flags, const void** out_ptr, spi_flash_mmap_handle_t* out_handle); extern void rom_spi_flash_munmap(spi_flash_mmap_handle_t handle); #define ROM_MMAP_HANDLE_PERMANENT_BIT BIT(31) esp_err_t spi_flash_mmap(size_t src_addr, size_t size, spi_flash_mmap_flag_t flags, const void** out_ptr, spi_flash_mmap_handle_t* out_handle) { bool permanent = !(flags & SPI_FLASH_MMAP_FLAG_BLOCKS_WRITE); flags &= ~SPI_FLASH_MMAP_FLAG_BLOCKS_WRITE; mmap_lock_acquire(); esp_err_t ret = rom_spi_flash_mmap(src_addr, size, flags, out_ptr, out_handle); if (ret != ESP_OK) { mmap_lock_release(); return ret; } MMAP_CNT_INCREASE(); if (permanent) { assert((*out_handle & ROM_MMAP_HANDLE_PERMANENT_BIT) == 0); *out_handle |= ROM_MMAP_HANDLE_PERMANENT_BIT; mmap_lock_release(); } return ESP_OK; } esp_err_t spi_flash_mmap_pages(const int *pages, size_t page_count, spi_flash_mmap_flag_t flags, const void** out_ptr, spi_flash_mmap_handle_t* out_handle) { bool permanent = !(flags & SPI_FLASH_MMAP_FLAG_BLOCKS_WRITE); flags &= ~SPI_FLASH_MMAP_FLAG_BLOCKS_WRITE; mmap_lock_acquire(); esp_err_t ret = rom_spi_flash_mmap_pages(pages, page_count, flags, out_ptr, out_handle); if (ret != ESP_OK) { mmap_lock_release(); return ret; } MMAP_CNT_INCREASE(); if (permanent) { assert((*out_handle & ROM_MMAP_HANDLE_PERMANENT_BIT) == 0); *out_handle |= ROM_MMAP_HANDLE_PERMANENT_BIT; mmap_lock_release(); } return ESP_OK; } void spi_flash_munmap(spi_flash_mmap_handle_t handle) { bool permanent = handle & ROM_MMAP_HANDLE_PERMANENT_BIT; spi_flash_mmap_handle_t rom_handle = handle & ~ROM_MMAP_HANDLE_PERMANENT_BIT; if (permanent) { mmap_lock_acquire(); } rom_spi_flash_munmap(rom_handle); MMAP_CNT_DECREASE(); mmap_lock_release(); } #endif //!MMAP_ROM_IMPL_ENABLED #if !MMAP_ROM_IMPL_ENABLED void spi_flash_mmap_dump(void) { esp_mmu_map_dump_mapped_blocks(stdout); } uint32_t spi_flash_mmap_get_free_pages(spi_flash_mmap_memory_t memory) { mmu_mem_caps_t caps = 0; if (memory == SPI_FLASH_MMAP_INST) { caps = MMU_MEM_CAP_EXEC | MMU_MEM_CAP_32BIT; } else { caps = MMU_MEM_CAP_READ | MMU_MEM_CAP_8BIT; } size_t len = 0; esp_mmu_map_get_max_consecutive_free_block_size(caps, MMU_TARGET_FLASH0, &len); return len / CONFIG_MMU_PAGE_SIZE; } static bool IRAM_ATTR is_page_mapped_in_cache(uint32_t phys_addr, const void **out_ptr) { *out_ptr = NULL; mmu_mem_caps_t caps = 0; esp_err_t err = esp_mmu_paddr_find_caps(phys_addr, &caps); if (err == ESP_OK) { // On ESP32, we will always flush all, so always return true, and don't care the vaddr #if !CONFIG_IDF_TARGET_ESP32 uint32_t vaddr = 0; if (caps & MMU_MEM_CAP_EXEC) { mmu_hal_paddr_to_vaddr(0, phys_addr, MMU_TARGET_FLASH0, MMU_VADDR_INSTRUCTION, &vaddr); } else { mmu_hal_paddr_to_vaddr(0, phys_addr, MMU_TARGET_FLASH0, MMU_VADDR_DATA, &vaddr); } *out_ptr = (void *)vaddr; #endif return true; } return false; } /* Validates if given flash address has corresponding cache mapping, if yes, flushes cache memories */ IRAM_ATTR bool spi_flash_check_and_flush_cache(size_t start_addr, size_t length) { bool ret = false; /* align start_addr & length to full MMU pages */ uint32_t page_start_addr = start_addr & ~(SPI_FLASH_MMU_PAGE_SIZE-1); length += (start_addr - page_start_addr); length = (length + SPI_FLASH_MMU_PAGE_SIZE - 1) & ~(SPI_FLASH_MMU_PAGE_SIZE-1); for (uint32_t addr = page_start_addr; addr < page_start_addr + length; addr += SPI_FLASH_MMU_PAGE_SIZE) { if (addr >= g_rom_flashchip.chip_size) { return false; /* invalid address */ } const void *vaddr = NULL; if (is_page_mapped_in_cache(addr, &vaddr)) { #if CONFIG_IDF_TARGET_ESP32 cache_sync(); return true; #else // CONFIG_IDF_TARGET_ESP32 if (vaddr != NULL) { esp_cache_sync_ops_enter_critical_section(); cache_hal_invalidate_addr((uint32_t)vaddr, SPI_FLASH_MMU_PAGE_SIZE); esp_cache_sync_ops_exit_critical_section(); ret = true; } #endif // CONFIG_IDF_TARGET_ESP32 } } return ret; } #endif //!MMAP_ROM_IMPL_ENABLED #if !MMAP_ROM_IMPL_ENABLED || CONFIG_SPIRAM_FETCH_INSTRUCTIONS || CONFIG_SPIRAM_RODATA /* ROM and patch information * Latest: Add the mapping from psram physical address to flash when CONFIG_SPIRAM_FETCH_INSTRUCTIONS or CONFIG_SPIRAM_RODATA enabled * V1 (Latest): added to ROM */ // The ROM implementation returns physical address of the PSRAM when the .text or .rodata is in the PSRAM. // Patched when XIP from PSRAM (partially) enabled. size_t spi_flash_cache2phys(const void *cached) { if (cached == NULL) { return SPI_FLASH_CACHE2PHYS_FAIL; } esp_err_t ret = ESP_FAIL; uint32_t paddr = 0; mmu_target_t target = 0; #if CONFIG_SPIRAM_FLASH_LOAD_TO_PSRAM //TODO: IDF-9049 paddr = mmu_xip_psram_flash_vaddr_to_paddr(cached); //SPI_FLASH_CACHE2PHYS_FAIL is UINT32_MAX if (paddr != SPI_FLASH_CACHE2PHYS_FAIL) { return paddr; } #endif ret = esp_mmu_vaddr_to_paddr((void *)cached, &paddr, &target); if (ret != ESP_OK) { return SPI_FLASH_CACHE2PHYS_FAIL; } int offset = 0; #if !CONFIG_SPIRAM_FLASH_LOAD_TO_PSRAM #if CONFIG_SPIRAM_RODATA if ((uint32_t)cached >= (uint32_t)&_rodata_reserved_start && (uint32_t)cached <= (uint32_t)&_rodata_reserved_end) { offset = rodata_flash2spiram_offset(); } #endif #if CONFIG_SPIRAM_FETCH_INSTRUCTIONS if ((uint32_t)cached >= (uint32_t)&_instruction_reserved_start && (uint32_t)cached <= (uint32_t)&_instruction_reserved_end) { offset = instruction_flash2spiram_offset(); } #endif #endif //#if !CONFIG_SPIRAM_FLASH_LOAD_TO_PSRAM return paddr + offset * CONFIG_MMU_PAGE_SIZE; } /* ROM and patch information * Latest: Add the mapping from flash physical address to psram when CONFIG_SPIRAM_FETCH_INSTRUCTIONS or CONFIG_SPIRAM_RODATA enabled * V1 (Latest): added to ROM */ // The ROM implementation takes physical address of the PSRAM when the .text or .rodata is in the PSRAM. // Patched when XIP from PSRAM (partially) enabled. const void * spi_flash_phys2cache(size_t phys_offs, spi_flash_mmap_memory_t memory) { esp_err_t ret = ESP_FAIL; void *ptr = NULL; mmu_target_t target = MMU_TARGET_FLASH0; __attribute__((unused)) uint32_t phys_page = phys_offs / CONFIG_MMU_PAGE_SIZE; #if !CONFIG_SPIRAM_FLASH_LOAD_TO_PSRAM #if CONFIG_SPIRAM_FETCH_INSTRUCTIONS if (phys_page >= instruction_flash_start_page_get() && phys_page <= instruction_flash_end_page_get()) { target = MMU_TARGET_PSRAM0; phys_offs -= instruction_flash2spiram_offset() * CONFIG_MMU_PAGE_SIZE; } #endif #if CONFIG_SPIRAM_RODATA if (phys_page >= rodata_flash_start_page_get() && phys_page <= rodata_flash_end_page_get()) { target = MMU_TARGET_PSRAM0; phys_offs -= rodata_flash2spiram_offset() * CONFIG_MMU_PAGE_SIZE; } #endif #endif //#if !CONFIG_SPIRAM_FLASH_LOAD_TO_PSRAM mmu_vaddr_t type = (memory == SPI_FLASH_MMAP_DATA) ? MMU_VADDR_DATA : MMU_VADDR_INSTRUCTION; ret = esp_mmu_paddr_to_vaddr(phys_offs, target, type, &ptr); if (ret == ESP_ERR_NOT_FOUND) { return NULL; } assert(ret == ESP_OK); return (const void *)ptr; } #endif //!MMAP_ROM_IMPL_ENABLED || CONFIG_SPIRAM_FETCH_INSTRUCTIONS || CONFIG_SPIRAM_RODATA ESP_STATIC_ASSERT(SPI_FLASH_MMAP_FLAG_DATA + SPI_FLASH_MMAP_FLAG_INST < SPI_FLASH_MMAP_FLAG_BLOCKS_WRITE, "spi_flash_mmap_memory_t not compatible with spi_flash_mmap_flag_t");