mirror of
https://github.com/espressif/esp-idf.git
synced 2025-10-02 18:10:57 +02:00
feat(esp_partition): add support for partition tables larger than 4MB with linux target
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
@@ -155,6 +155,29 @@ TEST(partition_api, test_partition_mmap)
|
||||
TEST_ASSERT_EQUAL(err, ESP_ERR_INVALID_SIZE);
|
||||
}
|
||||
|
||||
TEST(partition_api, test_partition_mmap_support_for_greater_than_4M)
|
||||
{
|
||||
// Scenario: Not specified flash size but provided partition table > 4M (default size supported)
|
||||
// esp_partition_mmap should calculate partition size from the binary, create mmap_flash_file and return ESP_OK
|
||||
|
||||
// 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));
|
||||
strlcpy(p_file_mmap_ctrl->partition_file_name, BUILD_DIR"/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);
|
||||
|
||||
// cleanup after test
|
||||
esp_partition_file_munmap();
|
||||
}
|
||||
|
||||
TEST(partition_api, test_partition_mmap_diff_size)
|
||||
{
|
||||
// Scenario: default temporary flash file, explicitly specified size and file with partition table
|
||||
@@ -336,14 +359,10 @@ TEST(partition_api, test_partition_mmap_name_size)
|
||||
memset(p_file_mmap_ctrl_input, 0, sizeof(*p_file_mmap_ctrl_input));
|
||||
}
|
||||
|
||||
/* Negative TC to ensure mmap setup checks presence of partition file name (partition table binary file)
|
||||
* if flash size parameter was specified.
|
||||
* This test case specifies just flash file size but omits partition table binary file name.
|
||||
*/
|
||||
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
|
||||
// Scenario: flash_file_name empty, incorrect flash_file_size set and partition_file_name not set
|
||||
// esp_partition_file_mmap should calculate correct flash_file_size based on default partition table and return ESP_OK
|
||||
|
||||
// unmap file to have correct initial conditions, regardless of result
|
||||
esp_partition_file_munmap();
|
||||
@@ -357,22 +376,17 @@ TEST(partition_api, test_partition_mmap_size_no_partition)
|
||||
|
||||
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);
|
||||
TEST_ESP_OK(err);
|
||||
|
||||
// cleanup after test
|
||||
esp_partition_file_munmap();
|
||||
memset(p_file_mmap_ctrl_input, 0, sizeof(*p_file_mmap_ctrl_input));
|
||||
}
|
||||
|
||||
/* Negative TC to ensure mmap setup checks presence of flash size parameter if partition file name (partition table binary file) was specified.
|
||||
* This test case specifies just partition table binary file name but omits flash file size.
|
||||
*/
|
||||
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
|
||||
// Scenario: - flash_file_name empty, flash_file_size not set and partition_file_name set
|
||||
// esp_partition_file_mmap() will calculate flash_file_size based on given partition_table and return ESP_OK
|
||||
|
||||
// unmap file to have correct initial conditions, regardless of result
|
||||
esp_partition_file_munmap();
|
||||
@@ -382,14 +396,12 @@ TEST(partition_api, test_partition_mmap_no_size_partition)
|
||||
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";
|
||||
const char *partition_file_name = BUILD_DIR"/partition_table/partition-table.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);
|
||||
TEST_ESP_OK(err);
|
||||
|
||||
// cleanup after test
|
||||
esp_partition_file_munmap();
|
||||
@@ -463,38 +475,6 @@ TEST(partition_api, test_partition_mmap_pfile_nf)
|
||||
memset(p_file_mmap_ctrl_input, 0, sizeof(*p_file_mmap_ctrl_input));
|
||||
}
|
||||
|
||||
/* Negative TC to check that requested size of emulated flash is at least so big to be able to load binary partition table.
|
||||
* Too small emulated flash size is introduced and respective error code is evaluated after mmap call.
|
||||
*/
|
||||
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_DIR "/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 {
|
||||
size_t read_ops;
|
||||
size_t write_ops;
|
||||
@@ -772,6 +752,7 @@ 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_support_for_greater_than_4M);
|
||||
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);
|
||||
@@ -780,7 +761,6 @@ TEST_GROUP_RUNNER(partition_api)
|
||||
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_power_off_emulation);
|
||||
RUN_TEST_CASE(partition_api, test_partition_copy);
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
@@ -109,6 +110,64 @@ const char *esp_partition_subtype_to_str(const uint32_t type, const uint32_t sub
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
esp_err_t esp_partition_file_mmap(const uint8_t **part_desc_addr_start)
|
||||
{
|
||||
// temporary file is used only if control structure doesn't specify file name.
|
||||
@@ -135,21 +194,10 @@ esp_err_t esp_partition_file_mmap(const uint8_t **part_desc_addr_start)
|
||||
|
||||
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
|
||||
|
||||
// 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
|
||||
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: %" PRIu32 " was specified. Use either both parameters or none.",
|
||||
s_esp_partition_file_mmap_ctrl_input.partition_file_name,
|
||||
(uint32_t) 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_DIR "/partition_table/partition-table.bin", sizeof(s_esp_partition_file_mmap_ctrl_act.partition_file_name));
|
||||
@@ -157,10 +205,13 @@ esp_err_t esp_partition_file_mmap(const uint8_t **part_desc_addr_start)
|
||||
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 {
|
||||
// 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) {
|
||||
s_esp_partition_file_mmap_ctrl_act.flash_file_size = s_esp_partition_file_mmap_ctrl_input.flash_file_size;
|
||||
}
|
||||
|
||||
@@ -381,7 +432,7 @@ esp_err_t esp_partition_file_munmap(void)
|
||||
|
||||
esp_err_t esp_partition_write(const esp_partition_t *partition, size_t dst_offset, const void *src, size_t size)
|
||||
{
|
||||
assert(partition != NULL && s_spiflash_mem_file_buf != NULL);
|
||||
assert(partition != NULL && s_spiflash_mem_file_buf != NULL && src != NULL);
|
||||
|
||||
if (partition->readonly) {
|
||||
return ESP_ERR_NOT_ALLOWED;
|
||||
@@ -396,6 +447,15 @@ esp_err_t esp_partition_write(const esp_partition_t *partition, size_t dst_offse
|
||||
return ESP_ERR_INVALID_SIZE;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
void *dst_addr = s_spiflash_mem_file_buf + partition->address + dst_offset;
|
||||
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);
|
||||
|
||||
@@ -431,7 +491,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 && s_spiflash_mem_file_buf != NULL);
|
||||
assert(partition != NULL && s_spiflash_mem_file_buf != NULL && dst != NULL);
|
||||
|
||||
if (partition->encrypted) {
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
@@ -443,6 +503,15 @@ esp_err_t esp_partition_read(const esp_partition_t *partition, size_t src_offset
|
||||
return ESP_ERR_INVALID_SIZE;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
void *src_addr = s_spiflash_mem_file_buf + partition->address + src_offset;
|
||||
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);
|
||||
|
||||
@@ -467,7 +536,7 @@ esp_err_t esp_partition_write_raw(const esp_partition_t *partition, size_t dst_o
|
||||
|
||||
esp_err_t esp_partition_erase_range(const esp_partition_t *partition, size_t offset, size_t size)
|
||||
{
|
||||
assert(partition != NULL);
|
||||
assert(partition != NULL && s_spiflash_mem_file_buf != NULL);
|
||||
|
||||
if (partition->readonly) {
|
||||
return ESP_ERR_NOT_ALLOWED;
|
||||
@@ -479,6 +548,15 @@ esp_err_t esp_partition_erase_range(const esp_partition_t *partition, size_t off
|
||||
return ESP_ERR_INVALID_SIZE;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
void *target_addr = s_spiflash_mem_file_buf + partition->address + offset;
|
||||
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);
|
||||
|
||||
|
Reference in New Issue
Block a user