mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
Storage: ESP Partition extended options for the SPI Flash emulation
Various extensions and fixes to improve Linux target SPI Flash emulation. Used by the host tests
This commit is contained in:
parent
370e250072
commit
e9e388a085
@ -21,6 +21,16 @@ idf_component_register(SRCS "${srcs}"
|
||||
REQUIRES ${reqs}
|
||||
PRIV_REQUIRES ${priv_reqs})
|
||||
|
||||
if(${target} STREQUAL "linux")
|
||||
# link bsd library for strlcpy
|
||||
find_library(LIB_BSD bsd)
|
||||
if(LIB_BSD)
|
||||
target_link_libraries(${COMPONENT_LIB} PRIVATE ${LIB_BSD})
|
||||
elseif(NOT CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin")
|
||||
message(WARNING "Missing LIBBSD library. Install libbsd-dev package and/or check linker directories.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(CMAKE_C_COMPILER_ID MATCHES "GNU")
|
||||
# These flags are GCC specific
|
||||
set_property(SOURCE ${cache_srcs} APPEND_STRING PROPERTY COMPILE_FLAGS
|
||||
|
@ -1,10 +1,10 @@
|
||||
menu "ESP_PARTITION"
|
||||
menu "Partition API Configuration"
|
||||
|
||||
# This option enables gathering statistics and flash wear levelling simulation.
|
||||
# Linux target only (host tests).
|
||||
config ESP_PARTITION_ENABLE_STATS
|
||||
bool "Enable esp_partition statistics gathering"
|
||||
default n
|
||||
bool
|
||||
depends on IDF_TARGET_LINUX
|
||||
help
|
||||
This option enables statistics gathering and flash wear simulation. Linux only.
|
||||
default n
|
||||
|
||||
endmenu
|
||||
|
@ -7,3 +7,38 @@ set(COMPONENTS main)
|
||||
list(APPEND EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/mocks/freertos/")
|
||||
|
||||
project(partition_api_test)
|
||||
|
||||
#extra step to build 8M partition table on top of (default) 4M partition table built by partition-table dependency
|
||||
set(flashsize_opt --flash-size 8MB)
|
||||
set(partition_csv "partition_table_8M.csv")
|
||||
set(partition_bin "partition-table_8M.bin")
|
||||
|
||||
idf_build_get_property(build_dir BUILD_DIR)
|
||||
idf_build_get_property(python PYTHON)
|
||||
|
||||
set(gen_partition_table "${python}" "${CMAKE_CURRENT_SOURCE_DIR}/../../../partition_table/gen_esp32part.py" "-q"
|
||||
"${flashsize_opt}" "--")
|
||||
|
||||
set(partition_table_display
|
||||
COMMAND ${CMAKE_COMMAND} -E echo "Partition table binary generated. Contents:"
|
||||
COMMAND ${CMAKE_COMMAND} -E echo "*******************************************************************************"
|
||||
COMMAND ${gen_partition_table} "${build_dir}/partition_table/${partition_bin}"
|
||||
COMMAND ${CMAKE_COMMAND} -E echo "*******************************************************************************"
|
||||
)
|
||||
|
||||
add_custom_command(OUTPUT "${build_dir}/partition_table/${partition_bin}"
|
||||
COMMAND ${gen_partition_table}
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/${partition_csv}"
|
||||
"${build_dir}/partition_table/${partition_bin}"
|
||||
${partition_table_display}
|
||||
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${partition_csv}
|
||||
VERBATIM)
|
||||
|
||||
add_custom_target(partition_table_bin_8M DEPENDS "${build_dir}/partition_table/${partition_bin}"
|
||||
)
|
||||
add_custom_target(partition-table-8M
|
||||
DEPENDS partition_table_bin_8M
|
||||
${partition_table_display}
|
||||
VERBATIM)
|
||||
|
||||
add_dependencies(partition_api_test.elf partition-table partition-table-8M)
|
||||
|
@ -7,6 +7,11 @@
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#if __has_include(<bsd/string.h>)
|
||||
// for strlcpy
|
||||
#include <bsd/string.h>
|
||||
#endif
|
||||
#include <unistd.h>
|
||||
#include "esp_err.h"
|
||||
#include "esp_partition.h"
|
||||
#include "esp_private/partition_linux.h"
|
||||
@ -144,7 +149,331 @@ TEST(partition_api, test_partition_mmap)
|
||||
TEST_ASSERT_EQUAL(err,ESP_ERR_INVALID_SIZE);
|
||||
}
|
||||
|
||||
#define EMULATED_VIRTUAL_SECTOR_COUNT (ESP_PARTITION_EMULATED_FLASH_SIZE / ESP_PARTITION_EMULATED_SECTOR_SIZE)
|
||||
TEST(partition_api, test_partition_mmap_diff_size)
|
||||
{
|
||||
// Scenario: default temporary flash file, explicitly specified size and file with partition table
|
||||
// Check the size of "storage" partition. Should be 6M = 6*1024*1024
|
||||
|
||||
// unmap file to have correct initial conditions, regardless of result
|
||||
esp_partition_file_munmap();
|
||||
|
||||
// get and initialize the control structure for file mmap
|
||||
esp_partition_file_mmap_ctrl_t *p_file_mmap_ctrl = esp_partition_get_file_mmap_ctrl_input();
|
||||
TEST_ASSERT_NOT_NULL(p_file_mmap_ctrl);
|
||||
|
||||
memset(p_file_mmap_ctrl, 0, sizeof(*p_file_mmap_ctrl));
|
||||
p_file_mmap_ctrl->flash_file_size = 0x800000; // 8MB
|
||||
strlcpy(p_file_mmap_ctrl->partition_file_name, "./build/partition_table/partition-table_8M.bin", sizeof(p_file_mmap_ctrl->partition_file_name));
|
||||
|
||||
// esp_partition_find_first calls the esp_partition_file_mmap in the background
|
||||
const esp_partition_t *partition_data = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, "storage");
|
||||
TEST_ASSERT_NOT_NULL(partition_data);
|
||||
|
||||
// Check partition size
|
||||
size_t exp_size = 0x600000; // 6MB
|
||||
size_t act_size = partition_data->size;
|
||||
TEST_ASSERT_EQUAL(exp_size, act_size);
|
||||
|
||||
// cleanup after test
|
||||
esp_partition_file_munmap();
|
||||
}
|
||||
|
||||
TEST(partition_api, test_partition_mmap_reopen)
|
||||
{
|
||||
// Scenario: default temporary flash file, write some data
|
||||
// Remember name of temporary file, reset remove flag, unmmap file.
|
||||
// Set file name from previous step, mmap file, read and compare data
|
||||
|
||||
// unmap file to have correct initial conditions, regardless of result
|
||||
esp_partition_file_munmap();
|
||||
|
||||
// get and initialize the control structure for file mmap
|
||||
esp_partition_file_mmap_ctrl_t *p_file_mmap_ctrl_input = esp_partition_get_file_mmap_ctrl_input();
|
||||
TEST_ASSERT_NOT_NULL(p_file_mmap_ctrl_input);
|
||||
|
||||
memset(p_file_mmap_ctrl_input, 0, sizeof(*p_file_mmap_ctrl_input));
|
||||
|
||||
// esp_partition_find_first calls the esp_partition_file_mmap in the background
|
||||
const esp_partition_t *partition_data = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, "storage");
|
||||
TEST_ASSERT_NOT_NULL(partition_data);
|
||||
|
||||
const char *test_string = "Is HAL6000 an IBM6000 ?";
|
||||
size_t test_string_len = strlen(test_string) + 1;
|
||||
|
||||
// write test string
|
||||
esp_err_t err = esp_partition_write(partition_data, 0, test_string, test_string_len);
|
||||
TEST_ESP_OK(err);
|
||||
|
||||
// remember memory mapped file name
|
||||
esp_partition_file_mmap_ctrl_t *p_file_mmap_ctrl_act = esp_partition_get_file_mmap_ctrl_act();
|
||||
TEST_ASSERT_NOT_NULL(p_file_mmap_ctrl_act);
|
||||
|
||||
char generated_file_name[PATH_MAX];
|
||||
strlcpy(generated_file_name, p_file_mmap_ctrl_act->flash_file_name, sizeof(generated_file_name));
|
||||
|
||||
// ensure remove flag is not set
|
||||
p_file_mmap_ctrl_input->remove_dump = false;
|
||||
|
||||
// unmap
|
||||
err = esp_partition_file_munmap();
|
||||
TEST_ESP_OK(err);
|
||||
|
||||
// initialize control struct with memory mapped file name
|
||||
memset(p_file_mmap_ctrl_input, 0, sizeof(*p_file_mmap_ctrl_input));
|
||||
strlcpy(p_file_mmap_ctrl_input->flash_file_name, generated_file_name, sizeof(p_file_mmap_ctrl_input->flash_file_name));
|
||||
|
||||
// get partiton
|
||||
partition_data = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, "storage");
|
||||
TEST_ASSERT_NOT_NULL(partition_data);
|
||||
|
||||
// read verify string
|
||||
char *verify_string = malloc(test_string_len);
|
||||
err = esp_partition_read(partition_data, 0, verify_string, test_string_len);
|
||||
TEST_ESP_OK(err);
|
||||
|
||||
// compare strings
|
||||
bool strings_equal = (strncmp(test_string, verify_string, test_string_len) == 0);
|
||||
TEST_ASSERT_EQUAL(strings_equal,true);
|
||||
|
||||
free(verify_string);
|
||||
|
||||
// cleanup after test
|
||||
esp_partition_file_munmap();
|
||||
}
|
||||
|
||||
TEST(partition_api, test_partition_mmap_remove)
|
||||
{
|
||||
// Scenario: default temporary flash file, write some data
|
||||
// Remember name of temporary file, set remove flag, unmmap file.
|
||||
// Set file name from previous step, try to get partition "storage", should fail with NULL returned
|
||||
|
||||
// unmap file to have correct initial conditions, regardless of result
|
||||
esp_partition_file_munmap();
|
||||
|
||||
// get and initialize the control structure for file mmap
|
||||
esp_partition_file_mmap_ctrl_t *p_file_mmap_ctrl_input = esp_partition_get_file_mmap_ctrl_input();
|
||||
TEST_ASSERT_NOT_NULL(p_file_mmap_ctrl_input);
|
||||
|
||||
memset(p_file_mmap_ctrl_input, 0, sizeof(*p_file_mmap_ctrl_input));
|
||||
|
||||
// esp_partition_find_first calls the esp_partition_file_mmap in the background
|
||||
const esp_partition_t *partition_data = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, "storage");
|
||||
TEST_ASSERT_NOT_NULL(partition_data);
|
||||
|
||||
const char *test_string = "This text should dismiss after esp_partition_file_munmap";
|
||||
size_t test_string_len = strlen(test_string) + 1;
|
||||
|
||||
// write test string
|
||||
esp_err_t err = esp_partition_write(partition_data, 0, test_string, test_string_len);
|
||||
TEST_ESP_OK(err);
|
||||
|
||||
// remember memory mapped file name
|
||||
esp_partition_file_mmap_ctrl_t *p_file_mmap_ctrl_act = esp_partition_get_file_mmap_ctrl_act();
|
||||
TEST_ASSERT_NOT_NULL(p_file_mmap_ctrl_act);
|
||||
|
||||
char generated_file_name[PATH_MAX];
|
||||
strlcpy(generated_file_name, p_file_mmap_ctrl_act->flash_file_name, sizeof(generated_file_name));
|
||||
|
||||
// ensure remove flag is set
|
||||
p_file_mmap_ctrl_input->remove_dump = true;
|
||||
|
||||
// unmap
|
||||
err = esp_partition_file_munmap();
|
||||
TEST_ESP_OK(err);
|
||||
|
||||
// initialize control struct with memory mapped file name
|
||||
memset(p_file_mmap_ctrl_input, 0, sizeof(*p_file_mmap_ctrl_input));
|
||||
strlcpy(p_file_mmap_ctrl_input->flash_file_name, generated_file_name, sizeof(p_file_mmap_ctrl_input->flash_file_name));
|
||||
|
||||
// get partiton, should fail with NULL returned
|
||||
partition_data = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, "storage");
|
||||
TEST_ASSERT_EQUAL(NULL, partition_data);
|
||||
|
||||
// cleanup after test
|
||||
esp_partition_file_munmap();
|
||||
memset(p_file_mmap_ctrl_input, 0, sizeof(*p_file_mmap_ctrl_input));
|
||||
}
|
||||
|
||||
TEST(partition_api, test_partition_mmap_name_size)
|
||||
{
|
||||
// Negative Scenario: conflicting settings - flash_file_name together with one or both of
|
||||
// flash_file_size, partition_file_name
|
||||
// esp_partition_file_mmap should return ESP_ERR_INVALID_ARG
|
||||
|
||||
// unmap file to have correct initial conditions, regardless of result
|
||||
esp_partition_file_munmap();
|
||||
|
||||
// get and initialize the control structure for file mmap
|
||||
esp_partition_file_mmap_ctrl_t *p_file_mmap_ctrl_input = esp_partition_get_file_mmap_ctrl_input();
|
||||
TEST_ASSERT_NOT_NULL(p_file_mmap_ctrl_input);
|
||||
|
||||
memset(p_file_mmap_ctrl_input, 0, sizeof(*p_file_mmap_ctrl_input));
|
||||
const char *flash_file_name = "/tmp/xyz";
|
||||
strlcpy(p_file_mmap_ctrl_input->flash_file_name, flash_file_name, sizeof(p_file_mmap_ctrl_input->flash_file_name));
|
||||
p_file_mmap_ctrl_input->flash_file_size = 1; // anything different from 0
|
||||
|
||||
const uint8_t *p_mem_block = NULL;
|
||||
esp_err_t err = esp_partition_file_mmap(&p_mem_block);
|
||||
|
||||
// expected result is invalid argument
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, err);
|
||||
|
||||
// cleanup after test
|
||||
esp_partition_file_munmap();
|
||||
memset(p_file_mmap_ctrl_input, 0, sizeof(*p_file_mmap_ctrl_input));
|
||||
}
|
||||
|
||||
TEST(partition_api, test_partition_mmap_size_no_partition)
|
||||
{
|
||||
// Negative Scenario: conflicting settings - flash_file_name empty, flash_file_size set
|
||||
// and partition_file_name not set
|
||||
// esp_partition_file_mmap should return ESP_ERR_INVALID_ARG
|
||||
|
||||
// unmap file to have correct initial conditions, regardless of result
|
||||
esp_partition_file_munmap();
|
||||
|
||||
// get and initialize the control structure for file mmap
|
||||
esp_partition_file_mmap_ctrl_t *p_file_mmap_ctrl_input = esp_partition_get_file_mmap_ctrl_input();
|
||||
TEST_ASSERT_NOT_NULL(p_file_mmap_ctrl_input);
|
||||
|
||||
memset(p_file_mmap_ctrl_input, 0, sizeof(*p_file_mmap_ctrl_input));
|
||||
p_file_mmap_ctrl_input->flash_file_size = 1; // anything different from 0
|
||||
|
||||
const uint8_t *p_mem_block = NULL;
|
||||
esp_err_t err = esp_partition_file_mmap(&p_mem_block);
|
||||
|
||||
// expected result is invalid argument
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, err);
|
||||
|
||||
// cleanup after test
|
||||
esp_partition_file_munmap();
|
||||
memset(p_file_mmap_ctrl_input, 0, sizeof(*p_file_mmap_ctrl_input));
|
||||
}
|
||||
|
||||
TEST(partition_api, test_partition_mmap_no_size_partition)
|
||||
{
|
||||
// Negative Scenario: conflicting settings - flash_file_name empty, flash_file_size not set
|
||||
// and partition_file_name set
|
||||
// esp_partition_file_mmap should return ESP_ERR_INVALID_ARG
|
||||
|
||||
// unmap file to have correct initial conditions, regardless of result
|
||||
esp_partition_file_munmap();
|
||||
|
||||
// get and initialize the control structure for file mmap
|
||||
esp_partition_file_mmap_ctrl_t *p_file_mmap_ctrl_input = esp_partition_get_file_mmap_ctrl_input();
|
||||
TEST_ASSERT_NOT_NULL(p_file_mmap_ctrl_input);
|
||||
|
||||
memset(p_file_mmap_ctrl_input, 0, sizeof(*p_file_mmap_ctrl_input));
|
||||
const char *partition_file_name = "/tmp/xyz.bin";
|
||||
strlcpy(p_file_mmap_ctrl_input->partition_file_name, partition_file_name, sizeof(p_file_mmap_ctrl_input->partition_file_name));
|
||||
|
||||
const uint8_t *p_mem_block = NULL;
|
||||
esp_err_t err = esp_partition_file_mmap(&p_mem_block);
|
||||
|
||||
// expected result is invalid argument
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, err);
|
||||
|
||||
// cleanup after test
|
||||
esp_partition_file_munmap();
|
||||
memset(p_file_mmap_ctrl_input, 0, sizeof(*p_file_mmap_ctrl_input));
|
||||
}
|
||||
|
||||
TEST(partition_api, test_partition_mmap_ffile_nf)
|
||||
{
|
||||
// Negative Scenario: specified flash_file_name file not found
|
||||
// esp_partition_file_mmap should return ESP_ERR_NOT_FOUND
|
||||
|
||||
// unmap file to have correct initial conditions, regardless of result
|
||||
esp_partition_file_munmap();
|
||||
|
||||
// get and initialize the control structure for file mmap
|
||||
esp_partition_file_mmap_ctrl_t *p_file_mmap_ctrl_input = esp_partition_get_file_mmap_ctrl_input();
|
||||
TEST_ASSERT_NOT_NULL(p_file_mmap_ctrl_input);
|
||||
|
||||
memset(p_file_mmap_ctrl_input, 0, sizeof(*p_file_mmap_ctrl_input));
|
||||
const char *flash_file_name = "/tmp/strangefilename.tmp";
|
||||
|
||||
// make sure file doesn't exist
|
||||
if (access(flash_file_name, F_OK) == 0) {
|
||||
// our strange file exists, skip rest of test
|
||||
} else {
|
||||
strlcpy(p_file_mmap_ctrl_input->flash_file_name, flash_file_name, sizeof(p_file_mmap_ctrl_input->flash_file_name));
|
||||
|
||||
const uint8_t *p_mem_block = NULL;
|
||||
esp_err_t err = esp_partition_file_mmap(&p_mem_block);
|
||||
|
||||
// expected result is file not found
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_NOT_FOUND, err);
|
||||
|
||||
// cleanup after test
|
||||
esp_partition_file_munmap();
|
||||
memset(p_file_mmap_ctrl_input, 0, sizeof(*p_file_mmap_ctrl_input));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(partition_api, test_partition_mmap_pfile_nf)
|
||||
{
|
||||
// Negative Scenario: specified partition_file_name file not found
|
||||
// esp_partition_file_mmap should return ESP_ERR_NOT_FOUND
|
||||
|
||||
// unmap file to have correct initial conditions, regardless of result
|
||||
esp_partition_file_munmap();
|
||||
|
||||
// get and initialize the control structure for file mmap
|
||||
esp_partition_file_mmap_ctrl_t *p_file_mmap_ctrl_input = esp_partition_get_file_mmap_ctrl_input();
|
||||
TEST_ASSERT_NOT_NULL(p_file_mmap_ctrl_input);
|
||||
|
||||
memset(p_file_mmap_ctrl_input, 0, sizeof(*p_file_mmap_ctrl_input));
|
||||
const char *partition_file_name = "/tmp/strangefilename.tmp";
|
||||
|
||||
// make sure file doesn't exist
|
||||
if (access(partition_file_name, F_OK) == 0) {
|
||||
// our strange file exists, skip rest of test
|
||||
} else {
|
||||
strlcpy(p_file_mmap_ctrl_input->partition_file_name, partition_file_name, sizeof(p_file_mmap_ctrl_input->partition_file_name));
|
||||
p_file_mmap_ctrl_input->flash_file_size = 0x10000; // any non zero value to pass validation
|
||||
|
||||
const uint8_t *p_mem_block = NULL;
|
||||
esp_err_t err = esp_partition_file_mmap(&p_mem_block);
|
||||
|
||||
// expected result is file not found
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_NOT_FOUND, err);
|
||||
|
||||
// cleanup after test
|
||||
esp_partition_file_munmap();
|
||||
memset(p_file_mmap_ctrl_input, 0, sizeof(*p_file_mmap_ctrl_input));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(partition_api, test_partition_mmap_size_too_small)
|
||||
{
|
||||
// Negative Scenario: specified flash file size too small
|
||||
// to hold at least partition table at default offset
|
||||
// esp_partition_file_mmap should return ESP_ERR_INVALID_SIZE
|
||||
|
||||
// unmap file to have correct initial conditions, regardless of result
|
||||
esp_partition_file_munmap();
|
||||
|
||||
// get and initialize the control structure for file mmap
|
||||
esp_partition_file_mmap_ctrl_t *p_file_mmap_ctrl_input = esp_partition_get_file_mmap_ctrl_input();
|
||||
TEST_ASSERT_NOT_NULL(p_file_mmap_ctrl_input);
|
||||
|
||||
memset(p_file_mmap_ctrl_input, 0, sizeof(*p_file_mmap_ctrl_input));
|
||||
|
||||
// set valid partition table name and very small flash size
|
||||
strlcpy(p_file_mmap_ctrl_input->partition_file_name, "./build/partition_table/partition-table.bin", sizeof(p_file_mmap_ctrl_input->partition_file_name));
|
||||
p_file_mmap_ctrl_input->flash_file_size = 1;
|
||||
|
||||
const uint8_t *p_mem_block = NULL;
|
||||
esp_err_t err = esp_partition_file_mmap(&p_mem_block);
|
||||
|
||||
// expected result is invalid argument
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_SIZE, err);
|
||||
|
||||
// cleanup after test
|
||||
esp_partition_file_munmap();
|
||||
memset(p_file_mmap_ctrl_input, 0, sizeof(*p_file_mmap_ctrl_input));
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
@ -154,9 +483,24 @@ typedef struct
|
||||
size_t read_bytes;
|
||||
size_t write_bytes;
|
||||
size_t total_time;
|
||||
size_t sector_erase_count[EMULATED_VIRTUAL_SECTOR_COUNT];
|
||||
size_t *sector_erase_count;
|
||||
size_t sector_erase_count_size;
|
||||
} t_stats;
|
||||
|
||||
void init_stats(t_stats *p_stats)
|
||||
{
|
||||
memset(p_stats, 0, sizeof(t_stats));
|
||||
p_stats->sector_erase_count_size = esp_partition_get_file_mmap_ctrl_act()->flash_file_size / ESP_PARTITION_EMULATED_SECTOR_SIZE;
|
||||
p_stats->sector_erase_count = calloc(p_stats->sector_erase_count_size, sizeof(size_t));
|
||||
}
|
||||
|
||||
void dispose_stats(t_stats *p_stats)
|
||||
{
|
||||
if(p_stats->sector_erase_count != NULL){
|
||||
free(p_stats->sector_erase_count);
|
||||
}
|
||||
}
|
||||
|
||||
void print_stats(const t_stats *p_stats)
|
||||
{
|
||||
ESP_LOGI(TAG, "read_ops:%06lu write_ops:%06lu erase_ops:%06lu read_bytes:%06lu write_bytes:%06lu total_time:%06lu\n",
|
||||
@ -177,7 +521,7 @@ void read_stats(t_stats *p_stats)
|
||||
p_stats->write_bytes = esp_partition_get_write_bytes();
|
||||
p_stats->total_time = esp_partition_get_total_time();
|
||||
|
||||
for(size_t i = 0; i < EMULATED_VIRTUAL_SECTOR_COUNT; i++)
|
||||
for(size_t i = 0; i < p_stats->sector_erase_count_size; i++)
|
||||
p_stats->sector_erase_count[i] = esp_partition_get_sector_erase_count(i);
|
||||
}
|
||||
|
||||
@ -198,7 +542,7 @@ bool evaluate_stats(const t_stats *p_initial_stats, const t_stats *p_final_stats
|
||||
if(p_expected_difference_stats->total_time != SIZE_MAX)
|
||||
TEST_ASSERT_EQUAL(p_initial_stats->total_time + p_expected_difference_stats->total_time, p_final_stats->total_time);
|
||||
|
||||
for(size_t i = 0; i < EMULATED_VIRTUAL_SECTOR_COUNT; i++)
|
||||
for(size_t i = 0; i < p_initial_stats->sector_erase_count_size; i++)
|
||||
{
|
||||
if(p_expected_difference_stats->sector_erase_count[i] != SIZE_MAX)
|
||||
{
|
||||
@ -220,7 +564,11 @@ TEST(partition_api, test_partition_stats)
|
||||
|
||||
t_stats initial_stats;
|
||||
t_stats final_stats;
|
||||
t_stats zero_stats = {0};
|
||||
t_stats zero_stats;
|
||||
|
||||
init_stats(&initial_stats);
|
||||
init_stats(&final_stats);
|
||||
init_stats(&zero_stats);
|
||||
|
||||
// get actual statistics
|
||||
read_stats(&initial_stats);
|
||||
@ -256,15 +604,16 @@ TEST(partition_api, test_partition_stats)
|
||||
if((non_aligned_portions % ESP_PARTITION_EMULATED_SECTOR_SIZE) > 0)
|
||||
erase_ops += 1;
|
||||
|
||||
t_stats expected_difference_stats = {
|
||||
.read_ops = 1,
|
||||
.write_ops = 1,
|
||||
.erase_ops = erase_ops,
|
||||
.read_bytes = size,
|
||||
.write_bytes = size,
|
||||
.total_time = SIZE_MAX
|
||||
};
|
||||
for (size_t i = 0; i < EMULATED_VIRTUAL_SECTOR_COUNT; i++)
|
||||
t_stats expected_difference_stats;
|
||||
init_stats(&expected_difference_stats);
|
||||
|
||||
expected_difference_stats.read_ops = 1;
|
||||
expected_difference_stats.write_ops = 1;
|
||||
expected_difference_stats.erase_ops = erase_ops;
|
||||
expected_difference_stats.read_bytes = size;
|
||||
expected_difference_stats.write_bytes = size;
|
||||
expected_difference_stats.total_time = SIZE_MAX;
|
||||
for (size_t i = 0; i < expected_difference_stats.sector_erase_count_size; i++)
|
||||
expected_difference_stats.sector_erase_count[i] = SIZE_MAX;
|
||||
|
||||
evaluate_stats(&initial_stats, &final_stats, &expected_difference_stats);
|
||||
@ -276,15 +625,20 @@ TEST(partition_api, test_partition_stats)
|
||||
// evaluate zero statistics
|
||||
evaluate_stats(&zero_stats, &final_stats, &zero_stats);
|
||||
|
||||
// free symanically allocated space
|
||||
dispose_stats(&initial_stats);
|
||||
dispose_stats(&final_stats);
|
||||
dispose_stats(&zero_stats);
|
||||
dispose_stats(&expected_difference_stats);
|
||||
free(test_data_ptr);
|
||||
}
|
||||
|
||||
TEST(partition_api, test_partition_wear_emulation)
|
||||
TEST(partition_api, test_partition_power_off_emulation)
|
||||
{
|
||||
const esp_partition_t *partition_data = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, "storage");
|
||||
TEST_ASSERT_NOT_NULL(partition_data);
|
||||
|
||||
// no offset, map whole partition
|
||||
//no offset, map whole partition
|
||||
size_t offset = 0;
|
||||
size_t size = partition_data->size;
|
||||
|
||||
@ -293,9 +647,9 @@ TEST(partition_api, test_partition_wear_emulation)
|
||||
TEST_ASSERT_NOT_NULL(test_data_ptr);
|
||||
memset(test_data_ptr, 0xff, size);
|
||||
|
||||
// --- wear off ---
|
||||
// ensure wear emulation is off
|
||||
esp_partition_fail_after(SIZE_MAX);
|
||||
// --- power-off off ---
|
||||
// ensure power-off emulation is off
|
||||
esp_partition_fail_after(SIZE_MAX, 0);
|
||||
|
||||
// erase partition data
|
||||
esp_err_t err = esp_partition_erase_range(partition_data, offset, size);
|
||||
@ -309,40 +663,39 @@ TEST(partition_api, test_partition_wear_emulation)
|
||||
err = esp_partition_erase_range(partition_data, offset, size);
|
||||
TEST_ESP_OK(err);
|
||||
|
||||
// --- wear on, write ---
|
||||
// ensure wear emulation is on, below the limit for size
|
||||
// esp_partition_write consumes one wear cycle per 4 bytes written
|
||||
esp_partition_fail_after(size / 4 - 1);
|
||||
// --- power-off on, write ---
|
||||
// ensure power-off emulation is on, below the limit for size
|
||||
// esp_partition_write consumes one power off failure cycle per 4 bytes written
|
||||
esp_partition_fail_after(size / 4 - 1, ESP_PARTITION_FAIL_AFTER_MODE_BOTH);
|
||||
|
||||
// write data - should fail
|
||||
err = esp_partition_write(partition_data, offset, test_data_ptr, size);
|
||||
TEST_ASSERT_EQUAL(ESP_FAIL, err);
|
||||
|
||||
// --- wear on, erase has just enough wear cycles available---
|
||||
// ensure wear emulation is on, at the limit for size
|
||||
// esp_partition_erase_range consumes one wear cycle per one virtual sector erased
|
||||
esp_partition_fail_after(size / ESP_PARTITION_EMULATED_SECTOR_SIZE);
|
||||
// --- power-off on, erase has just enough power off failure cycles available---
|
||||
// ensure power-off emulation is on, at the limit for size
|
||||
// esp_partition_erase_range consumes one power-off emulation cycle per one virtual sector erased
|
||||
esp_partition_fail_after(size / ESP_PARTITION_EMULATED_SECTOR_SIZE, ESP_PARTITION_FAIL_AFTER_MODE_BOTH);
|
||||
|
||||
// write data - should be ok
|
||||
err = esp_partition_erase_range(partition_data, offset, size);
|
||||
TEST_ASSERT_EQUAL(ESP_OK, err);
|
||||
|
||||
// --- wear on, erase has one cycle less than required---
|
||||
// ensure wear emulation is on, below the limit for size
|
||||
// esp_partition_erase_range consumes one wear cycle per one virtual sector erased
|
||||
esp_partition_fail_after(size / ESP_PARTITION_EMULATED_SECTOR_SIZE - 1);
|
||||
// --- power-off on, erase has one cycle less than required---
|
||||
// ensure power-off emulation is on, below the limit for size
|
||||
// esp_partition_erase_range consumes one power-off emulation cycle per one virtual sector erased
|
||||
esp_partition_fail_after(size / ESP_PARTITION_EMULATED_SECTOR_SIZE - 1, ESP_PARTITION_FAIL_AFTER_MODE_BOTH);
|
||||
|
||||
// write data - should fail
|
||||
err = esp_partition_erase_range(partition_data, offset, size);
|
||||
TEST_ASSERT_EQUAL(ESP_FAIL, err);
|
||||
|
||||
// ---cleanup ---
|
||||
// disable wear emulation
|
||||
esp_partition_fail_after(SIZE_MAX);
|
||||
// disable power-off emulation
|
||||
esp_partition_fail_after(SIZE_MAX, 0);
|
||||
free(test_data_ptr);
|
||||
}
|
||||
|
||||
|
||||
TEST_GROUP_RUNNER(partition_api)
|
||||
{
|
||||
RUN_TEST_CASE(partition_api, test_partition_find_basic);
|
||||
@ -351,8 +704,17 @@ TEST_GROUP_RUNNER(partition_api)
|
||||
RUN_TEST_CASE(partition_api, test_partition_find_first);
|
||||
RUN_TEST_CASE(partition_api, test_partition_ops);
|
||||
RUN_TEST_CASE(partition_api, test_partition_mmap);
|
||||
RUN_TEST_CASE(partition_api, test_partition_mmap_diff_size);
|
||||
RUN_TEST_CASE(partition_api, test_partition_mmap_reopen);
|
||||
RUN_TEST_CASE(partition_api, test_partition_mmap_remove);
|
||||
RUN_TEST_CASE(partition_api, test_partition_mmap_name_size);
|
||||
RUN_TEST_CASE(partition_api, test_partition_mmap_size_no_partition);
|
||||
RUN_TEST_CASE(partition_api, test_partition_mmap_no_size_partition);
|
||||
RUN_TEST_CASE(partition_api, test_partition_mmap_ffile_nf);
|
||||
RUN_TEST_CASE(partition_api, test_partition_mmap_pfile_nf);
|
||||
RUN_TEST_CASE(partition_api, test_partition_mmap_size_too_small);
|
||||
RUN_TEST_CASE(partition_api, test_partition_stats);
|
||||
RUN_TEST_CASE(partition_api, test_partition_wear_emulation);
|
||||
RUN_TEST_CASE(partition_api, test_partition_power_off_emulation);
|
||||
}
|
||||
|
||||
static void run_all_tests(void)
|
||||
|
@ -0,0 +1,6 @@
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
|
||||
nvs, data, nvs, 0x9000, 0x6000,
|
||||
phy_init, data, phy, 0xf000, 0x1000,
|
||||
factory, app, factory, 0x10000, 1M,
|
||||
storage, data, , , 6M,
|
|
@ -1,4 +1,5 @@
|
||||
CONFIG_IDF_TARGET="linux"
|
||||
IDF_TARGET_LINUX=y
|
||||
CONFIG_COMPILER_CXX_EXCEPTIONS=y
|
||||
CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=n
|
||||
CONFIG_UNITY_ENABLE_FIXTURE=y
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <limits.h>
|
||||
#include "esp_err.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
@ -24,7 +25,12 @@ extern "C" {
|
||||
#define ESP_PARTITION_EMULATED_SECTOR_SIZE 0x1000
|
||||
|
||||
/** @brief emulated whole flash size for the partition API on Linux */
|
||||
#define ESP_PARTITION_EMULATED_FLASH_SIZE 0x400000 //4MB fixed
|
||||
#define ESP_PARTITION_DEFAULT_EMULATED_FLASH_SIZE 0x400000 //4MB fixed
|
||||
|
||||
/** @brief mode of fail after */
|
||||
#define ESP_PARTITION_FAIL_AFTER_MODE_ERASE 0x01
|
||||
#define ESP_PARTITION_FAIL_AFTER_MODE_WRITE 0x02
|
||||
#define ESP_PARTITION_FAIL_AFTER_MODE_BOTH 0x03
|
||||
|
||||
/**
|
||||
* @brief Partition type to string conversion routine
|
||||
@ -166,21 +172,22 @@ size_t esp_partition_get_write_bytes(void);
|
||||
size_t esp_partition_get_total_time(void);
|
||||
|
||||
/**
|
||||
* @brief Initializes emulation of failure caused by wear on behalf of write/erase operations
|
||||
* @brief Initializes emulation of lost power failure in write/erase operations
|
||||
*
|
||||
* Function initializes down counter emulating remaining write / erase cycles.
|
||||
* Once this counter reaches 0, emulation of all subsequent write / erase operations fails
|
||||
* Function initializes down counter emulating power off failure during write / erase operations.
|
||||
* Once this counter reaches 0, actual as well as all subsequent write / erase operations fail
|
||||
* Initial state of down counter is disabled.
|
||||
*
|
||||
* @param[in] count Number of remaining write / erase cycles before failure. Call with SIZE_MAX to disable simulation of flash wear.
|
||||
* @param[in] count Number of remaining write / erase cycles before emulated failure. Call with SIZE_MAX to disable failure emulation.
|
||||
* @param[in] mode Controls whether remaining cycles are applied to erase, write or both operations
|
||||
*
|
||||
*/
|
||||
void esp_partition_fail_after(size_t count);
|
||||
void esp_partition_fail_after(size_t count, uint8_t mode);
|
||||
|
||||
/**
|
||||
* @brief Returns count of erase operations performed on virtual emulated sector
|
||||
*
|
||||
* Function returns number of erase operatinos performed on virtual sector specified by the parameter sector.
|
||||
* Function returns number of erase operations performed on virtual sector specified by the parameter sector.
|
||||
* The esp_parttion mapped address space is virtually split into sectors of the size ESP_PARTITION_EMULATED_SECTOR_SIZE.
|
||||
* Calls to the esp_partition_erase_range are impacting one or multiple virtual sectors, for each of them, the respective
|
||||
* count is incremented.
|
||||
@ -193,6 +200,46 @@ void esp_partition_fail_after(size_t count);
|
||||
*/
|
||||
size_t esp_partition_get_sector_erase_count(size_t sector);
|
||||
|
||||
typedef struct {
|
||||
char flash_file_name[PATH_MAX]; /*!< name of flash dump file, zero-terminated ASCII string */
|
||||
size_t flash_file_size; /*!< size of flash dump file in bytes */
|
||||
char partition_file_name[PATH_MAX]; /*!< name of file containing binary representation of partition table, zero-terminated ASCII string */
|
||||
bool remove_dump; /*!< flag is set to true if dump file has to be removed after esp_partition_file_munmap */
|
||||
} esp_partition_file_mmap_ctrl_t;
|
||||
|
||||
/**
|
||||
* @brief Returns pointer to the structure controlling mapping of flash file
|
||||
*
|
||||
* Function returns pointer to structure used by esp_partition_file_mmap and esp_partition_file_munmap
|
||||
* Caller can change this structure members prior calls involving the above functions to
|
||||
* Specify existing flash file which will represent the content of flash memory after mapping
|
||||
* Specify size and partition file name used to create empty flash memory
|
||||
* Control whether the actual flash file will be deleted of kept after call to esp_partition_file_munmap
|
||||
*
|
||||
* @return
|
||||
* - pointer to flash file mapping control structure
|
||||
*
|
||||
*/
|
||||
esp_partition_file_mmap_ctrl_t* esp_partition_get_file_mmap_ctrl_input(void);
|
||||
|
||||
/**
|
||||
* @brief Returns pointer to the structure reflecting actual settings of flash file emulation
|
||||
*
|
||||
* Function returns pointer to structure containing:
|
||||
* flash file name representing emulated flash memory
|
||||
* size of file representing emulated flash memory
|
||||
* file name holding binary used to initialize partition table (if it was used)
|
||||
*
|
||||
* @return
|
||||
* - pointer to flash file mapping actuall values in control structure
|
||||
*
|
||||
*/
|
||||
esp_partition_file_mmap_ctrl_t* esp_partition_get_file_mmap_ctrl_act(void);
|
||||
|
||||
// private function in partition.c to unload partitions and free space allocated by them
|
||||
void unload_partitions(void);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -225,6 +225,19 @@ static esp_err_t load_partitions(void)
|
||||
return err;
|
||||
}
|
||||
|
||||
void unload_partitions(void)
|
||||
{
|
||||
_lock_acquire(&s_partition_list_lock);
|
||||
partition_list_item_t *it;
|
||||
SLIST_FOREACH(it, &s_partition_list, next) {
|
||||
SLIST_REMOVE(&s_partition_list, it, partition_list_item_, next);
|
||||
free(it);
|
||||
}
|
||||
_lock_release(&s_partition_list_lock);
|
||||
|
||||
assert(SLIST_EMPTY(&s_partition_list));
|
||||
}
|
||||
|
||||
static esp_err_t ensure_partitions_loaded(void)
|
||||
{
|
||||
esp_err_t err = ESP_OK;
|
||||
|
@ -7,8 +7,13 @@
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#if __has_include(<bsd/string.h>)
|
||||
// for strlcpy
|
||||
#include <bsd/string.h>
|
||||
#endif
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
#include <errno.h>
|
||||
@ -19,21 +24,29 @@
|
||||
#include "esp_log.h"
|
||||
|
||||
static const char *TAG = "linux_spiflash";
|
||||
|
||||
static void *s_spiflash_mem_file_buf = NULL;
|
||||
static int s_spiflash_mem_file_fd = -1;
|
||||
static const esp_partition_mmap_handle_t s_default_partition_mmap_handle = 0;
|
||||
|
||||
// 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};
|
||||
|
||||
#ifdef CONFIG_ESP_PARTITION_ENABLE_STATS
|
||||
// variables holding stats and controlling wear emulation
|
||||
size_t s_esp_partition_stat_read_ops = 0;
|
||||
size_t s_esp_partition_stat_write_ops = 0;
|
||||
size_t s_esp_partition_stat_read_bytes = 0;
|
||||
size_t s_esp_partition_stat_write_bytes = 0;
|
||||
size_t s_esp_partition_stat_erase_ops = 0;
|
||||
size_t s_esp_partition_stat_total_time = 0;
|
||||
size_t s_esp_partition_emulated_flash_life = SIZE_MAX;
|
||||
// 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;
|
||||
|
||||
// tracking erase count individually for each emulated sector
|
||||
size_t s_esp_partition_stat_sector_erase_count[ESP_PARTITION_EMULATED_FLASH_SIZE / ESP_PARTITION_EMULATED_SECTOR_SIZE] = {0};
|
||||
static size_t *s_esp_partition_stat_sector_erase_count = NULL;
|
||||
|
||||
// forward declaration of hooks
|
||||
static void esp_partition_hook_read(const void *srcAddr, const size_t size);
|
||||
@ -86,71 +99,191 @@ const char *esp_partition_subtype_to_str(const uint32_t type, const uint32_t sub
|
||||
|
||||
esp_err_t esp_partition_file_mmap(const uint8_t **part_desc_addr_start)
|
||||
{
|
||||
//create temporary file to hold complete SPIFLASH size
|
||||
char temp_spiflash_mem_file_name[PATH_MAX] = {"/tmp/idf-partition-XXXXXX"};
|
||||
int spiflash_mem_file_fd = mkstemp(temp_spiflash_mem_file_name);
|
||||
// temporary file is used only if control structure doesn't specify file name.
|
||||
bool open_existing_file = false;
|
||||
|
||||
if (spiflash_mem_file_fd == -1) {
|
||||
ESP_LOGE(TAG, "Failed to create SPI FLASH emulation file %s: %s", temp_spiflash_mem_file_name, strerror(errno));
|
||||
return ESP_ERR_NOT_FINISHED;
|
||||
if(strlen(s_esp_partition_file_mmap_ctrl_input.flash_file_name) > 0) {
|
||||
// Open existing file. If size or partition table file were specified, raise errors
|
||||
if(s_esp_partition_file_mmap_ctrl_input.flash_file_size > 0) {
|
||||
ESP_LOGE(TAG, "Flash emulation file size: %u was specified while together with the file name: %s (illegal). Use file size = 0",
|
||||
s_esp_partition_file_mmap_ctrl_input.flash_file_size,
|
||||
s_esp_partition_file_mmap_ctrl_input.flash_file_name);
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
if(strlen(s_esp_partition_file_mmap_ctrl_input.partition_file_name) > 0) {
|
||||
ESP_LOGE(TAG, "Partition file name: %s was specified together with the flash emulation file name: %s (illegal). Use empty partition file name",
|
||||
s_esp_partition_file_mmap_ctrl_input.partition_file_name,
|
||||
s_esp_partition_file_mmap_ctrl_input.flash_file_name);
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
// 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));
|
||||
|
||||
open_existing_file = true;
|
||||
} else {
|
||||
// Open temporary file. If size was specified, also partition table has to be specified, otherwise raise error.
|
||||
// If none of size, partition table were specified, defaults are used.
|
||||
// Name of temporary file is available in s_esp_partition_file_mmap_ctrl.flash_file_name
|
||||
|
||||
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);
|
||||
|
||||
// conflicting input
|
||||
if(has_partfile != has_len) {
|
||||
ESP_LOGE(TAG, "Invalid combination of Partition file name: %s flash file size: %u was specified. Use either both parameters or none.",
|
||||
s_esp_partition_file_mmap_ctrl_input.partition_file_name,
|
||||
s_esp_partition_file_mmap_ctrl_input.flash_file_size);
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
// check if partition file is present, if not, use default
|
||||
if(!has_partfile) {
|
||||
strlcpy(s_esp_partition_file_mmap_ctrl_act.partition_file_name, "build/partition_table/partition-table.bin", sizeof(s_esp_partition_file_mmap_ctrl_act.partition_file_name));
|
||||
} 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));
|
||||
}
|
||||
|
||||
// check if flash size is present, if not set to default
|
||||
if(!has_len) {
|
||||
s_esp_partition_file_mmap_ctrl_act.flash_file_size = ESP_PARTITION_DEFAULT_EMULATED_FLASH_SIZE;
|
||||
} else {
|
||||
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));
|
||||
}
|
||||
|
||||
if (ftruncate(spiflash_mem_file_fd, ESP_PARTITION_EMULATED_FLASH_SIZE) != 0) {
|
||||
ESP_LOGE(TAG, "Failed to set size of SPI FLASH memory emulation file %s: %s", temp_spiflash_mem_file_name, strerror(errno));
|
||||
return ESP_ERR_INVALID_SIZE;
|
||||
esp_err_t ret = ESP_OK;
|
||||
|
||||
if(open_existing_file) {
|
||||
|
||||
s_spiflash_mem_file_fd = open(s_esp_partition_file_mmap_ctrl_act.flash_file_name, O_RDWR);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
do {
|
||||
// seek to the end
|
||||
off_t size = lseek(s_spiflash_mem_file_fd, 0L, SEEK_END);
|
||||
if(size < 0) {
|
||||
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);
|
||||
if(size < 0) {
|
||||
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;
|
||||
}
|
||||
} while(false);
|
||||
} 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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
ESP_LOGV(TAG, "SPIFLASH memory emulation file created: %s (size: %d B)", s_esp_partition_file_mmap_ctrl_act.flash_file_name, s_esp_partition_file_mmap_ctrl_act.flash_file_size);
|
||||
|
||||
// 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;
|
||||
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
|
||||
if(partition_table_file_size + ESP_PARTITION_TABLE_OFFSET > s_esp_partition_file_mmap_ctrl_act.flash_file_size) {
|
||||
ESP_LOGE(TAG, "Flash file: %s (size: %d B) cannot hold partition table requiring %d B",
|
||||
s_esp_partition_file_mmap_ctrl_act.flash_file_name,
|
||||
s_esp_partition_file_mmap_ctrl_act.flash_file_size,
|
||||
partition_table_file_size + ESP_PARTITION_TABLE_OFFSET);
|
||||
ret = ESP_ERR_INVALID_SIZE;
|
||||
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;
|
||||
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;
|
||||
}
|
||||
} while(false);
|
||||
}
|
||||
|
||||
ESP_LOGV(TAG, "SPIFLASH memory emulation file created: %s (size: %d B)", temp_spiflash_mem_file_name, ESP_PARTITION_EMULATED_FLASH_SIZE);
|
||||
if (ret != ESP_OK) {
|
||||
if(close(s_spiflash_mem_file_fd)) {
|
||||
ESP_LOGE(TAG, "Failed to close() SPIFLASH memory emulation file: %s", strerror(errno));
|
||||
}
|
||||
s_spiflash_mem_file_fd = -1;
|
||||
|
||||
//create memory-mapping for the partitions holder file
|
||||
if ((s_spiflash_mem_file_buf = mmap(NULL, ESP_PARTITION_EMULATED_FLASH_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, spiflash_mem_file_fd, 0)) == MAP_FAILED) {
|
||||
ESP_LOGE(TAG, "Failed to mmap() SPI FLASH memory emulation file: %s", strerror(errno));
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
//initialize whole range with bit-1 (NOR FLASH default)
|
||||
memset(s_spiflash_mem_file_buf, 0xFF, ESP_PARTITION_EMULATED_FLASH_SIZE);
|
||||
|
||||
//upload partition table to the mmap file at real offset as in SPIFLASH
|
||||
const char *partition_table_file_name = "build/partition_table/partition-table.bin";
|
||||
|
||||
FILE *f_partition_table = fopen(partition_table_file_name, "r+");
|
||||
if (f_partition_table == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to open partition table file %s: %s", partition_table_file_name, strerror(errno));
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
if (fseek(f_partition_table, 0L, SEEK_END) != 0) {
|
||||
ESP_LOGE(TAG, "Failed to seek in partition table file %s: %s", partition_table_file_name, strerror(errno));
|
||||
return ESP_ERR_INVALID_SIZE;
|
||||
}
|
||||
|
||||
int partition_table_file_size = ftell(f_partition_table);
|
||||
ESP_LOGV(TAG, "Using partition table file %s (size: %d B):", partition_table_file_name, partition_table_file_size);
|
||||
|
||||
uint8_t *part_table_in_spiflash = s_spiflash_mem_file_buf + ESP_PARTITION_TABLE_OFFSET;
|
||||
|
||||
//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", partition_table_file_name, strerror(errno));
|
||||
return ESP_ERR_INVALID_SIZE;
|
||||
}
|
||||
|
||||
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", partition_table_file_name);
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_LOG_DEFAULT_LEVEL_VERBOSE
|
||||
uint8_t *part_ptr = part_table_in_spiflash;
|
||||
uint8_t *part_end_ptr = part_table_in_spiflash + partition_table_file_size;
|
||||
uint8_t *part_ptr = s_spiflash_mem_file_buf + ESP_PARTITION_TABLE_OFFSET;
|
||||
|
||||
ESP_LOGV(TAG, "");
|
||||
ESP_LOGV(TAG, "Partition table sucessfully imported, partitions found:");
|
||||
|
||||
while (part_ptr < part_end_ptr) {
|
||||
while (true) {
|
||||
esp_partition_info_t *p_part_item = (esp_partition_info_t *)part_ptr;
|
||||
if (p_part_item->magic != ESP_PARTITION_MAGIC ) {
|
||||
break;
|
||||
@ -170,9 +303,17 @@ esp_err_t esp_partition_file_mmap(const uint8_t **part_desc_addr_start)
|
||||
ESP_LOGV(TAG, "");
|
||||
#endif
|
||||
|
||||
#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
|
||||
|
||||
//return mmapped file starting address
|
||||
*part_desc_addr_start = s_spiflash_mem_file_buf;
|
||||
|
||||
// clear input control structure
|
||||
memset(&s_esp_partition_file_mmap_ctrl_input, 0, sizeof(s_esp_partition_file_mmap_ctrl_input));
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
@ -181,26 +322,54 @@ esp_err_t esp_partition_file_munmap(void)
|
||||
if (s_spiflash_mem_file_buf == NULL) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
if (ESP_PARTITION_EMULATED_FLASH_SIZE == 0) {
|
||||
if (s_esp_partition_file_mmap_ctrl_act.flash_file_size == 0) {
|
||||
return ESP_ERR_INVALID_SIZE;
|
||||
}
|
||||
if(s_spiflash_mem_file_fd == -1) {
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
if (munmap(s_spiflash_mem_file_buf, ESP_PARTITION_EMULATED_FLASH_SIZE) != 0) {
|
||||
ESP_LOGE(TAG, "Failed to munmap() SPI FLASH memory emulation file: %s", strerror(errno));
|
||||
unload_partitions();
|
||||
|
||||
#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));
|
||||
return ESP_ERR_INVALID_RESPONSE;
|
||||
}
|
||||
|
||||
// close memory mapped file
|
||||
if(close(s_spiflash_mem_file_fd)) {
|
||||
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;
|
||||
}
|
||||
|
||||
if(s_esp_partition_file_mmap_ctrl_input.remove_dump) {
|
||||
// delete spi flash file
|
||||
if(remove(s_esp_partition_file_mmap_ctrl_act.flash_file_name) != 0) {
|
||||
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));
|
||||
s_spiflash_mem_file_buf = NULL;
|
||||
s_spiflash_mem_file_fd = -1;
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_partition_write(const esp_partition_t *partition, size_t dst_offset, const void *src, size_t size)
|
||||
{
|
||||
assert(partition != NULL);
|
||||
assert(partition != NULL && s_spiflash_mem_file_buf != NULL);
|
||||
|
||||
if (partition->encrypted) {
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
if (dst_offset > partition->size) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
@ -217,8 +386,9 @@ esp_err_t esp_partition_write(const esp_partition_t *partition, size_t dst_offse
|
||||
void *dst_addr = s_spiflash_mem_file_buf + partition->address + dst_offset;
|
||||
ESP_LOGV(TAG, "esp_partition_write(): partition=%s dst_offset=%zu src=%p size=%zu (real dst address: %p)", partition->label, dst_offset, src, size, dst_addr);
|
||||
|
||||
// hook gathers statistics and can emulate limited number of write cycles
|
||||
// hook gathers statistics and can emulate power-off
|
||||
if (!ESP_PARTITION_HOOK_WRITE(dst_addr, size)) {
|
||||
free(write_buf);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
@ -235,7 +405,7 @@ esp_err_t esp_partition_write(const esp_partition_t *partition, size_t dst_offse
|
||||
|
||||
esp_err_t esp_partition_read(const esp_partition_t *partition, size_t src_offset, void *dst, size_t size)
|
||||
{
|
||||
assert(partition != NULL);
|
||||
assert(partition != NULL && s_spiflash_mem_file_buf != NULL);
|
||||
|
||||
if (partition->encrypted) {
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
@ -248,7 +418,6 @@ esp_err_t esp_partition_read(const esp_partition_t *partition, size_t src_offset
|
||||
}
|
||||
|
||||
void *src_addr = s_spiflash_mem_file_buf + partition->address + src_offset;
|
||||
|
||||
ESP_LOGV(TAG, "esp_partition_read(): partition=%s src_offset=%zu dst=%p size=%zu (real src address: %p)", partition->label, src_offset, dst, size, src_addr);
|
||||
|
||||
memcpy(dst, src_addr, size);
|
||||
@ -284,7 +453,7 @@ esp_err_t esp_partition_erase_range(const esp_partition_t *partition, size_t off
|
||||
void *target_addr = s_spiflash_mem_file_buf + partition->address + offset;
|
||||
ESP_LOGV(TAG, "esp_partition_erase_range(): partition=%s offset=%zu size=%zu (real target address: %p)", partition->label, offset, size, target_addr);
|
||||
|
||||
// hook gathers statistics and can emulate limited number of write/erase cycles
|
||||
// hook gathers statistics and can emulate power-off
|
||||
if (!ESP_PARTITION_HOOK_ERASE(target_addr, size)) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
@ -328,7 +497,6 @@ esp_err_t esp_partition_mmap(const esp_partition_t *partition, size_t offset, si
|
||||
|
||||
// check if memory mapped file is already present, if not, map it now
|
||||
if (s_spiflash_mem_file_buf == NULL) {
|
||||
ESP_LOGE(TAG, "esp_partition_mmap(): in esp_partition_file_mmap");
|
||||
uint8_t *part_desc_addr_start = NULL;
|
||||
rc = esp_partition_file_mmap((const uint8_t **) &part_desc_addr_start);
|
||||
}
|
||||
@ -338,16 +506,25 @@ esp_err_t esp_partition_mmap(const esp_partition_t *partition, size_t offset, si
|
||||
*out_ptr = (void *) (s_spiflash_mem_file_buf + req_flash_addr);
|
||||
*out_handle = s_default_partition_mmap_handle;
|
||||
} else {
|
||||
*out_ptr = (void *) NULL;
|
||||
*out_ptr = NULL;
|
||||
*out_handle = 0;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
// Intentionally does nothing.
|
||||
void esp_partition_munmap(esp_partition_mmap_handle_t handle)
|
||||
void esp_partition_munmap(esp_partition_mmap_handle_t handle __attribute__((unused)))
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
esp_partition_file_mmap_ctrl_t* esp_partition_get_file_mmap_ctrl_input(void)
|
||||
{
|
||||
return &s_esp_partition_file_mmap_ctrl_input;
|
||||
}
|
||||
|
||||
esp_partition_file_mmap_ctrl_t* esp_partition_get_file_mmap_ctrl_act(void)
|
||||
{
|
||||
return &s_esp_partition_file_mmap_ctrl_act;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ESP_PARTITION_ENABLE_STATS
|
||||
@ -372,7 +549,7 @@ static size_t esp_partition_stat_time_interpolate(uint32_t bytes, size_t *lut)
|
||||
}
|
||||
|
||||
// Registers read access statistics of emulated SPI FLASH device (Linux host)
|
||||
// Ffunction increases nmuber of read operations, accumulates number of read bytes
|
||||
// Function increases nmuber of read operations, accumulates number of read bytes
|
||||
// and accumulates emulated read operation time (size dependent)
|
||||
static void esp_partition_hook_read(const void *srcAddr, const size_t size)
|
||||
{
|
||||
@ -385,40 +562,63 @@ static void esp_partition_hook_read(const void *srcAddr, const size_t size)
|
||||
}
|
||||
|
||||
// Registers write access statistics of emulated SPI FLASH device (Linux host)
|
||||
// If enabled by the esp_partition_fail_after, function emulates physical limitation of write/erase operations by
|
||||
// decrementing the s_esp_partition_emulated_life for each 4 bytes written
|
||||
// If enabled by the esp_partition_fail_after, function emulates power-off event during write/erase operations by
|
||||
// decrementing the s_esp_partition_emulated_power_off_counter for each 4 bytes written
|
||||
// If zero threshold is reached, false is returned.
|
||||
// Else the function increases nmuber of write operations, accumulates number
|
||||
// of bytes written and accumulates emulated write operation time (size dependent) and returns true.
|
||||
static bool esp_partition_hook_write(const void *dstAddr, const size_t size)
|
||||
{
|
||||
ESP_LOGV(TAG, "esp_partition_hook_write()");
|
||||
ESP_LOGV(TAG, "%s", __FUNCTION__);
|
||||
|
||||
// wear emulation
|
||||
// power-off emulation
|
||||
for (size_t i = 0; i < size / 4; ++i) {
|
||||
if (s_esp_partition_emulated_flash_life != SIZE_MAX && s_esp_partition_emulated_flash_life-- == 0) {
|
||||
if (s_esp_partition_emulated_power_off_counter != SIZE_MAX && s_esp_partition_emulated_power_off_counter-- == 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool ret_val = true;
|
||||
|
||||
// one power down cycle per 4 bytes written
|
||||
size_t write_cycles = size / 4;
|
||||
|
||||
// check whether power off simulation is active for write
|
||||
if(s_esp_partition_emulated_power_off_counter != SIZE_MAX &&
|
||||
s_esp_partition_emulated_power_off_counter & ESP_PARTITION_FAIL_AFTER_MODE_WRITE) {
|
||||
|
||||
// check if power down happens during this call
|
||||
if(s_esp_partition_emulated_power_off_counter >= write_cycles) {
|
||||
// OK
|
||||
s_esp_partition_emulated_power_off_counter -= write_cycles;
|
||||
} else {
|
||||
// failure in this call - reduce cycle count to the number of remainint power on cycles
|
||||
write_cycles = s_esp_partition_emulated_power_off_counter;
|
||||
// clear remaining cycles
|
||||
s_esp_partition_emulated_power_off_counter = 0;
|
||||
// final result value will be false
|
||||
ret_val = false;
|
||||
}
|
||||
}
|
||||
|
||||
// stats
|
||||
++s_esp_partition_stat_write_ops;
|
||||
s_esp_partition_stat_write_bytes += size;
|
||||
s_esp_partition_stat_total_time += esp_partition_stat_time_interpolate((uint32_t) size, s_esp_partition_stat_write_times);
|
||||
s_esp_partition_stat_write_bytes += write_cycles * 4;
|
||||
s_esp_partition_stat_total_time += esp_partition_stat_time_interpolate((uint32_t) (write_cycles * 4), s_esp_partition_stat_write_times);
|
||||
|
||||
return true;
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
// Registers erase access statistics of emulated SPI FLASH device (Linux host)
|
||||
// If enabled by the esp_partition_fail_after, function emulates physical limitation of write/erase operations by
|
||||
// decrementing the s_esp_partition_emulated_life for each erased virtual sector.
|
||||
// If enabled by 'esp_partition_fail_after' parameter, the function emulates a power-off event during write/erase
|
||||
// operations by decrementing the s_esp_partition_emulated_power_off_counterpower for each erased virtual sector.
|
||||
// If zero threshold is reached, false is returned.
|
||||
// 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
|
||||
static bool esp_partition_hook_erase(const void *dstAddr, const size_t size)
|
||||
{
|
||||
ESP_LOGV(TAG, "esp_partition_hook_erase()");
|
||||
ESP_LOGV(TAG, "%s", __FUNCTION__);
|
||||
|
||||
if (size == 0) {
|
||||
return true;
|
||||
@ -430,19 +630,34 @@ static bool esp_partition_hook_erase(const void *dstAddr, const size_t size)
|
||||
size_t last_sector_idx = (offset + size - 1) / ESP_PARTITION_EMULATED_SECTOR_SIZE;
|
||||
size_t sector_count = 1 + last_sector_idx - first_sector_idx;
|
||||
|
||||
for (size_t sector_index = first_sector_idx; sector_index < first_sector_idx + sector_count; sector_index++) {
|
||||
// wear emulation
|
||||
if (s_esp_partition_emulated_flash_life != SIZE_MAX && s_esp_partition_emulated_flash_life-- == 0) {
|
||||
return false;
|
||||
}
|
||||
bool ret_val = true;
|
||||
|
||||
// stats
|
||||
// check whether power off simulation is active for erase
|
||||
if(s_esp_partition_emulated_power_off_counter != SIZE_MAX &&
|
||||
s_esp_partition_emulated_power_off_counter & ESP_PARTITION_FAIL_AFTER_MODE_ERASE) {
|
||||
|
||||
// check if power down happens during this call
|
||||
if(s_esp_partition_emulated_power_off_counter >= sector_count) {
|
||||
// OK
|
||||
s_esp_partition_emulated_power_off_counter -= sector_count;
|
||||
} else {
|
||||
// failure in this call - reduce sector_count to the number of remainint power on cycles
|
||||
sector_count = s_esp_partition_emulated_power_off_counter;
|
||||
// clear remaining cycles
|
||||
s_esp_partition_emulated_power_off_counter = 0;
|
||||
// final result value will be false
|
||||
ret_val = false;
|
||||
}
|
||||
}
|
||||
|
||||
// update statistcs for all sectors until power down cycle
|
||||
for (size_t sector_index = first_sector_idx; sector_index < first_sector_idx + sector_count; sector_index++) {
|
||||
++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;
|
||||
}
|
||||
|
||||
return true;
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
void esp_partition_clear_stats(void)
|
||||
@ -454,7 +669,7 @@ void esp_partition_clear_stats(void)
|
||||
s_esp_partition_stat_write_ops = 0;
|
||||
s_esp_partition_stat_total_time = 0;
|
||||
|
||||
memset(s_esp_partition_stat_sector_erase_count, 0, sizeof(s_esp_partition_stat_sector_erase_count));
|
||||
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);
|
||||
}
|
||||
|
||||
size_t esp_partition_get_read_ops(void)
|
||||
@ -487,9 +702,10 @@ size_t esp_partition_get_total_time(void)
|
||||
return s_esp_partition_stat_total_time;
|
||||
}
|
||||
|
||||
void esp_partition_fail_after(size_t count)
|
||||
void esp_partition_fail_after(size_t count, uint8_t mode)
|
||||
{
|
||||
s_esp_partition_emulated_flash_life = count;
|
||||
s_esp_partition_emulated_power_off_counter = count;
|
||||
s_esp_partition_emulated_power_off_mode = mode;
|
||||
}
|
||||
|
||||
size_t esp_partition_get_sector_erase_count(size_t sector)
|
||||
|
@ -132,7 +132,6 @@ static void check_spiffs_files(spiffs *fs, const char *base_path, char *cur_path
|
||||
|
||||
struct stat sb;
|
||||
stat(path, &sb);
|
||||
|
||||
if (S_ISDIR(sb.st_mode)) {
|
||||
if (!strcmp(name, ".") || !strcmp(name, "..")) {
|
||||
continue;
|
||||
@ -249,7 +248,6 @@ TEST(spiffs, can_read_spiffs_image)
|
||||
char *img = (char *) malloc(img_size);
|
||||
TEST_ASSERT(fread(img, 1, img_size, img_file) == img_size);
|
||||
fclose(img_file);
|
||||
|
||||
TEST_ASSERT_TRUE(partition->size == img_size);
|
||||
|
||||
esp_partition_erase_range(partition, 0, partition->size);
|
||||
@ -265,7 +263,7 @@ TEST(spiffs, can_read_spiffs_image)
|
||||
spiffs_res = SPIFFS_check(&fs);
|
||||
TEST_ASSERT_TRUE(spiffs_res == SPIFFS_OK);
|
||||
|
||||
char path_buf[PATH_MAX];
|
||||
char path_buf[PATH_MAX] = {0};
|
||||
|
||||
// The image is created from the spiffs source directory. Compare the files in that
|
||||
// directory to the files read from the SPIFFS image.
|
||||
|
Loading…
Reference in New Issue
Block a user