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