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_b), &fa);
43 	zassert_equal(rc, -ENOENT, "Open did not fail");
44 
45 	/* Note lack of tests for FIXED_PARTITION(...) instantiation,
46 	 * because this macro will fail, at compile time, if node does not
47 	 * exist or is disabled.
48 	 */
49 #else
50 	ztest_test_skip();
51 #endif
52 }
53 
ZTEST(flash_map,test_flash_area_device_is_ready)54 ZTEST(flash_map, test_flash_area_device_is_ready)
55 {
56 	const struct flash_area no_dev = {
57 		.fa_dev = NULL,
58 	};
59 
60 	zassert_false(flash_area_device_is_ready(NULL));
61 	zassert_false(flash_area_device_is_ready(&no_dev));
62 	/* The below just assumes that tests are executed so late that
63 	 * all devices are already initialized and ready.
64 	 */
65 	zassert_true(flash_area_device_is_ready(
66 			FIXED_PARTITION(SLOT1_PARTITION)));
67 }
68 
layout_match(const struct device * flash_dev,uint32_t sec_cnt)69 static void layout_match(const struct device *flash_dev, uint32_t sec_cnt)
70 {
71 	off_t off = 0;
72 	int i;
73 
74 	/* For each reported sector, check if it corresponds to real page on device */
75 	for (i = 0; i < sec_cnt; ++i) {
76 		struct flash_pages_info fpi;
77 
78 		zassert_ok(
79 			flash_get_page_info_by_offs(flash_dev, SLOT1_PARTITION_OFFSET + off, &fpi));
80 		/* Offset of page taken directly from device corresponds to offset
81 		 * within flash area
82 		 */
83 		zassert_equal(fpi.start_offset, fs_sectors[i].fs_off + SLOT1_PARTITION_OFFSET);
84 		zassert_equal(fpi.size, fs_sectors[i].fs_size);
85 		off += fs_sectors[i].fs_size;
86 	}
87 }
88 
89 /**
90  * @brief Test flash_area_get_sectors()
91  */
ZTEST(flash_map,test_flash_area_get_sectors)92 ZTEST(flash_map, test_flash_area_get_sectors)
93 {
94 	const struct flash_area *fa;
95 	const struct device *flash_dev_a = SLOT1_PARTITION_DEV;
96 	uint32_t sec_cnt;
97 	int rc;
98 
99 	fa = FIXED_PARTITION(SLOT1_PARTITION);
100 
101 	zassert_true(flash_area_device_is_ready(fa));
102 
103 	zassert_true(device_is_ready(flash_dev_a));
104 
105 	/* Device obtained by label should match the one from fa object */
106 	zassert_equal(fa->fa_dev, flash_dev_a, "Device for slot1_partition do not match");
107 
108 	memset(&fs_sectors[0], 0, sizeof(fs_sectors));
109 
110 	sec_cnt = ARRAY_SIZE(fs_sectors);
111 	rc = flash_area_get_sectors(SLOT1_PARTITION_ID, &sec_cnt, fs_sectors);
112 	zassert_true(rc == 0, "flash_area_get_sectors failed");
113 
114 	layout_match(flash_dev_a, sec_cnt);
115 }
116 
ZTEST(flash_map,test_flash_area_sectors)117 ZTEST(flash_map, test_flash_area_sectors)
118 {
119 	const struct flash_area *fa;
120 	uint32_t sec_cnt;
121 	int rc;
122 	const struct device *flash_dev_a = SLOT1_PARTITION_DEV;
123 
124 	fa = FIXED_PARTITION(SLOT1_PARTITION);
125 
126 	zassert_true(flash_area_device_is_ready(fa));
127 
128 	zassert_true(device_is_ready(flash_dev_a));
129 
130 	/* Device obtained by label should match the one from fa object */
131 	zassert_equal(fa->fa_dev, flash_dev_a, "Device for slot1_partition do not match");
132 
133 	sec_cnt = ARRAY_SIZE(fs_sectors);
134 	rc = flash_area_sectors(fa, &sec_cnt, fs_sectors);
135 	zassert_true(rc == 0, "flash_area_get_sectors failed");
136 
137 	layout_match(flash_dev_a, sec_cnt);
138 }
139 
ZTEST(flash_map,test_flash_area_erased_val)140 ZTEST(flash_map, test_flash_area_erased_val)
141 {
142 	const struct flash_parameters *param;
143 	const struct flash_area *fa;
144 	uint8_t val;
145 
146 	fa = FIXED_PARTITION(SLOT1_PARTITION);
147 
148 	val = flash_area_erased_val(fa);
149 
150 	param = flash_get_parameters(fa->fa_dev);
151 
152 	zassert_equal(param->erase_value, val,
153 		      "value different than the flash erase value");
154 }
155 
ZTEST(flash_map,test_fixed_partition_node_macros)156 ZTEST(flash_map, test_fixed_partition_node_macros)
157 {
158 	/* DTS node macros, for accessing fixed partitions, are only available
159 	 * for DTS based partitions; custom flash map may define partition outside
160 	 * of DTS definition, making the NODE macros fail to evaluate.
161 	 */
162 #if defined(CONFIG_TEST_FLASH_MAP_NODE_MACROS)
163 	/* Test against changes in API */
164 	zassert_equal(FIXED_PARTITION_NODE_OFFSET(SLOT1_PARTITION_NODE),
165 		DT_REG_ADDR(SLOT1_PARTITION_NODE));
166 	zassert_equal(FIXED_PARTITION_NODE_SIZE(SLOT1_PARTITION_NODE),
167 		DT_REG_SIZE(SLOT1_PARTITION_NODE));
168 	zassert_equal(FIXED_PARTITION_NODE_DEVICE(SLOT1_PARTITION_NODE),
169 		DEVICE_DT_GET(DT_MTD_FROM_FIXED_PARTITION(SLOT1_PARTITION_NODE)));
170 
171 	/* Taking by node and taking by label should give same device */
172 	zassert_equal(FIXED_PARTITION_BY_NODE(DT_NODELABEL(SLOT1_PARTITION)),
173 		      FIXED_PARTITION(SLOT1_PARTITION));
174 #else
175 	ztest_test_skip();
176 #endif
177 }
178 
ZTEST(flash_map,test_flash_area_erase_and_flatten)179 ZTEST(flash_map, test_flash_area_erase_and_flatten)
180 {
181 	int i;
182 	bool erased = true;
183 	int rc;
184 	const struct flash_area *fa;
185 	const struct device *flash_dev;
186 
187 	fa = FIXED_PARTITION(SLOT1_PARTITION);
188 
189 	/* First erase the area so it's ready for use. */
190 	flash_dev = flash_area_get_device(fa);
191 
192 	rc = flash_erase(flash_dev, fa->fa_off, fa->fa_size);
193 	zassert_true(rc == 0, "flash area erase fail");
194 
195 	rc = flash_fill(flash_dev, 0xaa, fa->fa_off, fa->fa_size);
196 	zassert_true(rc == 0, "flash device fill fail");
197 
198 	rc = flash_area_erase(fa, 0, fa->fa_size);
199 	zassert_true(rc == 0, "flash area erase fail");
200 
201 	TC_PRINT("Flash area info:\n");
202 	TC_PRINT("\tpointer:\t %p\n", &fa);
203 	TC_PRINT("\toffset:\t %ld\n", (long)fa->fa_off);
204 	TC_PRINT("\tsize:\t %ld\n", (long)fa->fa_size);
205 
206 	/* we work under assumption that flash_fill is working and tested */
207 	i = 0;
208 	while (erased && i < fa->fa_size) {
209 		uint8_t buf[32];
210 		int chunk = MIN(sizeof(buf), fa->fa_size - i);
211 
212 		rc = flash_read(flash_dev, fa->fa_off + i, buf, chunk);
213 		zassert_equal(rc, 0, "Unexpected read fail with error %d", rc);
214 		for (int ii = 0; ii < chunk; ++ii, ++i) {
215 			if ((uint8_t)buf[ii] != (uint8_t)flash_area_erased_val(fa)) {
216 				erased = false;
217 				break;
218 			}
219 		}
220 	}
221 	zassert_true(erased, "Erase failed at dev abosolute offset index %d",
222 		     (int)(i + fa->fa_off));
223 
224 	rc = flash_fill(flash_dev, 0xaa, fa->fa_off, fa->fa_size);
225 	zassert_true(rc == 0, "flash device fill fail");
226 
227 	rc = flash_area_flatten(fa, 0, fa->fa_size);
228 
229 	erased = true;
230 	i = 0;
231 	while (erased && i < fa->fa_size) {
232 		uint8_t buf[32];
233 		int chunk = MIN(sizeof(buf), fa->fa_size - i);
234 
235 		rc = flash_read(flash_dev, fa->fa_off + i, buf, chunk);
236 		zassert_equal(rc, 0, "Unexpected read fail with error %d", rc);
237 		for (int ii = 0; ii < chunk; ++ii, ++i) {
238 			if ((uint8_t)buf[ii] != (uint8_t)flash_area_erased_val(fa)) {
239 				erased = false;
240 				break;
241 			}
242 		}
243 	}
244 	zassert_true(erased, "Flatten/Erase failed at dev absolute offset %d",
245 		     (int)(i + fa->fa_off));
246 }
247 
ZTEST(flash_map,test_flash_area_copy)248 ZTEST(flash_map, test_flash_area_copy)
249 {
250 	const struct flash_area *fa;
251 	uint8_t src_buf[FLASH_AREA_COPY_SIZE], dst_buf[FLASH_AREA_COPY_SIZE],
252 		copy_buf[32];
253 	int rc;
254 
255 	/* Get source and destination flash areas */
256 	fa = FIXED_PARTITION(SLOT1_PARTITION);
257 
258 	/* First erase the area so it's ready for use. */
259 	rc = flash_area_erase(fa, 0, fa->fa_size);
260 	zassert_true(rc == 0, "flash area erase fail");
261 
262 	/* Fill source area with test data */
263 	memset(src_buf, 0xAB, sizeof(src_buf));
264 	rc = flash_area_write(fa, 0, src_buf, sizeof(src_buf));
265 	zassert_true(rc == 0, "Failed to write to source flash area");
266 
267 	/* Perform the copy operation */
268 	rc = flash_area_copy(fa, 0, fa, FLASH_AREA_COPY_SIZE, sizeof(src_buf), copy_buf,
269 			     sizeof(copy_buf));
270 	zassert_true(rc == 0, "flash_area_copy failed");
271 
272 	/* Verify the copied data */
273 	rc = flash_area_read(fa, FLASH_AREA_COPY_SIZE, dst_buf, sizeof(dst_buf));
274 	zassert_true(rc == 0, "Failed to read from destination flash area");
275 	zassert_mem_equal(src_buf, dst_buf, sizeof(src_buf), "Data mismatch after copy");
276 }
277 
ZTEST(flash_map,test_parameter_overflows)278 ZTEST(flash_map, test_parameter_overflows)
279 {
280 	const struct flash_area *fa;
281 	uint8_t dst_buf[FLASH_AREA_COPY_SIZE];
282 	int rc;
283 
284 	fa = FIXED_PARTITION(SLOT1_PARTITION);
285 	/* -1 cast to size_t gives us max size_t value, added to offset of 1,
286 	 * it will overflow to 0.
287 	 */
288 	rc = flash_area_read(fa, 1, dst_buf, (size_t)(-1));
289 	zassert_equal(rc, -EINVAL, "1: Overflow should have been detected");
290 	/* Here we have offset 1 below size of area, with added max size_t
291 	 * it upper bound of read range should overflow to:
292 	 * (max(size_t) + fa->fa_size - 1) mod (max(size_t)) == fa->fa_size - 2
293 	 */
294 	rc = flash_area_read(fa, fa->fa_size - 1, dst_buf, (size_t)(-1));
295 	zassert_equal(rc, -EINVAL, "2: Overflow should have been detected");
296 }
297 
298 ZTEST_SUITE(flash_map, NULL, NULL, NULL, NULL, NULL);
299