1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * Copyright (C) 2006-2007 Nokia Corporation
4 *
5 * Test sub-page read and write on MTD device.
6 * Author: Adrian Hunter <ext-adrian.hunter@nokia.com>
7 */
8
9#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
10
11#include <linux/init.h>
12#include <linux/module.h>
13#include <linux/moduleparam.h>
14#include <linux/err.h>
15#include <linux/mtd/mtd.h>
16#include <linux/slab.h>
17#include <linux/sched.h>
18#include <linux/random.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 *writebuf;
28static unsigned char *readbuf;
29static unsigned char *bbt;
30
31static int subpgsize;
32static int bufsize;
33static int ebcnt;
34static int pgcnt;
35static int errcnt;
36static struct rnd_state rnd_state;
37
38static inline void clear_data(unsigned char *buf, size_t len)
39{
40	memset(buf, 0, len);
41}
42
43static int write_eraseblock(int ebnum)
44{
45	size_t written;
46	int err = 0;
47	loff_t addr = (loff_t)ebnum * mtd->erasesize;
48
49	prandom_bytes_state(&rnd_state, writebuf, subpgsize);
50	err = mtd_write(mtd, addr, subpgsize, &written, writebuf);
51	if (unlikely(err || written != subpgsize)) {
52		pr_err("error: write failed at %#llx\n",
53		       (long long)addr);
54		if (written != subpgsize) {
55			pr_err("  write size: %#x\n", subpgsize);
56			pr_err("  written: %#zx\n", written);
57		}
58		return err ? err : -1;
59	}
60
61	addr += subpgsize;
62
63	prandom_bytes_state(&rnd_state, writebuf, subpgsize);
64	err = mtd_write(mtd, addr, subpgsize, &written, writebuf);
65	if (unlikely(err || written != subpgsize)) {
66		pr_err("error: write failed at %#llx\n",
67		       (long long)addr);
68		if (written != subpgsize) {
69			pr_err("  write size: %#x\n", subpgsize);
70			pr_err("  written: %#zx\n", written);
71		}
72		return err ? err : -1;
73	}
74
75	return err;
76}
77
78static int write_eraseblock2(int ebnum)
79{
80	size_t written;
81	int err = 0, k;
82	loff_t addr = (loff_t)ebnum * mtd->erasesize;
83
84	for (k = 1; k < 33; ++k) {
85		if (addr + (subpgsize * k) > (loff_t)(ebnum + 1) * mtd->erasesize)
86			break;
87		prandom_bytes_state(&rnd_state, writebuf, subpgsize * k);
88		err = mtd_write(mtd, addr, subpgsize * k, &written, writebuf);
89		if (unlikely(err || written != subpgsize * k)) {
90			pr_err("error: write failed at %#llx\n",
91			       (long long)addr);
92			if (written != subpgsize * k) {
93				pr_err("  write size: %#x\n",
94				       subpgsize * k);
95				pr_err("  written: %#08zx\n",
96				       written);
97			}
98			return err ? err : -1;
99		}
100		addr += subpgsize * k;
101	}
102
103	return err;
104}
105
106static void print_subpage(unsigned char *p)
107{
108	int i, j;
109
110	for (i = 0; i < subpgsize; ) {
111		for (j = 0; i < subpgsize && j < 32; ++i, ++j)
112			printk("%02x", *p++);
113		printk("\n");
114	}
115}
116
117static int verify_eraseblock(int ebnum)
118{
119	size_t read;
120	int err = 0;
121	loff_t addr = (loff_t)ebnum * mtd->erasesize;
122
123	prandom_bytes_state(&rnd_state, writebuf, subpgsize);
124	clear_data(readbuf, subpgsize);
125	err = mtd_read(mtd, addr, subpgsize, &read, readbuf);
126	if (unlikely(err || read != subpgsize)) {
127		if (mtd_is_bitflip(err) && read == subpgsize) {
128			pr_info("ECC correction at %#llx\n",
129			       (long long)addr);
130			err = 0;
131		} else {
132			pr_err("error: read failed at %#llx\n",
133			       (long long)addr);
134			return err ? err : -1;
135		}
136	}
137	if (unlikely(memcmp(readbuf, writebuf, subpgsize))) {
138		pr_err("error: verify failed at %#llx\n",
139		       (long long)addr);
140		pr_info("------------- written----------------\n");
141		print_subpage(writebuf);
142		pr_info("------------- read ------------------\n");
143		print_subpage(readbuf);
144		pr_info("-------------------------------------\n");
145		errcnt += 1;
146	}
147
148	addr += subpgsize;
149
150	prandom_bytes_state(&rnd_state, writebuf, subpgsize);
151	clear_data(readbuf, subpgsize);
152	err = mtd_read(mtd, addr, subpgsize, &read, readbuf);
153	if (unlikely(err || read != subpgsize)) {
154		if (mtd_is_bitflip(err) && read == subpgsize) {
155			pr_info("ECC correction at %#llx\n",
156			       (long long)addr);
157			err = 0;
158		} else {
159			pr_err("error: read failed at %#llx\n",
160			       (long long)addr);
161			return err ? err : -1;
162		}
163	}
164	if (unlikely(memcmp(readbuf, writebuf, subpgsize))) {
165		pr_info("error: verify failed at %#llx\n",
166		       (long long)addr);
167		pr_info("------------- written----------------\n");
168		print_subpage(writebuf);
169		pr_info("------------- read ------------------\n");
170		print_subpage(readbuf);
171		pr_info("-------------------------------------\n");
172		errcnt += 1;
173	}
174
175	return err;
176}
177
178static int verify_eraseblock2(int ebnum)
179{
180	size_t read;
181	int err = 0, k;
182	loff_t addr = (loff_t)ebnum * mtd->erasesize;
183
184	for (k = 1; k < 33; ++k) {
185		if (addr + (subpgsize * k) > (loff_t)(ebnum + 1) * mtd->erasesize)
186			break;
187		prandom_bytes_state(&rnd_state, writebuf, subpgsize * k);
188		clear_data(readbuf, subpgsize * k);
189		err = mtd_read(mtd, addr, subpgsize * k, &read, readbuf);
190		if (unlikely(err || read != subpgsize * k)) {
191			if (mtd_is_bitflip(err) && read == subpgsize * k) {
192				pr_info("ECC correction at %#llx\n",
193				       (long long)addr);
194				err = 0;
195			} else {
196				pr_err("error: read failed at "
197				       "%#llx\n", (long long)addr);
198				return err ? err : -1;
199			}
200		}
201		if (unlikely(memcmp(readbuf, writebuf, subpgsize * k))) {
202			pr_err("error: verify failed at %#llx\n",
203			       (long long)addr);
204			errcnt += 1;
205		}
206		addr += subpgsize * k;
207	}
208
209	return err;
210}
211
212static int verify_eraseblock_ff(int ebnum)
213{
214	uint32_t j;
215	size_t read;
216	int err = 0;
217	loff_t addr = (loff_t)ebnum * mtd->erasesize;
218
219	memset(writebuf, 0xff, subpgsize);
220	for (j = 0; j < mtd->erasesize / subpgsize; ++j) {
221		clear_data(readbuf, subpgsize);
222		err = mtd_read(mtd, addr, subpgsize, &read, readbuf);
223		if (unlikely(err || read != subpgsize)) {
224			if (mtd_is_bitflip(err) && read == subpgsize) {
225				pr_info("ECC correction at %#llx\n",
226				       (long long)addr);
227				err = 0;
228			} else {
229				pr_err("error: read failed at "
230				       "%#llx\n", (long long)addr);
231				return err ? err : -1;
232			}
233		}
234		if (unlikely(memcmp(readbuf, writebuf, subpgsize))) {
235			pr_err("error: verify 0xff failed at "
236			       "%#llx\n", (long long)addr);
237			errcnt += 1;
238		}
239		addr += subpgsize;
240	}
241
242	return err;
243}
244
245static int verify_all_eraseblocks_ff(void)
246{
247	int err;
248	unsigned int i;
249
250	pr_info("verifying all eraseblocks for 0xff\n");
251	for (i = 0; i < ebcnt; ++i) {
252		if (bbt[i])
253			continue;
254		err = verify_eraseblock_ff(i);
255		if (err)
256			return err;
257		if (i % 256 == 0)
258			pr_info("verified up to eraseblock %u\n", i);
259
260		err = mtdtest_relax();
261		if (err)
262			return err;
263	}
264	pr_info("verified %u eraseblocks\n", i);
265	return 0;
266}
267
268static int __init mtd_subpagetest_init(void)
269{
270	int err = 0;
271	uint32_t i;
272	uint64_t tmp;
273
274	printk(KERN_INFO "\n");
275	printk(KERN_INFO "=================================================\n");
276
277	if (dev < 0) {
278		pr_info("Please specify a valid mtd-device via module parameter\n");
279		pr_crit("CAREFUL: This test wipes all data on the specified MTD device!\n");
280		return -EINVAL;
281	}
282
283	pr_info("MTD device: %d\n", dev);
284
285	mtd = get_mtd_device(NULL, dev);
286	if (IS_ERR(mtd)) {
287		err = PTR_ERR(mtd);
288		pr_err("error: cannot get MTD device\n");
289		return err;
290	}
291
292	if (!mtd_type_is_nand(mtd)) {
293		pr_info("this test requires NAND flash\n");
294		goto out;
295	}
296
297	subpgsize = mtd->writesize >> mtd->subpage_sft;
298	tmp = mtd->size;
299	do_div(tmp, mtd->erasesize);
300	ebcnt = tmp;
301	pgcnt = mtd->erasesize / mtd->writesize;
302
303	pr_info("MTD device size %llu, eraseblock size %u, "
304	       "page size %u, subpage size %u, count of eraseblocks %u, "
305	       "pages per eraseblock %u, OOB size %u\n",
306	       (unsigned long long)mtd->size, mtd->erasesize,
307	       mtd->writesize, subpgsize, ebcnt, pgcnt, mtd->oobsize);
308
309	err = -ENOMEM;
310	bufsize = subpgsize * 32;
311	writebuf = kmalloc(bufsize, GFP_KERNEL);
312	if (!writebuf)
313		goto out;
314	readbuf = kmalloc(bufsize, GFP_KERNEL);
315	if (!readbuf)
316		goto out;
317	bbt = kzalloc(ebcnt, GFP_KERNEL);
318	if (!bbt)
319		goto out;
320
321	err = mtdtest_scan_for_bad_eraseblocks(mtd, bbt, 0, ebcnt);
322	if (err)
323		goto out;
324
325	err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt);
326	if (err)
327		goto out;
328
329	pr_info("writing whole device\n");
330	prandom_seed_state(&rnd_state, 1);
331	for (i = 0; i < ebcnt; ++i) {
332		if (bbt[i])
333			continue;
334		err = write_eraseblock(i);
335		if (unlikely(err))
336			goto out;
337		if (i % 256 == 0)
338			pr_info("written up to eraseblock %u\n", i);
339
340		err = mtdtest_relax();
341		if (err)
342			goto out;
343	}
344	pr_info("written %u eraseblocks\n", i);
345
346	prandom_seed_state(&rnd_state, 1);
347	pr_info("verifying all eraseblocks\n");
348	for (i = 0; i < ebcnt; ++i) {
349		if (bbt[i])
350			continue;
351		err = verify_eraseblock(i);
352		if (unlikely(err))
353			goto out;
354		if (i % 256 == 0)
355			pr_info("verified up to eraseblock %u\n", i);
356
357		err = mtdtest_relax();
358		if (err)
359			goto out;
360	}
361	pr_info("verified %u eraseblocks\n", i);
362
363	err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt);
364	if (err)
365		goto out;
366
367	err = verify_all_eraseblocks_ff();
368	if (err)
369		goto out;
370
371	/* Write all eraseblocks */
372	prandom_seed_state(&rnd_state, 3);
373	pr_info("writing whole device\n");
374	for (i = 0; i < ebcnt; ++i) {
375		if (bbt[i])
376			continue;
377		err = write_eraseblock2(i);
378		if (unlikely(err))
379			goto out;
380		if (i % 256 == 0)
381			pr_info("written up to eraseblock %u\n", i);
382
383		err = mtdtest_relax();
384		if (err)
385			goto out;
386	}
387	pr_info("written %u eraseblocks\n", i);
388
389	/* Check all eraseblocks */
390	prandom_seed_state(&rnd_state, 3);
391	pr_info("verifying all eraseblocks\n");
392	for (i = 0; i < ebcnt; ++i) {
393		if (bbt[i])
394			continue;
395		err = verify_eraseblock2(i);
396		if (unlikely(err))
397			goto out;
398		if (i % 256 == 0)
399			pr_info("verified up to eraseblock %u\n", i);
400
401		err = mtdtest_relax();
402		if (err)
403			goto out;
404	}
405	pr_info("verified %u eraseblocks\n", i);
406
407	err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt);
408	if (err)
409		goto out;
410
411	err = verify_all_eraseblocks_ff();
412	if (err)
413		goto out;
414
415	pr_info("finished with %d errors\n", errcnt);
416
417out:
418	kfree(bbt);
419	kfree(readbuf);
420	kfree(writebuf);
421	put_mtd_device(mtd);
422	if (err)
423		pr_info("error %d occurred\n", err);
424	printk(KERN_INFO "=================================================\n");
425	return err;
426}
427module_init(mtd_subpagetest_init);
428
429static void __exit mtd_subpagetest_exit(void)
430{
431	return;
432}
433module_exit(mtd_subpagetest_exit);
434
435MODULE_DESCRIPTION("Subpage test module");
436MODULE_AUTHOR("Adrian Hunter");
437MODULE_LICENSE("GPL");
438