1 /*
2 * Copyright (c) 2025 Koppel Electronic
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6 #include <stdlib.h>
7 #include <zephyr/ztest.h>
8 #include <zephyr/drivers/flash.h>
9 #include <zephyr/drivers/flash/flash_simulator.h>
10 #include <zephyr/device.h>
11
12 /* Test the flash simulator callbacks to modify the behaviour of the
13 * flash simulator on the fly.
14 */
15
16 /* configuration derived from DT */
17 #ifdef CONFIG_ARCH_POSIX
18 #define SOC_NV_FLASH_NODE DT_CHILD(DT_INST(0, zephyr_sim_flash), flash_0)
19 #else
20 #define SOC_NV_FLASH_NODE DT_CHILD(DT_INST(0, zephyr_sim_flash), flash_sim_0)
21 #endif /* CONFIG_ARCH_POSIX */
22 #define FLASH_SIMULATOR_BASE_OFFSET DT_REG_ADDR(SOC_NV_FLASH_NODE)
23 #define FLASH_SIMULATOR_ERASE_UNIT DT_PROP(SOC_NV_FLASH_NODE, erase_block_size)
24 #define FLASH_SIMULATOR_PROG_UNIT DT_PROP(SOC_NV_FLASH_NODE, write_block_size)
25 #define FLASH_SIMULATOR_FLASH_SIZE DT_REG_SIZE(SOC_NV_FLASH_NODE)
26
27 #define FLASH_SIMULATOR_ERASE_VALUE \
28 DT_PROP(DT_PARENT(SOC_NV_FLASH_NODE), erase_value)
29
30 #if (defined(CONFIG_ARCH_POSIX) || defined(CONFIG_BOARD_QEMU_X86))
31 static const struct device *const flash_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_flash_controller));
32 #else
33 static const struct device *const flash_dev = DEVICE_DT_GET(DT_NODELABEL(sim_flash_controller));
34 #endif
35
36
37 /* We are simulating the broken FLASH memory.
38 * Simulate different types of errors depending on the erase page.
39 * Page 0: behaves normal
40 * Page 1: erase works as expected, all writes fails (no write, -EIO returned)
41 * Page 2: erase works as expected, all writes silently corrupt data (bits 0, 1, 20 in sticks to 1)
42 * Page 3: erase fails (no erase, -EIO returned), all writes fails (-EIO returned)
43 * Page 4: erase fails silently (erases writes 0x55 to all bytes), all writes fails (-EIO returned)
44 */
45 enum test_page_type {
46 TEST_PAGE_TYPE_NORMAL,
47 TEST_PAGE_TYPE_ERASE_OK_WRITE_FAIL,
48 TEST_PAGE_TYPE_ERASE_OK_WRITE_CORRUPT,
49 TEST_PAGE_TYPE_ERASE_FAIL_WRITE_FAIL,
50 TEST_PAGE_TYPE_ERASE_CORRUPT_WRITE_FAIL,
51 };
52
53 /* Corruption pattern for write operations: bits 0, 1, and 20 in 32-bit word */
54 static const uint32_t test_write_corruption_pattern = 0x00100003;
55
test_write_byte_callback(const struct device * dev,off_t offset,uint8_t data)56 static int test_write_byte_callback(const struct device *dev, off_t offset, uint8_t data)
57 {
58 const struct flash_simulator_params *params = flash_simulator_get_params(dev);
59 enum test_page_type page = offset / params->erase_unit;
60
61 switch (page) {
62 case TEST_PAGE_TYPE_ERASE_OK_WRITE_FAIL:
63 case TEST_PAGE_TYPE_ERASE_FAIL_WRITE_FAIL:
64 case TEST_PAGE_TYPE_ERASE_CORRUPT_WRITE_FAIL:
65 return -EIO;
66 case TEST_PAGE_TYPE_ERASE_OK_WRITE_CORRUPT: {
67 /* Apply corruption pattern based on byte position within word */
68 uint8_t byte_in_word = offset & 0x3;
69 uint8_t corruption_byte =
70 (test_write_corruption_pattern >> (byte_in_word * 8)) & 0xFF;
71
72 return data | corruption_byte;
73 }
74 case TEST_PAGE_TYPE_NORMAL:
75 default:
76 return data;
77 }
78 }
79
test_erase_unit_callback(const struct device * dev,off_t offset)80 static int test_erase_unit_callback(const struct device *dev, off_t offset)
81 {
82 const struct flash_simulator_params *params = flash_simulator_get_params(dev);
83 enum test_page_type page = offset / params->erase_unit;
84 size_t flash_size;
85 uint8_t *flash_mock = flash_simulator_get_memory(dev, &flash_size);
86
87 zassert_true(offset < flash_size, "Offset 0x%lx out of range (flash size 0x%zx)",
88 (long)offset, flash_size);
89
90 switch (page) {
91 case TEST_PAGE_TYPE_ERASE_FAIL_WRITE_FAIL:
92 return -EIO;
93 case TEST_PAGE_TYPE_ERASE_CORRUPT_WRITE_FAIL:
94 /* Corrupt erase by writing 0x55 to all bytes */
95 memset(flash_mock + offset, 0x55, params->erase_unit);
96 return 0;
97 case TEST_PAGE_TYPE_NORMAL:
98 case TEST_PAGE_TYPE_ERASE_OK_WRITE_FAIL:
99 case TEST_PAGE_TYPE_ERASE_OK_WRITE_CORRUPT:
100 default:
101 /* Normal erase behavior - callback must perform the erase */
102 memset(flash_mock + offset, params->erase_value, params->erase_unit);
103 return 0;
104 }
105 }
106
107 static struct flash_simulator_cb test_flash_sim_cbs = {
108 .write_byte = test_write_byte_callback,
109 .erase_unit = test_erase_unit_callback,
110 };
111
flash_sim_cbs_setup(void)112 void *flash_sim_cbs_setup(void)
113 {
114 zassert_true(device_is_ready(flash_dev),
115 "Simulated flash device not ready");
116
117 flash_simulator_set_callbacks(flash_dev, &test_flash_sim_cbs);
118
119 return NULL;
120 }
121
122 /* Disable callbacks after tests to restore default simulator behavior */
flash_sim_cbs_teardown(void * fixture)123 static void flash_sim_cbs_teardown(void *fixture)
124 {
125 ARG_UNUSED(fixture);
126 flash_simulator_set_callbacks(flash_dev, NULL);
127 }
128
ZTEST(flash_sim_cbs,test_page_behaviors)129 ZTEST(flash_sim_cbs, test_page_behaviors)
130 {
131 int ret;
132 uint32_t write_buf[FLASH_SIMULATOR_ERASE_UNIT / sizeof(uint32_t)];
133 uint32_t read_buf[FLASH_SIMULATOR_ERASE_UNIT / sizeof(uint32_t)];
134 off_t page_offset;
135 size_t buf_size = sizeof(write_buf);
136
137 /* Initialize write buffer with pseudo-random pattern */
138 srand(0x12345678);
139 for (size_t i = 0; i < ARRAY_SIZE(write_buf); i++) {
140 write_buf[i] = ((uint32_t)rand() << 16) | (uint32_t)rand();
141 }
142
143 /* Test Page 0: TEST_PAGE_TYPE_NORMAL - behaves normal */
144 page_offset = TEST_PAGE_TYPE_NORMAL * FLASH_SIMULATOR_ERASE_UNIT;
145
146 ret = flash_erase(flash_dev, page_offset, FLASH_SIMULATOR_ERASE_UNIT);
147 zassert_equal(ret, 0, "Page 0: Erase should succeed");
148
149 ret = flash_write(flash_dev, page_offset, write_buf, buf_size);
150 zassert_equal(ret, 0, "Page 0: Write should succeed");
151
152 ret = flash_read(flash_dev, page_offset, read_buf, buf_size);
153 zassert_equal(ret, 0, "Page 0: Read should succeed");
154 zassert_mem_equal(read_buf, write_buf, buf_size,
155 "Page 0: Data should match written data");
156
157 /* Test Page 1: TEST_PAGE_TYPE_ERASE_OK_WRITE_FAIL - erase OK, write fails */
158 page_offset = TEST_PAGE_TYPE_ERASE_OK_WRITE_FAIL * FLASH_SIMULATOR_ERASE_UNIT;
159
160 ret = flash_erase(flash_dev, page_offset, FLASH_SIMULATOR_ERASE_UNIT);
161 zassert_equal(ret, 0, "Page 1: Erase should succeed");
162
163 ret = flash_read(flash_dev, page_offset, read_buf, buf_size);
164 zassert_equal(ret, 0, "Page 1: Read after erase should succeed");
165 for (size_t i = 0; i < ARRAY_SIZE(read_buf); i++) {
166 uint32_t expected = FLASH_SIMULATOR_ERASE_VALUE;
167
168 expected |= (expected << 8) | (expected << 16) | (expected << 24);
169 zassert_equal(read_buf[i], expected,
170 "Page 1: After erase, data should be erase value");
171 }
172
173 ret = flash_write(flash_dev, page_offset, write_buf, buf_size);
174 zassert_equal(ret, -EIO, "Page 1: Write should fail with -EIO");
175
176 /* Test Page 2: TEST_PAGE_TYPE_ERASE_OK_WRITE_CORRUPT - erase OK, write corrupts */
177 page_offset = TEST_PAGE_TYPE_ERASE_OK_WRITE_CORRUPT * FLASH_SIMULATOR_ERASE_UNIT;
178
179 ret = flash_erase(flash_dev, page_offset, FLASH_SIMULATOR_ERASE_UNIT);
180 zassert_equal(ret, 0, "Page 2: Erase should succeed");
181
182 ret = flash_write(flash_dev, page_offset, write_buf, buf_size);
183 zassert_equal(ret, 0, "Page 2: Write should succeed (but corrupt data)");
184
185 ret = flash_read(flash_dev, page_offset, read_buf, buf_size);
186 zassert_equal(ret, 0, "Page 2: Read should succeed");
187
188 /* Verify corruption pattern: bits 0, 1, and 20 should be set */
189 for (size_t i = 0; i < ARRAY_SIZE(read_buf); i++) {
190 uint32_t expected = write_buf[i] | test_write_corruption_pattern;
191
192 zassert_equal(read_buf[i], expected,
193 "Page 2: Data should be corrupted with pattern 0x%08x",
194 test_write_corruption_pattern);
195 }
196
197 /* Test Page 3: TEST_PAGE_TYPE_ERASE_FAIL_WRITE_FAIL - both fail */
198 page_offset = TEST_PAGE_TYPE_ERASE_FAIL_WRITE_FAIL * FLASH_SIMULATOR_ERASE_UNIT;
199
200 ret = flash_erase(flash_dev, page_offset, FLASH_SIMULATOR_ERASE_UNIT);
201 zassert_equal(ret, -EIO, "Page 3: Erase should fail with -EIO");
202
203 ret = flash_write(flash_dev, page_offset, write_buf, buf_size);
204 zassert_equal(ret, -EIO, "Page 3: Write should fail with -EIO");
205
206 /* Test Page 4: TEST_PAGE_TYPE_ERASE_CORRUPT_WRITE_FAIL - erase corrupts, write fails */
207 page_offset = TEST_PAGE_TYPE_ERASE_CORRUPT_WRITE_FAIL * FLASH_SIMULATOR_ERASE_UNIT;
208
209 ret = flash_erase(flash_dev, page_offset, FLASH_SIMULATOR_ERASE_UNIT);
210 zassert_equal(ret, 0, "Page 4: Erase should succeed (but corrupt)");
211
212 ret = flash_read(flash_dev, page_offset, read_buf, buf_size);
213 zassert_equal(ret, 0, "Page 4: Read after erase should succeed");
214 for (size_t i = 0; i < ARRAY_SIZE(read_buf); i++) {
215 zassert_equal(read_buf[i], 0x55555555,
216 "Page 4: After erase, data should be 0x55555555 (corrupted erase)");
217 }
218
219 ret = flash_write(flash_dev, page_offset, write_buf, buf_size);
220 zassert_equal(ret, -EIO, "Page 4: Write should fail with -EIO");
221 }
222
223 ZTEST_SUITE(flash_sim_cbs, NULL, flash_sim_cbs_setup, NULL, NULL, flash_sim_cbs_teardown);
224