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; |
afc0ea1b | 37 | static int bitflip_limit; |
e3644da7 AB |
38 | module_param(dev, int, S_IRUGO); |
39 | MODULE_PARM_DESC(dev, "MTD device number to use"); | |
afc0ea1b RQ |
40 | module_param(bitflip_limit, int, S_IRUGO); |
41 | MODULE_PARM_DESC(bitflip_limit, "Max. allowed bitflips per page"); | |
e3644da7 AB |
42 | |
43 | static struct mtd_info *mtd; | |
44 | static unsigned char *readbuf; | |
45 | static unsigned char *writebuf; | |
46 | static unsigned char *bbt; | |
47 | ||
48 | static int ebcnt; | |
49 | static int pgcnt; | |
50 | static int errcnt; | |
51 | static int use_offset; | |
52 | static int use_len; | |
53 | static int use_len_max; | |
54 | static int vary_offset; | |
8dad0498 | 55 | static struct rnd_state rnd_state; |
e3644da7 | 56 | |
e3644da7 AB |
57 | static void do_vary_offset(void) |
58 | { | |
59 | use_len -= 1; | |
60 | if (use_len < 1) { | |
61 | use_offset += 1; | |
62 | if (use_offset >= use_len_max) | |
63 | use_offset = 0; | |
64 | use_len = use_len_max - use_offset; | |
65 | } | |
66 | } | |
67 | ||
68 | static int write_eraseblock(int ebnum) | |
69 | { | |
70 | int i; | |
71 | struct mtd_oob_ops ops; | |
72 | int err = 0; | |
b9da8bae | 73 | loff_t addr = (loff_t)ebnum * mtd->erasesize; |
e3644da7 | 74 | |
be54f8f1 | 75 | prandom_bytes_state(&rnd_state, writebuf, use_len_max * pgcnt); |
e3644da7 | 76 | for (i = 0; i < pgcnt; ++i, addr += mtd->writesize) { |
0612b9dd | 77 | ops.mode = MTD_OPS_AUTO_OOB; |
e3644da7 AB |
78 | ops.len = 0; |
79 | ops.retlen = 0; | |
80 | ops.ooblen = use_len; | |
81 | ops.oobretlen = 0; | |
82 | ops.ooboffs = use_offset; | |
23d42494 | 83 | ops.datbuf = NULL; |
be54f8f1 | 84 | ops.oobbuf = writebuf + (use_len_max * i) + use_offset; |
a2cc5ba0 | 85 | err = mtd_write_oob(mtd, addr, &ops); |
e3644da7 | 86 | if (err || ops.oobretlen != use_len) { |
04810274 | 87 | pr_err("error: writeoob failed at %#llx\n", |
e3644da7 | 88 | (long long)addr); |
04810274 | 89 | pr_err("error: use_len %d, use_offset %d\n", |
e3644da7 AB |
90 | use_len, use_offset); |
91 | errcnt += 1; | |
92 | return err ? err : -1; | |
93 | } | |
94 | if (vary_offset) | |
95 | do_vary_offset(); | |
96 | } | |
97 | ||
98 | return err; | |
99 | } | |
100 | ||
101 | static int write_whole_device(void) | |
102 | { | |
103 | int err; | |
104 | unsigned int i; | |
105 | ||
04810274 | 106 | pr_info("writing OOBs of whole device\n"); |
e3644da7 AB |
107 | for (i = 0; i < ebcnt; ++i) { |
108 | if (bbt[i]) | |
109 | continue; | |
110 | err = write_eraseblock(i); | |
111 | if (err) | |
112 | return err; | |
113 | if (i % 256 == 0) | |
04810274 | 114 | pr_info("written up to eraseblock %u\n", i); |
2a6a28e7 RW |
115 | |
116 | err = mtdtest_relax(); | |
117 | if (err) | |
118 | return err; | |
e3644da7 | 119 | } |
04810274 | 120 | pr_info("written %u eraseblocks\n", i); |
e3644da7 AB |
121 | return 0; |
122 | } | |
123 | ||
afc0ea1b RQ |
124 | /* |
125 | * Display the address, offset and data bytes at comparison failure. | |
126 | * Return number of bitflips encountered. | |
127 | */ | |
718e38b4 RQ |
128 | static size_t memcmpshowoffset(loff_t addr, loff_t offset, const void *cs, |
129 | const void *ct, size_t count) | |
5a66088b RQ |
130 | { |
131 | const unsigned char *su1, *su2; | |
132 | int res; | |
5a66088b | 133 | size_t i = 0; |
afc0ea1b | 134 | size_t bitflips = 0; |
5a66088b RQ |
135 | |
136 | for (su1 = cs, su2 = ct; 0 < count; ++su1, ++su2, count--, i++) { | |
137 | res = *su1 ^ *su2; | |
138 | if (res) { | |
718e38b4 RQ |
139 | pr_info("error @addr[0x%lx:0x%lx] 0x%x -> 0x%x diff 0x%x\n", |
140 | (unsigned long)addr, (unsigned long)offset + i, | |
141 | *su1, *su2, res); | |
afc0ea1b | 142 | bitflips += hweight8(res); |
5a66088b RQ |
143 | } |
144 | } | |
145 | ||
afc0ea1b | 146 | return bitflips; |
5a66088b RQ |
147 | } |
148 | ||
718e38b4 RQ |
149 | #define memcmpshow(addr, cs, ct, count) memcmpshowoffset((addr), 0, (cs), (ct),\ |
150 | (count)) | |
151 | ||
d2b51c80 RQ |
152 | /* |
153 | * Compare with 0xff and show the address, offset and data bytes at | |
154 | * comparison failure. Return number of bitflips encountered. | |
155 | */ | |
156 | static size_t memffshow(loff_t addr, loff_t offset, const void *cs, | |
157 | size_t count) | |
158 | { | |
159 | const unsigned char *su1; | |
160 | int res; | |
161 | size_t i = 0; | |
162 | size_t bitflips = 0; | |
163 | ||
164 | for (su1 = cs; 0 < count; ++su1, count--, i++) { | |
165 | res = *su1 ^ 0xff; | |
166 | if (res) { | |
167 | pr_info("error @addr[0x%lx:0x%lx] 0x%x -> 0xff diff 0x%x\n", | |
168 | (unsigned long)addr, (unsigned long)offset + i, | |
169 | *su1, res); | |
170 | bitflips += hweight8(res); | |
171 | } | |
172 | } | |
173 | ||
174 | return bitflips; | |
175 | } | |
176 | ||
e3644da7 AB |
177 | static int verify_eraseblock(int ebnum) |
178 | { | |
179 | int i; | |
180 | struct mtd_oob_ops ops; | |
181 | int err = 0; | |
1001ff7a | 182 | loff_t addr = (loff_t)ebnum * mtd->erasesize; |
afc0ea1b | 183 | size_t bitflips; |
e3644da7 | 184 | |
be54f8f1 | 185 | prandom_bytes_state(&rnd_state, writebuf, use_len_max * pgcnt); |
e3644da7 | 186 | for (i = 0; i < pgcnt; ++i, addr += mtd->writesize) { |
0612b9dd | 187 | ops.mode = MTD_OPS_AUTO_OOB; |
e3644da7 AB |
188 | ops.len = 0; |
189 | ops.retlen = 0; | |
190 | ops.ooblen = use_len; | |
191 | ops.oobretlen = 0; | |
192 | ops.ooboffs = use_offset; | |
23d42494 | 193 | ops.datbuf = NULL; |
e3644da7 | 194 | ops.oobbuf = readbuf; |
fd2819bb | 195 | err = mtd_read_oob(mtd, addr, &ops); |
e3644da7 | 196 | if (err || ops.oobretlen != use_len) { |
04810274 | 197 | pr_err("error: readoob failed at %#llx\n", |
e3644da7 AB |
198 | (long long)addr); |
199 | errcnt += 1; | |
200 | return err ? err : -1; | |
201 | } | |
afc0ea1b RQ |
202 | |
203 | bitflips = memcmpshow(addr, readbuf, | |
204 | writebuf + (use_len_max * i) + use_offset, | |
205 | use_len); | |
206 | if (bitflips > bitflip_limit) { | |
04810274 | 207 | pr_err("error: verify failed at %#llx\n", |
e3644da7 AB |
208 | (long long)addr); |
209 | errcnt += 1; | |
210 | if (errcnt > 1000) { | |
04810274 | 211 | pr_err("error: too many errors\n"); |
e3644da7 AB |
212 | return -1; |
213 | } | |
afc0ea1b RQ |
214 | } else if (bitflips) { |
215 | pr_info("ignoring error as within bitflip_limit\n"); | |
e3644da7 | 216 | } |
afc0ea1b | 217 | |
f5b8aa78 | 218 | if (use_offset != 0 || use_len < mtd->oobavail) { |
e3644da7 AB |
219 | int k; |
220 | ||
0612b9dd | 221 | ops.mode = MTD_OPS_AUTO_OOB; |
e3644da7 AB |
222 | ops.len = 0; |
223 | ops.retlen = 0; | |
f5b8aa78 | 224 | ops.ooblen = mtd->oobavail; |
e3644da7 AB |
225 | ops.oobretlen = 0; |
226 | ops.ooboffs = 0; | |
23d42494 | 227 | ops.datbuf = NULL; |
e3644da7 | 228 | ops.oobbuf = readbuf; |
fd2819bb | 229 | err = mtd_read_oob(mtd, addr, &ops); |
f5b8aa78 | 230 | if (err || ops.oobretlen != mtd->oobavail) { |
04810274 VN |
231 | pr_err("error: readoob failed at %#llx\n", |
232 | (long long)addr); | |
e3644da7 AB |
233 | errcnt += 1; |
234 | return err ? err : -1; | |
235 | } | |
718e38b4 RQ |
236 | bitflips = memcmpshowoffset(addr, use_offset, |
237 | readbuf + use_offset, | |
238 | writebuf + (use_len_max * i) + use_offset, | |
239 | use_len); | |
d2b51c80 RQ |
240 | |
241 | /* verify pre-offset area for 0xff */ | |
242 | bitflips += memffshow(addr, 0, readbuf, use_offset); | |
243 | ||
244 | /* verify post-(use_offset + use_len) area for 0xff */ | |
245 | k = use_offset + use_len; | |
246 | bitflips += memffshow(addr, k, readbuf + k, | |
f5b8aa78 | 247 | mtd->oobavail - k); |
d2b51c80 | 248 | |
afc0ea1b | 249 | if (bitflips > bitflip_limit) { |
04810274 VN |
250 | pr_err("error: verify failed at %#llx\n", |
251 | (long long)addr); | |
e3644da7 AB |
252 | errcnt += 1; |
253 | if (errcnt > 1000) { | |
04810274 | 254 | pr_err("error: too many errors\n"); |
e3644da7 AB |
255 | return -1; |
256 | } | |
afc0ea1b | 257 | } else if (bitflips) { |
d2b51c80 | 258 | pr_info("ignoring errors as within bitflip limit\n"); |
e3644da7 | 259 | } |
e3644da7 AB |
260 | } |
261 | if (vary_offset) | |
262 | do_vary_offset(); | |
263 | } | |
264 | return err; | |
265 | } | |
266 | ||
267 | static int verify_eraseblock_in_one_go(int ebnum) | |
268 | { | |
269 | struct mtd_oob_ops ops; | |
270 | int err = 0; | |
1001ff7a | 271 | loff_t addr = (loff_t)ebnum * mtd->erasesize; |
f5b8aa78 BB |
272 | size_t len = mtd->oobavail * pgcnt; |
273 | size_t oobavail = mtd->oobavail; | |
afc0ea1b RQ |
274 | size_t bitflips; |
275 | int i; | |
e3644da7 | 276 | |
8dad0498 | 277 | prandom_bytes_state(&rnd_state, writebuf, len); |
0612b9dd | 278 | ops.mode = MTD_OPS_AUTO_OOB; |
e3644da7 AB |
279 | ops.len = 0; |
280 | ops.retlen = 0; | |
281 | ops.ooblen = len; | |
282 | ops.oobretlen = 0; | |
283 | ops.ooboffs = 0; | |
23d42494 | 284 | ops.datbuf = NULL; |
e3644da7 | 285 | ops.oobbuf = readbuf; |
afc0ea1b RQ |
286 | |
287 | /* read entire block's OOB at one go */ | |
fd2819bb | 288 | err = mtd_read_oob(mtd, addr, &ops); |
e3644da7 | 289 | if (err || ops.oobretlen != len) { |
04810274 | 290 | pr_err("error: readoob failed at %#llx\n", |
e3644da7 AB |
291 | (long long)addr); |
292 | errcnt += 1; | |
293 | return err ? err : -1; | |
294 | } | |
afc0ea1b RQ |
295 | |
296 | /* verify one page OOB at a time for bitflip per page limit check */ | |
297 | for (i = 0; i < pgcnt; ++i, addr += mtd->writesize) { | |
298 | bitflips = memcmpshow(addr, readbuf + (i * oobavail), | |
299 | writebuf + (i * oobavail), oobavail); | |
300 | if (bitflips > bitflip_limit) { | |
301 | pr_err("error: verify failed at %#llx\n", | |
302 | (long long)addr); | |
303 | errcnt += 1; | |
304 | if (errcnt > 1000) { | |
305 | pr_err("error: too many errors\n"); | |
306 | return -1; | |
307 | } | |
308 | } else if (bitflips) { | |
309 | pr_info("ignoring error as within bitflip_limit\n"); | |
e3644da7 AB |
310 | } |
311 | } | |
312 | ||
313 | return err; | |
314 | } | |
315 | ||
316 | static int verify_all_eraseblocks(void) | |
317 | { | |
318 | int err; | |
319 | unsigned int i; | |
320 | ||
04810274 | 321 | pr_info("verifying all eraseblocks\n"); |
e3644da7 AB |
322 | for (i = 0; i < ebcnt; ++i) { |
323 | if (bbt[i]) | |
324 | continue; | |
325 | err = verify_eraseblock(i); | |
326 | if (err) | |
327 | return err; | |
328 | if (i % 256 == 0) | |
04810274 | 329 | pr_info("verified up to eraseblock %u\n", i); |
2a6a28e7 RW |
330 | |
331 | err = mtdtest_relax(); | |
332 | if (err) | |
333 | return err; | |
e3644da7 | 334 | } |
04810274 | 335 | pr_info("verified %u eraseblocks\n", i); |
e3644da7 AB |
336 | return 0; |
337 | } | |
338 | ||
e3644da7 AB |
339 | static int __init mtd_oobtest_init(void) |
340 | { | |
341 | int err = 0; | |
342 | unsigned int i; | |
343 | uint64_t tmp; | |
344 | struct mtd_oob_ops ops; | |
345 | loff_t addr = 0, addr0; | |
346 | ||
347 | printk(KERN_INFO "\n"); | |
348 | printk(KERN_INFO "=================================================\n"); | |
7406060e WS |
349 | |
350 | if (dev < 0) { | |
064a7694 MI |
351 | pr_info("Please specify a valid mtd-device via module parameter\n"); |
352 | pr_crit("CAREFUL: This test wipes all data on the specified MTD device!\n"); | |
7406060e WS |
353 | return -EINVAL; |
354 | } | |
355 | ||
04810274 | 356 | pr_info("MTD device: %d\n", dev); |
e3644da7 AB |
357 | |
358 | mtd = get_mtd_device(NULL, dev); | |
359 | if (IS_ERR(mtd)) { | |
360 | err = PTR_ERR(mtd); | |
04810274 | 361 | pr_err("error: cannot get MTD device\n"); |
e3644da7 AB |
362 | return err; |
363 | } | |
364 | ||
818b9739 | 365 | if (!mtd_type_is_nand(mtd)) { |
04810274 | 366 | pr_info("this test requires NAND flash\n"); |
e3644da7 AB |
367 | goto out; |
368 | } | |
369 | ||
370 | tmp = mtd->size; | |
371 | do_div(tmp, mtd->erasesize); | |
372 | ebcnt = tmp; | |
373 | pgcnt = mtd->erasesize / mtd->writesize; | |
374 | ||
04810274 | 375 | pr_info("MTD device size %llu, eraseblock size %u, " |
e3644da7 AB |
376 | "page size %u, count of eraseblocks %u, pages per " |
377 | "eraseblock %u, OOB size %u\n", | |
378 | (unsigned long long)mtd->size, mtd->erasesize, | |
379 | mtd->writesize, ebcnt, pgcnt, mtd->oobsize); | |
380 | ||
381 | err = -ENOMEM; | |
e3644da7 | 382 | readbuf = kmalloc(mtd->erasesize, GFP_KERNEL); |
33777e66 | 383 | if (!readbuf) |
e3644da7 | 384 | goto out; |
e3644da7 | 385 | writebuf = kmalloc(mtd->erasesize, GFP_KERNEL); |
33777e66 | 386 | if (!writebuf) |
e3644da7 | 387 | goto out; |
4bf527aa AM |
388 | bbt = kzalloc(ebcnt, GFP_KERNEL); |
389 | if (!bbt) | |
390 | goto out; | |
e3644da7 | 391 | |
4bf527aa | 392 | err = mtdtest_scan_for_bad_eraseblocks(mtd, bbt, 0, ebcnt); |
e3644da7 AB |
393 | if (err) |
394 | goto out; | |
395 | ||
396 | use_offset = 0; | |
f5b8aa78 BB |
397 | use_len = mtd->oobavail; |
398 | use_len_max = mtd->oobavail; | |
e3644da7 AB |
399 | vary_offset = 0; |
400 | ||
401 | /* First test: write all OOB, read it back and verify */ | |
04810274 | 402 | pr_info("test 1 of 5\n"); |
e3644da7 | 403 | |
4bf527aa | 404 | err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt); |
e3644da7 AB |
405 | if (err) |
406 | goto out; | |
407 | ||
8dad0498 | 408 | prandom_seed_state(&rnd_state, 1); |
e3644da7 AB |
409 | err = write_whole_device(); |
410 | if (err) | |
411 | goto out; | |
412 | ||
8dad0498 | 413 | prandom_seed_state(&rnd_state, 1); |
e3644da7 AB |
414 | err = verify_all_eraseblocks(); |
415 | if (err) | |
416 | goto out; | |
417 | ||
418 | /* | |
419 | * Second test: write all OOB, a block at a time, read it back and | |
420 | * verify. | |
421 | */ | |
04810274 | 422 | pr_info("test 2 of 5\n"); |
e3644da7 | 423 | |
4bf527aa | 424 | err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt); |
e3644da7 AB |
425 | if (err) |
426 | goto out; | |
427 | ||
8dad0498 | 428 | prandom_seed_state(&rnd_state, 3); |
e3644da7 AB |
429 | err = write_whole_device(); |
430 | if (err) | |
431 | goto out; | |
432 | ||
433 | /* Check all eraseblocks */ | |
8dad0498 | 434 | prandom_seed_state(&rnd_state, 3); |
04810274 | 435 | pr_info("verifying all eraseblocks\n"); |
e3644da7 AB |
436 | for (i = 0; i < ebcnt; ++i) { |
437 | if (bbt[i]) | |
438 | continue; | |
439 | err = verify_eraseblock_in_one_go(i); | |
440 | if (err) | |
441 | goto out; | |
442 | if (i % 256 == 0) | |
04810274 | 443 | pr_info("verified up to eraseblock %u\n", i); |
2a6a28e7 RW |
444 | |
445 | err = mtdtest_relax(); | |
446 | if (err) | |
447 | goto out; | |
e3644da7 | 448 | } |
04810274 | 449 | pr_info("verified %u eraseblocks\n", i); |
e3644da7 AB |
450 | |
451 | /* | |
452 | * Third test: write OOB at varying offsets and lengths, read it back | |
453 | * and verify. | |
454 | */ | |
04810274 | 455 | pr_info("test 3 of 5\n"); |
e3644da7 | 456 | |
4bf527aa | 457 | err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt); |
e3644da7 AB |
458 | if (err) |
459 | goto out; | |
460 | ||
461 | /* Write all eraseblocks */ | |
462 | use_offset = 0; | |
f5b8aa78 BB |
463 | use_len = mtd->oobavail; |
464 | use_len_max = mtd->oobavail; | |
e3644da7 | 465 | vary_offset = 1; |
8dad0498 | 466 | prandom_seed_state(&rnd_state, 5); |
f54d6336 AM |
467 | |
468 | err = write_whole_device(); | |
469 | if (err) | |
470 | goto out; | |
e3644da7 AB |
471 | |
472 | /* Check all eraseblocks */ | |
473 | use_offset = 0; | |
f5b8aa78 BB |
474 | use_len = mtd->oobavail; |
475 | use_len_max = mtd->oobavail; | |
e3644da7 | 476 | vary_offset = 1; |
8dad0498 | 477 | prandom_seed_state(&rnd_state, 5); |
e3644da7 AB |
478 | err = verify_all_eraseblocks(); |
479 | if (err) | |
480 | goto out; | |
481 | ||
482 | use_offset = 0; | |
f5b8aa78 BB |
483 | use_len = mtd->oobavail; |
484 | use_len_max = mtd->oobavail; | |
e3644da7 AB |
485 | vary_offset = 0; |
486 | ||
487 | /* Fourth test: try to write off end of device */ | |
04810274 | 488 | pr_info("test 4 of 5\n"); |
e3644da7 | 489 | |
4bf527aa | 490 | err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt); |
e3644da7 AB |
491 | if (err) |
492 | goto out; | |
493 | ||
494 | addr0 = 0; | |
c6f7e7be | 495 | for (i = 0; i < ebcnt && bbt[i]; ++i) |
e3644da7 AB |
496 | addr0 += mtd->erasesize; |
497 | ||
498 | /* Attempt to write off end of OOB */ | |
0612b9dd | 499 | ops.mode = MTD_OPS_AUTO_OOB; |
e3644da7 AB |
500 | ops.len = 0; |
501 | ops.retlen = 0; | |
502 | ops.ooblen = 1; | |
503 | ops.oobretlen = 0; | |
f5b8aa78 | 504 | ops.ooboffs = mtd->oobavail; |
23d42494 | 505 | ops.datbuf = NULL; |
e3644da7 | 506 | ops.oobbuf = writebuf; |
04810274 VN |
507 | pr_info("attempting to start write past end of OOB\n"); |
508 | pr_info("an error is expected...\n"); | |
a2cc5ba0 | 509 | err = mtd_write_oob(mtd, addr0, &ops); |
e3644da7 | 510 | if (err) { |
04810274 | 511 | pr_info("error occurred as expected\n"); |
e3644da7 AB |
512 | err = 0; |
513 | } else { | |
04810274 | 514 | pr_err("error: can write past end of OOB\n"); |
e3644da7 AB |
515 | errcnt += 1; |
516 | } | |
517 | ||
518 | /* Attempt to read off end of OOB */ | |
0612b9dd | 519 | ops.mode = MTD_OPS_AUTO_OOB; |
e3644da7 AB |
520 | ops.len = 0; |
521 | ops.retlen = 0; | |
522 | ops.ooblen = 1; | |
523 | ops.oobretlen = 0; | |
f5b8aa78 | 524 | ops.ooboffs = mtd->oobavail; |
23d42494 | 525 | ops.datbuf = NULL; |
e3644da7 | 526 | ops.oobbuf = readbuf; |
04810274 VN |
527 | pr_info("attempting to start read past end of OOB\n"); |
528 | pr_info("an error is expected...\n"); | |
fd2819bb | 529 | err = mtd_read_oob(mtd, addr0, &ops); |
e3644da7 | 530 | if (err) { |
04810274 | 531 | pr_info("error occurred as expected\n"); |
e3644da7 AB |
532 | err = 0; |
533 | } else { | |
04810274 | 534 | pr_err("error: can read past end of OOB\n"); |
e3644da7 AB |
535 | errcnt += 1; |
536 | } | |
537 | ||
538 | if (bbt[ebcnt - 1]) | |
04810274 | 539 | pr_info("skipping end of device tests because last " |
e3644da7 AB |
540 | "block is bad\n"); |
541 | else { | |
542 | /* Attempt to write off end of device */ | |
0612b9dd | 543 | ops.mode = MTD_OPS_AUTO_OOB; |
e3644da7 AB |
544 | ops.len = 0; |
545 | ops.retlen = 0; | |
f5b8aa78 | 546 | ops.ooblen = mtd->oobavail + 1; |
e3644da7 AB |
547 | ops.oobretlen = 0; |
548 | ops.ooboffs = 0; | |
23d42494 | 549 | ops.datbuf = NULL; |
e3644da7 | 550 | ops.oobbuf = writebuf; |
04810274 VN |
551 | pr_info("attempting to write past end of device\n"); |
552 | pr_info("an error is expected...\n"); | |
a2cc5ba0 | 553 | err = mtd_write_oob(mtd, mtd->size - mtd->writesize, &ops); |
e3644da7 | 554 | if (err) { |
04810274 | 555 | pr_info("error occurred as expected\n"); |
e3644da7 AB |
556 | err = 0; |
557 | } else { | |
04810274 | 558 | pr_err("error: wrote past end of device\n"); |
e3644da7 AB |
559 | errcnt += 1; |
560 | } | |
561 | ||
562 | /* Attempt to read off end of device */ | |
0612b9dd | 563 | ops.mode = MTD_OPS_AUTO_OOB; |
e3644da7 AB |
564 | ops.len = 0; |
565 | ops.retlen = 0; | |
f5b8aa78 | 566 | ops.ooblen = mtd->oobavail + 1; |
e3644da7 AB |
567 | ops.oobretlen = 0; |
568 | ops.ooboffs = 0; | |
23d42494 | 569 | ops.datbuf = NULL; |
e3644da7 | 570 | ops.oobbuf = readbuf; |
04810274 VN |
571 | pr_info("attempting to read past end of device\n"); |
572 | pr_info("an error is expected...\n"); | |
fd2819bb | 573 | err = mtd_read_oob(mtd, mtd->size - mtd->writesize, &ops); |
e3644da7 | 574 | if (err) { |
04810274 | 575 | pr_info("error occurred as expected\n"); |
e3644da7 AB |
576 | err = 0; |
577 | } else { | |
04810274 | 578 | pr_err("error: read past end of device\n"); |
e3644da7 AB |
579 | errcnt += 1; |
580 | } | |
581 | ||
4bf527aa | 582 | err = mtdtest_erase_eraseblock(mtd, ebcnt - 1); |
e3644da7 AB |
583 | if (err) |
584 | goto out; | |
585 | ||
586 | /* Attempt to write off end of device */ | |
0612b9dd | 587 | ops.mode = MTD_OPS_AUTO_OOB; |
e3644da7 AB |
588 | ops.len = 0; |
589 | ops.retlen = 0; | |
f5b8aa78 | 590 | ops.ooblen = mtd->oobavail; |
e3644da7 AB |
591 | ops.oobretlen = 0; |
592 | ops.ooboffs = 1; | |
23d42494 | 593 | ops.datbuf = NULL; |
e3644da7 | 594 | ops.oobbuf = writebuf; |
04810274 VN |
595 | pr_info("attempting to write past end of device\n"); |
596 | pr_info("an error is expected...\n"); | |
a2cc5ba0 | 597 | err = mtd_write_oob(mtd, mtd->size - mtd->writesize, &ops); |
e3644da7 | 598 | if (err) { |
04810274 | 599 | pr_info("error occurred as expected\n"); |
e3644da7 AB |
600 | err = 0; |
601 | } else { | |
04810274 | 602 | pr_err("error: wrote past end of device\n"); |
e3644da7 AB |
603 | errcnt += 1; |
604 | } | |
605 | ||
606 | /* Attempt to read off end of device */ | |
0612b9dd | 607 | ops.mode = MTD_OPS_AUTO_OOB; |
e3644da7 AB |
608 | ops.len = 0; |
609 | ops.retlen = 0; | |
f5b8aa78 | 610 | ops.ooblen = mtd->oobavail; |
e3644da7 AB |
611 | ops.oobretlen = 0; |
612 | ops.ooboffs = 1; | |
23d42494 | 613 | ops.datbuf = NULL; |
e3644da7 | 614 | ops.oobbuf = readbuf; |
04810274 VN |
615 | pr_info("attempting to read past end of device\n"); |
616 | pr_info("an error is expected...\n"); | |
fd2819bb | 617 | err = mtd_read_oob(mtd, mtd->size - mtd->writesize, &ops); |
e3644da7 | 618 | if (err) { |
04810274 | 619 | pr_info("error occurred as expected\n"); |
e3644da7 AB |
620 | err = 0; |
621 | } else { | |
04810274 | 622 | pr_err("error: read past end of device\n"); |
e3644da7 AB |
623 | errcnt += 1; |
624 | } | |
625 | } | |
626 | ||
627 | /* Fifth test: write / read across block boundaries */ | |
04810274 | 628 | pr_info("test 5 of 5\n"); |
e3644da7 AB |
629 | |
630 | /* Erase all eraseblocks */ | |
4bf527aa | 631 | err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt); |
e3644da7 AB |
632 | if (err) |
633 | goto out; | |
634 | ||
635 | /* Write all eraseblocks */ | |
8dad0498 | 636 | prandom_seed_state(&rnd_state, 11); |
04810274 | 637 | pr_info("writing OOBs of whole device\n"); |
e3644da7 AB |
638 | for (i = 0; i < ebcnt - 1; ++i) { |
639 | int cnt = 2; | |
640 | int pg; | |
f5b8aa78 | 641 | size_t sz = mtd->oobavail; |
e3644da7 AB |
642 | if (bbt[i] || bbt[i + 1]) |
643 | continue; | |
1001ff7a | 644 | addr = (loff_t)(i + 1) * mtd->erasesize - mtd->writesize; |
be54f8f1 | 645 | prandom_bytes_state(&rnd_state, writebuf, sz * cnt); |
e3644da7 | 646 | for (pg = 0; pg < cnt; ++pg) { |
0612b9dd | 647 | ops.mode = MTD_OPS_AUTO_OOB; |
e3644da7 AB |
648 | ops.len = 0; |
649 | ops.retlen = 0; | |
650 | ops.ooblen = sz; | |
651 | ops.oobretlen = 0; | |
652 | ops.ooboffs = 0; | |
23d42494 | 653 | ops.datbuf = NULL; |
be54f8f1 | 654 | ops.oobbuf = writebuf + pg * sz; |
a2cc5ba0 | 655 | err = mtd_write_oob(mtd, addr, &ops); |
e3644da7 AB |
656 | if (err) |
657 | goto out; | |
658 | if (i % 256 == 0) | |
04810274 | 659 | pr_info("written up to eraseblock %u\n", i); |
2a6a28e7 RW |
660 | |
661 | err = mtdtest_relax(); | |
662 | if (err) | |
663 | goto out; | |
664 | ||
e3644da7 AB |
665 | addr += mtd->writesize; |
666 | } | |
667 | } | |
04810274 | 668 | pr_info("written %u eraseblocks\n", i); |
e3644da7 AB |
669 | |
670 | /* Check all eraseblocks */ | |
8dad0498 | 671 | prandom_seed_state(&rnd_state, 11); |
04810274 | 672 | pr_info("verifying all eraseblocks\n"); |
e3644da7 AB |
673 | for (i = 0; i < ebcnt - 1; ++i) { |
674 | if (bbt[i] || bbt[i + 1]) | |
675 | continue; | |
f5b8aa78 | 676 | prandom_bytes_state(&rnd_state, writebuf, mtd->oobavail * 2); |
1001ff7a | 677 | addr = (loff_t)(i + 1) * mtd->erasesize - mtd->writesize; |
0612b9dd | 678 | ops.mode = MTD_OPS_AUTO_OOB; |
e3644da7 AB |
679 | ops.len = 0; |
680 | ops.retlen = 0; | |
f5b8aa78 | 681 | ops.ooblen = mtd->oobavail * 2; |
e3644da7 AB |
682 | ops.oobretlen = 0; |
683 | ops.ooboffs = 0; | |
23d42494 | 684 | ops.datbuf = NULL; |
e3644da7 | 685 | ops.oobbuf = readbuf; |
fd2819bb | 686 | err = mtd_read_oob(mtd, addr, &ops); |
e3644da7 AB |
687 | if (err) |
688 | goto out; | |
5a66088b | 689 | if (memcmpshow(addr, readbuf, writebuf, |
f5b8aa78 | 690 | mtd->oobavail * 2)) { |
04810274 | 691 | pr_err("error: verify failed at %#llx\n", |
e3644da7 AB |
692 | (long long)addr); |
693 | errcnt += 1; | |
694 | if (errcnt > 1000) { | |
04810274 | 695 | pr_err("error: too many errors\n"); |
e3644da7 AB |
696 | goto out; |
697 | } | |
698 | } | |
699 | if (i % 256 == 0) | |
04810274 | 700 | pr_info("verified up to eraseblock %u\n", i); |
2a6a28e7 RW |
701 | |
702 | err = mtdtest_relax(); | |
703 | if (err) | |
704 | goto out; | |
e3644da7 | 705 | } |
04810274 | 706 | pr_info("verified %u eraseblocks\n", i); |
e3644da7 | 707 | |
04810274 | 708 | pr_info("finished with %d errors\n", errcnt); |
e3644da7 AB |
709 | out: |
710 | kfree(bbt); | |
711 | kfree(writebuf); | |
712 | kfree(readbuf); | |
713 | put_mtd_device(mtd); | |
714 | if (err) | |
04810274 | 715 | pr_info("error %d occurred\n", err); |
e3644da7 AB |
716 | printk(KERN_INFO "=================================================\n"); |
717 | return err; | |
718 | } | |
719 | module_init(mtd_oobtest_init); | |
720 | ||
721 | static void __exit mtd_oobtest_exit(void) | |
722 | { | |
723 | return; | |
724 | } | |
725 | module_exit(mtd_oobtest_exit); | |
726 | ||
727 | MODULE_DESCRIPTION("Out-of-band test module"); | |
728 | MODULE_AUTHOR("Adrian Hunter"); | |
729 | MODULE_LICENSE("GPL"); |