mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-02 11:10:54 +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
39a219331c
commit
3e8389cc31
@@ -289,7 +289,7 @@ menu "SPI Flash driver"
|
||||
config SPI_FLASH_SHARE_SPI1_BUS
|
||||
bool "Support other devices attached to SPI1 bus"
|
||||
default n
|
||||
depends on IDF_TARGET_ESP32
|
||||
depends on IDF_TARGET_ESP32 && !CONFIG_APP_BUILD_TYPE_RAM
|
||||
select SPI_MASTER_ISR_IN_IRAM
|
||||
help
|
||||
Each SPI bus needs a lock for arbitration among devices. This allows multiple
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -176,7 +176,7 @@ static IRAM_ATTR NOINLINE_ATTR void cs_initialize(esp_flash_t *chip, const esp_f
|
||||
|
||||
//To avoid the panic caused by flash data line conflicts during cs line
|
||||
//initialization, disable the cache temporarily
|
||||
chip->os_func->start(chip->os_func_data, 0);
|
||||
chip->os_func->start(chip->os_func_data, ESP_FLASH_START_FLAG_NO_READ);
|
||||
gpio_hal_input_enable(&gpio_hal, cs_io_num);
|
||||
if (cs_use_iomux) {
|
||||
gpio_hal_func_sel(&gpio_hal, cs_io_num, spics_func);
|
||||
@@ -632,10 +632,8 @@ esp_err_t esp_flash_app_init(void)
|
||||
#if CONFIG_SPI_FLASH_ENABLE_COUNTERS
|
||||
esp_flash_reset_counters();
|
||||
#endif
|
||||
#if CONFIG_SPI_FLASH_SHARE_SPI1_BUS
|
||||
err = esp_flash_init_main_bus_lock();
|
||||
err = esp_flash_app_init_os_functions();
|
||||
if (err != ESP_OK) return err;
|
||||
#endif
|
||||
err = esp_flash_app_enable_os_functions(&default_chip);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/semphr.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "esp_bit_defs.h"
|
||||
#include "esp_attr.h"
|
||||
@@ -39,6 +40,7 @@
|
||||
|
||||
#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;
|
||||
@@ -50,18 +52,217 @@ extern char _rodata_reserved_start;
|
||||
extern char _rodata_reserved_end;
|
||||
#endif
|
||||
|
||||
#if !ESP_ROM_HAS_SPI_FLASH_MMAP || !CONFIG_SPI_FLASH_ROM_IMPL
|
||||
|
||||
/* 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;
|
||||
|
||||
|
||||
esp_err_t spi_flash_mmap(size_t src_addr, size_t size, spi_flash_mmap_memory_t memory,
|
||||
/* 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
|
||||
@@ -89,12 +290,15 @@ esp_err_t spi_flash_mmap(size_t src_addr, size_t size, spi_flash_mmap_memory_t m
|
||||
}
|
||||
|
||||
block->vaddr_list = vaddr_list;
|
||||
block->permanent = !(flags & SPI_FLASH_MMAP_FLAG_BLOCKS_WRITE);
|
||||
|
||||
if (memory == SPI_FLASH_MMAP_INST) {
|
||||
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;
|
||||
@@ -109,12 +313,18 @@ esp_err_t spi_flash_mmap(size_t src_addr, size_t size, spi_flash_mmap_memory_t m
|
||||
*/
|
||||
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:
|
||||
@@ -172,7 +382,12 @@ static void s_pages_to_bytes(int (*blocks)[2], int block_nums)
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t spi_flash_mmap_pages(const int *pages, size_t page_count, spi_flash_mmap_memory_t memory,
|
||||
/* 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
|
||||
@@ -189,6 +404,7 @@ esp_err_t spi_flash_mmap_pages(const int *pages, size_t page_count, spi_flash_mm
|
||||
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];
|
||||
@@ -207,11 +423,14 @@ esp_err_t spi_flash_mmap_pages(const int *pages, size_t page_count, spi_flash_mm
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (memory == SPI_FLASH_MMAP_INST) {
|
||||
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);
|
||||
@@ -229,9 +448,12 @@ esp_err_t spi_flash_mmap_pages(const int *pages, size_t page_count, spi_flash_mm
|
||||
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.
|
||||
@@ -239,6 +461,11 @@ esp_err_t spi_flash_mmap_pages(const int *pages, size_t page_count, spi_flash_mm
|
||||
*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:
|
||||
@@ -248,36 +475,125 @@ err:
|
||||
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;
|
||||
@@ -345,9 +661,9 @@ IRAM_ATTR bool spi_flash_check_and_flush_cache(size_t start_addr, size_t length)
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
#endif // !ESP_ROM_HAS_SPI_FLASH_MMAP || !CONFIG_SPI_FLASH_ROM_IMPL
|
||||
#endif //!MMAP_ROM_IMPL_ENABLED
|
||||
|
||||
#if !ESP_ROM_HAS_SPI_FLASH_MMAP || !CONFIG_SPI_FLASH_ROM_IMPL || CONFIG_SPIRAM_FETCH_INSTRUCTIONS || CONFIG_SPIRAM_RODATA
|
||||
#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
|
||||
@@ -432,4 +748,6 @@ const void * spi_flash_phys2cache(size_t phys_offs, spi_flash_mmap_memory_t memo
|
||||
assert(ret == ESP_OK);
|
||||
return (const void *)ptr;
|
||||
}
|
||||
#endif //!ESP_ROM_HAS_SPI_FLASH_MMAP || !CONFIG_SPI_FLASH_ROM_IMPL || CONFIG_SPIRAM_FETCH_INSTRUCTIONS || CONFIG_SPIRAM_RODATA
|
||||
#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");
|
||||
|
||||
@@ -31,6 +31,10 @@ typedef struct esp_flash_os_functions_t {
|
||||
/** Limit CPU frequency during flash operations (ESP32-C5 only, 240MHz).
|
||||
*/
|
||||
#define ESP_FLASH_START_FLAG_LIMIT_CPU_FREQ BIT(0)
|
||||
/** Indicates that this operation forbids flash from being read (e.g., write/erase).
|
||||
* The OS layer implementation needs to take appropriate measures to avoid concurrent read operations.
|
||||
*/
|
||||
#define ESP_FLASH_START_FLAG_NO_READ BIT(1)
|
||||
/**
|
||||
* Called before commencing any flash operation. Does not need to be
|
||||
* recursive (ie is called at most once for each call to 'end').
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -27,6 +27,12 @@ extern "C" {
|
||||
*/
|
||||
esp_err_t esp_flash_init_default_chip(void);
|
||||
|
||||
/**
|
||||
* @brief Initialize main flash
|
||||
* @param chip Pointer to main SPI flash(SPI1 CS0) chip to use..
|
||||
*/
|
||||
esp_err_t esp_flash_init_main(esp_flash_t *chip);
|
||||
|
||||
/**
|
||||
* Enable OS-level SPI flash protections in IDF
|
||||
*
|
||||
@@ -76,7 +82,7 @@ esp_err_t esp_flash_deinit_os_functions(esp_flash_t* chip, spi_bus_lock_dev_hand
|
||||
*
|
||||
* @return esp_err_t always ESP_OK.
|
||||
*/
|
||||
esp_err_t esp_flash_init_main_bus_lock(void);
|
||||
esp_err_t esp_flash_app_init_os_functions(void);
|
||||
|
||||
/**
|
||||
* Initialize OS-level functions for the main flash chip.
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "esp_rom_caps.h"
|
||||
#include "esp_err.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//executables are placed on flash
|
||||
#define MMAP_EXECUTABLES_FROM_FLASH (!((CONFIG_SPIRAM_FETCH_INSTRUCTIONS && CONFIG_SPIRAM_RODATA) || CONFIG_APP_BUILD_TYPE_RAM))
|
||||
#define MMAP_ROM_IMPL_ENABLED (CONFIG_SPI_FLASH_ROM_IMPL && ESP_ROM_HAS_SPI_FLASH_MMAP)
|
||||
|
||||
#if !CONFIG_IDF_TARGET_ESP32
|
||||
//No mmap lock for ESP32, since it's useless to block while can't read from flash with SPI0 (cache) and SPI1 (driver) at the same time.
|
||||
|
||||
/**
|
||||
* Initialize the internal lock earlier to bypass the memory leak check.
|
||||
*/
|
||||
esp_err_t flash_mmap_lock_init(void);
|
||||
|
||||
/**
|
||||
* Freeze the mmap table
|
||||
*/
|
||||
void flash_mmap_lock_freeze(void);
|
||||
|
||||
/**
|
||||
* Unfreeze the mmap table
|
||||
*/
|
||||
void flash_mmap_lock_unfreeze(void);
|
||||
|
||||
/**
|
||||
* Return whether there is remaining mmap regions, must be called between flash_mmap_lock_freeze() and flash_mmap_lock_unfreeze().
|
||||
*/
|
||||
bool flash_mmap_remain(void);
|
||||
|
||||
#endif //!CONFIG_IDF_TARGET_ESP32
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -78,12 +78,6 @@ uint8_t esp_mspi_get_io(esp_mspi_io_t io);
|
||||
*/
|
||||
void spi_flash_set_rom_required_regs(void);
|
||||
|
||||
/**
|
||||
* @brief Initialize main flash
|
||||
* @param chip Pointer to main SPI flash(SPI1 CS0) chip to use..
|
||||
*/
|
||||
esp_err_t esp_flash_init_main(esp_flash_t *chip);
|
||||
|
||||
/**
|
||||
* @brief Should be only used by SPI1 Flash driver to know the necessary timing registers
|
||||
* @param out_timing_config Pointer to timing_tuning parameters.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -15,7 +15,10 @@
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <esp_bit_defs.h>
|
||||
#include <esp_assert.h>
|
||||
#include "esp_err.h"
|
||||
#include "esp_attr.h"
|
||||
#include "sdkconfig.h"
|
||||
#include "esp_spi_flash_counters.h"
|
||||
|
||||
@@ -31,12 +34,40 @@ extern "C" {
|
||||
#define SPI_FLASH_MMU_PAGE_SIZE CONFIG_MMU_PAGE_SIZE /**< Flash cache MMU mapping page size */
|
||||
|
||||
/**
|
||||
* @brief Enumeration which specifies memory space requested in an mmap call
|
||||
* @brief Flags for spi_flash_mmap and spi_flash_mmap_pages calls
|
||||
*/
|
||||
typedef enum {
|
||||
SPI_FLASH_MMAP_DATA, /**< map to data memory, allows byte-aligned access*/
|
||||
SPI_FLASH_MMAP_INST, /**< map to instruction memory, allows only 4-byte-aligned access*/
|
||||
} spi_flash_mmap_memory_t;
|
||||
SPI_FLASH_MMAP_FLAG_DATA = 0, /**< map to data memory, allows byte-aligned access*/
|
||||
SPI_FLASH_MMAP_FLAG_INST = 1, /**< map to instruction memory, allows only 4-byte-aligned access*/
|
||||
|
||||
SPI_FLASH_MMAP_FLAG_BLOCKS_WRITE = BIT(1),
|
||||
/**< Blocks flash erasing/programming until spi_flash_munmap when necessary. Flash can't be read when there is
|
||||
* erasing/programming in progress, even if the regions to be read from/written to don't overlap.
|
||||
*
|
||||
* This flag helps avoid cache disabling and its influence to system when when XIP from PSRAM
|
||||
* (`CONFIG_SPIRAM_XIP_FROM_PSRAM`) is enabled.
|
||||
*
|
||||
* Call mmap with this flag unless you want to do erasing/programming between this mmap and its munmap, or want to
|
||||
* create a mapping that will last for very long.
|
||||
*
|
||||
* Ignored on ESP32.
|
||||
*/
|
||||
} spi_flash_mmap_flag_t;
|
||||
|
||||
/** @def SPI_FLASH_MMAP_DATA
|
||||
*
|
||||
* Data memory.
|
||||
*/
|
||||
#define SPI_FLASH_MMAP_DATA SPI_FLASH_MMAP_FLAG_DATA
|
||||
|
||||
/** @def SPI_FLASH_MMAP_INST
|
||||
*
|
||||
* Instruction memory.
|
||||
*/
|
||||
#define SPI_FLASH_MMAP_INST SPI_FLASH_MMAP_FLAG_INST
|
||||
|
||||
/** Enumeration which specifies memory space requested. SPI_FLASH_MMAP_DATA or SPI_FLASH_MMAP_INST */
|
||||
typedef spi_flash_mmap_flag_t spi_flash_mmap_memory_t;
|
||||
|
||||
/**
|
||||
* @brief Opaque handle for memory region obtained from spi_flash_mmap.
|
||||
@@ -54,18 +85,21 @@ typedef uint32_t spi_flash_mmap_handle_t;
|
||||
* may become fragmented. To troubleshoot issues with page allocation, use
|
||||
* spi_flash_mmap_dump() function.
|
||||
*
|
||||
* Call with SPI_FLASH_MMAP_FLAG_BLOCKS_WRITE flag when you only want to read some data from the flash without writing
|
||||
* to it before the spi_flash_munmap. This can reduce the influence to system due to cache disabling on non-ESP32 chips.
|
||||
*
|
||||
* @param src_addr Physical address in flash where requested region starts.
|
||||
* This address *must* be aligned to 64kB boundary
|
||||
* (SPI_FLASH_MMU_PAGE_SIZE)
|
||||
* @param size Size of region to be mapped. This size will be rounded
|
||||
* up to a 64kB boundary
|
||||
* @param memory Address space where the region should be mapped (data or instruction)
|
||||
* @param flags Flags of the mapping, including address space where the region should be mapped (data or instruction)
|
||||
* @param[out] out_ptr Output, pointer to the mapped memory region
|
||||
* @param[out] out_handle Output, handle which should be used for spi_flash_munmap call
|
||||
*
|
||||
* @return ESP_OK on success, ESP_ERR_NO_MEM if pages can not be allocated
|
||||
*/
|
||||
esp_err_t spi_flash_mmap(size_t src_addr, size_t size, spi_flash_mmap_memory_t memory,
|
||||
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);
|
||||
|
||||
/**
|
||||
@@ -76,12 +110,15 @@ esp_err_t spi_flash_mmap(size_t src_addr, size_t size, spi_flash_mmap_memory_t m
|
||||
* In this respect, it works in a similar way as spi_flash_mmap() but it allows mapping
|
||||
* a (maybe non-contiguous) set of pages into a contiguous region of memory.
|
||||
*
|
||||
* Call with SPI_FLASH_MMAP_FLAG_BLOCKS_WRITE flag when you only want to read some data from the flash without writing
|
||||
* to it before the spi_flash_munmap. This can reduce the influence to system due to cache disabling on non-ESP32 chips.
|
||||
*
|
||||
* @param pages An array of numbers indicating the 64kB pages in flash to be mapped
|
||||
* contiguously into memory. These indicate the indexes of the 64kB pages,
|
||||
* not the byte-size addresses as used in other functions.
|
||||
* Array must be located in internal memory.
|
||||
* @param page_count Number of entries in the pages array
|
||||
* @param memory Address space where the region should be mapped (instruction or data)
|
||||
* @param flags Flags of the mapping, including address space where the region should be mapped (data or instruction)
|
||||
* @param[out] out_ptr Output, pointer to the mapped memory region
|
||||
* @param[out] out_handle Output, handle which should be used for spi_flash_munmap call
|
||||
*
|
||||
@@ -91,7 +128,7 @@ esp_err_t spi_flash_mmap(size_t src_addr, size_t size, spi_flash_mmap_memory_t m
|
||||
* - ESP_ERR_INVALID_ARG if pagecount is zero or pages array is not in
|
||||
* internal memory
|
||||
*/
|
||||
esp_err_t spi_flash_mmap_pages(const int *pages, size_t page_count, spi_flash_mmap_memory_t memory,
|
||||
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);
|
||||
|
||||
|
||||
@@ -125,7 +162,7 @@ void spi_flash_mmap_dump(void);
|
||||
*
|
||||
* @param memory memory type of MMU table free page
|
||||
*
|
||||
* @return number of free pages which can be mmaped
|
||||
* @return number of free pages which can be mapped
|
||||
*/
|
||||
uint32_t spi_flash_mmap_get_free_pages(spi_flash_mmap_memory_t memory);
|
||||
|
||||
@@ -168,3 +205,5 @@ const void *spi_flash_phys2cache(size_t phys_offs, spi_flash_mmap_memory_t memor
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
FLAG_ATTR(spi_flash_mmap_flag_t)
|
||||
|
||||
@@ -49,9 +49,10 @@ entries:
|
||||
if SPI_FLASH_ROM_IMPL = n || ESP_ROM_HAS_ENCRYPTED_WRITES_USING_LEGACY_DRV = y || SPI_FLASH_FREQ_LIMIT_C5_240MHZ = y:
|
||||
esp_flash_api: esp_flash_write_encrypted (noflash)
|
||||
|
||||
if SPI_FLASH_ROM_IMPL = n || SPI_FLASH_FREQ_LIMIT_C5_240MHZ = y:
|
||||
esp_flash_api: spiflash_start_default (noflash)
|
||||
esp_flash_api: spiflash_start_core (noflash)
|
||||
esp_flash_api: spiflash_start_prog (noflash)
|
||||
esp_flash_api: spiflash_start_core (noflash)
|
||||
esp_flash_api: spiflash_start_read (noflash)
|
||||
esp_flash_api: esp_flash_read (noflash)
|
||||
|
||||
if SPI_FLASH_ROM_IMPL = n || ESP_ROM_HAS_ENCRYPTED_WRITES_USING_LEGACY_DRV = y:
|
||||
esp_flash_api: flash_end_flush_cache (noflash)
|
||||
@@ -65,7 +66,6 @@ entries:
|
||||
esp_flash_api: esp_flash_set_chip_write_protect (noflash)
|
||||
esp_flash_api: esp_flash_get_protected_region (noflash)
|
||||
esp_flash_api: esp_flash_set_protected_region (noflash)
|
||||
esp_flash_api: esp_flash_read (noflash)
|
||||
esp_flash_api: esp_flash_write (noflash)
|
||||
esp_flash_api: esp_flash_read_encrypted (noflash)
|
||||
esp_flash_api: esp_flash_get_io_mode (noflash)
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
@@ -23,18 +23,13 @@
|
||||
|
||||
#include "esp_private/spi_flash_os.h"
|
||||
#include "esp_private/cache_utils.h"
|
||||
#include "esp_private/flash_mmap.h"
|
||||
#include "esp_private/spi_share_hw_ctrl.h"
|
||||
|
||||
// For C5 encrypted write workaround
|
||||
// Functions are only available when CONFIG_SPI_FLASH_FREQ_LIMIT_C5_240MHZ is true
|
||||
#include "esp_private/spi_flash_freq_limit_cbs.h"
|
||||
|
||||
#define SPI_FLASH_CACHE_NO_DISABLE (CONFIG_SPI_FLASH_AUTO_SUSPEND || (CONFIG_SPIRAM_FETCH_INSTRUCTIONS && CONFIG_SPIRAM_RODATA) || CONFIG_APP_BUILD_TYPE_RAM)
|
||||
static const char TAG[] = "spi_flash";
|
||||
|
||||
#if SPI_FLASH_CACHE_NO_DISABLE
|
||||
static _lock_t s_spi1_flash_mutex;
|
||||
#endif // #if SPI_FLASH_CACHE_NO_DISABLE
|
||||
|
||||
/*
|
||||
* OS functions providing delay service and arbitration among chips, and with the cache.
|
||||
@@ -43,6 +38,14 @@ static _lock_t s_spi1_flash_mutex;
|
||||
* into the IRAM,and their data should be put into the DRAM.
|
||||
*/
|
||||
|
||||
typedef enum {
|
||||
OP_TYPE_MUTEX = 0,
|
||||
OP_TYPE_MMAP_LOCK = 1,
|
||||
OP_TYPE_SCHEDULER_DIS = 2,
|
||||
OP_TYPE_CACHE_DIS = 3,
|
||||
OP_TYPE_BUS_LOCK = 4,
|
||||
} spi1_op_type_t;
|
||||
|
||||
/*
|
||||
* Time yield algorithm:
|
||||
* Every time spi_flash_os_check_yield() is called:
|
||||
@@ -56,18 +59,22 @@ static _lock_t s_spi1_flash_mutex;
|
||||
*/
|
||||
typedef struct {
|
||||
spi_bus_lock_dev_handle_t dev_lock;
|
||||
bool no_protect; //to decide whether to check protected region (for the main chip) or not.
|
||||
uint32_t no_protect : 1; //to decide whether to check protected region (for the main chip) or not.
|
||||
uint32_t current_op_type : 3; //Whether the mmap lock is already taken, only for SPI1.
|
||||
uint32_t reserved : 28;
|
||||
uint32_t acquired_since_us; // Time since last explicit yield()
|
||||
uint32_t released_since_us; // Time since last end() (implicit yield)
|
||||
uint32_t start_flags; // Flags passed to start() function, used to determine if freq_limit was called
|
||||
} app_func_arg_t;
|
||||
|
||||
static const char TAG[] = "spi_flash";
|
||||
|
||||
static inline void on_spi_released(app_func_arg_t* ctx);
|
||||
static inline void on_spi_acquired(app_func_arg_t* ctx);
|
||||
static inline void on_spi_yielded(app_func_arg_t* ctx);
|
||||
static inline bool on_spi_check_yield(app_func_arg_t* ctx);
|
||||
|
||||
#if !SPI_FLASH_CACHE_NO_DISABLE
|
||||
#if !CONFIG_SPI_FLASH_AUTO_SUSPEND
|
||||
IRAM_ATTR static void cache_enable(void* arg)
|
||||
{
|
||||
spi_flash_enable_interrupts_caches_and_other_cpu();
|
||||
@@ -77,7 +84,7 @@ IRAM_ATTR static void cache_disable(void* arg)
|
||||
{
|
||||
spi_flash_disable_interrupts_caches_and_other_cpu();
|
||||
}
|
||||
#endif //#if !SPI_FLASH_CACHE_NO_DISABLE
|
||||
#endif //#if !CONFIG_SPI_FLASH_AUTO_SUSPEND
|
||||
|
||||
static IRAM_ATTR esp_err_t acquire_spi_bus_lock(void *arg)
|
||||
{
|
||||
@@ -112,10 +119,11 @@ static esp_err_t spi23_end(void *arg)
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
static IRAM_ATTR esp_err_t spi1_start(void *arg, uint32_t flags)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
app_func_arg_t* ctx = (app_func_arg_t*)arg;
|
||||
esp_err_t ret = ESP_OK;
|
||||
ctx->start_flags = flags;
|
||||
|
||||
/**
|
||||
@@ -128,26 +136,133 @@ static IRAM_ATTR esp_err_t spi1_start(void *arg, uint32_t flags)
|
||||
*/
|
||||
#if CONFIG_SPI_FLASH_SHARE_SPI1_BUS
|
||||
//use the lock to disable the cache and interrupts before using the SPI bus
|
||||
ret = acquire_spi_bus_lock(arg);
|
||||
#elif SPI_FLASH_CACHE_NO_DISABLE
|
||||
_lock_acquire(&s_spi1_flash_mutex);
|
||||
ret = acquire_spi_bus_lock(ctx);
|
||||
ctx->current_op_type = OP_TYPE_BUS_LOCK;
|
||||
#else
|
||||
//directly disable the cache and interrupts when lock is not used
|
||||
// For RAM App, it's possible to keep cache enabled when there is no mapping exists, or mutex between erasing and
|
||||
// the mmap. However this will increase the complexity of mmap lock and decrease the performance.
|
||||
// Not doing this for now.
|
||||
cache_disable(NULL);
|
||||
ctx->current_op_type = OP_TYPE_CACHE_DIS;
|
||||
#endif //CONFIG_SPI_FLASH_SHARE_SPI1_BUS
|
||||
|
||||
on_spi_acquired(ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static IRAM_ATTR esp_err_t spi1_end(void *arg)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
app_func_arg_t* ctx = (app_func_arg_t*)arg;
|
||||
|
||||
/**
|
||||
* There are three ways for ESP Flash API lock, see `spi1_start`
|
||||
*/
|
||||
#if CONFIG_SPI_FLASH_SHARE_SPI1_BUS
|
||||
assert(ctx->current_op_type == OP_TYPE_BUS_LOCK);
|
||||
ret = release_spi_bus_lock(ctx);
|
||||
#else
|
||||
assert(ctx->current_op_type == OP_TYPE_CACHE_DIS);
|
||||
cache_enable(NULL);
|
||||
#endif
|
||||
|
||||
on_spi_released(ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#else //CONFIG_IDF_TARGET_ESP32
|
||||
|
||||
#if CONFIG_SPI_FLASH_DISABLE_SCHEDULER_IN_SUSPEND
|
||||
// Disable scheduler
|
||||
static void disable_scheduler(void)
|
||||
{
|
||||
if (xTaskGetSchedulerState() == taskSCHEDULER_RUNNING) {
|
||||
#ifdef CONFIG_FREERTOS_SMP
|
||||
# ifdef CONFIG_FREERTOS_SMP
|
||||
//Note: Scheduler suspension behavior changed in FreeRTOS SMP
|
||||
vTaskPreemptionDisable(NULL);
|
||||
#else
|
||||
# else
|
||||
// Disable scheduler on the current CPU
|
||||
vTaskSuspendAll();
|
||||
#endif // CONFIG_FREERTOS_SMP
|
||||
# endif // CONFIG_FREERTOS_SMP
|
||||
}
|
||||
#endif // CONFIG_SPI_FLASH_DISABLE_SCHEDULER_IN_SUSPEND
|
||||
}
|
||||
|
||||
static void restore_scheduler(void)
|
||||
{
|
||||
if (xTaskGetSchedulerState() == taskSCHEDULER_RUNNING) {
|
||||
# ifdef CONFIG_FREERTOS_SMP
|
||||
//Note: Scheduler suspension behavior changed in FreeRTOS SMP
|
||||
vTaskPreemptionEnable(NULL);
|
||||
# else
|
||||
xTaskResumeAll();
|
||||
# endif // CONFIG_FREERTOS_SMP
|
||||
}
|
||||
}
|
||||
#endif //CONFIG_SPI_FLASH_DISABLE_SCHEDULER_IN_SUSPEND
|
||||
|
||||
static _lock_t s_spi1_flash_mutex; //Lock preventing concurrent access to SPI1 Flash operations.
|
||||
|
||||
static IRAM_ATTR esp_err_t spi1_start(void *arg, uint32_t flags)
|
||||
{
|
||||
//context members can only be accessed after the lock is acquired
|
||||
app_func_arg_t* ctx = (app_func_arg_t*)arg;
|
||||
esp_err_t ret = ESP_OK;
|
||||
/**
|
||||
* There are different locks used in the SPI Flash API:
|
||||
* 1. Mutex protecting concurrent access to SPI1. (s_spi1_flash_mutex)
|
||||
* 2. Mmap lock (from flash_mmap.c), avoid access from SPI0 due to mmap
|
||||
* 3. Disable scheduler, this is used in auto-suspend mode when we want to disable the scheduler to avoid the suspend caused by tasks
|
||||
* 4. Cache lock (from cache_utils.h), this is used when we need to disable Cache to avoid cache access from CPU via SPI0
|
||||
* From 1 to 4, the lock overhead increases.
|
||||
*
|
||||
* Different modes take different locks:
|
||||
*
|
||||
* Mode Write/Erase Read
|
||||
* !EXEC_FROM_FLASH Mutex + Mmap Lock (+ Disable Cache) Mutex only
|
||||
* Auto suspend Mutex + Mmap Lock + Dis. Scheduler Mutex + Dis. Scheduler
|
||||
* EXEC_FROM_FLASH Mmap Lock + Disable Cache Disable Cache
|
||||
*/
|
||||
|
||||
if (flags & ESP_FLASH_START_FLAG_NO_READ) {
|
||||
/**
|
||||
* Take the mmap lock to minimize remain mmap pages. If there is no mmap page remain, we can keep the cache
|
||||
* enabled. For Auto suspend, though the cache is able to read, we still wait for the mmap to finish to avoid
|
||||
* unnecessary suspend.
|
||||
*
|
||||
* Write/Erase path: take mmap lock before SPI1 mutex, so that when erase/prog is blocked by mmap, read is still
|
||||
* available.
|
||||
*/
|
||||
flash_mmap_lock_freeze();
|
||||
}
|
||||
|
||||
#if !CONFIG_SPI_FLASH_AUTO_SUSPEND
|
||||
# if !MMAP_EXECUTABLES_FROM_FLASH
|
||||
// XIP from PSRAM/RAM app: cache accesses go to PSRAM/RAM, not flash.
|
||||
// Mutex serializes concurrent SPI1 access.
|
||||
_lock_acquire(&s_spi1_flash_mutex);
|
||||
if (!(flags & ESP_FLASH_START_FLAG_NO_READ) || !flash_mmap_remain()) {
|
||||
// When read, or write while no mmap page remaining, we can keep the cache enabled.
|
||||
ctx->current_op_type = OP_TYPE_MMAP_LOCK;
|
||||
} else {
|
||||
// Otherwise, we still need to disable the cache.
|
||||
cache_disable(NULL);
|
||||
ctx->current_op_type = OP_TYPE_CACHE_DIS;
|
||||
}
|
||||
# else
|
||||
// Code runs from flash: cache_disable already ensures the serialization. No extra mutex needed here.
|
||||
cache_disable(NULL);
|
||||
ctx->current_op_type = OP_TYPE_CACHE_DIS;
|
||||
# endif
|
||||
|
||||
#else //CONFIG_SPI_FLASH_AUTO_SUSPEND
|
||||
_lock_acquire(&s_spi1_flash_mutex);
|
||||
# if CONFIG_SPI_FLASH_DISABLE_SCHEDULER_IN_SUSPEND
|
||||
disable_scheduler();
|
||||
# endif // CONFIG_SPI_FLASH_DISABLE_SCHEDULER_IN_SUSPEND
|
||||
ctx->current_op_type = OP_TYPE_SCHEDULER_DIS;
|
||||
|
||||
#endif //CONFIG_SPI_FLASH_AUTO_SUSPEND
|
||||
|
||||
ctx->start_flags = flags;
|
||||
|
||||
#if CONFIG_SPI_FLASH_FREQ_LIMIT_C5_240MHZ
|
||||
if (flags & ESP_FLASH_START_FLAG_LIMIT_CPU_FREQ) {
|
||||
@@ -163,41 +278,48 @@ static IRAM_ATTR esp_err_t spi1_end(void *arg)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
app_func_arg_t* ctx = (app_func_arg_t*)arg;
|
||||
|
||||
// Call freq_limit_unlock if needed, before releasing the lock
|
||||
#if CONFIG_SPI_FLASH_FREQ_LIMIT_C5_240MHZ
|
||||
uint32_t flags = ctx->start_flags;
|
||||
|
||||
#if CONFIG_SPI_FLASH_FREQ_LIMIT_C5_240MHZ
|
||||
if (flags & ESP_FLASH_START_FLAG_LIMIT_CPU_FREQ) {
|
||||
esp_flash_freq_unlimit_cb();
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* There are three ways for ESP Flash API lock, see `spi1_start`
|
||||
* There are different lock paths, see `spi1_start`
|
||||
*/
|
||||
#if CONFIG_SPI_FLASH_SHARE_SPI1_BUS
|
||||
ret = release_spi_bus_lock(arg);
|
||||
#elif SPI_FLASH_CACHE_NO_DISABLE
|
||||
_lock_release(&s_spi1_flash_mutex);
|
||||
#else
|
||||
cache_enable(NULL);
|
||||
#endif
|
||||
|
||||
#if CONFIG_SPI_FLASH_DISABLE_SCHEDULER_IN_SUSPEND
|
||||
if (xTaskGetSchedulerState() == taskSCHEDULER_RUNNING) {
|
||||
#ifdef CONFIG_FREERTOS_SMP
|
||||
//Note: Scheduler suspension behavior changed in FreeRTOS SMP
|
||||
vTaskPreemptionEnable(NULL);
|
||||
#else
|
||||
xTaskResumeAll();
|
||||
#endif // CONFIG_FREERTOS_SMP
|
||||
#if !CONFIG_SPI_FLASH_AUTO_SUSPEND
|
||||
# if !MMAP_EXECUTABLES_FROM_FLASH
|
||||
if (ctx->current_op_type != OP_TYPE_MMAP_LOCK) {
|
||||
assert(ctx->current_op_type == OP_TYPE_CACHE_DIS);
|
||||
cache_enable(NULL);
|
||||
}
|
||||
_lock_release(&s_spi1_flash_mutex);
|
||||
# else
|
||||
assert(ctx->current_op_type == OP_TYPE_CACHE_DIS);
|
||||
cache_enable(NULL);
|
||||
# endif
|
||||
|
||||
#else //!CONFIG_SPI_FLASH_AUTO_SUSPEND
|
||||
# if CONFIG_SPI_FLASH_DISABLE_SCHEDULER_IN_SUSPEND
|
||||
restore_scheduler();
|
||||
# endif // CONFIG_SPI_FLASH_DISABLE_SCHEDULER_IN_SUSPEND
|
||||
assert(ctx->current_op_type == OP_TYPE_SCHEDULER_DIS);
|
||||
_lock_release(&s_spi1_flash_mutex);
|
||||
|
||||
#endif //!CONFIG_SPI_FLASH_AUTO_SUSPEND
|
||||
|
||||
if (flags & ESP_FLASH_START_FLAG_NO_READ) {
|
||||
flash_mmap_lock_unfreeze();
|
||||
}
|
||||
#endif // CONFIG_SPI_FLASH_DISABLE_SCHEDULER_IN_SUSPEND
|
||||
|
||||
on_spi_released(ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif // !CONFIG_IDF_TARGET_ESP32
|
||||
|
||||
static esp_err_t spi_flash_os_check_yield(void *arg, uint32_t chip_status, uint32_t* out_request)
|
||||
{
|
||||
assert (chip_status == 0); //TODO: support suspend
|
||||
@@ -376,8 +498,18 @@ esp_err_t esp_flash_deinit_os_functions(esp_flash_t* chip, spi_bus_lock_dev_hand
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_flash_init_main_bus_lock(void)
|
||||
esp_err_t esp_flash_app_init_os_functions(void)
|
||||
{
|
||||
esp_err_t err = ESP_OK;
|
||||
|
||||
#if !CONFIG_IDF_TARGET_ESP32
|
||||
_lock_init(&s_spi1_flash_mutex);
|
||||
err = flash_mmap_lock_init();
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* The following called functions are only defined if CONFIG_SPI_FLASH_SHARE_SPI1_BUS
|
||||
* is set. Thus, we must not call them if the macro is not defined, else the linker
|
||||
* would trigger errors. */
|
||||
@@ -385,14 +517,13 @@ esp_err_t esp_flash_init_main_bus_lock(void)
|
||||
/* bus_lock is registered by `spi_bus_lock_init_main_bus` constructor in spi_common.c */
|
||||
spi_bus_lock_set_bg_control(g_main_spi_bus_lock, cache_enable, cache_disable, NULL);
|
||||
|
||||
esp_err_t err = spi_bus_lock_init_main_dev();
|
||||
err = spi_bus_lock_init_main_dev();
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
return ESP_OK;
|
||||
#else
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
#endif
|
||||
(void)err;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_flash_app_enable_os_functions(esp_flash_t* chip)
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
set(srcs "test_app_main.c"
|
||||
"test_flash_mmap.c")
|
||||
"test_mmap_utils.c"
|
||||
"test_flash_mmap.c"
|
||||
"test_mmap_api_concurrent.c")
|
||||
|
||||
# In order for the cases defined by `TEST_CASE` to be linked into the final elf,
|
||||
# the component can be registered as WHOLE_ARCHIVE
|
||||
idf_component_register(SRCS ${srcs}
|
||||
PRIV_REQUIRES unity test_utils spi_flash esp_partition efuse
|
||||
PRIV_REQUIRES unity test_utils spi_flash bootloader_support esp_partition esp_mm efuse
|
||||
PRIV_INCLUDE_DIRS .
|
||||
WHOLE_ARCHIVE)
|
||||
|
||||
@@ -20,114 +20,35 @@
|
||||
#include "esp_flash.h"
|
||||
|
||||
#include "test_utils.h"
|
||||
#include "test_mmap_utils.h"
|
||||
|
||||
|
||||
static uint32_t buffer[1024];
|
||||
|
||||
/* read-only region used for mmap tests, initialised in setup_mmap_tests() */
|
||||
static uint32_t start;
|
||||
static uint32_t end;
|
||||
|
||||
static spi_flash_mmap_handle_t handle1, handle2, handle3;
|
||||
|
||||
static esp_err_t spi_flash_read_maybe_encrypted(size_t src_addr, void *des_addr, size_t size)
|
||||
{
|
||||
if (!esp_efuse_is_flash_encryption_enabled()) {
|
||||
return esp_flash_read(NULL, des_addr, src_addr, size);
|
||||
} else {
|
||||
return esp_flash_read_encrypted(NULL, src_addr, des_addr, size);
|
||||
}
|
||||
}
|
||||
|
||||
static esp_err_t spi_flash_write_maybe_encrypted(size_t des_addr, const void *src_addr, size_t size)
|
||||
{
|
||||
if (!esp_efuse_is_flash_encryption_enabled()) {
|
||||
return esp_flash_write(NULL, src_addr, des_addr, size);
|
||||
} else {
|
||||
return esp_flash_write_encrypted(NULL, des_addr, src_addr, size);
|
||||
}
|
||||
}
|
||||
|
||||
static void setup_mmap_tests(void)
|
||||
{
|
||||
if (start == 0) {
|
||||
const esp_partition_t *part = get_test_data_partition();
|
||||
start = part->address;
|
||||
end = part->address + part->size;
|
||||
printf("Test data partition @ 0x%"PRIx32" - 0x%"PRIx32"\n", start, end);
|
||||
}
|
||||
TEST_ASSERT(end > start);
|
||||
TEST_ASSERT(end - start >= 512 * 1024);
|
||||
|
||||
/* clean up any mmap handles left over from failed tests */
|
||||
if (handle1) {
|
||||
spi_flash_munmap(handle1);
|
||||
handle1 = 0;
|
||||
}
|
||||
if (handle2) {
|
||||
spi_flash_munmap(handle2);
|
||||
handle2 = 0;
|
||||
}
|
||||
if (handle3) {
|
||||
spi_flash_munmap(handle3);
|
||||
handle3 = 0;
|
||||
}
|
||||
|
||||
/* prepare flash contents */
|
||||
srand(0);
|
||||
for (int block = start / 0x10000; block < end / 0x10000; ++block) {
|
||||
for (int sector = 0; sector < 16; ++sector) {
|
||||
uint32_t abs_sector = (block * 16) + sector;
|
||||
uint32_t sector_offs = abs_sector * SPI_FLASH_SEC_SIZE;
|
||||
bool sector_needs_write = false;
|
||||
|
||||
TEST_ESP_OK( spi_flash_read_maybe_encrypted(sector_offs, buffer, sizeof(buffer)) );
|
||||
|
||||
for (uint32_t word = 0; word < 1024; ++word) {
|
||||
uint32_t val = rand();
|
||||
if (block == start / 0x10000 && sector == 0 && word == 0) {
|
||||
printf("setup_mmap_tests(): first prepped word: 0x%08"PRIx32" (flash holds 0x%08"PRIx32")\n", val, buffer[word]);
|
||||
}
|
||||
if (buffer[word] != val) {
|
||||
buffer[word] = val;
|
||||
sector_needs_write = true;
|
||||
}
|
||||
}
|
||||
/* Only rewrite the sector if it has changed */
|
||||
if (sector_needs_write) {
|
||||
TEST_ESP_OK( esp_flash_erase_region(NULL, (uint16_t) abs_sector * SPI_FLASH_SEC_SIZE, SPI_FLASH_SEC_SIZE) );
|
||||
TEST_ESP_OK( spi_flash_write_maybe_encrypted(sector_offs, (const uint8_t *) buffer, sizeof(buffer)) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Can get correct data in existing mapped region", "[spi_flash][mmap]")
|
||||
{
|
||||
setup_mmap_tests();
|
||||
|
||||
printf("Mapping %"PRIx32" (+%"PRIx32")\n", start, end - start);
|
||||
printf("Mapping %"PRIx32" (+%"PRIx32")\n", test_start, test_end - test_start);
|
||||
const void *ptr1;
|
||||
TEST_ESP_OK( spi_flash_mmap(start, end - start, SPI_FLASH_MMAP_DATA, &ptr1, &handle1) );
|
||||
TEST_ESP_OK( spi_flash_mmap(test_start, test_end - test_start, SPI_FLASH_MMAP_FLAG_DATA | SPI_FLASH_MMAP_FLAG_BLOCKS_WRITE, &ptr1, &handle1) );
|
||||
printf("mmap_res: handle=%"PRIx32" ptr=%p\n", (uint32_t)handle1, ptr1);
|
||||
|
||||
/* Remap in the previously mapped region itself */
|
||||
uint32_t new_start = start + CONFIG_MMU_PAGE_SIZE;
|
||||
printf("Mapping %"PRIx32" (+%"PRIx32")\n", new_start, end - new_start);
|
||||
uint32_t new_start = test_start + CONFIG_MMU_PAGE_SIZE;
|
||||
printf("Mapping %"PRIx32" (+%"PRIx32")\n", new_start, test_end - new_start);
|
||||
const void *ptr2;
|
||||
TEST_ESP_OK( spi_flash_mmap(new_start, end - new_start, SPI_FLASH_MMAP_DATA, &ptr2, &handle2) );
|
||||
TEST_ESP_OK( spi_flash_mmap(new_start, test_end - new_start, SPI_FLASH_MMAP_FLAG_DATA | SPI_FLASH_MMAP_FLAG_BLOCKS_WRITE, &ptr2, &handle2) );
|
||||
printf("mmap_res: handle=%"PRIx32" ptr=%p\n", (uint32_t)handle2, ptr2);
|
||||
|
||||
const void *src1 = (void *) ((uint32_t) ptr1 + CONFIG_MMU_PAGE_SIZE);
|
||||
const void *src2 = ptr2;
|
||||
/* Memory contents should be identical - as the region is same */
|
||||
TEST_ASSERT_EQUAL(0, memcmp(src1, src2, end - new_start));
|
||||
TEST_ASSERT_EQUAL(0, memcmp(src1, src2, test_end - new_start));
|
||||
|
||||
spi_flash_munmap(handle1);
|
||||
handle1 = 0;
|
||||
spi_flash_munmap(handle2);
|
||||
handle2 = 0;
|
||||
TEST_ASSERT_EQUAL_PTR(NULL, spi_flash_phys2cache(start, SPI_FLASH_MMAP_DATA));
|
||||
TEST_ASSERT_EQUAL_PTR(NULL, spi_flash_phys2cache(test_start, SPI_FLASH_MMAP_DATA));
|
||||
}
|
||||
|
||||
TEST_CASE("Can mmap into data address space", "[spi_flash][mmap]")
|
||||
@@ -135,14 +56,14 @@ TEST_CASE("Can mmap into data address space", "[spi_flash][mmap]")
|
||||
esp_err_t ret = ESP_FAIL;
|
||||
setup_mmap_tests();
|
||||
|
||||
printf("Mapping %"PRIx32" (+%"PRIx32")\n", start, end - start);
|
||||
printf("Mapping %"PRIx32" (+%"PRIx32")\n", test_start, test_end - test_start);
|
||||
const void *ptr1;
|
||||
TEST_ESP_OK( spi_flash_mmap(start, end - start, SPI_FLASH_MMAP_DATA, &ptr1, &handle1) );
|
||||
TEST_ESP_OK( spi_flash_mmap(test_start, test_end - test_start, SPI_FLASH_MMAP_FLAG_DATA | SPI_FLASH_MMAP_FLAG_BLOCKS_WRITE, &ptr1, &handle1) );
|
||||
printf("mmap_res: handle=%"PRIx32" ptr=%p\n", (uint32_t)handle1, ptr1);
|
||||
|
||||
srand(0);
|
||||
const uint32_t *data = (const uint32_t *) ptr1;
|
||||
for (int block = 0; block < (end - start) / 0x10000; ++block) {
|
||||
for (int block = 0; block < (test_end - test_start) / 0x10000; ++block) {
|
||||
printf("block %d\n", block);
|
||||
for (int sector = 0; sector < 16; ++sector) {
|
||||
printf("sector %d\n", sector);
|
||||
@@ -151,25 +72,25 @@ TEST_CASE("Can mmap into data address space", "[spi_flash][mmap]")
|
||||
}
|
||||
}
|
||||
}
|
||||
printf("Mapping %"PRIx32" (+%x)\n", start - 0x10000, 0x20000);
|
||||
printf("Mapping %"PRIx32" (+%x)\n", test_start - 0x10000, 0x20000);
|
||||
const void *ptr2;
|
||||
TEST_ESP_OK( spi_flash_mmap(start - 0x10000, 0x20000, SPI_FLASH_MMAP_DATA, &ptr2, &handle2) );
|
||||
TEST_ESP_OK( spi_flash_mmap(test_start - 0x10000, 0x20000, SPI_FLASH_MMAP_FLAG_DATA | SPI_FLASH_MMAP_FLAG_BLOCKS_WRITE, &ptr2, &handle2) );
|
||||
printf("mmap_res: handle=%"PRIx32" ptr=%p\n", (uint32_t)handle2, ptr2);
|
||||
|
||||
TEST_ASSERT_EQUAL_HEX32(start - 0x10000, spi_flash_cache2phys(ptr2));
|
||||
TEST_ASSERT_EQUAL_HEX32(test_start - 0x10000, spi_flash_cache2phys(ptr2));
|
||||
|
||||
TEST_ASSERT_EQUAL_PTR(ptr2, spi_flash_phys2cache(start - 0x10000, SPI_FLASH_MMAP_DATA));
|
||||
TEST_ASSERT_EQUAL_PTR(ptr2, spi_flash_phys2cache(test_start - 0x10000, SPI_FLASH_MMAP_DATA));
|
||||
|
||||
printf("Mapping %"PRIx32" (+%x)\n", start, 0x10000);
|
||||
printf("Mapping %"PRIx32" (+%x)\n", test_start, 0x10000);
|
||||
const void *ptr3;
|
||||
ret = spi_flash_mmap(start, 0x10000, SPI_FLASH_MMAP_DATA, &ptr3, &handle3);
|
||||
ret = spi_flash_mmap(test_start, 0x10000, SPI_FLASH_MMAP_FLAG_DATA | SPI_FLASH_MMAP_FLAG_BLOCKS_WRITE, &ptr3, &handle3);
|
||||
printf("ret: 0x%x\n", ret);
|
||||
TEST_ASSERT(ret == ESP_OK);
|
||||
printf("mmap_res: handle=%"PRIx32" ptr=%p\n", (uint32_t)handle3, ptr3);
|
||||
|
||||
TEST_ASSERT_EQUAL_HEX32(start, spi_flash_cache2phys(ptr3));
|
||||
TEST_ASSERT_EQUAL_PTR(ptr3, spi_flash_phys2cache(start, SPI_FLASH_MMAP_DATA));
|
||||
TEST_ASSERT_EQUAL_PTR((intptr_t)ptr3 + 0x4444, spi_flash_phys2cache(start + 0x4444, SPI_FLASH_MMAP_DATA));
|
||||
TEST_ASSERT_EQUAL_HEX32(test_start, spi_flash_cache2phys(ptr3));
|
||||
TEST_ASSERT_EQUAL_PTR(ptr3, spi_flash_phys2cache(test_start, SPI_FLASH_MMAP_DATA));
|
||||
TEST_ASSERT_EQUAL_PTR((intptr_t)ptr3 + 0x4444, spi_flash_phys2cache(test_start + 0x4444, SPI_FLASH_MMAP_DATA));
|
||||
|
||||
printf("Unmapping handle1\n");
|
||||
spi_flash_munmap(handle1);
|
||||
@@ -183,8 +104,8 @@ TEST_CASE("Can mmap into data address space", "[spi_flash][mmap]")
|
||||
spi_flash_munmap(handle3);
|
||||
handle3 = 0;
|
||||
|
||||
printf("start corresponding vaddr: 0x%x\n", (int)spi_flash_phys2cache(start, SPI_FLASH_MMAP_DATA));
|
||||
TEST_ASSERT_EQUAL_PTR(NULL, spi_flash_phys2cache(start, SPI_FLASH_MMAP_DATA));
|
||||
printf("start corresponding vaddr: 0x%x\n", (int)spi_flash_phys2cache(test_start, SPI_FLASH_MMAP_DATA));
|
||||
TEST_ASSERT_EQUAL_PTR(NULL, spi_flash_phys2cache(test_start, SPI_FLASH_MMAP_DATA));
|
||||
}
|
||||
|
||||
#if !CONFIG_SPI_FLASH_ROM_IMPL //flash mmap API in ROM does not support mmap into instruction address
|
||||
@@ -192,47 +113,47 @@ TEST_CASE("Can mmap into instruction address space", "[spi_flash][mmap]")
|
||||
{
|
||||
setup_mmap_tests();
|
||||
|
||||
printf("Mapping %"PRIx32" (+%"PRIx32")\n", start, end - start);
|
||||
spi_flash_mmap_handle_t handle1;
|
||||
printf("Mapping %"PRIx32" (+%"PRIx32")\n", test_start, test_end - test_start);
|
||||
const void *ptr1;
|
||||
TEST_ESP_OK( spi_flash_mmap(start, end - start, SPI_FLASH_MMAP_INST, &ptr1, &handle1) );
|
||||
TEST_ESP_OK( spi_flash_mmap(test_start, test_end - test_start, SPI_FLASH_MMAP_FLAG_INST | SPI_FLASH_MMAP_FLAG_BLOCKS_WRITE, &ptr1, &handle1) );
|
||||
printf("mmap_res: handle=%"PRIx32" ptr=%p\n", (uint32_t)handle1, ptr1);
|
||||
|
||||
srand(0);
|
||||
const uint32_t *data = (const uint32_t *) ptr1;
|
||||
for (int block = 0; block < (end - start) / 0x10000; ++block) {
|
||||
for (int block = 0; block < (test_end - test_start) / 0x10000; ++block) {
|
||||
for (int sector = 0; sector < 16; ++sector) {
|
||||
for (uint32_t word = 0; word < 1024; ++word) {
|
||||
TEST_ASSERT_EQUAL_UINT32(rand(), data[(block * 16 + sector) * 1024 + word]);
|
||||
}
|
||||
}
|
||||
}
|
||||
printf("Mapping %"PRIx32" (+%x)\n", start - 0x10000, 0x20000);
|
||||
spi_flash_mmap_handle_t handle2;
|
||||
printf("Mapping %"PRIx32" (+%x)\n", test_start - 0x10000, 0x20000);
|
||||
const void *ptr2;
|
||||
TEST_ESP_OK( spi_flash_mmap(start - 0x10000, 0x20000, SPI_FLASH_MMAP_INST, &ptr2, &handle2) );
|
||||
TEST_ESP_OK( spi_flash_mmap(test_start - 0x10000, 0x20000, SPI_FLASH_MMAP_FLAG_INST | SPI_FLASH_MMAP_FLAG_BLOCKS_WRITE, &ptr2, &handle2) );
|
||||
printf("mmap_res: handle=%"PRIx32" ptr=%p\n", (uint32_t)handle2, ptr2);
|
||||
|
||||
TEST_ASSERT_EQUAL_HEX32(start - 0x10000, spi_flash_cache2phys(ptr2));
|
||||
TEST_ASSERT_EQUAL_PTR(ptr2, spi_flash_phys2cache(start - 0x10000, SPI_FLASH_MMAP_INST));
|
||||
TEST_ASSERT_EQUAL_HEX32(test_start - 0x10000, spi_flash_cache2phys(ptr2));
|
||||
TEST_ASSERT_EQUAL_PTR(ptr2, spi_flash_phys2cache(test_start - 0x10000, SPI_FLASH_MMAP_INST));
|
||||
|
||||
printf("Mapping %"PRIx32" (+%x)\n", start, 0x10000);
|
||||
spi_flash_mmap_handle_t handle3;
|
||||
printf("Mapping %"PRIx32" (+%x)\n", test_start, 0x10000);
|
||||
const void *ptr3;
|
||||
TEST_ESP_OK( spi_flash_mmap(start, 0x10000, SPI_FLASH_MMAP_INST, &ptr3, &handle3) );
|
||||
TEST_ESP_OK( spi_flash_mmap(test_start, 0x10000, SPI_FLASH_MMAP_FLAG_INST | SPI_FLASH_MMAP_FLAG_BLOCKS_WRITE, &ptr3, &handle3) );
|
||||
printf("mmap_res: handle=%"PRIx32" ptr=%p\n", (uint32_t)handle3, ptr3);
|
||||
|
||||
TEST_ASSERT_EQUAL_HEX32(start, spi_flash_cache2phys(ptr3));
|
||||
TEST_ASSERT_EQUAL_PTR(ptr3, spi_flash_phys2cache(start, SPI_FLASH_MMAP_INST));
|
||||
TEST_ASSERT_EQUAL_HEX32(test_start, spi_flash_cache2phys(ptr3));
|
||||
TEST_ASSERT_EQUAL_PTR(ptr3, spi_flash_phys2cache(test_start, SPI_FLASH_MMAP_INST));
|
||||
|
||||
printf("Unmapping handle1\n");
|
||||
spi_flash_munmap(handle1);
|
||||
handle1 = 0;
|
||||
|
||||
printf("Unmapping handle2\n");
|
||||
spi_flash_munmap(handle2);
|
||||
handle2 = 0;
|
||||
|
||||
printf("Unmapping handle3\n");
|
||||
spi_flash_munmap(handle3);
|
||||
handle3 = 0;
|
||||
}
|
||||
#endif // !CONFIG_SPI_FLASH_ROM_IMPL
|
||||
|
||||
@@ -243,10 +164,10 @@ TEST_CASE("Can mmap unordered pages into contiguous memory", "[spi_flash][mmap]"
|
||||
int startpage;
|
||||
|
||||
setup_mmap_tests();
|
||||
nopages = (end - start) / SPI_FLASH_MMU_PAGE_SIZE;
|
||||
nopages = (test_end - test_start) / SPI_FLASH_MMU_PAGE_SIZE;
|
||||
pages = alloca(sizeof(int) * nopages);
|
||||
|
||||
startpage = start / SPI_FLASH_MMU_PAGE_SIZE;
|
||||
startpage = test_start / SPI_FLASH_MMU_PAGE_SIZE;
|
||||
|
||||
//make inverse mapping: virt 0 -> page (nopages-1), virt 1 -> page (nopages-2), ...
|
||||
for (int i = 0; i < nopages; i++) {
|
||||
@@ -256,9 +177,8 @@ TEST_CASE("Can mmap unordered pages into contiguous memory", "[spi_flash][mmap]"
|
||||
|
||||
printf("Attempting mapping of unordered pages to contiguous memory area\n");
|
||||
|
||||
spi_flash_mmap_handle_t handle1;
|
||||
const void *ptr1;
|
||||
TEST_ESP_OK( spi_flash_mmap_pages(pages, nopages, SPI_FLASH_MMAP_DATA, &ptr1, &handle1) );
|
||||
TEST_ESP_OK( spi_flash_mmap_pages(pages, nopages, SPI_FLASH_MMAP_FLAG_DATA | SPI_FLASH_MMAP_FLAG_BLOCKS_WRITE, &ptr1, &handle1) );
|
||||
printf("mmap_res: handle=%"PRIx32" ptr=%p\n", (uint32_t)handle1, ptr1);
|
||||
|
||||
#if (CONFIG_MMU_PAGE_SIZE == 0x10000)
|
||||
@@ -283,6 +203,7 @@ TEST_CASE("Can mmap unordered pages into contiguous memory", "[spi_flash][mmap]"
|
||||
|
||||
printf("Unmapping handle1\n");
|
||||
spi_flash_munmap(handle1);
|
||||
handle1 = 0;
|
||||
}
|
||||
|
||||
TEST_CASE("flash_mmap invalidates just-written data", "[spi_flash][mmap]")
|
||||
@@ -297,10 +218,10 @@ TEST_CASE("flash_mmap invalidates just-written data", "[spi_flash][mmap]")
|
||||
TEST_IGNORE_MESSAGE("flash encryption enabled, spi_flash_write_encrypted() test won't pass as-is");
|
||||
}
|
||||
|
||||
TEST_ESP_OK( esp_flash_erase_region(NULL, start, SPI_FLASH_SEC_SIZE) );
|
||||
TEST_ESP_OK( esp_flash_erase_region(NULL, test_start, SPI_FLASH_SEC_SIZE) );
|
||||
|
||||
/* map erased test region to ptr1 */
|
||||
TEST_ESP_OK( spi_flash_mmap(start, test_size, SPI_FLASH_MMAP_DATA, &ptr1, &handle1) );
|
||||
TEST_ESP_OK( spi_flash_mmap(test_start, test_size, SPI_FLASH_MMAP_FLAG_DATA | SPI_FLASH_MMAP_FLAG_BLOCKS_WRITE, &ptr1, &handle1) );
|
||||
printf("mmap_res ptr1: handle=%"PRIx32" ptr=%p\n", (uint32_t)handle1, ptr1);
|
||||
|
||||
/* verify it's all 0xFF */
|
||||
@@ -315,14 +236,14 @@ TEST_CASE("flash_mmap invalidates just-written data", "[spi_flash][mmap]")
|
||||
/* write flash region to 0xEE */
|
||||
uint8_t buf[test_size];
|
||||
memset(buf, 0xEE, test_size);
|
||||
TEST_ESP_OK( esp_flash_write(NULL, buf, start, test_size) );
|
||||
TEST_ESP_OK( esp_flash_write(NULL, buf, test_start, test_size) );
|
||||
|
||||
/* re-map the test region at ptr1.
|
||||
|
||||
this is a fresh mmap call so should trigger a cache flush,
|
||||
ensuring we see the updated flash.
|
||||
*/
|
||||
TEST_ESP_OK( spi_flash_mmap(start, test_size, SPI_FLASH_MMAP_DATA, &ptr1, &handle1) );
|
||||
TEST_ESP_OK( spi_flash_mmap(test_start, test_size, SPI_FLASH_MMAP_FLAG_DATA | SPI_FLASH_MMAP_FLAG_BLOCKS_WRITE, &ptr1, &handle1) );
|
||||
printf("mmap_res ptr1 #2: handle=%"PRIx32" ptr=%p\n", (uint32_t)handle1, ptr1);
|
||||
|
||||
/* assert that ptr1 now maps to the new values on flash,
|
||||
@@ -339,14 +260,14 @@ TEST_CASE("flash_mmap can mmap after get enough free MMU pages", "[spi_flash][mm
|
||||
//this test case should make flash size >= 4MB, because max size of Dcache can mapped is 4MB
|
||||
setup_mmap_tests();
|
||||
|
||||
printf("Mapping %"PRIx32" (+%"PRIx32")\n", start, end - start);
|
||||
printf("Mapping %"PRIx32" (+%"PRIx32")\n", test_start, test_end - test_start);
|
||||
const void *ptr1;
|
||||
TEST_ESP_OK( spi_flash_mmap(start, end - start, SPI_FLASH_MMAP_DATA, &ptr1, &handle1) );
|
||||
TEST_ESP_OK( spi_flash_mmap(test_start, test_end - test_start, SPI_FLASH_MMAP_FLAG_DATA | SPI_FLASH_MMAP_FLAG_BLOCKS_WRITE, &ptr1, &handle1) );
|
||||
printf("mmap_res: handle=%"PRIx32" ptr=%p\n", (uint32_t)handle1, ptr1);
|
||||
|
||||
srand(0);
|
||||
const uint32_t *data = (const uint32_t *) ptr1;
|
||||
for (int block = 0; block < (end - start) / 0x10000; ++block) {
|
||||
for (int block = 0; block < (test_end - test_start) / 0x10000; ++block) {
|
||||
printf("block %d\n", block);
|
||||
for (int sector = 0; sector < 16; ++sector) {
|
||||
printf("sector %d\n", sector);
|
||||
@@ -363,7 +284,7 @@ TEST_CASE("flash_mmap can mmap after get enough free MMU pages", "[spi_flash][mm
|
||||
|
||||
printf("Mapping %x (+%"PRIx32")\n", 0, free_pages * SPI_FLASH_MMU_PAGE_SIZE);
|
||||
const void *ptr2;
|
||||
TEST_ESP_OK( spi_flash_mmap(0, free_pages * SPI_FLASH_MMU_PAGE_SIZE, SPI_FLASH_MMAP_DATA, &ptr2, &handle2) );
|
||||
TEST_ESP_OK( spi_flash_mmap(0, free_pages * SPI_FLASH_MMU_PAGE_SIZE, SPI_FLASH_MMAP_FLAG_DATA | SPI_FLASH_MMAP_FLAG_BLOCKS_WRITE, &ptr2, &handle2) );
|
||||
printf("mmap_res: handle=%"PRIx32" ptr=%p\n", (uint32_t)handle2, ptr2);
|
||||
|
||||
printf("Unmapping handle1\n");
|
||||
@@ -374,7 +295,7 @@ TEST_CASE("flash_mmap can mmap after get enough free MMU pages", "[spi_flash][mm
|
||||
spi_flash_munmap(handle2);
|
||||
handle2 = 0;
|
||||
|
||||
TEST_ASSERT_EQUAL_PTR(NULL, spi_flash_phys2cache(start, SPI_FLASH_MMAP_DATA));
|
||||
TEST_ASSERT_EQUAL_PTR(NULL, spi_flash_phys2cache(test_start, SPI_FLASH_MMAP_DATA));
|
||||
}
|
||||
|
||||
TEST_CASE("phys2cache/cache2phys basic checks", "[spi_flash][mmap]")
|
||||
@@ -432,15 +353,15 @@ TEST_CASE("mmap consistent with phys2cache/cache2phys", "[spi_flash][mmap]")
|
||||
|
||||
TEST_ASSERT_EQUAL_HEX(SPI_FLASH_CACHE2PHYS_FAIL, spi_flash_cache2phys(ptr));
|
||||
|
||||
TEST_ESP_OK( spi_flash_mmap(start, test_size, SPI_FLASH_MMAP_DATA, &ptr, &handle1) );
|
||||
TEST_ESP_OK( spi_flash_mmap(test_start, test_size, SPI_FLASH_MMAP_FLAG_DATA | SPI_FLASH_MMAP_FLAG_BLOCKS_WRITE, &ptr, &handle1) );
|
||||
TEST_ASSERT_NOT_NULL(ptr);
|
||||
TEST_ASSERT_NOT_EQUAL(0, handle1);
|
||||
|
||||
TEST_ASSERT_EQUAL_HEX(start, spi_flash_cache2phys(ptr));
|
||||
TEST_ASSERT_EQUAL_HEX(start + 1024, spi_flash_cache2phys((void *)((intptr_t)ptr + 1024)));
|
||||
TEST_ASSERT_EQUAL_HEX(start + 3000, spi_flash_cache2phys((void *)((intptr_t)ptr + 3000)));
|
||||
TEST_ASSERT_EQUAL_HEX(test_start, spi_flash_cache2phys(ptr));
|
||||
TEST_ASSERT_EQUAL_HEX(test_start + 1024, spi_flash_cache2phys((void *)((intptr_t)ptr + 1024)));
|
||||
TEST_ASSERT_EQUAL_HEX(test_start + 3000, spi_flash_cache2phys((void *)((intptr_t)ptr + 3000)));
|
||||
/* this pointer lands in a different MMU table entry */
|
||||
TEST_ASSERT_EQUAL_HEX(start + test_size - 4, spi_flash_cache2phys((void *)((intptr_t)ptr + test_size - 4)));
|
||||
TEST_ASSERT_EQUAL_HEX(test_start + test_size - 4, spi_flash_cache2phys((void *)((intptr_t)ptr + test_size - 4)));
|
||||
|
||||
spi_flash_munmap(handle1);
|
||||
handle1 = 0;
|
||||
@@ -468,14 +389,16 @@ TEST_CASE("munmap followed by mmap flushes cache", "[spi_flash][mmap]")
|
||||
const uint32_t *data;
|
||||
esp_partition_mmap_handle_t handle;
|
||||
TEST_ESP_OK( esp_partition_mmap(p, 0, SPI_FLASH_MMU_PAGE_SIZE,
|
||||
ESP_PARTITION_MMAP_DATA, (const void **) &data, &handle) );
|
||||
ESP_PARTITION_MMAP_DATA | ESP_PARTITION_MMAP_BLOCKS_WRITE, (const void **) &data, &handle) );
|
||||
uint32_t buf[16];
|
||||
memcpy(buf, data, sizeof(buf));
|
||||
|
||||
esp_partition_munmap(handle);
|
||||
|
||||
TEST_ESP_OK( esp_partition_mmap(p, SPI_FLASH_MMU_PAGE_SIZE, SPI_FLASH_MMU_PAGE_SIZE,
|
||||
ESP_PARTITION_MMAP_DATA, (const void **) &data, &handle) );
|
||||
ESP_PARTITION_MMAP_DATA | ESP_PARTITION_MMAP_BLOCKS_WRITE, (const void **) &data, &handle) );
|
||||
TEST_ASSERT_NOT_EQUAL(0, memcmp(buf, data, sizeof(buf)));
|
||||
esp_partition_munmap(handle);
|
||||
}
|
||||
|
||||
TEST_CASE("no stale data read post mmap and write partition", "[spi_flash][mmap]")
|
||||
|
||||
@@ -0,0 +1,381 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/semphr.h"
|
||||
|
||||
#include "test_utils.h"
|
||||
#include "test_mmap_utils.h"
|
||||
#include "spi_flash_mmap.h"
|
||||
#include "esp_flash.h"
|
||||
#include "esp_cache.h"
|
||||
|
||||
|
||||
//XIP_PSRAM or RAM_APP enabled
|
||||
#if (CONFIG_SPIRAM_FETCH_INSTRUCTIONS && CONFIG_SPIRAM_RODATA) || CONFIG_APP_BUILD_TYPE_RAM
|
||||
#define EXECUTE_IN_FLASH 0
|
||||
#else
|
||||
#define EXECUTE_IN_FLASH 1
|
||||
#endif
|
||||
|
||||
TEST_CASE("flash_mmap allows esp_flash erase/write/read when mapped", "[spi_flash][mmap]")
|
||||
{
|
||||
const void *ptr1;
|
||||
const size_t test_size = 128;
|
||||
|
||||
spi_flash_mmap_handle_t handle1;
|
||||
setup_mmap_tests();
|
||||
|
||||
/* map erased test region to ptr1 */
|
||||
TEST_ESP_OK( spi_flash_mmap(test_start, test_size, SPI_FLASH_MMAP_FLAG_DATA, &ptr1, &handle1) );
|
||||
printf("mmap_res ptr1: handle=%"PRIx32" ptr=%p\n", (uint32_t)handle1, ptr1);
|
||||
|
||||
//Make sure ptr1 is cached
|
||||
volatile uint8_t val = 0xff;
|
||||
for (int i = 0; i < test_size; i++) {
|
||||
val = val ^ ((uint8_t *)ptr1)[i];
|
||||
}
|
||||
|
||||
TEST_ESP_OK( esp_flash_erase_region(NULL, test_start, SPI_FLASH_SEC_SIZE) );
|
||||
|
||||
/* verify it's all 0xFF after the erase operation */
|
||||
for (int i = 0; i < test_size; i++) {
|
||||
TEST_ASSERT_EQUAL_HEX(0xFF, ((uint8_t *)ptr1)[i]);
|
||||
}
|
||||
|
||||
/* unmap the erased region */
|
||||
spi_flash_munmap(handle1);
|
||||
handle1 = 0;
|
||||
|
||||
/* write flash region to 0xEE */
|
||||
uint8_t buf[test_size];
|
||||
uint8_t read_buf[test_size];
|
||||
memset(buf, 0xEE, test_size);
|
||||
TEST_ESP_OK( esp_flash_write(NULL, buf, test_start, test_size) );
|
||||
|
||||
/* re-map the test region at ptr1.
|
||||
|
||||
this is a fresh mmap call so should trigger a cache flush,
|
||||
ensuring we see the updated flash.
|
||||
*/
|
||||
TEST_ESP_OK( spi_flash_mmap(test_start, test_size, SPI_FLASH_MMAP_FLAG_DATA, &ptr1, &handle1) );
|
||||
printf("mmap_res ptr1 #2: handle=%"PRIx32" ptr=%p\n", (uint32_t)handle1, ptr1);
|
||||
|
||||
/* assert that ptr1 now maps to the new values on flash,
|
||||
ie contents of buf array.
|
||||
*/
|
||||
TEST_ASSERT_EQUAL_HEX8_ARRAY(buf, ptr1, test_size);
|
||||
|
||||
memset(buf, 0x66, test_size); //this data is select on purpose (can be modified from 0xEE set above)
|
||||
TEST_ESP_OK( esp_flash_write(NULL, buf, test_start, test_size) );
|
||||
TEST_ASSERT_EQUAL_HEX8_ARRAY(buf, ptr1, test_size);
|
||||
|
||||
memset(read_buf, 0x33, test_size);
|
||||
TEST_ESP_OK( esp_flash_read(NULL, read_buf, test_start, test_size) );
|
||||
TEST_ASSERT_EQUAL_HEX8_ARRAY(buf, read_buf, test_size);
|
||||
|
||||
spi_flash_munmap(handle1);
|
||||
handle1 = 0;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
uint8_t expected_data[128];
|
||||
const size_t test_size;
|
||||
SemaphoreHandle_t mmap_start; //Given from main to mmap, to start mmap
|
||||
SemaphoreHandle_t mmap_end; //Given from mmap to main indicating end of mmap and start another round
|
||||
bool finish;
|
||||
bool use_flag_blocks_write;
|
||||
} test_mmap_concurrent_ctx_t;
|
||||
|
||||
//Delay time should be longer than the erasing time
|
||||
#define MMAP_DELAY (1000 / portTICK_PERIOD_MS)
|
||||
|
||||
static void mmap_task(void* args)
|
||||
{
|
||||
test_mmap_concurrent_ctx_t *ctx = (test_mmap_concurrent_ctx_t*)args;
|
||||
const uint32_t mmap_start = test_start;
|
||||
const size_t mmap_len = CONFIG_MMU_PAGE_SIZE;
|
||||
|
||||
while (1) {
|
||||
xSemaphoreTake(ctx->mmap_start, portMAX_DELAY);
|
||||
if (ctx->finish) {
|
||||
break;
|
||||
}
|
||||
uint32_t extra_flags = ctx->use_flag_blocks_write? SPI_FLASH_MMAP_FLAG_BLOCKS_WRITE: 0;
|
||||
|
||||
const void *ptr1;
|
||||
TEST_ESP_OK( spi_flash_mmap(mmap_start, mmap_len, SPI_FLASH_MMAP_FLAG_DATA | extra_flags, &ptr1, &handle1) );
|
||||
printf("mmap_res ptr1: handle=%"PRIx32" ptr=%p\n", (uint32_t)handle1, ptr1);
|
||||
|
||||
TEST_ASSERT_EQUAL_UINT8_ARRAY(ctx->expected_data, ptr1, ctx->test_size);
|
||||
|
||||
//Delay enough time so that API on other task must happen if it can.
|
||||
vTaskDelay(MMAP_DELAY);
|
||||
|
||||
TEST_ASSERT_EQUAL_UINT8_ARRAY(ctx->expected_data, ptr1, ctx->test_size);
|
||||
|
||||
/* unmap the region for test*/
|
||||
spi_flash_munmap(handle1);
|
||||
handle1 = 0;
|
||||
|
||||
xSemaphoreGive(ctx->mmap_end);
|
||||
}
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
|
||||
activate main
|
||||
|
||||
main ->>+ mmap : Semphr start
|
||||
|
||||
mmap -> mmap : mmap
|
||||
mmap -> mmap : read & verify data
|
||||
mmap ->>+ mmap : Delay start
|
||||
|
||||
main ->>+ main : flash erase (block starts)
|
||||
|
||||
mmap ->>- mmap : Delay end
|
||||
|
||||
|
||||
mmap-> mmap : read & verify data
|
||||
|
||||
mmap -> mmap : unmap
|
||||
mmap ->> main : unblocks
|
||||
deactivate main
|
||||
|
||||
main -> main : flash erase starts
|
||||
main ->- main : check if unmapped
|
||||
|
||||
mmap ->>- main : Semphr ret
|
||||
|
||||
activate main
|
||||
main ->>+ mmap : Semphr start
|
||||
mmap -> mmap : ...
|
||||
deactivate mmap
|
||||
|
||||
deactivate main
|
||||
```
|
||||
*/
|
||||
|
||||
static void check_mmap_executed_and_wait(test_mmap_concurrent_ctx_t* ctx, bool expect_unmap)
|
||||
{
|
||||
portBASE_TYPE unmapped = xSemaphoreTake(ctx->mmap_end, 0);
|
||||
if (expect_unmap) {
|
||||
TEST_ASSERT_EQUAL_INT(pdTRUE, unmapped);
|
||||
} else {
|
||||
TEST_ASSERT_EQUAL_INT(pdFALSE, unmapped);
|
||||
|
||||
//wait until we can start the next operation
|
||||
xSemaphoreTake(ctx->mmap_end, portMAX_DELAY);
|
||||
}
|
||||
}
|
||||
|
||||
static void test_concurrent_mmap_core(test_mmap_concurrent_ctx_t *ctx, int test_size, bool use_flag_blocks_write)
|
||||
{
|
||||
const uint32_t api_addr = test_start + CONFIG_MMU_PAGE_SIZE;
|
||||
uint8_t read_buf[test_size];
|
||||
uint8_t buf[test_size];
|
||||
bool expect_blocked = false;
|
||||
|
||||
ctx->use_flag_blocks_write = use_flag_blocks_write;
|
||||
|
||||
// For erase operation, it should take the mmap mutex, and will start after unmap.
|
||||
printf("test erase in mmap...\n");
|
||||
xSemaphoreGive(ctx->mmap_start);
|
||||
esp_rom_delay_us(1000); //delay 1ms to make sure mmap is done
|
||||
TEST_ESP_OK(esp_flash_erase_region(NULL, api_addr, SPI_FLASH_SEC_SIZE));
|
||||
expect_blocked = use_flag_blocks_write;
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
expect_blocked = false;
|
||||
#endif
|
||||
check_mmap_executed_and_wait(ctx, expect_blocked);
|
||||
|
||||
// Read operations do NOT set ESP_FLASH_START_FLAG_NO_READ, so they are never blocked by mmap.
|
||||
printf("test read in mmap...\n");
|
||||
xSemaphoreGive(ctx->mmap_start);
|
||||
esp_rom_delay_us(1000); //delay 1ms to make sure mmap is done
|
||||
TEST_ESP_OK(esp_flash_read(NULL, read_buf, api_addr, test_size));
|
||||
check_mmap_executed_and_wait(ctx, false);
|
||||
|
||||
// For write operation, it's same as erase operation.
|
||||
printf("test write in mmap...\n");
|
||||
xSemaphoreGive(ctx->mmap_start);
|
||||
/* write flash region to 0xEE */
|
||||
memset(buf, 0xEE, test_size);
|
||||
esp_rom_delay_us(1000); //delay 1ms to make sure mmap is done
|
||||
TEST_ESP_OK(esp_flash_write(NULL, buf, api_addr, test_size));
|
||||
expect_blocked = use_flag_blocks_write;
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
expect_blocked = false;
|
||||
#endif
|
||||
check_mmap_executed_and_wait(ctx, expect_blocked);
|
||||
|
||||
printf("test read in mmap...\n");
|
||||
xSemaphoreGive(ctx->mmap_start);
|
||||
esp_rom_delay_us(1000); //delay 1ms to make sure mmap is done
|
||||
TEST_ESP_OK(esp_flash_read(NULL, read_buf, api_addr, test_size));
|
||||
check_mmap_executed_and_wait(ctx, false);
|
||||
}
|
||||
|
||||
TEST_CASE("flash_mmap concurrent access to flash erase/prog", "[spi_flash][mmap]")
|
||||
{
|
||||
setup_mmap_tests();
|
||||
const int test_size = 128;
|
||||
|
||||
test_mmap_concurrent_ctx_t ctx = {
|
||||
.finish = false,
|
||||
.test_size = test_size,
|
||||
.use_flag_blocks_write = true
|
||||
};
|
||||
ctx.mmap_start = xSemaphoreCreateBinary();
|
||||
TEST_ASSERT_NOT_NULL(ctx.mmap_start);
|
||||
ctx.mmap_end = xSemaphoreCreateBinary();
|
||||
TEST_ASSERT_NOT_NULL(ctx.mmap_end);
|
||||
TEST_ESP_OK(esp_flash_read(NULL, ctx.expected_data, test_start, test_size));
|
||||
|
||||
TaskHandle_t task_handle;
|
||||
//Create task with higher priority so that once semphr given, task is unblocked immediately.
|
||||
TEST_ASSERT_EQUAL(pdTRUE, xTaskCreate(mmap_task, "mmap_task", 4096, &ctx, 5, &task_handle));
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
test_concurrent_mmap_core(&ctx, test_size, true);
|
||||
test_concurrent_mmap_core(&ctx, test_size, false);
|
||||
}
|
||||
|
||||
ctx.finish = true;
|
||||
xSemaphoreGive(ctx.mmap_start);
|
||||
|
||||
vTaskDelay(10 / portTICK_PERIOD_MS);//wait for mmap task to delete
|
||||
|
||||
//recycle the context
|
||||
vSemaphoreDelete(ctx.mmap_start);
|
||||
vSemaphoreDelete(ctx.mmap_end);
|
||||
}
|
||||
|
||||
/* Number of erase rounds for the non-overlapping concurrent erase test */
|
||||
#define CONCURRENT_ERASE_ROUNDS 10
|
||||
|
||||
typedef struct {
|
||||
uint8_t expected_data[128];
|
||||
uint32_t region_a_start;
|
||||
uint32_t region_b_start;
|
||||
size_t test_size;
|
||||
bool finish;
|
||||
bool use_flag_blocks_write;
|
||||
bool data_ok;
|
||||
SemaphoreHandle_t erase_done;
|
||||
SemaphoreHandle_t erase_op_start;
|
||||
} test_mmap_erase_concurrent_ctx_t;
|
||||
|
||||
static void mmap_verify_task(void *args)
|
||||
{
|
||||
test_mmap_erase_concurrent_ctx_t *ctx = (test_mmap_erase_concurrent_ctx_t *)args;
|
||||
uint32_t extra_flags = ctx->use_flag_blocks_write? SPI_FLASH_MMAP_FLAG_BLOCKS_WRITE: 0;
|
||||
|
||||
xSemaphoreTake(ctx->erase_op_start, portMAX_DELAY);
|
||||
|
||||
while (!ctx->finish) {
|
||||
//For single core case, delay 1ms to allow erase task to start and finish
|
||||
vTaskDelay(1);
|
||||
spi_flash_mmap_handle_t verify_handle;
|
||||
const void *ptr;
|
||||
esp_err_t err = spi_flash_mmap(ctx->region_a_start, ctx->test_size, SPI_FLASH_MMAP_FLAG_DATA | extra_flags, &ptr, &verify_handle);
|
||||
if (err != ESP_OK) {
|
||||
ctx->data_ok = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (memcmp(ctx->expected_data, ptr, ctx->test_size) != 0) {
|
||||
ctx->data_ok = false;
|
||||
spi_flash_munmap(verify_handle);
|
||||
break;
|
||||
}
|
||||
|
||||
spi_flash_munmap(verify_handle);
|
||||
printf("mmap_verify_task: data ok, calling munmap\n");
|
||||
}
|
||||
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
static void erase_task(void *args)
|
||||
{
|
||||
test_mmap_erase_concurrent_ctx_t *ctx = (test_mmap_erase_concurrent_ctx_t *)args;
|
||||
xSemaphoreGive(ctx->erase_op_start);
|
||||
|
||||
for (int i = 0; i < CONCURRENT_ERASE_ROUNDS; i++) {
|
||||
printf("erase region B: round %d/%d\n", i + 1, CONCURRENT_ERASE_ROUNDS);
|
||||
TEST_ESP_OK(esp_flash_erase_region(NULL, ctx->region_b_start, SPI_FLASH_SEC_SIZE));
|
||||
printf("erase region B: round %d done\n", i + 1);
|
||||
}
|
||||
|
||||
ctx->finish = true;
|
||||
xSemaphoreGive(ctx->erase_done);
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
static void test_concurrent_erase_core(test_mmap_erase_concurrent_ctx_t *ctx, bool use_flag_blocks_write)
|
||||
{
|
||||
ctx->finish = false;
|
||||
ctx->use_flag_blocks_write = use_flag_blocks_write;
|
||||
ctx->erase_op_start = xSemaphoreCreateBinary();
|
||||
TEST_ASSERT_NOT_NULL(ctx->erase_op_start);
|
||||
ctx->erase_done = xSemaphoreCreateBinary();
|
||||
TEST_ASSERT_NOT_NULL(ctx->erase_done);
|
||||
|
||||
printf("test_concurrent_erase_core: use_flag_blocks_write=%d\n", use_flag_blocks_write);
|
||||
|
||||
//Let mmap happen during erasing
|
||||
TEST_ASSERT_EQUAL(pdTRUE, xTaskCreate(mmap_verify_task, "mmap_verify", 4096, ctx, 6, NULL));
|
||||
TEST_ASSERT_EQUAL(pdTRUE, xTaskCreate(erase_task, "erase_task", 4096, ctx, 5, NULL));
|
||||
|
||||
//Wait for erase task to complete all rounds, then stop mmap task
|
||||
xSemaphoreTake(ctx->erase_done, portMAX_DELAY);
|
||||
vTaskDelay(50 / portTICK_PERIOD_MS);
|
||||
|
||||
vSemaphoreDelete(ctx->erase_done);
|
||||
vSemaphoreDelete(ctx->erase_op_start);
|
||||
}
|
||||
|
||||
TEST_CASE("flash_mmap region unaffected by concurrent flash erase of separate region", "[spi_flash][mmap]")
|
||||
{
|
||||
setup_mmap_tests();
|
||||
|
||||
const size_t test_size = 128;
|
||||
|
||||
/* Region A: beginning of test partition (mmap + verify target) */
|
||||
uint32_t region_a = test_start;
|
||||
/* Region B: second sector of test partition (erase target), does not overlap with region A */
|
||||
uint32_t region_b = test_start + SPI_FLASH_SEC_SIZE;
|
||||
|
||||
//Prepare random data to write to region A
|
||||
uint8_t write_buf[128];
|
||||
srand(789);
|
||||
for (int i = 0; i < test_size; i ++) {
|
||||
write_buf[i] = rand() % 0xff;
|
||||
}
|
||||
TEST_ESP_OK(esp_flash_erase_region(NULL, region_a, SPI_FLASH_SEC_SIZE));
|
||||
TEST_ESP_OK(esp_flash_write(NULL, write_buf, region_a, test_size));
|
||||
|
||||
test_mmap_erase_concurrent_ctx_t ctx = {
|
||||
.region_a_start = region_a,
|
||||
.region_b_start = region_b,
|
||||
.test_size = test_size,
|
||||
.data_ok = true,
|
||||
};
|
||||
memcpy(ctx.expected_data, write_buf, test_size);
|
||||
|
||||
test_concurrent_erase_core(&ctx, false);
|
||||
test_concurrent_erase_core(&ctx, true);
|
||||
|
||||
TEST_ASSERT_TRUE_MESSAGE(ctx.data_ok, "Region A data was corrupted during concurrent erase of region B");
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
#include <inttypes.h>
|
||||
#include <esp_partition.h>
|
||||
#include <esp_efuse.h>
|
||||
|
||||
#include "test_utils.h"
|
||||
#include "test_mmap_utils.h"
|
||||
#include "esp_flash.h"
|
||||
|
||||
|
||||
/* read-only region used for mmap tests, initialised in setup_mmap_tests() */
|
||||
uint32_t test_start;
|
||||
uint32_t test_end;
|
||||
|
||||
spi_flash_mmap_handle_t handle1, handle2, handle3;
|
||||
|
||||
static uint32_t buffer[1024];
|
||||
|
||||
|
||||
esp_err_t spi_flash_read_maybe_encrypted(size_t src_addr, void *des_addr, size_t size)
|
||||
{
|
||||
if (!esp_efuse_is_flash_encryption_enabled()) {
|
||||
return esp_flash_read(NULL, des_addr, src_addr, size);
|
||||
} else {
|
||||
return esp_flash_read_encrypted(NULL, src_addr, des_addr, size);
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t spi_flash_write_maybe_encrypted(size_t des_addr, const void *src_addr, size_t size)
|
||||
{
|
||||
if (!esp_efuse_is_flash_encryption_enabled()) {
|
||||
return esp_flash_write(NULL, src_addr, des_addr, size);
|
||||
} else {
|
||||
return esp_flash_write_encrypted(NULL, des_addr, src_addr, size);
|
||||
}
|
||||
}
|
||||
|
||||
void setup_mmap_tests(void)
|
||||
{
|
||||
if (test_start == 0) {
|
||||
const esp_partition_t *part = get_test_data_partition();
|
||||
test_start = part->address;
|
||||
test_end = part->address + part->size;
|
||||
printf("Test data partition @ 0x%"PRIx32" - 0x%"PRIx32"\n", test_start, test_end);
|
||||
}
|
||||
TEST_ASSERT(test_end > test_start);
|
||||
TEST_ASSERT(test_end - test_start >= 512 * 1024);
|
||||
|
||||
/* clean up any mmap handles left over from failed tests */
|
||||
if (handle1) {
|
||||
spi_flash_munmap(handle1);
|
||||
handle1 = 0;
|
||||
}
|
||||
if (handle2) {
|
||||
spi_flash_munmap(handle2);
|
||||
handle2 = 0;
|
||||
}
|
||||
if (handle3) {
|
||||
spi_flash_munmap(handle3);
|
||||
handle3 = 0;
|
||||
}
|
||||
|
||||
/* prepare flash contents */
|
||||
srand(0);
|
||||
for (int block = test_start / 0x10000; block < test_end / 0x10000; ++block) {
|
||||
for (int sector = 0; sector < 16; ++sector) {
|
||||
uint32_t abs_sector = (block * 16) + sector;
|
||||
uint32_t sector_offs = abs_sector * SPI_FLASH_SEC_SIZE;
|
||||
bool sector_needs_write = false;
|
||||
|
||||
TEST_ESP_OK( spi_flash_read_maybe_encrypted(sector_offs, buffer, sizeof(buffer)) );
|
||||
|
||||
for (uint32_t word = 0; word < 1024; ++word) {
|
||||
uint32_t val = rand();
|
||||
if (block == test_start / 0x10000 && sector == 0 && word == 0) {
|
||||
printf("setup_mmap_tests(): first prepped word: 0x%08"PRIx32" (flash holds 0x%08"PRIx32")\n", val, buffer[word]);
|
||||
}
|
||||
if (buffer[word] != val) {
|
||||
buffer[word] = val;
|
||||
sector_needs_write = true;
|
||||
}
|
||||
}
|
||||
/* Only rewrite the sector if it has changed */
|
||||
if (sector_needs_write) {
|
||||
TEST_ESP_OK( esp_flash_erase_region(NULL, (uint16_t) abs_sector * SPI_FLASH_SEC_SIZE, SPI_FLASH_SEC_SIZE) );
|
||||
TEST_ESP_OK( spi_flash_write_maybe_encrypted(sector_offs, (const uint8_t *) buffer, sizeof(buffer)) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "spi_flash_mmap.h"
|
||||
|
||||
extern void setup_mmap_tests(void);
|
||||
extern uint32_t test_start;
|
||||
extern uint32_t test_end;
|
||||
|
||||
//Use and maintain these handles. When next test starts, setup_mmap_tests will unmap these handle if they are not zero.
|
||||
extern spi_flash_mmap_handle_t handle1, handle2, handle3;
|
||||
|
||||
esp_err_t spi_flash_read_maybe_encrypted(size_t src_addr, void *des_addr, size_t size);
|
||||
esp_err_t spi_flash_write_maybe_encrypted(size_t des_addr, const void *src_addr, size_t size);
|
||||
@@ -68,3 +68,16 @@ def test_flash_mmap_psram(dut: Dut) -> None:
|
||||
@idf_parametrize('target', ['supported_targets'], indirect=['target'])
|
||||
def test_flash_mmap_xip_psram_rom_impl(dut: Dut) -> None:
|
||||
dut.run_all_single_board_cases(timeout=30)
|
||||
|
||||
|
||||
@pytest.mark.flash_suspend
|
||||
@pytest.mark.parametrize(
|
||||
'config',
|
||||
[
|
||||
'suspend_with_rom_impl',
|
||||
],
|
||||
indirect=True,
|
||||
)
|
||||
@idf_parametrize('target', ['esp32c3'], indirect=['target'])
|
||||
def test_flash_mmap_suspend_with_rom_impl(dut: Dut) -> None:
|
||||
dut.run_all_single_board_cases(timeout=30)
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
CONFIG_SPI_FLASH_ROM_IMPL=y
|
||||
CONFIG_SPI_FLASH_AUTO_SUSPEND=y
|
||||
CONFIG_SPI_FLASH_FORCE_ENABLE_XMC_C_SUSPEND=y
|
||||
Reference in New Issue
Block a user