root/drivers/mtd/tests/pagetest.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. write_eraseblock
  2. verify_eraseblock
  3. crosstest
  4. erasecrosstest
  5. erasetest
  6. mtd_pagetest_init
  7. mtd_pagetest_exit

   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 
  24 static int dev = -EINVAL;
  25 module_param(dev, int, S_IRUGO);
  26 MODULE_PARM_DESC(dev, "MTD device number to use");
  27 
  28 static struct mtd_info *mtd;
  29 static unsigned char *twopages;
  30 static unsigned char *writebuf;
  31 static unsigned char *boundary;
  32 static unsigned char *bbt;
  33 
  34 static int pgsize;
  35 static int bufsize;
  36 static int ebcnt;
  37 static int pgcnt;
  38 static int errcnt;
  39 static struct rnd_state rnd_state;
  40 
  41 static 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 
  50 static 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 
 111 static 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 
 188 static 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 
 267 static 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 
 319 static 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);
 439 out:
 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 }
 451 module_init(mtd_pagetest_init);
 452 
 453 static void __exit mtd_pagetest_exit(void)
 454 {
 455         return;
 456 }
 457 module_exit(mtd_pagetest_exit);
 458 
 459 MODULE_DESCRIPTION("NAND page test");
 460 MODULE_AUTHOR("Adrian Hunter");
 461 MODULE_LICENSE("GPL");

/* [<][>][^][v][top][bottom][index][help] */