blob: 5e061186eab181c927d41568dd73b7cd9e486b25 [file] [log] [blame]
Artem Bityutskiye3644da2008-12-08 13:33:29 +02001/*
2 * Copyright (C) 2006-2008 Nokia Corporation
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 2 as published by
6 * the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
11 * more details.
12 *
13 * You should have received a copy of the GNU General Public License along with
14 * this program; see the file COPYING. If not, write to the Free Software
15 * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
16 *
17 * Test OOB read and write on MTD device.
18 *
19 * Author: Adrian Hunter <ext-adrian.hunter@nokia.com>
20 */
21
Vikram Narayanan04810272012-10-10 23:12:02 +053022#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
23
Artem Bityutskiye3644da2008-12-08 13:33:29 +020024#include <asm/div64.h>
25#include <linux/init.h>
26#include <linux/module.h>
27#include <linux/moduleparam.h>
28#include <linux/err.h>
29#include <linux/mtd/mtd.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090030#include <linux/slab.h>
Artem Bityutskiye3644da2008-12-08 13:33:29 +020031#include <linux/sched.h>
Akinobu Mita8dad0492013-02-27 17:05:33 -080032#include <linux/random.h>
Artem Bityutskiye3644da2008-12-08 13:33:29 +020033
Akinobu Mita4bf527a2013-08-03 18:52:09 +090034#include "mtd_test.h"
35
Wolfram Sang74060602011-10-30 00:11:53 +020036static int dev = -EINVAL;
Roger Quadrosafc0ea12014-10-21 16:53:28 +030037static int bitflip_limit;
Artem Bityutskiye3644da2008-12-08 13:33:29 +020038module_param(dev, int, S_IRUGO);
39MODULE_PARM_DESC(dev, "MTD device number to use");
Roger Quadrosafc0ea12014-10-21 16:53:28 +030040module_param(bitflip_limit, int, S_IRUGO);
41MODULE_PARM_DESC(bitflip_limit, "Max. allowed bitflips per page");
Artem Bityutskiye3644da2008-12-08 13:33:29 +020042
43static struct mtd_info *mtd;
44static unsigned char *readbuf;
45static unsigned char *writebuf;
46static unsigned char *bbt;
47
48static int ebcnt;
49static int pgcnt;
50static int errcnt;
51static int use_offset;
52static int use_len;
53static int use_len_max;
54static int vary_offset;
Akinobu Mita8dad0492013-02-27 17:05:33 -080055static struct rnd_state rnd_state;
Artem Bityutskiye3644da2008-12-08 13:33:29 +020056
Artem Bityutskiye3644da2008-12-08 13:33:29 +020057static void do_vary_offset(void)
58{
59 use_len -= 1;
60 if (use_len < 1) {
61 use_offset += 1;
62 if (use_offset >= use_len_max)
63 use_offset = 0;
64 use_len = use_len_max - use_offset;
65 }
66}
67
68static int write_eraseblock(int ebnum)
69{
70 int i;
71 struct mtd_oob_ops ops;
72 int err = 0;
73 loff_t addr = ebnum * mtd->erasesize;
74
Akinobu Mitabe54f8f2014-03-08 00:24:10 +090075 prandom_bytes_state(&rnd_state, writebuf, use_len_max * pgcnt);
Artem Bityutskiye3644da2008-12-08 13:33:29 +020076 for (i = 0; i < pgcnt; ++i, addr += mtd->writesize) {
Brian Norris0612b9d2011-08-30 18:45:40 -070077 ops.mode = MTD_OPS_AUTO_OOB;
Artem Bityutskiye3644da2008-12-08 13:33:29 +020078 ops.len = 0;
79 ops.retlen = 0;
80 ops.ooblen = use_len;
81 ops.oobretlen = 0;
82 ops.ooboffs = use_offset;
Hannes Eder23d42492009-03-05 20:15:01 +010083 ops.datbuf = NULL;
Akinobu Mitabe54f8f2014-03-08 00:24:10 +090084 ops.oobbuf = writebuf + (use_len_max * i) + use_offset;
Artem Bityutskiya2cc5ba2011-12-23 18:29:55 +020085 err = mtd_write_oob(mtd, addr, &ops);
Artem Bityutskiye3644da2008-12-08 13:33:29 +020086 if (err || ops.oobretlen != use_len) {
Vikram Narayanan04810272012-10-10 23:12:02 +053087 pr_err("error: writeoob failed at %#llx\n",
Artem Bityutskiye3644da2008-12-08 13:33:29 +020088 (long long)addr);
Vikram Narayanan04810272012-10-10 23:12:02 +053089 pr_err("error: use_len %d, use_offset %d\n",
Artem Bityutskiye3644da2008-12-08 13:33:29 +020090 use_len, use_offset);
91 errcnt += 1;
92 return err ? err : -1;
93 }
94 if (vary_offset)
95 do_vary_offset();
96 }
97
98 return err;
99}
100
101static int write_whole_device(void)
102{
103 int err;
104 unsigned int i;
105
Vikram Narayanan04810272012-10-10 23:12:02 +0530106 pr_info("writing OOBs of whole device\n");
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200107 for (i = 0; i < ebcnt; ++i) {
108 if (bbt[i])
109 continue;
110 err = write_eraseblock(i);
111 if (err)
112 return err;
113 if (i % 256 == 0)
Vikram Narayanan04810272012-10-10 23:12:02 +0530114 pr_info("written up to eraseblock %u\n", i);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200115 cond_resched();
116 }
Vikram Narayanan04810272012-10-10 23:12:02 +0530117 pr_info("written %u eraseblocks\n", i);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200118 return 0;
119}
120
Roger Quadrosafc0ea12014-10-21 16:53:28 +0300121/*
122 * Display the address, offset and data bytes at comparison failure.
123 * Return number of bitflips encountered.
124 */
125static size_t memcmpshow(loff_t addr, const void *cs, const void *ct, size_t count)
Roger Quadros5a660882014-10-21 16:53:27 +0300126{
127 const unsigned char *su1, *su2;
128 int res;
Roger Quadros5a660882014-10-21 16:53:27 +0300129 size_t i = 0;
Roger Quadrosafc0ea12014-10-21 16:53:28 +0300130 size_t bitflips = 0;
Roger Quadros5a660882014-10-21 16:53:27 +0300131
132 for (su1 = cs, su2 = ct; 0 < count; ++su1, ++su2, count--, i++) {
133 res = *su1 ^ *su2;
134 if (res) {
Brian Norris806b6ef2014-11-20 01:26:51 -0800135 pr_info("error @addr[0x%lx:0x%zx] 0x%x -> 0x%x diff 0x%x\n",
Roger Quadros5a660882014-10-21 16:53:27 +0300136 (unsigned long)addr, i, *su1, *su2, res);
Roger Quadrosafc0ea12014-10-21 16:53:28 +0300137 bitflips += hweight8(res);
Roger Quadros5a660882014-10-21 16:53:27 +0300138 }
139 }
140
Roger Quadrosafc0ea12014-10-21 16:53:28 +0300141 return bitflips;
Roger Quadros5a660882014-10-21 16:53:27 +0300142}
143
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200144static int verify_eraseblock(int ebnum)
145{
146 int i;
147 struct mtd_oob_ops ops;
148 int err = 0;
Brian Norris1001ff72014-07-21 19:07:12 -0700149 loff_t addr = (loff_t)ebnum * mtd->erasesize;
Roger Quadrosafc0ea12014-10-21 16:53:28 +0300150 size_t bitflips;
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200151
Akinobu Mitabe54f8f2014-03-08 00:24:10 +0900152 prandom_bytes_state(&rnd_state, writebuf, use_len_max * pgcnt);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200153 for (i = 0; i < pgcnt; ++i, addr += mtd->writesize) {
Brian Norris0612b9d2011-08-30 18:45:40 -0700154 ops.mode = MTD_OPS_AUTO_OOB;
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200155 ops.len = 0;
156 ops.retlen = 0;
157 ops.ooblen = use_len;
158 ops.oobretlen = 0;
159 ops.ooboffs = use_offset;
Hannes Eder23d42492009-03-05 20:15:01 +0100160 ops.datbuf = NULL;
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200161 ops.oobbuf = readbuf;
Artem Bityutskiyfd2819b2011-12-23 18:27:05 +0200162 err = mtd_read_oob(mtd, addr, &ops);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200163 if (err || ops.oobretlen != use_len) {
Vikram Narayanan04810272012-10-10 23:12:02 +0530164 pr_err("error: readoob failed at %#llx\n",
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200165 (long long)addr);
166 errcnt += 1;
167 return err ? err : -1;
168 }
Roger Quadrosafc0ea12014-10-21 16:53:28 +0300169
170 bitflips = memcmpshow(addr, readbuf,
171 writebuf + (use_len_max * i) + use_offset,
172 use_len);
173 if (bitflips > bitflip_limit) {
Vikram Narayanan04810272012-10-10 23:12:02 +0530174 pr_err("error: verify failed at %#llx\n",
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200175 (long long)addr);
176 errcnt += 1;
177 if (errcnt > 1000) {
Vikram Narayanan04810272012-10-10 23:12:02 +0530178 pr_err("error: too many errors\n");
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200179 return -1;
180 }
Roger Quadrosafc0ea12014-10-21 16:53:28 +0300181 } else if (bitflips) {
182 pr_info("ignoring error as within bitflip_limit\n");
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200183 }
Roger Quadrosafc0ea12014-10-21 16:53:28 +0300184
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200185 if (use_offset != 0 || use_len < mtd->ecclayout->oobavail) {
186 int k;
187
Brian Norris0612b9d2011-08-30 18:45:40 -0700188 ops.mode = MTD_OPS_AUTO_OOB;
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200189 ops.len = 0;
190 ops.retlen = 0;
191 ops.ooblen = mtd->ecclayout->oobavail;
192 ops.oobretlen = 0;
193 ops.ooboffs = 0;
Hannes Eder23d42492009-03-05 20:15:01 +0100194 ops.datbuf = NULL;
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200195 ops.oobbuf = readbuf;
Artem Bityutskiyfd2819b2011-12-23 18:27:05 +0200196 err = mtd_read_oob(mtd, addr, &ops);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200197 if (err || ops.oobretlen != mtd->ecclayout->oobavail) {
Vikram Narayanan04810272012-10-10 23:12:02 +0530198 pr_err("error: readoob failed at %#llx\n",
199 (long long)addr);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200200 errcnt += 1;
201 return err ? err : -1;
202 }
Roger Quadrosafc0ea12014-10-21 16:53:28 +0300203 bitflips = memcmpshow(addr, readbuf + use_offset,
204 writebuf + (use_len_max * i) + use_offset,
205 use_len);
206 if (bitflips > bitflip_limit) {
Vikram Narayanan04810272012-10-10 23:12:02 +0530207 pr_err("error: verify failed at %#llx\n",
208 (long long)addr);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200209 errcnt += 1;
210 if (errcnt > 1000) {
Vikram Narayanan04810272012-10-10 23:12:02 +0530211 pr_err("error: too many errors\n");
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200212 return -1;
213 }
Roger Quadrosafc0ea12014-10-21 16:53:28 +0300214 } else if (bitflips) {
215 pr_info("ignoring error as within bitflip_limit\n");
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200216 }
Roger Quadrosafc0ea12014-10-21 16:53:28 +0300217
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200218 for (k = 0; k < use_offset; ++k)
219 if (readbuf[k] != 0xff) {
Vikram Narayanan04810272012-10-10 23:12:02 +0530220 pr_err("error: verify 0xff "
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200221 "failed at %#llx\n",
222 (long long)addr);
223 errcnt += 1;
224 if (errcnt > 1000) {
Vikram Narayanan04810272012-10-10 23:12:02 +0530225 pr_err("error: too "
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200226 "many errors\n");
227 return -1;
228 }
229 }
230 for (k = use_offset + use_len;
231 k < mtd->ecclayout->oobavail; ++k)
232 if (readbuf[k] != 0xff) {
Vikram Narayanan04810272012-10-10 23:12:02 +0530233 pr_err("error: verify 0xff "
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200234 "failed at %#llx\n",
235 (long long)addr);
236 errcnt += 1;
237 if (errcnt > 1000) {
Vikram Narayanan04810272012-10-10 23:12:02 +0530238 pr_err("error: too "
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200239 "many errors\n");
240 return -1;
241 }
242 }
243 }
244 if (vary_offset)
245 do_vary_offset();
246 }
247 return err;
248}
249
250static int verify_eraseblock_in_one_go(int ebnum)
251{
252 struct mtd_oob_ops ops;
253 int err = 0;
Brian Norris1001ff72014-07-21 19:07:12 -0700254 loff_t addr = (loff_t)ebnum * mtd->erasesize;
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200255 size_t len = mtd->ecclayout->oobavail * pgcnt;
Roger Quadrosafc0ea12014-10-21 16:53:28 +0300256 size_t oobavail = mtd->ecclayout->oobavail;
257 size_t bitflips;
258 int i;
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200259
Akinobu Mita8dad0492013-02-27 17:05:33 -0800260 prandom_bytes_state(&rnd_state, writebuf, len);
Brian Norris0612b9d2011-08-30 18:45:40 -0700261 ops.mode = MTD_OPS_AUTO_OOB;
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200262 ops.len = 0;
263 ops.retlen = 0;
264 ops.ooblen = len;
265 ops.oobretlen = 0;
266 ops.ooboffs = 0;
Hannes Eder23d42492009-03-05 20:15:01 +0100267 ops.datbuf = NULL;
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200268 ops.oobbuf = readbuf;
Roger Quadrosafc0ea12014-10-21 16:53:28 +0300269
270 /* read entire block's OOB at one go */
Artem Bityutskiyfd2819b2011-12-23 18:27:05 +0200271 err = mtd_read_oob(mtd, addr, &ops);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200272 if (err || ops.oobretlen != len) {
Vikram Narayanan04810272012-10-10 23:12:02 +0530273 pr_err("error: readoob failed at %#llx\n",
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200274 (long long)addr);
275 errcnt += 1;
276 return err ? err : -1;
277 }
Roger Quadrosafc0ea12014-10-21 16:53:28 +0300278
279 /* verify one page OOB at a time for bitflip per page limit check */
280 for (i = 0; i < pgcnt; ++i, addr += mtd->writesize) {
281 bitflips = memcmpshow(addr, readbuf + (i * oobavail),
282 writebuf + (i * oobavail), oobavail);
283 if (bitflips > bitflip_limit) {
284 pr_err("error: verify failed at %#llx\n",
285 (long long)addr);
286 errcnt += 1;
287 if (errcnt > 1000) {
288 pr_err("error: too many errors\n");
289 return -1;
290 }
291 } else if (bitflips) {
292 pr_info("ignoring error as within bitflip_limit\n");
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200293 }
294 }
295
296 return err;
297}
298
299static int verify_all_eraseblocks(void)
300{
301 int err;
302 unsigned int i;
303
Vikram Narayanan04810272012-10-10 23:12:02 +0530304 pr_info("verifying all eraseblocks\n");
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200305 for (i = 0; i < ebcnt; ++i) {
306 if (bbt[i])
307 continue;
308 err = verify_eraseblock(i);
309 if (err)
310 return err;
311 if (i % 256 == 0)
Vikram Narayanan04810272012-10-10 23:12:02 +0530312 pr_info("verified up to eraseblock %u\n", i);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200313 cond_resched();
314 }
Vikram Narayanan04810272012-10-10 23:12:02 +0530315 pr_info("verified %u eraseblocks\n", i);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200316 return 0;
317}
318
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200319static int __init mtd_oobtest_init(void)
320{
321 int err = 0;
322 unsigned int i;
323 uint64_t tmp;
324 struct mtd_oob_ops ops;
325 loff_t addr = 0, addr0;
326
327 printk(KERN_INFO "\n");
328 printk(KERN_INFO "=================================================\n");
Wolfram Sang74060602011-10-30 00:11:53 +0200329
330 if (dev < 0) {
Masanari Iida064a7692012-11-09 23:20:58 +0900331 pr_info("Please specify a valid mtd-device via module parameter\n");
332 pr_crit("CAREFUL: This test wipes all data on the specified MTD device!\n");
Wolfram Sang74060602011-10-30 00:11:53 +0200333 return -EINVAL;
334 }
335
Vikram Narayanan04810272012-10-10 23:12:02 +0530336 pr_info("MTD device: %d\n", dev);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200337
338 mtd = get_mtd_device(NULL, dev);
339 if (IS_ERR(mtd)) {
340 err = PTR_ERR(mtd);
Vikram Narayanan04810272012-10-10 23:12:02 +0530341 pr_err("error: cannot get MTD device\n");
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200342 return err;
343 }
344
Huang Shijie818b9732013-09-25 14:58:17 +0800345 if (!mtd_type_is_nand(mtd)) {
Vikram Narayanan04810272012-10-10 23:12:02 +0530346 pr_info("this test requires NAND flash\n");
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200347 goto out;
348 }
349
350 tmp = mtd->size;
351 do_div(tmp, mtd->erasesize);
352 ebcnt = tmp;
353 pgcnt = mtd->erasesize / mtd->writesize;
354
Vikram Narayanan04810272012-10-10 23:12:02 +0530355 pr_info("MTD device size %llu, eraseblock size %u, "
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200356 "page size %u, count of eraseblocks %u, pages per "
357 "eraseblock %u, OOB size %u\n",
358 (unsigned long long)mtd->size, mtd->erasesize,
359 mtd->writesize, ebcnt, pgcnt, mtd->oobsize);
360
361 err = -ENOMEM;
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200362 readbuf = kmalloc(mtd->erasesize, GFP_KERNEL);
Brian Norris33777e62013-05-02 14:18:51 -0700363 if (!readbuf)
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200364 goto out;
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200365 writebuf = kmalloc(mtd->erasesize, GFP_KERNEL);
Brian Norris33777e62013-05-02 14:18:51 -0700366 if (!writebuf)
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200367 goto out;
Akinobu Mita4bf527a2013-08-03 18:52:09 +0900368 bbt = kzalloc(ebcnt, GFP_KERNEL);
369 if (!bbt)
370 goto out;
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200371
Akinobu Mita4bf527a2013-08-03 18:52:09 +0900372 err = mtdtest_scan_for_bad_eraseblocks(mtd, bbt, 0, ebcnt);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200373 if (err)
374 goto out;
375
376 use_offset = 0;
377 use_len = mtd->ecclayout->oobavail;
378 use_len_max = mtd->ecclayout->oobavail;
379 vary_offset = 0;
380
381 /* First test: write all OOB, read it back and verify */
Vikram Narayanan04810272012-10-10 23:12:02 +0530382 pr_info("test 1 of 5\n");
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200383
Akinobu Mita4bf527a2013-08-03 18:52:09 +0900384 err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200385 if (err)
386 goto out;
387
Akinobu Mita8dad0492013-02-27 17:05:33 -0800388 prandom_seed_state(&rnd_state, 1);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200389 err = write_whole_device();
390 if (err)
391 goto out;
392
Akinobu Mita8dad0492013-02-27 17:05:33 -0800393 prandom_seed_state(&rnd_state, 1);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200394 err = verify_all_eraseblocks();
395 if (err)
396 goto out;
397
398 /*
399 * Second test: write all OOB, a block at a time, read it back and
400 * verify.
401 */
Vikram Narayanan04810272012-10-10 23:12:02 +0530402 pr_info("test 2 of 5\n");
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200403
Akinobu Mita4bf527a2013-08-03 18:52:09 +0900404 err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200405 if (err)
406 goto out;
407
Akinobu Mita8dad0492013-02-27 17:05:33 -0800408 prandom_seed_state(&rnd_state, 3);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200409 err = write_whole_device();
410 if (err)
411 goto out;
412
413 /* Check all eraseblocks */
Akinobu Mita8dad0492013-02-27 17:05:33 -0800414 prandom_seed_state(&rnd_state, 3);
Vikram Narayanan04810272012-10-10 23:12:02 +0530415 pr_info("verifying all eraseblocks\n");
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200416 for (i = 0; i < ebcnt; ++i) {
417 if (bbt[i])
418 continue;
419 err = verify_eraseblock_in_one_go(i);
420 if (err)
421 goto out;
422 if (i % 256 == 0)
Vikram Narayanan04810272012-10-10 23:12:02 +0530423 pr_info("verified up to eraseblock %u\n", i);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200424 cond_resched();
425 }
Vikram Narayanan04810272012-10-10 23:12:02 +0530426 pr_info("verified %u eraseblocks\n", i);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200427
428 /*
429 * Third test: write OOB at varying offsets and lengths, read it back
430 * and verify.
431 */
Vikram Narayanan04810272012-10-10 23:12:02 +0530432 pr_info("test 3 of 5\n");
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200433
Akinobu Mita4bf527a2013-08-03 18:52:09 +0900434 err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200435 if (err)
436 goto out;
437
438 /* Write all eraseblocks */
439 use_offset = 0;
440 use_len = mtd->ecclayout->oobavail;
441 use_len_max = mtd->ecclayout->oobavail;
442 vary_offset = 1;
Akinobu Mita8dad0492013-02-27 17:05:33 -0800443 prandom_seed_state(&rnd_state, 5);
Akinobu Mitaf54d6332009-10-09 18:43:52 +0900444
445 err = write_whole_device();
446 if (err)
447 goto out;
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200448
449 /* Check all eraseblocks */
450 use_offset = 0;
451 use_len = mtd->ecclayout->oobavail;
452 use_len_max = mtd->ecclayout->oobavail;
453 vary_offset = 1;
Akinobu Mita8dad0492013-02-27 17:05:33 -0800454 prandom_seed_state(&rnd_state, 5);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200455 err = verify_all_eraseblocks();
456 if (err)
457 goto out;
458
459 use_offset = 0;
460 use_len = mtd->ecclayout->oobavail;
461 use_len_max = mtd->ecclayout->oobavail;
462 vary_offset = 0;
463
464 /* Fourth test: try to write off end of device */
Vikram Narayanan04810272012-10-10 23:12:02 +0530465 pr_info("test 4 of 5\n");
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200466
Akinobu Mita4bf527a2013-08-03 18:52:09 +0900467 err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200468 if (err)
469 goto out;
470
471 addr0 = 0;
Roel Kluinc6f7e7b2009-07-31 16:21:01 +0200472 for (i = 0; i < ebcnt && bbt[i]; ++i)
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200473 addr0 += mtd->erasesize;
474
475 /* Attempt to write off end of OOB */
Brian Norris0612b9d2011-08-30 18:45:40 -0700476 ops.mode = MTD_OPS_AUTO_OOB;
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200477 ops.len = 0;
478 ops.retlen = 0;
479 ops.ooblen = 1;
480 ops.oobretlen = 0;
481 ops.ooboffs = mtd->ecclayout->oobavail;
Hannes Eder23d42492009-03-05 20:15:01 +0100482 ops.datbuf = NULL;
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200483 ops.oobbuf = writebuf;
Vikram Narayanan04810272012-10-10 23:12:02 +0530484 pr_info("attempting to start write past end of OOB\n");
485 pr_info("an error is expected...\n");
Artem Bityutskiya2cc5ba2011-12-23 18:29:55 +0200486 err = mtd_write_oob(mtd, addr0, &ops);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200487 if (err) {
Vikram Narayanan04810272012-10-10 23:12:02 +0530488 pr_info("error occurred as expected\n");
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200489 err = 0;
490 } else {
Vikram Narayanan04810272012-10-10 23:12:02 +0530491 pr_err("error: can write past end of OOB\n");
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200492 errcnt += 1;
493 }
494
495 /* Attempt to read off end of OOB */
Brian Norris0612b9d2011-08-30 18:45:40 -0700496 ops.mode = MTD_OPS_AUTO_OOB;
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200497 ops.len = 0;
498 ops.retlen = 0;
499 ops.ooblen = 1;
500 ops.oobretlen = 0;
501 ops.ooboffs = mtd->ecclayout->oobavail;
Hannes Eder23d42492009-03-05 20:15:01 +0100502 ops.datbuf = NULL;
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200503 ops.oobbuf = readbuf;
Vikram Narayanan04810272012-10-10 23:12:02 +0530504 pr_info("attempting to start read past end of OOB\n");
505 pr_info("an error is expected...\n");
Artem Bityutskiyfd2819b2011-12-23 18:27:05 +0200506 err = mtd_read_oob(mtd, addr0, &ops);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200507 if (err) {
Vikram Narayanan04810272012-10-10 23:12:02 +0530508 pr_info("error occurred as expected\n");
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200509 err = 0;
510 } else {
Vikram Narayanan04810272012-10-10 23:12:02 +0530511 pr_err("error: can read past end of OOB\n");
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200512 errcnt += 1;
513 }
514
515 if (bbt[ebcnt - 1])
Vikram Narayanan04810272012-10-10 23:12:02 +0530516 pr_info("skipping end of device tests because last "
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200517 "block is bad\n");
518 else {
519 /* Attempt to write off end of device */
Brian Norris0612b9d2011-08-30 18:45:40 -0700520 ops.mode = MTD_OPS_AUTO_OOB;
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200521 ops.len = 0;
522 ops.retlen = 0;
523 ops.ooblen = mtd->ecclayout->oobavail + 1;
524 ops.oobretlen = 0;
525 ops.ooboffs = 0;
Hannes Eder23d42492009-03-05 20:15:01 +0100526 ops.datbuf = NULL;
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200527 ops.oobbuf = writebuf;
Vikram Narayanan04810272012-10-10 23:12:02 +0530528 pr_info("attempting to write past end of device\n");
529 pr_info("an error is expected...\n");
Artem Bityutskiya2cc5ba2011-12-23 18:29:55 +0200530 err = mtd_write_oob(mtd, mtd->size - mtd->writesize, &ops);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200531 if (err) {
Vikram Narayanan04810272012-10-10 23:12:02 +0530532 pr_info("error occurred as expected\n");
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200533 err = 0;
534 } else {
Vikram Narayanan04810272012-10-10 23:12:02 +0530535 pr_err("error: wrote past end of device\n");
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200536 errcnt += 1;
537 }
538
539 /* Attempt to read off end of device */
Brian Norris0612b9d2011-08-30 18:45:40 -0700540 ops.mode = MTD_OPS_AUTO_OOB;
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200541 ops.len = 0;
542 ops.retlen = 0;
543 ops.ooblen = mtd->ecclayout->oobavail + 1;
544 ops.oobretlen = 0;
545 ops.ooboffs = 0;
Hannes Eder23d42492009-03-05 20:15:01 +0100546 ops.datbuf = NULL;
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200547 ops.oobbuf = readbuf;
Vikram Narayanan04810272012-10-10 23:12:02 +0530548 pr_info("attempting to read past end of device\n");
549 pr_info("an error is expected...\n");
Artem Bityutskiyfd2819b2011-12-23 18:27:05 +0200550 err = mtd_read_oob(mtd, mtd->size - mtd->writesize, &ops);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200551 if (err) {
Vikram Narayanan04810272012-10-10 23:12:02 +0530552 pr_info("error occurred as expected\n");
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200553 err = 0;
554 } else {
Vikram Narayanan04810272012-10-10 23:12:02 +0530555 pr_err("error: read past end of device\n");
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200556 errcnt += 1;
557 }
558
Akinobu Mita4bf527a2013-08-03 18:52:09 +0900559 err = mtdtest_erase_eraseblock(mtd, ebcnt - 1);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200560 if (err)
561 goto out;
562
563 /* Attempt to write off end of device */
Brian Norris0612b9d2011-08-30 18:45:40 -0700564 ops.mode = MTD_OPS_AUTO_OOB;
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200565 ops.len = 0;
566 ops.retlen = 0;
567 ops.ooblen = mtd->ecclayout->oobavail;
568 ops.oobretlen = 0;
569 ops.ooboffs = 1;
Hannes Eder23d42492009-03-05 20:15:01 +0100570 ops.datbuf = NULL;
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200571 ops.oobbuf = writebuf;
Vikram Narayanan04810272012-10-10 23:12:02 +0530572 pr_info("attempting to write past end of device\n");
573 pr_info("an error is expected...\n");
Artem Bityutskiya2cc5ba2011-12-23 18:29:55 +0200574 err = mtd_write_oob(mtd, mtd->size - mtd->writesize, &ops);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200575 if (err) {
Vikram Narayanan04810272012-10-10 23:12:02 +0530576 pr_info("error occurred as expected\n");
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200577 err = 0;
578 } else {
Vikram Narayanan04810272012-10-10 23:12:02 +0530579 pr_err("error: wrote past end of device\n");
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200580 errcnt += 1;
581 }
582
583 /* Attempt to read off end of device */
Brian Norris0612b9d2011-08-30 18:45:40 -0700584 ops.mode = MTD_OPS_AUTO_OOB;
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200585 ops.len = 0;
586 ops.retlen = 0;
587 ops.ooblen = mtd->ecclayout->oobavail;
588 ops.oobretlen = 0;
589 ops.ooboffs = 1;
Hannes Eder23d42492009-03-05 20:15:01 +0100590 ops.datbuf = NULL;
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200591 ops.oobbuf = readbuf;
Vikram Narayanan04810272012-10-10 23:12:02 +0530592 pr_info("attempting to read past end of device\n");
593 pr_info("an error is expected...\n");
Artem Bityutskiyfd2819b2011-12-23 18:27:05 +0200594 err = mtd_read_oob(mtd, mtd->size - mtd->writesize, &ops);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200595 if (err) {
Vikram Narayanan04810272012-10-10 23:12:02 +0530596 pr_info("error occurred as expected\n");
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200597 err = 0;
598 } else {
Vikram Narayanan04810272012-10-10 23:12:02 +0530599 pr_err("error: read past end of device\n");
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200600 errcnt += 1;
601 }
602 }
603
604 /* Fifth test: write / read across block boundaries */
Vikram Narayanan04810272012-10-10 23:12:02 +0530605 pr_info("test 5 of 5\n");
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200606
607 /* Erase all eraseblocks */
Akinobu Mita4bf527a2013-08-03 18:52:09 +0900608 err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200609 if (err)
610 goto out;
611
612 /* Write all eraseblocks */
Akinobu Mita8dad0492013-02-27 17:05:33 -0800613 prandom_seed_state(&rnd_state, 11);
Vikram Narayanan04810272012-10-10 23:12:02 +0530614 pr_info("writing OOBs of whole device\n");
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200615 for (i = 0; i < ebcnt - 1; ++i) {
616 int cnt = 2;
617 int pg;
618 size_t sz = mtd->ecclayout->oobavail;
619 if (bbt[i] || bbt[i + 1])
620 continue;
Brian Norris1001ff72014-07-21 19:07:12 -0700621 addr = (loff_t)(i + 1) * mtd->erasesize - mtd->writesize;
Akinobu Mitabe54f8f2014-03-08 00:24:10 +0900622 prandom_bytes_state(&rnd_state, writebuf, sz * cnt);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200623 for (pg = 0; pg < cnt; ++pg) {
Brian Norris0612b9d2011-08-30 18:45:40 -0700624 ops.mode = MTD_OPS_AUTO_OOB;
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200625 ops.len = 0;
626 ops.retlen = 0;
627 ops.ooblen = sz;
628 ops.oobretlen = 0;
629 ops.ooboffs = 0;
Hannes Eder23d42492009-03-05 20:15:01 +0100630 ops.datbuf = NULL;
Akinobu Mitabe54f8f2014-03-08 00:24:10 +0900631 ops.oobbuf = writebuf + pg * sz;
Artem Bityutskiya2cc5ba2011-12-23 18:29:55 +0200632 err = mtd_write_oob(mtd, addr, &ops);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200633 if (err)
634 goto out;
635 if (i % 256 == 0)
Vikram Narayanan04810272012-10-10 23:12:02 +0530636 pr_info("written up to eraseblock %u\n", i);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200637 cond_resched();
638 addr += mtd->writesize;
639 }
640 }
Vikram Narayanan04810272012-10-10 23:12:02 +0530641 pr_info("written %u eraseblocks\n", i);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200642
643 /* Check all eraseblocks */
Akinobu Mita8dad0492013-02-27 17:05:33 -0800644 prandom_seed_state(&rnd_state, 11);
Vikram Narayanan04810272012-10-10 23:12:02 +0530645 pr_info("verifying all eraseblocks\n");
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200646 for (i = 0; i < ebcnt - 1; ++i) {
647 if (bbt[i] || bbt[i + 1])
648 continue;
Akinobu Mita8dad0492013-02-27 17:05:33 -0800649 prandom_bytes_state(&rnd_state, writebuf,
650 mtd->ecclayout->oobavail * 2);
Brian Norris1001ff72014-07-21 19:07:12 -0700651 addr = (loff_t)(i + 1) * mtd->erasesize - mtd->writesize;
Brian Norris0612b9d2011-08-30 18:45:40 -0700652 ops.mode = MTD_OPS_AUTO_OOB;
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200653 ops.len = 0;
654 ops.retlen = 0;
655 ops.ooblen = mtd->ecclayout->oobavail * 2;
656 ops.oobretlen = 0;
657 ops.ooboffs = 0;
Hannes Eder23d42492009-03-05 20:15:01 +0100658 ops.datbuf = NULL;
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200659 ops.oobbuf = readbuf;
Artem Bityutskiyfd2819b2011-12-23 18:27:05 +0200660 err = mtd_read_oob(mtd, addr, &ops);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200661 if (err)
662 goto out;
Roger Quadros5a660882014-10-21 16:53:27 +0300663 if (memcmpshow(addr, readbuf, writebuf,
664 mtd->ecclayout->oobavail * 2)) {
Vikram Narayanan04810272012-10-10 23:12:02 +0530665 pr_err("error: verify failed at %#llx\n",
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200666 (long long)addr);
667 errcnt += 1;
668 if (errcnt > 1000) {
Vikram Narayanan04810272012-10-10 23:12:02 +0530669 pr_err("error: too many errors\n");
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200670 goto out;
671 }
672 }
673 if (i % 256 == 0)
Vikram Narayanan04810272012-10-10 23:12:02 +0530674 pr_info("verified up to eraseblock %u\n", i);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200675 cond_resched();
676 }
Vikram Narayanan04810272012-10-10 23:12:02 +0530677 pr_info("verified %u eraseblocks\n", i);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200678
Vikram Narayanan04810272012-10-10 23:12:02 +0530679 pr_info("finished with %d errors\n", errcnt);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200680out:
681 kfree(bbt);
682 kfree(writebuf);
683 kfree(readbuf);
684 put_mtd_device(mtd);
685 if (err)
Vikram Narayanan04810272012-10-10 23:12:02 +0530686 pr_info("error %d occurred\n", err);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200687 printk(KERN_INFO "=================================================\n");
688 return err;
689}
690module_init(mtd_oobtest_init);
691
692static void __exit mtd_oobtest_exit(void)
693{
694 return;
695}
696module_exit(mtd_oobtest_exit);
697
698MODULE_DESCRIPTION("Out-of-band test module");
699MODULE_AUTHOR("Adrian Hunter");
700MODULE_LICENSE("GPL");