fix(mmap): fixed some API read wrong data via mmap when flash being erased/written while XIP on PSRAM

Before:

The cache won't be disabled when XIP on psram. But during flash
erasing/programming, read data will be courrupt.

When XIP in psram is enabled, the image is not mapped to the cache so
usually there will be no flash access. The only way to read from flash
is via the driver or use mmap. The driver has protection during erasing,
while th mmap region not.

Now:

Mmap APIs provide a flag to make mmap->unmap region mutually exclusive
to flash erase/programming when XIP from psram. SPI Flash write APIs
will benefit from this. When the flag is used, no concurrent access to
mapped region will happen while writing; otherwise the cache will be
disable to avoid data corruption.

Most ESP-IDF APIs calls mmap with this flag. As for users calling
mmap-like APIs directly, they can choose whether to enable this by a
flag.

Closes https://github.com/espressif/esp-idf/issues/14897
This commit is contained in:
Xiao Xufeng
2026-06-16 01:00:43 +08:00
committed by Michael (XIAO Xufeng)
parent ec956162ab
commit 789ce684c9
73 changed files with 1678 additions and 487 deletions
+58 -60
View File
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -137,7 +137,7 @@ static ESP_LOG_ATTR const char io_mode_str[][IO_STR_LEN] = {
_Static_assert(sizeof(io_mode_str)/IO_STR_LEN == SPI_FLASH_READ_MODE_MAX, "the io_mode_str should be consistent with the esp_flash_io_mode_t defined in spi_flash_types.h");
typedef struct {
esp_err_t (*start)(esp_flash_t *chip);
esp_err_t (*start_prog)(esp_flash_t *chip);
esp_err_t (*end)(esp_flash_t *chip, esp_err_t err);
esp_err_t (*chip_check)(esp_flash_t **inout_chip);
esp_err_t (*flash_end_flush_cache)(esp_flash_t* chip, esp_err_t err, bool bus_acquired, uint32_t address, uint32_t length);
@@ -155,14 +155,14 @@ extern rom_spiflash_api_func_t *esp_flash_api_funcs;
#if !CONFIG_SPI_FLASH_ROM_IMPL
// API funcs case 1: Not using ROM - define our own pointer and all functions
static esp_err_t spiflash_start_default(esp_flash_t *chip);
static esp_err_t spiflash_start_prog(esp_flash_t *chip);
static esp_err_t spiflash_end_default(esp_flash_t *chip, esp_err_t err);
static esp_err_t check_chip_pointer_default(esp_flash_t **inout_chip);
static esp_err_t flash_end_flush_cache(esp_flash_t* chip, esp_err_t err, bool bus_acquired, uint32_t address, uint32_t length);
// These functions can be placed in the ROM. For now we use the code in IDF.
DRAM_ATTR static rom_spiflash_api_func_t esp_flash_api_funcs_patched = {
.start = spiflash_start_default,
.start_prog = spiflash_start_prog,
.end = spiflash_end_default,
.chip_check = check_chip_pointer_default,
.flash_end_flush_cache = flash_end_flush_cache,
@@ -172,44 +172,27 @@ DRAM_ATTR static rom_spiflash_api_func_t *esp_flash_api_funcs_patched_ptr = &esp
#else // CONFIG_SPI_FLASH_ROM_IMPL
// Using ROM implementation
# if CONFIG_SPI_FLASH_FREQ_LIMIT_C5_240MHZ
// API funcs case 2: Using ROM APIs but patch start function to support flags parameter
static esp_err_t spiflash_start_default(esp_flash_t *chip);
// All ROM impl cases patch the start function to spiflash_start_prog, so that
// the flags parameter (esp_flash_os_functions_t.start) is always passed correctly.
// The ROM's original start does not pass flags, which would leave the parameter undefined.
static esp_err_t spiflash_start_prog(esp_flash_t *chip);
DRAM_ATTR static rom_spiflash_api_func_t esp_flash_api_funcs_patched;
// Copy ROM structure to RAM and patch start function to support flags
void esp_flash_rom_api_funcs_init(void)
{
rom_spiflash_api_func_t *rom_ptr = esp_flash_api_funcs;
memcpy(&esp_flash_api_funcs_patched, rom_ptr, sizeof(rom_spiflash_api_func_t));
esp_flash_api_funcs_patched.start = spiflash_start_default;
esp_flash_api_funcs = &esp_flash_api_funcs_patched;
}
# elif ESP_ROM_HAS_ENCRYPTED_WRITES_USING_LEGACY_DRV
// API funcs case 3: Using ROM APIs but patch flash_end_flush_cache function
// When ESP_ROM_HAS_ENCRYPTED_WRITES_USING_LEGACY_DRV, the api_funcs provided by ROM does not have flash_end_flush_cache member.
# if ESP_ROM_HAS_ENCRYPTED_WRITES_USING_LEGACY_DRV
static esp_err_t flash_end_flush_cache(esp_flash_t* chip, esp_err_t err, bool bus_acquired, uint32_t address, uint32_t length);
DRAM_ATTR static rom_spiflash_api_func_t esp_flash_api_funcs_patched;
# endif
// Copy ROM structure to RAM and patch flash_end_flush_cache function
void esp_flash_rom_api_funcs_init(void)
{
rom_spiflash_api_func_t *rom_ptr = esp_flash_api_funcs;
memcpy(&esp_flash_api_funcs_patched, rom_ptr, sizeof(rom_spiflash_api_func_t));
esp_flash_api_funcs_patched.start_prog = spiflash_start_prog;
# if ESP_ROM_HAS_ENCRYPTED_WRITES_USING_LEGACY_DRV
esp_flash_api_funcs_patched.flash_end_flush_cache = flash_end_flush_cache;
# endif
esp_flash_api_funcs = &esp_flash_api_funcs_patched;
}
# else
// API funcs case 4: Using All ROM APIs directly
void esp_flash_rom_api_funcs_init(void)
{
// Do nothing
}
# endif // CONFIG_SPI_FLASH_FREQ_LIMIT_C5_240MHZ
#endif // !CONFIG_SPI_FLASH_ROM_IMPL
/* Static function to notify OS of a new SPI flash operation.
@@ -217,7 +200,6 @@ void esp_flash_rom_api_funcs_init(void)
If returns an error result, caller must abort. If returns ESP_OK, caller must
call rom_spiflash_api_funcs->end() before returning.
*/
#if !CONFIG_SPI_FLASH_ROM_IMPL || CONFIG_SPI_FLASH_FREQ_LIMIT_C5_240MHZ
//Avoid constprop issue that place this function into flash.
__attribute__((optimize("O0"))) //IDF-14941
static esp_err_t spiflash_start_core(esp_flash_t *chip, uint32_t flags)
@@ -232,11 +214,17 @@ static esp_err_t spiflash_start_core(esp_flash_t *chip, uint32_t flags)
return ESP_OK;
}
static esp_err_t spiflash_start_default(esp_flash_t *chip)
// Prog start: used by write/erase and misc operations (via rom_spiflash_api_funcs->start_prog).
// Sets ESP_FLASH_START_FLAG_NO_READ to avoid concurrent read operations.
static esp_err_t spiflash_start_prog(esp_flash_t *chip)
{
return spiflash_start_core(chip, ESP_FLASH_START_FLAG_NO_READ);
}
static esp_err_t spiflash_start_read(esp_flash_t *chip)
{
return spiflash_start_core(chip, 0);
}
#endif //!CONFIG_SPI_FLASH_ROM_IMPL || CONFIG_SPI_FLASH_FREQ_LIMIT_C5_240MHZ
#if !CONFIG_SPI_FLASH_ROM_IMPL
/* Static function to notify OS that SPI flash operation is complete.
@@ -277,7 +265,7 @@ static esp_err_t flash_end_flush_cache(esp_flash_t* chip, esp_err_t err, bool bu
{
if (!bus_acquired) {
// Try to acquire the bus again to flush the cache before exit.
esp_err_t acquire_err = rom_spiflash_api_funcs->start(chip);
esp_err_t acquire_err = rom_spiflash_api_funcs->start_prog(chip);
if (acquire_err != ESP_OK) {
return (err == ESP_OK)? acquire_err: err;
}
@@ -355,7 +343,7 @@ esp_err_t esp_flash_init(esp_flash_t *chip)
}
ESP_LOGI(TAG, "flash io: %s", io_mode_str[chip->read_mode]);
err = rom_spiflash_api_funcs->start(chip);
err = rom_spiflash_api_funcs->start_prog(chip);
if (err != ESP_OK) {
return err;
}
@@ -435,7 +423,7 @@ esp_err_t esp_flash_init_main(esp_flash_t *chip)
}
ESP_EARLY_LOGI(TAG, "flash io: %s", io_mode_str[chip->read_mode]);
err = rom_spiflash_api_funcs->start(chip);
err = rom_spiflash_api_funcs->start_prog(chip);
if (err != ESP_OK) {
return err;
}
@@ -455,7 +443,8 @@ esp_err_t esp_flash_init_main(esp_flash_t *chip)
static esp_err_t IRAM_ATTR read_id_core(esp_flash_t* chip, uint32_t* out_id, bool sanity_check)
{
bool installed = esp_flash_chip_driver_initialized(chip);
esp_err_t err = rom_spiflash_api_funcs->start(chip);
//Should be read-only. But keep `start_prog` to avoid the need of patching ROM functions.
esp_err_t err = rom_spiflash_api_funcs->start_prog(chip);
if (err != ESP_OK) {
return err;
}
@@ -507,7 +496,8 @@ esp_err_t esp_flash_read_id(esp_flash_t* chip, uint32_t* out_id)
static esp_err_t NOINLINE_ATTR read_unique_id(esp_flash_t* chip, uint64_t* out_uid)
{
esp_err_t err = rom_spiflash_api_funcs->start(chip);
//Should be read-only. But keep `start_prog` to avoid the need of patching ROM functions.
esp_err_t err = rom_spiflash_api_funcs->start_prog(chip);
if (err != ESP_OK) {
return err;
}
@@ -553,7 +543,7 @@ static esp_err_t detect_spi_flash_chip(esp_flash_t *chip)
// and also so esp_flash_registered_flash_drivers can live in flash
ESP_EARLY_LOGD(TAG, "trying chip: %s", chip->chip_drv->name);
err = rom_spiflash_api_funcs->start(chip);
err = spiflash_start_read(chip);
if (err != ESP_OK) {
return err;
}
@@ -587,7 +577,8 @@ esp_err_t esp_flash_get_physical_size(esp_flash_t *chip, uint32_t *flash_size)
return ESP_ERR_INVALID_ARG;
}
err = rom_spiflash_api_funcs->start(chip);
//Should be read-only. But keep `start_prog` to avoid the need of patching ROM functions.
err = rom_spiflash_api_funcs->start_prog(chip);
if (err != ESP_OK) {
return err;
}
@@ -681,7 +672,7 @@ esp_err_t esp_flash_erase_region(esp_flash_t *chip, uint32_t start, uint32_t len
if (chip->chip_drv->get_protected_regions != NULL &&
chip->chip_drv->num_protectable_regions > 0) {
err = rom_spiflash_api_funcs->start(chip);
err = rom_spiflash_api_funcs->start_prog(chip);
if (err != ESP_OK) {
return err;
}
@@ -716,7 +707,7 @@ esp_err_t esp_flash_erase_region(esp_flash_t *chip, uint32_t start, uint32_t len
}
}
err = rom_spiflash_api_funcs->start(chip);
err = rom_spiflash_api_funcs->start_prog(chip);
if (err != ESP_OK) {
break;
}
@@ -810,7 +801,8 @@ esp_err_t esp_flash_get_chip_write_protect(esp_flash_t *chip, bool *out_write_pr
return ESP_ERR_INVALID_ARG;
}
err = rom_spiflash_api_funcs->start(chip);
//Should be read-only. But keep `start_prog` to avoid the need of patching ROM functions.
err = rom_spiflash_api_funcs->start_prog(chip);
if (err != ESP_OK) {
return err;
}
@@ -826,7 +818,7 @@ esp_err_t esp_flash_set_chip_write_protect(esp_flash_t *chip, bool write_protect
VERIFY_CHIP_OP(set_chip_write_protect);
//TODO: skip writing if already locked or unlocked
err = rom_spiflash_api_funcs->start(chip);
err = rom_spiflash_api_funcs->start_prog(chip);
if (err != ESP_OK) {
return err;
}
@@ -885,7 +877,8 @@ esp_err_t esp_flash_get_protected_region(esp_flash_t *chip, const esp_flash_regi
}
uint64_t protection_mask = 0;
err = rom_spiflash_api_funcs->start(chip);
//Should be read-only. But keep `start_prog` to avoid the need of patching ROM functions.
err = rom_spiflash_api_funcs->start_prog(chip);
if (err != ESP_OK) {
return err;
}
@@ -910,7 +903,7 @@ esp_err_t esp_flash_set_protected_region(esp_flash_t *chip, const esp_flash_regi
}
uint64_t protection_mask = 0;
err = rom_spiflash_api_funcs->start(chip);
err = rom_spiflash_api_funcs->start_prog(chip);
if (err != ESP_OK) {
return err;
}
@@ -927,7 +920,12 @@ esp_err_t esp_flash_set_protected_region(esp_flash_t *chip, const esp_flash_regi
return rom_spiflash_api_funcs->end(chip, err);
}
#endif // !CONFIG_SPI_FLASH_ROM_IMPL
/* ROM and patch information
* Latest: patched to use spiflash_start_read instead of rom_spiflash_api_funcs->start_prog
* V1: Added to ROM (Not used)
*/
esp_err_t esp_flash_read(esp_flash_t *chip, void *buffer, uint32_t address, uint32_t length)
{
esp_err_t err = rom_spiflash_api_funcs->chip_check(&chip);
@@ -966,7 +964,7 @@ esp_err_t esp_flash_read(esp_flash_t *chip, void *buffer, uint32_t address, uint
err = ESP_OK;
do {
err = rom_spiflash_api_funcs->start(chip);
err = spiflash_start_read(chip);
if (err != ESP_OK) {
break;
}
@@ -1003,7 +1001,6 @@ esp_err_t esp_flash_read(esp_flash_t *chip, void *buffer, uint32_t address, uint
COUNTER_STOP(read);
return err;
}
#endif //!CONFIG_SPI_FLASH_ROM_IMPL
#ifndef CONFIG_SPI_FLASH_ROM_IMPL
//This checking is available only when !CONFIG_SPI_FLASH_ROM_IMPL
@@ -1169,7 +1166,7 @@ esp_err_t esp_flash_write(esp_flash_t *chip, const void *buffer, uint32_t addres
}
}
err = rom_spiflash_api_funcs->start(chip);
err = rom_spiflash_api_funcs->start_prog(chip);
if (err != ESP_OK) {
goto restore_cache;
}
@@ -1248,7 +1245,9 @@ esp_err_t IRAM_ATTR esp_flash_write(esp_flash_t *chip, const void *buffer, uint3
}
#endif //!CONFIG_SPI_FLASH_ROM_IMPL
#ifndef CONFIG_SPI_FLASH_ROM_IMPL
/* ROM and patch information
* Latest: Call mmap that has block write flag
*/
esp_err_t esp_flash_read_encrypted(esp_flash_t *chip, uint32_t address, void *out_buffer, uint32_t length)
{
esp_err_t err = rom_spiflash_api_funcs->chip_check(&chip);
@@ -1269,7 +1268,7 @@ esp_err_t esp_flash_read_encrypted(esp_flash_t *chip, uint32_t address, void *ou
size_t map_src = address & ~(SPI_FLASH_MMU_PAGE_SIZE - 1);
size_t map_size = length + (address - map_src);
err = spi_flash_mmap(map_src, map_size, SPI_FLASH_MMAP_DATA, (const void **)&map, &map_handle);
err = spi_flash_mmap(map_src, map_size, SPI_FLASH_MMAP_DATA | SPI_FLASH_MMAP_FLAG_BLOCKS_WRITE, (const void **)&map, &map_handle);
if (err != ESP_OK) {
return err;
}
@@ -1281,6 +1280,7 @@ esp_err_t esp_flash_read_encrypted(esp_flash_t *chip, uint32_t address, void *ou
return err;
}
#if !CONFIG_SPI_FLASH_ROM_IMPL
// test only, non-public
esp_err_t esp_flash_get_io_mode(esp_flash_t* chip, bool* qe)
{
@@ -1288,7 +1288,7 @@ esp_err_t esp_flash_get_io_mode(esp_flash_t* chip, bool* qe)
VERIFY_CHIP_OP(get_io_mode);
esp_flash_io_mode_t io_mode;
err = rom_spiflash_api_funcs->start(chip);
err = spiflash_start_read(chip);
if (err != ESP_OK) {
return err;
}
@@ -1306,7 +1306,7 @@ esp_err_t esp_flash_set_io_mode(esp_flash_t* chip, bool qe)
VERIFY_CHIP_OP(set_io_mode);
chip->read_mode = (qe? SPI_FLASH_QOUT: SPI_FLASH_SLOWRD);
err = rom_spiflash_api_funcs->start(chip);
err = rom_spiflash_api_funcs->start_prog(chip);
if (err != ESP_OK) {
return err;
}
@@ -1317,17 +1317,15 @@ esp_err_t esp_flash_set_io_mode(esp_flash_t* chip, bool qe)
#if !CONFIG_SPI_FLASH_ROM_IMPL || ESP_ROM_HAS_ENCRYPTED_WRITES_USING_LEGACY_DRV || CONFIG_SPI_FLASH_FREQ_LIMIT_C5_240MHZ
// use `esp_flash_write_encrypted` ROM version on chips later than C3, S3
// For ESP32-C5, use IDF implementation when CPU frequency is 240MHz (calling start() with arg is required)
FORCE_INLINE_ATTR esp_err_t s_encryption_write_lock(esp_flash_t *chip)
{
#if CONFIG_SPI_FLASH_FREQ_LIMIT_C5_240MHZ
return spiflash_start_core(chip, ESP_FLASH_START_FLAG_NO_READ | ESP_FLASH_START_FLAG_LIMIT_CPU_FREQ);
#else
#if CONFIG_IDF_TARGET_ESP32S2
esp_crypto_dma_lock_acquire();
#endif //CONFIG_IDF_TARGET_ESP32S2
#if CONFIG_SPI_FLASH_FREQ_LIMIT_C5_240MHZ
// Use start_core with LIMIT_CPU_FREQ flag to trigger freq_limit_lock in OS layer
return spiflash_start_core(chip, ESP_FLASH_START_FLAG_LIMIT_CPU_FREQ);
#else
return rom_spiflash_api_funcs->start(chip);
#endif
return rom_spiflash_api_funcs->start_prog(chip);
#endif
}