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