brintos

brintos / linux-shallow public Read only

0
0
Text · 17.0 KiB · 13fed39 Raw
734 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2006-2008 Nokia Corporation4 *5 * Test OOB read and write on MTD device.6 *7 * Author: Adrian Hunter <ext-adrian.hunter@nokia.com>8 */9 10#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt11 12#include <asm/div64.h>13#include <linux/init.h>14#include <linux/module.h>15#include <linux/moduleparam.h>16#include <linux/err.h>17#include <linux/mtd/mtd.h>18#include <linux/slab.h>19#include <linux/sched.h>20#include <linux/random.h>21 22#include "mtd_test.h"23 24static int dev = -EINVAL;25static int bitflip_limit;26module_param(dev, int, S_IRUGO);27MODULE_PARM_DESC(dev, "MTD device number to use");28module_param(bitflip_limit, int, S_IRUGO);29MODULE_PARM_DESC(bitflip_limit, "Max. allowed bitflips per page");30 31static struct mtd_info *mtd;32static unsigned char *readbuf;33static unsigned char *writebuf;34static unsigned char *bbt;35 36static int ebcnt;37static int pgcnt;38static int errcnt;39static int use_offset;40static int use_len;41static int use_len_max;42static int vary_offset;43static struct rnd_state rnd_state;44 45static void do_vary_offset(void)46{47	use_len -= 1;48	if (use_len < 1) {49		use_offset += 1;50		if (use_offset >= use_len_max)51			use_offset = 0;52		use_len = use_len_max - use_offset;53	}54}55 56static int write_eraseblock(int ebnum)57{58	int i;59	struct mtd_oob_ops ops = { };60	int err = 0;61	loff_t addr = (loff_t)ebnum * mtd->erasesize;62 63	prandom_bytes_state(&rnd_state, writebuf, use_len_max * pgcnt);64	for (i = 0; i < pgcnt; ++i, addr += mtd->writesize) {65		ops.mode      = MTD_OPS_AUTO_OOB;66		ops.len       = 0;67		ops.retlen    = 0;68		ops.ooblen    = use_len;69		ops.oobretlen = 0;70		ops.ooboffs   = use_offset;71		ops.datbuf    = NULL;72		ops.oobbuf    = writebuf + (use_len_max * i) + use_offset;73		err = mtd_write_oob(mtd, addr, &ops);74		if (err || ops.oobretlen != use_len) {75			pr_err("error: writeoob failed at %#llx\n",76			       (long long)addr);77			pr_err("error: use_len %d, use_offset %d\n",78			       use_len, use_offset);79			errcnt += 1;80			return err ? err : -1;81		}82		if (vary_offset)83			do_vary_offset();84	}85 86	return err;87}88 89static int write_whole_device(void)90{91	int err;92	unsigned int i;93 94	pr_info("writing OOBs of whole device\n");95	for (i = 0; i < ebcnt; ++i) {96		if (bbt[i])97			continue;98		err = write_eraseblock(i);99		if (err)100			return err;101		if (i % 256 == 0)102			pr_info("written up to eraseblock %u\n", i);103 104		err = mtdtest_relax();105		if (err)106			return err;107	}108	pr_info("written %u eraseblocks\n", i);109	return 0;110}111 112/*113 * Display the address, offset and data bytes at comparison failure.114 * Return number of bitflips encountered.115 */116static size_t memcmpshowoffset(loff_t addr, loff_t offset, const void *cs,117			       const void *ct, size_t count)118{119	const unsigned char *su1, *su2;120	int res;121	size_t i = 0;122	size_t bitflips = 0;123 124	for (su1 = cs, su2 = ct; 0 < count; ++su1, ++su2, count--, i++) {125		res = *su1 ^ *su2;126		if (res) {127			pr_info("error @addr[0x%lx:0x%lx] 0x%x -> 0x%x diff 0x%x\n",128				(unsigned long)addr, (unsigned long)offset + i,129				*su1, *su2, res);130			bitflips += hweight8(res);131		}132	}133 134	return bitflips;135}136 137#define memcmpshow(addr, cs, ct, count) memcmpshowoffset((addr), 0, (cs), (ct),\138							 (count))139 140/*141 * Compare with 0xff and show the address, offset and data bytes at142 * comparison failure. Return number of bitflips encountered.143 */144static size_t memffshow(loff_t addr, loff_t offset, const void *cs,145			size_t count)146{147	const unsigned char *su1;148	int res;149	size_t i = 0;150	size_t bitflips = 0;151 152	for (su1 = cs; 0 < count; ++su1, count--, i++) {153		res = *su1 ^ 0xff;154		if (res) {155			pr_info("error @addr[0x%lx:0x%lx] 0x%x -> 0xff diff 0x%x\n",156				(unsigned long)addr, (unsigned long)offset + i,157				*su1, res);158			bitflips += hweight8(res);159		}160	}161 162	return bitflips;163}164 165static int verify_eraseblock(int ebnum)166{167	int i;168	struct mtd_oob_ops ops = { };169	int err = 0;170	loff_t addr = (loff_t)ebnum * mtd->erasesize;171	size_t bitflips;172 173	prandom_bytes_state(&rnd_state, writebuf, use_len_max * pgcnt);174	for (i = 0; i < pgcnt; ++i, addr += mtd->writesize) {175		ops.mode      = MTD_OPS_AUTO_OOB;176		ops.len       = 0;177		ops.retlen    = 0;178		ops.ooblen    = use_len;179		ops.oobretlen = 0;180		ops.ooboffs   = use_offset;181		ops.datbuf    = NULL;182		ops.oobbuf    = readbuf;183		err = mtd_read_oob(mtd, addr, &ops);184		if (mtd_is_bitflip(err))185			err = 0;186 187		if (err || ops.oobretlen != use_len) {188			pr_err("error: readoob failed at %#llx\n",189			       (long long)addr);190			errcnt += 1;191			return err ? err : -1;192		}193 194		bitflips = memcmpshow(addr, readbuf,195				      writebuf + (use_len_max * i) + use_offset,196				      use_len);197		if (bitflips > bitflip_limit) {198			pr_err("error: verify failed at %#llx\n",199			       (long long)addr);200			errcnt += 1;201			if (errcnt > 1000) {202				pr_err("error: too many errors\n");203				return -1;204			}205		} else if (bitflips) {206			pr_info("ignoring error as within bitflip_limit\n");207		}208 209		if (use_offset != 0 || use_len < mtd->oobavail) {210			int k;211 212			ops.mode      = MTD_OPS_AUTO_OOB;213			ops.len       = 0;214			ops.retlen    = 0;215			ops.ooblen    = mtd->oobavail;216			ops.oobretlen = 0;217			ops.ooboffs   = 0;218			ops.datbuf    = NULL;219			ops.oobbuf    = readbuf;220			err = mtd_read_oob(mtd, addr, &ops);221			if (mtd_is_bitflip(err))222				err = 0;223 224			if (err || ops.oobretlen != mtd->oobavail) {225				pr_err("error: readoob failed at %#llx\n",226						(long long)addr);227				errcnt += 1;228				return err ? err : -1;229			}230			bitflips = memcmpshowoffset(addr, use_offset,231						    readbuf + use_offset,232						    writebuf + (use_len_max * i) + use_offset,233						    use_len);234 235			/* verify pre-offset area for 0xff */236			bitflips += memffshow(addr, 0, readbuf, use_offset);237 238			/* verify post-(use_offset + use_len) area for 0xff */239			k = use_offset + use_len;240			bitflips += memffshow(addr, k, readbuf + k,241					      mtd->oobavail - k);242 243			if (bitflips > bitflip_limit) {244				pr_err("error: verify failed at %#llx\n",245						(long long)addr);246				errcnt += 1;247				if (errcnt > 1000) {248					pr_err("error: too many errors\n");249					return -1;250				}251			} else if (bitflips) {252				pr_info("ignoring errors as within bitflip limit\n");253			}254		}255		if (vary_offset)256			do_vary_offset();257	}258	return err;259}260 261static int verify_eraseblock_in_one_go(int ebnum)262{263	struct mtd_oob_ops ops = { };264	int err = 0;265	loff_t addr = (loff_t)ebnum * mtd->erasesize;266	size_t len = mtd->oobavail * pgcnt;267	size_t oobavail = mtd->oobavail;268	size_t bitflips;269	int i;270 271	prandom_bytes_state(&rnd_state, writebuf, len);272	ops.mode      = MTD_OPS_AUTO_OOB;273	ops.len       = 0;274	ops.retlen    = 0;275	ops.ooblen    = len;276	ops.oobretlen = 0;277	ops.ooboffs   = 0;278	ops.datbuf    = NULL;279	ops.oobbuf    = readbuf;280 281	/* read entire block's OOB at one go */282	err = mtd_read_oob(mtd, addr, &ops);283	if (mtd_is_bitflip(err))284		err = 0;285 286	if (err || ops.oobretlen != len) {287		pr_err("error: readoob failed at %#llx\n",288		       (long long)addr);289		errcnt += 1;290		return err ? err : -1;291	}292 293	/* verify one page OOB at a time for bitflip per page limit check */294	for (i = 0; i < pgcnt; ++i, addr += mtd->writesize) {295		bitflips = memcmpshow(addr, readbuf + (i * oobavail),296				      writebuf + (i * oobavail), oobavail);297		if (bitflips > bitflip_limit) {298			pr_err("error: verify failed at %#llx\n",299			       (long long)addr);300			errcnt += 1;301			if (errcnt > 1000) {302				pr_err("error: too many errors\n");303				return -1;304			}305		} else if (bitflips) {306			pr_info("ignoring error as within bitflip_limit\n");307		}308	}309 310	return err;311}312 313static int verify_all_eraseblocks(void)314{315	int err;316	unsigned int i;317 318	pr_info("verifying all eraseblocks\n");319	for (i = 0; i < ebcnt; ++i) {320		if (bbt[i])321			continue;322		err = verify_eraseblock(i);323		if (err)324			return err;325		if (i % 256 == 0)326			pr_info("verified up to eraseblock %u\n", i);327 328		err = mtdtest_relax();329		if (err)330			return err;331	}332	pr_info("verified %u eraseblocks\n", i);333	return 0;334}335 336static int __init mtd_oobtest_init(void)337{338	int err = 0;339	unsigned int i;340	uint64_t tmp;341	struct mtd_oob_ops ops = { };342	loff_t addr = 0, addr0;343 344	printk(KERN_INFO "\n");345	printk(KERN_INFO "=================================================\n");346 347	if (dev < 0) {348		pr_info("Please specify a valid mtd-device via module parameter\n");349		pr_crit("CAREFUL: This test wipes all data on the specified MTD device!\n");350		return -EINVAL;351	}352 353	pr_info("MTD device: %d\n", dev);354 355	mtd = get_mtd_device(NULL, dev);356	if (IS_ERR(mtd)) {357		err = PTR_ERR(mtd);358		pr_err("error: cannot get MTD device\n");359		return err;360	}361 362	if (!mtd_type_is_nand(mtd)) {363		pr_info("this test requires NAND flash\n");364		goto out;365	}366 367	tmp = mtd->size;368	do_div(tmp, mtd->erasesize);369	ebcnt = tmp;370	pgcnt = mtd->erasesize / mtd->writesize;371 372	pr_info("MTD device size %llu, eraseblock size %u, "373	       "page size %u, count of eraseblocks %u, pages per "374	       "eraseblock %u, OOB size %u\n",375	       (unsigned long long)mtd->size, mtd->erasesize,376	       mtd->writesize, ebcnt, pgcnt, mtd->oobsize);377 378	err = -ENOMEM;379	readbuf = kmalloc(mtd->erasesize, GFP_KERNEL);380	if (!readbuf)381		goto out;382	writebuf = kmalloc(mtd->erasesize, GFP_KERNEL);383	if (!writebuf)384		goto out;385	bbt = kzalloc(ebcnt, GFP_KERNEL);386	if (!bbt)387		goto out;388 389	err = mtdtest_scan_for_bad_eraseblocks(mtd, bbt, 0, ebcnt);390	if (err)391		goto out;392 393	use_offset = 0;394	use_len = mtd->oobavail;395	use_len_max = mtd->oobavail;396	vary_offset = 0;397 398	/* First test: write all OOB, read it back and verify */399	pr_info("test 1 of 5\n");400 401	err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt);402	if (err)403		goto out;404 405	prandom_seed_state(&rnd_state, 1);406	err = write_whole_device();407	if (err)408		goto out;409 410	prandom_seed_state(&rnd_state, 1);411	err = verify_all_eraseblocks();412	if (err)413		goto out;414 415	/*416	 * Second test: write all OOB, a block at a time, read it back and417	 * verify.418	 */419	pr_info("test 2 of 5\n");420 421	err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt);422	if (err)423		goto out;424 425	prandom_seed_state(&rnd_state, 3);426	err = write_whole_device();427	if (err)428		goto out;429 430	/* Check all eraseblocks */431	prandom_seed_state(&rnd_state, 3);432	pr_info("verifying all eraseblocks\n");433	for (i = 0; i < ebcnt; ++i) {434		if (bbt[i])435			continue;436		err = verify_eraseblock_in_one_go(i);437		if (err)438			goto out;439		if (i % 256 == 0)440			pr_info("verified up to eraseblock %u\n", i);441 442		err = mtdtest_relax();443		if (err)444			goto out;445	}446	pr_info("verified %u eraseblocks\n", i);447 448	/*449	 * Third test: write OOB at varying offsets and lengths, read it back450	 * and verify.451	 */452	pr_info("test 3 of 5\n");453 454	err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt);455	if (err)456		goto out;457 458	/* Write all eraseblocks */459	use_offset = 0;460	use_len = mtd->oobavail;461	use_len_max = mtd->oobavail;462	vary_offset = 1;463	prandom_seed_state(&rnd_state, 5);464 465	err = write_whole_device();466	if (err)467		goto out;468 469	/* Check all eraseblocks */470	use_offset = 0;471	use_len = mtd->oobavail;472	use_len_max = mtd->oobavail;473	vary_offset = 1;474	prandom_seed_state(&rnd_state, 5);475	err = verify_all_eraseblocks();476	if (err)477		goto out;478 479	use_offset = 0;480	use_len = mtd->oobavail;481	use_len_max = mtd->oobavail;482	vary_offset = 0;483 484	/* Fourth test: try to write off end of device */485	pr_info("test 4 of 5\n");486 487	err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt);488	if (err)489		goto out;490 491	addr0 = 0;492	for (i = 0; i < ebcnt && bbt[i]; ++i)493		addr0 += mtd->erasesize;494 495	/* Attempt to write off end of OOB */496	ops.mode      = MTD_OPS_AUTO_OOB;497	ops.len       = 0;498	ops.retlen    = 0;499	ops.ooblen    = 1;500	ops.oobretlen = 0;501	ops.ooboffs   = mtd->oobavail;502	ops.datbuf    = NULL;503	ops.oobbuf    = writebuf;504	pr_info("attempting to start write past end of OOB\n");505	pr_info("an error is expected...\n");506	err = mtd_write_oob(mtd, addr0, &ops);507	if (err) {508		pr_info("error occurred as expected\n");509	} else {510		pr_err("error: can write past end of OOB\n");511		errcnt += 1;512	}513 514	/* Attempt to read off end of OOB */515	ops.mode      = MTD_OPS_AUTO_OOB;516	ops.len       = 0;517	ops.retlen    = 0;518	ops.ooblen    = 1;519	ops.oobretlen = 0;520	ops.ooboffs   = mtd->oobavail;521	ops.datbuf    = NULL;522	ops.oobbuf    = readbuf;523	pr_info("attempting to start read past end of OOB\n");524	pr_info("an error is expected...\n");525	err = mtd_read_oob(mtd, addr0, &ops);526	if (mtd_is_bitflip(err))527		err = 0;528 529	if (err) {530		pr_info("error occurred as expected\n");531	} else {532		pr_err("error: can read past end of OOB\n");533		errcnt += 1;534	}535 536	if (bbt[ebcnt - 1])537		pr_info("skipping end of device tests because last "538		       "block is bad\n");539	else {540		/* Attempt to write off end of device */541		ops.mode      = MTD_OPS_AUTO_OOB;542		ops.len       = 0;543		ops.retlen    = 0;544		ops.ooblen    = mtd->oobavail + 1;545		ops.oobretlen = 0;546		ops.ooboffs   = 0;547		ops.datbuf    = NULL;548		ops.oobbuf    = writebuf;549		pr_info("attempting to write past end of device\n");550		pr_info("an error is expected...\n");551		err = mtd_write_oob(mtd, mtd->size - mtd->writesize, &ops);552		if (err) {553			pr_info("error occurred as expected\n");554		} else {555			pr_err("error: wrote past end of device\n");556			errcnt += 1;557		}558 559		/* Attempt to read off end of device */560		ops.mode      = MTD_OPS_AUTO_OOB;561		ops.len       = 0;562		ops.retlen    = 0;563		ops.ooblen    = mtd->oobavail + 1;564		ops.oobretlen = 0;565		ops.ooboffs   = 0;566		ops.datbuf    = NULL;567		ops.oobbuf    = readbuf;568		pr_info("attempting to read past end of device\n");569		pr_info("an error is expected...\n");570		err = mtd_read_oob(mtd, mtd->size - mtd->writesize, &ops);571		if (mtd_is_bitflip(err))572			err = 0;573 574		if (err) {575			pr_info("error occurred as expected\n");576		} else {577			pr_err("error: read past end of device\n");578			errcnt += 1;579		}580 581		err = mtdtest_erase_eraseblock(mtd, ebcnt - 1);582		if (err)583			goto out;584 585		/* Attempt to write off end of device */586		ops.mode      = MTD_OPS_AUTO_OOB;587		ops.len       = 0;588		ops.retlen    = 0;589		ops.ooblen    = mtd->oobavail;590		ops.oobretlen = 0;591		ops.ooboffs   = 1;592		ops.datbuf    = NULL;593		ops.oobbuf    = writebuf;594		pr_info("attempting to write past end of device\n");595		pr_info("an error is expected...\n");596		err = mtd_write_oob(mtd, mtd->size - mtd->writesize, &ops);597		if (err) {598			pr_info("error occurred as expected\n");599		} else {600			pr_err("error: wrote past end of device\n");601			errcnt += 1;602		}603 604		/* Attempt to read off end of device */605		ops.mode      = MTD_OPS_AUTO_OOB;606		ops.len       = 0;607		ops.retlen    = 0;608		ops.ooblen    = mtd->oobavail;609		ops.oobretlen = 0;610		ops.ooboffs   = 1;611		ops.datbuf    = NULL;612		ops.oobbuf    = readbuf;613		pr_info("attempting to read past end of device\n");614		pr_info("an error is expected...\n");615		err = mtd_read_oob(mtd, mtd->size - mtd->writesize, &ops);616		if (mtd_is_bitflip(err))617			err = 0;618 619		if (err) {620			pr_info("error occurred as expected\n");621		} else {622			pr_err("error: read past end of device\n");623			errcnt += 1;624		}625	}626 627	/* Fifth test: write / read across block boundaries */628	pr_info("test 5 of 5\n");629 630	/* Erase all eraseblocks */631	err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt);632	if (err)633		goto out;634 635	/* Write all eraseblocks */636	prandom_seed_state(&rnd_state, 11);637	pr_info("writing OOBs of whole device\n");638	for (i = 0; i < ebcnt - 1; ++i) {639		int cnt = 2;640		int pg;641		size_t sz = mtd->oobavail;642		if (bbt[i] || bbt[i + 1])643			continue;644		addr = (loff_t)(i + 1) * mtd->erasesize - mtd->writesize;645		prandom_bytes_state(&rnd_state, writebuf, sz * cnt);646		for (pg = 0; pg < cnt; ++pg) {647			ops.mode      = MTD_OPS_AUTO_OOB;648			ops.len       = 0;649			ops.retlen    = 0;650			ops.ooblen    = sz;651			ops.oobretlen = 0;652			ops.ooboffs   = 0;653			ops.datbuf    = NULL;654			ops.oobbuf    = writebuf + pg * sz;655			err = mtd_write_oob(mtd, addr, &ops);656			if (err)657				goto out;658			if (i % 256 == 0)659				pr_info("written up to eraseblock %u\n", i);660 661			err = mtdtest_relax();662			if (err)663				goto out;664 665			addr += mtd->writesize;666		}667	}668	pr_info("written %u eraseblocks\n", i);669 670	/* Check all eraseblocks */671	prandom_seed_state(&rnd_state, 11);672	pr_info("verifying all eraseblocks\n");673	for (i = 0; i < ebcnt - 1; ++i) {674		if (bbt[i] || bbt[i + 1])675			continue;676		prandom_bytes_state(&rnd_state, writebuf, mtd->oobavail * 2);677		addr = (loff_t)(i + 1) * mtd->erasesize - mtd->writesize;678		ops.mode      = MTD_OPS_AUTO_OOB;679		ops.len       = 0;680		ops.retlen    = 0;681		ops.ooblen    = mtd->oobavail * 2;682		ops.oobretlen = 0;683		ops.ooboffs   = 0;684		ops.datbuf    = NULL;685		ops.oobbuf    = readbuf;686		err = mtd_read_oob(mtd, addr, &ops);687		if (mtd_is_bitflip(err))688			err = 0;689 690		if (err)691			goto out;692		if (memcmpshow(addr, readbuf, writebuf,693			       mtd->oobavail * 2)) {694			pr_err("error: verify failed at %#llx\n",695			       (long long)addr);696			errcnt += 1;697			if (errcnt > 1000) {698				err = -EINVAL;699				pr_err("error: too many errors\n");700				goto out;701			}702		}703		if (i % 256 == 0)704			pr_info("verified up to eraseblock %u\n", i);705 706		err = mtdtest_relax();707		if (err)708			goto out;709	}710	pr_info("verified %u eraseblocks\n", i);711 712	pr_info("finished with %d errors\n", errcnt);713out:714	kfree(bbt);715	kfree(writebuf);716	kfree(readbuf);717	put_mtd_device(mtd);718	if (err)719		pr_info("error %d occurred\n", err);720	printk(KERN_INFO "=================================================\n");721	return err;722}723module_init(mtd_oobtest_init);724 725static void __exit mtd_oobtest_exit(void)726{727	return;728}729module_exit(mtd_oobtest_exit);730 731MODULE_DESCRIPTION("Out-of-band test module");732MODULE_AUTHOR("Adrian Hunter");733MODULE_LICENSE("GPL");734