blob: edd859d96d8509a134727769e0baa4ae9f4ee7a3 [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;
Artem Bityutskiye3644da2008-12-08 13:33:29 +020037module_param(dev, int, S_IRUGO);
38MODULE_PARM_DESC(dev, "MTD device number to use");
39
40static struct mtd_info *mtd;
41static unsigned char *readbuf;
42static unsigned char *writebuf;
43static unsigned char *bbt;
44
45static int ebcnt;
46static int pgcnt;
47static int errcnt;
48static int use_offset;
49static int use_len;
50static int use_len_max;
51static int vary_offset;
Akinobu Mita8dad0492013-02-27 17:05:33 -080052static struct rnd_state rnd_state;
Artem Bityutskiye3644da2008-12-08 13:33:29 +020053
Artem Bityutskiye3644da2008-12-08 13:33:29 +020054static void do_vary_offset(void)
55{
56 use_len -= 1;
57 if (use_len < 1) {
58 use_offset += 1;
59 if (use_offset >= use_len_max)
60 use_offset = 0;
61 use_len = use_len_max - use_offset;
62 }
63}
64
65static int write_eraseblock(int ebnum)
66{
67 int i;
68 struct mtd_oob_ops ops;
69 int err = 0;
70 loff_t addr = ebnum * mtd->erasesize;
71
Akinobu Mitabe54f8f2014-03-08 00:24:10 +090072 prandom_bytes_state(&rnd_state, writebuf, use_len_max * pgcnt);
Artem Bityutskiye3644da2008-12-08 13:33:29 +020073 for (i = 0; i < pgcnt; ++i, addr += mtd->writesize) {
Brian Norris0612b9d2011-08-30 18:45:40 -070074 ops.mode = MTD_OPS_AUTO_OOB;
Artem Bityutskiye3644da2008-12-08 13:33:29 +020075 ops.len = 0;
76 ops.retlen = 0;
77 ops.ooblen = use_len;
78 ops.oobretlen = 0;
79 ops.ooboffs = use_offset;
Hannes Eder23d42492009-03-05 20:15:01 +010080 ops.datbuf = NULL;
Akinobu Mitabe54f8f2014-03-08 00:24:10 +090081 ops.oobbuf = writebuf + (use_len_max * i) + use_offset;
Artem Bityutskiya2cc5ba2011-12-23 18:29:55 +020082 err = mtd_write_oob(mtd, addr, &ops);
Artem Bityutskiye3644da2008-12-08 13:33:29 +020083 if (err || ops.oobretlen != use_len) {
Vikram Narayanan04810272012-10-10 23:12:02 +053084 pr_err("error: writeoob failed at %#llx\n",
Artem Bityutskiye3644da2008-12-08 13:33:29 +020085 (long long)addr);
Vikram Narayanan04810272012-10-10 23:12:02 +053086 pr_err("error: use_len %d, use_offset %d\n",
Artem Bityutskiye3644da2008-12-08 13:33:29 +020087 use_len, use_offset);
88 errcnt += 1;
89 return err ? err : -1;
90 }
91 if (vary_offset)
92 do_vary_offset();
93 }
94
95 return err;
96}
97
98static int write_whole_device(void)
99{
100 int err;
101 unsigned int i;
102
Vikram Narayanan04810272012-10-10 23:12:02 +0530103 pr_info("writing OOBs of whole device\n");
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200104 for (i = 0; i < ebcnt; ++i) {
105 if (bbt[i])
106 continue;
107 err = write_eraseblock(i);
108 if (err)
109 return err;
110 if (i % 256 == 0)
Vikram Narayanan04810272012-10-10 23:12:02 +0530111 pr_info("written up to eraseblock %u\n", i);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200112 cond_resched();
113 }
Vikram Narayanan04810272012-10-10 23:12:02 +0530114 pr_info("written %u eraseblocks\n", i);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200115 return 0;
116}
117
Roger Quadros5a660882014-10-21 16:53:27 +0300118/* Display the address, offset and data bytes at comparison failure */
119static int memcmpshow(loff_t addr, const void *cs, const void *ct, size_t count)
120{
121 const unsigned char *su1, *su2;
122 int res;
123 int ret = 0;
124 size_t i = 0;
125
126 for (su1 = cs, su2 = ct; 0 < count; ++su1, ++su2, count--, i++) {
127 res = *su1 ^ *su2;
128 if (res) {
129 pr_info("error @addr[0x%lx:0x%x] 0x%x -> 0x%x diff 0x%x\n",
130 (unsigned long)addr, i, *su1, *su2, res);
131 ret = 1;
132 }
133 }
134
135 return ret;
136}
137
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200138static int verify_eraseblock(int ebnum)
139{
140 int i;
141 struct mtd_oob_ops ops;
142 int err = 0;
Brian Norris1001ff72014-07-21 19:07:12 -0700143 loff_t addr = (loff_t)ebnum * mtd->erasesize;
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200144
Akinobu Mitabe54f8f2014-03-08 00:24:10 +0900145 prandom_bytes_state(&rnd_state, writebuf, use_len_max * pgcnt);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200146 for (i = 0; i < pgcnt; ++i, addr += mtd->writesize) {
Brian Norris0612b9d2011-08-30 18:45:40 -0700147 ops.mode = MTD_OPS_AUTO_OOB;
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200148 ops.len = 0;
149 ops.retlen = 0;
150 ops.ooblen = use_len;
151 ops.oobretlen = 0;
152 ops.ooboffs = use_offset;
Hannes Eder23d42492009-03-05 20:15:01 +0100153 ops.datbuf = NULL;
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200154 ops.oobbuf = readbuf;
Artem Bityutskiyfd2819b2011-12-23 18:27:05 +0200155 err = mtd_read_oob(mtd, addr, &ops);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200156 if (err || ops.oobretlen != use_len) {
Vikram Narayanan04810272012-10-10 23:12:02 +0530157 pr_err("error: readoob failed at %#llx\n",
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200158 (long long)addr);
159 errcnt += 1;
160 return err ? err : -1;
161 }
Roger Quadros5a660882014-10-21 16:53:27 +0300162 if (memcmpshow(addr, readbuf,
163 writebuf + (use_len_max * i) + use_offset,
164 use_len)) {
Vikram Narayanan04810272012-10-10 23:12:02 +0530165 pr_err("error: verify failed at %#llx\n",
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200166 (long long)addr);
167 errcnt += 1;
168 if (errcnt > 1000) {
Vikram Narayanan04810272012-10-10 23:12:02 +0530169 pr_err("error: too many errors\n");
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200170 return -1;
171 }
172 }
173 if (use_offset != 0 || use_len < mtd->ecclayout->oobavail) {
174 int k;
175
Brian Norris0612b9d2011-08-30 18:45:40 -0700176 ops.mode = MTD_OPS_AUTO_OOB;
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200177 ops.len = 0;
178 ops.retlen = 0;
179 ops.ooblen = mtd->ecclayout->oobavail;
180 ops.oobretlen = 0;
181 ops.ooboffs = 0;
Hannes Eder23d42492009-03-05 20:15:01 +0100182 ops.datbuf = NULL;
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200183 ops.oobbuf = readbuf;
Artem Bityutskiyfd2819b2011-12-23 18:27:05 +0200184 err = mtd_read_oob(mtd, addr, &ops);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200185 if (err || ops.oobretlen != mtd->ecclayout->oobavail) {
Vikram Narayanan04810272012-10-10 23:12:02 +0530186 pr_err("error: readoob failed at %#llx\n",
187 (long long)addr);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200188 errcnt += 1;
189 return err ? err : -1;
190 }
Roger Quadros5a660882014-10-21 16:53:27 +0300191 if (memcmpshow(addr, readbuf + use_offset,
192 writebuf + (use_len_max * i) + use_offset,
193 use_len)) {
Vikram Narayanan04810272012-10-10 23:12:02 +0530194 pr_err("error: verify failed at %#llx\n",
195 (long long)addr);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200196 errcnt += 1;
197 if (errcnt > 1000) {
Vikram Narayanan04810272012-10-10 23:12:02 +0530198 pr_err("error: too many errors\n");
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200199 return -1;
200 }
201 }
202 for (k = 0; k < use_offset; ++k)
203 if (readbuf[k] != 0xff) {
Vikram Narayanan04810272012-10-10 23:12:02 +0530204 pr_err("error: verify 0xff "
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200205 "failed at %#llx\n",
206 (long long)addr);
207 errcnt += 1;
208 if (errcnt > 1000) {
Vikram Narayanan04810272012-10-10 23:12:02 +0530209 pr_err("error: too "
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200210 "many errors\n");
211 return -1;
212 }
213 }
214 for (k = use_offset + use_len;
215 k < mtd->ecclayout->oobavail; ++k)
216 if (readbuf[k] != 0xff) {
Vikram Narayanan04810272012-10-10 23:12:02 +0530217 pr_err("error: verify 0xff "
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200218 "failed at %#llx\n",
219 (long long)addr);
220 errcnt += 1;
221 if (errcnt > 1000) {
Vikram Narayanan04810272012-10-10 23:12:02 +0530222 pr_err("error: too "
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200223 "many errors\n");
224 return -1;
225 }
226 }
227 }
228 if (vary_offset)
229 do_vary_offset();
230 }
231 return err;
232}
233
234static int verify_eraseblock_in_one_go(int ebnum)
235{
236 struct mtd_oob_ops ops;
237 int err = 0;
Brian Norris1001ff72014-07-21 19:07:12 -0700238 loff_t addr = (loff_t)ebnum * mtd->erasesize;
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200239 size_t len = mtd->ecclayout->oobavail * pgcnt;
240
Akinobu Mita8dad0492013-02-27 17:05:33 -0800241 prandom_bytes_state(&rnd_state, writebuf, len);
Brian Norris0612b9d2011-08-30 18:45:40 -0700242 ops.mode = MTD_OPS_AUTO_OOB;
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200243 ops.len = 0;
244 ops.retlen = 0;
245 ops.ooblen = len;
246 ops.oobretlen = 0;
247 ops.ooboffs = 0;
Hannes Eder23d42492009-03-05 20:15:01 +0100248 ops.datbuf = NULL;
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200249 ops.oobbuf = readbuf;
Artem Bityutskiyfd2819b2011-12-23 18:27:05 +0200250 err = mtd_read_oob(mtd, addr, &ops);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200251 if (err || ops.oobretlen != len) {
Vikram Narayanan04810272012-10-10 23:12:02 +0530252 pr_err("error: readoob failed at %#llx\n",
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200253 (long long)addr);
254 errcnt += 1;
255 return err ? err : -1;
256 }
Roger Quadros5a660882014-10-21 16:53:27 +0300257 if (memcmpshow(addr, readbuf, writebuf, len)) {
Vikram Narayanan04810272012-10-10 23:12:02 +0530258 pr_err("error: verify failed at %#llx\n",
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200259 (long long)addr);
260 errcnt += 1;
261 if (errcnt > 1000) {
Vikram Narayanan04810272012-10-10 23:12:02 +0530262 pr_err("error: too many errors\n");
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200263 return -1;
264 }
265 }
266
267 return err;
268}
269
270static int verify_all_eraseblocks(void)
271{
272 int err;
273 unsigned int i;
274
Vikram Narayanan04810272012-10-10 23:12:02 +0530275 pr_info("verifying all eraseblocks\n");
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200276 for (i = 0; i < ebcnt; ++i) {
277 if (bbt[i])
278 continue;
279 err = verify_eraseblock(i);
280 if (err)
281 return err;
282 if (i % 256 == 0)
Vikram Narayanan04810272012-10-10 23:12:02 +0530283 pr_info("verified up to eraseblock %u\n", i);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200284 cond_resched();
285 }
Vikram Narayanan04810272012-10-10 23:12:02 +0530286 pr_info("verified %u eraseblocks\n", i);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200287 return 0;
288}
289
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200290static int __init mtd_oobtest_init(void)
291{
292 int err = 0;
293 unsigned int i;
294 uint64_t tmp;
295 struct mtd_oob_ops ops;
296 loff_t addr = 0, addr0;
297
298 printk(KERN_INFO "\n");
299 printk(KERN_INFO "=================================================\n");
Wolfram Sang74060602011-10-30 00:11:53 +0200300
301 if (dev < 0) {
Masanari Iida064a7692012-11-09 23:20:58 +0900302 pr_info("Please specify a valid mtd-device via module parameter\n");
303 pr_crit("CAREFUL: This test wipes all data on the specified MTD device!\n");
Wolfram Sang74060602011-10-30 00:11:53 +0200304 return -EINVAL;
305 }
306
Vikram Narayanan04810272012-10-10 23:12:02 +0530307 pr_info("MTD device: %d\n", dev);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200308
309 mtd = get_mtd_device(NULL, dev);
310 if (IS_ERR(mtd)) {
311 err = PTR_ERR(mtd);
Vikram Narayanan04810272012-10-10 23:12:02 +0530312 pr_err("error: cannot get MTD device\n");
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200313 return err;
314 }
315
Huang Shijie818b9732013-09-25 14:58:17 +0800316 if (!mtd_type_is_nand(mtd)) {
Vikram Narayanan04810272012-10-10 23:12:02 +0530317 pr_info("this test requires NAND flash\n");
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200318 goto out;
319 }
320
321 tmp = mtd->size;
322 do_div(tmp, mtd->erasesize);
323 ebcnt = tmp;
324 pgcnt = mtd->erasesize / mtd->writesize;
325
Vikram Narayanan04810272012-10-10 23:12:02 +0530326 pr_info("MTD device size %llu, eraseblock size %u, "
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200327 "page size %u, count of eraseblocks %u, pages per "
328 "eraseblock %u, OOB size %u\n",
329 (unsigned long long)mtd->size, mtd->erasesize,
330 mtd->writesize, ebcnt, pgcnt, mtd->oobsize);
331
332 err = -ENOMEM;
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200333 readbuf = kmalloc(mtd->erasesize, GFP_KERNEL);
Brian Norris33777e62013-05-02 14:18:51 -0700334 if (!readbuf)
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200335 goto out;
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200336 writebuf = kmalloc(mtd->erasesize, GFP_KERNEL);
Brian Norris33777e62013-05-02 14:18:51 -0700337 if (!writebuf)
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200338 goto out;
Akinobu Mita4bf527a2013-08-03 18:52:09 +0900339 bbt = kzalloc(ebcnt, GFP_KERNEL);
340 if (!bbt)
341 goto out;
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200342
Akinobu Mita4bf527a2013-08-03 18:52:09 +0900343 err = mtdtest_scan_for_bad_eraseblocks(mtd, bbt, 0, ebcnt);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200344 if (err)
345 goto out;
346
347 use_offset = 0;
348 use_len = mtd->ecclayout->oobavail;
349 use_len_max = mtd->ecclayout->oobavail;
350 vary_offset = 0;
351
352 /* First test: write all OOB, read it back and verify */
Vikram Narayanan04810272012-10-10 23:12:02 +0530353 pr_info("test 1 of 5\n");
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200354
Akinobu Mita4bf527a2013-08-03 18:52:09 +0900355 err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200356 if (err)
357 goto out;
358
Akinobu Mita8dad0492013-02-27 17:05:33 -0800359 prandom_seed_state(&rnd_state, 1);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200360 err = write_whole_device();
361 if (err)
362 goto out;
363
Akinobu Mita8dad0492013-02-27 17:05:33 -0800364 prandom_seed_state(&rnd_state, 1);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200365 err = verify_all_eraseblocks();
366 if (err)
367 goto out;
368
369 /*
370 * Second test: write all OOB, a block at a time, read it back and
371 * verify.
372 */
Vikram Narayanan04810272012-10-10 23:12:02 +0530373 pr_info("test 2 of 5\n");
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200374
Akinobu Mita4bf527a2013-08-03 18:52:09 +0900375 err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200376 if (err)
377 goto out;
378
Akinobu Mita8dad0492013-02-27 17:05:33 -0800379 prandom_seed_state(&rnd_state, 3);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200380 err = write_whole_device();
381 if (err)
382 goto out;
383
384 /* Check all eraseblocks */
Akinobu Mita8dad0492013-02-27 17:05:33 -0800385 prandom_seed_state(&rnd_state, 3);
Vikram Narayanan04810272012-10-10 23:12:02 +0530386 pr_info("verifying all eraseblocks\n");
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200387 for (i = 0; i < ebcnt; ++i) {
388 if (bbt[i])
389 continue;
390 err = verify_eraseblock_in_one_go(i);
391 if (err)
392 goto out;
393 if (i % 256 == 0)
Vikram Narayanan04810272012-10-10 23:12:02 +0530394 pr_info("verified up to eraseblock %u\n", i);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200395 cond_resched();
396 }
Vikram Narayanan04810272012-10-10 23:12:02 +0530397 pr_info("verified %u eraseblocks\n", i);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200398
399 /*
400 * Third test: write OOB at varying offsets and lengths, read it back
401 * and verify.
402 */
Vikram Narayanan04810272012-10-10 23:12:02 +0530403 pr_info("test 3 of 5\n");
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200404
Akinobu Mita4bf527a2013-08-03 18:52:09 +0900405 err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200406 if (err)
407 goto out;
408
409 /* Write all eraseblocks */
410 use_offset = 0;
411 use_len = mtd->ecclayout->oobavail;
412 use_len_max = mtd->ecclayout->oobavail;
413 vary_offset = 1;
Akinobu Mita8dad0492013-02-27 17:05:33 -0800414 prandom_seed_state(&rnd_state, 5);
Akinobu Mitaf54d6332009-10-09 18:43:52 +0900415
416 err = write_whole_device();
417 if (err)
418 goto out;
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200419
420 /* Check all eraseblocks */
421 use_offset = 0;
422 use_len = mtd->ecclayout->oobavail;
423 use_len_max = mtd->ecclayout->oobavail;
424 vary_offset = 1;
Akinobu Mita8dad0492013-02-27 17:05:33 -0800425 prandom_seed_state(&rnd_state, 5);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200426 err = verify_all_eraseblocks();
427 if (err)
428 goto out;
429
430 use_offset = 0;
431 use_len = mtd->ecclayout->oobavail;
432 use_len_max = mtd->ecclayout->oobavail;
433 vary_offset = 0;
434
435 /* Fourth test: try to write off end of device */
Vikram Narayanan04810272012-10-10 23:12:02 +0530436 pr_info("test 4 of 5\n");
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200437
Akinobu Mita4bf527a2013-08-03 18:52:09 +0900438 err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200439 if (err)
440 goto out;
441
442 addr0 = 0;
Roel Kluinc6f7e7b2009-07-31 16:21:01 +0200443 for (i = 0; i < ebcnt && bbt[i]; ++i)
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200444 addr0 += mtd->erasesize;
445
446 /* Attempt to write off end of OOB */
Brian Norris0612b9d2011-08-30 18:45:40 -0700447 ops.mode = MTD_OPS_AUTO_OOB;
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200448 ops.len = 0;
449 ops.retlen = 0;
450 ops.ooblen = 1;
451 ops.oobretlen = 0;
452 ops.ooboffs = mtd->ecclayout->oobavail;
Hannes Eder23d42492009-03-05 20:15:01 +0100453 ops.datbuf = NULL;
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200454 ops.oobbuf = writebuf;
Vikram Narayanan04810272012-10-10 23:12:02 +0530455 pr_info("attempting to start write past end of OOB\n");
456 pr_info("an error is expected...\n");
Artem Bityutskiya2cc5ba2011-12-23 18:29:55 +0200457 err = mtd_write_oob(mtd, addr0, &ops);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200458 if (err) {
Vikram Narayanan04810272012-10-10 23:12:02 +0530459 pr_info("error occurred as expected\n");
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200460 err = 0;
461 } else {
Vikram Narayanan04810272012-10-10 23:12:02 +0530462 pr_err("error: can write past end of OOB\n");
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200463 errcnt += 1;
464 }
465
466 /* Attempt to read off end of OOB */
Brian Norris0612b9d2011-08-30 18:45:40 -0700467 ops.mode = MTD_OPS_AUTO_OOB;
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200468 ops.len = 0;
469 ops.retlen = 0;
470 ops.ooblen = 1;
471 ops.oobretlen = 0;
472 ops.ooboffs = mtd->ecclayout->oobavail;
Hannes Eder23d42492009-03-05 20:15:01 +0100473 ops.datbuf = NULL;
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200474 ops.oobbuf = readbuf;
Vikram Narayanan04810272012-10-10 23:12:02 +0530475 pr_info("attempting to start read past end of OOB\n");
476 pr_info("an error is expected...\n");
Artem Bityutskiyfd2819b2011-12-23 18:27:05 +0200477 err = mtd_read_oob(mtd, addr0, &ops);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200478 if (err) {
Vikram Narayanan04810272012-10-10 23:12:02 +0530479 pr_info("error occurred as expected\n");
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200480 err = 0;
481 } else {
Vikram Narayanan04810272012-10-10 23:12:02 +0530482 pr_err("error: can read past end of OOB\n");
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200483 errcnt += 1;
484 }
485
486 if (bbt[ebcnt - 1])
Vikram Narayanan04810272012-10-10 23:12:02 +0530487 pr_info("skipping end of device tests because last "
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200488 "block is bad\n");
489 else {
490 /* Attempt to write off end of device */
Brian Norris0612b9d2011-08-30 18:45:40 -0700491 ops.mode = MTD_OPS_AUTO_OOB;
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200492 ops.len = 0;
493 ops.retlen = 0;
494 ops.ooblen = mtd->ecclayout->oobavail + 1;
495 ops.oobretlen = 0;
496 ops.ooboffs = 0;
Hannes Eder23d42492009-03-05 20:15:01 +0100497 ops.datbuf = NULL;
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200498 ops.oobbuf = writebuf;
Vikram Narayanan04810272012-10-10 23:12:02 +0530499 pr_info("attempting to write past end of device\n");
500 pr_info("an error is expected...\n");
Artem Bityutskiya2cc5ba2011-12-23 18:29:55 +0200501 err = mtd_write_oob(mtd, mtd->size - mtd->writesize, &ops);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200502 if (err) {
Vikram Narayanan04810272012-10-10 23:12:02 +0530503 pr_info("error occurred as expected\n");
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200504 err = 0;
505 } else {
Vikram Narayanan04810272012-10-10 23:12:02 +0530506 pr_err("error: wrote past end of device\n");
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200507 errcnt += 1;
508 }
509
510 /* Attempt to read off end of device */
Brian Norris0612b9d2011-08-30 18:45:40 -0700511 ops.mode = MTD_OPS_AUTO_OOB;
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200512 ops.len = 0;
513 ops.retlen = 0;
514 ops.ooblen = mtd->ecclayout->oobavail + 1;
515 ops.oobretlen = 0;
516 ops.ooboffs = 0;
Hannes Eder23d42492009-03-05 20:15:01 +0100517 ops.datbuf = NULL;
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200518 ops.oobbuf = readbuf;
Vikram Narayanan04810272012-10-10 23:12:02 +0530519 pr_info("attempting to read past end of device\n");
520 pr_info("an error is expected...\n");
Artem Bityutskiyfd2819b2011-12-23 18:27:05 +0200521 err = mtd_read_oob(mtd, mtd->size - mtd->writesize, &ops);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200522 if (err) {
Vikram Narayanan04810272012-10-10 23:12:02 +0530523 pr_info("error occurred as expected\n");
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200524 err = 0;
525 } else {
Vikram Narayanan04810272012-10-10 23:12:02 +0530526 pr_err("error: read past end of device\n");
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200527 errcnt += 1;
528 }
529
Akinobu Mita4bf527a2013-08-03 18:52:09 +0900530 err = mtdtest_erase_eraseblock(mtd, ebcnt - 1);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200531 if (err)
532 goto out;
533
534 /* Attempt to write off end of device */
Brian Norris0612b9d2011-08-30 18:45:40 -0700535 ops.mode = MTD_OPS_AUTO_OOB;
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200536 ops.len = 0;
537 ops.retlen = 0;
538 ops.ooblen = mtd->ecclayout->oobavail;
539 ops.oobretlen = 0;
540 ops.ooboffs = 1;
Hannes Eder23d42492009-03-05 20:15:01 +0100541 ops.datbuf = NULL;
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200542 ops.oobbuf = writebuf;
Vikram Narayanan04810272012-10-10 23:12:02 +0530543 pr_info("attempting to write past end of device\n");
544 pr_info("an error is expected...\n");
Artem Bityutskiya2cc5ba2011-12-23 18:29:55 +0200545 err = mtd_write_oob(mtd, mtd->size - mtd->writesize, &ops);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200546 if (err) {
Vikram Narayanan04810272012-10-10 23:12:02 +0530547 pr_info("error occurred as expected\n");
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200548 err = 0;
549 } else {
Vikram Narayanan04810272012-10-10 23:12:02 +0530550 pr_err("error: wrote past end of device\n");
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200551 errcnt += 1;
552 }
553
554 /* Attempt to read off end of device */
Brian Norris0612b9d2011-08-30 18:45:40 -0700555 ops.mode = MTD_OPS_AUTO_OOB;
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200556 ops.len = 0;
557 ops.retlen = 0;
558 ops.ooblen = mtd->ecclayout->oobavail;
559 ops.oobretlen = 0;
560 ops.ooboffs = 1;
Hannes Eder23d42492009-03-05 20:15:01 +0100561 ops.datbuf = NULL;
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200562 ops.oobbuf = readbuf;
Vikram Narayanan04810272012-10-10 23:12:02 +0530563 pr_info("attempting to read past end of device\n");
564 pr_info("an error is expected...\n");
Artem Bityutskiyfd2819b2011-12-23 18:27:05 +0200565 err = mtd_read_oob(mtd, mtd->size - mtd->writesize, &ops);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200566 if (err) {
Vikram Narayanan04810272012-10-10 23:12:02 +0530567 pr_info("error occurred as expected\n");
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200568 err = 0;
569 } else {
Vikram Narayanan04810272012-10-10 23:12:02 +0530570 pr_err("error: read past end of device\n");
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200571 errcnt += 1;
572 }
573 }
574
575 /* Fifth test: write / read across block boundaries */
Vikram Narayanan04810272012-10-10 23:12:02 +0530576 pr_info("test 5 of 5\n");
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200577
578 /* Erase all eraseblocks */
Akinobu Mita4bf527a2013-08-03 18:52:09 +0900579 err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200580 if (err)
581 goto out;
582
583 /* Write all eraseblocks */
Akinobu Mita8dad0492013-02-27 17:05:33 -0800584 prandom_seed_state(&rnd_state, 11);
Vikram Narayanan04810272012-10-10 23:12:02 +0530585 pr_info("writing OOBs of whole device\n");
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200586 for (i = 0; i < ebcnt - 1; ++i) {
587 int cnt = 2;
588 int pg;
589 size_t sz = mtd->ecclayout->oobavail;
590 if (bbt[i] || bbt[i + 1])
591 continue;
Brian Norris1001ff72014-07-21 19:07:12 -0700592 addr = (loff_t)(i + 1) * mtd->erasesize - mtd->writesize;
Akinobu Mitabe54f8f2014-03-08 00:24:10 +0900593 prandom_bytes_state(&rnd_state, writebuf, sz * cnt);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200594 for (pg = 0; pg < cnt; ++pg) {
Brian Norris0612b9d2011-08-30 18:45:40 -0700595 ops.mode = MTD_OPS_AUTO_OOB;
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200596 ops.len = 0;
597 ops.retlen = 0;
598 ops.ooblen = sz;
599 ops.oobretlen = 0;
600 ops.ooboffs = 0;
Hannes Eder23d42492009-03-05 20:15:01 +0100601 ops.datbuf = NULL;
Akinobu Mitabe54f8f2014-03-08 00:24:10 +0900602 ops.oobbuf = writebuf + pg * sz;
Artem Bityutskiya2cc5ba2011-12-23 18:29:55 +0200603 err = mtd_write_oob(mtd, addr, &ops);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200604 if (err)
605 goto out;
606 if (i % 256 == 0)
Vikram Narayanan04810272012-10-10 23:12:02 +0530607 pr_info("written up to eraseblock %u\n", i);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200608 cond_resched();
609 addr += mtd->writesize;
610 }
611 }
Vikram Narayanan04810272012-10-10 23:12:02 +0530612 pr_info("written %u eraseblocks\n", i);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200613
614 /* Check all eraseblocks */
Akinobu Mita8dad0492013-02-27 17:05:33 -0800615 prandom_seed_state(&rnd_state, 11);
Vikram Narayanan04810272012-10-10 23:12:02 +0530616 pr_info("verifying all eraseblocks\n");
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200617 for (i = 0; i < ebcnt - 1; ++i) {
618 if (bbt[i] || bbt[i + 1])
619 continue;
Akinobu Mita8dad0492013-02-27 17:05:33 -0800620 prandom_bytes_state(&rnd_state, writebuf,
621 mtd->ecclayout->oobavail * 2);
Brian Norris1001ff72014-07-21 19:07:12 -0700622 addr = (loff_t)(i + 1) * mtd->erasesize - mtd->writesize;
Brian Norris0612b9d2011-08-30 18:45:40 -0700623 ops.mode = MTD_OPS_AUTO_OOB;
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200624 ops.len = 0;
625 ops.retlen = 0;
626 ops.ooblen = mtd->ecclayout->oobavail * 2;
627 ops.oobretlen = 0;
628 ops.ooboffs = 0;
Hannes Eder23d42492009-03-05 20:15:01 +0100629 ops.datbuf = NULL;
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200630 ops.oobbuf = readbuf;
Artem Bityutskiyfd2819b2011-12-23 18:27:05 +0200631 err = mtd_read_oob(mtd, addr, &ops);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200632 if (err)
633 goto out;
Roger Quadros5a660882014-10-21 16:53:27 +0300634 if (memcmpshow(addr, readbuf, writebuf,
635 mtd->ecclayout->oobavail * 2)) {
Vikram Narayanan04810272012-10-10 23:12:02 +0530636 pr_err("error: verify failed at %#llx\n",
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200637 (long long)addr);
638 errcnt += 1;
639 if (errcnt > 1000) {
Vikram Narayanan04810272012-10-10 23:12:02 +0530640 pr_err("error: too many errors\n");
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200641 goto out;
642 }
643 }
644 if (i % 256 == 0)
Vikram Narayanan04810272012-10-10 23:12:02 +0530645 pr_info("verified up to eraseblock %u\n", i);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200646 cond_resched();
647 }
Vikram Narayanan04810272012-10-10 23:12:02 +0530648 pr_info("verified %u eraseblocks\n", i);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200649
Vikram Narayanan04810272012-10-10 23:12:02 +0530650 pr_info("finished with %d errors\n", errcnt);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200651out:
652 kfree(bbt);
653 kfree(writebuf);
654 kfree(readbuf);
655 put_mtd_device(mtd);
656 if (err)
Vikram Narayanan04810272012-10-10 23:12:02 +0530657 pr_info("error %d occurred\n", err);
Artem Bityutskiye3644da2008-12-08 13:33:29 +0200658 printk(KERN_INFO "=================================================\n");
659 return err;
660}
661module_init(mtd_oobtest_init);
662
663static void __exit mtd_oobtest_exit(void)
664{
665 return;
666}
667module_exit(mtd_oobtest_exit);
668
669MODULE_DESCRIPTION("Out-of-band test module");
670MODULE_AUTHOR("Adrian Hunter");
671MODULE_LICENSE("GPL");