Skip to content

Commit 1e1b95c

Browse files
committed
Merge branch 'feature/esp_partition_bdl_support' into 'master'
Feat(storage): esp_partition BDL support Closes IDF-12748 See merge request espressif/esp-idf!40288
2 parents c27bcca + 3ce5725 commit 1e1b95c

File tree

22 files changed

+794
-49
lines changed

22 files changed

+794
-49
lines changed

components/esp_blockdev/include/esp_blockdev.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -217,8 +217,8 @@ typedef struct esp_blockdev_t {
217217
/* Device context pointer */
218218
void* ctx;
219219

220-
const esp_blockdev_flags_t device_flags;
221-
const esp_blockdev_geometry_t geometry;
220+
esp_blockdev_flags_t device_flags;
221+
esp_blockdev_geometry_t geometry;
222222
const esp_blockdev_ops_t* ops;
223223

224224
} esp_blockdev_t;
Lines changed: 47 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,67 @@
11
idf_build_get_property(esp_tee_build ESP_TEE_BUILD)
22

3+
set(reqs esp_blockdev)
4+
35
# bootloader build simplified version
46
if(BOOTLOADER_BUILD)
5-
set(srcs "partition_bootloader.c")
6-
set(reqs "spi_flash")
7-
set(priv_reqs "bootloader_support")
7+
set(srcs "partition_bootloader.c")
8+
list(APPEND reqs spi_flash)
9+
set(priv_reqs "bootloader_support")
810

9-
idf_component_register(SRCS "${srcs}"
10-
INCLUDE_DIRS "include"
11-
PRIV_INCLUDE_DIRS ${private_include_dirs}
12-
REQUIRES ${reqs}
13-
PRIV_REQUIRES ${priv_reqs})
11+
idf_component_register(SRCS "${srcs}"
12+
INCLUDE_DIRS "include"
13+
PRIV_INCLUDE_DIRS ${private_include_dirs}
14+
REQUIRES ${reqs}
15+
PRIV_REQUIRES ${priv_reqs})
1416

1517
# esp-tee build simplified version
1618
elseif(esp_tee_build)
17-
set(srcs "partition_tee.c")
18-
set(reqs "spi_flash")
19-
set(priv_reqs "tee_flash_mgr")
19+
set(srcs "partition_tee.c")
20+
list(APPEND reqs spi_flash)
21+
set(priv_reqs "tee_flash_mgr")
2022

21-
idf_component_register(SRCS "${srcs}"
22-
INCLUDE_DIRS "include"
23-
PRIV_INCLUDE_DIRS ${private_include_dirs}
24-
REQUIRES ${reqs}
25-
PRIV_REQUIRES ${priv_reqs})
23+
idf_component_register(SRCS "${srcs}"
24+
INCLUDE_DIRS "include"
25+
PRIV_INCLUDE_DIRS ${private_include_dirs}
26+
REQUIRES ${reqs}
27+
PRIV_REQUIRES ${priv_reqs})
2628

2729
# regular, OS build
2830
else()
29-
set(srcs "partition.c")
30-
set(priv_reqs esp_system spi_flash partition_table)
31-
set(reqs)
32-
set(private_include_dirs)
31+
set(srcs "partition.c")
32+
set(priv_reqs esp_system spi_flash partition_table)
33+
set(reqs esp_blockdev)
34+
set(private_include_dirs)
3335

34-
idf_build_get_property(build_dir BUILD_DIR)
35-
idf_build_get_property(target IDF_TARGET)
36+
idf_build_get_property(build_dir BUILD_DIR)
37+
idf_build_get_property(target IDF_TARGET)
3638

37-
if(${target} STREQUAL "linux")
38-
list(APPEND srcs "partition_linux.c")
39+
if(${target} STREQUAL "linux")
40+
list(APPEND srcs "partition_linux.c")
3941

