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 #if defined(CONFIG_DISK_DRIVER_SDMMC)
18 #define DISK_NAME CONFIG_SDMMC_VOLUME_NAME
19 #elif IS_ENABLED(CONFIG_DISK_DRIVER_MMC)
20 #define DISK_NAME CONFIG_MMC_VOLUME_NAME
21 #elif IS_ENABLED(CONFIG_DISK_DRIVER_RAM)
22 #define DISK_NAME "RAM"
23 #elif IS_ENABLED(CONFIG_NVME)
24 #define DISK_NAME "nvme0n0"
25 #else
26 #error "No disk device defined, is your board supported?"
27 #endif
28 
29 /* Assume the largest sector we will encounter is 512 bytes */
30 #define SECTOR_SIZE 512
31 
32 /* Sector counts to read */
33 #define SECTOR_COUNT1 8
34 #define SECTOR_COUNT2 1
35 #define SECTOR_COUNT3 29
36 #define SECTOR_COUNT4 31
37 
38 #define OVERFLOW_CANARY 0xDE
39 
40 static const char *disk_pdrv = DISK_NAME;
41 static uint32_t disk_sector_count;
42 static uint32_t disk_sector_size;
43 
44 /* + 4 to make sure the second buffer is dword-aligned for NVME */
45 static uint8_t scratch_buf[2][SECTOR_COUNT4 * SECTOR_SIZE + 4];
46 
47 
48 /* Sets up test by initializing disk */
test_setup(void)49 static void test_setup(void)
50 {
51 	int rc;
52 	uint32_t cmd_buf;
53 
54 	rc = disk_access_init(disk_pdrv);
55 	zassert_equal(rc, 0, "Disk access initialization failed");
56 
57 	rc = disk_access_status(disk_pdrv);
58 	zassert_equal(rc, DISK_STATUS_OK, "Disk status is not OK");
59 
60 	rc = disk_access_ioctl(disk_pdrv, DISK_IOCTL_GET_SECTOR_COUNT, &cmd_buf);
61 	zassert_equal(rc, 0, "Disk ioctl get sector count failed");
62 
63 	TC_PRINT("Disk reports %u sectors\n", cmd_buf);
64 	disk_sector_count = cmd_buf;
65 
66 	rc = disk_access_ioctl(disk_pdrv, DISK_IOCTL_GET_SECTOR_SIZE, &cmd_buf);
67 	zassert_equal(rc, 0, "Disk ioctl get sector size failed");
68 	TC_PRINT("Disk reports sector size %u\n", cmd_buf);
69 	disk_sector_size = cmd_buf;
70 
71 	/* We could allocate memory once we know the sector size, but instead
72 	 * just verify our assumed maximum size
73 	 */
74 	zassert_true(cmd_buf <= SECTOR_SIZE,
75 		"Test will fail, SECTOR_SIZE definition must be increased");
76 }
77 
78 /* Reads sectors, verifying overflow does not occur */
read_sector(uint8_t * buf,uint32_t start,uint32_t num_sectors)79 static int read_sector(uint8_t *buf, uint32_t start, uint32_t num_sectors)
80 {
81 	int rc;
82 
83 	/* Set up overflow canary */
84 	buf[num_sectors * disk_sector_size] = OVERFLOW_CANARY;
85 	rc = disk_access_read(disk_pdrv, buf, start, num_sectors);
86 	/* Check canary */
87 	zassert_equal(buf[num_sectors * disk_sector_size], OVERFLOW_CANARY,
88 		"Read overflowed requested length");
89 	return rc; /* Let calling function check return code */
90 }
91 
92 /* Tests reading from a variety of sectors */
test_sector_read(uint8_t * buf,uint32_t num_sectors)93 static void test_sector_read(uint8_t *buf, uint32_t num_sectors)
94 {
95 	int rc, sector;
96 
97 	TC_PRINT("Testing reads of %u sectors\n", num_sectors);
98 	/* Read from disk sector 0*/
99 	rc = read_sector(buf, 0, num_sectors);
100 	zassert_equal(rc, 0, "Failed to read from sector zero");
101 	/* Read from a sector in the "middle" of the disk */
102 	if (disk_sector_count / 2 > num_sectors) {
103 		sector = disk_sector_count / 2 - num_sectors;
104 	} else {
105 		sector = 0;
106 	}
107 
108 	rc = read_sector(buf, sector, num_sectors);
109 	zassert_equal(rc, 0, "Failed to read from mid disk sector");
110 	/* Read from the last sector */
111 	rc = read_sector(buf, disk_sector_count - 1, num_sectors);
112 	if (num_sectors == 1) {
113 		zassert_equal(rc, 0, "Failed to read from last sector");
114 	} else {
115 		zassert_not_equal(rc, 0, "Disk should fail to read out of sector bounds");
116 	}
117 }
118 
119 /* Write sector of disk, and check the data to ensure it is valid
120  * WARNING: this test is destructive- it will overwrite data on the disk!
121  */
write_sector_checked(uint8_t * wbuf,uint8_t * rbuf,uint32_t start,uint32_t num_sectors)122 static int write_sector_checked(uint8_t *wbuf, uint8_t *rbuf,
123 			uint32_t start, uint32_t num_sectors)
124 {
125 	int rc, i;
126 
127 	/* First, fill the write buffer with data */
128 	for (i = 0; i < num_sectors * disk_sector_size; i++) {
129 		wbuf[i] = (i & (~num_sectors));
130 	}
131 	/* Now write data to the sector */
132 	rc = disk_access_write(disk_pdrv, wbuf, start, num_sectors);
133 	if (rc) {
134 		return rc; /* Let calling function handle disk error */
135 	}
136 	/* Read back the written data into another buffer */
137 	memset(rbuf, 0, num_sectors * disk_sector_size);
138 	rc = read_sector(rbuf, start, num_sectors);
139 	if (rc) {
140 		return rc;
141 	}
142 	/* Check the read data versus the written data */
143 	zassert_mem_equal(wbuf, rbuf, num_sectors * disk_sector_size,
144 		"Read data did not match data written to disk");
145 	return rc;
146 }
147 
148 /* Tests writing to a variety of sectors
149  * WARNING: this test is destructive- it will overwrite data on the disk!
150  */
test_sector_write(uint8_t * wbuf,uint8_t * rbuf,uint32_t num_sectors)151 static void test_sector_write(uint8_t *wbuf, uint8_t *rbuf, uint32_t num_sectors)
152 {
153 	int rc, sector;
154 
155 	TC_PRINT("Testing writes of %u sectors\n", num_sectors);
156 	/* Write to disk sector zero */
157 	rc = write_sector_checked(wbuf, rbuf, 0, num_sectors);
158 	zassert_equal(rc, 0, "Failed to write to sector zero");
159 	/* Write to a sector in the "middle" of the disk */
160 	if (disk_sector_count / 2 > num_sectors) {
161 		sector = disk_sector_count / 2 - num_sectors;
162 	} else {
163 		sector = 0;
164 	}
165 
166 	rc = write_sector_checked(wbuf, rbuf, sector, num_sectors);
167 	zassert_equal(rc, 0, "Failed to write to mid disk sector");
168 	/* Write to the last sector */
169 	rc = write_sector_checked(wbuf, rbuf, disk_sector_count - 1, num_sectors);
170 	if (num_sectors == 1) {
171 		zassert_equal(rc, 0, "Failed to write to last sector");
172 	} else {
173 		zassert_not_equal(rc, 0, "Disk should fail to write out of sector bounds");
174 	}
175 }
176 
177 /* Test multiple reads in series, and reading from a variety of blocks */
ZTEST(disk_driver,test_read)178 ZTEST(disk_driver, test_read)
179 {
180 	int rc, i;
181 
182 	/* Verify all 4 read sizes work */
183 	test_sector_read(scratch_buf[0], SECTOR_COUNT1);
184 	test_sector_read(scratch_buf[0], SECTOR_COUNT2);
185 	test_sector_read(scratch_buf[0], SECTOR_COUNT3);
186 	test_sector_read(scratch_buf[0], SECTOR_COUNT4);
187 
188 	/* Verify that reading from the same location returns to same data */
189 	memset(scratch_buf[0], 0, SECTOR_COUNT1 * disk_sector_size);
190 	rc = read_sector(scratch_buf[0], 0, SECTOR_COUNT1);
191 	zassert_equal(rc, 0, "Failed to read from disk");
192 	for (i = 0; i < 10; i++) {
193 		/* Read from sector, and compare it to the first read */
194 		memset(scratch_buf[1], 0xff, SECTOR_COUNT1 * disk_sector_size);
195 		rc = read_sector(scratch_buf[1], 0, SECTOR_COUNT1);
196 		zassert_equal(rc, 0, "Failed to read from disk at same sector location");
197 		zassert_mem_equal(scratch_buf[1], scratch_buf[0],
198 				SECTOR_COUNT1 * disk_sector_size,
199 				"Multiple reads mismatch");
200 	}
201 }
202 
203 /* test writing data, and then verifying it was written correctly.
204  * WARNING: this test is destructive- it will overwrite data on the disk!
205  */
ZTEST(disk_driver,test_write)206 ZTEST(disk_driver, test_write)
207 {
208 	int rc, i;
209 
210 	/* Verify all 4 sector write sizes work */
211 	test_sector_write(scratch_buf[0], scratch_buf[1], SECTOR_COUNT1);
212 	test_sector_write(scratch_buf[0], scratch_buf[1], SECTOR_COUNT2);
213 	test_sector_write(scratch_buf[0], scratch_buf[1], SECTOR_COUNT3);
214 	test_sector_write(scratch_buf[0], scratch_buf[1], SECTOR_COUNT4);
215 
216 	/* Verify that multiple writes to the same location work */
217 	for (i = 0; i < 10; i++) {
218 		/* Write to sector- helper function verifies written data is correct */
219 		rc = write_sector_checked(scratch_buf[0], scratch_buf[1], 0, SECTOR_COUNT1);
220 		zassert_equal(rc, 0, "Failed to write to disk at same sector location");
221 	}
222 }
223 
224 
disk_driver_setup(void)225 static void *disk_driver_setup(void)
226 {
227 	test_setup();
228 
229 	return NULL;
230 }
231 
232 ZTEST_SUITE(disk_driver, NULL, disk_driver_setup, NULL, NULL, NULL);
233