Skip to content

Commit 94b8128

Browse files
committed
Merge branch 'feat/support_larger_than_4m_partition_on_linux_target' into 'master'
feat(esp_partition): add support for partition tables larger than 4MB with linux target See merge request espressif/esp-idf!41312
2 parents 28c4172 + 2fa2010 commit 94b8128

File tree

2 files changed

+130
-72
lines changed

2 files changed

+130
-72
lines changed

components/esp_partition/host_test/partition_api_test/main/partition_api_test.c

Lines changed: 32 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
2+
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
33
*
44
* SPDX-License-Identifier: Apache-2.0
55
*
@@ -155,6 +155,29 @@ TEST(partition_api, test_partition_mmap)
155155
TEST_ASSERT_EQUAL(err, ESP_ERR_INVALID_SIZE);
156156
}
157157

158+
TEST(partition_api, test_partition_mmap_support_for_greater_than_4M)
159+
{
160+
// Scenario: Not specified flash size but provided partition table > 4M (default size supported)
161+
// esp_partition_mmap should calculate partition size from the binary, create mmap_flash_file and return ESP_OK
162+
163+
// unmap file to have correct initial conditions, regardless of result
164+
esp_partition_file_munmap();
165+
166+
// get and initialize the control structure for file mmap
167+
esp_partition_file_mmap_ctrl_t *p_file_mmap_ctrl = esp_partition_get_file_mmap_ctrl_input();
168+
TEST_ASSERT_NOT_NULL(p_file_mmap_ctrl);
169+
170+
memset(p_file_mmap_ctrl, 0, sizeof(*p_file_mmap_ctrl));
171+
strlcpy(p_file_mmap_ctrl->partition_file_name, BUILD_DIR"/partition_table/partition-table_8M.bin", sizeof(p_file_mmap_ctrl->partition_file_name));
172+
173+
// esp_partition_find_first calls the esp_partition_file_mmap in the background
174+
const esp_partition_t *partition_data = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, "storage");
175+
TEST_ASSERT_NOT_NULL(partition_data);
176+
177+
// cleanup after test
178+
esp_partition_file_munmap();
179+
}
180+
158181
TEST(partition_api, test_partition_mmap_diff_size)
159182
{
160183
// 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)
336359
memset(p_file_mmap_ctrl_input, 0, sizeof(*p_file_mmap_ctrl_input));
337360
}
338361

339-
/* Negative TC to ensure mmap setup checks presence of partition file name (partition table binary file)
340-
* if flash size parameter was specified.
341-
* This test case specifies just flash file size but omits partition table binary file name.
342-
*/
343362
TEST(partition_api, test_partition_mmap_size_no_partition)
344363
{
345-
// Negative Scenario: conflicting settings - flash_file_name empty, flash_file_size set and partition_file_name not set
346-
// esp_partition_file_mmap should return ESP_ERR_INVALID_ARG
364+
// Scenario: flash_file_name empty, incorrect flash_file_size set and partition_file_name not set
365+
// esp_partition_file_mmap should calculate correct flash_file_size based on default partition table and return ESP_OK
347366

348367
// unmap file to have correct initial conditions, regardless of result
349368
esp_partition_file_munmap();
@@ -357,22 +376,17 @@ TEST(partition_api, test_partition_mmap_size_no_partition)
357376

358377
const uint8_t *p_mem_block = NULL;
359378
esp_err_t err = esp_partition_file_mmap(&p_mem_block);
360-
361-
// expected result is invalid argument
362-
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, err);
379+
TEST_ESP_OK(err);
363380

364381
// cleanup after test
365382
esp_partition_file_munmap();
366383
memset(p_file_mmap_ctrl_input, 0, sizeof(*p_file_mmap_ctrl_input));
367384
}
368385

