1 /*
2 * Copyright (c) 2024 BayLibre SAS
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 /*
8 * This test is designed to be run using flash-simulator which provide
9 * functionality for flash property customization and emulating errors in
10 * flash operation in parallel to regular flash API.
11 * Test should be run on qemu_x86 or native_sim target.
12 */
13
14 #if !defined(CONFIG_BOARD_QEMU_X86) && !defined(CONFIG_ARCH_POSIX)
15 #error "Run only on qemu_x86 or a posix architecture based target (for ex. native_sim)"
16 #endif
17
18 #include <stdio.h>
19 #include <string.h>
20 #include <zephyr/ztest.h>
21
22 #include <zephyr/drivers/flash.h>
23 #include <zephyr/fs/zms.h>
24 #include <zephyr/stats/stats.h>
25 #include <zephyr/storage/flash_map.h>
26 #include <zephyr/sys/crc.h>
27 #include "zms_priv.h"
28
29 #define TEST_ZMS_AREA storage_partition
30 #define TEST_ZMS_AREA_OFFSET FIXED_PARTITION_OFFSET(TEST_ZMS_AREA)
31 #define TEST_ZMS_AREA_ID FIXED_PARTITION_ID(TEST_ZMS_AREA)
32 #define TEST_ZMS_AREA_DEV DEVICE_DT_GET(DT_MTD_FROM_FIXED_PARTITION(DT_NODELABEL(TEST_ZMS_AREA)))
33 #define TEST_DATA_ID 1
34 #define TEST_SECTOR_COUNT 5U
35
36 static const struct device *const flash_dev = TEST_ZMS_AREA_DEV;
37
38 struct zms_fixture {
39 struct zms_fs fs;
40 struct stats_hdr *sim_stats;
41 struct stats_hdr *sim_thresholds;
42 };
43
setup(void)44 static void *setup(void)
45 {
46 int err;
47 const struct flash_area *fa;
48 struct flash_pages_info info;
49 static struct zms_fixture fixture;
50
51 __ASSERT_NO_MSG(device_is_ready(flash_dev));
52
53 err = flash_area_open(TEST_ZMS_AREA_ID, &fa);
54 zassert_true(err == 0, "flash_area_open() fail: %d", err);
55
56 fixture.fs.offset = TEST_ZMS_AREA_OFFSET;
57 err = flash_get_page_info_by_offs(flash_area_get_device(fa), fixture.fs.offset, &info);
58 zassert_true(err == 0, "Unable to get page info: %d", err);
59
60 fixture.fs.sector_size = info.size;
61 fixture.fs.sector_count = TEST_SECTOR_COUNT;
62 fixture.fs.flash_device = flash_area_get_device(fa);
63
64 return &fixture;
65 }
66
before(void * data)67 static void before(void *data)
68 {
69 struct zms_fixture *fixture = (struct zms_fixture *)data;
70
71 fixture->sim_stats = stats_group_find("flash_sim_stats");
72 fixture->sim_thresholds = stats_group_find("flash_sim_thresholds");
73 }
74
after(void * data)75 static void after(void *data)
76 {
77 struct zms_fixture *fixture = (struct zms_fixture *)data;
78
79 if (fixture->sim_stats) {
80 stats_reset(fixture->sim_stats);
81 }
82 if (fixture->sim_thresholds) {
83 stats_reset(fixture->sim_thresholds);
84 }
85
86 /* Clear ZMS */
87 if (fixture->fs.ready) {
88 int err;
89
90 err = zms_clear(&fixture->fs);
91 zassert_true(err == 0, "zms_clear call failure: %d", err);
92 }
93
94 fixture->fs.sector_count = TEST_SECTOR_COUNT;
95 }
96
97 ZTEST_SUITE(zms, NULL, setup, before, after, NULL);
98
ZTEST_F(zms,test_zms_mount)99 ZTEST_F(zms, test_zms_mount)
100 {
101 int err;
102
103 err = zms_mount(&fixture->fs);
104 zassert_true(err == 0, "zms_mount call failure: %d", err);
105 }
106
execute_long_pattern_write(uint32_t id,struct zms_fs * fs)107 static void execute_long_pattern_write(uint32_t id, struct zms_fs *fs)
108 {
109 char rd_buf[512];
110 char wr_buf[512];
111 char pattern[] = {0xDE, 0xAD, 0xBE, 0xEF};
112 size_t len;
113
114 len = zms_read(fs, id, rd_buf, sizeof(rd_buf));
115 zassert_true(len == -ENOENT, "zms_read unexpected failure: %d", len);
116
117 BUILD_ASSERT((sizeof(wr_buf) % sizeof(pattern)) == 0);
118 for (int i = 0; i < sizeof(wr_buf); i += sizeof(pattern)) {
119 memcpy(wr_buf + i, pattern, sizeof(pattern));
120 }
121
122 len = zms_write(fs, id, wr_buf, sizeof(wr_buf));
123 zassert_true(len == sizeof(wr_buf), "zms_write failed: %d", len);
124
125 len = zms_read(fs, id, rd_buf, sizeof(rd_buf));
126 zassert_true(len == sizeof(rd_buf), "zms_read unexpected failure: %d", len);
127 zassert_mem_equal(wr_buf, rd_buf, sizeof(rd_buf), "RD buff should be equal to the WR buff");
128 }
129
ZTEST_F(zms,test_zms_write)130 ZTEST_F(zms, test_zms_write)
131 {
132 int err;
133
134 err = zms_mount(&fixture->fs);
135 zassert_true(err == 0, "zms_mount call failure: %d", err);
136
137 execute_long_pattern_write(TEST_DATA_ID, &fixture->fs);
138 }
139
flash_sim_write_calls_find(struct stats_hdr * hdr,void * arg,const char * name,uint16_t off)140 static int flash_sim_write_calls_find(struct stats_hdr *hdr, void *arg, const char *name,
141 uint16_t off)
142 {
143 if (!strcmp(name, "flash_write_calls")) {
144 uint32_t **flash_write_stat = (uint32_t **)arg;
145 *flash_write_stat = (uint32_t *)((uint8_t *)hdr + off);
146 }
147
148 return 0;
149 }
150
flash_sim_max_write_calls_find(struct stats_hdr * hdr,void * arg,const char * name,uint16_t off)151 static int flash_sim_max_write_calls_find(struct stats_hdr *hdr, void *arg, const char *name,
152 uint16_t off)
153 {
154 if (!strcmp(name, "max_write_calls")) {
155 uint32_t **max_write_calls = (uint32_t **)arg;
156 *max_write_calls = (uint32_t *)((uint8_t *)hdr + off);
157 }
158
159 return 0;
160 }
161
ZTEST_F(zms,test_zms_corrupted_write)162 ZTEST_F(zms, test_zms_corrupted_write)
163 {
164 int err;
165 size_t len;
166 char rd_buf[512];
167 char wr_buf_1[512];
168 char wr_buf_2[512];
169 char pattern_1[] = {0xDE, 0xAD, 0xBE, 0xEF};
170 char pattern_2[] = {0x03, 0xAA, 0x85, 0x6F};
171 uint32_t *flash_write_stat;
172 uint32_t *flash_max_write_calls;
173
174 err = zms_mount(&fixture->fs);
175 zassert_true(err == 0, "zms_mount call failure: %d", err);
176
177 err = zms_read(&fixture->fs, TEST_DATA_ID, rd_buf, sizeof(rd_buf));
178 zassert_true(err == -ENOENT, "zms_read unexpected failure: %d", err);
179
180 BUILD_ASSERT((sizeof(wr_buf_1) % sizeof(pattern_1)) == 0);
181 for (int i = 0; i < sizeof(wr_buf_1); i += sizeof(pattern_1)) {
182 memcpy(wr_buf_1 + i, pattern_1, sizeof(pattern_1));
183 }
184
185 len = zms_write(&fixture->fs, TEST_DATA_ID, wr_buf_1, sizeof(wr_buf_1));
186 zassert_true(len == sizeof(wr_buf_1), "zms_write failed: %d", len);
187
188 len = zms_read(&fixture->fs, TEST_DATA_ID, rd_buf, sizeof(rd_buf));
189 zassert_true(len == sizeof(rd_buf), "zms_read unexpected failure: %d", len);
190 zassert_mem_equal(wr_buf_1, rd_buf, sizeof(rd_buf),
191 "RD buff should be equal to the first WR buff");
192
193 BUILD_ASSERT((sizeof(wr_buf_2) % sizeof(pattern_2)) == 0);
194 for (int i = 0; i < sizeof(wr_buf_2); i += sizeof(pattern_2)) {
195 memcpy(wr_buf_2 + i, pattern_2, sizeof(pattern_2));
196 }
197
198 /* Set the maximum number of writes that the flash simulator can
199 * execute.
200 */
201 stats_walk(fixture->sim_thresholds, flash_sim_max_write_calls_find, &flash_max_write_calls);
202 stats_walk(fixture->sim_stats, flash_sim_write_calls_find, &flash_write_stat);
203
204 *flash_max_write_calls = *flash_write_stat - 1;
205 *flash_write_stat = 0;
206
207 /* Flash simulator will lose part of the data at the end of this write.
208 * This should simulate power down during flash write. The written data
209 * are corrupted at this point and should be discarded by the ZMS.
210 */
211 len = zms_write(&fixture->fs, TEST_DATA_ID, wr_buf_2, sizeof(wr_buf_2));
212 zassert_true(len == sizeof(wr_buf_2), "zms_write failed: %d", len);
213
214 /* Reinitialize the ZMS. */
215 memset(&fixture->fs, 0, sizeof(fixture->fs));
216 (void)setup();
217 err = zms_mount(&fixture->fs);
218 zassert_true(err == 0, "zms_mount call failure: %d", err);
219
220 len = zms_read(&fixture->fs, TEST_DATA_ID, rd_buf, sizeof(rd_buf));
221 zassert_true(len == sizeof(rd_buf), "zms_read unexpected failure: %d", len);
222 zassert_true(memcmp(wr_buf_2, rd_buf, sizeof(rd_buf)) != 0,
223 "RD buff should not be equal to the second WR buff because of "
224 "corrupted write operation");
225 zassert_mem_equal(wr_buf_1, rd_buf, sizeof(rd_buf),
226 "RD buff should be equal to the first WR buff because subsequent "
227 "write operation has failed");
228 }
229
ZTEST_F(zms,test_zms_gc)230 ZTEST_F(zms, test_zms_gc)
231 {
232 int err;
233 int len;
234 uint8_t buf[32];
235 uint8_t rd_buf[32];
236 const uint8_t max_id = 10;
237 /* 21st write will trigger GC. */
238 const uint16_t max_writes = 21;
239
240 fixture->fs.sector_count = 2;
241
242 err = zms_mount(&fixture->fs);
243 zassert_true(err == 0, "zms_mount call failure: %d", err);
244
245 for (int i = 0; i < max_writes; i++) {
246 uint8_t id = (i % max_id);
247 uint8_t id_data = id + max_id * (i / max_id);
248
249 memset(buf, id_data, sizeof(buf));
250
251 len = zms_write(&fixture->fs, id, buf, sizeof(buf));
252 zassert_true(len == sizeof(buf), "zms_write failed: %d", len);
253 }
254
255 for (int id = 0; id < max_id; id++) {
256 len = zms_read(&fixture->fs, id, rd_buf, sizeof(buf));
257 zassert_true(len == sizeof(rd_buf), "zms_read unexpected failure: %d", len);
258
259 for (int i = 0; i < sizeof(rd_buf); i++) {
260 rd_buf[i] = rd_buf[i] % max_id;
261 buf[i] = id;
262 }
263 zassert_mem_equal(buf, rd_buf, sizeof(rd_buf),
264 "RD buff should be equal to the WR buff");
265 }
266
267 err = zms_mount(&fixture->fs);
268 zassert_true(err == 0, "zms_mount call failure: %d", err);
269
270 for (int id = 0; id < max_id; id++) {
271 len = zms_read(&fixture->fs, id, rd_buf, sizeof(buf));
272 zassert_true(len == sizeof(rd_buf), "zms_read unexpected failure: %d", len);
273
274 for (int i = 0; i < sizeof(rd_buf); i++) {
275 rd_buf[i] = rd_buf[i] % max_id;
276 buf[i] = id;
277 }
278 zassert_mem_equal(buf, rd_buf, sizeof(rd_buf),
279 "RD buff should be equal to the WR buff");
280 }
281 }
282
write_content(uint32_t max_id,uint32_t begin,uint32_t end,struct zms_fs * fs)283 static void write_content(uint32_t max_id, uint32_t begin, uint32_t end, struct zms_fs *fs)
284 {
285 uint8_t buf[32];
286 ssize_t len;
287
288 for (int i = begin; i < end; i++) {
289 uint8_t id = (i % max_id);
290 uint8_t id_data = id + max_id * (i / max_id);
291
292 memset(buf, id_data, sizeof(buf));
293
294 len = zms_write(fs, id, buf, sizeof(buf));
295 zassert_true(len == sizeof(buf), "zms_write failed: %d", len);
296 }
297 }
298
check_content(uint32_t max_id,struct zms_fs * fs)299 static void check_content(uint32_t max_id, struct zms_fs *fs)
300 {
301 uint8_t rd_buf[32];
302 uint8_t buf[32];
303 ssize_t len;
304
305 for (int id = 0; id < max_id; id++) {
306 len = zms_read(fs, id, rd_buf, sizeof(buf));
307 zassert_true(len == sizeof(rd_buf), "zms_read unexpected failure: %d", len);
308
309 for (int i = 0; i < ARRAY_SIZE(rd_buf); i++) {
310 rd_buf[i] = rd_buf[i] % max_id;
311 buf[i] = id;
312 }
313 zassert_mem_equal(buf, rd_buf, sizeof(rd_buf),
314 "RD buff should be equal to the WR buff");
315 }
316 }
317
318 /**
319 * Full round of GC over 3 sectors
320 */
ZTEST_F(zms,test_zms_gc_3sectors)321 ZTEST_F(zms, test_zms_gc_3sectors)
322 {
323 int err;
324 const uint16_t max_id = 10;
325 /* 41st write will trigger 1st GC. */
326 const uint16_t max_writes = 41;
327 /* 61st write will trigger 2nd GC. */
328 const uint16_t max_writes_2 = 41 + 20;
329 /* 81st write will trigger 3rd GC. */
330 const uint16_t max_writes_3 = 41 + 20 + 20;
331 /* 101st write will trigger 4th GC. */
332 const uint16_t max_writes_4 = 41 + 20 + 20 + 20;
333
334 fixture->fs.sector_count = 3;
335
336 err = zms_mount(&fixture->fs);
337 zassert_true(err == 0, "zms_mount call failure: %d", err);
338 zassert_equal(fixture->fs.ate_wra >> ADDR_SECT_SHIFT, 0, "unexpected write sector");
339
340 /* Trigger 1st GC */
341 write_content(max_id, 0, max_writes, &fixture->fs);
342
343 /* sector sequence: empty,closed, write */
344 zassert_equal(fixture->fs.ate_wra >> ADDR_SECT_SHIFT, 2, "unexpected write sector");
345 check_content(max_id, &fixture->fs);
346
347 err = zms_mount(&fixture->fs);
348 zassert_true(err == 0, "zms_mount call failure: %d", err);
349
350 zassert_equal(fixture->fs.ate_wra >> ADDR_SECT_SHIFT, 2, "unexpected write sector");
351 check_content(max_id, &fixture->fs);
352
353 /* Trigger 2nd GC */
354 write_content(max_id, max_writes, max_writes_2, &fixture->fs);
355
356 /* sector sequence: write, empty, closed */
357 zassert_equal(fixture->fs.ate_wra >> ADDR_SECT_SHIFT, 0, "unexpected write sector");
358 check_content(max_id, &fixture->fs);
359
360 err = zms_mount(&fixture->fs);
361 zassert_true(err == 0, "zms_mount call failure: %d", err);
362
363 zassert_equal(fixture->fs.ate_wra >> ADDR_SECT_SHIFT, 0, "unexpected write sector");
364 check_content(max_id, &fixture->fs);
365
366 /* Trigger 3rd GC */
367 write_content(max_id, max_writes_2, max_writes_3, &fixture->fs);
368
369 /* sector sequence: closed, write, empty */
370 zassert_equal(fixture->fs.ate_wra >> ADDR_SECT_SHIFT, 1, "unexpected write sector");
371 check_content(max_id, &fixture->fs);
372
373 err = zms_mount(&fixture->fs);
374 zassert_true(err == 0, "zms_mount call failure: %d", err);
375
376 zassert_equal(fixture->fs.ate_wra >> ADDR_SECT_SHIFT, 1, "unexpected write sector");
377 check_content(max_id, &fixture->fs);
378
379 /* Trigger 4th GC */
380 write_content(max_id, max_writes_3, max_writes_4, &fixture->fs);
381
382 /* sector sequence: empty,closed, write */
383 zassert_equal(fixture->fs.ate_wra >> ADDR_SECT_SHIFT, 2, "unexpected write sector");
384 check_content(max_id, &fixture->fs);
385
386 err = zms_mount(&fixture->fs);
387 zassert_true(err == 0, "zms_mount call failure: %d", err);
388
389 zassert_equal(fixture->fs.ate_wra >> ADDR_SECT_SHIFT, 2, "unexpected write sector");
390 check_content(max_id, &fixture->fs);
391 }
392
flash_sim_max_len_find(struct stats_hdr * hdr,void * arg,const char * name,uint16_t off)393 static int flash_sim_max_len_find(struct stats_hdr *hdr, void *arg, const char *name, uint16_t off)
394 {
395 if (!strcmp(name, "max_len")) {
396 uint32_t **max_len = (uint32_t **)arg;
397 *max_len = (uint32_t *)((uint8_t *)hdr + off);
398 }
399
400 return 0;
401 }
402
ZTEST_F(zms,test_zms_corrupted_sector_close_operation)403 ZTEST_F(zms, test_zms_corrupted_sector_close_operation)
404 {
405 int err;
406 int len;
407 uint8_t buf[32];
408 uint32_t *flash_write_stat;
409 uint32_t *flash_max_write_calls;
410 uint32_t *flash_max_len;
411 const uint16_t max_id = 10;
412 /* 21st write will trigger GC. */
413 const uint16_t max_writes = 21;
414
415 /* Get the address of simulator parameters. */
416 stats_walk(fixture->sim_thresholds, flash_sim_max_write_calls_find, &flash_max_write_calls);
417 stats_walk(fixture->sim_thresholds, flash_sim_max_len_find, &flash_max_len);
418 stats_walk(fixture->sim_stats, flash_sim_write_calls_find, &flash_write_stat);
419
420 err = zms_mount(&fixture->fs);
421 zassert_true(err == 0, "zms_mount call failure: %d", err);
422
423 for (int i = 0; i < max_writes; i++) {
424 uint8_t id = (i % max_id);
425 uint8_t id_data = id + max_id * (i / max_id);
426
427 memset(buf, id_data, sizeof(buf));
428
429 if (i == max_writes - 1) {
430 /* Reset stats. */
431 *flash_write_stat = 0;
432
433 /* Block write calls and simulate power down during
434 * sector closing operation, so only a part of a ZMS
435 * closing ate will be written.
436 */
437 *flash_max_write_calls = 1;
438 *flash_max_len = 4;
439 }
440 len = zms_write(&fixture->fs, id, buf, sizeof(buf));
441 zassert_true(len == sizeof(buf), "zms_write failed: %d", len);
442 }
443
444 /* Make the flash simulator functional again. */
445 *flash_max_write_calls = 0;
446 *flash_max_len = 0;
447
448 err = zms_mount(&fixture->fs);
449 zassert_true(err == 0, "zms_mount call failure: %d", err);
450
451 check_content(max_id, &fixture->fs);
452
453 /* Ensure that the ZMS is able to store new content. */
454 execute_long_pattern_write(max_id, &fixture->fs);
455 }
456
457 /**
458 * @brief Test case when storage become full, so only deletion is possible.
459 */
ZTEST_F(zms,test_zms_full_sector)460 ZTEST_F(zms, test_zms_full_sector)
461 {
462 int err;
463 ssize_t len;
464 uint32_t filling_id = 0;
465 uint32_t data_read;
466
467 fixture->fs.sector_count = 3;
468
469 err = zms_mount(&fixture->fs);
470 zassert_true(err == 0, "zms_mount call failure: %d", err);
471
472 while (1) {
473 len = zms_write(&fixture->fs, filling_id, &filling_id, sizeof(filling_id));
474 if (len == -ENOSPC) {
475 break;
476 }
477 zassert_true(len == sizeof(filling_id), "zms_write failed: %d", len);
478 filling_id++;
479 }
480
481 /* check whether can delete whatever from full storage */
482 err = zms_delete(&fixture->fs, 1);
483 zassert_true(err == 0, "zms_delete call failure: %d", err);
484
485 /* the last sector is full now, test re-initialization */
486 err = zms_mount(&fixture->fs);
487 zassert_true(err == 0, "zms_mount call failure: %d", err);
488
489 len = zms_write(&fixture->fs, filling_id, &filling_id, sizeof(filling_id));
490 zassert_true(len == sizeof(filling_id), "zms_write failed: %d", len);
491
492 /* sanitycheck on ZMS content */
493 for (int i = 0; i <= filling_id; i++) {
494 len = zms_read(&fixture->fs, i, &data_read, sizeof(data_read));
495 if (i == 1) {
496 zassert_true(len == -ENOENT, "zms_read shouldn't found the entry: %d", len);
497 } else {
498 zassert_true(len == sizeof(data_read),
499 "zms_read #%d failed: len is %zd instead of %zu", i, len,
500 sizeof(data_read));
501 zassert_equal(data_read, i, "read unexpected data: %d instead of %d",
502 data_read, i);
503 }
504 }
505 }
506
ZTEST_F(zms,test_delete)507 ZTEST_F(zms, test_delete)
508 {
509 int err;
510 ssize_t len;
511 uint32_t filling_id;
512 uint32_t data_read;
513 uint32_t ate_wra;
514 uint32_t data_wra;
515
516 fixture->fs.sector_count = 3;
517
518 err = zms_mount(&fixture->fs);
519 zassert_true(err == 0, "zms_mount call failure: %d", err);
520
521 for (filling_id = 0; filling_id < 10; filling_id++) {
522 len = zms_write(&fixture->fs, filling_id, &filling_id, sizeof(filling_id));
523
524 zassert_true(len == sizeof(filling_id), "zms_write failed: %d", len);
525
526 if (filling_id != 0) {
527 continue;
528 }
529
530 /* delete the first entry while it is the most recent one */
531 err = zms_delete(&fixture->fs, filling_id);
532 zassert_true(err == 0, "zms_delete call failure: %d", err);
533
534 len = zms_read(&fixture->fs, filling_id, &data_read, sizeof(data_read));
535 zassert_true(len == -ENOENT, "zms_read shouldn't found the entry: %d", len);
536 }
537
538 /* delete existing entry */
539 err = zms_delete(&fixture->fs, 1);
540 zassert_true(err == 0, "zms_delete call failure: %d", err);
541
542 len = zms_read(&fixture->fs, 1, &data_read, sizeof(data_read));
543 zassert_true(len == -ENOENT, "zms_read shouldn't found the entry: %d", len);
544
545 ate_wra = fixture->fs.ate_wra;
546 data_wra = fixture->fs.data_wra;
547
548 #ifdef CONFIG_ZMS_NO_DOUBLE_WRITE
549 /* delete already deleted entry */
550 err = zms_delete(&fixture->fs, 1);
551 zassert_true(err == 0, "zms_delete call failure: %d", err);
552 zassert_true(ate_wra == fixture->fs.ate_wra && data_wra == fixture->fs.data_wra,
553 "delete already deleted entry should not make"
554 " any footprint in the storage");
555
556 /* delete nonexisting entry */
557 err = zms_delete(&fixture->fs, filling_id);
558 zassert_true(err == 0, "zms_delete call failure: %d", err);
559 zassert_true(ate_wra == fixture->fs.ate_wra && data_wra == fixture->fs.data_wra,
560 "delete nonexistent entry should not make"
561 " any footprint in the storage");
562 #endif
563 }
564
565 /*
566 * Test that garbage-collection can recover all ate's even when the last ate,
567 * ie close_ate, is corrupt. In this test the close_ate is set to point to the
568 * last ate at -5. A valid ate is however present at -6. Since the close_ate
569 * has an invalid crc8, the offset should not be used and a recover of the
570 * last ate should be done instead.
571 */
ZTEST_F(zms,test_zms_gc_corrupt_close_ate)572 ZTEST_F(zms, test_zms_gc_corrupt_close_ate)
573 {
574 struct zms_ate ate;
575 struct zms_ate close_ate;
576 struct zms_ate empty_ate;
577 uint32_t data;
578 ssize_t len;
579 int err;
580
581 Z_TEST_SKIP_IFNDEF(CONFIG_FLASH_SIMULATOR_DOUBLE_WRITES);
582 close_ate.id = 0xffffffff;
583 close_ate.offset = fixture->fs.sector_size - sizeof(struct zms_ate) * 5;
584 close_ate.len = 0;
585 close_ate.metadata = 0xffffffff;
586 close_ate.cycle_cnt = 1;
587 close_ate.crc8 = 0xff; /* Incorrect crc8 */
588
589 empty_ate.id = 0xffffffff;
590 empty_ate.offset = 0;
591 empty_ate.len = 0xffff;
592 empty_ate.metadata = 0x4201;
593 empty_ate.cycle_cnt = 1;
594 empty_ate.crc8 =
595 crc8_ccitt(0xff, (uint8_t *)&empty_ate + SIZEOF_FIELD(struct zms_ate, crc8),
596 sizeof(struct zms_ate) - SIZEOF_FIELD(struct zms_ate, crc8));
597
598 memset(&ate, 0, sizeof(struct zms_ate));
599 ate.id = 0x1;
600 ate.len = sizeof(data);
601 ate.cycle_cnt = 1;
602 data = 0xaa55aa55;
603 memcpy(&ate.data, &data, sizeof(data));
604 ate.crc8 = crc8_ccitt(0xff, (uint8_t *)&ate + SIZEOF_FIELD(struct zms_ate, crc8),
605 sizeof(struct zms_ate) - SIZEOF_FIELD(struct zms_ate, crc8));
606
607 /* Add empty ATE */
608 err = flash_write(fixture->fs.flash_device,
609 fixture->fs.offset + fixture->fs.sector_size - sizeof(struct zms_ate),
610 &empty_ate, sizeof(empty_ate));
611 zassert_true(err == 0, "flash_write failed: %d", err);
612
613 /* Mark sector 0 as closed */
614 err = flash_write(fixture->fs.flash_device,
615 fixture->fs.offset + fixture->fs.sector_size - 2 * sizeof(struct zms_ate),
616 &close_ate, sizeof(close_ate));
617 zassert_true(err == 0, "flash_write failed: %d", err);
618
619 /* Write valid ate at -6 */
620 err = flash_write(fixture->fs.flash_device,
621 fixture->fs.offset + fixture->fs.sector_size - 6 * sizeof(struct zms_ate),
622 &ate, sizeof(ate));
623 zassert_true(err == 0, "flash_write failed: %d", err);
624
625 /* Mark sector 1 as closed */
626 err = flash_write(fixture->fs.flash_device,
627 fixture->fs.offset + (2 * fixture->fs.sector_size) -
628 2 * sizeof(struct zms_ate),
629 &close_ate, sizeof(close_ate));
630 zassert_true(err == 0, "flash_write failed: %d", err);
631
632 fixture->fs.sector_count = 3;
633
634 err = zms_mount(&fixture->fs);
635 zassert_true(err == 0, "zms_mount call failure: %d", err);
636
637 data = 0;
638 len = zms_read(&fixture->fs, 1, &data, sizeof(data));
639 zassert_true(len == sizeof(data), "zms_read should have read %d bytes", sizeof(data));
640 zassert_true(data == 0xaa55aa55, "unexpected value %d", data);
641 }
642
643 /*
644 * Test that garbage-collection correctly handles corrupt ate's.
645 */
ZTEST_F(zms,test_zms_gc_corrupt_ate)646 ZTEST_F(zms, test_zms_gc_corrupt_ate)
647 {
648 struct zms_ate corrupt_ate;
649 struct zms_ate close_ate;
650 int err;
651
652 close_ate.id = 0xffffffff;
653 close_ate.offset = fixture->fs.sector_size / 2;
654 close_ate.len = 0;
655 close_ate.crc8 =
656 crc8_ccitt(0xff, (uint8_t *)&close_ate + SIZEOF_FIELD(struct zms_ate, crc8),
657 sizeof(struct zms_ate) - SIZEOF_FIELD(struct zms_ate, crc8));
658
659 corrupt_ate.id = 0xdeadbeef;
660 corrupt_ate.offset = 0;
661 corrupt_ate.len = 20;
662 corrupt_ate.crc8 = 0xff; /* Incorrect crc8 */
663
664 /* Mark sector 0 as closed */
665 err = flash_write(fixture->fs.flash_device,
666 fixture->fs.offset + fixture->fs.sector_size - 2 * sizeof(struct zms_ate),
667 &close_ate, sizeof(close_ate));
668 zassert_true(err == 0, "flash_write failed: %d", err);
669
670 /* Write a corrupt ate */
671 err = flash_write(fixture->fs.flash_device,
672 fixture->fs.offset + (fixture->fs.sector_size / 2), &corrupt_ate,
673 sizeof(corrupt_ate));
674 zassert_true(err == 0, "flash_write failed: %d", err);
675
676 /* Mark sector 1 as closed */
677 err = flash_write(fixture->fs.flash_device,
678 fixture->fs.offset + (2 * fixture->fs.sector_size) -
679 2 * sizeof(struct zms_ate),
680 &close_ate, sizeof(close_ate));
681 zassert_true(err == 0, "flash_write failed: %d", err);
682
683 fixture->fs.sector_count = 3;
684
685 err = zms_mount(&fixture->fs);
686 zassert_true(err == 0, "zms_mount call failure: %d", err);
687 }
688
689 #ifdef CONFIG_ZMS_LOOKUP_CACHE
num_matching_cache_entries(uint64_t addr,bool compare_sector_only,struct zms_fs * fs)690 static size_t num_matching_cache_entries(uint64_t addr, bool compare_sector_only, struct zms_fs *fs)
691 {
692 size_t num = 0;
693 uint64_t mask = compare_sector_only ? ADDR_SECT_MASK : UINT64_MAX;
694
695 for (int i = 0; i < CONFIG_ZMS_LOOKUP_CACHE_SIZE; i++) {
696 if ((fs->lookup_cache[i] & mask) == addr) {
697 num++;
698 }
699 }
700
701 return num;
702 }
703
num_occupied_cache_entries(struct zms_fs * fs)704 static size_t num_occupied_cache_entries(struct zms_fs *fs)
705 {
706 return CONFIG_ZMS_LOOKUP_CACHE_SIZE -
707 num_matching_cache_entries(ZMS_LOOKUP_CACHE_NO_ADDR, false, fs);
708 }
709 #endif
710
711 /*
712 * Test that ZMS lookup cache is properly rebuilt on zms_mount(), or initialized
713 * to ZMS_LOOKUP_CACHE_NO_ADDR if the store is empty.
714 */
ZTEST_F(zms,test_zms_cache_init)715 ZTEST_F(zms, test_zms_cache_init)
716 {
717 #ifdef CONFIG_ZMS_LOOKUP_CACHE
718 int err;
719 size_t num;
720 uint64_t ate_addr;
721 uint8_t data = 0;
722
723 /* Test cache initialization when the store is empty */
724
725 fixture->fs.sector_count = 3;
726 err = zms_mount(&fixture->fs);
727 zassert_true(err == 0, "zms_mount call failure: %d", err);
728
729 num = num_occupied_cache_entries(&fixture->fs);
730 zassert_equal(num, 0, "uninitialized cache");
731
732 /* Test cache update after zms_write() */
733
734 ate_addr = fixture->fs.ate_wra;
735 err = zms_write(&fixture->fs, 1, &data, sizeof(data));
736 zassert_equal(err, sizeof(data), "zms_write call failure: %d", err);
737
738 num = num_occupied_cache_entries(&fixture->fs);
739 zassert_equal(num, 1, "cache not updated after write");
740
741 num = num_matching_cache_entries(ate_addr, false, &fixture->fs);
742 zassert_equal(num, 1, "invalid cache entry after write");
743
744 /* Test cache initialization when the store is non-empty */
745
746 memset(fixture->fs.lookup_cache, 0xAA, sizeof(fixture->fs.lookup_cache));
747 err = zms_mount(&fixture->fs);
748 zassert_true(err == 0, "zms_mount call failure: %d", err);
749
750 num = num_occupied_cache_entries(&fixture->fs);
751 zassert_equal(num, 1, "uninitialized cache after restart");
752
753 num = num_matching_cache_entries(ate_addr, false, &fixture->fs);
754 zassert_equal(num, 1, "invalid cache entry after restart");
755 #endif
756 }
757
758 /*
759 * Test that even after writing more ZMS IDs than the number of ZMS lookup cache
760 * entries they all can be read correctly.
761 */
ZTEST_F(zms,test_zms_cache_collission)762 ZTEST_F(zms, test_zms_cache_collission)
763 {
764 #ifdef CONFIG_ZMS_LOOKUP_CACHE
765 int err;
766 uint16_t data;
767
768 fixture->fs.sector_count = 4;
769 err = zms_mount(&fixture->fs);
770 zassert_true(err == 0, "zms_mount call failure: %d", err);
771
772 for (int id = 0; id < CONFIG_ZMS_LOOKUP_CACHE_SIZE + 1; id++) {
773 data = id;
774 err = zms_write(&fixture->fs, id, &data, sizeof(data));
775 zassert_equal(err, sizeof(data), "zms_write call failure: %d", err);
776 }
777
778 for (int id = 0; id < CONFIG_ZMS_LOOKUP_CACHE_SIZE + 1; id++) {
779 err = zms_read(&fixture->fs, id, &data, sizeof(data));
780 zassert_equal(err, sizeof(data), "zms_read call failure: %d", err);
781 zassert_equal(data, id, "incorrect data read");
782 }
783 #endif
784 }
785
786 /*
787 * Test that ZMS lookup cache does not contain any address from gc-ed sector
788 */
ZTEST_F(zms,test_zms_cache_gc)789 ZTEST_F(zms, test_zms_cache_gc)
790 {
791 #ifdef CONFIG_ZMS_LOOKUP_CACHE
792 int err;
793 size_t num;
794 uint16_t data = 0;
795
796 fixture->fs.sector_count = 3;
797 err = zms_mount(&fixture->fs);
798 zassert_true(err == 0, "zms_mount call failure: %d", err);
799
800 /* Fill the first sector with writes of ID 1 */
801
802 while (fixture->fs.data_wra + sizeof(data) + sizeof(struct zms_ate) <=
803 fixture->fs.ate_wra) {
804 ++data;
805 err = zms_write(&fixture->fs, 1, &data, sizeof(data));
806 zassert_equal(err, sizeof(data), "zms_write call failure: %d", err);
807 }
808
809 /* Verify that cache contains a single entry for sector 0 */
810
811 num = num_matching_cache_entries(0ULL << ADDR_SECT_SHIFT, true, &fixture->fs);
812 zassert_equal(num, 1, "invalid cache content after filling sector 0");
813
814 /* Fill the second sector with writes of ID 2 */
815
816 while ((fixture->fs.ate_wra >> ADDR_SECT_SHIFT) != 2) {
817 ++data;
818 err = zms_write(&fixture->fs, 2, &data, sizeof(data));
819 zassert_equal(err, sizeof(data), "zms_write call failure: %d", err);
820 }
821
822 /*
823 * At this point sector 0 should have been gc-ed. Verify that action is
824 * reflected by the cache content.
825 */
826
827 num = num_matching_cache_entries(0ULL << ADDR_SECT_SHIFT, true, &fixture->fs);
828 zassert_equal(num, 0, "not invalidated cache entries aftetr gc");
829
830 num = num_matching_cache_entries(2ULL << ADDR_SECT_SHIFT, true, &fixture->fs);
831 zassert_equal(num, 2, "invalid cache content after gc");
832 #endif
833 }
834
835 /*
836 * Test ZMS lookup cache hash quality.
837 */
ZTEST_F(zms,test_zms_cache_hash_quality)838 ZTEST_F(zms, test_zms_cache_hash_quality)
839 {
840 #ifdef CONFIG_ZMS_LOOKUP_CACHE
841 const size_t MIN_CACHE_OCCUPANCY = CONFIG_ZMS_LOOKUP_CACHE_SIZE * 6 / 10;
842 int err;
843 size_t num;
844 uint32_t id;
845 uint16_t data;
846
847 err = zms_mount(&fixture->fs);
848 zassert_true(err == 0, "zms_mount call failure: %d", err);
849
850 /* Write ZMS IDs from 0 to CONFIG_ZMS_LOOKUP_CACHE_SIZE - 1 */
851
852 for (int i = 0; i < CONFIG_ZMS_LOOKUP_CACHE_SIZE; i++) {
853 id = i;
854 data = 0;
855
856 err = zms_write(&fixture->fs, id, &data, sizeof(data));
857 zassert_equal(err, sizeof(data), "zms_write call failure: %d", err);
858 }
859
860 /* Verify that at least 60% cache entries are occupied */
861
862 num = num_occupied_cache_entries(&fixture->fs);
863 TC_PRINT("Cache occupancy: %u\n", (unsigned int)num);
864 zassert_between_inclusive(num, MIN_CACHE_OCCUPANCY, CONFIG_ZMS_LOOKUP_CACHE_SIZE,
865 "too low cache occupancy - poor hash quality");
866
867 err = zms_clear(&fixture->fs);
868 zassert_true(err == 0, "zms_clear call failure: %d", err);
869
870 err = zms_mount(&fixture->fs);
871 zassert_true(err == 0, "zms_mount call failure: %d", err);
872
873 /* Write CONFIG_ZMS_LOOKUP_CACHE_SIZE ZMS IDs that form the following series: 0, 4, 8... */
874
875 for (int i = 0; i < CONFIG_ZMS_LOOKUP_CACHE_SIZE; i++) {
876 id = i * 4;
877 data = 0;
878
879 err = zms_write(&fixture->fs, id, &data, sizeof(data));
880 zassert_equal(err, sizeof(data), "zms_write call failure: %d", err);
881 }
882
883 /* Verify that at least 60% cache entries are occupied */
884
885 num = num_occupied_cache_entries(&fixture->fs);
886 TC_PRINT("Cache occupancy: %u\n", (unsigned int)num);
887 zassert_between_inclusive(num, MIN_CACHE_OCCUPANCY, CONFIG_ZMS_LOOKUP_CACHE_SIZE,
888 "too low cache occupancy - poor hash quality");
889
890 #endif
891 }
892