1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (C) 2006-2008 Nokia Corporation 4 * 5 * Check MTD device read. 6 * 7 * Author: Adrian Hunter <ext-adrian.hunter@nokia.com> 8 */ 9 10 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 11 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 22 static int dev = -EINVAL; 23 module_param(dev, int, S_IRUGO); 24 MODULE_PARM_DESC(dev, "MTD device number to use"); 25 26 static struct mtd_info *mtd; 27 static unsigned char *iobuf; 28 static unsigned char *iobuf1; 29 static unsigned char *bbt; 30 31 static int pgsize; 32 static int ebcnt; 33 static int pgcnt; 34 35 static 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 79 static 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 113 static 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 } else 140 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 else 192 pr_info("finished\n"); 193 194 out: 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 } 205 module_init(mtd_readtest_init); 206 207 static void __exit mtd_readtest_exit(void) 208 { 209 return; 210 } 211 module_exit(mtd_readtest_exit); 212 213 MODULE_DESCRIPTION("Read test module"); 214 MODULE_AUTHOR("Adrian Hunter"); 215 MODULE_LICENSE("GPL"); 216