40-
# Steal some include directories from bootloader_support components:
41-
idf_component_get_property(bootloader_support_dir bootloader_support COMPONENT_DIR)
42-
set(private_include_dirs ${bootloader_support_dir}/include)
43-
else()
44-
list(APPEND priv_reqs bootloader_support app_update)
45-
list(APPEND srcs "partition_target.c")
46-
endif()
42+
# Steal some include directories from bootloader_support components:
43+
idf_component_get_property(bootloader_support_dir bootloader_support COMPONENT_DIR)
44+
set(private_include_dirs ${bootloader_support_dir}/include)
45+
else()
46+
list(APPEND priv_reqs bootloader_support app_update)
47+
list(APPEND srcs "partition_target.c")
48+
endif()
4749

48-
idf_component_register(SRCS "${srcs}"
49-
INCLUDE_DIRS "include"
50-
PRIV_INCLUDE_DIRS ${private_include_dirs}
51-
REQUIRES ${reqs}
52-
PRIV_REQUIRES ${priv_reqs})
50+
idf_component_register(SRCS "${srcs}"
51+
INCLUDE_DIRS "include"
52+
PRIV_INCLUDE_DIRS ${private_include_dirs}
53+
REQUIRES ${reqs}
54+
PRIV_REQUIRES ${priv_reqs})
5355

54-
if(${target} STREQUAL "linux")
55-
# set BUILD_DIR because partition_linux.c uses a file created in the build directory
56-
target_compile_definitions(${COMPONENT_LIB} PRIVATE "BUILD_DIR=\"${build_dir}\"")
57-
endif()
56+
if(${target} STREQUAL "linux")
57+
# set BUILD_DIR because partition_linux.c uses a file created in the build directory
58+
target_compile_definitions(${COMPONENT_LIB} PRIVATE "BUILD_DIR=\"${build_dir}\"")
59+
endif()
5860

59-
if(CMAKE_C_COMPILER_ID MATCHES "GNU")
60-
# These flags are GCC specific
61-
set_property(SOURCE ${cache_srcs} APPEND_STRING PROPERTY COMPILE_FLAGS
62-
" -fno-inline-small-functions -fno-inline-functions-called-once")
63-
endif()
61+
if(CMAKE_C_COMPILER_ID MATCHES "GNU")
62+
# These flags are GCC specific
63+
set_property(SOURCE ${cache_srcs} APPEND_STRING PROPERTY COMPILE_FLAGS
64+
" -fno-inline-small-functions -fno-inline-functions-called-once")
65+
endif()
6466

6567
endif()

components/esp_partition/host_test/.build-test-rules.yml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,12 @@ components/esp_partition/host_test/partition_api_test:
55
- if: IDF_TARGET == "linux"
66
reason: only test on linux
77
depends_components:
8-
- spi_flash
8+
- esp_partition
9+
10+
components/esp_partition/host_test/partition_bdl_test:
11+
enable:
12+
- if: IDF_TARGET == "linux"
13+
reason: only test on linux
14+
depends_components:
15+
- esp_blockdev
916
- esp_partition

components/esp_partition/host_test/partition_api_test/README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
| Supported Targets | Linux |
22
| ----------------- | ----- |
33

4-
This is a test project for partition-related APIs on Linux target (CONFIG_IDF_TARGET_LINUX).
4+
This is a test project for verification of 'esp_partition' component APIs on Linux target (CONFIG_IDF_TARGET_LINUX).
5+
It verifies all important APIs and properties, and prints the results.
6+
The Block-Device Layer tests have names with a prefix 'test_partition_bdl', available in 'components/esp_partition/host_test/partition_bdl_test', and the tests check all the BDL operations and commands related to 'esp_partition' (on host side)
57

