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