1 /*
2 * Copyright (c) 2017 Nordic Semiconductor ASA
3 * Copyright (c) 2015 Runtime Inc
4 * Copyright (c) 2020 Gerson Fernando Budke <nandojve@gmail.com>
5 * Copyright 2024 NXP
6 *
7 * SPDX-License-Identifier: Apache-2.0
8 */
9
10 #include <zephyr/ztest.h>
11 #include <zephyr/drivers/flash.h>
12 #include <zephyr/storage/flash_map.h>
13
14 #define SLOT1_PARTITION slot1_partition
15 #define SLOT1_PARTITION_ID FIXED_PARTITION_ID(SLOT1_PARTITION)
16 #define SLOT1_PARTITION_DEV FIXED_PARTITION_DEVICE(SLOT1_PARTITION)
17 #define SLOT1_PARTITION_NODE DT_NODELABEL(SLOT1_PARTITION)
18 #define SLOT1_PARTITION_OFFSET FIXED_PARTITION_OFFSET(SLOT1_PARTITION)
19 #define SLOT1_PARTITION_SIZE FIXED_PARTITION_SIZE(SLOT1_PARTITION)
20
21 #define FLASH_AREA_COPY_SIZE MIN((SLOT1_PARTITION_SIZE / 2), 128)
22
23 extern int flash_map_entries;
24 struct flash_sector fs_sectors[2048];
25
ZTEST(flash_map,test_flash_area_disabled_device)26 ZTEST(flash_map, test_flash_area_disabled_device)
27 {
28 /* The test checks if Flash Map will report partition
29 * non-existend if it is disabled, but it also assumes that
30 * disabled parition will have an ID generated.
31 * Custom partition maps may not be generating entries for
32 * disabled partitions nor identifiers, which makes the
33 * test fail with custom partition manager, for no real reason.
34 */
35 #if defined(CONFIG_TEST_FLASH_MAP_DISABLED_PARTITIONS)
36 const struct flash_area *fa;
37 int rc;
38
39 /* Test that attempting to open a disabled flash area fails */
40 rc = flash_area_open(FIXED_PARTITION_ID(disabled_a), &fa);
41 zassert_equal(rc, -ENOENT, "Open did not fail");
42 rc = flash_area_open(FIXED_PARTITION_ID(disabled_a_a), &fa);
43 zassert_equal(rc, -ENOENT, "Open did not fail");
44 rc = flash_area_open(FIXED_PARTITION_ID(disabled_a_b), &fa);
45 zassert_equal(rc, -ENOENT, "Open did not fail");
46 rc = flash_area_open(FIXED_PARTITION_ID(disabled_b), &fa);
47 zassert_equal(rc, -ENOENT, "Open did not fail");
48 rc = flash_area_open(FIXED_PARTITION_ID(disabled_b_a), &fa);
49 zassert_equal(rc, -ENOENT, "Open did not fail");
50 rc = flash_area_open(FIXED_PARTITION_ID(disabled_b_b), &fa);
51 zassert_equal(rc, -ENOENT, "Open did not fail");
52
53 /* Note lack of tests for FIXED_PARTITION(...) instantiation,
54 * because this macro will fail, at compile time, if node does not
55 * exist or is disabled.
56 */
57 #else
58 ztest_test_skip();
59 #endif
60 }
61
ZTEST(flash_map,test_flash_area_device_is_ready)62 ZTEST(flash_map, test_flash_area_device_is_ready)
63 {
64 const struct flash_area no_dev = {
65 .fa_dev = NULL,
66 };
67
68 zassert_false(flash_area_device_is_ready(NULL));
69 zassert_false(flash_area_device_is_ready(&no_dev));
70 /* The below just assumes that tests are executed so late that
71 * all devices are already initialized and ready.
72 */
73 zassert_true(flash_area_device_is_ready(
74 FIXED_PARTITION(SLOT1_PARTITION)));
75 }
76
layout_match(const struct device * flash_dev,uint32_t sec_cnt)77 static void layout_match(const struct device *flash_dev, uint32_t sec_cnt)
78 {
79 off_t off = 0;
80 int i;
81
82 /* For each reported sector, check if it corresponds to real page on device */
83 for (i = 0; i < sec_cnt; ++i) {
84 struct flash_pages_info fpi;
85
86 zassert_ok(
87 flash_get_page_info_by_offs(flash_dev, SLOT1_PARTITION_OFFSET + off, &fpi));
88 /* Offset of page taken directly from device corresponds to offset
89 * within flash area
90 */
91 zassert_equal(fpi.start_offset, fs_sectors[i].fs_off + SLOT1_PARTITION_OFFSET);
92 zassert_equal(fpi.size, fs_sectors[i].fs_size);
93 off += fs_sectors[i].fs_size;
94 }
95 }
96
97 /**
98 * @brief Test flash_area_get_sectors()
99 */
ZTEST(flash_map,test_flash_area_get_sectors)100 ZTEST(flash_map, test_flash_area_get_sectors)
101 {
102 const struct flash_area *fa;
103 const struct device *flash_dev_a = SLOT1_PARTITION_DEV;
104 uint32_t sec_cnt;
105 int rc;
106
107 fa = FIXED_PARTITION(SLOT1_PARTITION);
108
109 zassert_true(flash_area_device_is_ready(fa));
110
111 zassert_true(device_is_ready(flash_dev_a));
112
113 /* Device obtained by label should match the one from fa object */
114 zassert_equal(fa->fa_dev, flash_dev_a, "Device for slot1_partition do not match");
115
116 memset(&fs_sectors[0], 0, sizeof(fs_sectors));
117
118 sec_cnt = ARRAY_SIZE(fs_sectors);
119 rc = flash_area_get_sectors(SLOT1_PARTITION_ID, &sec_cnt, fs_sectors);
120 zassert_true(rc == 0, "flash_area_get_sectors failed");
121
122 layout_match(flash_dev_a, sec_cnt);
123 }
124
ZTEST(flash_map,test_flash_area_sectors)125 ZTEST(flash_map, test_flash_area_sectors)
126 {
127 const struct flash_area *fa;
128 uint32_t sec_cnt;
129 int rc;
130 const struct device *flash_dev_a = SLOT1_PARTITION_DEV;
131
132 fa = FIXED_PARTITION(SLOT1_PARTITION);
133
134 zassert_true(flash_area_device_is_ready(fa));
135
136 zassert_true(device_is_ready(flash_dev_a));
137
138 /* Device obtained by label should match the one from fa object */
139 zassert_equal(fa->fa_dev, flash_dev_a, "Device for slot1_partition do not match");
140
141 sec_cnt = ARRAY_SIZE(fs_sectors);
142 rc = flash_area_sectors(fa, &sec_cnt, fs_sectors);
143 zassert_true(rc == 0, "flash_area_get_sectors failed");
144
145 layout_match(flash_dev_a, sec_cnt);
146 }
147
ZTEST(flash_map,test_flash_area_erased_val)148 ZTEST(flash_map, test_flash_area_erased_val)
149 {
150 const struct flash_parameters *param;
151 const struct flash_area *fa;
152 uint8_t val;
153
154 fa = FIXED_PARTITION(SLOT1_PARTITION);
155
156 val = flash_area_erased_val(fa);
157
158 param = flash_get_parameters(fa->fa_dev);
159
160 zassert_equal(param->erase_value, val,
161 "value different than the flash erase value");
162 }
163
ZTEST(flash_map,test_fixed_partition_node_macros)164 ZTEST(flash_map, test_fixed_partition_node_macros)
165 {
166 /* DTS node macros, for accessing fixed partitions, are only available
167 * for DTS based partitions; custom flash map may define partition outside
168 * of DTS definition, making the NODE macros fail to evaluate.
169 */
170 #if defined(CONFIG_TEST_FLASH_MAP_NODE_MACROS)
171 /* Test against changes in API */
172 zassert_equal(FIXED_PARTITION_NODE_OFFSET(SLOT1_PARTITION_NODE),
173 DT_REG_ADDR(SLOT1_PARTITION_NODE));
174 zassert_equal(FIXED_PARTITION_NODE_SIZE(SLOT1_PARTITION_NODE),
175 DT_REG_SIZE(SLOT1_PARTITION_NODE));
176 zassert_equal(FIXED_PARTITION_NODE_DEVICE(SLOT1_PARTITION_NODE),
177 DEVICE_DT_GET(DT_MTD_FROM_FIXED_PARTITION(SLOT1_PARTITION_NODE)));
178
179 /* Taking by node and taking by label should give same device */
180 zassert_equal(FIXED_PARTITION_BY_NODE(DT_NODELABEL(SLOT1_PARTITION)),
181 FIXED_PARTITION(SLOT1_PARTITION));
182 #else
183 ztest_test_skip();
184 #endif
185 }
186
ZTEST(flash_map,test_fixed_subpartition_node_macros)187 ZTEST(flash_map, test_fixed_subpartition_node_macros)
188 {
189 /* DTS node macros, for accessing fixed partitions, are only available
190 * for DTS based partitions; custom flash map may define partition outside
191 * of DTS definition, making the NODE macros fail to evaluate.
192 */
193 #if defined(CONFIG_TEST_FLASH_MAP_NODE_MACROS)
194 /* Test that both partitions and subpartitions exist */
195 zassert_true(FIXED_PARTITION_EXISTS(disabled_a));
196 zassert_true(FIXED_PARTITION_EXISTS(disabled_a_a));
197 zassert_true(FIXED_PARTITION_EXISTS(disabled_a_b));
198
199 /* Test that the subpartition offset is relative to the parent */
200 zassert_equal(FIXED_PARTITION_OFFSET(disabled_b),
201 FIXED_PARTITION_OFFSET(disabled_b_a));
202 zassert_equal(FIXED_PARTITION_OFFSET(disabled_b) + 0x100,
203 FIXED_PARTITION_OFFSET(disabled_b_b));
204 zassert_equal(FIXED_PARTITION_NODE_OFFSET(DT_NODELABEL(disabled_b)),
205 FIXED_PARTITION_NODE_OFFSET(DT_NODELABEL(disabled_b_a)));
206 zassert_equal(
207 FIXED_PARTITION_NODE_OFFSET(DT_NODELABEL(disabled_b)) + 0x100,
208 FIXED_PARTITION_NODE_OFFSET(DT_NODELABEL(disabled_b_b)));
209
210 /* Test that the subpartition address is relative to the parent */
211 zassert_equal(FIXED_PARTITION_ADDRESS(disabled_b),
212 FIXED_PARTITION_ADDRESS(disabled_b_a));
213 zassert_equal(FIXED_PARTITION_ADDRESS(disabled_b) + 0x100,
214 FIXED_PARTITION_ADDRESS(disabled_b_b));
215 zassert_equal(FIXED_PARTITION_NODE_ADDRESS(DT_NODELABEL(disabled_b)),
216 FIXED_PARTITION_NODE_ADDRESS(DT_NODELABEL(disabled_b_a)));
217 zassert_equal(
218 FIXED_PARTITION_NODE_ADDRESS(DT_NODELABEL(disabled_b)) + 0x100,
219 FIXED_PARTITION_NODE_ADDRESS(DT_NODELABEL(disabled_b_b)));
220 #else
221 ztest_test_skip();
222 #endif
223 }
224
ZTEST(flash_map,test_flash_area_erase_and_flatten)225 ZTEST(flash_map, test_flash_area_erase_and_flatten)
226 {
227 int i;
228 bool erased = true;
229 int rc;
230 const struct flash_area *fa;
231 const struct device *flash_dev;
232
233 fa = FIXED_PARTITION(SLOT1_PARTITION);
234
235 /* First erase the area so it's ready for use. */
236 flash_dev = flash_area_get_device(fa);
237
238 rc = flash_erase(flash_dev, fa->fa_off, fa->fa_size);
239 zassert_true(rc == 0, "flash area erase fail");
240
241 rc = flash_fill(flash_dev, 0xaa, fa->fa_off, fa->fa_size);
242 zassert_true(rc == 0, "flash device fill fail");
243
244 rc = flash_area_erase(fa, 0, fa->fa_size);
245 zassert_true(rc == 0, "flash area erase fail");
246
247 TC_PRINT("Flash area info:\n");
248 TC_PRINT("\tpointer:\t %p\n", &fa);
249 TC_PRINT("\toffset:\t %ld\n", (long)fa->fa_off);
250 TC_PRINT("\tsize:\t %ld\n", (long)fa->fa_size);
251
252 /* we work under assumption that flash_fill is working and tested */
253 i = 0;
254 while (erased && i < fa->fa_size) {
255 uint8_t buf[32];
256 int chunk = MIN(sizeof(buf), fa->fa_size - i);
257
258 rc = flash_read(flash_dev, fa->fa_off + i, buf, chunk);
259 zassert_equal(rc, 0, "Unexpected read fail with error %d", rc);
260 for (int ii = 0; ii < chunk; ++ii, ++i) {
261 if ((uint8_t)buf[ii] != (uint8_t)flash_area_erased_val(fa)) {
262 erased = false;
263 break;
264 }
265 }
266 }
267 zassert_true(erased, "Erase failed at dev abosolute offset index %d",
268 (int)(i + fa->fa_off));
269
270 rc = flash_fill(flash_dev, 0xaa, fa->fa_off, fa->fa_size);
271 zassert_true(rc == 0, "flash device fill fail");
272
273 rc = flash_area_flatten(fa, 0, fa->fa_size);
274
275 erased = true;
276 i = 0;
277 while (erased && i < fa->fa_size) {
278 uint8_t buf[32];
279 int chunk = MIN(sizeof(buf), fa->fa_size - i);
280
281 rc = flash_read(flash_dev, fa->fa_off + i, buf, chunk);
282 zassert_equal(rc, 0, "Unexpected read fail with error %d", rc);
283 for (int ii = 0; ii < chunk; ++ii, ++i) {
284 if ((uint8_t)buf[ii] != (uint8_t)flash_area_erased_val(fa)) {
285 erased = false;
286 break;
287 }
288 }
289 }
290 zassert_true(erased, "Flatten/Erase failed at dev absolute offset %d",
291 (int)(i + fa->fa_off));
292 }
293
ZTEST(flash_map,test_flash_area_copy)294 ZTEST(flash_map, test_flash_area_copy)
295 {
296 const struct flash_area *fa;
297 uint8_t src_buf[FLASH_AREA_COPY_SIZE], dst_buf[FLASH_AREA_COPY_SIZE],
298 copy_buf[32];
299 int rc;
300
301 /* Get source and destination flash areas */
302 fa = FIXED_PARTITION(SLOT1_PARTITION);
303
304 /* First erase the area so it's ready for use. */
305 rc = flash_area_erase(fa, 0, fa->fa_size);
306 zassert_true(rc == 0, "flash area erase fail");
307
308 /* Fill source area with test data */
309 memset(src_buf, 0xAB, sizeof(src_buf));
310 rc = flash_area_write(fa, 0, src_buf, sizeof(src_buf));
311 zassert_true(rc == 0, "Failed to write to source flash area");
312
313 /* Perform the copy operation */
314 rc = flash_area_copy(fa, 0, fa, FLASH_AREA_COPY_SIZE, sizeof(src_buf), copy_buf,
315 sizeof(copy_buf));
316 zassert_true(rc == 0, "flash_area_copy failed");
317
318 /* Verify the copied data */
319 rc = flash_area_read(fa, FLASH_AREA_COPY_SIZE, dst_buf, sizeof(dst_buf));
320 zassert_true(rc == 0, "Failed to read from destination flash area");
321 zassert_mem_equal(src_buf, dst_buf, sizeof(src_buf), "Data mismatch after copy");
322 }
323
ZTEST(flash_map,test_parameter_overflows)324 ZTEST(flash_map, test_parameter_overflows)
325 {
326 const struct flash_area *fa;
327 uint8_t dst_buf[FLASH_AREA_COPY_SIZE];
328 int rc;
329
330 fa = FIXED_PARTITION(SLOT1_PARTITION);
331 /* -1 cast to size_t gives us max size_t value, added to offset of 1,
332 * it will overflow to 0.
333 */
334 rc = flash_area_read(fa, 1, dst_buf, (size_t)(-1));
335 zassert_equal(rc, -EINVAL, "1: Overflow should have been detected");
336 /* Here we have offset 1 below size of area, with added max size_t
337 * it upper bound of read range should overflow to:
338 * (max(size_t) + fa->fa_size - 1) mod (max(size_t)) == fa->fa_size - 2
339 */
340 rc = flash_area_read(fa, fa->fa_size - 1, dst_buf, (size_t)(-1));
341 zassert_equal(rc, -EINVAL, "2: Overflow should have been detected");
342 }
343
344 ZTEST_SUITE(flash_map, NULL, NULL, NULL, NULL, NULL);
345