369-
/* Negative TC to ensure mmap setup checks presence of flash size parameter if partition file name (partition table binary file) was specified.
370-
* This test case specifies just partition table binary file name but omits flash file size.
371-
*/
372386
TEST(partition_api, test_partition_mmap_no_size_partition)
373387
{
374-
// Negative Scenario: conflicting settings - flash_file_name empty, flash_file_size not set and partition_file_name set
375-
// esp_partition_file_mmap should return ESP_ERR_INVALID_ARG
388+
// Scenario: - flash_file_name empty, flash_file_size not set and partition_file_name set
389+
// esp_partition_file_mmap() will calculate flash_file_size based on given partition_table and return ESP_OK
376390

377391
// unmap file to have correct initial conditions, regardless of result
378392
esp_partition_file_munmap();
@@ -382,14 +396,12 @@ TEST(partition_api, test_partition_mmap_no_size_partition)
382396
TEST_ASSERT_NOT_NULL(p_file_mmap_ctrl_input);
383397

384398
memset(p_file_mmap_ctrl_input, 0, sizeof(*p_file_mmap_ctrl_input));
385-
const char *partition_file_name = "/tmp/xyz.bin";
399+
const char *partition_file_name = BUILD_DIR"/partition_table/partition-table.bin";
386400
strlcpy(p_file_mmap_ctrl_input->partition_file_name, partition_file_name, sizeof(p_file_mmap_ctrl_input->partition_file_name));
387401

388402
const uint8_t *p_mem_block = NULL;
389403
esp_err_t err = esp_partition_file_mmap(&p_mem_block);
390-
391-
// expected result is invalid argument
392-
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, err);
404+
TEST_ESP_OK(err);
393405

394406
// cleanup after test
395407
esp_partition_file_munmap();
@@ -463,38 +475,6 @@ TEST(partition_api, test_partition_mmap_pfile_nf)
463475
memset(p_file_mmap_ctrl_input, 0, sizeof(*p_file_mmap_ctrl_input));
464476
}
465477

466-
/* Negative TC to check that requested size of emulated flash is at least so big to be able to load binary partition table.
467-
* Too small emulated flash size is introduced and respective error code is evaluated after mmap call.
468-
*/
469-
TEST(partition_api, test_partition_mmap_size_too_small)
470-
{
471-
// Negative Scenario: specified flash file size too small to hold at least partition table at default offset
472-
// esp_partition_file_mmap should return ESP_ERR_INVALID_SIZE
473-
474-
// unmap file to have correct initial conditions, regardless of result
475-
esp_partition_file_munmap();
476-
477-
// get and initialize the control structure for file mmap
478-
esp_partition_file_mmap_ctrl_t *p_file_mmap_ctrl_input = esp_partition_get_file_mmap_ctrl_input();
479-
TEST_ASSERT_NOT_NULL(p_file_mmap_ctrl_input);
480-
481-
memset(p_file_mmap_ctrl_input, 0, sizeof(*p_file_mmap_ctrl_input));
482-
483-
// set valid partition table name and very small flash size
484-
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));
485-
p_file_mmap_ctrl_input->flash_file_size = 1;
486-
487-
const uint8_t *p_mem_block = NULL;
488-
esp_err_t err = esp_partition_file_mmap(&p_mem_block);
489-
490-
// expected result is invalid argument
491-
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_SIZE, err);
492-
493-
// cleanup after test
494-
esp_partition_file_munmap();
495-
memset(p_file_mmap_ctrl_input, 0, sizeof(*p_file_mmap_ctrl_input));
496-
}
497-
498478
typedef struct {
499479
size_t read_ops;
500480
size_t write_ops;
@@ -772,6 +752,7 @@ TEST_GROUP_RUNNER(partition_api)
772752
RUN_TEST_CASE(partition_api, test_partition_find_first);
773753
RUN_TEST_CASE(partition_api, test_partition_ops);
774754
RUN_TEST_CASE(partition_api, test_partition_mmap);
755+
RUN_TEST_CASE(partition_api, test_partition_mmap_support_for_greater_than_4M);
775756
RUN_TEST_CASE(partition_api, test_partition_mmap_diff_size);
776757
RUN_TEST_CASE(partition_api, test_partition_mmap_reopen);
777758
RUN_TEST_CASE(partition_api, test_partition_mmap_remove);
@@ -780,7 +761,6 @@ TEST_GROUP_RUNNER(partition_api)
780761
RUN_TEST_CASE(partition_api, test_partition_mmap_no_size_partition);
781762
RUN_TEST_CASE(partition_api, test_partition_mmap_ffile_nf);
782763
RUN_TEST_CASE(partition_api, test_partition_mmap_pfile_nf);
783-
RUN_TEST_CASE(partition_api, test_partition_mmap_size_too_small);
784764
RUN_TEST_CASE(partition_api, test_partition_stats);
785765
RUN_TEST_CASE(partition_api, test_partition_power_off_emulation);
786766
RUN_TEST_CASE(partition_api, test_partition_copy);

components/esp_partition/partition_linux.c

Lines changed: 98 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
2+
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
33
*
44
* SPDX-License-Identifier: Apache-2.0
55
*/
@@ -8,6 +8,7 @@
88
#include <assert.h>
99
#include <string.h>
1010
#include <inttypes.h>
11+
#include <stdio.h>
1112
#include <sys/mman.h>
1213
#include <sys/stat.h>
1314
#include <fcntl.h>
@@ -110,6 +111,64 @@ const char *esp_partition_subtype_to_str(const uint32_t type, const uint32_t sub
110111
}
111112
}
112113

