mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-02 03:00:34 +03:00
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:
committed by
Michael (XIAO Xufeng)
parent
ec956162ab
commit
789ce684c9
@@ -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
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user