linux/drivers/mtd/tests/pagetest.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (C) 2006-2008 Nokia Corporation
   4 *
   5 * Test page 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 ": " fmt
  11
  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;
  25module_param(dev, int, S_IRUGO);
  26MODULE_PARM_DESC(dev, "MTD device number to use");
  27
  28static struct mtd_info *mtd;
  29static unsigned char *twopages;
  30static unsigned char *writebuf;
  31static unsigned char *boundary;
  32static unsigned char *bbt;
  33
  34static int pgsize;
  35static int bufsize;
  36static int ebcnt;
  37static int pgcnt;
  38static int errcnt;
  39static struct rnd_state rnd_state;
  40
  41static int write_eraseblock(int ebnum)
  42{
  43        loff_t addr = (loff_t)ebnum * mtd->erasesize;
  44
  45        prandom_bytes_state(&rnd_state, writebuf, mtd->erasesize);
  46        cond_resched();
  47        return mtdtest_write(mtd, addr, mtd->erasesize, writebuf);
  48}
  49
  50static int verify_eraseblock(int ebnum)
  51{
  52        uint32_t j;
  53        int err = 0, i;
  54        loff_t addr0, addrn;
  55        loff_t addr = (loff_t)ebnum * mtd->erasesize;
  56
  57        addr0 = 0;
  58        for (i = 0; i < ebcnt && bbt[i]; ++i)
  59                addr0 += mtd->erasesize;
  60
  61        addrn = mtd->size;
  62        for (i = 0; i < ebcnt && bbt[ebcnt - i - 1]; ++i)
  63                addrn -= mtd->erasesize;
  64
  65        prandom_bytes_state(&rnd_state, writebuf, mtd->erasesize);
  66        for (j = 0; j < pgcnt - 1; ++j, addr += pgsize) {
  67                /* Do a read to set the internal dataRAMs to different data */
  68                err = mtdtest_read(mtd, addr0, bufsize, twopages);
  69                if (err)
  70                        return err;
  71                err = mtdtest_read(mtd, addrn - bufsize, bufsize, twopages);
  72                if (err)
  73                        return err;
  74                memset(twopages, 0, bufsize);
  75                err = mtdtest_read(mtd, addr, bufsize, twopages);
  76                if (err)
  77                        break;
  78                if (memcmp(twopages, writebuf + (j * pgsize), bufsize)) {
  79                        pr_err("error: verify failed at %#llx\n",
  80                               (long long)addr);
  81                        errcnt += 1;
  82                }
  83        }
  84        /* Check boundary between eraseblocks */
  85        if (addr <= addrn - pgsize - pgsize && !bbt[ebnum + 1]) {
  86                struct rnd_state old_state = rnd_state;
  87
  88                /* Do a read to set the internal dataRAMs to different data */
  89                err = mtdtest_read(mtd, addr0, bufsize, twopages);
  90                if (err)
  91                        return err;
  92                err = mtdtest_read(mtd, addrn - bufsize, bufsize, twopages);
  93                if (err)
  94                        return err;
  95                memset(twopages, 0, bufsize);
  96                err = mtdtest_read(mtd, addr, bufsize, twopages);
  97                if (err)
  98                        return err;
  99                memcpy(boundary, writebuf + mtd->erasesize - pgsize, pgsize);
 100                prandom_bytes_state(&rnd_state, boundary + pgsize, pgsize);
 101                if (memcmp(twopages, boundary, bufsize)) {
 102                        pr_err("error: verify failed at %#llx\n",
 103                               (long long)addr);
 104                        errcnt += 1;
 105                }
 106                rnd_state = old_state;
 107        }
 108        return err;
 109}
 110
 111static int crosstest(void)
 112{
 113        int err = 0, i;
 114        loff_t addr, addr0, addrn;
 115        unsigned char *pp1, *pp2, *pp3, *pp4;
 116
 117        pr_info("crosstest\n");
 118        pp1 = kcalloc(pgsize, 4, GFP_KERNEL);
 119        if (!pp1)
 120                return -ENOMEM;
 121        pp2 = pp1 + pgsize;
 122        pp3 = pp2 + pgsize;
 123        pp4 = pp3 + pgsize;
 124
 125        addr0 = 0;
 126        for (i = 0; i < ebcnt && bbt[i]; ++i)
 127                addr0 += mtd->erasesize;
 128
 129        addrn = mtd->size;
 130        for (i = 0; i < ebcnt && bbt[ebcnt - i - 1]; ++i)
 131                addrn -= mtd->erasesize;
 132
 133        /* Read 2nd-to-last page to pp1 */
 134        addr = addrn - pgsize - pgsize;
 135        err = mtdtest_read(mtd, addr, pgsize, pp1);
 136        if (err) {
 137                kfree(pp1);
 138                return err;
 139        }
 140
 141        /* Read 3rd-to-last page to pp1 */
 142        addr = addrn - pgsize - pgsize - pgsize;
 143        err = mtdtest_read(mtd, addr, pgsize, pp1);
 144        if (err) {
 145                kfree(pp1);
 146                return err;
 147        }
 148
 149        /* Read first page to pp2 */
 150        addr = addr0;
 151        pr_info("reading page at %#llx\n", (long long)addr);
 152        err = mtdtest_read(mtd, addr, pgsize, pp2);
 153        if (err) {
 154                kfree(pp1);
 155                return err;
 156        }
 157
 158        /* Read last page to pp3 */
 159        addr = addrn - pgsize;
 160        pr_info("reading page at %#llx\n", (long long)addr);
 161        err = mtdtest_read(mtd, addr, pgsize, pp3);
 162        if (err) {
 163                kfree(pp1);
 164                return err;
 165        }
 166
 167        /* Read first page again to pp4 */
 168        addr = addr0;
 169        pr_info("reading page at %#llx\n", (long long)addr);
 170        err = mtdtest_read(mtd, addr, pgsize, pp4);
 171        if (err) {
 172                kfree(pp1);
 173                return err;
 174        }
 175
 176        /* pp2 and pp4 should be the same */
 177        pr_info("verifying pages read at %#llx match\n",
 178               (long long)addr0);
 179        if (memcmp(pp2, pp4, pgsize)) {
 180                pr_err("verify failed!\n");
 181                errcnt += 1;
 182        } else if (!err)
 183                pr_info("crosstest ok\n");
 184        kfree(pp1);
 185        return err;
 186}
 187
 188static int erasecrosstest(void)
 189{
 190        int err = 0, i, ebnum, ebnum2;
 191        loff_t addr0;
 192        char *readbuf = twopages;
 193
 194        pr_info("erasecrosstest\n");
 195
 196        ebnum = 0;
 197        addr0 = 0;
 198        for (i = 0; i < ebcnt && bbt[i]; ++i) {
 199                addr0 += mtd->erasesize;
 200                ebnum += 1;
 201        }
 202
 203        ebnum2 = ebcnt - 1;
 204        while (ebnum2 && bbt[ebnum2])
 205                ebnum2 -= 1;
 206
 207        pr_info("erasing block %d\n", ebnum);
 208        err = mtdtest_erase_eraseblock(mtd, ebnum);
 209        if (err)
 210                return err;
 211
 212        pr_info("writing 1st page of block %d\n", ebnum);
 213        prandom_bytes_state(&rnd_state, writebuf, pgsize);
 214        strcpy(writebuf, "There is no data like this!");
 215        err = mtdtest_write(mtd, addr0, pgsize, writebuf);
 216        if (err)
 217                return err;
 218
 219        pr_info("reading 1st page of block %d\n", ebnum);
 220        memset(readbuf, 0, pgsize);
 221        err = mtdtest_read(mtd, addr0, pgsize, readbuf);
 222        if (err)
 223                return err;
 224
 225        pr_info("verifying 1st page of block %d\n", ebnum);
 226        if (memcmp(writebuf, readbuf, pgsize)) {
 227                pr_err("verify failed!\n");
 228                errcnt += 1;
 229                return -1;
 230        }
 231
 232        pr_info("erasing block %d\n", ebnum);
 233        err = mtdtest_erase_eraseblock(mtd, ebnum);
 234        if (err)
 235                return err;
 236
 237        pr_info("writing 1st page of block %d\n", ebnum);
 238        prandom_bytes_state(&rnd_state, writebuf, pgsize);
 239        strcpy(writebuf, "There is no data like this!");
 240        err = mtdtest_write(mtd, addr0, pgsize, writebuf);
 241        if (err)
 242                return err;
 243
 244        pr_info("erasing block %d\n", ebnum2);
 245        err = mtdtest_erase_eraseblock(mtd, ebnum2);
 246        if (err)
 247                return err;
 248
 249        pr_info("reading 1st page of block %d\n", ebnum);
 250        memset(readbuf, 0, pgsize);
 251        err = mtdtest_read(mtd, addr0, pgsize, readbuf);
 252        if (err)
 253                return err;
 254
 255        pr_info("verifying 1st page of block %d\n", ebnum);
 256        if (memcmp(writebuf, readbuf, pgsize)) {
 257                pr_err("verify failed!\n");
 258                errcnt += 1;
 259                return -1;
 260        }
 261
 262        if (!err)
 263                pr_info("erasecrosstest ok\n");
 264        return err;
 265}
 266
 267static int erasetest(void)
 268{
 269        int err = 0, i, ebnum, ok = 1;
 270        loff_t addr0;
 271
 272        pr_info("erasetest\n");
 273
 274        ebnum = 0;
 275        addr0 = 0;
 276        for (i = 0; i < ebcnt && bbt[i]; ++i) {
 277                addr0 += mtd->erasesize;
 278                ebnum += 1;
 279        }
 280
 281        pr_info("erasing block %d\n", ebnum);
 282        err = mtdtest_erase_eraseblock(mtd, ebnum);
 283        if (err)
 284                return err;
 285
 286        pr_info("writing 1st page of block %d\n", ebnum);
 287        prandom_bytes_state(&rnd_state, writebuf, pgsize);
 288        err = mtdtest_write(mtd, addr0, pgsize, writebuf);
 289        if (err)
 290                return err;
 291
 292        pr_info("erasing block %d\n", ebnum);
 293        err = mtdtest_erase_eraseblock(mtd, ebnum);
 294        if (err)
 295                return err;
 296
 297        pr_info("reading 1st page of block %d\n", ebnum);
 298        err = mtdtest_read(mtd, addr0, pgsize, twopages);
 299        if (err)
 300                return err;
 301
 302        pr_info("verifying 1st page of block %d is all 0xff\n",
 303               ebnum);
 304        for (i = 0; i < pgsize; ++i)
 305                if (twopages[i] != 0xff) {
 306                        pr_err("verifying all 0xff failed at %d\n",
 307                               i);
 308                        errcnt += 1;
 309                        ok = 0;
 310                        break;
 311                }
 312
 313        if (ok && !err)
 314                pr_info("erasetest ok\n");
 315
 316        return err;
 317}
 318
 319static int __init mtd_pagetest_init(void)
 320{
 321        int err = 0;
 322        uint64_t tmp;
 323        uint32_t i;
 324
 325        printk(KERN_INFO "\n");
 326        printk(KERN_INFO "=================================================\n");
 327
 328        if (dev < 0) {
 329                pr_info("Please specify a valid mtd-device via module parameter\n");
 330                pr_crit("CAREFUL: This test wipes all data on the specified MTD device!\n");
 331                return -EINVAL;
 332        }
 333
 334        pr_info("MTD device: %d\n", dev);
 335
 336        mtd = get_mtd_device(NULL, dev);
 337        if (IS_ERR(mtd)) {
 338                err = PTR_ERR(mtd);
 339                pr_err("error: cannot get MTD device\n");
 340                return err;
 341        }
 342
 343        if (!mtd_type_is_nand(mtd)) {
 344                pr_info("this test requires NAND flash\n");
 345                goto out;
 346        }
 347
 348        tmp = mtd->size;
 349        do_div(tmp, mtd->erasesize);
 350        ebcnt = tmp;
 351        pgcnt = mtd->erasesize / mtd->writesize;
 352        pgsize = mtd->writesize;
 353
 354        pr_info("MTD device size %llu, eraseblock size %u, "
 355               "page size %u, count of eraseblocks %u, pages per "
 356               "eraseblock %u, OOB size %u\n",
 357               (unsigned long long)mtd->size, mtd->erasesize,
 358               pgsize, ebcnt, pgcnt, mtd->oobsize);
 359
 360        err = -ENOMEM;
 361        bufsize = pgsize * 2;
 362        writebuf = kmalloc(mtd->erasesize, GFP_KERNEL);
 363        if (!writebuf)
 364                goto out;
 365        twopages = kmalloc(bufsize, GFP_KERNEL);
 366        if (!twopages)
 367                goto out;
 368        boundary = kmalloc(bufsize, GFP_KERNEL);
 369        if (!boundary)
 370                goto out;
 371
 372        bbt = kzalloc(ebcnt, GFP_KERNEL);
 373        if (!bbt)
 374                goto out;
 375        err = mtdtest_scan_for_bad_eraseblocks(mtd, bbt, 0, ebcnt);
 376        if (err)
 377                goto out;
 378
 379        /* Erase all eraseblocks */
 380        pr_info("erasing whole device\n");
 381        err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt);
 382        if (err)
 383                goto out;
 384        pr_info("erased %u eraseblocks\n", ebcnt);
 385
 386        /* Write all eraseblocks */
 387        prandom_seed_state(&rnd_state, 1);
 388        pr_info("writing whole device\n");
 389        for (i = 0; i < ebcnt; ++i) {
 390                if (bbt[i])
 391                        continue;
 392                err = write_eraseblock(i);
 393                if (err)
 394                        goto out;
 395                if (i % 256 == 0)
 396                        pr_info("written up to eraseblock %u\n", i);
 397
 398                err = mtdtest_relax();
 399                if (err)
 400                        goto out;
 401        }
 402        pr_info("written %u eraseblocks\n", i);
 403
 404        /* Check all eraseblocks */
 405        prandom_seed_state(&rnd_state, 1);
 406        pr_info("verifying all eraseblocks\n");
 407        for (i = 0; i < ebcnt; ++i) {
 408                if (bbt[i])
 409                        continue;
 410                err = verify_eraseblock(i);
 411                if (err)
 412                        goto out;
 413                if (i % 256 == 0)
 414                        pr_info("verified up to eraseblock %u\n", i);
 415
 416                err = mtdtest_relax();
 417                if (err)
 418                        goto out;
 419        }
 420        pr_info("verified %u eraseblocks\n", i);
 421
 422        err = crosstest();
 423        if (err)
 424                goto out;
 425
 426        if (ebcnt > 1) {
 427                err = erasecrosstest();
 428                if (err)
 429                        goto out;
 430        } else {
 431                pr_info("skipping erasecrosstest, 2 erase blocks needed\n");
 432        }
 433
 434        err = erasetest();
 435        if (err)
 436                goto out;
 437
 438        pr_info("finished with %d errors\n", errcnt);
 439out:
 440
 441        kfree(bbt);
 442        kfree(boundary);
 443        kfree(twopages);
 444        kfree(writebuf);
 445        put_mtd_device(mtd);
 446        if (err)
 447                pr_info("error %d occurred\n", err);
 448        printk(KERN_INFO "=================================================\n");
 449        return err;
 450}
 451module_init(mtd_pagetest_init);
 452
 453static void __exit mtd_pagetest_exit(void)
 454{
 455        return;
 456}
 457module_exit(mtd_pagetest_exit);
 458
 459MODULE_DESCRIPTION("NAND page test");
 460MODULE_AUTHOR("Adrian Hunter");
 461MODULE_LICENSE("GPL");
 462