216 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2006-2008 Nokia Corporation4 *5 * Check MTD device read.6 *7 * Author: Adrian Hunter <ext-adrian.hunter@nokia.com>8 */9 10#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt11 12#include <linux/init.h>13#include <linux/module.h>14#include <linux/moduleparam.h>15#include <linux/err.h>16#include <linux/mtd/mtd.h>17#include <linux/slab.h>18#include <linux/sched.h>19 20#include "mtd_test.h"21 22static int dev = -EINVAL;23module_param(dev, int, S_IRUGO);24MODULE_PARM_DESC(dev, "MTD device number to use");25 26static struct mtd_info *mtd;27static unsigned char *iobuf;28static unsigned char *iobuf1;29static unsigned char *bbt;30 31static int pgsize;32static int ebcnt;33static int pgcnt;34 35static int read_eraseblock_by_page(int ebnum)36{37 int i, ret, err = 0;38 loff_t addr = (loff_t)ebnum * mtd->erasesize;39 void *buf = iobuf;40 void *oobbuf = iobuf1;41 42 for (i = 0; i < pgcnt; i++) {43 memset(buf, 0 , pgsize);44 ret = mtdtest_read(mtd, addr, pgsize, buf);45 if (ret) {46 if (!err)47 err = ret;48 }49 if (mtd->oobsize) {50 struct mtd_oob_ops ops = { };51 52 ops.mode = MTD_OPS_PLACE_OOB;53 ops.len = 0;54 ops.retlen = 0;55 ops.ooblen = mtd->oobsize;56 ops.oobretlen = 0;57 ops.ooboffs = 0;58 ops.datbuf = NULL;59 ops.oobbuf = oobbuf;60 ret = mtd_read_oob(mtd, addr, &ops);61 if ((ret && !mtd_is_bitflip(ret)) ||62 ops.oobretlen != mtd->oobsize) {63 pr_err("error: read oob failed at "64 "%#llx\n", (long long)addr);65 if (!err)66 err = ret;67 if (!err)68 err = -EINVAL;69 }70 oobbuf += mtd->oobsize;71 }72 addr += pgsize;73 buf += pgsize;74 }75 76 return err;77}78 79static void dump_eraseblock(int ebnum)80{81 int i, j, n;82 char line[128];83 int pg, oob;84 85 pr_info("dumping eraseblock %d\n", ebnum);86 n = mtd->erasesize;87 for (i = 0; i < n;) {88 char *p = line;89 90 p += sprintf(p, "%05x: ", i);91 for (j = 0; j < 32 && i < n; j++, i++)92 p += sprintf(p, "%02x", (unsigned int)iobuf[i]);93 printk(KERN_CRIT "%s\n", line);94 cond_resched();95 }96 if (!mtd->oobsize)97 return;98 pr_info("dumping oob from eraseblock %d\n", ebnum);99 n = mtd->oobsize;100 for (pg = 0, i = 0; pg < pgcnt; pg++)101 for (oob = 0; oob < n;) {102 char *p = line;103 104 p += sprintf(p, "%05x: ", i);105 for (j = 0; j < 32 && oob < n; j++, oob++, i++)106 p += sprintf(p, "%02x",107 (unsigned int)iobuf1[i]);108 printk(KERN_CRIT "%s\n", line);109 cond_resched();110 }111}112 113static int __init mtd_readtest_init(void)114{115 uint64_t tmp;116 int err, i;117 118 printk(KERN_INFO "\n");119 printk(KERN_INFO "=================================================\n");120 121 if (dev < 0) {122 pr_info("Please specify a valid mtd-device via module parameter\n");123 return -EINVAL;124 }125 126 pr_info("MTD device: %d\n", dev);127 128 mtd = get_mtd_device(NULL, dev);129 if (IS_ERR(mtd)) {130 err = PTR_ERR(mtd);131 pr_err("error: Cannot get MTD device\n");132 return err;133 }134 135 if (mtd->writesize == 1) {136 pr_info("not NAND flash, assume page size is 512 "137 "bytes.\n");138 pgsize = 512;139 } else140 pgsize = mtd->writesize;141 142 tmp = mtd->size;143 do_div(tmp, mtd->erasesize);144 ebcnt = tmp;145 pgcnt = mtd->erasesize / pgsize;146 147 pr_info("MTD device size %llu, eraseblock size %u, "148 "page size %u, count of eraseblocks %u, pages per "149 "eraseblock %u, OOB size %u\n",150 (unsigned long long)mtd->size, mtd->erasesize,151 pgsize, ebcnt, pgcnt, mtd->oobsize);152 153 err = -ENOMEM;154 iobuf = kmalloc(mtd->erasesize, GFP_KERNEL);155 if (!iobuf)156 goto out;157 iobuf1 = kmalloc(mtd->erasesize, GFP_KERNEL);158 if (!iobuf1)159 goto out;160 161 bbt = kzalloc(ebcnt, GFP_KERNEL);162 if (!bbt)163 goto out;164 err = mtdtest_scan_for_bad_eraseblocks(mtd, bbt, 0, ebcnt);165 if (err)166 goto out;167 168 /* Read all eraseblocks 1 page at a time */169 pr_info("testing page read\n");170 for (i = 0; i < ebcnt; ++i) {171 int ret;172 173 if (bbt[i])174 continue;175 ret = read_eraseblock_by_page(i);176 if (ret) {177 dump_eraseblock(i);178 if (!err)179 err = ret;180 }181 182 ret = mtdtest_relax();183 if (ret) {184 err = ret;185 goto out;186 }187 }188 189 if (err)190 pr_info("finished with errors\n");191 else192 pr_info("finished\n");193 194out:195 196 kfree(iobuf);197 kfree(iobuf1);198 kfree(bbt);199 put_mtd_device(mtd);200 if (err)201 pr_info("error %d occurred\n", err);202 printk(KERN_INFO "=================================================\n");203 return err;204}205module_init(mtd_readtest_init);206 207static void __exit mtd_readtest_exit(void)208{209 return;210}211module_exit(mtd_readtest_exit);212 213MODULE_DESCRIPTION("Read test module");214MODULE_AUTHOR("Adrian Hunter");215MODULE_LICENSE("GPL");216