114+
// Calculate required emulated flash size from a partition table binary.
115+
// Returns 0 on failure.
116+
static size_t esp_partition_calc_required_flash_size_from_file(const char *partition_file_path)
117+
{
118+
if (partition_file_path == NULL || partition_file_path[0] == '\0') {
119+
return 0;
120+
}
121+
122+
FILE *fp = fopen(partition_file_path, "rb");
123+
if (fp == NULL) {
124+
return 0;
125+
}
126+
127+
// Determine file size as an additional lower bound
128+
if (fseek(fp, 0L, SEEK_END) != 0) {
129+
fclose(fp);
130+
return 0;
131+
}
132+
long file_size = ftell(fp);
133+
if (file_size < 0) {
134+
fclose(fp);
135+
return 0;
136+
}
137+
if (fseek(fp, 0L, SEEK_SET) != 0) {
138+
fclose(fp);
139+
return 0;
140+
}
141+
142+
size_t max_end = 0;
143+
size_t max_entries = file_size / sizeof(esp_partition_info_t);
144+
for (size_t i = 0; i < max_entries; i++) {
145+
esp_partition_info_t entry;
146+
size_t r = fread(&entry, 1, sizeof(entry), fp);
147+
if (r != sizeof(entry) || entry.magic != ESP_PARTITION_MAGIC) {
148+
break;
149+
}
150+
uint32_t end = entry.pos.offset + entry.pos.size;
151+
if (end > max_end) {
152+
max_end = end;
153+
}
154+
}
155+
156+
fclose(fp);
157+
158+
// Also ensure the flash holds the partition table itself at its offset
159+
size_t min_from_table_blob = (size_t)file_size + ESP_PARTITION_TABLE_OFFSET;
160+
size_t required = (max_end > min_from_table_blob) ? max_end : min_from_table_blob;
161+
162+
// Round up to emulated sector size
163+
size_t sector = ESP_PARTITION_EMULATED_SECTOR_SIZE;
164+
size_t rem = required % sector;
165+
if (rem != 0) {
166+
required += (sector - rem);
167+
}
168+
169+
return required;
170+
}
171+
113172
esp_err_t esp_partition_file_mmap(const uint8_t **part_desc_addr_start)
114173
{
115174
// temporary file is used only if control structure doesn't specify file name.
@@ -136,32 +195,24 @@ esp_err_t esp_partition_file_mmap(const uint8_t **part_desc_addr_start)
136195

137196
open_existing_file = true;
138197
} else {
139-
// Open temporary file. If size was specified, also partition table has to be specified, otherwise raise error.
140-
// If none of size, partition table were specified, defaults are used.
141-
// Name of temporary file is available in s_esp_partition_file_mmap_ctrl.flash_file_name
142-
198+
// 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
143199
bool has_partfile = (strlen(s_esp_partition_file_mmap_ctrl_input.partition_file_name) > 0);
144200
bool has_len = (s_esp_partition_file_mmap_ctrl_input.flash_file_size > 0);
145201

146-
// conflicting input
147-
if (has_partfile != has_len) {
148-
ESP_LOGE(TAG, "Invalid combination of Partition file name: %s flash file size: %" PRIu32 " was specified. Use either both parameters or none.",
149-
s_esp_partition_file_mmap_ctrl_input.partition_file_name,
150-
(uint32_t) s_esp_partition_file_mmap_ctrl_input.flash_file_size);
151-
return ESP_ERR_INVALID_ARG;
152-
}
153-
154202
// check if partition file is present, if not, use default
155203
if (!has_partfile) {
156204
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));
157205
} else {
158206
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));
159207
}
160208

