xref: /linux/drivers/mtd/tests/oobtest.c (revision 3932b9ca55b0be314a36d3e84faff3e823c081f5)
1 /*
2  * Copyright (C) 2006-2008 Nokia Corporation
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms of the GNU General Public License version 2 as published by
6  * the Free Software Foundation.
7  *
8  * This program is distributed in the hope that it will be useful, but WITHOUT
9  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
11  * more details.
12  *
13  * You should have received a copy of the GNU General Public License along with
14  * this program; see the file COPYING. If not, write to the Free Software
15  * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
16  *
17  * Test OOB read and write on MTD device.
18  *
19  * Author: Adrian Hunter <ext-adrian.hunter@nokia.com>
20  */
21 
22 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
23 
24 #include <asm/div64.h>
25 #include <linux/init.h>
26 #include <linux/module.h>
27 #include <linux/moduleparam.h>
28 #include <linux/err.h>
29 #include <linux/mtd/mtd.h>
30 #include <linux/slab.h>
31 #include <linux/sched.h>
32 #include <linux/random.h>
33 
34 #include "mtd_test.h"
35 
36 static int dev = -EINVAL;
37 module_param(dev, int, S_IRUGO);
38 MODULE_PARM_DESC(dev, "MTD device number to use");
39 
40 static struct mtd_info *mtd;
41 static unsigned char *readbuf;
42 static unsigned char *writebuf;
43 static unsigned char *bbt;
44 
45 static int ebcnt;
46 static int pgcnt;
47 static int errcnt;
48 static int use_offset;
49 static int use_len;
50 static int use_len_max;
51 static int vary_offset;
52 static struct rnd_state rnd_state;
53 
54 static void do_vary_offset(void)
55 {
56 	use_len -= 1;
57 	if (use_len < 1) {
58 		use_offset += 1;
59 		if (use_offset >= use_len_max)
60 			use_offset = 0;
61 		use_len = use_len_max - use_offset;
62 	}
63 }
64 
65 static int write_eraseblock(int ebnum)
66 {
67 	int i;
68 	struct mtd_oob_ops ops;
69 	int err = 0;
70 	loff_t addr = ebnum * mtd->erasesize;
71 
72 	prandom_bytes_state(&rnd_state, writebuf, use_len_max * pgcnt);
73 	for (i = 0; i < pgcnt; ++i, addr += mtd->writesize) {
74 		ops.mode      = MTD_OPS_AUTO_OOB;
75 		ops.len       = 0;
76 		ops.retlen    = 0;
77 		ops.ooblen    = use_len;
78 		ops.oobretlen = 0;
79 		ops.ooboffs   = use_offset;
80 		ops.datbuf    = NULL;
81 		ops.oobbuf    = writebuf + (use_len_max * i) + use_offset;
82 		err = mtd_write_oob(mtd, addr, &ops);
83 		if (err || ops.oobretlen != use_len) {
84 			pr_err("error: writeoob failed at %#llx\n",
85 			       (long long)addr);
86 			pr_err("error: use_len %d, use_offset %d\n",
87 			       use_len, use_offset);
88 			errcnt += 1;
89 			return err ? err : -1;
90 		}
91 		if (vary_offset)
92 			do_vary_offset();
93 	}
94 
95 	return err;
96 }
97 
98 static int write_whole_device(void)
99 {
100 	int err;
101 	unsigned int i;
102 
103 	pr_info("writing OOBs of whole device\n");
104 	for (i = 0; i < ebcnt; ++i) {
105 		if (bbt[i])
106 			continue;
107 		err = write_eraseblock(i);
108 		if (err)
109 			return err;
110 		if (i % 256 == 0)
111 			pr_info("written up to eraseblock %u\n", i);
112 		cond_resched();
113 	}
114 	pr_info("written %u eraseblocks\n", i);
115 	return 0;
116 }
117 
118 static int verify_eraseblock(int ebnum)
119 {
120 	int i;
121 	struct mtd_oob_ops ops;
122 	int err = 0;
123 	loff_t addr = ebnum * mtd->erasesize;
124 
125 	prandom_bytes_state(&rnd_state, writebuf, use_len_max * pgcnt);
126 	for (i = 0; i < pgcnt; ++i, addr += mtd->writesize) {
127 		ops.mode      = MTD_OPS_AUTO_OOB;
128 		ops.len       = 0;
129 		ops.retlen    = 0;
130 		ops.ooblen    = use_len;
131 		ops.oobretlen = 0;
132 		ops.ooboffs   = use_offset;
133 		ops.datbuf    = NULL;
134 		ops.oobbuf    = readbuf;
135 		err = mtd_read_oob(mtd, addr, &ops);
136 		if (err || ops.oobretlen != use_len) {
137 			pr_err("error: readoob failed at %#llx\n",
138 			       (long long)addr);
139 			errcnt += 1;
140 			return err ? err : -1;
141 		}
142 		if (memcmp(readbuf, writebuf + (use_len_max * i) + use_offset,
143 			   use_len)) {
144 			pr_err("error: verify failed at %#llx\n",
145 			       (long long)addr);
146 			errcnt += 1;
147 			if (errcnt > 1000) {
148 				pr_err("error: too many errors\n");
149 				return -1;
150 			}
151 		}
152 		if (use_offset != 0 || use_len < mtd->ecclayout->oobavail) {
153 			int k;
154 
155 			ops.mode      = MTD_OPS_AUTO_OOB;
156 			ops.len       = 0;
157 			ops.retlen    = 0;
158 			ops.ooblen    = mtd->ecclayout->oobavail;
159 			ops.oobretlen = 0;
160 			ops.ooboffs   = 0;
161 			ops.datbuf    = NULL;
162 			ops.oobbuf    = readbuf;
163 			err = mtd_read_oob(mtd, addr, &ops);
164 			if (err || ops.oobretlen != mtd->ecclayout->oobavail) {
165 				pr_err("error: readoob failed at %#llx\n",
166 						(long long)addr);
167 				errcnt += 1;
168 				return err ? err : -1;
169 			}
170 			if (memcmp(readbuf + use_offset,
171 				   writebuf + (use_len_max * i) + use_offset,
172 				   use_len)) {
173 				pr_err("error: verify failed at %#llx\n",
174 						(long long)addr);
175 				errcnt += 1;
176 				if (errcnt > 1000) {
177 					pr_err("error: too many errors\n");
178 					return -1;
179 				}
180 			}
181 			for (k = 0; k < use_offset; ++k)
182 				if (readbuf[k] != 0xff) {
183 					pr_err("error: verify 0xff "
184 					       "failed at %#llx\n",
185 					       (long long)addr);
186 					errcnt += 1;
187 					if (errcnt > 1000) {
188 						pr_err("error: too "
189 						       "many errors\n");
190 						return -1;
191 					}
192 				}
193 			for (k = use_offset + use_len;
194 			     k < mtd->ecclayout->oobavail; ++k)
195 				if (readbuf[k] != 0xff) {
196 					pr_err("error: verify 0xff "
197 					       "failed at %#llx\n",
198 					       (long long)addr);
199 					errcnt += 1;
200 					if (errcnt > 1000) {
201 						pr_err("error: too "
202 						       "many errors\n");
203 						return -1;
204 					}
205 				}
206 		}
207 		if (vary_offset)
208 			do_vary_offset();
209 	}
210 	return err;
211 }
212 
213 static int verify_eraseblock_in_one_go(int ebnum)
214 {
215 	struct mtd_oob_ops ops;
216 	int err = 0;
217 	loff_t addr = ebnum * mtd->erasesize;
218 	size_t len = mtd->ecclayout->oobavail * pgcnt;
219 
220 	prandom_bytes_state(&rnd_state, writebuf, len);
221 	ops.mode      = MTD_OPS_AUTO_OOB;
222 	ops.len       = 0;
223 	ops.retlen    = 0;
224 	ops.ooblen    = len;
225 	ops.oobretlen = 0;
226 	ops.ooboffs   = 0;
227 	ops.datbuf    = NULL;
228 	ops.oobbuf    = readbuf;
229 	err = mtd_read_oob(mtd, addr, &ops);
230 	if (err || ops.oobretlen != len) {
231 		pr_err("error: readoob failed at %#llx\n",
232 		       (long long)addr);
233 		errcnt += 1;
234 		return err ? err : -1;
235 	}
236 	if (memcmp(readbuf, writebuf, len)) {
237 		pr_err("error: verify failed at %#llx\n",
238 		       (long long)addr);
239 		errcnt += 1;
240 		if (errcnt > 1000) {
241 			pr_err("error: too many errors\n");
242 			return -1;
243 		}
244 	}
245 
246 	return err;
247 }
248 
249 static int verify_all_eraseblocks(void)
250 {
251 	int err;
252 	unsigned int i;
253 
254 	pr_info("verifying all eraseblocks\n");
255 	for (i = 0; i < ebcnt; ++i) {
256 		if (bbt[i])
257 			continue;
258 		err = verify_eraseblock(i);
259 		if (err)
260 			return err;
261 		if (i % 256 == 0)
262 			pr_info("verified up to eraseblock %u\n", i);
263 		cond_resched();
264 	}
265 	pr_info("verified %u eraseblocks\n", i);
266 	return 0;
267 }
268 
269 static int __init mtd_oobtest_init(void)
270 {
271 	int err = 0;
272 	unsigned int i;
273 	uint64_t tmp;
274 	struct mtd_oob_ops ops;
275 	loff_t addr = 0, addr0;
276 
277 	printk(KERN_INFO "\n");
278 	printk(KERN_INFO "=================================================\n");
279 
280 	if (dev < 0) {
281 		pr_info("Please specify a valid mtd-device via module parameter\n");
282 		pr_crit("CAREFUL: This test wipes all data on the specified MTD device!\n");
283 		return -EINVAL;
284 	}
285 
286 	pr_info("MTD device: %d\n", dev);
287 
288 	mtd = get_mtd_device(NULL, dev);
289 	if (IS_ERR(mtd)) {
290 		err = PTR_ERR(mtd);
291 		pr_err("error: cannot get MTD device\n");
292 		return err;
293 	}
294 
295 	if (!mtd_type_is_nand(mtd)) {
296 		pr_info("this test requires NAND flash\n");
297 		goto out;
298 	}
299 
300 	tmp = mtd->size;
301 	do_div(tmp, mtd->erasesize);
302 	ebcnt = tmp;
303 	pgcnt = mtd->erasesize / mtd->writesize;
304 
305 	pr_info("MTD device size %llu, eraseblock size %u, "
306 	       "page size %u, count of eraseblocks %u, pages per "
307 	       "eraseblock %u, OOB size %u\n",
308 	       (unsigned long long)mtd->size, mtd->erasesize,
309 	       mtd->writesize, ebcnt, pgcnt, mtd->oobsize);
310 
311 	err = -ENOMEM;
312 	readbuf = kmalloc(mtd->erasesize, GFP_KERNEL);
313 	if (!readbuf)
314 		goto out;
315 	writebuf = kmalloc(mtd->erasesize, GFP_KERNEL);
316 	if (!writebuf)
317 		goto out;
318 	bbt = kzalloc(ebcnt, GFP_KERNEL);
319 	if (!bbt)
320 		goto out;
321 
322 	err = mtdtest_scan_for_bad_eraseblocks(mtd, bbt, 0, ebcnt);
323 	if (err)
324 		goto out;
325 
326 	use_offset = 0;
327 	use_len = mtd->ecclayout->oobavail;
328 	use_len_max = mtd->ecclayout->oobavail;
329 	vary_offset = 0;
330 
331 	/* First test: write all OOB, read it back and verify */
332 	pr_info("test 1 of 5\n");
333 
334 	err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt);
335 	if (err)
336 		goto out;
337 
338 	prandom_seed_state(&rnd_state, 1);
339 	err = write_whole_device();
340 	if (err)
341 		goto out;
342 
343 	prandom_seed_state(&rnd_state, 1);
344 	err = verify_all_eraseblocks();
345 	if (err)
346 		goto out;
347 
348 	/*
349 	 * Second test: write all OOB, a block at a time, read it back and
350 	 * verify.
351 	 */
352 	pr_info("test 2 of 5\n");
353 
354 	err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt);
355 	if (err)
356 		goto out;
357 
358 	prandom_seed_state(&rnd_state, 3);
359 	err = write_whole_device();
360 	if (err)
361 		goto out;
362 
363 	/* Check all eraseblocks */
364 	prandom_seed_state(&rnd_state, 3);
365 	pr_info("verifying all eraseblocks\n");
366 	for (i = 0; i < ebcnt; ++i) {
367 		if (bbt[i])
368 			continue;
369 		err = verify_eraseblock_in_one_go(i);
370 		if (err)
371 			goto out;
372 		if (i % 256 == 0)
373 			pr_info("verified up to eraseblock %u\n", i);
374 		cond_resched();
375 	}
376 	pr_info("verified %u eraseblocks\n", i);
377 
378 	/*
379 	 * Third test: write OOB at varying offsets and lengths, read it back
380 	 * and verify.
381 	 */
382 	pr_info("test 3 of 5\n");
383 
384 	err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt);
385 	if (err)
386 		goto out;
387 
388 	/* Write all eraseblocks */
389 	use_offset = 0;
390 	use_len = mtd->ecclayout->oobavail;
391 	use_len_max = mtd->ecclayout->oobavail;
392 	vary_offset = 1;
393 	prandom_seed_state(&rnd_state, 5);
394 
395 	err = write_whole_device();
396 	if (err)
397 		goto out;
398 
399 	/* Check all eraseblocks */
400 	use_offset = 0;
401 	use_len = mtd->ecclayout->oobavail;
402 	use_len_max = mtd->ecclayout->oobavail;
403 	vary_offset = 1;
404 	prandom_seed_state(&rnd_state, 5);
405 	err = verify_all_eraseblocks();
406 	if (err)
407 		goto out;
408 
409 	use_offset = 0;
410 	use_len = mtd->ecclayout->oobavail;
411 	use_len_max = mtd->ecclayout->oobavail;
412 	vary_offset = 0;
413 
414 	/* Fourth test: try to write off end of device */
415 	pr_info("test 4 of 5\n");
416 
417 	err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt);
418 	if (err)
419 		goto out;
420 
421 	addr0 = 0;
422 	for (i = 0; i < ebcnt && bbt[i]; ++i)
423 		addr0 += mtd->erasesize;
424 
425 	/* Attempt to write off end of OOB */
426 	ops.mode      = MTD_OPS_AUTO_OOB;
427 	ops.len       = 0;
428 	ops.retlen    = 0;
429 	ops.ooblen    = 1;
430 	ops.oobretlen = 0;
431 	ops.ooboffs   = mtd->ecclayout->oobavail;
432 	ops.datbuf    = NULL;
433 	ops.oobbuf    = writebuf;
434 	pr_info("attempting to start write past end of OOB\n");
435 	pr_info("an error is expected...\n");
436 	err = mtd_write_oob(mtd, addr0, &ops);
437 	if (err) {
438 		pr_info("error occurred as expected\n");
439 		err = 0;
440 	} else {
441 		pr_err("error: can write past end of OOB\n");
442 		errcnt += 1;
443 	}
444 
445 	/* Attempt to read off end of OOB */
446 	ops.mode      = MTD_OPS_AUTO_OOB;
447 	ops.len       = 0;
448 	ops.retlen    = 0;
449 	ops.ooblen    = 1;
450 	ops.oobretlen = 0;
451 	ops.ooboffs   = mtd->ecclayout->oobavail;
452 	ops.datbuf    = NULL;
453 	ops.oobbuf    = readbuf;
454 	pr_info("attempting to start read past end of OOB\n");
455 	pr_info("an error is expected...\n");
456 	err = mtd_read_oob(mtd, addr0, &ops);
457 	if (err) {
458 		pr_info("error occurred as expected\n");
459 		err = 0;
460 	} else {
461 		pr_err("error: can read past end of OOB\n");
462 		errcnt += 1;
463 	}
464 
465 	if (bbt[ebcnt - 1])
466 		pr_info("skipping end of device tests because last "
467 		       "block is bad\n");
468 	else {
469 		/* Attempt to write off end of device */
470 		ops.mode      = MTD_OPS_AUTO_OOB;
471 		ops.len       = 0;
472 		ops.retlen    = 0;
473 		ops.ooblen    = mtd->ecclayout->oobavail + 1;
474 		ops.oobretlen = 0;
475 		ops.ooboffs   = 0;
476 		ops.datbuf    = NULL;
477 		ops.oobbuf    = writebuf;
478 		pr_info("attempting to write past end of device\n");
479 		pr_info("an error is expected...\n");
480 		err = mtd_write_oob(mtd, mtd->size - mtd->writesize, &ops);
481 		if (err) {
482 			pr_info("error occurred as expected\n");
483 			err = 0;
484 		} else {
485 			pr_err("error: wrote past end of device\n");
486 			errcnt += 1;
487 		}
488 
489 		/* Attempt to read off end of device */
490 		ops.mode      = MTD_OPS_AUTO_OOB;
491 		ops.len       = 0;
492 		ops.retlen    = 0;
493 		ops.ooblen    = mtd->ecclayout->oobavail + 1;
494 		ops.oobretlen = 0;
495 		ops.ooboffs   = 0;
496 		ops.datbuf    = NULL;
497 		ops.oobbuf    = readbuf;
498 		pr_info("attempting to read past end of device\n");
499 		pr_info("an error is expected...\n");
500 		err = mtd_read_oob(mtd, mtd->size - mtd->writesize, &ops);
501 		if (err) {
502 			pr_info("error occurred as expected\n");
503 			err = 0;
504 		} else {
505 			pr_err("error: read past end of device\n");
506 			errcnt += 1;
507 		}
508 
509 		err = mtdtest_erase_eraseblock(mtd, ebcnt - 1);
510 		if (err)
511 			goto out;
512 
513 		/* Attempt to write off end of device */
514 		ops.mode      = MTD_OPS_AUTO_OOB;
515 		ops.len       = 0;
516 		ops.retlen    = 0;
517 		ops.ooblen    = mtd->ecclayout->oobavail;
518 		ops.oobretlen = 0;
519 		ops.ooboffs   = 1;
520 		ops.datbuf    = NULL;
521 		ops.oobbuf    = writebuf;
522 		pr_info("attempting to write past end of device\n");
523 		pr_info("an error is expected...\n");
524 		err = mtd_write_oob(mtd, mtd->size - mtd->writesize, &ops);
525 		if (err) {
526 			pr_info("error occurred as expected\n");
527 			err = 0;
528 		} else {
529 			pr_err("error: wrote past end of device\n");
530 			errcnt += 1;
531 		}
532 
533 		/* Attempt to read off end of device */
534 		ops.mode      = MTD_OPS_AUTO_OOB;
535 		ops.len       = 0;
536 		ops.retlen    = 0;
537 		ops.ooblen    = mtd->ecclayout->oobavail;
538 		ops.oobretlen = 0;
539 		ops.ooboffs   = 1;
540 		ops.datbuf    = NULL;
541 		ops.oobbuf    = readbuf;
542 		pr_info("attempting to read past end of device\n");
543 		pr_info("an error is expected...\n");
544 		err = mtd_read_oob(mtd, mtd->size - mtd->writesize, &ops);
545 		if (err) {
546 			pr_info("error occurred as expected\n");
547 			err = 0;
548 		} else {
549 			pr_err("error: read past end of device\n");
550 			errcnt += 1;
551 		}
552 	}
553 
554 	/* Fifth test: write / read across block boundaries */
555 	pr_info("test 5 of 5\n");
556 
557 	/* Erase all eraseblocks */
558 	err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt);
559 	if (err)
560 		goto out;
561 
562 	/* Write all eraseblocks */
563 	prandom_seed_state(&rnd_state, 11);
564 	pr_info("writing OOBs of whole device\n");
565 	for (i = 0; i < ebcnt - 1; ++i) {
566 		int cnt = 2;
567 		int pg;
568 		size_t sz = mtd->ecclayout->oobavail;
569 		if (bbt[i] || bbt[i + 1])
570 			continue;
571 		addr = (i + 1) * mtd->erasesize - mtd->writesize;
572 		prandom_bytes_state(&rnd_state, writebuf, sz * cnt);
573 		for (pg = 0; pg < cnt; ++pg) {
574 			ops.mode      = MTD_OPS_AUTO_OOB;
575 			ops.len       = 0;
576 			ops.retlen    = 0;
577 			ops.ooblen    = sz;
578 			ops.oobretlen = 0;
579 			ops.ooboffs   = 0;
580 			ops.datbuf    = NULL;
581 			ops.oobbuf    = writebuf + pg * sz;
582 			err = mtd_write_oob(mtd, addr, &ops);
583 			if (err)
584 				goto out;
585 			if (i % 256 == 0)
586 				pr_info("written up to eraseblock %u\n", i);
587 			cond_resched();
588 			addr += mtd->writesize;
589 		}
590 	}
591 	pr_info("written %u eraseblocks\n", i);
592 
593 	/* Check all eraseblocks */
594 	prandom_seed_state(&rnd_state, 11);
595 	pr_info("verifying all eraseblocks\n");
596 	for (i = 0; i < ebcnt - 1; ++i) {
597 		if (bbt[i] || bbt[i + 1])
598 			continue;
599 		prandom_bytes_state(&rnd_state, writebuf,
600 					mtd->ecclayout->oobavail * 2);
601 		addr = (i + 1) * mtd->erasesize - mtd->writesize;
602 		ops.mode      = MTD_OPS_AUTO_OOB;
603 		ops.len       = 0;
604 		ops.retlen    = 0;
605 		ops.ooblen    = mtd->ecclayout->oobavail * 2;
606 		ops.oobretlen = 0;
607 		ops.ooboffs   = 0;
608 		ops.datbuf    = NULL;
609 		ops.oobbuf    = readbuf;
610 		err = mtd_read_oob(mtd, addr, &ops);
611 		if (err)
612 			goto out;
613 		if (memcmp(readbuf, writebuf, mtd->ecclayout->oobavail * 2)) {
614 			pr_err("error: verify failed at %#llx\n",
615 			       (long long)addr);
616 			errcnt += 1;
617 			if (errcnt > 1000) {
618 				pr_err("error: too many errors\n");
619 				goto out;
620 			}
621 		}
622 		if (i % 256 == 0)
623 			pr_info("verified up to eraseblock %u\n", i);
624 		cond_resched();
625 	}
626 	pr_info("verified %u eraseblocks\n", i);
627 
628 	pr_info("finished with %d errors\n", errcnt);
629 out:
630 	kfree(bbt);
631 	kfree(writebuf);
632 	kfree(readbuf);
633 	put_mtd_device(mtd);
634 	if (err)
635 		pr_info("error %d occurred\n", err);
636 	printk(KERN_INFO "=================================================\n");
637 	return err;
638 }
639 module_init(mtd_oobtest_init);
640 
641 static void __exit mtd_oobtest_exit(void)
642 {
643 	return;
644 }
645 module_exit(mtd_oobtest_exit);
646 
647 MODULE_DESCRIPTION("Out-of-band test module");
648 MODULE_AUTHOR("Adrian Hunter");
649 MODULE_LICENSE("GPL");
650