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 
55 #define OVERFLOW_CANARY 0xDE
56 
57 static const char *disk_pdrv = DISK_NAME;
58 static uint32_t disk_sector_count;
59 static uint32_t disk_sector_size;
60 
61 /* + 4 to make sure the second buffer is dword-aligned for NVME */
62 static uint8_t scratch_buf[2][SECTOR_COUNT4 * SECTOR_SIZE + 4];
63 
64 #ifdef CONFIG_DISK_DRIVER_LOOPBACK
65 #define BACKING_PATH "/"DISK_NAME_PHYS":"
66 
67 static struct loopback_disk_access lo_access;
68 static FATFS fat_fs;
69 static struct fs_mount_t backing_mount = {
70 	.type = FS_FATFS,
71 	.mnt_point = BACKING_PATH,
72 	.fs_data = &fat_fs,
73 };
74 static const uint8_t zero_kb[1024] = {};
setup_loopback_backing(void)75 static void setup_loopback_backing(void)
76 {
77 	int rc;
78 
79 	rc = fs_mkfs(FS_FATFS, (uintptr_t)&BACKING_PATH[1], NULL, 0);
80 	zassert_equal(rc, 0, "Failed to format backing file system");
81 
82 	rc = fs_mount(&backing_mount);
83 	zassert_equal(rc, 0, "Failed to mount backing file system");
84 
85 	struct fs_file_t f;
86 
87 	fs_file_t_init(&f);
88 	rc = fs_open(&f, BACKING_PATH "/loopback.img", FS_O_WRITE | FS_O_CREATE);
89 	zassert_equal(rc, 0, "Failed to create backing file");
90 	for (int i = 0; i < 64; i++) {
91 		rc = fs_write(&f, zero_kb, sizeof(zero_kb));
92 		zassert_equal(rc, sizeof(zero_kb), "Failed to enlarge backing file");
93 	}
94 	rc = fs_close(&f);
95 	zassert_equal(rc, 0, "Failed to close backing file");
96 
97 	rc = loopback_disk_access_register(&lo_access, BACKING_PATH "/loopback.img", DISK_NAME);
98 	zassert_equal(rc, 0, "Loopback disk access initialization failed");
99 }
100 #endif
101 
102 /* Sets up test by initializing disk */
test_setup(void)103 static void test_setup(void)
104 {
105 	int rc;
106 	uint32_t cmd_buf;
107 
108 	rc = disk_access_init(disk_pdrv);
109 	zassert_equal(rc, 0, "Disk access initialization failed");
110 
111 	rc = disk_access_status(disk_pdrv);
112 	zassert_equal(rc, DISK_STATUS_OK, "Disk status is not OK");
113 
114 	rc = disk_access_ioctl(disk_pdrv, DISK_IOCTL_GET_SECTOR_COUNT, &cmd_buf);
115 	zassert_equal(rc, 0, "Disk ioctl get sector count failed");
116 
117 	TC_PRINT("Disk reports %u sectors\n", cmd_buf);
118 	disk_sector_count = cmd_buf;
119 
120 	rc = disk_access_ioctl(disk_pdrv, DISK_IOCTL_GET_SECTOR_SIZE, &cmd_buf);
121 	zassert_equal(rc, 0, "Disk ioctl get sector size failed");
122 	TC_PRINT("Disk reports sector size %u\n", cmd_buf);
123 	disk_sector_size = cmd_buf;
124 
125 	/* We could allocate memory once we know the sector size, but instead
126 	 * just verify our assumed maximum size
127 	 */
128 	zassert_true(cmd_buf <= SECTOR_SIZE,
129 		"Test will fail, SECTOR_SIZE definition must be increased");
130 }
131 
132 /* Reads sectors, verifying overflow does not occur */
read_sector(uint8_t * buf,uint32_t start,uint32_t num_sectors)133 static int read_sector(uint8_t *buf, uint32_t start, uint32_t num_sectors)
134 {
135 	int rc;
136 
137 	/* Set up overflow canary */
138 	buf[num_sectors * disk_sector_size] = OVERFLOW_CANARY;
139 	rc = disk_access_read(disk_pdrv, buf, start, num_sectors);
140 	/* Check canary */
141 	zassert_equal(buf[num_sectors * disk_sector_size], OVERFLOW_CANARY,
142 		"Read overflowed requested length");
143 	return rc; /* Let calling function check return code */
144 }
145 
146 /* Tests reading from a variety of sectors */
test_sector_read(uint8_t * buf,uint32_t num_sectors)147 static void test_sector_read(uint8_t *buf, uint32_t num_sectors)
148 {
149 	int rc, sector;
150 
151 	TC_PRINT("Testing reads of %u sectors\n", num_sectors);
152 	/* Read from disk sector 0*/
153 	rc = read_sector(buf, 0, num_sectors);
154 	zassert_equal(rc, 0, "Failed to read from sector zero");
155 	/* Read from a sector in the "middle" of the disk */
156 	if (disk_sector_count / 2 > num_sectors) {
157 		sector = disk_sector_count / 2 - num_sectors;
158 	} else {
159 		sector = 0;
160 	}
161 
162 	rc = read_sector(buf, sector, num_sectors);
163 	zassert_equal(rc, 0, "Failed to read from mid disk sector");
164 	/* Read from the last sector */
165 	rc = read_sector(buf, disk_sector_count - 1, num_sectors);
166 	if (num_sectors == 1) {
167 		zassert_equal(rc, 0, "Failed to read from last sector");
168 	} else {
169 		zassert_not_equal(rc, 0, "Disk should fail to read out of sector bounds");
170 	}
171 }
172 
173 /* Write sector of disk, and check the data to ensure it is valid
174  * WARNING: this test is destructive- it will overwrite data on the disk!
175  */
write_sector_checked(uint8_t * wbuf,uint8_t * rbuf,uint32_t start,uint32_t num_sectors)176 static int write_sector_checked(uint8_t *wbuf, uint8_t *rbuf,
177 			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 
202 /* Tests writing to a variety of sectors
203  * WARNING: this test is destructive- it will overwrite data on the disk!
204  */
test_sector_write(uint8_t * wbuf,uint8_t * rbuf,uint32_t num_sectors)205 static void test_sector_write(uint8_t *wbuf, uint8_t *rbuf, uint32_t num_sectors)
206 {
207 	int rc, sector;
208 
209 	TC_PRINT("Testing writes of %u sectors\n", num_sectors);
210 	/* Write to disk sector zero */
211 	rc = write_sector_checked(wbuf, rbuf, 0, num_sectors);
212 	zassert_equal(rc, 0, "Failed to write to sector zero");
213 	/* Write to a sector in the "middle" of the disk */
214 	if (disk_sector_count / 2 > num_sectors) {
215 		sector = disk_sector_count / 2 - num_sectors;
216 	} else {
217 		sector = 0;
218 	}
219 
220 	rc = write_sector_checked(wbuf, rbuf, sector, num_sectors);
221 	zassert_equal(rc, 0, "Failed to write to mid disk sector");
222 	/* Write to the last sector */
223 	rc = write_sector_checked(wbuf, rbuf, disk_sector_count - 1, num_sectors);
224 	if (num_sectors == 1) {
225 		zassert_equal(rc, 0, "Failed to write to last sector");
226 	} else {
227 		zassert_not_equal(rc, 0, "Disk should fail to write out of sector bounds");
228 	}
229 }
230 
231 /* Test multiple reads in series, and reading from a variety of blocks */
ZTEST(disk_driver,test_read)232 ZTEST(disk_driver, test_read)
233 {
234 	int rc, i;
235 
236 	/* Verify all 4 read sizes work */
237 	test_sector_read(scratch_buf[0], SECTOR_COUNT1);
238 	test_sector_read(scratch_buf[0], SECTOR_COUNT2);
239 	test_sector_read(scratch_buf[0], SECTOR_COUNT3);
240 	test_sector_read(scratch_buf[0], SECTOR_COUNT4);
241 
242 	/* Verify that reading from the same location returns to same data */
243 	memset(scratch_buf[0], 0, SECTOR_COUNT1 * disk_sector_size);
244 	rc = read_sector(scratch_buf[0], 0, SECTOR_COUNT1);
245 	zassert_equal(rc, 0, "Failed to read from disk");
246 	for (i = 0; i < 10; i++) {
247 		/* Read from sector, and compare it to the first read */
248 		memset(scratch_buf[1], 0xff, SECTOR_COUNT1 * disk_sector_size);
249 		rc = read_sector(scratch_buf[1], 0, SECTOR_COUNT1);
250 		zassert_equal(rc, 0, "Failed to read from disk at same sector location");
251 		zassert_mem_equal(scratch_buf[1], scratch_buf[0],
252 				SECTOR_COUNT1 * disk_sector_size,
253 				"Multiple reads mismatch");
254 	}
255 }
256 
257 /* test writing data, and then verifying it was written correctly.
258  * WARNING: this test is destructive- it will overwrite data on the disk!
259  */
ZTEST(disk_driver,test_write)260 ZTEST(disk_driver, test_write)
261 {
262 	int rc, i;
263 
264 	/* Verify all 4 sector write sizes work */
265 	test_sector_write(scratch_buf[0], scratch_buf[1], SECTOR_COUNT1);
266 	test_sector_write(scratch_buf[0], scratch_buf[1], SECTOR_COUNT2);
267 	test_sector_write(scratch_buf[0], scratch_buf[1], SECTOR_COUNT3);
268 	test_sector_write(scratch_buf[0], scratch_buf[1], SECTOR_COUNT4);
269 
270 	/* Verify that multiple writes to the same location work */
271 	for (i = 0; i < 10; i++) {
272 		/* Write to sector- helper function verifies written data is correct */
273 		rc = write_sector_checked(scratch_buf[0], scratch_buf[1], 0, SECTOR_COUNT1);
274 		zassert_equal(rc, 0, "Failed to write to disk at same sector location");
275 	}
276 }
277 
disk_driver_setup(void)278 static void *disk_driver_setup(void)
279 {
280 #ifdef CONFIG_DISK_DRIVER_LOOPBACK
281 	setup_loopback_backing();
282 #endif
283 	test_setup();
284 
285 	return NULL;
286 }
287 
288 ZTEST_SUITE(disk_driver, NULL, disk_driver_setup, NULL, NULL, NULL);
289