462 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2006-2008 Nokia Corporation4 *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 ": " 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;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