1 /*
2  * Copyright (c) 2023 Bjarki Arge Andreasen
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/kernel.h>
8 #include <string.h>
9 #include <zephyr/drivers/flash.h>
10 #include <zephyr/devicetree.h>
11 #include <zephyr/ztest.h>
12 #include <zephyr/logging/log.h>
13 
14 LOG_MODULE_REGISTER(test_flash);
15 
16 #ifdef CONFIG_BOOTLOADER_MCUBOOT
17 
18 #define TEST_FLASH_PART_NODE \
19 	DT_NODELABEL(boot_partition)
20 
21 #else
22 
23 #define TEST_FLASH_PART_NODE \
24 	DT_NODELABEL(slot1_partition)
25 
26 #endif
27 
28 #define TEST_FLASH_PART_OFFSET \
29 	DT_REG_ADDR(TEST_FLASH_PART_NODE)
30 
31 #define TEST_FLASH_PART_SIZE \
32 	DT_REG_SIZE(TEST_FLASH_PART_NODE)
33 
34 #define TEST_FLASH_PART_END_OFFSET \
35 	(TEST_FLASH_PART_OFFSET + TEST_FLASH_PART_SIZE)
36 
37 #define TEST_FLASH_CONTROLLER_NODE \
38 	DT_MTD_FROM_FIXED_PARTITION(TEST_FLASH_PART_NODE)
39 
40 static const struct device *flash_controller = DEVICE_DT_GET(TEST_FLASH_CONTROLLER_NODE);
41 static uint8_t test_write_block[512];
42 static uint8_t test_read_block[512];
43 
test_flash_fill_test_write_block(void)44 static void test_flash_fill_test_write_block(void)
45 {
46 	for (size_t i = 0; i < sizeof(test_write_block); i++) {
47 		test_write_block[i] = (uint8_t)i;
48 	}
49 }
50 
test_flash_setup(void)51 static void *test_flash_setup(void)
52 {
53 	test_flash_fill_test_write_block();
54 	return NULL;
55 }
56 
test_flash_mem_is_set_to(const uint8_t * data,uint8_t to,size_t size)57 static bool test_flash_mem_is_set_to(const uint8_t *data, uint8_t to, size_t size)
58 {
59 	for (size_t i = 0; i < size; i++) {
60 		if (data[i] != to) {
61 			return false;
62 		}
63 	}
64 
65 	return true;
66 }
67 
test_flash_is_erased(off_t offset,size_t size)68 static bool test_flash_is_erased(off_t offset, size_t size)
69 {
70 	static uint8_t test_erase_buffer[99];
71 	const struct flash_parameters *parameters = flash_get_parameters(flash_controller);
72 	size_t remaining;
73 	size_t readsize;
74 
75 	while (offset < size) {
76 		remaining = size - (size_t)offset;
77 		readsize = MIN(sizeof(test_erase_buffer), remaining);
78 
79 		if (flash_read(flash_controller, offset, test_erase_buffer, readsize) < 0) {
80 			return false;
81 		}
82 
83 		if (!test_flash_mem_is_set_to(test_erase_buffer, parameters->erase_value,
84 					      readsize)) {
85 			return false;
86 		}
87 
88 		offset += (off_t)readsize;
89 	}
90 
91 	return true;
92 }
93 
test_flash_verify_partition_is_erased(void)94 static bool test_flash_verify_partition_is_erased(void)
95 {
96 	return test_flash_is_erased(TEST_FLASH_PART_OFFSET, TEST_FLASH_PART_SIZE);
97 }
98 
test_flash_erase_partition(void)99 static int test_flash_erase_partition(void)
100 {
101 	LOG_INF("Erasing section of size %zu at offset %zu controlled by %s",
102 		TEST_FLASH_PART_SIZE,
103 		TEST_FLASH_PART_OFFSET,
104 		flash_controller->name);
105 
106 	return flash_erase(flash_controller, TEST_FLASH_PART_OFFSET,
107 			   TEST_FLASH_PART_SIZE);
108 }
109 
test_flash_before(void * f)110 static void test_flash_before(void *f)
111 {
112 	ARG_UNUSED(f);
113 	__ASSERT(test_flash_erase_partition() == 0,
114 		 "Failed to erase partition");
115 	__ASSERT(test_flash_verify_partition_is_erased(),
116 		 "Failed to erase partition");
117 }
118 
test_flash_write_block_at_offset(off_t offset,size_t size)119 static void test_flash_write_block_at_offset(off_t offset, size_t size)
120 {
121 	zassert_ok(flash_write(flash_controller, offset, test_write_block, size),
122 		   "Failed to write block at offset %zu, of size %zu", (size_t)offset, size);
123 	zassert_ok(flash_read(flash_controller, offset, test_read_block, size),
124 		   "Failed to read block at offset %zu, of size %zu", (size_t)offset, size);
125 	zassert_ok(memcmp(test_write_block, test_read_block, size),
126 		   "Failed to write block at offset %zu, of size %zu to page", (size_t)offset,
127 		   size);
128 }
129 
test_flash_write_across_page_boundary(const struct flash_pages_info * info,size_t write_block_size)130 static void test_flash_write_across_page_boundary(const struct flash_pages_info *info,
131 						  size_t write_block_size)
132 {
133 	off_t page_boundary = info->start_offset;
134 	uint32_t page0_index = info->index - 1;
135 	uint32_t page1_index = info->index;
136 	off_t cross_write_start_offset = page_boundary - (off_t)write_block_size;
137 	size_t cross_write_size = write_block_size * 2;
138 
139 	LOG_INF("Writing across page boundary at %zu, between page index %u and %u",
140 		(size_t)page_boundary,
141 		page0_index,
142 		page1_index);
143 
144 	test_flash_write_block_at_offset(cross_write_start_offset, cross_write_size);
145 }
146 
test_flash_write_across_page_boundaries(const struct flash_pages_info * info,void * data)147 static bool test_flash_write_across_page_boundaries(const struct flash_pages_info *info,
148 						    void *data)
149 {
150 	size_t write_block_size = *((size_t *)data);
151 
152 	if (info->start_offset <= TEST_FLASH_PART_OFFSET) {
153 		/* Not yet reached second page within partition */
154 		return true;
155 	}
156 
157 	if (info->start_offset >= TEST_FLASH_PART_END_OFFSET) {
158 		/* Reached first page after partition end */
159 		return false;
160 	}
161 
162 	test_flash_write_across_page_boundary(info, write_block_size);
163 	return true;
164 }
165 
ZTEST(flash_page_layout,test_write_across_page_boundaries_in_part)166 ZTEST(flash_page_layout, test_write_across_page_boundaries_in_part)
167 {
168 	const struct flash_parameters *parameters = flash_get_parameters(flash_controller);
169 	size_t write_block_size = parameters->write_block_size;
170 
171 	flash_page_foreach(flash_controller, test_flash_write_across_page_boundaries,
172 			   &write_block_size);
173 }
174 
test_flash_erase_page(const struct flash_pages_info * info)175 static void test_flash_erase_page(const struct flash_pages_info *info)
176 {
177 	off_t page_offset = info->start_offset;
178 	size_t page_size = info->size;
179 	size_t page_index = info->index;
180 
181 	LOG_INF("Erasing page at %zu of size %zu with index %zu",
182 		(size_t)page_offset, page_size, page_index);
183 
184 	zassert_ok(flash_erase(flash_controller, page_offset, page_size),
185 		   "Failed to erase page");
186 
187 	zassert_true(test_flash_is_erased(page_offset, page_size),
188 		     "Failed to erase page");
189 }
190 
test_flash_erase_pages(const struct flash_pages_info * info,void * data)191 static bool test_flash_erase_pages(const struct flash_pages_info *info, void *data)
192 {
193 	if (info->start_offset < TEST_FLASH_PART_OFFSET) {
194 		/* Not yet reached first page within partition */
195 		return true;
196 	}
197 
198 	if (info->start_offset >= TEST_FLASH_PART_END_OFFSET) {
199 		/* Reached first page after partition end */
200 		return false;
201 	}
202 
203 	test_flash_erase_page(info);
204 	return true;
205 }
206 
ZTEST(flash_page_layout,test_erase_single_pages_in_part)207 ZTEST(flash_page_layout, test_erase_single_pages_in_part)
208 {
209 	const struct flash_parameters *parameters = flash_get_parameters(flash_controller);
210 	size_t write_block_size = parameters->write_block_size;
211 
212 	flash_page_foreach(flash_controller, test_flash_write_across_page_boundaries,
213 			   &write_block_size);
214 
215 	flash_page_foreach(flash_controller, test_flash_erase_pages, NULL);
216 }
217 
218 ZTEST_SUITE(flash_page_layout, NULL, test_flash_setup, test_flash_before, NULL, NULL);
219