xref: /linux/drivers/mtd/tests/pagetest.c (revision 75bf465f0bc33e9b776a46d6a1b9b990f5fb7c37)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (C) 2006-2008 Nokia Corporation
4  *
5  * Test page read and write on MTD device.
6  *
7  * Author: Adrian Hunter <ext-adrian.hunter@nokia.com>
8  */
9 
10 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
11 
12 #include <asm/div64.h>
13 #include <linux/init.h>
14 #include <linux/module.h>
15 #include <linux/moduleparam.h>
16 #include <linux/err.h>
17 #include <linux/mtd/mtd.h>
18 #include <linux/slab.h>
19 #include <linux/sched.h>
20 #include <linux/random.h>
21 
22 #include "mtd_test.h"
23 
24 static int dev = -EINVAL;
25 module_param(dev, int, S_IRUGO);
26 MODULE_PARM_DESC(dev, "MTD device number to use");
27 
28 static struct mtd_info *mtd;
29 static unsigned char *twopages;
30 static unsigned char *writebuf;
31 static unsigned char *boundary;
32 static unsigned char *bbt;
33 
34 static int pgsize;
35 static int bufsize;
36 static int ebcnt;
37 static int pgcnt;
38 static int errcnt;
39 static struct rnd_state rnd_state;
40 
write_eraseblock(int ebnum)41 static int write_eraseblock(int ebnum)
42 {
43 	loff_t addr = (loff_t)ebnum * mtd->erasesize;
44 
45 	prandom_bytes_state(&rnd_state, writebuf, mtd->erasesize);
46 	cond_resched();
47 	return mtdtest_write(mtd, addr, mtd->erasesize, writebuf);
48 }
49 
verify_eraseblock(int ebnum)50 static int verify_eraseblock(int ebnum)
51 {
52 	uint32_t j;
53 	int err = 0, i;
54 	loff_t addr0, addrn;
55 	loff_t addr = (loff_t)ebnum * mtd->erasesize;
56 
57 	addr0 = 0;
58 	for (i = 0; i < ebcnt && bbt[i]; ++i)
59 		addr0 += mtd->erasesize;
60 
61 	addrn = mtd->size;
62 	for (i = 0; i < ebcnt && bbt[ebcnt - i - 1]; ++i)
63 		addrn -= mtd->erasesize;
64 
65 	prandom_bytes_state(&rnd_state, writebuf, mtd->erasesize);
66 	for (j = 0; j < pgcnt - 1; ++j, addr += pgsize) {
67 		/* Do a read to set the internal dataRAMs to different data */
68 		err = mtdtest_read(mtd, addr0, bufsize, twopages);
69 		if (err)
70 			return err;
71 		err = mtdtest_read(mtd, addrn - bufsize, bufsize, twopages);
72 		if (err)
73 			return err;
74 		memset(twopages, 0, bufsize);
75 		err = mtdtest_read(mtd, addr, bufsize, twopages);
76 		if (err)
77 			break;
78 		if (memcmp(twopages, writebuf + (j * pgsize), bufsize)) {
79 			pr_err("error: verify failed at %#llx\n",
80 			       (long long)addr);
81 			errcnt += 1;
82 		}
83 	}
84 	/* Check boundary between eraseblocks */
85 	if (addr <= addrn - pgsize - pgsize && !bbt[ebnum + 1]) {
86 		struct rnd_state old_state = rnd_state;
87 
88 		/* Do a read to set the internal dataRAMs to different data */
89 		err = mtdtest_read(mtd, addr0, bufsize, twopages);
90 		if (err)
91 			return err;
92 		err = mtdtest_read(mtd, addrn - bufsize, bufsize, twopages);
93 		if (err)
94 			return err;
95 		memset(twopages, 0, bufsize);
96 		err = mtdtest_read(mtd, addr, bufsize, twopages);
97 		if (err)
98 			return err;
99 		memcpy(boundary, writebuf + mtd->erasesize - pgsize, pgsize);
100 		prandom_bytes_state(&rnd_state, boundary + pgsize, pgsize);
101 		if (memcmp(twopages, boundary, bufsize)) {
102 			pr_err("error: verify failed at %#llx\n",
103 			       (long long)addr);
104 			errcnt += 1;
105 		}
106 		rnd_state = old_state;
107 	}
108 	return err;
109 }
110 
crosstest(void)111 static int crosstest(void)
112 {
113 	int err = 0, i;
114 	loff_t addr, addr0, addrn;
115 	unsigned char *pp1, *pp2, *pp3, *pp4;
116 
117 	pr_info("crosstest\n");
118 	pp1 = kcalloc(pgsize, 4, GFP_KERNEL);
119 	if (!pp1)
120 		return -ENOMEM;
121 	pp2 = pp1 + pgsize;
122 	pp3 = pp2 + pgsize;
123 	pp4 = pp3 + pgsize;
124 
125 	addr0 = 0;
126 	for (i = 0; i < ebcnt && bbt[i]; ++i)
127 		addr0 += mtd->erasesize;
128 
129 	addrn = mtd->size;
130 	for (i = 0; i < ebcnt && bbt[ebcnt - i - 1]; ++i)
131 		addrn -= mtd->erasesize;
132 
133 	/* Read 2nd-to-last page to pp1 */
134 	addr = addrn - pgsize - pgsize;
135 	err = mtdtest_read(mtd, addr, pgsize, pp1);
136 	if (err) {
137 		kfree(pp1);
138 		return err;
139 	}
140 
141 	/* Read 3rd-to-last page to pp1 */
142 	addr = addrn - pgsize - pgsize - pgsize;
143 	err = mtdtest_read(mtd, addr, pgsize, pp1);
144 	if (err) {
145 		kfree(pp1);
146 		return err;
147 	}
148 
149 	/* Read first page to pp2 */
150 	addr = addr0;
151 	pr_info("reading page at %#llx\n", (long long)addr);
152 	err = mtdtest_read(mtd, addr, pgsize, pp2);
153 	if (err) {
154 		kfree(pp1);
155 		return err;
156 	}
157 
158 	/* Read last page to pp3 */
159 	addr = addrn - pgsize;
160 	pr_info("reading page at %#llx\n", (long long)addr);
161 	err = mtdtest_read(mtd, addr, pgsize, pp3);
162 	if (err) {
163 		kfree(pp1);
164 		return err;
165 	}
166 
167 	/* Read first page again to pp4 */
168 	addr = addr0;
169 	pr_info("reading page at %#llx\n", (long long)addr);
170 	err = mtdtest_read(mtd, addr, pgsize, pp4);
171 	if (err) {
172 		kfree(pp1);
173 		return err;
174 	}
175 
176 	/* pp2 and pp4 should be the same */
177 	pr_info("verifying pages read at %#llx match\n",
178 	       (long long)addr0);
179 	if (memcmp(pp2, pp4, pgsize)) {
180 		pr_err("verify failed!\n");
181 		errcnt += 1;
182 	} else if (!err)
183 		pr_info("crosstest ok\n");
184 	kfree(pp1);
185 	return err;
186 }
187 
erasecrosstest(void)188 static int erasecrosstest(void)
189 {
190 	int err = 0, i, ebnum, ebnum2;
191 	loff_t addr0;
192 	char *readbuf = twopages;
193 
194 	pr_info("erasecrosstest\n");
195 
196 	ebnum = 0;
197 	addr0 = 0;
198 	for (i = 0; i < ebcnt && bbt[i]; ++i) {
199 		addr0 += mtd->erasesize;
200 		ebnum += 1;
201 	}
202 
203 	ebnum2 = ebcnt - 1;
204 	while (ebnum2 && bbt[ebnum2])
205 		ebnum2 -= 1;
206 
207 	pr_info("erasing block %d\n", ebnum);
208 	err = mtdtest_erase_eraseblock(mtd, ebnum);
209 	if (err)
210 		return err;
211 
212 	pr_info("writing 1st page of block %d\n", ebnum);
213 	prandom_bytes_state(&rnd_state, writebuf, pgsize);
214 	strcpy(writebuf, "There is no data like this!");
215 	err = mtdtest_write(mtd, addr0, pgsize, writebuf);
216 	if (err)
217 		return err;
218 
219 	pr_info("reading 1st page of block %d\n", ebnum);
220 	memset(readbuf, 0, pgsize);
221 	err = mtdtest_read(mtd, addr0, pgsize, readbuf);
222 	if (err)
223 		return err;
224 
225 	pr_info("verifying 1st page of block %d\n", ebnum);
226 	if (memcmp(writebuf, readbuf, pgsize)) {
227 		pr_err("verify failed!\n");
228 		errcnt += 1;
229 		return -1;
230 	}
231 
232 	pr_info("erasing block %d\n", ebnum);
233 	err = mtdtest_erase_eraseblock(mtd, ebnum);
234 	if (err)
235 		return err;
236 
237 	pr_info("writing 1st page of block %d\n", ebnum);
238 	prandom_bytes_state(&rnd_state, writebuf, pgsize);
239 	strcpy(writebuf, "There is no data like this!");
240 	err = mtdtest_write(mtd, addr0, pgsize, writebuf);
241 	if (err)
242 		return err;
243 
244 	pr_info("erasing block %d\n", ebnum2);
245 	err = mtdtest_erase_eraseblock(mtd, ebnum2);
246 	if (err)
247 		return err;
248 
249 	pr_info("reading 1st page of block %d\n", ebnum);
250 	memset(readbuf, 0, pgsize);
251 	err = mtdtest_read(mtd, addr0, pgsize, readbuf);
252 	if (err)
253 		return err;
254 
255 	pr_info("verifying 1st page of block %d\n", ebnum);
256 	if (memcmp(writebuf, readbuf, pgsize)) {
257 		pr_err("verify failed!\n");
258 		errcnt += 1;
259 		return -1;
260 	}
261 
262 	if (!err)
263 		pr_info("erasecrosstest ok\n");
264 	return err;
265 }
266 
erasetest(void)267 static int erasetest(void)
268 {
269 	int err = 0, i, ebnum, ok = 1;
270 	loff_t addr0;
271 
272 	pr_info("erasetest\n");
273 
274 	ebnum = 0;
275 	addr0 = 0;
276 	for (i = 0; i < ebcnt && bbt[i]; ++i) {
277 		addr0 += mtd->erasesize;
278 		ebnum += 1;
279 	}
280 
281 	pr_info("erasing block %d\n", ebnum);
282 	err = mtdtest_erase_eraseblock(mtd, ebnum);
283 	if (err)
284 		return err;
285 
286 	pr_info("writing 1st page of block %d\n", ebnum);
287 	prandom_bytes_state(&rnd_state, writebuf, pgsize);
288 	err = mtdtest_write(mtd, addr0, pgsize, writebuf);
289 	if (err)
290 		return err;
291 
292 	pr_info("erasing block %d\n", ebnum);
293 	err = mtdtest_erase_eraseblock(mtd, ebnum);
294 	if (err)
295 		return err;
296 
297 	pr_info("reading 1st page of block %d\n", ebnum);
298 	err = mtdtest_read(mtd, addr0, pgsize, twopages);
299 	if (err)
300 		return err;
301 
302 	pr_info("verifying 1st page of block %d is all 0xff\n",
303 	       ebnum);
304 	for (i = 0; i < pgsize; ++i)
305 		if (twopages[i] != 0xff) {
306 			pr_err("verifying all 0xff failed at %d\n",
307 			       i);
308 			errcnt += 1;
309 			ok = 0;
310 			break;
311 		}
312 
313 	if (ok && !err)
314 		pr_info("erasetest ok\n");
315 
316 	return err;
317 }
318 
mtd_pagetest_init(void)319 static int __init mtd_pagetest_init(void)
320 {
321 	int err = 0;
322 	uint64_t tmp;
323 	uint32_t i;
324 
325 	printk(KERN_INFO "\n");
326 	printk(KERN_INFO "=================================================\n");
327 
328 	if (dev < 0) {
329 		pr_info("Please specify a valid mtd-device via module parameter\n");
330 		pr_crit("CAREFUL: This test wipes all data on the specified MTD device!\n");
331 		return -EINVAL;
332 	}
333 
334 	pr_info("MTD device: %d\n", dev);
335 
336 	mtd = get_mtd_device(NULL, dev);
337 	if (IS_ERR(mtd)) {
338 		err = PTR_ERR(mtd);
339 		pr_err("error: cannot get MTD device\n");
340 		return err;
341 	}
342 
343 	if (!mtd_type_is_nand(mtd)) {
344 		pr_info("this test requires NAND flash\n");
345 		goto out;
346 	}
347 
348 	tmp = mtd->size;
349 	do_div(tmp, mtd->erasesize);
350 	ebcnt = tmp;
351 	pgcnt = mtd->erasesize / mtd->writesize;
352 	pgsize = mtd->writesize;
353 
354 	pr_info("MTD device size %llu, eraseblock size %u, "
355 	       "page size %u, count of eraseblocks %u, pages per "
356 	       "eraseblock %u, OOB size %u\n",
357 	       (unsigned long long)mtd->size, mtd->erasesize,
358 	       pgsize, ebcnt, pgcnt, mtd->oobsize);
359 
360 	err = -ENOMEM;
361 	bufsize = pgsize * 2;
362 	writebuf = kmalloc(mtd->erasesize, GFP_KERNEL);
363 	if (!writebuf)
364 		goto out;
365 	twopages = kmalloc(bufsize, GFP_KERNEL);
366 	if (!twopages)
367 		goto out;
368 	boundary = kmalloc(bufsize, GFP_KERNEL);
369 	if (!boundary)
370 		goto out;
371 
372 	bbt = kzalloc(ebcnt, GFP_KERNEL);
373 	if (!bbt)
374 		goto out;
375 	err = mtdtest_scan_for_bad_eraseblocks(mtd, bbt, 0, ebcnt);
376 	if (err)
377 		goto out;
378 
379 	/* Erase all eraseblocks */
380 	pr_info("erasing whole device\n");
381 	err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt);
382 	if (err)
383 		goto out;
384 	pr_info("erased %u eraseblocks\n", ebcnt);
385 
386 	/* Write all eraseblocks */
387 	prandom_seed_state(&rnd_state, 1);
388 	pr_info("writing whole device\n");
389 	for (i = 0; i < ebcnt; ++i) {
390 		if (bbt[i])
391 			continue;
392 		err = write_eraseblock(i);
393 		if (err)
394 			goto out;
395 		if (i % 256 == 0)
396 			pr_info("written up to eraseblock %u\n", i);
397 
398 		err = mtdtest_relax();
399 		if (err)
400 			goto out;
401 	}
402 	pr_info("written %u eraseblocks\n", i);
403 
404 	/* Check all eraseblocks */
405 	prandom_seed_state(&rnd_state, 1);
406 	pr_info("verifying all eraseblocks\n");
407 	for (i = 0; i < ebcnt; ++i) {
408 		if (bbt[i])
409 			continue;
410 		err = verify_eraseblock(i);
411 		if (err)
412 			goto out;
413 		if (i % 256 == 0)
414 			pr_info("verified up to eraseblock %u\n", i);
415 
416 		err = mtdtest_relax();
417 		if (err)
418 			goto out;
419 	}
420 	pr_info("verified %u eraseblocks\n", i);
421 
422 	err = crosstest();
423 	if (err)
424 		goto out;
425 
426 	if (ebcnt > 1) {
427 		err = erasecrosstest();
428 		if (err)
429 			goto out;
430 	} else {
431 		pr_info("skipping erasecrosstest, 2 erase blocks needed\n");
432 	}
433 
434 	err = erasetest();
435 	if (err)
436 		goto out;
437 
438 	pr_info("finished with %d errors\n", errcnt);
439 out:
440 
441 	kfree(bbt);
442 	kfree(boundary);
443 	kfree(twopages);
444 	kfree(writebuf);
445 	put_mtd_device(mtd);
446 	if (err)
447 		pr_info("error %d occurred\n", err);
448 	printk(KERN_INFO "=================================================\n");
449 	return err;
450 }
451 module_init(mtd_pagetest_init);
452 
mtd_pagetest_exit(void)453 static void __exit mtd_pagetest_exit(void)
454 {
455 	return;
456 }
457 module_exit(mtd_pagetest_exit);
458 
459 MODULE_DESCRIPTION("NAND page test");
460 MODULE_AUTHOR("Adrian Hunter");
461 MODULE_LICENSE("GPL");
462