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