162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2006-2008 Artem Bityutskiy 462306a36Sopenharmony_ci * Copyright (C) 2006-2008 Jarkko Lavinen 562306a36Sopenharmony_ci * Copyright (C) 2006-2008 Adrian Hunter 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Authors: Artem Bityutskiy, Jarkko Lavinen, Adria Hunter 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * WARNING: this test program may kill your flash and your device. Do not 1062306a36Sopenharmony_ci * use it unless you know what you do. Authors are not responsible for any 1162306a36Sopenharmony_ci * damage caused by this program. 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include <linux/init.h> 1762306a36Sopenharmony_ci#include <linux/ktime.h> 1862306a36Sopenharmony_ci#include <linux/module.h> 1962306a36Sopenharmony_ci#include <linux/moduleparam.h> 2062306a36Sopenharmony_ci#include <linux/err.h> 2162306a36Sopenharmony_ci#include <linux/mtd/mtd.h> 2262306a36Sopenharmony_ci#include <linux/slab.h> 2362306a36Sopenharmony_ci#include <linux/sched.h> 2462306a36Sopenharmony_ci#include "mtd_test.h" 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define RETRIES 3 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic int eb = 8; 2962306a36Sopenharmony_cimodule_param(eb, int, S_IRUGO); 3062306a36Sopenharmony_ciMODULE_PARM_DESC(eb, "eraseblock number within the selected MTD device"); 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistatic int ebcnt = 32; 3362306a36Sopenharmony_cimodule_param(ebcnt, int, S_IRUGO); 3462306a36Sopenharmony_ciMODULE_PARM_DESC(ebcnt, "number of consecutive eraseblocks to torture"); 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistatic int pgcnt; 3762306a36Sopenharmony_cimodule_param(pgcnt, int, S_IRUGO); 3862306a36Sopenharmony_ciMODULE_PARM_DESC(pgcnt, "number of pages per eraseblock to torture (0 => all)"); 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistatic int dev = -EINVAL; 4162306a36Sopenharmony_cimodule_param(dev, int, S_IRUGO); 4262306a36Sopenharmony_ciMODULE_PARM_DESC(dev, "MTD device number to use"); 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistatic int gran = 512; 4562306a36Sopenharmony_cimodule_param(gran, int, S_IRUGO); 4662306a36Sopenharmony_ciMODULE_PARM_DESC(gran, "how often the status information should be printed"); 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic int check = 1; 4962306a36Sopenharmony_cimodule_param(check, int, S_IRUGO); 5062306a36Sopenharmony_ciMODULE_PARM_DESC(check, "if the written data should be checked"); 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic unsigned int cycles_count; 5362306a36Sopenharmony_cimodule_param(cycles_count, uint, S_IRUGO); 5462306a36Sopenharmony_ciMODULE_PARM_DESC(cycles_count, "how many erase cycles to do " 5562306a36Sopenharmony_ci "(infinite by default)"); 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic struct mtd_info *mtd; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci/* This buffer contains 0x555555...0xAAAAAA... pattern */ 6062306a36Sopenharmony_cistatic unsigned char *patt_5A5; 6162306a36Sopenharmony_ci/* This buffer contains 0xAAAAAA...0x555555... pattern */ 6262306a36Sopenharmony_cistatic unsigned char *patt_A5A; 6362306a36Sopenharmony_ci/* This buffer contains all 0xFF bytes */ 6462306a36Sopenharmony_cistatic unsigned char *patt_FF; 6562306a36Sopenharmony_ci/* This a temporary buffer is use when checking data */ 6662306a36Sopenharmony_cistatic unsigned char *check_buf; 6762306a36Sopenharmony_ci/* How many erase cycles were done */ 6862306a36Sopenharmony_cistatic unsigned int erase_cycles; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistatic int pgsize; 7162306a36Sopenharmony_cistatic ktime_t start, finish; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic void report_corrupt(unsigned char *read, unsigned char *written); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic inline void start_timing(void) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci start = ktime_get(); 7862306a36Sopenharmony_ci} 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_cistatic inline void stop_timing(void) 8162306a36Sopenharmony_ci{ 8262306a36Sopenharmony_ci finish = ktime_get(); 8362306a36Sopenharmony_ci} 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci/* 8662306a36Sopenharmony_ci * Check that the contents of eraseblock number @enbum is equivalent to the 8762306a36Sopenharmony_ci * @buf buffer. 8862306a36Sopenharmony_ci */ 8962306a36Sopenharmony_cistatic inline int check_eraseblock(int ebnum, unsigned char *buf) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci int err, retries = 0; 9262306a36Sopenharmony_ci size_t read; 9362306a36Sopenharmony_ci loff_t addr = (loff_t)ebnum * mtd->erasesize; 9462306a36Sopenharmony_ci size_t len = mtd->erasesize; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci if (pgcnt) { 9762306a36Sopenharmony_ci addr = (loff_t)(ebnum + 1) * mtd->erasesize - pgcnt * pgsize; 9862306a36Sopenharmony_ci len = pgcnt * pgsize; 9962306a36Sopenharmony_ci } 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ciretry: 10262306a36Sopenharmony_ci err = mtd_read(mtd, addr, len, &read, check_buf); 10362306a36Sopenharmony_ci if (mtd_is_bitflip(err)) 10462306a36Sopenharmony_ci pr_err("single bit flip occurred at EB %d " 10562306a36Sopenharmony_ci "MTD reported that it was fixed.\n", ebnum); 10662306a36Sopenharmony_ci else if (err) { 10762306a36Sopenharmony_ci pr_err("error %d while reading EB %d, " 10862306a36Sopenharmony_ci "read %zd\n", err, ebnum, read); 10962306a36Sopenharmony_ci return err; 11062306a36Sopenharmony_ci } 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci if (read != len) { 11362306a36Sopenharmony_ci pr_err("failed to read %zd bytes from EB %d, " 11462306a36Sopenharmony_ci "read only %zd, but no error reported\n", 11562306a36Sopenharmony_ci len, ebnum, read); 11662306a36Sopenharmony_ci return -EIO; 11762306a36Sopenharmony_ci } 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci if (memcmp(buf, check_buf, len)) { 12062306a36Sopenharmony_ci pr_err("read wrong data from EB %d\n", ebnum); 12162306a36Sopenharmony_ci report_corrupt(check_buf, buf); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci if (retries++ < RETRIES) { 12462306a36Sopenharmony_ci /* Try read again */ 12562306a36Sopenharmony_ci yield(); 12662306a36Sopenharmony_ci pr_info("re-try reading data from EB %d\n", 12762306a36Sopenharmony_ci ebnum); 12862306a36Sopenharmony_ci goto retry; 12962306a36Sopenharmony_ci } else { 13062306a36Sopenharmony_ci pr_info("retried %d times, still errors, " 13162306a36Sopenharmony_ci "give-up\n", RETRIES); 13262306a36Sopenharmony_ci return -EINVAL; 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci } 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci if (retries != 0) 13762306a36Sopenharmony_ci pr_info("only attempt number %d was OK (!!!)\n", 13862306a36Sopenharmony_ci retries); 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci return 0; 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_cistatic inline int write_pattern(int ebnum, void *buf) 14462306a36Sopenharmony_ci{ 14562306a36Sopenharmony_ci int err; 14662306a36Sopenharmony_ci size_t written; 14762306a36Sopenharmony_ci loff_t addr = (loff_t)ebnum * mtd->erasesize; 14862306a36Sopenharmony_ci size_t len = mtd->erasesize; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci if (pgcnt) { 15162306a36Sopenharmony_ci addr = (loff_t)(ebnum + 1) * mtd->erasesize - pgcnt * pgsize; 15262306a36Sopenharmony_ci len = pgcnt * pgsize; 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci err = mtd_write(mtd, addr, len, &written, buf); 15562306a36Sopenharmony_ci if (err) { 15662306a36Sopenharmony_ci pr_err("error %d while writing EB %d, written %zd" 15762306a36Sopenharmony_ci " bytes\n", err, ebnum, written); 15862306a36Sopenharmony_ci return err; 15962306a36Sopenharmony_ci } 16062306a36Sopenharmony_ci if (written != len) { 16162306a36Sopenharmony_ci pr_info("written only %zd bytes of %zd, but no error" 16262306a36Sopenharmony_ci " reported\n", written, len); 16362306a36Sopenharmony_ci return -EIO; 16462306a36Sopenharmony_ci } 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci return 0; 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistatic int __init tort_init(void) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci int err = 0, i, infinite = !cycles_count; 17262306a36Sopenharmony_ci unsigned char *bad_ebs; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci printk(KERN_INFO "\n"); 17562306a36Sopenharmony_ci printk(KERN_INFO "=================================================\n"); 17662306a36Sopenharmony_ci pr_info("Warning: this program is trying to wear out your " 17762306a36Sopenharmony_ci "flash, stop it if this is not wanted.\n"); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci if (dev < 0) { 18062306a36Sopenharmony_ci pr_info("Please specify a valid mtd-device via module parameter\n"); 18162306a36Sopenharmony_ci pr_crit("CAREFUL: This test wipes all data on the specified MTD device!\n"); 18262306a36Sopenharmony_ci return -EINVAL; 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci pr_info("MTD device: %d\n", dev); 18662306a36Sopenharmony_ci pr_info("torture %d eraseblocks (%d-%d) of mtd%d\n", 18762306a36Sopenharmony_ci ebcnt, eb, eb + ebcnt - 1, dev); 18862306a36Sopenharmony_ci if (pgcnt) 18962306a36Sopenharmony_ci pr_info("torturing just %d pages per eraseblock\n", 19062306a36Sopenharmony_ci pgcnt); 19162306a36Sopenharmony_ci pr_info("write verify %s\n", check ? "enabled" : "disabled"); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci mtd = get_mtd_device(NULL, dev); 19462306a36Sopenharmony_ci if (IS_ERR(mtd)) { 19562306a36Sopenharmony_ci err = PTR_ERR(mtd); 19662306a36Sopenharmony_ci pr_err("error: cannot get MTD device\n"); 19762306a36Sopenharmony_ci return err; 19862306a36Sopenharmony_ci } 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci if (mtd->writesize == 1) { 20162306a36Sopenharmony_ci pr_info("not NAND flash, assume page size is 512 " 20262306a36Sopenharmony_ci "bytes.\n"); 20362306a36Sopenharmony_ci pgsize = 512; 20462306a36Sopenharmony_ci } else 20562306a36Sopenharmony_ci pgsize = mtd->writesize; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci if (pgcnt && (pgcnt > mtd->erasesize / pgsize || pgcnt < 0)) { 20862306a36Sopenharmony_ci pr_err("error: invalid pgcnt value %d\n", pgcnt); 20962306a36Sopenharmony_ci goto out_mtd; 21062306a36Sopenharmony_ci } 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci err = -ENOMEM; 21362306a36Sopenharmony_ci patt_5A5 = kmalloc(mtd->erasesize, GFP_KERNEL); 21462306a36Sopenharmony_ci if (!patt_5A5) 21562306a36Sopenharmony_ci goto out_mtd; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci patt_A5A = kmalloc(mtd->erasesize, GFP_KERNEL); 21862306a36Sopenharmony_ci if (!patt_A5A) 21962306a36Sopenharmony_ci goto out_patt_5A5; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci patt_FF = kmalloc(mtd->erasesize, GFP_KERNEL); 22262306a36Sopenharmony_ci if (!patt_FF) 22362306a36Sopenharmony_ci goto out_patt_A5A; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci check_buf = kmalloc(mtd->erasesize, GFP_KERNEL); 22662306a36Sopenharmony_ci if (!check_buf) 22762306a36Sopenharmony_ci goto out_patt_FF; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci bad_ebs = kzalloc(ebcnt, GFP_KERNEL); 23062306a36Sopenharmony_ci if (!bad_ebs) 23162306a36Sopenharmony_ci goto out_check_buf; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci /* Initialize patterns */ 23462306a36Sopenharmony_ci memset(patt_FF, 0xFF, mtd->erasesize); 23562306a36Sopenharmony_ci for (i = 0; i < mtd->erasesize / pgsize; i++) { 23662306a36Sopenharmony_ci if (!(i & 1)) { 23762306a36Sopenharmony_ci memset(patt_5A5 + i * pgsize, 0x55, pgsize); 23862306a36Sopenharmony_ci memset(patt_A5A + i * pgsize, 0xAA, pgsize); 23962306a36Sopenharmony_ci } else { 24062306a36Sopenharmony_ci memset(patt_5A5 + i * pgsize, 0xAA, pgsize); 24162306a36Sopenharmony_ci memset(patt_A5A + i * pgsize, 0x55, pgsize); 24262306a36Sopenharmony_ci } 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci err = mtdtest_scan_for_bad_eraseblocks(mtd, bad_ebs, eb, ebcnt); 24662306a36Sopenharmony_ci if (err) 24762306a36Sopenharmony_ci goto out; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci start_timing(); 25062306a36Sopenharmony_ci while (1) { 25162306a36Sopenharmony_ci int i; 25262306a36Sopenharmony_ci void *patt; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci err = mtdtest_erase_good_eraseblocks(mtd, bad_ebs, eb, ebcnt); 25562306a36Sopenharmony_ci if (err) 25662306a36Sopenharmony_ci goto out; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci /* Check if the eraseblocks contain only 0xFF bytes */ 25962306a36Sopenharmony_ci if (check) { 26062306a36Sopenharmony_ci for (i = eb; i < eb + ebcnt; i++) { 26162306a36Sopenharmony_ci if (bad_ebs[i - eb]) 26262306a36Sopenharmony_ci continue; 26362306a36Sopenharmony_ci err = check_eraseblock(i, patt_FF); 26462306a36Sopenharmony_ci if (err) { 26562306a36Sopenharmony_ci pr_info("verify failed" 26662306a36Sopenharmony_ci " for 0xFF... pattern\n"); 26762306a36Sopenharmony_ci goto out; 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci err = mtdtest_relax(); 27162306a36Sopenharmony_ci if (err) 27262306a36Sopenharmony_ci goto out; 27362306a36Sopenharmony_ci } 27462306a36Sopenharmony_ci } 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci /* Write the pattern */ 27762306a36Sopenharmony_ci for (i = eb; i < eb + ebcnt; i++) { 27862306a36Sopenharmony_ci if (bad_ebs[i - eb]) 27962306a36Sopenharmony_ci continue; 28062306a36Sopenharmony_ci if ((eb + erase_cycles) & 1) 28162306a36Sopenharmony_ci patt = patt_5A5; 28262306a36Sopenharmony_ci else 28362306a36Sopenharmony_ci patt = patt_A5A; 28462306a36Sopenharmony_ci err = write_pattern(i, patt); 28562306a36Sopenharmony_ci if (err) 28662306a36Sopenharmony_ci goto out; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci err = mtdtest_relax(); 28962306a36Sopenharmony_ci if (err) 29062306a36Sopenharmony_ci goto out; 29162306a36Sopenharmony_ci } 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci /* Verify what we wrote */ 29462306a36Sopenharmony_ci if (check) { 29562306a36Sopenharmony_ci for (i = eb; i < eb + ebcnt; i++) { 29662306a36Sopenharmony_ci if (bad_ebs[i - eb]) 29762306a36Sopenharmony_ci continue; 29862306a36Sopenharmony_ci if ((eb + erase_cycles) & 1) 29962306a36Sopenharmony_ci patt = patt_5A5; 30062306a36Sopenharmony_ci else 30162306a36Sopenharmony_ci patt = patt_A5A; 30262306a36Sopenharmony_ci err = check_eraseblock(i, patt); 30362306a36Sopenharmony_ci if (err) { 30462306a36Sopenharmony_ci pr_info("verify failed for %s" 30562306a36Sopenharmony_ci " pattern\n", 30662306a36Sopenharmony_ci ((eb + erase_cycles) & 1) ? 30762306a36Sopenharmony_ci "0x55AA55..." : "0xAA55AA..."); 30862306a36Sopenharmony_ci goto out; 30962306a36Sopenharmony_ci } 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci err = mtdtest_relax(); 31262306a36Sopenharmony_ci if (err) 31362306a36Sopenharmony_ci goto out; 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci erase_cycles += 1; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci if (erase_cycles % gran == 0) { 32062306a36Sopenharmony_ci long ms; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci stop_timing(); 32362306a36Sopenharmony_ci ms = ktime_ms_delta(finish, start); 32462306a36Sopenharmony_ci pr_info("%08u erase cycles done, took %lu " 32562306a36Sopenharmony_ci "milliseconds (%lu seconds)\n", 32662306a36Sopenharmony_ci erase_cycles, ms, ms / 1000); 32762306a36Sopenharmony_ci start_timing(); 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci if (!infinite && --cycles_count == 0) 33162306a36Sopenharmony_ci break; 33262306a36Sopenharmony_ci } 33362306a36Sopenharmony_ciout: 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci pr_info("finished after %u erase cycles\n", 33662306a36Sopenharmony_ci erase_cycles); 33762306a36Sopenharmony_ci kfree(bad_ebs); 33862306a36Sopenharmony_ciout_check_buf: 33962306a36Sopenharmony_ci kfree(check_buf); 34062306a36Sopenharmony_ciout_patt_FF: 34162306a36Sopenharmony_ci kfree(patt_FF); 34262306a36Sopenharmony_ciout_patt_A5A: 34362306a36Sopenharmony_ci kfree(patt_A5A); 34462306a36Sopenharmony_ciout_patt_5A5: 34562306a36Sopenharmony_ci kfree(patt_5A5); 34662306a36Sopenharmony_ciout_mtd: 34762306a36Sopenharmony_ci put_mtd_device(mtd); 34862306a36Sopenharmony_ci if (err) 34962306a36Sopenharmony_ci pr_info("error %d occurred during torturing\n", err); 35062306a36Sopenharmony_ci printk(KERN_INFO "=================================================\n"); 35162306a36Sopenharmony_ci return err; 35262306a36Sopenharmony_ci} 35362306a36Sopenharmony_cimodule_init(tort_init); 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_cistatic void __exit tort_exit(void) 35662306a36Sopenharmony_ci{ 35762306a36Sopenharmony_ci return; 35862306a36Sopenharmony_ci} 35962306a36Sopenharmony_cimodule_exit(tort_exit); 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_cistatic int countdiffs(unsigned char *buf, unsigned char *check_buf, 36262306a36Sopenharmony_ci unsigned offset, unsigned len, unsigned *bytesp, 36362306a36Sopenharmony_ci unsigned *bitsp); 36462306a36Sopenharmony_cistatic void print_bufs(unsigned char *read, unsigned char *written, int start, 36562306a36Sopenharmony_ci int len); 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci/* 36862306a36Sopenharmony_ci * Report the detailed information about how the read EB differs from what was 36962306a36Sopenharmony_ci * written. 37062306a36Sopenharmony_ci */ 37162306a36Sopenharmony_cistatic void report_corrupt(unsigned char *read, unsigned char *written) 37262306a36Sopenharmony_ci{ 37362306a36Sopenharmony_ci int i; 37462306a36Sopenharmony_ci int bytes, bits, pages, first; 37562306a36Sopenharmony_ci int offset, len; 37662306a36Sopenharmony_ci size_t check_len = mtd->erasesize; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci if (pgcnt) 37962306a36Sopenharmony_ci check_len = pgcnt * pgsize; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci bytes = bits = pages = 0; 38262306a36Sopenharmony_ci for (i = 0; i < check_len; i += pgsize) 38362306a36Sopenharmony_ci if (countdiffs(written, read, i, pgsize, &bytes, 38462306a36Sopenharmony_ci &bits) >= 0) 38562306a36Sopenharmony_ci pages++; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci pr_info("verify fails on %d pages, %d bytes/%d bits\n", 38862306a36Sopenharmony_ci pages, bytes, bits); 38962306a36Sopenharmony_ci pr_info("The following is a list of all differences between" 39062306a36Sopenharmony_ci " what was read from flash and what was expected\n"); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci for (i = 0; i < check_len; i += pgsize) { 39362306a36Sopenharmony_ci cond_resched(); 39462306a36Sopenharmony_ci bytes = bits = 0; 39562306a36Sopenharmony_ci first = countdiffs(written, read, i, pgsize, &bytes, 39662306a36Sopenharmony_ci &bits); 39762306a36Sopenharmony_ci if (first < 0) 39862306a36Sopenharmony_ci continue; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci printk("-------------------------------------------------------" 40162306a36Sopenharmony_ci "----------------------------------\n"); 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci pr_info("Page %zd has %d bytes/%d bits failing verify," 40462306a36Sopenharmony_ci " starting at offset 0x%x\n", 40562306a36Sopenharmony_ci (mtd->erasesize - check_len + i) / pgsize, 40662306a36Sopenharmony_ci bytes, bits, first); 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci offset = first & ~0x7; 40962306a36Sopenharmony_ci len = ((first + bytes) | 0x7) + 1 - offset; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci print_bufs(read, written, offset, len); 41262306a36Sopenharmony_ci } 41362306a36Sopenharmony_ci} 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_cistatic void print_bufs(unsigned char *read, unsigned char *written, int start, 41662306a36Sopenharmony_ci int len) 41762306a36Sopenharmony_ci{ 41862306a36Sopenharmony_ci int i = 0, j1, j2; 41962306a36Sopenharmony_ci char *diff; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci printk("Offset Read Written\n"); 42262306a36Sopenharmony_ci while (i < len) { 42362306a36Sopenharmony_ci printk("0x%08x: ", start + i); 42462306a36Sopenharmony_ci diff = " "; 42562306a36Sopenharmony_ci for (j1 = 0; j1 < 8 && i + j1 < len; j1++) { 42662306a36Sopenharmony_ci printk(" %02x", read[start + i + j1]); 42762306a36Sopenharmony_ci if (read[start + i + j1] != written[start + i + j1]) 42862306a36Sopenharmony_ci diff = "***"; 42962306a36Sopenharmony_ci } 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci while (j1 < 8) { 43262306a36Sopenharmony_ci printk(" "); 43362306a36Sopenharmony_ci j1 += 1; 43462306a36Sopenharmony_ci } 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci printk(" %s ", diff); 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci for (j2 = 0; j2 < 8 && i + j2 < len; j2++) 43962306a36Sopenharmony_ci printk(" %02x", written[start + i + j2]); 44062306a36Sopenharmony_ci printk("\n"); 44162306a36Sopenharmony_ci i += 8; 44262306a36Sopenharmony_ci } 44362306a36Sopenharmony_ci} 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci/* 44662306a36Sopenharmony_ci * Count the number of differing bytes and bits and return the first differing 44762306a36Sopenharmony_ci * offset. 44862306a36Sopenharmony_ci */ 44962306a36Sopenharmony_cistatic int countdiffs(unsigned char *buf, unsigned char *check_buf, 45062306a36Sopenharmony_ci unsigned offset, unsigned len, unsigned *bytesp, 45162306a36Sopenharmony_ci unsigned *bitsp) 45262306a36Sopenharmony_ci{ 45362306a36Sopenharmony_ci unsigned i, bit; 45462306a36Sopenharmony_ci int first = -1; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci for (i = offset; i < offset + len; i++) 45762306a36Sopenharmony_ci if (buf[i] != check_buf[i]) { 45862306a36Sopenharmony_ci first = i; 45962306a36Sopenharmony_ci break; 46062306a36Sopenharmony_ci } 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci while (i < offset + len) { 46362306a36Sopenharmony_ci if (buf[i] != check_buf[i]) { 46462306a36Sopenharmony_ci (*bytesp)++; 46562306a36Sopenharmony_ci bit = 1; 46662306a36Sopenharmony_ci while (bit < 256) { 46762306a36Sopenharmony_ci if ((buf[i] & bit) != (check_buf[i] & bit)) 46862306a36Sopenharmony_ci (*bitsp)++; 46962306a36Sopenharmony_ci bit <<= 1; 47062306a36Sopenharmony_ci } 47162306a36Sopenharmony_ci } 47262306a36Sopenharmony_ci i++; 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci return first; 47662306a36Sopenharmony_ci} 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ciMODULE_DESCRIPTION("Eraseblock torturing module"); 47962306a36Sopenharmony_ciMODULE_AUTHOR("Artem Bityutskiy, Jarkko Lavinen, Adrian Hunter"); 48062306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 481