2022-01-28 18:16:13 +01:00
/*
2025-08-14 16:25:08 +02:00
* SPDX - FileCopyrightText : 2021 - 2025 Espressif Systems ( Shanghai ) CO LTD
2022-01-28 18:16:13 +01:00
*
* SPDX - License - Identifier : Apache - 2.0
*/
# include <stdlib.h>
# include <assert.h>
# include <string.h>
2023-10-17 16:13:32 +02:00
# include <inttypes.h>
2025-08-14 16:25:08 +02:00
# include <stdio.h>
2022-01-28 18:16:13 +01:00
# include <sys/mman.h>
# include <sys/stat.h>
2023-01-20 17:58:52 +01:00
# include <fcntl.h>
2022-01-28 18:16:13 +01:00
# include <unistd.h>
# include <limits.h>
# include <errno.h>
# include "sdkconfig.h"
# include "esp_partition.h"
# include "esp_flash_partitions.h"
# include "esp_private/partition_linux.h"
# include "esp_log.h"
2023-06-20 15:23:12 +02:00
# include "spi_flash_mmap.h"
2022-01-28 18:16:13 +01:00
2025-07-01 13:57:47 +03:00
ESP_LOG_ATTR_TAG ( TAG , " linux_spiflash " ) ;
2023-01-20 17:58:52 +01:00
2022-01-28 18:16:13 +01:00
static void * s_spiflash_mem_file_buf = NULL ;
2023-01-20 17:58:52 +01:00
static int s_spiflash_mem_file_fd = - 1 ;
2022-12-22 08:39:22 +01:00
static const esp_partition_mmap_handle_t s_default_partition_mmap_handle = 0 ;
2023-01-20 17:58:52 +01:00
// input control structure, always contains what was specified by caller
static esp_partition_file_mmap_ctrl_t s_esp_partition_file_mmap_ctrl_input = { 0 } ;
// actual control structure, contains what is actually used by the esp_partition
static esp_partition_file_mmap_ctrl_t s_esp_partition_file_mmap_ctrl_act = { 0 } ;
2022-12-22 08:39:22 +01:00
# ifdef CONFIG_ESP_PARTITION_ENABLE_STATS
2023-01-20 17:58:52 +01:00
// variables holding stats and controlling power-off emulation
static size_t s_esp_partition_stat_read_ops = 0 ;
static size_t s_esp_partition_stat_write_ops = 0 ;
static size_t s_esp_partition_stat_read_bytes = 0 ;
static size_t s_esp_partition_stat_write_bytes = 0 ;
static size_t s_esp_partition_stat_erase_ops = 0 ;
static size_t s_esp_partition_stat_total_time = 0 ;
static size_t s_esp_partition_emulated_power_off_counter = SIZE_MAX ;
static uint8_t s_esp_partition_emulated_power_off_mode = 0 ;
2022-12-22 08:39:22 +01:00
// tracking erase count individually for each emulated sector
2023-01-20 17:58:52 +01:00
static size_t * s_esp_partition_stat_sector_erase_count = NULL ;
2022-12-22 08:39:22 +01:00
// forward declaration of hooks
static void esp_partition_hook_read ( const void * srcAddr , const size_t size ) ;
2023-05-25 14:51:56 +02:00
static bool esp_partition_hook_write ( const void * dstAddr , size_t * size ) ;
static bool esp_partition_hook_erase ( const void * dstAddr , size_t * size ) ;
2022-12-22 08:39:22 +01:00
// redirect hooks to functions
# define ESP_PARTITION_HOOK_READ(srcAddr, size) esp_partition_hook_read(srcAddr, size)
# define ESP_PARTITION_HOOK_WRITE(dstAddr, size) esp_partition_hook_write(dstAddr, size)
# define ESP_PARTITION_HOOK_ERASE(dstAddr, size) esp_partition_hook_erase(dstAddr, size)
# else
// redirect hooks to "do nothing code"
# define ESP_PARTITION_HOOK_READ(srcAddr, size)
# define ESP_PARTITION_HOOK_WRITE(dstAddr, size) true
# define ESP_PARTITION_HOOK_ERASE(dstAddr, size) true
# endif
2022-01-28 18:16:13 +01:00
const char * esp_partition_type_to_str ( const uint32_t type )
{
switch ( type ) {
case PART_TYPE_APP : return " app " ;
case PART_TYPE_DATA : return " data " ;
2024-08-12 12:49:27 +03:00
case PART_TYPE_BOOTLOADER : return " bootloader " ;
case PART_TYPE_PARTITION_TABLE : return " partition_table " ;
2022-01-28 18:16:13 +01:00
default : return " unknown " ;
}
}
const char * esp_partition_subtype_to_str ( const uint32_t type , const uint32_t subtype )
{
switch ( type ) {
2024-08-12 12:49:27 +03:00
case PART_TYPE_BOOTLOADER :
switch ( subtype ) {
case PART_SUBTYPE_BOOTLOADER_PRIMARY : return " primary " ;
case PART_SUBTYPE_BOOTLOADER_OTA : return " ota " ;
2024-07-03 17:28:12 +03:00
case PART_SUBTYPE_BOOTLOADER_RECOVERY : return " recovery " ;
2024-08-12 12:49:27 +03:00
default : return " unknown " ;
}
case PART_TYPE_PARTITION_TABLE :
switch ( subtype ) {
case PART_SUBTYPE_PARTITION_TABLE_PRIMARY : return " primary " ;
case PART_SUBTYPE_PARTITION_TABLE_OTA : return " ota " ;
default : return " unknown " ;
}
2022-01-28 18:16:13 +01:00
case PART_TYPE_APP :
switch ( subtype ) {
case PART_SUBTYPE_FACTORY : return " factory " ;
case PART_SUBTYPE_OTA_FLAG : return " ota_flag " ;
case PART_SUBTYPE_OTA_MASK : return " ota_mask " ;
case PART_SUBTYPE_TEST : return " test " ;
default : return " unknown " ;
}
case PART_TYPE_DATA :
switch ( subtype ) {
case PART_SUBTYPE_DATA_OTA : return " data_ota " ;
case PART_SUBTYPE_DATA_RF : return " data_rf " ;
case PART_SUBTYPE_DATA_WIFI : return " data_wifi " ;
case PART_SUBTYPE_DATA_NVS_KEYS : return " nvs_keys " ;
case PART_SUBTYPE_DATA_EFUSE_EM : return " efuse_em " ;
default : return " unknown " ;
}
default : return " unknown " ;
}
}
2025-08-14 16:25:08 +02:00
// Calculate required emulated flash size from a partition table binary.
// Returns 0 on failure.
static size_t esp_partition_calc_required_flash_size_from_file ( const char * partition_file_path )
{
if ( partition_file_path = = NULL | | partition_file_path [ 0 ] = = ' \0 ' ) {
return 0 ;
}
FILE * fp = fopen ( partition_file_path , " rb " ) ;
if ( fp = = NULL ) {
return 0 ;
}
// Determine file size as an additional lower bound
if ( fseek ( fp , 0L , SEEK_END ) ! = 0 ) {
fclose ( fp ) ;
return 0 ;
}
long file_size = ftell ( fp ) ;
if ( file_size < 0 ) {
fclose ( fp ) ;
return 0 ;
}
if ( fseek ( fp , 0L , SEEK_SET ) ! = 0 ) {
fclose ( fp ) ;
return 0 ;
}
size_t max_end = 0 ;
size_t max_entries = file_size / sizeof ( esp_partition_info_t ) ;
for ( size_t i = 0 ; i < max_entries ; i + + ) {
esp_partition_info_t entry ;
size_t r = fread ( & entry , 1 , sizeof ( entry ) , fp ) ;
if ( r ! = sizeof ( entry ) | | entry . magic ! = ESP_PARTITION_MAGIC ) {
break ;
}
uint32_t end = entry . pos . offset + entry . pos . size ;
if ( end > max_end ) {
max_end = end ;
}
}
fclose ( fp ) ;
// Also ensure the flash holds the partition table itself at its offset
size_t min_from_table_blob = ( size_t ) file_size + ESP_PARTITION_TABLE_OFFSET ;
size_t required = ( max_end > min_from_table_blob ) ? max_end : min_from_table_blob ;
// Round up to emulated sector size
size_t sector = ESP_PARTITION_EMULATED_SECTOR_SIZE ;
size_t rem = required % sector ;
if ( rem ! = 0 ) {
required + = ( sector - rem ) ;
}
return required ;
}
2022-01-28 18:16:13 +01:00
esp_err_t esp_partition_file_mmap ( const uint8_t * * part_desc_addr_start )
{
2023-01-20 17:58:52 +01:00
// temporary file is used only if control structure doesn't specify file name.
bool open_existing_file = false ;
2023-02-28 23:47:11 +01:00
if ( strlen ( s_esp_partition_file_mmap_ctrl_input . flash_file_name ) > 0 ) {
2023-01-20 17:58:52 +01:00
// Open existing file. If size or partition table file were specified, raise errors
2023-02-28 23:47:11 +01:00
if ( s_esp_partition_file_mmap_ctrl_input . flash_file_size > 0 ) {
2023-10-17 16:13:32 +02:00
ESP_LOGE ( TAG , " Flash emulation file size: % " PRIu32 " was specified while together with the file name: %s (illegal). Use file size = 0 " ,
( uint32_t ) s_esp_partition_file_mmap_ctrl_input . flash_file_size ,
2023-02-28 23:47:11 +01:00
s_esp_partition_file_mmap_ctrl_input . flash_file_name ) ;
2023-01-20 17:58:52 +01:00
return ESP_ERR_INVALID_ARG ;
}
2022-01-28 18:16:13 +01:00
2023-02-28 23:47:11 +01:00
if ( strlen ( s_esp_partition_file_mmap_ctrl_input . partition_file_name ) > 0 ) {
2023-01-20 17:58:52 +01:00
ESP_LOGE ( TAG , " Partition file name: %s was specified together with the flash emulation file name: %s (illegal). Use empty partition file name " ,
2023-02-28 23:47:11 +01:00
s_esp_partition_file_mmap_ctrl_input . partition_file_name ,
s_esp_partition_file_mmap_ctrl_input . flash_file_name ) ;
2023-01-20 17:58:52 +01:00
return ESP_ERR_INVALID_ARG ;
}
2022-01-28 18:16:13 +01:00
2023-01-20 17:58:52 +01:00
// copy flash file name to actual control struct
strlcpy ( s_esp_partition_file_mmap_ctrl_act . flash_file_name , s_esp_partition_file_mmap_ctrl_input . flash_file_name , sizeof ( s_esp_partition_file_mmap_ctrl_act . flash_file_name ) ) ;
2022-01-28 18:16:13 +01:00
2023-01-20 17:58:52 +01:00
open_existing_file = true ;
} else {
2025-08-14 16:25:08 +02:00
// name of temporary file and its size is available in s_esp_partition_file_mmap_ctrl.flash_file_name and s_esp_partition_file_mmap_ctrl_input.flash_file_size respectively
2023-01-20 17:58:52 +01:00
bool has_partfile = ( strlen ( s_esp_partition_file_mmap_ctrl_input . partition_file_name ) > 0 ) ;
bool has_len = ( s_esp_partition_file_mmap_ctrl_input . flash_file_size > 0 ) ;
// check if partition file is present, if not, use default
2023-02-28 23:47:11 +01:00
if ( ! has_partfile ) {
2023-03-14 16:58:35 +08:00
strlcpy ( s_esp_partition_file_mmap_ctrl_act . partition_file_name , BUILD_DIR " /partition_table/partition-table.bin " , sizeof ( s_esp_partition_file_mmap_ctrl_act . partition_file_name ) ) ;
2023-01-20 17:58:52 +01:00
} else {
strlcpy ( s_esp_partition_file_mmap_ctrl_act . partition_file_name , s_esp_partition_file_mmap_ctrl_input . partition_file_name , sizeof ( s_esp_partition_file_mmap_ctrl_act . partition_file_name ) ) ;
}
2025-08-14 16:25:08 +02:00
// derive the partition size from the s_esp_partition_file_mmap_ctrl_act.partition_file_name
size_t derived_size = esp_partition_calc_required_flash_size_from_file ( s_esp_partition_file_mmap_ctrl_act . partition_file_name ) ;
// if derived size is zero, use default partition size
s_esp_partition_file_mmap_ctrl_act . flash_file_size = ( derived_size > 0 ) ? derived_size : ESP_PARTITION_DEFAULT_EMULATED_FLASH_SIZE ;
// if the size of the temporary file is specified, check if the given partition size fits within it
if ( has_len & & s_esp_partition_file_mmap_ctrl_input . flash_file_size > derived_size ) {
2023-01-20 17:58:52 +01:00
s_esp_partition_file_mmap_ctrl_act . flash_file_size = s_esp_partition_file_mmap_ctrl_input . flash_file_size ;
}
// specify pattern file name for temporary flash file
strlcpy ( s_esp_partition_file_mmap_ctrl_act . flash_file_name , " /tmp/idf-partition-XXXXXX " , sizeof ( s_esp_partition_file_mmap_ctrl_act . flash_file_name ) ) ;
2022-01-28 18:16:13 +01:00
}
2023-01-20 17:58:52 +01:00
esp_err_t ret = ESP_OK ;
2022-01-28 18:16:13 +01:00
2023-02-28 23:47:11 +01:00
if ( open_existing_file ) {
2022-01-28 18:16:13 +01:00
2023-01-20 17:58:52 +01:00
s_spiflash_mem_file_fd = open ( s_esp_partition_file_mmap_ctrl_act . flash_file_name , O_RDWR ) ;
2022-01-28 18:16:13 +01:00
2023-01-20 17:58:52 +01:00
if ( s_spiflash_mem_file_fd = = - 1 ) {
ESP_LOGE ( TAG , " Failed to open SPI FLASH emulation file %s: %s " , s_esp_partition_file_mmap_ctrl_act . flash_file_name , strerror ( errno ) ) ;
return ESP_ERR_NOT_FOUND ;
}
2022-01-28 18:16:13 +01:00
2023-01-20 17:58:52 +01:00
do {
// seek to the end
off_t size = lseek ( s_spiflash_mem_file_fd , 0L , SEEK_END ) ;
2023-02-28 23:47:11 +01:00
if ( size < 0 ) {
2023-01-20 17:58:52 +01:00
ESP_LOGE ( TAG , " Failed to seek in SPI FLASH emulation file %s: %s " , s_esp_partition_file_mmap_ctrl_act . flash_file_name , strerror ( errno ) ) ;
ret = ESP_ERR_NOT_FINISHED ;
break ;
}
s_esp_partition_file_mmap_ctrl_act . flash_file_size = size ;
// seek to beginning
size = lseek ( s_spiflash_mem_file_fd , 0L , SEEK_SET ) ;
2023-02-28 23:47:11 +01:00
if ( size < 0 ) {
2023-01-20 17:58:52 +01:00
ESP_LOGE ( TAG , " Failed to seek in SPI FLASH emulation file %s: %s " , s_esp_partition_file_mmap_ctrl_act . flash_file_name , strerror ( errno ) ) ;
ret = ESP_ERR_NOT_FINISHED ;
break ;
}
//create memory-mapping for the flash holder file
if ( ( s_spiflash_mem_file_buf = mmap ( NULL , s_esp_partition_file_mmap_ctrl_act . flash_file_size , PROT_READ | PROT_WRITE , MAP_SHARED , s_spiflash_mem_file_fd , 0 ) ) = = MAP_FAILED ) {
ESP_LOGE ( TAG , " Failed to mmap() SPI FLASH memory emulation file %s: %s " , s_esp_partition_file_mmap_ctrl_act . flash_file_name , strerror ( errno ) ) ;
ret = ESP_ERR_NOT_FINISHED ;
break ;
}
2023-02-28 23:47:11 +01:00
} while ( false ) ;
2023-01-20 17:58:52 +01:00
} else {
//create temporary file to hold complete SPIFLASH size
s_spiflash_mem_file_fd = mkstemp ( s_esp_partition_file_mmap_ctrl_act . flash_file_name ) ;
2022-01-28 18:16:13 +01:00
2023-01-20 17:58:52 +01:00
if ( s_spiflash_mem_file_fd = = - 1 ) {
ESP_LOGE ( TAG , " Failed to create SPI FLASH emulation file %s: %s " , s_esp_partition_file_mmap_ctrl_act . flash_file_name , strerror ( errno ) ) ;
return ESP_ERR_NOT_FINISHED ;
}
2022-01-28 18:16:13 +01:00
2023-01-20 17:58:52 +01:00
do {
// resize file
if ( ftruncate ( s_spiflash_mem_file_fd , s_esp_partition_file_mmap_ctrl_act . flash_file_size ) ! = 0 ) {
ESP_LOGE ( TAG , " Failed to set size of SPI FLASH memory emulation file %s: %s " , s_esp_partition_file_mmap_ctrl_act . flash_file_name , strerror ( errno ) ) ;
ret = ESP_ERR_INVALID_SIZE ;
break ;
}
2023-10-17 16:13:32 +02:00
ESP_LOGV ( TAG , " SPIFLASH memory emulation file created: %s (size: % " PRIu32 " B) " , s_esp_partition_file_mmap_ctrl_act . flash_file_name , ( uint32_t ) s_esp_partition_file_mmap_ctrl_act . flash_file_size ) ;
2023-01-20 17:58:52 +01:00
// create memory-mapping for the flash holder file
if ( ( s_spiflash_mem_file_buf = mmap ( NULL , s_esp_partition_file_mmap_ctrl_act . flash_file_size , PROT_READ | PROT_WRITE , MAP_SHARED , s_spiflash_mem_file_fd , 0 ) ) = = MAP_FAILED ) {
ESP_LOGE ( TAG , " Failed to mmap() SPI FLASH memory emulation file %s: %s " , s_esp_partition_file_mmap_ctrl_act . flash_file_name , strerror ( errno ) ) ;
ret = ESP_ERR_NO_MEM ;
break ;
}
// initialize whole range with bit-1 (NOR FLASH default)
memset ( s_spiflash_mem_file_buf , 0xFF , s_esp_partition_file_mmap_ctrl_act . flash_file_size ) ;
// upload partition table to the mmap file at real offset as in SPIFLASH
FILE * f_partition_table = fopen ( s_esp_partition_file_mmap_ctrl_act . partition_file_name , " r+ " ) ;
if ( f_partition_table = = NULL ) {
ESP_LOGE ( TAG , " Failed to open partition table file %s: %s " , s_esp_partition_file_mmap_ctrl_act . partition_file_name , strerror ( errno ) ) ;
ret = ESP_ERR_NOT_FOUND ;
break ;
}
if ( fseek ( f_partition_table , 0L , SEEK_END ) ! = 0 ) {
ESP_LOGE ( TAG , " Failed to seek in partition table file %s: %s " , s_esp_partition_file_mmap_ctrl_act . partition_file_name , strerror ( errno ) ) ;
ret = ESP_ERR_INVALID_SIZE ;
2024-06-27 15:19:26 +03:00
fclose ( f_partition_table ) ;
2023-01-20 17:58:52 +01:00
break ;
}
int partition_table_file_size = ftell ( f_partition_table ) ;
ESP_LOGV ( TAG , " Using partition table file %s (size: %d B): " , s_esp_partition_file_mmap_ctrl_act . partition_file_name , partition_table_file_size ) ;
// check whether partition table fits into the memory mapped file
2023-02-28 23:47:11 +01:00
if ( partition_table_file_size + ESP_PARTITION_TABLE_OFFSET > s_esp_partition_file_mmap_ctrl_act . flash_file_size ) {
2023-10-17 16:13:32 +02:00
ESP_LOGE ( TAG , " Flash file: %s (size: % " PRIu32 " B) cannot hold partition table requiring %d B " ,
2023-02-28 23:47:11 +01:00
s_esp_partition_file_mmap_ctrl_act . flash_file_name ,
2023-10-17 16:13:32 +02:00
( uint32_t ) s_esp_partition_file_mmap_ctrl_act . flash_file_size ,
( int ) ( partition_table_file_size + ESP_PARTITION_TABLE_OFFSET ) ) ;
2023-01-20 17:58:52 +01:00
ret = ESP_ERR_INVALID_SIZE ;
2024-06-27 15:19:26 +03:00
fclose ( f_partition_table ) ;
2023-01-20 17:58:52 +01:00
break ;
}
//copy partition table from the file to emulated SPIFLASH memory space
if ( fseek ( f_partition_table , 0L , SEEK_SET ) ! = 0 ) {
ESP_LOGE ( TAG , " Failed to seek in partition table file %s: %s " , s_esp_partition_file_mmap_ctrl_act . partition_file_name , strerror ( errno ) ) ;
ret = ESP_ERR_INVALID_SIZE ;
2024-06-27 15:19:26 +03:00
fclose ( f_partition_table ) ;
2023-01-20 17:58:52 +01:00
break ;
}
uint8_t * part_table_in_spiflash = s_spiflash_mem_file_buf + ESP_PARTITION_TABLE_OFFSET ;
size_t res = fread ( part_table_in_spiflash , 1 , partition_table_file_size , f_partition_table ) ;
fclose ( f_partition_table ) ;
if ( res ! = partition_table_file_size ) {
ESP_LOGE ( TAG , " Failed to read partition table file %s " , s_esp_partition_file_mmap_ctrl_act . partition_file_name ) ;
ret = ESP_ERR_INVALID_STATE ;
break ;
}
2023-02-28 23:47:11 +01:00
} while ( false ) ;
2023-01-20 17:58:52 +01:00
}
if ( ret ! = ESP_OK ) {
2023-02-28 23:47:11 +01:00
if ( close ( s_spiflash_mem_file_fd ) ) {
2023-01-20 17:58:52 +01:00
ESP_LOGE ( TAG , " Failed to close() SPIFLASH memory emulation file: %s " , strerror ( errno ) ) ;
}
s_spiflash_mem_file_fd = - 1 ;
2022-01-28 18:16:13 +01:00
2023-01-20 17:58:52 +01:00
return ret ;
2022-01-28 18:16:13 +01:00
}
# ifdef CONFIG_LOG_DEFAULT_LEVEL_VERBOSE
2023-01-20 17:58:52 +01:00
uint8_t * part_ptr = s_spiflash_mem_file_buf + ESP_PARTITION_TABLE_OFFSET ;
2022-01-28 18:16:13 +01:00
ESP_LOGV ( TAG , " " ) ;
2024-03-28 09:14:05 +01:00
ESP_LOGV ( TAG , " Partition table successfully imported, partitions found: " ) ;
2022-01-28 18:16:13 +01:00
2023-01-20 17:58:52 +01:00
while ( true ) {
2022-01-28 18:16:13 +01:00
esp_partition_info_t * p_part_item = ( esp_partition_info_t * ) part_ptr ;
if ( p_part_item - > magic ! = ESP_PARTITION_MAGIC ) {
break ;
}
ESP_LOGV ( TAG , " -------------- " ) ;
ESP_LOGV ( TAG , " label: %s " , p_part_item - > label ) ;
ESP_LOGV ( TAG , " type: %s " , esp_partition_type_to_str ( p_part_item - > type ) ) ;
ESP_LOGV ( TAG , " subtype: %s " , esp_partition_subtype_to_str ( p_part_item - > type , p_part_item - > subtype ) ) ;
2023-10-17 16:13:32 +02:00
ESP_LOGV ( TAG , " offset: 0x%08 " PRIX32 , ( uint32_t ) p_part_item - > pos . offset ) ;
ESP_LOGV ( TAG , " size: % " PRIu32 , ( uint32_t ) p_part_item - > pos . size ) ;
ESP_LOGV ( TAG , " flags: % " PRIu32 , ( uint32_t ) p_part_item - > flags ) ;
2022-01-28 18:16:13 +01:00
part_ptr + = sizeof ( esp_partition_info_t ) ;
}
ESP_LOGV ( TAG , " " ) ;
# endif
2023-01-20 17:58:52 +01:00
# ifdef CONFIG_ESP_PARTITION_ENABLE_STATS
free ( s_esp_partition_stat_sector_erase_count ) ;
s_esp_partition_stat_sector_erase_count = malloc ( sizeof ( size_t ) * s_esp_partition_file_mmap_ctrl_act . flash_file_size / ESP_PARTITION_EMULATED_SECTOR_SIZE ) ;
# endif
2022-01-28 18:16:13 +01:00
//return mmapped file starting address
* part_desc_addr_start = s_spiflash_mem_file_buf ;
2023-01-20 17:58:52 +01:00
// clear input control structure
memset ( & s_esp_partition_file_mmap_ctrl_input , 0 , sizeof ( s_esp_partition_file_mmap_ctrl_input ) ) ;
2022-01-28 18:16:13 +01:00
return ESP_OK ;
}
2023-01-17 11:44:14 +08:00
esp_err_t esp_partition_file_munmap ( void )
2022-01-28 18:16:13 +01:00
{
if ( s_spiflash_mem_file_buf = = NULL ) {
return ESP_ERR_NO_MEM ;
}
2023-01-20 17:58:52 +01:00
if ( s_esp_partition_file_mmap_ctrl_act . flash_file_size = = 0 ) {
2022-01-28 18:16:13 +01:00
return ESP_ERR_INVALID_SIZE ;
}
2023-02-28 23:47:11 +01:00
if ( s_spiflash_mem_file_fd = = - 1 ) {
2023-01-20 17:58:52 +01:00
return ESP_ERR_NOT_FOUND ;
}
2022-01-28 18:16:13 +01:00
2023-11-22 11:11:04 +01:00
esp_partition_unload_all ( ) ;
2023-01-20 17:58:52 +01:00
# ifdef CONFIG_ESP_PARTITION_ENABLE_STATS
free ( s_esp_partition_stat_sector_erase_count ) ;
s_esp_partition_stat_sector_erase_count = NULL ;
# endif
// unmap the flash emulation memory file
if ( munmap ( s_spiflash_mem_file_buf , s_esp_partition_file_mmap_ctrl_act . flash_file_size ) ! = 0 ) {
ESP_LOGE ( TAG , " Failed to munmap() SPIFLASH memory emulation file %s: %s " , s_esp_partition_file_mmap_ctrl_act . flash_file_name , strerror ( errno ) ) ;
2022-01-28 18:16:13 +01:00
return ESP_ERR_INVALID_RESPONSE ;
}
2023-01-20 17:58:52 +01:00
// close memory mapped file
2023-02-28 23:47:11 +01:00
if ( close ( s_spiflash_mem_file_fd ) ) {
2023-01-20 17:58:52 +01:00
ESP_LOGE ( TAG , " Failed to close() SPIFLASH memory emulation file %s: %s " , s_esp_partition_file_mmap_ctrl_act . flash_file_name , strerror ( errno ) ) ;
return ESP_ERR_INVALID_RESPONSE ;
}
2023-02-28 23:47:11 +01:00
if ( s_esp_partition_file_mmap_ctrl_input . remove_dump ) {
2023-01-20 17:58:52 +01:00
// delete spi flash file
2023-02-28 23:47:11 +01:00
if ( remove ( s_esp_partition_file_mmap_ctrl_act . flash_file_name ) ! = 0 ) {
2023-01-20 17:58:52 +01:00
ESP_LOGE ( TAG , " Failed to remove() SPI FLASH memory emulation file %s: %s " , s_esp_partition_file_mmap_ctrl_act . flash_file_name , strerror ( errno ) ) ;
return ESP_ERR_INVALID_RESPONSE ;
}
}
// cleanup
memset ( & s_esp_partition_file_mmap_ctrl_act , 0 , sizeof ( s_esp_partition_file_mmap_ctrl_act ) ) ;
2022-01-28 18:16:13 +01:00
s_spiflash_mem_file_buf = NULL ;
2023-01-20 17:58:52 +01:00
s_spiflash_mem_file_fd = - 1 ;
2022-01-28 18:16:13 +01:00
return ESP_OK ;
}
esp_err_t esp_partition_write ( const esp_partition_t * partition , size_t dst_offset , const void * src , size_t size )
{
2025-08-14 16:25:08 +02:00
assert ( partition ! = NULL & & s_spiflash_mem_file_buf ! = NULL & & src ! = NULL ) ;
2022-01-28 18:16:13 +01:00
2023-07-17 11:59:28 +02:00
if ( partition - > readonly ) {
return ESP_ERR_NOT_ALLOWED ;
}
2022-01-28 18:16:13 +01:00
if ( partition - > encrypted ) {
2023-01-20 17:58:52 +01:00
return ESP_ERR_NOT_SUPPORTED ;
2022-01-28 18:16:13 +01:00
}
if ( dst_offset > partition - > size ) {
return ESP_ERR_INVALID_ARG ;
}
if ( dst_offset + size > partition - > size ) {
return ESP_ERR_INVALID_SIZE ;
}
2025-08-14 16:25:08 +02:00
// Ensure write stays within mapped flash file size
if ( s_esp_partition_file_mmap_ctrl_act . flash_file_size > 0 ) {
size_t start = ( size_t ) partition - > address + dst_offset ;
size_t max_len = s_esp_partition_file_mmap_ctrl_act . flash_file_size ;
if ( ( start > max_len ) | | ( ( size + start ) > max_len ) ) {
return ESP_ERR_INVALID_SIZE ;
}
}
2022-01-28 18:16:13 +01:00
void * dst_addr = s_spiflash_mem_file_buf + partition - > address + dst_offset ;
2023-10-17 16:13:32 +02:00
ESP_LOGV ( TAG , " esp_partition_write(): partition=%s dst_offset=% " PRIu32 " src=%p size=% " PRIu32 " (real dst address: %p) " , partition - > label , ( uint32_t ) dst_offset , src , ( uint32_t ) size , dst_addr ) ;
2022-01-28 18:16:13 +01:00
2023-05-25 14:51:56 +02:00
// local size, can be modified by the write hook in case of simulated power-off
size_t new_size = size ;
esp_err_t ret = ESP_OK ;
2023-01-20 17:58:52 +01:00
// hook gathers statistics and can emulate power-off
2023-05-25 14:51:56 +02:00
// in case of power - off it decreases new_size to the number of bytes written
2024-03-28 09:14:05 +01:00
// before power event occurred
2023-05-25 14:51:56 +02:00
if ( ! ESP_PARTITION_HOOK_WRITE ( dst_addr , & new_size ) ) {
2023-06-20 15:23:12 +02:00
ret = ESP_ERR_FLASH_OP_FAIL ;
2022-12-22 08:39:22 +01:00
}
2023-05-25 14:51:56 +02:00
for ( size_t x = 0 ; x < new_size ; x + + ) {
2024-02-27 12:38:10 +01:00
# ifdef CONFIG_ESP_PARTITION_ERASE_CHECK
2023-05-25 14:51:56 +02:00
// Check if address to be written was erased first
if ( ( ~ ( ( uint8_t * ) dst_addr ) [ x ] & ( ( uint8_t * ) src ) [ x ] ) ! = 0 ) {
ESP_LOGW ( TAG , " invalid flash operation detected " ) ;
2023-06-20 15:23:12 +02:00
ret = ESP_ERR_FLASH_OP_FAIL ;
2023-05-25 14:51:56 +02:00
break ;
}
2024-02-27 12:38:10 +01:00
# endif // CONFIG_ESP_PARTITION_ERASE_CHECK
2023-05-25 14:51:56 +02:00
// AND with destination byte (to emulate real NOR FLASH behavior)
( ( uint8_t * ) dst_addr ) [ x ] & = ( ( uint8_t * ) src ) [ x ] ;
2022-01-28 18:16:13 +01:00
}
2023-05-25 14:51:56 +02:00
return ret ;
2022-01-28 18:16:13 +01:00
}
esp_err_t esp_partition_read ( const esp_partition_t * partition , size_t src_offset , void * dst , size_t size )
{
2025-08-14 16:25:08 +02:00
assert ( partition ! = NULL & & s_spiflash_mem_file_buf ! = NULL & & dst ! = NULL ) ;
2022-01-28 18:16:13 +01:00
if ( partition - > encrypted ) {
return ESP_ERR_NOT_SUPPORTED ;
}
if ( src_offset > partition - > size ) {
return ESP_ERR_INVALID_ARG ;
}
if ( src_offset + size > partition - > size ) {
return ESP_ERR_INVALID_SIZE ;
}
2025-08-14 16:25:08 +02:00
// Ensure read stays within mapped flash file size
if ( s_esp_partition_file_mmap_ctrl_act . flash_file_size > 0 ) {
size_t start = ( size_t ) partition - > address + src_offset ;
size_t max_len = s_esp_partition_file_mmap_ctrl_act . flash_file_size ;
if ( ( start > max_len ) | | ( ( size + start ) > max_len ) ) {
return ESP_ERR_INVALID_SIZE ;
}
}
2022-01-28 18:16:13 +01:00
void * src_addr = s_spiflash_mem_file_buf + partition - > address + src_offset ;
2023-10-17 16:13:32 +02:00
ESP_LOGV ( TAG , " esp_partition_read(): partition=%s src_offset=% " PRIu32 " dst=%p size=% " PRIu32 " (real src address: %p) " , partition - > label , ( uint32_t ) src_offset , dst , ( uint32_t ) size , src_addr ) ;
2022-01-28 18:16:13 +01:00
memcpy ( dst , src_addr , size ) ;
2022-12-22 08:39:22 +01:00
ESP_PARTITION_HOOK_READ ( src_addr , size ) ; // statistics
2022-01-28 18:16:13 +01:00
return ESP_OK ;
}
esp_err_t esp_partition_read_raw ( const esp_partition_t * partition , size_t src_offset , void * dst , size_t size )
{
ESP_LOGV ( TAG , " esp_partition_read_raw(): calling esp_partition_read() " ) ;
return esp_partition_read ( partition , src_offset , dst , size ) ;
}
esp_err_t esp_partition_write_raw ( const esp_partition_t * partition , size_t dst_offset , const void * src , size_t size )
{
ESP_LOGV ( TAG , " esp_partition_write_raw(): calling esp_partition_write() " ) ;
return esp_partition_write ( partition , dst_offset , src , size ) ;
}
esp_err_t esp_partition_erase_range ( const esp_partition_t * partition , size_t offset , size_t size )
{
2025-08-14 16:25:08 +02:00
assert ( partition ! = NULL & & s_spiflash_mem_file_buf ! = NULL ) ;
2022-01-28 18:16:13 +01:00
2023-07-17 11:59:28 +02:00
if ( partition - > readonly ) {
return ESP_ERR_NOT_ALLOWED ;
}
2022-10-14 14:15:32 +02:00
if ( offset > partition - > size | | offset % partition - > erase_size ! = 0 ) {
2022-01-28 18:16:13 +01:00
return ESP_ERR_INVALID_ARG ;
}
2022-10-14 14:15:32 +02:00
if ( offset + size > partition - > size | | size % partition - > erase_size ! = 0 ) {
2022-01-28 18:16:13 +01:00
return ESP_ERR_INVALID_SIZE ;
}
2025-08-14 16:25:08 +02:00
// Ensure erase stays within mapped flash file size
if ( s_esp_partition_file_mmap_ctrl_act . flash_file_size > 0 ) {
size_t start = ( size_t ) partition - > address + offset ;
size_t max_len = s_esp_partition_file_mmap_ctrl_act . flash_file_size ;
if ( ( start > max_len ) | | ( ( size + start ) > max_len ) ) {
return ESP_ERR_INVALID_SIZE ;
}
}
2022-01-28 18:16:13 +01:00
void * target_addr = s_spiflash_mem_file_buf + partition - > address + offset ;
2023-10-17 16:13:32 +02:00
ESP_LOGV ( TAG , " esp_partition_erase_range(): partition=%s offset=% " PRIu32 " size=% " PRIu32 " (real target address: %p) " , partition - > label , ( uint32_t ) offset , ( uint32_t ) size , target_addr ) ;
2022-01-28 18:16:13 +01:00
2023-05-25 14:51:56 +02:00
// local size to be potentially updated by the hook in case of power-off event
size_t new_size = size ;
2023-01-20 17:58:52 +01:00
// hook gathers statistics and can emulate power-off
2023-05-25 14:51:56 +02:00
esp_err_t ret = ESP_OK ;
if ( ! ESP_PARTITION_HOOK_ERASE ( target_addr , & new_size ) ) {
2023-06-20 15:23:12 +02:00
ret = ESP_ERR_FLASH_OP_FAIL ;
2022-12-22 08:39:22 +01:00
}
2022-01-28 18:16:13 +01:00
//set all bits to 1 (NOR FLASH default)
2023-05-25 14:51:56 +02:00
memset ( target_addr , 0xFF , new_size ) ;
2022-01-28 18:16:13 +01:00
2023-05-25 14:51:56 +02:00
return ret ;
2022-01-28 18:16:13 +01:00
}
2022-12-22 08:39:22 +01:00
/*
* Exposes direct pointer to the memory mapped file created by esp_partition_file_mmap
* No address alignment is performed
* Default handle is always returned
* Returns :
* ESP_ERR_INVALID_ARG - offset exceeds size of partition
* ESP_ERR_INVALID_SIZE - address range defined by offset + size is beyond the size of partition
* ESP_ERR_NOT_SUPPORTED - flash_chip of partition is not NULL
* ESP_OK - calculated out parameters hold pointer to the requested memory area and default handle respectively
*/
esp_err_t esp_partition_mmap ( const esp_partition_t * partition , size_t offset , size_t size ,
esp_partition_mmap_memory_t memory ,
const void * * out_ptr , esp_partition_mmap_handle_t * out_handle )
{
2023-10-17 16:13:32 +02:00
ESP_LOGV ( TAG , " esp_partition_mmap(): partition=%s offset=% " PRIu32 " size=% " PRIu32 " " , partition - > label , ( uint32_t ) offset , ( uint32_t ) size ) ;
2022-12-22 08:39:22 +01:00
assert ( partition ! = NULL ) ;
if ( offset > partition - > size ) {
return ESP_ERR_INVALID_ARG ;
}
if ( offset + size > partition - > size ) {
return ESP_ERR_INVALID_SIZE ;
}
if ( partition - > flash_chip ! = NULL ) {
return ESP_ERR_NOT_SUPPORTED ;
}
// required starting address in flash aka offset from the flash beginning
size_t req_flash_addr = ( size_t ) ( partition - > address ) + offset ;
esp_err_t rc = ESP_OK ;
// check if memory mapped file is already present, if not, map it now
if ( s_spiflash_mem_file_buf = = NULL ) {
uint8_t * part_desc_addr_start = NULL ;
rc = esp_partition_file_mmap ( ( const uint8_t * * ) & part_desc_addr_start ) ;
}
// adjust memory mapped pointer to the required offset
if ( rc = = ESP_OK ) {
* out_ptr = ( void * ) ( s_spiflash_mem_file_buf + req_flash_addr ) ;
* out_handle = s_default_partition_mmap_handle ;
} else {
2023-01-20 17:58:52 +01:00
* out_ptr = NULL ;
2022-12-22 08:39:22 +01:00
* out_handle = 0 ;
}
return rc ;
}
// Intentionally does nothing.
2023-01-20 17:58:52 +01:00
void esp_partition_munmap ( esp_partition_mmap_handle_t handle __attribute__ ( ( unused ) ) )
2022-12-22 08:39:22 +01:00
{
2023-01-20 17:58:52 +01:00
}
2023-02-28 23:47:11 +01:00
esp_partition_file_mmap_ctrl_t * esp_partition_get_file_mmap_ctrl_input ( void )
2023-01-20 17:58:52 +01:00
{
return & s_esp_partition_file_mmap_ctrl_input ;
}
2023-02-28 23:47:11 +01:00
esp_partition_file_mmap_ctrl_t * esp_partition_get_file_mmap_ctrl_act ( void )
2023-01-20 17:58:52 +01:00
{
return & s_esp_partition_file_mmap_ctrl_act ;
2022-12-22 08:39:22 +01:00
}
2024-09-26 13:01:24 +08:00
uint32_t esp_partition_get_main_flash_sector_size ( void )
{
return ESP_PARTITION_EMULATED_SECTOR_SIZE ;
}
2022-12-22 08:39:22 +01:00
# ifdef CONFIG_ESP_PARTITION_ENABLE_STATS
2024-03-28 09:14:05 +01:00
// timing data for ESP8266, 160MHz CPU frequency, 80MHz flash frequency
2022-12-22 08:39:22 +01:00
// all values in microseconds
// values are for block sizes starting at 4 bytes and going up to 4096 bytes
static size_t s_esp_partition_stat_read_times [ ] = { 7 , 5 , 6 , 7 , 11 , 18 , 32 , 60 , 118 , 231 , 459 } ;
static size_t s_esp_partition_stat_write_times [ ] = { 19 , 23 , 35 , 57 , 106 , 205 , 417 , 814 , 1622 , 3200 , 6367 } ;
static size_t s_esp_partition_stat_block_erase_time = 37142 ;
static size_t esp_partition_stat_time_interpolate ( uint32_t bytes , size_t * lut )
{
const int lut_size = sizeof ( s_esp_partition_stat_read_times ) / sizeof ( s_esp_partition_stat_read_times [ 0 ] ) ;
int lz = __builtin_clz ( bytes / 4 ) ;
int log_size = 32 - lz ;
size_t x2 = 1 < < ( log_size + 2 ) ;
size_t upper_index = ( log_size < lut_size - 1 ) ? log_size : lut_size - 1 ;
size_t y2 = lut [ upper_index ] ;
size_t x1 = 1 < < ( log_size + 1 ) ;
size_t y1 = lut [ log_size - 1 ] ;
return ( bytes - x1 ) * ( y2 - y1 ) / ( x2 - x1 ) + y1 ;
}
// Registers read access statistics of emulated SPI FLASH device (Linux host)
2023-01-20 17:58:52 +01:00
// Function increases nmuber of read operations, accumulates number of read bytes
2022-12-22 08:39:22 +01:00
// and accumulates emulated read operation time (size dependent)
static void esp_partition_hook_read ( const void * srcAddr , const size_t size )
{
ESP_LOGV ( TAG , " esp_partition_hook_read() " ) ;
// stats
+ + s_esp_partition_stat_read_ops ;
s_esp_partition_stat_read_bytes + = size ;
s_esp_partition_stat_total_time + = esp_partition_stat_time_interpolate ( ( uint32_t ) size , s_esp_partition_stat_read_times ) ;
}
// Registers write access statistics of emulated SPI FLASH device (Linux host)
2023-05-25 14:51:56 +02:00
// If enabled by the esp_partition_fail_after, function emulates power-off event during write operations by
2023-01-20 17:58:52 +01:00
// decrementing the s_esp_partition_emulated_power_off_counter for each 4 bytes written
2023-05-25 14:51:56 +02:00
// If zero threshold is reached, false is returned. In this case the size parameter contains number of successfully written bytes
2022-12-22 08:39:22 +01:00
// Else the function increases nmuber of write operations, accumulates number
// of bytes written and accumulates emulated write operation time (size dependent) and returns true.
2023-05-25 14:51:56 +02:00
static bool esp_partition_hook_write ( const void * dstAddr , size_t * size )
2022-12-22 08:39:22 +01:00
{
2023-01-20 17:58:52 +01:00
ESP_LOGV ( TAG , " %s " , __FUNCTION__ ) ;
2022-12-22 08:39:22 +01:00
2023-01-20 17:58:52 +01:00
bool ret_val = true ;
// one power down cycle per 4 bytes written
2023-05-25 14:51:56 +02:00
size_t write_cycles = * size / 4 ;
2023-01-20 17:58:52 +01:00
// check whether power off simulation is active for write
2023-02-28 23:47:11 +01:00
if ( s_esp_partition_emulated_power_off_counter ! = SIZE_MAX & &
2023-05-25 14:51:56 +02:00
ESP_PARTITION_FAIL_AFTER_MODE_WRITE ) {
2023-01-20 17:58:52 +01:00
// check if power down happens during this call
2023-05-25 14:51:56 +02:00
if ( s_esp_partition_emulated_power_off_counter > write_cycles ) {
2023-01-20 17:58:52 +01:00
// OK
s_esp_partition_emulated_power_off_counter - = write_cycles ;
} else {
2023-05-25 14:51:56 +02:00
// failure in this call
// update number of bytes written to the in/out parameter
* size = s_esp_partition_emulated_power_off_counter * 4 ;
// disable power on cycles for further calls
s_esp_partition_emulated_power_off_counter = SIZE_MAX ;
2023-01-20 17:58:52 +01:00
// final result value will be false
ret_val = false ;
}
}
2023-05-25 14:51:56 +02:00
if ( ret_val ) {
// stats
+ + s_esp_partition_stat_write_ops ;
s_esp_partition_stat_write_bytes + = write_cycles * 4 ;
s_esp_partition_stat_total_time + = esp_partition_stat_time_interpolate ( ( uint32_t ) ( * size ) , s_esp_partition_stat_write_times ) ;
}
2022-12-22 08:39:22 +01:00
2023-01-20 17:58:52 +01:00
return ret_val ;
2022-12-22 08:39:22 +01:00
}
// Registers erase access statistics of emulated SPI FLASH device (Linux host)
2023-05-25 14:51:56 +02:00
// If enabled by 'esp_partition_fail_after' parameter, the function emulates a power-off event during erase
// operation by decrementing the s_esp_partition_emulated_power_off_counterpower for each erased virtual sector.
// If zero threshold is reached, false is returned. In out parameter size is updated with number of bytes erased until power-off
2022-12-22 08:39:22 +01:00
// Else, for statistics purpose, the impacted virtual sectors are identified based on
// ESP_PARTITION_EMULATED_SECTOR_SIZE and their respective counts of erase operations are incremented
// Total number of erase operations is increased by the number of impacted virtual sectors
2023-05-25 14:51:56 +02:00
static bool esp_partition_hook_erase ( const void * dstAddr , size_t * size )
2022-12-22 08:39:22 +01:00
{
2023-01-20 17:58:52 +01:00
ESP_LOGV ( TAG , " %s " , __FUNCTION__ ) ;
2022-12-22 08:39:22 +01:00
2023-05-25 14:51:56 +02:00
if ( * size = = 0 ) {
2022-12-22 08:39:22 +01:00
return true ;
}
// cycle over virtual sectors
ptrdiff_t offset = dstAddr - s_spiflash_mem_file_buf ;
size_t first_sector_idx = offset / ESP_PARTITION_EMULATED_SECTOR_SIZE ;
2023-05-25 14:51:56 +02:00
size_t last_sector_idx = ( offset + * size - 1 ) / ESP_PARTITION_EMULATED_SECTOR_SIZE ;
2022-12-22 08:39:22 +01:00
size_t sector_count = 1 + last_sector_idx - first_sector_idx ;
2023-01-20 17:58:52 +01:00
bool ret_val = true ;
// check whether power off simulation is active for erase
2023-02-28 23:47:11 +01:00
if ( s_esp_partition_emulated_power_off_counter ! = SIZE_MAX & &
2023-05-25 14:51:56 +02:00
ESP_PARTITION_FAIL_AFTER_MODE_ERASE ) {
2023-01-20 17:58:52 +01:00
// check if power down happens during this call
2023-05-25 14:51:56 +02:00
if ( s_esp_partition_emulated_power_off_counter > sector_count ) {
2023-01-20 17:58:52 +01:00
// OK
s_esp_partition_emulated_power_off_counter - = sector_count ;
} else {
2023-05-25 14:51:56 +02:00
// failure in this call - reduce sector_count to the number of remaining power on cycles
2023-01-20 17:58:52 +01:00
sector_count = s_esp_partition_emulated_power_off_counter ;
2023-05-25 14:51:56 +02:00
// disable power on cycles for further calls
s_esp_partition_emulated_power_off_counter = SIZE_MAX ;
2023-06-20 15:23:12 +02:00
// update number of bytes to be really erased before power-off event
* size = sector_count * ESP_PARTITION_EMULATED_SECTOR_SIZE ;
2023-01-20 17:58:52 +01:00
// final result value will be false
ret_val = false ;
2022-12-22 08:39:22 +01:00
}
2023-01-20 17:58:52 +01:00
}
2022-12-22 08:39:22 +01:00
2024-06-27 15:19:26 +03:00
// update statistics for all sectors until power down cycle
2023-01-20 17:58:52 +01:00
for ( size_t sector_index = first_sector_idx ; sector_index < first_sector_idx + sector_count ; sector_index + + ) {
2022-12-22 08:39:22 +01:00
+ + s_esp_partition_stat_erase_ops ;
s_esp_partition_stat_sector_erase_count [ sector_index ] + + ;
s_esp_partition_stat_total_time + = s_esp_partition_stat_block_erase_time ;
}
2023-01-20 17:58:52 +01:00
return ret_val ;
2022-12-22 08:39:22 +01:00
}
2023-01-17 11:44:14 +08:00
void esp_partition_clear_stats ( void )
2022-12-22 08:39:22 +01:00
{
s_esp_partition_stat_read_bytes = 0 ;
s_esp_partition_stat_write_bytes = 0 ;
s_esp_partition_stat_erase_ops = 0 ;
s_esp_partition_stat_read_ops = 0 ;
s_esp_partition_stat_write_ops = 0 ;
s_esp_partition_stat_total_time = 0 ;
2023-01-20 17:58:52 +01:00
memset ( s_esp_partition_stat_sector_erase_count , 0 , sizeof ( size_t ) * s_esp_partition_file_mmap_ctrl_act . flash_file_size / ESP_PARTITION_EMULATED_SECTOR_SIZE ) ;
2022-12-22 08:39:22 +01:00
}
2023-01-17 11:44:14 +08:00
size_t esp_partition_get_read_ops ( void )
2022-12-22 08:39:22 +01:00
{
return s_esp_partition_stat_read_ops ;
}
2023-01-17 11:44:14 +08:00
size_t esp_partition_get_write_ops ( void )
2022-12-22 08:39:22 +01:00
{
return s_esp_partition_stat_write_ops ;
}
2023-01-17 11:44:14 +08:00
size_t esp_partition_get_erase_ops ( void )
2022-12-22 08:39:22 +01:00
{
return s_esp_partition_stat_erase_ops ;
}
2023-01-17 11:44:14 +08:00
size_t esp_partition_get_read_bytes ( void )
2022-12-22 08:39:22 +01:00
{
return s_esp_partition_stat_read_bytes ;
}
2023-01-17 11:44:14 +08:00
size_t esp_partition_get_write_bytes ( void )
2022-12-22 08:39:22 +01:00
{
return s_esp_partition_stat_write_bytes ;
}
2023-01-17 11:44:14 +08:00
size_t esp_partition_get_total_time ( void )
2022-12-22 08:39:22 +01:00
{
return s_esp_partition_stat_total_time ;
}
2023-01-20 17:58:52 +01:00
void esp_partition_fail_after ( size_t count , uint8_t mode )
2022-12-22 08:39:22 +01:00
{
2023-01-20 17:58:52 +01:00
s_esp_partition_emulated_power_off_counter = count ;
s_esp_partition_emulated_power_off_mode = mode ;
2022-12-22 08:39:22 +01:00
}
size_t esp_partition_get_sector_erase_count ( size_t sector )
{
return s_esp_partition_stat_sector_erase_count [ sector ] ;
}
# endif