161-
// check if flash size is present, if not set to default
162-
if (!has_len) {
163-
s_esp_partition_file_mmap_ctrl_act.flash_file_size = ESP_PARTITION_DEFAULT_EMULATED_FLASH_SIZE;
164-
} else {
209+
// derive the partition size from the s_esp_partition_file_mmap_ctrl_act.partition_file_name
210+
size_t derived_size = esp_partition_calc_required_flash_size_from_file(s_esp_partition_file_mmap_ctrl_act.partition_file_name);
211+
// if derived size is zero, use default partition size
212+
s_esp_partition_file_mmap_ctrl_act.flash_file_size = (derived_size > 0) ? derived_size : ESP_PARTITION_DEFAULT_EMULATED_FLASH_SIZE;
213+
214+
// if the size of the temporary file is specified, check if the given partition size fits within it
215+
if (has_len && s_esp_partition_file_mmap_ctrl_input.flash_file_size > derived_size) {
165216
s_esp_partition_file_mmap_ctrl_act.flash_file_size = s_esp_partition_file_mmap_ctrl_input.flash_file_size;
166217
}
167218

@@ -382,7 +433,7 @@ esp_err_t esp_partition_file_munmap(void)
382433

383434
esp_err_t esp_partition_write(const esp_partition_t *partition, size_t dst_offset, const void *src, size_t size)
384435
{
385-
assert(partition != NULL && s_spiflash_mem_file_buf != NULL);
436+
assert(partition != NULL && s_spiflash_mem_file_buf != NULL && src != NULL);
386437

387438
if (partition->readonly) {
388439
return ESP_ERR_NOT_ALLOWED;
@@ -397,6 +448,15 @@ esp_err_t esp_partition_write(const esp_partition_t *partition, size_t dst_offse
397448
return ESP_ERR_INVALID_SIZE;
398449
}
399450

451+
// Ensure write stays within mapped flash file size
452+
if (s_esp_partition_file_mmap_ctrl_act.flash_file_size > 0) {
453+
size_t start = (size_t)partition->address + dst_offset;
454+
size_t max_len = s_esp_partition_file_mmap_ctrl_act.flash_file_size;
455+
if ((start > max_len) || ((size + start) > max_len)) {
456+
return ESP_ERR_INVALID_SIZE;
457+
}
458+
}
459+
400460
void *dst_addr = s_spiflash_mem_file_buf + partition->address + dst_offset;
401461
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);
402462

@@ -432,7 +492,7 @@ esp_err_t esp_partition_write(const esp_partition_t *partition, size_t dst_offse
432492

433493
esp_err_t esp_partition_read(const esp_partition_t *partition, size_t src_offset, void *dst, size_t size)
434494
{
435-
assert(partition != NULL && s_spiflash_mem_file_buf != NULL);
495+
assert(partition != NULL && s_spiflash_mem_file_buf != NULL && dst != NULL);
436496

437497
if (partition->encrypted) {
438498
return ESP_ERR_NOT_SUPPORTED;
@@ -444,6 +504,15 @@ esp_err_t esp_partition_read(const esp_partition_t *partition, size_t src_offset
444504
return ESP_ERR_INVALID_SIZE;
445505
}
446506

507+
// Ensure read stays within mapped flash file size
508+
if (s_esp_partition_file_mmap_ctrl_act.flash_file_size > 0) {
509+
size_t start = (size_t)partition->address + src_offset;
510+
size_t max_len = s_esp_partition_file_mmap_ctrl_act.flash_file_size;
511+
if ((start > max_len) || ((size + start) > max_len)) {
512+
return ESP_ERR_INVALID_SIZE;
513+
}
514+
}
515+
447516
void *src_addr = s_spiflash_mem_file_buf + partition->address + src_offset;
448517
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);
449518

@@ -468,7 +537,7 @@ esp_err_t esp_partition_write_raw(const esp_partition_t *partition, size_t dst_o
468537

469538
esp_err_t esp_partition_erase_range(const esp_partition_t *partition, size_t offset, size_t size)
470539
{
471-
assert(partition != NULL);
540+
assert(partition != NULL && s_spiflash_mem_file_buf != NULL);
472541

473542
if (partition->readonly) {
474543
return ESP_ERR_NOT_ALLOWED;
@@ -480,6 +549,15 @@ esp_err_t esp_partition_erase_range(const esp_partition_t *partition, size_t off
480549
return ESP_ERR_INVALID_SIZE;
481550
}
482551

552+
// Ensure erase stays within mapped flash file size
553+
if (s_esp_partition_file_mmap_ctrl_act.flash_file_size > 0) {
554+
size_t start = (size_t)partition->address + offset;
555+
size_t max_len = s_esp_partition_file_mmap_ctrl_act.flash_file_size;
556+
if ((start > max_len) || ((size + start) > max_len)) {
557+
return ESP_ERR_INVALID_SIZE;
558+
}
559+
}
560+
483561
void *target_addr = s_spiflash_mem_file_buf + partition->address + offset;
484562
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);
485563

0 commit comments

Comments
 (0)