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