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