1 /*
2 * Copyright (c) 2021 NXP
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 /*
8 * WARNING: This test will overwrite data on any disk utilized. Do not run
9 * this test with an disk that has useful data
10 */
11
12 #include <zephyr/kernel.h>
13 #include <zephyr/ztest.h>
14 #include <zephyr/storage/disk_access.h>
15 #include <zephyr/device.h>
16
17 #ifdef CONFIG_DISK_DRIVER_LOOPBACK
18 #include <ff.h>
19 #include <zephyr/fs/fs.h>
20 #include <zephyr/drivers/loopback_disk.h>
21 #endif
22
23 #if defined(CONFIG_DISK_DRIVER_SDMMC)
24 #define DISK_NAME_PHYS "SD"
25 #elif defined(CONFIG_DISK_DRIVER_MMC)
26 #define DISK_NAME_PHYS "SD2"
27 #elif defined(CONFIG_DISK_DRIVER_FLASH)
28 #define DISK_NAME_PHYS "NAND"
29 #elif defined(CONFIG_NVME)
30 #define DISK_NAME_PHYS "nvme0n0"
31 #elif defined(CONFIG_DISK_DRIVER_RAM)
32 /* Since ramdisk is enabled by default on e.g. qemu boards, it needs to be checked last to not
33 * override other backends.
34 */
35 #define DISK_NAME_PHYS "RAM"
36 #else
37 #error "No disk device defined, is your board supported?"
38 #endif
39
40 #ifdef CONFIG_DISK_DRIVER_LOOPBACK
41 #define DISK_NAME "loopback0"
42 #else
43 #define DISK_NAME DISK_NAME_PHYS
44 #endif
45
46 /* Assume the largest sector we will encounter is 512 bytes */
47 #define SECTOR_SIZE 512
48
49 /* Sector counts to read */
50 #define SECTOR_COUNT1 8
51 #define SECTOR_COUNT2 1
52 #define SECTOR_COUNT3 29
53 #define SECTOR_COUNT4 31
54 #define SECTOR_COUNT_MAX 32
55
56 #define OVERFLOW_CANARY 0xDE
57
58 static const char *disk_pdrv = DISK_NAME;
59 static uint32_t disk_sector_count;
60 static uint32_t disk_sector_size;
61
62 /* + 4 to make sure the second buffer is dword-aligned for NVME */
63 static uint8_t scratch_buf[2][SECTOR_COUNT_MAX * SECTOR_SIZE + 4];
64
65 #ifdef CONFIG_DISK_DRIVER_LOOPBACK
66 #define BACKING_PATH "/" DISK_NAME_PHYS ":"
67
68 static struct loopback_disk_access lo_access;
69 static FATFS fat_fs;
70 static struct fs_mount_t backing_mount = {
71 .type = FS_FATFS,
72 .mnt_point = BACKING_PATH,
73 .fs_data = &fat_fs,
74 };
75 static const uint8_t zero_kb[1024] = {};
setup_loopback_backing(void)76 static void setup_loopback_backing(void)
77 {
78 int rc;
79
80 rc = fs_mkfs(FS_FATFS, (uintptr_t)&BACKING_PATH[1], NULL, 0);
81 zassert_equal(rc, 0, "Failed to format backing file system");
82
83 rc = fs_mount(&backing_mount);
84 zassert_equal(rc, 0, "Failed to mount backing file system");
85
86 struct fs_file_t f;
87
88 fs_file_t_init(&f);
89 rc = fs_open(&f, BACKING_PATH "/loopback.img", FS_O_WRITE | FS_O_CREATE);
90 zassert_equal(rc, 0, "Failed to create backing file");
91 for (int i = 0; i < 64; i++) {
92 rc = fs_write(&f, zero_kb, sizeof(zero_kb));
93 zassert_equal(rc, sizeof(zero_kb), "Failed to enlarge backing file");
94 }
95 rc = fs_close(&f);
96 zassert_equal(rc, 0, "Failed to close backing file");
97
98 rc = loopback_disk_access_register(&lo_access, BACKING_PATH "/loopback.img", DISK_NAME);
99 zassert_equal(rc, 0, "Loopback disk access initialization failed");
100 }
101 #endif
102
103 /* Sets up test by initializing disk */
test_setup(void)104 static void test_setup(void)
105 {
106 int rc;
107 uint32_t cmd_buf;
108
109 rc = disk_access_init(disk_pdrv);
110 zassert_equal(rc, 0, "Disk access initialization failed");
111
112 rc = disk_access_status(disk_pdrv);
113 zassert_equal(rc, DISK_STATUS_OK, "Disk status is not OK");
114
115 rc = disk_access_ioctl(disk_pdrv, DISK_IOCTL_GET_SECTOR_COUNT, &cmd_buf);
116 zassert_equal(rc, 0, "Disk ioctl get sector count failed");
117
118 TC_PRINT("Disk reports %u sectors\n", cmd_buf);
119 disk_sector_count = cmd_buf;
120
121 rc = disk_access_ioctl(disk_pdrv, DISK_IOCTL_GET_SECTOR_SIZE, &cmd_buf);
122 zassert_equal(rc, 0, "Disk ioctl get sector size failed");
123 TC_PRINT("Disk reports sector size %u\n", cmd_buf);
124 disk_sector_size = cmd_buf;
125
126 /* We could allocate memory once we know the sector size, but instead
127 * just verify our assumed maximum size
128 */
129 zassert_true(cmd_buf <= SECTOR_SIZE,
130 "Test will fail, SECTOR_SIZE definition must be increased");
131 }
132
133 /* Reads sectors, verifying overflow does not occur */
read_sector(uint8_t * buf,uint32_t start,uint32_t num_sectors)134 static int read_sector(uint8_t *buf, uint32_t start, uint32_t num_sectors)
135 {
136 int rc;
137
138 /* Set up overflow canary */
139 buf[num_sectors * disk_sector_size] = OVERFLOW_CANARY;
140 rc = disk_access_read(disk_pdrv, buf, start, num_sectors);
141 /* Check canary */
142 zassert_equal(buf[num_sectors * disk_sector_size], OVERFLOW_CANARY,
143 "Read overflowed requested length");
144 return rc; /* Let calling function check return code */
145 }
146
147 /* Tests reading from a variety of sectors */
test_sector_read(uint8_t * buf,uint32_t num_sectors)148 static void test_sector_read(uint8_t *buf, uint32_t num_sectors)
149 {
150 int rc, sector;
151
152 TC_PRINT("Testing reads of %u sectors\n", num_sectors);
153 /* Read from disk sector 0*/
154 rc = read_sector(buf, 0, num_sectors);
155 zassert_equal(rc, 0, "Failed to read from sector zero");
156 /* Read from a sector in the "middle" of the disk */
157 if (disk_sector_count / 2 > num_sectors) {
158 sector = disk_sector_count / 2 - num_sectors;
159 } else {
160 sector = 0;
161 }
162
163 rc = read_sector(buf, sector, num_sectors);
164 zassert_equal(rc, 0, "Failed to read from mid disk sector");
165 /* Read from the last sector */
166 rc = read_sector(buf, disk_sector_count - 1, num_sectors);
167 if (num_sectors == 1) {
168 zassert_equal(rc, 0, "Failed to read from last sector");
169 } else {
170 zassert_not_equal(rc, 0, "Disk should fail to read out of sector bounds");
171 }
172 }
173
174 /* Write sector of disk, and check the data to ensure it is valid
175 * WARNING: this test is destructive- it will overwrite data on the disk!
176 */
write_sector_checked(uint8_t * wbuf,uint8_t * rbuf,uint32_t start,uint32_t num_sectors)177 static int write_sector_checked(uint8_t *wbuf, uint8_t *rbuf, uint32_t start, uint32_t num_sectors)
178 {
179 int rc, i;
180
181 /* First, fill the write buffer with data */
182 for (i = 0; i < num_sectors * disk_sector_size; i++) {
183 wbuf[i] = (i & (~num_sectors));
184 }
185 /* Now write data to the sector */
186 rc = disk_access_write(disk_pdrv, wbuf, start, num_sectors);
187 if (rc) {
188 return rc; /* Let calling function handle disk error */
189 }
190 /* Read back the written data into another buffer */
191 memset(rbuf, 0, num_sectors * disk_sector_size);
192 rc = read_sector(rbuf, start, num_sectors);
193 if (rc) {
194 return rc;
195 }
196 /* Check the read data versus the written data */
197 zassert_mem_equal(wbuf, rbuf, num_sectors * disk_sector_size,
198 "Read data did not match data written to disk");
199 return rc;
200 }
201
erase_sector_checked(uint8_t * rbuf,uint32_t start,uint32_t num_sectors)202 static int erase_sector_checked(uint8_t *rbuf, uint32_t start, uint32_t num_sectors)
203 {
204 int rc, i;
205
206 /* Erase the specified sectors */
207 rc = disk_access_erase(disk_pdrv, start, num_sectors, DISK_ACCESS_ERASE_PHYSICAL);
208 if (rc) {
209 return rc; /* Let calling function handle disk error */
210 }
211
212 /* Read the erased sectors */
213 rc = read_sector(rbuf, start, num_sectors);
214 if (rc) {
215 return rc;
216 }
217
218 /* All data should be equal 0x00 or 0xFF */
219 for (i = 0; i < num_sectors * disk_sector_size; i++) {
220 zassert_true((rbuf[i] == 0x00) || (rbuf[i] == 0xFF),
221 "Data not erased from disk sector %u", start + i);
222 }
223 return 0;
224 }
225
226 /* Tests writing to a variety of sectors
227 * WARNING: this test is destructive- it will overwrite data on the disk!
228 */
test_sector_write(uint8_t * wbuf,uint8_t * rbuf,uint32_t num_sectors)229 static void test_sector_write(uint8_t *wbuf, uint8_t *rbuf, uint32_t num_sectors)
230 {
231 int rc, sector;
232
233 TC_PRINT("Testing writes of %u sectors\n", num_sectors);
234 /* Write to disk sector zero */
235 rc = write_sector_checked(wbuf, rbuf, 0, num_sectors);
236 zassert_equal(rc, 0, "Failed to write to sector zero");
237 /* Write to a sector in the "middle" of the disk */
238 if (disk_sector_count / 2 > num_sectors) {
239 sector = disk_sector_count / 2 - num_sectors;
240 } else {
241 sector = 0;
242 }
243
244 rc = write_sector_checked(wbuf, rbuf, sector, num_sectors);
245 zassert_equal(rc, 0, "Failed to write to mid disk sector");
246 /* Write to the last sector */
247 rc = write_sector_checked(wbuf, rbuf, disk_sector_count - 1, num_sectors);
248 if (num_sectors == 1) {
249 zassert_equal(rc, 0, "Failed to write to last sector");
250 } else {
251 zassert_not_equal(rc, 0, "Disk should fail to write out of sector bounds");
252 }
253 }
254
255 /* Tests erasing a variety of sectors
256 * WARNING: this test is destructive- it will overwrite data on the disk!
257 */
test_sector_erase(uint8_t * wbuf,uint8_t * rbuf,uint32_t num_sectors)258 static void test_sector_erase(uint8_t *wbuf, uint8_t *rbuf, uint32_t num_sectors)
259 {
260 int rc, sector;
261
262 TC_PRINT("Testing erase of %u sectors\n", num_sectors);
263 /* Write and erase disk sector zero */
264 rc = write_sector_checked(wbuf, rbuf, 0, num_sectors);
265 zassert_equal(rc, 0, "Failed to write to sector zero");
266 rc = erase_sector_checked(rbuf, 0, num_sectors);
267 zassert_equal(rc, 0, "Failed to erase sector zero");
268
269 /* Write and erase sectors in the "middle" of the disk */
270 if (disk_sector_count / 2 > num_sectors) {
271 sector = disk_sector_count / 2 - num_sectors;
272 } else {
273 sector = 0;
274 }
275 rc = write_sector_checked(wbuf, rbuf, sector, num_sectors);
276 zassert_equal(rc, 0, "Failed to write to mid disk sector");
277 rc = erase_sector_checked(rbuf, sector, num_sectors);
278 zassert_equal(rc, 0, "Failed to erase mid disk sector");
279
280 /* Write and erase the last sector */
281 rc = write_sector_checked(wbuf, rbuf, disk_sector_count - num_sectors, num_sectors);
282 zassert_equal(rc, 0, "Failed to write to last sector");
283 rc = erase_sector_checked(rbuf, disk_sector_count - num_sectors, num_sectors);
284 zassert_equal(rc, 0, "Failed to erase last sector");
285
286 /* Try and erase past the last sector */
287 rc = erase_sector_checked(rbuf, disk_sector_count - num_sectors + 1, num_sectors);
288 zassert_equal(rc, -EINVAL,
289 "Unexpected error code when attempting to erase past end of disk");
290 rc = erase_sector_checked(rbuf, disk_sector_count + 1, num_sectors);
291 zassert_equal(rc, -EINVAL,
292 "Unexpected error code when attempting to erase past end of disk");
293 rc = erase_sector_checked(rbuf, UINT32_MAX, num_sectors);
294 zassert_equal(rc, -EINVAL,
295 "Unexpected error code when attempting to erase past end of disk");
296 }
297
298 /* Test multiple reads in series, and reading from a variety of blocks */
ZTEST(disk_driver,test_read)299 ZTEST(disk_driver, test_read)
300 {
301 int rc, i;
302
303 /* Verify all 4 read sizes work */
304 test_sector_read(scratch_buf[0], SECTOR_COUNT1);
305 test_sector_read(scratch_buf[0], SECTOR_COUNT2);
306 test_sector_read(scratch_buf[0], SECTOR_COUNT3);
307 test_sector_read(scratch_buf[0], SECTOR_COUNT4);
308
309 /* Verify that reading from the same location returns to same data */
310 memset(scratch_buf[0], 0, SECTOR_COUNT1 * disk_sector_size);
311 rc = read_sector(scratch_buf[0], 0, SECTOR_COUNT1);
312 zassert_equal(rc, 0, "Failed to read from disk");
313 for (i = 0; i < 10; i++) {
314 /* Read from sector, and compare it to the first read */
315 memset(scratch_buf[1], 0xff, SECTOR_COUNT1 * disk_sector_size);
316 rc = read_sector(scratch_buf[1], 0, SECTOR_COUNT1);
317 zassert_equal(rc, 0, "Failed to read from disk at same sector location");
318 zassert_mem_equal(scratch_buf[1], scratch_buf[0], SECTOR_COUNT1 * disk_sector_size,
319 "Multiple reads mismatch");
320 }
321 }
322
323 /* test writing data, and then verifying it was written correctly.
324 * WARNING: this test is destructive- it will overwrite data on the disk!
325 */
ZTEST(disk_driver,test_write)326 ZTEST(disk_driver, test_write)
327 {
328 int rc, i;
329
330 /* Verify all 4 sector write sizes work */
331 test_sector_write(scratch_buf[0], scratch_buf[1], SECTOR_COUNT1);
332 test_sector_write(scratch_buf[0], scratch_buf[1], SECTOR_COUNT2);
333 test_sector_write(scratch_buf[0], scratch_buf[1], SECTOR_COUNT3);
334 test_sector_write(scratch_buf[0], scratch_buf[1], SECTOR_COUNT4);
335
336 /* Verify that multiple writes to the same location work */
337 for (i = 0; i < 10; i++) {
338 /* Write to sector- helper function verifies written data is correct */
339 rc = write_sector_checked(scratch_buf[0], scratch_buf[1], 0, SECTOR_COUNT1);
340 zassert_equal(rc, 0, "Failed to write to disk at same sector location");
341 }
342 }
343
344 /* Test multiple erases in series, and erasing from a variety of blocks */
ZTEST(disk_driver,test_erase)345 ZTEST(disk_driver, test_erase)
346 {
347 int rc, i;
348
349 /* Skip the test if erasing not supported by the driver */
350 if (disk_access_erase(disk_pdrv, 0, 1, DISK_ACCESS_ERASE_PHYSICAL) == -EINVAL) {
351 ztest_test_skip();
352 return;
353 }
354
355 /* Test erasing with a bad erase type */
356 rc = disk_access_erase(disk_pdrv, 0, 1, DISK_ACCESS_ERASE_PHYSICAL + 1);
357 zassert_equal(-EINVAL, rc);
358
359 /* Verify a range of erase sizes work */
360 test_sector_erase(scratch_buf[0], scratch_buf[1], 8);
361 test_sector_erase(scratch_buf[0], scratch_buf[1], 16);
362 test_sector_erase(scratch_buf[0], scratch_buf[1], 24);
363 test_sector_erase(scratch_buf[0], scratch_buf[1], 32);
364
365 /* Verify that multiple erases to the same location work */
366 for (i = 0; i < 10; i++) {
367 rc = erase_sector_checked(scratch_buf[1], 0, 16);
368 zassert_equal(0, rc, "Failed to erase sector zero");
369 }
370 }
371
disk_driver_setup(void)372 static void *disk_driver_setup(void)
373 {
374 #ifdef CONFIG_DISK_DRIVER_LOOPBACK
375 setup_loopback_backing();
376 #endif
377 test_setup();
378
379 return NULL;
380 }
381
disk_driver_teardown(void * fixture)382 static void disk_driver_teardown(void *fixture)
383 {
384 #ifdef CONFIG_DISK_DRIVER_LOOPBACK
385 (void)loopback_disk_access_unregister(&lo_access);
386 #endif
387 }
388
389 ZTEST_SUITE(disk_driver, NULL, disk_driver_setup, NULL, NULL, disk_driver_teardown);
390