• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2006-2007 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 sub-page read and write on MTD device.
18  * Author: Adrian Hunter <ext-adrian.hunter@nokia.com>
19  *
20  */
21 
22 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
23 
24 #include <linux/init.h>
25 #include <linux/module.h>
26 #include <linux/moduleparam.h>
27 #include <linux/err.h>
28 #include <linux/mtd/mtd.h>
29 #include <linux/slab.h>
30 #include <linux/sched.h>
31 #include <linux/random.h>
32 
33 static int dev = -EINVAL;
34 module_param(dev, int, S_IRUGO);
35 MODULE_PARM_DESC(dev, "MTD device number to use");
36 
37 static struct mtd_info *mtd;
38 static unsigned char *writebuf;
39 static unsigned char *readbuf;
40 static unsigned char *bbt;
41 
42 static int subpgsize;
43 static int bufsize;
44 static int ebcnt;
45 static int pgcnt;
46 static int errcnt;
47 static struct rnd_state rnd_state;
48 
clear_data(unsigned char * buf,size_t len)49 static inline void clear_data(unsigned char *buf, size_t len)
50 {
51 	memset(buf, 0, len);
52 }
53 
erase_eraseblock(int ebnum)54 static int erase_eraseblock(int ebnum)
55 {
56 	int err;
57 	struct erase_info ei;
58 	loff_t addr = ebnum * mtd->erasesize;
59 
60 	memset(&ei, 0, sizeof(struct erase_info));
61 	ei.mtd  = mtd;
62 	ei.addr = addr;
63 	ei.len  = mtd->erasesize;
64 
65 	err = mtd_erase(mtd, &ei);
66 	if (err) {
67 		pr_err("error %d while erasing EB %d\n", err, ebnum);
68 		return err;
69 	}
70 
71 	if (ei.state == MTD_ERASE_FAILED) {
72 		pr_err("some erase error occurred at EB %d\n",
73 		       ebnum);
74 		return -EIO;
75 	}
76 
77 	return 0;
78 }
79 
erase_whole_device(void)80 static int erase_whole_device(void)
81 {
82 	int err;
83 	unsigned int i;
84 
85 	pr_info("erasing whole device\n");
86 	for (i = 0; i < ebcnt; ++i) {
87 		if (bbt[i])
88 			continue;
89 		err = erase_eraseblock(i);
90 		if (err)
91 			return err;
92 		cond_resched();
93 	}
94 	pr_info("erased %u eraseblocks\n", i);
95 	return 0;
96 }
97 
write_eraseblock(int ebnum)98 static int write_eraseblock(int ebnum)
99 {
100 	size_t written;
101 	int err = 0;
102 	loff_t addr = ebnum * mtd->erasesize;
103 
104 	prandom_bytes_state(&rnd_state, writebuf, subpgsize);
105 	err = mtd_write(mtd, addr, subpgsize, &written, writebuf);
106 	if (unlikely(err || written != subpgsize)) {
107 		pr_err("error: write failed at %#llx\n",
108 		       (long long)addr);
109 		if (written != subpgsize) {
110 			pr_err("  write size: %#x\n", subpgsize);
111 			pr_err("  written: %#zx\n", written);
112 		}
113 		return err ? err : -1;
114 	}
115 
116 	addr += subpgsize;
117 
118 	prandom_bytes_state(&rnd_state, writebuf, subpgsize);
119 	err = mtd_write(mtd, addr, subpgsize, &written, writebuf);
120 	if (unlikely(err || written != subpgsize)) {
121 		pr_err("error: write failed at %#llx\n",
122 		       (long long)addr);
123 		if (written != subpgsize) {
124 			pr_err("  write size: %#x\n", subpgsize);
125 			pr_err("  written: %#zx\n", written);
126 		}
127 		return err ? err : -1;
128 	}
129 
130 	return err;
131 }
132 
write_eraseblock2(int ebnum)133 static int write_eraseblock2(int ebnum)
134 {
135 	size_t written;
136 	int err = 0, k;
137 	loff_t addr = ebnum * mtd->erasesize;
138 
139 	for (k = 1; k < 33; ++k) {
140 		if (addr + (subpgsize * k) > (ebnum + 1) * mtd->erasesize)
141 			break;
142 		prandom_bytes_state(&rnd_state, writebuf, subpgsize * k);
143 		err = mtd_write(mtd, addr, subpgsize * k, &written, writebuf);
144 		if (unlikely(err || written != subpgsize * k)) {
145 			pr_err("error: write failed at %#llx\n",
146 			       (long long)addr);
147 			if (written != subpgsize) {
148 				pr_err("  write size: %#x\n",
149 				       subpgsize * k);
150 				pr_err("  written: %#08zx\n",
151 				       written);
152 			}
153 			return err ? err : -1;
154 		}
155 		addr += subpgsize * k;
156 	}
157 
158 	return err;
159 }
160 
print_subpage(unsigned char * p)161 static void print_subpage(unsigned char *p)
162 {
163 	int i, j;
164 
165 	for (i = 0; i < subpgsize; ) {
166 		for (j = 0; i < subpgsize && j < 32; ++i, ++j)
167 			printk("%02x", *p++);
168 		printk("\n");
169 	}
170 }
171 
verify_eraseblock(int ebnum)172 static int verify_eraseblock(int ebnum)
173 {
174 	size_t read;
175 	int err = 0;
176 	loff_t addr = ebnum * mtd->erasesize;
177 
178 	prandom_bytes_state(&rnd_state, writebuf, subpgsize);
179 	clear_data(readbuf, subpgsize);
180 	err = mtd_read(mtd, addr, subpgsize, &read, readbuf);
181 	if (unlikely(err || read != subpgsize)) {
182 		if (mtd_is_bitflip(err) && read == subpgsize) {
183 			pr_info("ECC correction at %#llx\n",
184 			       (long long)addr);
185 			err = 0;
186 		} else {
187 			pr_err("error: read failed at %#llx\n",
188 			       (long long)addr);
189 			return err ? err : -1;
190 		}
191 	}
192 	if (unlikely(memcmp(readbuf, writebuf, subpgsize))) {
193 		pr_err("error: verify failed at %#llx\n",
194 		       (long long)addr);
195 		pr_info("------------- written----------------\n");
196 		print_subpage(writebuf);
197 		pr_info("------------- read ------------------\n");
198 		print_subpage(readbuf);
199 		pr_info("-------------------------------------\n");
200 		errcnt += 1;
201 	}
202 
203 	addr += subpgsize;
204 
205 	prandom_bytes_state(&rnd_state, writebuf, subpgsize);
206 	clear_data(readbuf, subpgsize);
207 	err = mtd_read(mtd, addr, subpgsize, &read, readbuf);
208 	if (unlikely(err || read != subpgsize)) {
209 		if (mtd_is_bitflip(err) && read == subpgsize) {
210 			pr_info("ECC correction at %#llx\n",
211 			       (long long)addr);
212 			err = 0;
213 		} else {
214 			pr_err("error: read failed at %#llx\n",
215 			       (long long)addr);
216 			return err ? err : -1;
217 		}
218 	}
219 	if (unlikely(memcmp(readbuf, writebuf, subpgsize))) {
220 		pr_info("error: verify failed at %#llx\n",
221 		       (long long)addr);
222 		pr_info("------------- written----------------\n");
223 		print_subpage(writebuf);
224 		pr_info("------------- read ------------------\n");
225 		print_subpage(readbuf);
226 		pr_info("-------------------------------------\n");
227 		errcnt += 1;
228 	}
229 
230 	return err;
231 }
232 
verify_eraseblock2(int ebnum)233 static int verify_eraseblock2(int ebnum)
234 {
235 	size_t read;
236 	int err = 0, k;
237 	loff_t addr = ebnum * mtd->erasesize;
238 
239 	for (k = 1; k < 33; ++k) {
240 		if (addr + (subpgsize * k) > (ebnum + 1) * mtd->erasesize)
241 			break;
242 		prandom_bytes_state(&rnd_state, writebuf, subpgsize * k);
243 		clear_data(readbuf, subpgsize * k);
244 		err = mtd_read(mtd, addr, subpgsize * k, &read, readbuf);
245 		if (unlikely(err || read != subpgsize * k)) {
246 			if (mtd_is_bitflip(err) && read == subpgsize * k) {
247 				pr_info("ECC correction at %#llx\n",
248 				       (long long)addr);
249 				err = 0;
250 			} else {
251 				pr_err("error: read failed at "
252 				       "%#llx\n", (long long)addr);
253 				return err ? err : -1;
254 			}
255 		}
256 		if (unlikely(memcmp(readbuf, writebuf, subpgsize * k))) {
257 			pr_err("error: verify failed at %#llx\n",
258 			       (long long)addr);
259 			errcnt += 1;
260 		}
261 		addr += subpgsize * k;
262 	}
263 
264 	return err;
265 }
266 
verify_eraseblock_ff(int ebnum)267 static int verify_eraseblock_ff(int ebnum)
268 {
269 	uint32_t j;
270 	size_t read;
271 	int err = 0;
272 	loff_t addr = ebnum * mtd->erasesize;
273 
274 	memset(writebuf, 0xff, subpgsize);
275 	for (j = 0; j < mtd->erasesize / subpgsize; ++j) {
276 		clear_data(readbuf, subpgsize);
277 		err = mtd_read(mtd, addr, subpgsize, &read, readbuf);
278 		if (unlikely(err || read != subpgsize)) {
279 			if (mtd_is_bitflip(err) && read == subpgsize) {
280 				pr_info("ECC correction at %#llx\n",
281 				       (long long)addr);
282 				err = 0;
283 			} else {
284 				pr_err("error: read failed at "
285 				       "%#llx\n", (long long)addr);
286 				return err ? err : -1;
287 			}
288 		}
289 		if (unlikely(memcmp(readbuf, writebuf, subpgsize))) {
290 			pr_err("error: verify 0xff failed at "
291 			       "%#llx\n", (long long)addr);
292 			errcnt += 1;
293 		}
294 		addr += subpgsize;
295 	}
296 
297 	return err;
298 }
299 
verify_all_eraseblocks_ff(void)300 static int verify_all_eraseblocks_ff(void)
301 {
302 	int err;
303 	unsigned int i;
304 
305 	pr_info("verifying all eraseblocks for 0xff\n");
306 	for (i = 0; i < ebcnt; ++i) {
307 		if (bbt[i])
308 			continue;
309 		err = verify_eraseblock_ff(i);
310 		if (err)
311 			return err;
312 		if (i % 256 == 0)
313 			pr_info("verified up to eraseblock %u\n", i);
314 		cond_resched();
315 	}
316 	pr_info("verified %u eraseblocks\n", i);
317 	return 0;
318 }
319 
is_block_bad(int ebnum)320 static int is_block_bad(int ebnum)
321 {
322 	loff_t addr = ebnum * mtd->erasesize;
323 	int ret;
324 
325 	ret = mtd_block_isbad(mtd, addr);
326 	if (ret)
327 		pr_info("block %d is bad\n", ebnum);
328 	return ret;
329 }
330 
scan_for_bad_eraseblocks(void)331 static int scan_for_bad_eraseblocks(void)
332 {
333 	int i, bad = 0;
334 
335 	bbt = kzalloc(ebcnt, GFP_KERNEL);
336 	if (!bbt) {
337 		pr_err("error: cannot allocate memory\n");
338 		return -ENOMEM;
339 	}
340 
341 	pr_info("scanning for bad eraseblocks\n");
342 	for (i = 0; i < ebcnt; ++i) {
343 		bbt[i] = is_block_bad(i) ? 1 : 0;
344 		if (bbt[i])
345 			bad += 1;
346 		cond_resched();
347 	}
348 	pr_info("scanned %d eraseblocks, %d are bad\n", i, bad);
349 	return 0;
350 }
351 
mtd_subpagetest_init(void)352 static int __init mtd_subpagetest_init(void)
353 {
354 	int err = 0;
355 	uint32_t i;
356 	uint64_t tmp;
357 
358 	printk(KERN_INFO "\n");
359 	printk(KERN_INFO "=================================================\n");
360 
361 	if (dev < 0) {
362 		pr_info("Please specify a valid mtd-device via module parameter\n");
363 		pr_crit("CAREFUL: This test wipes all data on the specified MTD device!\n");
364 		return -EINVAL;
365 	}
366 
367 	pr_info("MTD device: %d\n", dev);
368 
369 	mtd = get_mtd_device(NULL, dev);
370 	if (IS_ERR(mtd)) {
371 		err = PTR_ERR(mtd);
372 		pr_err("error: cannot get MTD device\n");
373 		return err;
374 	}
375 
376 	if (mtd->type != MTD_NANDFLASH) {
377 		pr_info("this test requires NAND flash\n");
378 		goto out;
379 	}
380 
381 	subpgsize = mtd->writesize >> mtd->subpage_sft;
382 	tmp = mtd->size;
383 	do_div(tmp, mtd->erasesize);
384 	ebcnt = tmp;
385 	pgcnt = mtd->erasesize / mtd->writesize;
386 
387 	pr_info("MTD device size %llu, eraseblock size %u, "
388 	       "page size %u, subpage size %u, count of eraseblocks %u, "
389 	       "pages per eraseblock %u, OOB size %u\n",
390 	       (unsigned long long)mtd->size, mtd->erasesize,
391 	       mtd->writesize, subpgsize, ebcnt, pgcnt, mtd->oobsize);
392 
393 	err = -ENOMEM;
394 	bufsize = subpgsize * 32;
395 	writebuf = kmalloc(bufsize, GFP_KERNEL);
396 	if (!writebuf) {
397 		pr_info("error: cannot allocate memory\n");
398 		goto out;
399 	}
400 	readbuf = kmalloc(bufsize, GFP_KERNEL);
401 	if (!readbuf) {
402 		pr_info("error: cannot allocate memory\n");
403 		goto out;
404 	}
405 
406 	err = scan_for_bad_eraseblocks();
407 	if (err)
408 		goto out;
409 
410 	err = erase_whole_device();
411 	if (err)
412 		goto out;
413 
414 	pr_info("writing whole device\n");
415 	prandom_seed_state(&rnd_state, 1);
416 	for (i = 0; i < ebcnt; ++i) {
417 		if (bbt[i])
418 			continue;
419 		err = write_eraseblock(i);
420 		if (unlikely(err))
421 			goto out;
422 		if (i % 256 == 0)
423 			pr_info("written up to eraseblock %u\n", i);
424 		cond_resched();
425 	}
426 	pr_info("written %u eraseblocks\n", i);
427 
428 	prandom_seed_state(&rnd_state, 1);
429 	pr_info("verifying all eraseblocks\n");
430 	for (i = 0; i < ebcnt; ++i) {
431 		if (bbt[i])
432 			continue;
433 		err = verify_eraseblock(i);
434 		if (unlikely(err))
435 			goto out;
436 		if (i % 256 == 0)
437 			pr_info("verified up to eraseblock %u\n", i);
438 		cond_resched();
439 	}
440 	pr_info("verified %u eraseblocks\n", i);
441 
442 	err = erase_whole_device();
443 	if (err)
444 		goto out;
445 
446 	err = verify_all_eraseblocks_ff();
447 	if (err)
448 		goto out;
449 
450 	/* Write all eraseblocks */
451 	prandom_seed_state(&rnd_state, 3);
452 	pr_info("writing whole device\n");
453 	for (i = 0; i < ebcnt; ++i) {
454 		if (bbt[i])
455 			continue;
456 		err = write_eraseblock2(i);
457 		if (unlikely(err))
458 			goto out;
459 		if (i % 256 == 0)
460 			pr_info("written up to eraseblock %u\n", i);
461 		cond_resched();
462 	}
463 	pr_info("written %u eraseblocks\n", i);
464 
465 	/* Check all eraseblocks */
466 	prandom_seed_state(&rnd_state, 3);
467 	pr_info("verifying all eraseblocks\n");
468 	for (i = 0; i < ebcnt; ++i) {
469 		if (bbt[i])
470 			continue;
471 		err = verify_eraseblock2(i);
472 		if (unlikely(err))
473 			goto out;
474 		if (i % 256 == 0)
475 			pr_info("verified up to eraseblock %u\n", i);
476 		cond_resched();
477 	}
478 	pr_info("verified %u eraseblocks\n", i);
479 
480 	err = erase_whole_device();
481 	if (err)
482 		goto out;
483 
484 	err = verify_all_eraseblocks_ff();
485 	if (err)
486 		goto out;
487 
488 	pr_info("finished with %d errors\n", errcnt);
489 
490 out:
491 	kfree(bbt);
492 	kfree(readbuf);
493 	kfree(writebuf);
494 	put_mtd_device(mtd);
495 	if (err)
496 		pr_info("error %d occurred\n", err);
497 	printk(KERN_INFO "=================================================\n");
498 	return err;
499 }
500 module_init(mtd_subpagetest_init);
501 
mtd_subpagetest_exit(void)502 static void __exit mtd_subpagetest_exit(void)
503 {
504 	return;
505 }
506 module_exit(mtd_subpagetest_exit);
507 
508 MODULE_DESCRIPTION("Subpage test module");
509 MODULE_AUTHOR("Adrian Hunter");
510 MODULE_LICENSE("GPL");
511