Commit | Line | Data |
---|---|---|
e3644da7 AB |
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 | ||
04810274 VN |
22 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
23 | ||
e3644da7 AB |
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> | |
5a0e3ad6 | 30 | #include <linux/slab.h> |
e3644da7 | 31 | #include <linux/sched.h> |
8dad0498 | 32 | #include <linux/random.h> |
e3644da7 | 33 | |
4bf527aa AM |
34 | #include "mtd_test.h" |
35 | ||
7406060e | 36 | static int dev = -EINVAL; |
e3644da7 AB |
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; | |
8dad0498 | 52 | static struct rnd_state rnd_state; |
e3644da7 | 53 | |
e3644da7 AB |
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 | for (i = 0; i < pgcnt; ++i, addr += mtd->writesize) { | |
8dad0498 | 73 | prandom_bytes_state(&rnd_state, writebuf, use_len); |
0612b9dd | 74 | ops.mode = MTD_OPS_AUTO_OOB; |
e3644da7 AB |
75 | ops.len = 0; |
76 | ops.retlen = 0; | |
77 | ops.ooblen = use_len; | |
78 | ops.oobretlen = 0; | |
79 | ops.ooboffs = use_offset; | |
23d42494 | 80 | ops.datbuf = NULL; |
e3644da7 | 81 | ops.oobbuf = writebuf; |
a2cc5ba0 | 82 | err = mtd_write_oob(mtd, addr, &ops); |
e3644da7 | 83 | if (err || ops.oobretlen != use_len) { |
04810274 | 84 | pr_err("error: writeoob failed at %#llx\n", |
e3644da7 | 85 | (long long)addr); |
04810274 | 86 | pr_err("error: use_len %d, use_offset %d\n", |
e3644da7 AB |
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 | ||
04810274 | 103 | pr_info("writing OOBs of whole device\n"); |
e3644da7 AB |
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) | |
04810274 | 111 | pr_info("written up to eraseblock %u\n", i); |
e3644da7 AB |
112 | cond_resched(); |
113 | } | |
04810274 | 114 | pr_info("written %u eraseblocks\n", i); |
e3644da7 AB |
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 | for (i = 0; i < pgcnt; ++i, addr += mtd->writesize) { | |
8dad0498 | 126 | prandom_bytes_state(&rnd_state, writebuf, use_len); |
0612b9dd | 127 | ops.mode = MTD_OPS_AUTO_OOB; |
e3644da7 AB |
128 | ops.len = 0; |
129 | ops.retlen = 0; | |
130 | ops.ooblen = use_len; | |
131 | ops.oobretlen = 0; | |
132 | ops.ooboffs = use_offset; | |
23d42494 | 133 | ops.datbuf = NULL; |
e3644da7 | 134 | ops.oobbuf = readbuf; |
fd2819bb | 135 | err = mtd_read_oob(mtd, addr, &ops); |
e3644da7 | 136 | if (err || ops.oobretlen != use_len) { |
04810274 | 137 | pr_err("error: readoob failed at %#llx\n", |
e3644da7 AB |
138 | (long long)addr); |
139 | errcnt += 1; | |
140 | return err ? err : -1; | |
141 | } | |
142 | if (memcmp(readbuf, writebuf, use_len)) { | |
04810274 | 143 | pr_err("error: verify failed at %#llx\n", |
e3644da7 AB |
144 | (long long)addr); |
145 | errcnt += 1; | |
146 | if (errcnt > 1000) { | |
04810274 | 147 | pr_err("error: too many errors\n"); |
e3644da7 AB |
148 | return -1; |
149 | } | |
150 | } | |
151 | if (use_offset != 0 || use_len < mtd->ecclayout->oobavail) { | |
152 | int k; | |
153 | ||
0612b9dd | 154 | ops.mode = MTD_OPS_AUTO_OOB; |
e3644da7 AB |
155 | ops.len = 0; |
156 | ops.retlen = 0; | |
157 | ops.ooblen = mtd->ecclayout->oobavail; | |
158 | ops.oobretlen = 0; | |
159 | ops.ooboffs = 0; | |
23d42494 | 160 | ops.datbuf = NULL; |
e3644da7 | 161 | ops.oobbuf = readbuf; |
fd2819bb | 162 | err = mtd_read_oob(mtd, addr, &ops); |
e3644da7 | 163 | if (err || ops.oobretlen != mtd->ecclayout->oobavail) { |
04810274 VN |
164 | pr_err("error: readoob failed at %#llx\n", |
165 | (long long)addr); | |
e3644da7 AB |
166 | errcnt += 1; |
167 | return err ? err : -1; | |
168 | } | |
169 | if (memcmp(readbuf + use_offset, writebuf, use_len)) { | |
04810274 VN |
170 | pr_err("error: verify failed at %#llx\n", |
171 | (long long)addr); | |
e3644da7 AB |
172 | errcnt += 1; |
173 | if (errcnt > 1000) { | |
04810274 | 174 | pr_err("error: too many errors\n"); |
e3644da7 AB |
175 | return -1; |
176 | } | |
177 | } | |
178 | for (k = 0; k < use_offset; ++k) | |
179 | if (readbuf[k] != 0xff) { | |
04810274 | 180 | pr_err("error: verify 0xff " |
e3644da7 AB |
181 | "failed at %#llx\n", |
182 | (long long)addr); | |
183 | errcnt += 1; | |
184 | if (errcnt > 1000) { | |
04810274 | 185 | pr_err("error: too " |
e3644da7 AB |
186 | "many errors\n"); |
187 | return -1; | |
188 | } | |
189 | } | |
190 | for (k = use_offset + use_len; | |
191 | k < mtd->ecclayout->oobavail; ++k) | |
192 | if (readbuf[k] != 0xff) { | |
04810274 | 193 | pr_err("error: verify 0xff " |
e3644da7 AB |
194 | "failed at %#llx\n", |
195 | (long long)addr); | |
196 | errcnt += 1; | |
197 | if (errcnt > 1000) { | |
04810274 | 198 | pr_err("error: too " |
e3644da7 AB |
199 | "many errors\n"); |
200 | return -1; | |
201 | } | |
202 | } | |
203 | } | |
204 | if (vary_offset) | |
205 | do_vary_offset(); | |
206 | } | |
207 | return err; | |
208 | } | |
209 | ||
210 | static int verify_eraseblock_in_one_go(int ebnum) | |
211 | { | |
212 | struct mtd_oob_ops ops; | |
213 | int err = 0; | |
214 | loff_t addr = ebnum * mtd->erasesize; | |
215 | size_t len = mtd->ecclayout->oobavail * pgcnt; | |
216 | ||
8dad0498 | 217 | prandom_bytes_state(&rnd_state, writebuf, len); |
0612b9dd | 218 | ops.mode = MTD_OPS_AUTO_OOB; |
e3644da7 AB |
219 | ops.len = 0; |
220 | ops.retlen = 0; | |
221 | ops.ooblen = len; | |
222 | ops.oobretlen = 0; | |
223 | ops.ooboffs = 0; | |
23d42494 | 224 | ops.datbuf = NULL; |
e3644da7 | 225 | ops.oobbuf = readbuf; |
fd2819bb | 226 | err = mtd_read_oob(mtd, addr, &ops); |
e3644da7 | 227 | if (err || ops.oobretlen != len) { |
04810274 | 228 | pr_err("error: readoob failed at %#llx\n", |
e3644da7 AB |
229 | (long long)addr); |
230 | errcnt += 1; | |
231 | return err ? err : -1; | |
232 | } | |
233 | if (memcmp(readbuf, writebuf, len)) { | |
04810274 | 234 | pr_err("error: verify failed at %#llx\n", |
e3644da7 AB |
235 | (long long)addr); |
236 | errcnt += 1; | |
237 | if (errcnt > 1000) { | |
04810274 | 238 | pr_err("error: too many errors\n"); |
e3644da7 AB |
239 | return -1; |
240 | } | |
241 | } | |
242 | ||
243 | return err; | |
244 | } | |
245 | ||
246 | static int verify_all_eraseblocks(void) | |
247 | { | |
248 | int err; | |
249 | unsigned int i; | |
250 | ||
04810274 | 251 | pr_info("verifying all eraseblocks\n"); |
e3644da7 AB |
252 | for (i = 0; i < ebcnt; ++i) { |
253 | if (bbt[i]) | |
254 | continue; | |
255 | err = verify_eraseblock(i); | |
256 | if (err) | |
257 | return err; | |
258 | if (i % 256 == 0) | |
04810274 | 259 | pr_info("verified up to eraseblock %u\n", i); |
e3644da7 AB |
260 | cond_resched(); |
261 | } | |
04810274 | 262 | pr_info("verified %u eraseblocks\n", i); |
e3644da7 AB |
263 | return 0; |
264 | } | |
265 | ||
e3644da7 AB |
266 | static int __init mtd_oobtest_init(void) |
267 | { | |
268 | int err = 0; | |
269 | unsigned int i; | |
270 | uint64_t tmp; | |
271 | struct mtd_oob_ops ops; | |
272 | loff_t addr = 0, addr0; | |
273 | ||
274 | printk(KERN_INFO "\n"); | |
275 | printk(KERN_INFO "=================================================\n"); | |
7406060e WS |
276 | |
277 | if (dev < 0) { | |
064a7694 MI |
278 | pr_info("Please specify a valid mtd-device via module parameter\n"); |
279 | pr_crit("CAREFUL: This test wipes all data on the specified MTD device!\n"); | |
7406060e WS |
280 | return -EINVAL; |
281 | } | |
282 | ||
04810274 | 283 | pr_info("MTD device: %d\n", dev); |
e3644da7 AB |
284 | |
285 | mtd = get_mtd_device(NULL, dev); | |
286 | if (IS_ERR(mtd)) { | |
287 | err = PTR_ERR(mtd); | |
04810274 | 288 | pr_err("error: cannot get MTD device\n"); |
e3644da7 AB |
289 | return err; |
290 | } | |
291 | ||
292 | if (mtd->type != MTD_NANDFLASH) { | |
04810274 | 293 | pr_info("this test requires NAND flash\n"); |
e3644da7 AB |
294 | goto out; |
295 | } | |
296 | ||
297 | tmp = mtd->size; | |
298 | do_div(tmp, mtd->erasesize); | |
299 | ebcnt = tmp; | |
300 | pgcnt = mtd->erasesize / mtd->writesize; | |
301 | ||
04810274 | 302 | pr_info("MTD device size %llu, eraseblock size %u, " |
e3644da7 AB |
303 | "page size %u, count of eraseblocks %u, pages per " |
304 | "eraseblock %u, OOB size %u\n", | |
305 | (unsigned long long)mtd->size, mtd->erasesize, | |
306 | mtd->writesize, ebcnt, pgcnt, mtd->oobsize); | |
307 | ||
308 | err = -ENOMEM; | |
e3644da7 | 309 | readbuf = kmalloc(mtd->erasesize, GFP_KERNEL); |
33777e66 | 310 | if (!readbuf) |
e3644da7 | 311 | goto out; |
e3644da7 | 312 | writebuf = kmalloc(mtd->erasesize, GFP_KERNEL); |
33777e66 | 313 | if (!writebuf) |
e3644da7 | 314 | goto out; |
4bf527aa AM |
315 | bbt = kzalloc(ebcnt, GFP_KERNEL); |
316 | if (!bbt) | |
317 | goto out; | |
e3644da7 | 318 | |
4bf527aa | 319 | err = mtdtest_scan_for_bad_eraseblocks(mtd, bbt, 0, ebcnt); |
e3644da7 AB |
320 | if (err) |
321 | goto out; | |
322 | ||
323 | use_offset = 0; | |
324 | use_len = mtd->ecclayout->oobavail; | |
325 | use_len_max = mtd->ecclayout->oobavail; | |
326 | vary_offset = 0; | |
327 | ||
328 | /* First test: write all OOB, read it back and verify */ | |
04810274 | 329 | pr_info("test 1 of 5\n"); |
e3644da7 | 330 | |
4bf527aa | 331 | err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt); |
e3644da7 AB |
332 | if (err) |
333 | goto out; | |
334 | ||
8dad0498 | 335 | prandom_seed_state(&rnd_state, 1); |
e3644da7 AB |
336 | err = write_whole_device(); |
337 | if (err) | |
338 | goto out; | |
339 | ||
8dad0498 | 340 | prandom_seed_state(&rnd_state, 1); |
e3644da7 AB |
341 | err = verify_all_eraseblocks(); |
342 | if (err) | |
343 | goto out; | |
344 | ||
345 | /* | |
346 | * Second test: write all OOB, a block at a time, read it back and | |
347 | * verify. | |
348 | */ | |
04810274 | 349 | pr_info("test 2 of 5\n"); |
e3644da7 | 350 | |
4bf527aa | 351 | err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt); |
e3644da7 AB |
352 | if (err) |
353 | goto out; | |
354 | ||
8dad0498 | 355 | prandom_seed_state(&rnd_state, 3); |
e3644da7 AB |
356 | err = write_whole_device(); |
357 | if (err) | |
358 | goto out; | |
359 | ||
360 | /* Check all eraseblocks */ | |
8dad0498 | 361 | prandom_seed_state(&rnd_state, 3); |
04810274 | 362 | pr_info("verifying all eraseblocks\n"); |
e3644da7 AB |
363 | for (i = 0; i < ebcnt; ++i) { |
364 | if (bbt[i]) | |
365 | continue; | |
366 | err = verify_eraseblock_in_one_go(i); | |
367 | if (err) | |
368 | goto out; | |
369 | if (i % 256 == 0) | |
04810274 | 370 | pr_info("verified up to eraseblock %u\n", i); |
e3644da7 AB |
371 | cond_resched(); |
372 | } | |
04810274 | 373 | pr_info("verified %u eraseblocks\n", i); |
e3644da7 AB |
374 | |
375 | /* | |
376 | * Third test: write OOB at varying offsets and lengths, read it back | |
377 | * and verify. | |
378 | */ | |
04810274 | 379 | pr_info("test 3 of 5\n"); |
e3644da7 | 380 | |
4bf527aa | 381 | err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt); |
e3644da7 AB |
382 | if (err) |
383 | goto out; | |
384 | ||
385 | /* Write all eraseblocks */ | |
386 | use_offset = 0; | |
387 | use_len = mtd->ecclayout->oobavail; | |
388 | use_len_max = mtd->ecclayout->oobavail; | |
389 | vary_offset = 1; | |
8dad0498 | 390 | prandom_seed_state(&rnd_state, 5); |
f54d6336 AM |
391 | |
392 | err = write_whole_device(); | |
393 | if (err) | |
394 | goto out; | |
e3644da7 AB |
395 | |
396 | /* Check all eraseblocks */ | |
397 | use_offset = 0; | |
398 | use_len = mtd->ecclayout->oobavail; | |
399 | use_len_max = mtd->ecclayout->oobavail; | |
400 | vary_offset = 1; | |
8dad0498 | 401 | prandom_seed_state(&rnd_state, 5); |
e3644da7 AB |
402 | err = verify_all_eraseblocks(); |
403 | if (err) | |
404 | goto out; | |
405 | ||
406 | use_offset = 0; | |
407 | use_len = mtd->ecclayout->oobavail; | |
408 | use_len_max = mtd->ecclayout->oobavail; | |
409 | vary_offset = 0; | |
410 | ||
411 | /* Fourth test: try to write off end of device */ | |
04810274 | 412 | pr_info("test 4 of 5\n"); |
e3644da7 | 413 | |
4bf527aa | 414 | err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt); |
e3644da7 AB |
415 | if (err) |
416 | goto out; | |
417 | ||
418 | addr0 = 0; | |
c6f7e7be | 419 | for (i = 0; i < ebcnt && bbt[i]; ++i) |
e3644da7 AB |
420 | addr0 += mtd->erasesize; |
421 | ||
422 | /* Attempt to write off end of OOB */ | |
0612b9dd | 423 | ops.mode = MTD_OPS_AUTO_OOB; |
e3644da7 AB |
424 | ops.len = 0; |
425 | ops.retlen = 0; | |
426 | ops.ooblen = 1; | |
427 | ops.oobretlen = 0; | |
428 | ops.ooboffs = mtd->ecclayout->oobavail; | |
23d42494 | 429 | ops.datbuf = NULL; |
e3644da7 | 430 | ops.oobbuf = writebuf; |
04810274 VN |
431 | pr_info("attempting to start write past end of OOB\n"); |
432 | pr_info("an error is expected...\n"); | |
a2cc5ba0 | 433 | err = mtd_write_oob(mtd, addr0, &ops); |
e3644da7 | 434 | if (err) { |
04810274 | 435 | pr_info("error occurred as expected\n"); |
e3644da7 AB |
436 | err = 0; |
437 | } else { | |
04810274 | 438 | pr_err("error: can write past end of OOB\n"); |
e3644da7 AB |
439 | errcnt += 1; |
440 | } | |
441 | ||
442 | /* Attempt to read off end of OOB */ | |
0612b9dd | 443 | ops.mode = MTD_OPS_AUTO_OOB; |
e3644da7 AB |
444 | ops.len = 0; |
445 | ops.retlen = 0; | |
446 | ops.ooblen = 1; | |
447 | ops.oobretlen = 0; | |
448 | ops.ooboffs = mtd->ecclayout->oobavail; | |
23d42494 | 449 | ops.datbuf = NULL; |
e3644da7 | 450 | ops.oobbuf = readbuf; |
04810274 VN |
451 | pr_info("attempting to start read past end of OOB\n"); |
452 | pr_info("an error is expected...\n"); | |
fd2819bb | 453 | err = mtd_read_oob(mtd, addr0, &ops); |
e3644da7 | 454 | if (err) { |
04810274 | 455 | pr_info("error occurred as expected\n"); |
e3644da7 AB |
456 | err = 0; |
457 | } else { | |
04810274 | 458 | pr_err("error: can read past end of OOB\n"); |
e3644da7 AB |
459 | errcnt += 1; |
460 | } | |
461 | ||
462 | if (bbt[ebcnt - 1]) | |
04810274 | 463 | pr_info("skipping end of device tests because last " |
e3644da7 AB |
464 | "block is bad\n"); |
465 | else { | |
466 | /* Attempt to write off end of device */ | |
0612b9dd | 467 | ops.mode = MTD_OPS_AUTO_OOB; |
e3644da7 AB |
468 | ops.len = 0; |
469 | ops.retlen = 0; | |
470 | ops.ooblen = mtd->ecclayout->oobavail + 1; | |
471 | ops.oobretlen = 0; | |
472 | ops.ooboffs = 0; | |
23d42494 | 473 | ops.datbuf = NULL; |
e3644da7 | 474 | ops.oobbuf = writebuf; |
04810274 VN |
475 | pr_info("attempting to write past end of device\n"); |
476 | pr_info("an error is expected...\n"); | |
a2cc5ba0 | 477 | err = mtd_write_oob(mtd, mtd->size - mtd->writesize, &ops); |
e3644da7 | 478 | if (err) { |
04810274 | 479 | pr_info("error occurred as expected\n"); |
e3644da7 AB |
480 | err = 0; |
481 | } else { | |
04810274 | 482 | pr_err("error: wrote past end of device\n"); |
e3644da7 AB |
483 | errcnt += 1; |
484 | } | |
485 | ||
486 | /* Attempt to read off end of device */ | |
0612b9dd | 487 | ops.mode = MTD_OPS_AUTO_OOB; |
e3644da7 AB |
488 | ops.len = 0; |
489 | ops.retlen = 0; | |
490 | ops.ooblen = mtd->ecclayout->oobavail + 1; | |
491 | ops.oobretlen = 0; | |
492 | ops.ooboffs = 0; | |
23d42494 | 493 | ops.datbuf = NULL; |
e3644da7 | 494 | ops.oobbuf = readbuf; |
04810274 VN |
495 | pr_info("attempting to read past end of device\n"); |
496 | pr_info("an error is expected...\n"); | |
fd2819bb | 497 | err = mtd_read_oob(mtd, mtd->size - mtd->writesize, &ops); |
e3644da7 | 498 | if (err) { |
04810274 | 499 | pr_info("error occurred as expected\n"); |
e3644da7 AB |
500 | err = 0; |
501 | } else { | |
04810274 | 502 | pr_err("error: read past end of device\n"); |
e3644da7 AB |
503 | errcnt += 1; |
504 | } | |
505 | ||
4bf527aa | 506 | err = mtdtest_erase_eraseblock(mtd, ebcnt - 1); |
e3644da7 AB |
507 | if (err) |
508 | goto out; | |
509 | ||
510 | /* Attempt to write off end of device */ | |
0612b9dd | 511 | ops.mode = MTD_OPS_AUTO_OOB; |
e3644da7 AB |
512 | ops.len = 0; |
513 | ops.retlen = 0; | |
514 | ops.ooblen = mtd->ecclayout->oobavail; | |
515 | ops.oobretlen = 0; | |
516 | ops.ooboffs = 1; | |
23d42494 | 517 | ops.datbuf = NULL; |
e3644da7 | 518 | ops.oobbuf = writebuf; |
04810274 VN |
519 | pr_info("attempting to write past end of device\n"); |
520 | pr_info("an error is expected...\n"); | |
a2cc5ba0 | 521 | err = mtd_write_oob(mtd, mtd->size - mtd->writesize, &ops); |
e3644da7 | 522 | if (err) { |
04810274 | 523 | pr_info("error occurred as expected\n"); |
e3644da7 AB |
524 | err = 0; |
525 | } else { | |
04810274 | 526 | pr_err("error: wrote past end of device\n"); |
e3644da7 AB |
527 | errcnt += 1; |
528 | } | |
529 | ||
530 | /* Attempt to read off end of device */ | |
0612b9dd | 531 | ops.mode = MTD_OPS_AUTO_OOB; |
e3644da7 AB |
532 | ops.len = 0; |
533 | ops.retlen = 0; | |
534 | ops.ooblen = mtd->ecclayout->oobavail; | |
535 | ops.oobretlen = 0; | |
536 | ops.ooboffs = 1; | |
23d42494 | 537 | ops.datbuf = NULL; |
e3644da7 | 538 | ops.oobbuf = readbuf; |
04810274 VN |
539 | pr_info("attempting to read past end of device\n"); |
540 | pr_info("an error is expected...\n"); | |
fd2819bb | 541 | err = mtd_read_oob(mtd, mtd->size - mtd->writesize, &ops); |
e3644da7 | 542 | if (err) { |
04810274 | 543 | pr_info("error occurred as expected\n"); |
e3644da7 AB |
544 | err = 0; |
545 | } else { | |
04810274 | 546 | pr_err("error: read past end of device\n"); |
e3644da7 AB |
547 | errcnt += 1; |
548 | } | |
549 | } | |
550 | ||
551 | /* Fifth test: write / read across block boundaries */ | |
04810274 | 552 | pr_info("test 5 of 5\n"); |
e3644da7 AB |
553 | |
554 | /* Erase all eraseblocks */ | |
4bf527aa | 555 | err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt); |
e3644da7 AB |
556 | if (err) |
557 | goto out; | |
558 | ||
559 | /* Write all eraseblocks */ | |
8dad0498 | 560 | prandom_seed_state(&rnd_state, 11); |
04810274 | 561 | pr_info("writing OOBs of whole device\n"); |
e3644da7 AB |
562 | for (i = 0; i < ebcnt - 1; ++i) { |
563 | int cnt = 2; | |
564 | int pg; | |
565 | size_t sz = mtd->ecclayout->oobavail; | |
566 | if (bbt[i] || bbt[i + 1]) | |
567 | continue; | |
568 | addr = (i + 1) * mtd->erasesize - mtd->writesize; | |
569 | for (pg = 0; pg < cnt; ++pg) { | |
8dad0498 | 570 | prandom_bytes_state(&rnd_state, writebuf, sz); |
0612b9dd | 571 | ops.mode = MTD_OPS_AUTO_OOB; |
e3644da7 AB |
572 | ops.len = 0; |
573 | ops.retlen = 0; | |
574 | ops.ooblen = sz; | |
575 | ops.oobretlen = 0; | |
576 | ops.ooboffs = 0; | |
23d42494 | 577 | ops.datbuf = NULL; |
e3644da7 | 578 | ops.oobbuf = writebuf; |
a2cc5ba0 | 579 | err = mtd_write_oob(mtd, addr, &ops); |
e3644da7 AB |
580 | if (err) |
581 | goto out; | |
582 | if (i % 256 == 0) | |
04810274 | 583 | pr_info("written up to eraseblock %u\n", i); |
e3644da7 AB |
584 | cond_resched(); |
585 | addr += mtd->writesize; | |
586 | } | |
587 | } | |
04810274 | 588 | pr_info("written %u eraseblocks\n", i); |
e3644da7 AB |
589 | |
590 | /* Check all eraseblocks */ | |
8dad0498 | 591 | prandom_seed_state(&rnd_state, 11); |
04810274 | 592 | pr_info("verifying all eraseblocks\n"); |
e3644da7 AB |
593 | for (i = 0; i < ebcnt - 1; ++i) { |
594 | if (bbt[i] || bbt[i + 1]) | |
595 | continue; | |
8dad0498 AM |
596 | prandom_bytes_state(&rnd_state, writebuf, |
597 | mtd->ecclayout->oobavail * 2); | |
e3644da7 | 598 | addr = (i + 1) * mtd->erasesize - mtd->writesize; |
0612b9dd | 599 | ops.mode = MTD_OPS_AUTO_OOB; |
e3644da7 AB |
600 | ops.len = 0; |
601 | ops.retlen = 0; | |
602 | ops.ooblen = mtd->ecclayout->oobavail * 2; | |
603 | ops.oobretlen = 0; | |
604 | ops.ooboffs = 0; | |
23d42494 | 605 | ops.datbuf = NULL; |
e3644da7 | 606 | ops.oobbuf = readbuf; |
fd2819bb | 607 | err = mtd_read_oob(mtd, addr, &ops); |
e3644da7 AB |
608 | if (err) |
609 | goto out; | |
610 | if (memcmp(readbuf, writebuf, mtd->ecclayout->oobavail * 2)) { | |
04810274 | 611 | pr_err("error: verify failed at %#llx\n", |
e3644da7 AB |
612 | (long long)addr); |
613 | errcnt += 1; | |
614 | if (errcnt > 1000) { | |
04810274 | 615 | pr_err("error: too many errors\n"); |
e3644da7 AB |
616 | goto out; |
617 | } | |
618 | } | |
619 | if (i % 256 == 0) | |
04810274 | 620 | pr_info("verified up to eraseblock %u\n", i); |
e3644da7 AB |
621 | cond_resched(); |
622 | } | |
04810274 | 623 | pr_info("verified %u eraseblocks\n", i); |
e3644da7 | 624 | |
04810274 | 625 | pr_info("finished with %d errors\n", errcnt); |
e3644da7 AB |
626 | out: |
627 | kfree(bbt); | |
628 | kfree(writebuf); | |
629 | kfree(readbuf); | |
630 | put_mtd_device(mtd); | |
631 | if (err) | |
04810274 | 632 | pr_info("error %d occurred\n", err); |
e3644da7 AB |
633 | printk(KERN_INFO "=================================================\n"); |
634 | return err; | |
635 | } | |
636 | module_init(mtd_oobtest_init); | |
637 | ||
638 | static void __exit mtd_oobtest_exit(void) | |
639 | { | |
640 | return; | |
641 | } | |
642 | module_exit(mtd_oobtest_exit); | |
643 | ||
644 | MODULE_DESCRIPTION("Out-of-band test module"); | |
645 | MODULE_AUTHOR("Adrian Hunter"); | |
646 | MODULE_LICENSE("GPL"); |