68
# Build
79
Source the IDF environment as usual.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
cmake_minimum_required(VERSION 3.16)
2+
3+
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
4+
set(COMPONENTS main)
5+
# Freertos is included via common components, however, currently only the mock component is compatible with linux
6+
# target.
7+
list(APPEND EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/mocks/freertos/")
8+
9+
project(partition_bdl_test)
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
| Supported Targets | Linux |
2+
| ----------------- | ----- |
3+
4+
This is a test project for 'esp_partition' Block Device Layer interface on Linux target (CONFIG_IDF_TARGET_LINUX).
5+
6+
# Build
7+
Source the IDF environment as usual.
8+
9+
Once this is done, build the application:
10+
```bash
11+
idf.py build
12+
```
13+
14+
# Run
15+
```bash
16+
idf.py monitor
17+
```
18+
19+
The tests will be executed and the summary will be printed.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
idf_component_register(SRCS "partition_bdl_test.c"
2+
PRIV_REQUIRES esp_partition unity spi_flash)
3+
4+
# set BUILD_DIR because test uses a file created in the build directory
5+
target_compile_definitions(${COMPONENT_LIB} PRIVATE "BUILD_DIR=\"${build_dir}\"")
Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
/*
2+
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*
6+
* Linux host partition API test
7+
*/
8+
9+
#include <string.h>
10+
#include <unistd.h>
11+
#include "esp_partition.h"
12+
#include "esp_private/partition_linux.h"
13+
#include "unity.h"
14+
#include "unity_fixture.h"
15+
16+
const char *TAG = "partition_bdl_test";
17+
18+
TEST_GROUP(partition_bdl);
19+
20+
TEST_SETUP(partition_bdl)
21+
{
22+
}
23+
24+
TEST_TEAR_DOWN(partition_bdl)
25+
{
26+
}
27+
28+
/* Test all the basic Block Device Layer APIs working correctly over emulated esp_partition instance.
29+
* NOR flash behavior expected.
30+
* */
31+
TEST(partition_bdl, test_partition_bdl_ops)
32+
{
33+
//get the block-device interface instance
34+
esp_blockdev_handle_t part_blockdev = NULL;
35+
TEST_ESP_OK(esp_partition_get_blockdev(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, "storage1", &part_blockdev));
36+
37+
const size_t data_size = 256;
38+
uint8_t test_data[data_size];
39+
const off_t target_addr = 3*4*1024;
40+
uint8_t data_buffer[data_size];
41+
memset((void*)data_buffer, 0, data_size);
42+
43+
//erase the first sector data from the blockdev and check it's really wiped
44+
TEST_ESP_OK(part_blockdev->ops->erase(part_blockdev, target_addr, part_blockdev->geometry.erase_size));
45+
memset((void*)test_data, 0xFF, data_size); //erased NOR flash sector contains only 1s
46+
47+
TEST_ESP_OK(part_blockdev->ops->read(part_blockdev, data_buffer, sizeof(data_buffer), target_addr, data_size));
48+
TEST_ASSERT_EQUAL(0, memcmp(test_data, data_buffer, data_size));
49+
50+
//write to the blockdev
51+
memset((void*)test_data, 'A', data_size);
52+
TEST_ESP_OK(part_blockdev->ops->write(part_blockdev, test_data, target_addr, data_size));
53+
54+
//read from the blockdev the data written before
55+
TEST_ESP_OK(part_blockdev->ops->read(part_blockdev, data_buffer, sizeof(data_buffer), target_addr, data_size));
56+
TEST_ASSERT_EQUAL(0, memcmp(test_data, data_buffer, data_size));
57+
58+
//release the BDL object
59+
TEST_ESP_OK(part_blockdev->ops->release(part_blockdev));
60+
}
61+
62+
/* Test parallel access to two independent partitions through BDL interface.
63+
* Use both 'esp_partition_t' and type-subtype-label BDL getters.
64+
* NOR flash behavior expected.
65+
* */
66+
TEST(partition_bdl, test_two_partitions_bdl_ops)
67+
{
68+
//get the block-device interface instance for partition 'storage1'
69+
esp_blockdev_handle_t part_blockdev_1 = NULL;
70+
TEST_ESP_OK(esp_partition_get_blockdev(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, "storage1", &part_blockdev_1));
71+
72+
//get pointer to esp_partition_t object for partition 'storage2'
73+
esp_partition_iterator_t iter = esp_partition_find(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, "storage2");
74+
TEST_ASSERT_NOT_NULL(iter);
75+
esp_partition_t *part = (esp_partition_t*)esp_partition_get(iter);
76+
TEST_ASSERT_NOT_NULL(part);
77+
esp_partition_iterator_release(iter);
78+
79+
esp_blockdev_handle_t part_blockdev_2 = NULL;
80+
TEST_ESP_OK(esp_partition_ptr_get_blockdev(part, &part_blockdev_2));
81+
82+
//erase & write & read data on both partitions in parallel
83+
const size_t data_size = 256;
84+
uint8_t test_data[data_size];
85+
const off_t target_addr = 3*4*1024;
86+
uint8_t data_buffer_1[data_size];
87+
uint8_t data_buffer_2[data_size];
88+
memset((void*)data_buffer_1, 0, data_size);
89+
memset((void*)data_buffer_2, 0, data_size);
90+
91+
//erase the first sector data from the blockdev and check it's really wiped
92+
TEST_ESP_OK(part_blockdev_1->ops->erase(part_blockdev_1, target_addr, part_blockdev_1->geometry.erase_size));
93+
TEST_ESP_OK(part_blockdev_2->ops->erase(part_blockdev_2, target_addr, part_blockdev_2->geometry.erase_size));
94+
memset((void*)test_data, 0xFF, data_size); //erased NOR flash sector contains only 1s
95+
96+
TEST_ESP_OK(part_blockdev_1->ops->read(part_blockdev_1, data_buffer_1, sizeof(data_buffer_1), target_addr, data_size));
97+
TEST_ESP_OK(part_blockdev_2->ops->read(part_blockdev_2, data_buffer_2, sizeof(data_buffer_2), target_addr, data_size));
98+
TEST_ASSERT_EQUAL(0, memcmp(test_data, data_buffer_1, data_size));
99+
TEST_ASSERT_EQUAL(0, memcmp(test_data, data_buffer_2, data_size));
100+
101+
//write to the blockdev 1
102+
memset((void*)test_data, 'A', data_size);
103+
TEST_ESP_OK(part_blockdev_1->ops->write(part_blockdev_1, test_data, target_addr, data_size));
104+
105+
//read the data written before from the blockdev 1
106+
TEST_ESP_OK(part_blockdev_1->ops->read(part_blockdev_1, data_buffer_1, sizeof(data_buffer_1), target_addr, data_size));
107+
TEST_ASSERT_EQUAL(0, memcmp(test_data, data_buffer_1, data_size));
108+
109+
//write to the blockdev 2
110+
memset((void*)test_data, 'B', data_size);
111+
TEST_ESP_OK(part_blockdev_2->ops->write(part_blockdev_2, test_data, target_addr, data_size));
112+
113+
//read the data written before from the blockdev 2
114+
TEST_ESP_OK(part_blockdev_2->ops->read(part_blockdev_2, data_buffer_2, sizeof(data_buffer_2), target_addr, data_size));
115+
TEST_ASSERT_EQUAL(0, memcmp(test_data, data_buffer_2, data_size));
116+
117+
//release the BDL objects
118+
TEST_ESP_OK(part_blockdev_1->ops->release(part_blockdev_1));
119+
TEST_ESP_OK(part_blockdev_2->ops->release(part_blockdev_2));
120+
}
121+
122+
TEST(partition_bdl, test_partition_bdl_limits)
123+
{
124+
// Limits tested:
125+
// - geometry alignment with parameter size for all ops
126+
// - dst buffer size check when reading
127+
// Partition boundaries and other checks are provided by underlying partition APIs and are not verified here
128+
129+
esp_blockdev_handle_t part_blockdev = NULL;
130+
TEST_ESP_OK(esp_partition_get_blockdev(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, "storage1", &part_blockdev));
131+
132+
//artifitial setup setup for this test only
133+
part_blockdev->geometry.erase_size = 512;
134+
part_blockdev->geometry.read_size = 4;
135+
part_blockdev->geometry.write_size = 16;
136+
137+
const size_t data_size = part_blockdev->geometry.erase_size;
138+
uint8_t test_data[data_size];
139+
uint8_t test_data_err[data_size-3];
140+
const off_t target_addr_err = 3;
141+
142+
//correct addr, wrong length
143+
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_SIZE, part_blockdev->ops->erase(part_blockdev, 0, data_size+3));
144+
//wrong addr, correct length
145+
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_SIZE, part_blockdev->ops->erase(part_blockdev, target_addr_err, part_blockdev->geometry.erase_size));
146+
147+
//correct addr, correct dst buff, wrong length
148+
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_SIZE, part_blockdev->ops->read(part_blockdev, test_data, data_size, 0, data_size+3));
149+
//wrong addr, correct dst buff, correct length
150+
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_SIZE, part_blockdev->ops->read(part_blockdev, test_data, data_size, target_addr_err, data_size));
151+
//correct addr, wrong dst buff, correct length
152+
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, part_blockdev->ops->read(part_blockdev, test_data_err, sizeof(test_data_err), 0, data_size));
153+
154+
//correct addr, wrong length
155+
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_SIZE, part_blockdev->ops->write(part_blockdev, test_data, 0, data_size+3));
156+
//wrong addr, correct length
157+
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_SIZE, part_blockdev->ops->write(part_blockdev, test_data, target_addr_err, data_size));
158+
159+
//release the BDL objects
160+
TEST_ESP_OK(part_blockdev->ops->release(part_blockdev));
161+
}
162+
163+
TEST(partition_bdl, test_bdl_partition_readonly)
164+
{
165+
//storage3 is readonly partition
166+
esp_blockdev_handle_t part_blockdev = NULL;
167+
TEST_ESP_OK(esp_partition_get_blockdev(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, "storage3", &part_blockdev));
168+
169+
//test flags
170+
TEST_ASSERT_EQUAL(1, part_blockdev->device_flags.read_only);
171+
TEST_ASSERT_EQUAL(0, part_blockdev->geometry.write_size);
172+
TEST_ASSERT_EQUAL(0, part_blockdev->geometry.erase_size);
173+
TEST_ASSERT_EQUAL(0, part_blockdev->geometry.recommended_write_size);
174+
TEST_ASSERT_EQUAL(0, part_blockdev->geometry.recommended_erase_size);
175+
176+
uint8_t dummy_test_buff[1024];
177+
178+
//write & erase must fail and read must work normally
179+
TEST_ASSERT_EQUAL(ESP_ERR_NOT_SUPPORTED, part_blockdev->ops->erase(part_blockdev, 0, sizeof(dummy_test_buff)));
180+
TEST_ASSERT_EQUAL(ESP_ERR_NOT_SUPPORTED, part_blockdev->ops->write(part_blockdev, dummy_test_buff, 0, sizeof(dummy_test_buff)));
181+
TEST_ESP_OK(part_blockdev->ops->read(part_blockdev, dummy_test_buff, sizeof(dummy_test_buff), 0, sizeof(dummy_test_buff)));
182+
183+
TEST_ESP_OK(part_blockdev->ops->release(part_blockdev));
184+
}
185+
186+
TEST_GROUP_RUNNER(partition_bdl)
187+
{
188+
RUN_TEST_CASE(partition_bdl, test_partition_bdl_ops);
189+
RUN_TEST_CASE(partition_bdl, test_two_partitions_bdl_ops);
190+
RUN_TEST_CASE(partition_bdl, test_partition_bdl_limits);
191+
RUN_TEST_CASE(partition_bdl, test_bdl_partition_readonly);
192+
}
193+
194+
static void run_all_tests(void)
195+
{
196+
RUN_TEST_GROUP(partition_bdl);
197+
}
198+
199+
int main(int argc, char **argv)
200+
{
201+
UNITY_MAIN_FUNC(run_all_tests);
202+
return 0;
203+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Name, Type, SubType, Offset, Size, Flags
2+
# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
3+
nvs, data, nvs, 0x9000, 0x6000,
4+
phy_init, data, phy, 0xf000, 0x1000,
5+
factory, app, factory, 0x10000, 1M,
6+
storage1, data, , 0x110000, 512K,
7+
storage2, data, , 0x190000, 128K,
8+
storage3, data, , 0x1B0000, 128K, readonly

0 commit comments

Comments
 (0)