18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2006-2008 Artem Bityutskiy 48c2ecf20Sopenharmony_ci * Copyright (C) 2006-2008 Jarkko Lavinen 58c2ecf20Sopenharmony_ci * Copyright (C) 2006-2008 Adrian Hunter 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Authors: Artem Bityutskiy, Jarkko Lavinen, Adria Hunter 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * WARNING: this test program may kill your flash and your device. Do not 108c2ecf20Sopenharmony_ci * use it unless you know what you do. Authors are not responsible for any 118c2ecf20Sopenharmony_ci * damage caused by this program. 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <linux/init.h> 178c2ecf20Sopenharmony_ci#include <linux/ktime.h> 188c2ecf20Sopenharmony_ci#include <linux/module.h> 198c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 208c2ecf20Sopenharmony_ci#include <linux/err.h> 218c2ecf20Sopenharmony_ci#include <linux/mtd/mtd.h> 228c2ecf20Sopenharmony_ci#include <linux/slab.h> 238c2ecf20Sopenharmony_ci#include <linux/sched.h> 248c2ecf20Sopenharmony_ci#include "mtd_test.h" 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#define RETRIES 3 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistatic int eb = 8; 298c2ecf20Sopenharmony_cimodule_param(eb, int, S_IRUGO); 308c2ecf20Sopenharmony_ciMODULE_PARM_DESC(eb, "eraseblock number within the selected MTD device"); 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic int ebcnt = 32; 338c2ecf20Sopenharmony_cimodule_param(ebcnt, int, S_IRUGO); 348c2ecf20Sopenharmony_ciMODULE_PARM_DESC(ebcnt, "number of consecutive eraseblocks to torture"); 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic int pgcnt; 378c2ecf20Sopenharmony_cimodule_param(pgcnt, int, S_IRUGO); 388c2ecf20Sopenharmony_ciMODULE_PARM_DESC(pgcnt, "number of pages per eraseblock to torture (0 => all)"); 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic int dev = -EINVAL; 418c2ecf20Sopenharmony_cimodule_param(dev, int, S_IRUGO); 428c2ecf20Sopenharmony_ciMODULE_PARM_DESC(dev, "MTD device number to use"); 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic int gran = 512; 458c2ecf20Sopenharmony_cimodule_param(gran, int, S_IRUGO); 468c2ecf20Sopenharmony_ciMODULE_PARM_DESC(gran, "how often the status information should be printed"); 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic int check = 1; 498c2ecf20Sopenharmony_cimodule_param(check, int, S_IRUGO); 508c2ecf20Sopenharmony_ciMODULE_PARM_DESC(check, "if the written data should be checked"); 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic unsigned int cycles_count; 538c2ecf20Sopenharmony_cimodule_param(cycles_count, uint, S_IRUGO); 548c2ecf20Sopenharmony_ciMODULE_PARM_DESC(cycles_count, "how many erase cycles to do " 558c2ecf20Sopenharmony_ci "(infinite by default)"); 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistatic struct mtd_info *mtd; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci/* This buffer contains 0x555555...0xAAAAAA... pattern */ 608c2ecf20Sopenharmony_cistatic unsigned char *patt_5A5; 618c2ecf20Sopenharmony_ci/* This buffer contains 0xAAAAAA...0x555555... pattern */ 628c2ecf20Sopenharmony_cistatic unsigned char *patt_A5A; 638c2ecf20Sopenharmony_ci/* This buffer contains all 0xFF bytes */ 648c2ecf20Sopenharmony_cistatic unsigned char *patt_FF; 658c2ecf20Sopenharmony_ci/* This a temporary buffer is use when checking data */ 668c2ecf20Sopenharmony_cistatic unsigned char *check_buf; 678c2ecf20Sopenharmony_ci/* How many erase cycles were done */ 688c2ecf20Sopenharmony_cistatic unsigned int erase_cycles; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic int pgsize; 718c2ecf20Sopenharmony_cistatic ktime_t start, finish; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic void report_corrupt(unsigned char *read, unsigned char *written); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistatic inline void start_timing(void) 768c2ecf20Sopenharmony_ci{ 778c2ecf20Sopenharmony_ci start = ktime_get(); 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic inline void stop_timing(void) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci finish = ktime_get(); 838c2ecf20Sopenharmony_ci} 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci/* 868c2ecf20Sopenharmony_ci * Check that the contents of eraseblock number @enbum is equivalent to the 878c2ecf20Sopenharmony_ci * @buf buffer. 888c2ecf20Sopenharmony_ci */ 898c2ecf20Sopenharmony_cistatic inline int check_eraseblock(int ebnum, unsigned char *buf) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci int err, retries = 0; 928c2ecf20Sopenharmony_ci size_t read; 938c2ecf20Sopenharmony_ci loff_t addr = (loff_t)ebnum * mtd->erasesize; 948c2ecf20Sopenharmony_ci size_t len = mtd->erasesize; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci if (pgcnt) { 978c2ecf20Sopenharmony_ci addr = (loff_t)(ebnum + 1) * mtd->erasesize - pgcnt * pgsize; 988c2ecf20Sopenharmony_ci len = pgcnt * pgsize; 998c2ecf20Sopenharmony_ci } 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ciretry: 1028c2ecf20Sopenharmony_ci err = mtd_read(mtd, addr, len, &read, check_buf); 1038c2ecf20Sopenharmony_ci if (mtd_is_bitflip(err)) 1048c2ecf20Sopenharmony_ci pr_err("single bit flip occurred at EB %d " 1058c2ecf20Sopenharmony_ci "MTD reported that it was fixed.\n", ebnum); 1068c2ecf20Sopenharmony_ci else if (err) { 1078c2ecf20Sopenharmony_ci pr_err("error %d while reading EB %d, " 1088c2ecf20Sopenharmony_ci "read %zd\n", err, ebnum, read); 1098c2ecf20Sopenharmony_ci return err; 1108c2ecf20Sopenharmony_ci } 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci if (read != len) { 1138c2ecf20Sopenharmony_ci pr_err("failed to read %zd bytes from EB %d, " 1148c2ecf20Sopenharmony_ci "read only %zd, but no error reported\n", 1158c2ecf20Sopenharmony_ci len, ebnum, read); 1168c2ecf20Sopenharmony_ci return -EIO; 1178c2ecf20Sopenharmony_ci } 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci if (memcmp(buf, check_buf, len)) { 1208c2ecf20Sopenharmony_ci pr_err("read wrong data from EB %d\n", ebnum); 1218c2ecf20Sopenharmony_ci report_corrupt(check_buf, buf); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci if (retries++ < RETRIES) { 1248c2ecf20Sopenharmony_ci /* Try read again */ 1258c2ecf20Sopenharmony_ci yield(); 1268c2ecf20Sopenharmony_ci pr_info("re-try reading data from EB %d\n", 1278c2ecf20Sopenharmony_ci ebnum); 1288c2ecf20Sopenharmony_ci goto retry; 1298c2ecf20Sopenharmony_ci } else { 1308c2ecf20Sopenharmony_ci pr_info("retried %d times, still errors, " 1318c2ecf20Sopenharmony_ci "give-up\n", RETRIES); 1328c2ecf20Sopenharmony_ci return -EINVAL; 1338c2ecf20Sopenharmony_ci } 1348c2ecf20Sopenharmony_ci } 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci if (retries != 0) 1378c2ecf20Sopenharmony_ci pr_info("only attempt number %d was OK (!!!)\n", 1388c2ecf20Sopenharmony_ci retries); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci return 0; 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_cistatic inline int write_pattern(int ebnum, void *buf) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci int err; 1468c2ecf20Sopenharmony_ci size_t written; 1478c2ecf20Sopenharmony_ci loff_t addr = (loff_t)ebnum * mtd->erasesize; 1488c2ecf20Sopenharmony_ci size_t len = mtd->erasesize; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci if (pgcnt) { 1518c2ecf20Sopenharmony_ci addr = (loff_t)(ebnum + 1) * mtd->erasesize - pgcnt * pgsize; 1528c2ecf20Sopenharmony_ci len = pgcnt * pgsize; 1538c2ecf20Sopenharmony_ci } 1548c2ecf20Sopenharmony_ci err = mtd_write(mtd, addr, len, &written, buf); 1558c2ecf20Sopenharmony_ci if (err) { 1568c2ecf20Sopenharmony_ci pr_err("error %d while writing EB %d, written %zd" 1578c2ecf20Sopenharmony_ci " bytes\n", err, ebnum, written); 1588c2ecf20Sopenharmony_ci return err; 1598c2ecf20Sopenharmony_ci } 1608c2ecf20Sopenharmony_ci if (written != len) { 1618c2ecf20Sopenharmony_ci pr_info("written only %zd bytes of %zd, but no error" 1628c2ecf20Sopenharmony_ci " reported\n", written, len); 1638c2ecf20Sopenharmony_ci return -EIO; 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci return 0; 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistatic int __init tort_init(void) 1708c2ecf20Sopenharmony_ci{ 1718c2ecf20Sopenharmony_ci int err = 0, i, infinite = !cycles_count; 1728c2ecf20Sopenharmony_ci unsigned char *bad_ebs; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci printk(KERN_INFO "\n"); 1758c2ecf20Sopenharmony_ci printk(KERN_INFO "=================================================\n"); 1768c2ecf20Sopenharmony_ci pr_info("Warning: this program is trying to wear out your " 1778c2ecf20Sopenharmony_ci "flash, stop it if this is not wanted.\n"); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci if (dev < 0) { 1808c2ecf20Sopenharmony_ci pr_info("Please specify a valid mtd-device via module parameter\n"); 1818c2ecf20Sopenharmony_ci pr_crit("CAREFUL: This test wipes all data on the specified MTD device!\n"); 1828c2ecf20Sopenharmony_ci return -EINVAL; 1838c2ecf20Sopenharmony_ci } 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci pr_info("MTD device: %d\n", dev); 1868c2ecf20Sopenharmony_ci pr_info("torture %d eraseblocks (%d-%d) of mtd%d\n", 1878c2ecf20Sopenharmony_ci ebcnt, eb, eb + ebcnt - 1, dev); 1888c2ecf20Sopenharmony_ci if (pgcnt) 1898c2ecf20Sopenharmony_ci pr_info("torturing just %d pages per eraseblock\n", 1908c2ecf20Sopenharmony_ci pgcnt); 1918c2ecf20Sopenharmony_ci pr_info("write verify %s\n", check ? "enabled" : "disabled"); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci mtd = get_mtd_device(NULL, dev); 1948c2ecf20Sopenharmony_ci if (IS_ERR(mtd)) { 1958c2ecf20Sopenharmony_ci err = PTR_ERR(mtd); 1968c2ecf20Sopenharmony_ci pr_err("error: cannot get MTD device\n"); 1978c2ecf20Sopenharmony_ci return err; 1988c2ecf20Sopenharmony_ci } 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci if (mtd->writesize == 1) { 2018c2ecf20Sopenharmony_ci pr_info("not NAND flash, assume page size is 512 " 2028c2ecf20Sopenharmony_ci "bytes.\n"); 2038c2ecf20Sopenharmony_ci pgsize = 512; 2048c2ecf20Sopenharmony_ci } else 2058c2ecf20Sopenharmony_ci pgsize = mtd->writesize; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci if (pgcnt && (pgcnt > mtd->erasesize / pgsize || pgcnt < 0)) { 2088c2ecf20Sopenharmony_ci pr_err("error: invalid pgcnt value %d\n", pgcnt); 2098c2ecf20Sopenharmony_ci goto out_mtd; 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci err = -ENOMEM; 2138c2ecf20Sopenharmony_ci patt_5A5 = kmalloc(mtd->erasesize, GFP_KERNEL); 2148c2ecf20Sopenharmony_ci if (!patt_5A5) 2158c2ecf20Sopenharmony_ci goto out_mtd; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci patt_A5A = kmalloc(mtd->erasesize, GFP_KERNEL); 2188c2ecf20Sopenharmony_ci if (!patt_A5A) 2198c2ecf20Sopenharmony_ci goto out_patt_5A5; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci patt_FF = kmalloc(mtd->erasesize, GFP_KERNEL); 2228c2ecf20Sopenharmony_ci if (!patt_FF) 2238c2ecf20Sopenharmony_ci goto out_patt_A5A; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci check_buf = kmalloc(mtd->erasesize, GFP_KERNEL); 2268c2ecf20Sopenharmony_ci if (!check_buf) 2278c2ecf20Sopenharmony_ci goto out_patt_FF; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci bad_ebs = kzalloc(ebcnt, GFP_KERNEL); 2308c2ecf20Sopenharmony_ci if (!bad_ebs) 2318c2ecf20Sopenharmony_ci goto out_check_buf; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci err = 0; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci /* Initialize patterns */ 2368c2ecf20Sopenharmony_ci memset(patt_FF, 0xFF, mtd->erasesize); 2378c2ecf20Sopenharmony_ci for (i = 0; i < mtd->erasesize / pgsize; i++) { 2388c2ecf20Sopenharmony_ci if (!(i & 1)) { 2398c2ecf20Sopenharmony_ci memset(patt_5A5 + i * pgsize, 0x55, pgsize); 2408c2ecf20Sopenharmony_ci memset(patt_A5A + i * pgsize, 0xAA, pgsize); 2418c2ecf20Sopenharmony_ci } else { 2428c2ecf20Sopenharmony_ci memset(patt_5A5 + i * pgsize, 0xAA, pgsize); 2438c2ecf20Sopenharmony_ci memset(patt_A5A + i * pgsize, 0x55, pgsize); 2448c2ecf20Sopenharmony_ci } 2458c2ecf20Sopenharmony_ci } 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci err = mtdtest_scan_for_bad_eraseblocks(mtd, bad_ebs, eb, ebcnt); 2488c2ecf20Sopenharmony_ci if (err) 2498c2ecf20Sopenharmony_ci goto out; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci start_timing(); 2528c2ecf20Sopenharmony_ci while (1) { 2538c2ecf20Sopenharmony_ci int i; 2548c2ecf20Sopenharmony_ci void *patt; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci err = mtdtest_erase_good_eraseblocks(mtd, bad_ebs, eb, ebcnt); 2578c2ecf20Sopenharmony_ci if (err) 2588c2ecf20Sopenharmony_ci goto out; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci /* Check if the eraseblocks contain only 0xFF bytes */ 2618c2ecf20Sopenharmony_ci if (check) { 2628c2ecf20Sopenharmony_ci for (i = eb; i < eb + ebcnt; i++) { 2638c2ecf20Sopenharmony_ci if (bad_ebs[i - eb]) 2648c2ecf20Sopenharmony_ci continue; 2658c2ecf20Sopenharmony_ci err = check_eraseblock(i, patt_FF); 2668c2ecf20Sopenharmony_ci if (err) { 2678c2ecf20Sopenharmony_ci pr_info("verify failed" 2688c2ecf20Sopenharmony_ci " for 0xFF... pattern\n"); 2698c2ecf20Sopenharmony_ci goto out; 2708c2ecf20Sopenharmony_ci } 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci err = mtdtest_relax(); 2738c2ecf20Sopenharmony_ci if (err) 2748c2ecf20Sopenharmony_ci goto out; 2758c2ecf20Sopenharmony_ci } 2768c2ecf20Sopenharmony_ci } 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci /* Write the pattern */ 2798c2ecf20Sopenharmony_ci for (i = eb; i < eb + ebcnt; i++) { 2808c2ecf20Sopenharmony_ci if (bad_ebs[i - eb]) 2818c2ecf20Sopenharmony_ci continue; 2828c2ecf20Sopenharmony_ci if ((eb + erase_cycles) & 1) 2838c2ecf20Sopenharmony_ci patt = patt_5A5; 2848c2ecf20Sopenharmony_ci else 2858c2ecf20Sopenharmony_ci patt = patt_A5A; 2868c2ecf20Sopenharmony_ci err = write_pattern(i, patt); 2878c2ecf20Sopenharmony_ci if (err) 2888c2ecf20Sopenharmony_ci goto out; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci err = mtdtest_relax(); 2918c2ecf20Sopenharmony_ci if (err) 2928c2ecf20Sopenharmony_ci goto out; 2938c2ecf20Sopenharmony_ci } 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci /* Verify what we wrote */ 2968c2ecf20Sopenharmony_ci if (check) { 2978c2ecf20Sopenharmony_ci for (i = eb; i < eb + ebcnt; i++) { 2988c2ecf20Sopenharmony_ci if (bad_ebs[i - eb]) 2998c2ecf20Sopenharmony_ci continue; 3008c2ecf20Sopenharmony_ci if ((eb + erase_cycles) & 1) 3018c2ecf20Sopenharmony_ci patt = patt_5A5; 3028c2ecf20Sopenharmony_ci else 3038c2ecf20Sopenharmony_ci patt = patt_A5A; 3048c2ecf20Sopenharmony_ci err = check_eraseblock(i, patt); 3058c2ecf20Sopenharmony_ci if (err) { 3068c2ecf20Sopenharmony_ci pr_info("verify failed for %s" 3078c2ecf20Sopenharmony_ci " pattern\n", 3088c2ecf20Sopenharmony_ci ((eb + erase_cycles) & 1) ? 3098c2ecf20Sopenharmony_ci "0x55AA55..." : "0xAA55AA..."); 3108c2ecf20Sopenharmony_ci goto out; 3118c2ecf20Sopenharmony_ci } 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci err = mtdtest_relax(); 3148c2ecf20Sopenharmony_ci if (err) 3158c2ecf20Sopenharmony_ci goto out; 3168c2ecf20Sopenharmony_ci } 3178c2ecf20Sopenharmony_ci } 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci erase_cycles += 1; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci if (erase_cycles % gran == 0) { 3228c2ecf20Sopenharmony_ci long ms; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci stop_timing(); 3258c2ecf20Sopenharmony_ci ms = ktime_ms_delta(finish, start); 3268c2ecf20Sopenharmony_ci pr_info("%08u erase cycles done, took %lu " 3278c2ecf20Sopenharmony_ci "milliseconds (%lu seconds)\n", 3288c2ecf20Sopenharmony_ci erase_cycles, ms, ms / 1000); 3298c2ecf20Sopenharmony_ci start_timing(); 3308c2ecf20Sopenharmony_ci } 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci if (!infinite && --cycles_count == 0) 3338c2ecf20Sopenharmony_ci break; 3348c2ecf20Sopenharmony_ci } 3358c2ecf20Sopenharmony_ciout: 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci pr_info("finished after %u erase cycles\n", 3388c2ecf20Sopenharmony_ci erase_cycles); 3398c2ecf20Sopenharmony_ci kfree(bad_ebs); 3408c2ecf20Sopenharmony_ciout_check_buf: 3418c2ecf20Sopenharmony_ci kfree(check_buf); 3428c2ecf20Sopenharmony_ciout_patt_FF: 3438c2ecf20Sopenharmony_ci kfree(patt_FF); 3448c2ecf20Sopenharmony_ciout_patt_A5A: 3458c2ecf20Sopenharmony_ci kfree(patt_A5A); 3468c2ecf20Sopenharmony_ciout_patt_5A5: 3478c2ecf20Sopenharmony_ci kfree(patt_5A5); 3488c2ecf20Sopenharmony_ciout_mtd: 3498c2ecf20Sopenharmony_ci put_mtd_device(mtd); 3508c2ecf20Sopenharmony_ci if (err) 3518c2ecf20Sopenharmony_ci pr_info("error %d occurred during torturing\n", err); 3528c2ecf20Sopenharmony_ci printk(KERN_INFO "=================================================\n"); 3538c2ecf20Sopenharmony_ci return err; 3548c2ecf20Sopenharmony_ci} 3558c2ecf20Sopenharmony_cimodule_init(tort_init); 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_cistatic void __exit tort_exit(void) 3588c2ecf20Sopenharmony_ci{ 3598c2ecf20Sopenharmony_ci return; 3608c2ecf20Sopenharmony_ci} 3618c2ecf20Sopenharmony_cimodule_exit(tort_exit); 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_cistatic int countdiffs(unsigned char *buf, unsigned char *check_buf, 3648c2ecf20Sopenharmony_ci unsigned offset, unsigned len, unsigned *bytesp, 3658c2ecf20Sopenharmony_ci unsigned *bitsp); 3668c2ecf20Sopenharmony_cistatic void print_bufs(unsigned char *read, unsigned char *written, int start, 3678c2ecf20Sopenharmony_ci int len); 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci/* 3708c2ecf20Sopenharmony_ci * Report the detailed information about how the read EB differs from what was 3718c2ecf20Sopenharmony_ci * written. 3728c2ecf20Sopenharmony_ci */ 3738c2ecf20Sopenharmony_cistatic void report_corrupt(unsigned char *read, unsigned char *written) 3748c2ecf20Sopenharmony_ci{ 3758c2ecf20Sopenharmony_ci int i; 3768c2ecf20Sopenharmony_ci int bytes, bits, pages, first; 3778c2ecf20Sopenharmony_ci int offset, len; 3788c2ecf20Sopenharmony_ci size_t check_len = mtd->erasesize; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci if (pgcnt) 3818c2ecf20Sopenharmony_ci check_len = pgcnt * pgsize; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci bytes = bits = pages = 0; 3848c2ecf20Sopenharmony_ci for (i = 0; i < check_len; i += pgsize) 3858c2ecf20Sopenharmony_ci if (countdiffs(written, read, i, pgsize, &bytes, 3868c2ecf20Sopenharmony_ci &bits) >= 0) 3878c2ecf20Sopenharmony_ci pages++; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci pr_info("verify fails on %d pages, %d bytes/%d bits\n", 3908c2ecf20Sopenharmony_ci pages, bytes, bits); 3918c2ecf20Sopenharmony_ci pr_info("The following is a list of all differences between" 3928c2ecf20Sopenharmony_ci " what was read from flash and what was expected\n"); 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci for (i = 0; i < check_len; i += pgsize) { 3958c2ecf20Sopenharmony_ci cond_resched(); 3968c2ecf20Sopenharmony_ci bytes = bits = 0; 3978c2ecf20Sopenharmony_ci first = countdiffs(written, read, i, pgsize, &bytes, 3988c2ecf20Sopenharmony_ci &bits); 3998c2ecf20Sopenharmony_ci if (first < 0) 4008c2ecf20Sopenharmony_ci continue; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci printk("-------------------------------------------------------" 4038c2ecf20Sopenharmony_ci "----------------------------------\n"); 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci pr_info("Page %zd has %d bytes/%d bits failing verify," 4068c2ecf20Sopenharmony_ci " starting at offset 0x%x\n", 4078c2ecf20Sopenharmony_ci (mtd->erasesize - check_len + i) / pgsize, 4088c2ecf20Sopenharmony_ci bytes, bits, first); 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci offset = first & ~0x7; 4118c2ecf20Sopenharmony_ci len = ((first + bytes) | 0x7) + 1 - offset; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci print_bufs(read, written, offset, len); 4148c2ecf20Sopenharmony_ci } 4158c2ecf20Sopenharmony_ci} 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_cistatic void print_bufs(unsigned char *read, unsigned char *written, int start, 4188c2ecf20Sopenharmony_ci int len) 4198c2ecf20Sopenharmony_ci{ 4208c2ecf20Sopenharmony_ci int i = 0, j1, j2; 4218c2ecf20Sopenharmony_ci char *diff; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci printk("Offset Read Written\n"); 4248c2ecf20Sopenharmony_ci while (i < len) { 4258c2ecf20Sopenharmony_ci printk("0x%08x: ", start + i); 4268c2ecf20Sopenharmony_ci diff = " "; 4278c2ecf20Sopenharmony_ci for (j1 = 0; j1 < 8 && i + j1 < len; j1++) { 4288c2ecf20Sopenharmony_ci printk(" %02x", read[start + i + j1]); 4298c2ecf20Sopenharmony_ci if (read[start + i + j1] != written[start + i + j1]) 4308c2ecf20Sopenharmony_ci diff = "***"; 4318c2ecf20Sopenharmony_ci } 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci while (j1 < 8) { 4348c2ecf20Sopenharmony_ci printk(" "); 4358c2ecf20Sopenharmony_ci j1 += 1; 4368c2ecf20Sopenharmony_ci } 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci printk(" %s ", diff); 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci for (j2 = 0; j2 < 8 && i + j2 < len; j2++) 4418c2ecf20Sopenharmony_ci printk(" %02x", written[start + i + j2]); 4428c2ecf20Sopenharmony_ci printk("\n"); 4438c2ecf20Sopenharmony_ci i += 8; 4448c2ecf20Sopenharmony_ci } 4458c2ecf20Sopenharmony_ci} 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci/* 4488c2ecf20Sopenharmony_ci * Count the number of differing bytes and bits and return the first differing 4498c2ecf20Sopenharmony_ci * offset. 4508c2ecf20Sopenharmony_ci */ 4518c2ecf20Sopenharmony_cistatic int countdiffs(unsigned char *buf, unsigned char *check_buf, 4528c2ecf20Sopenharmony_ci unsigned offset, unsigned len, unsigned *bytesp, 4538c2ecf20Sopenharmony_ci unsigned *bitsp) 4548c2ecf20Sopenharmony_ci{ 4558c2ecf20Sopenharmony_ci unsigned i, bit; 4568c2ecf20Sopenharmony_ci int first = -1; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci for (i = offset; i < offset + len; i++) 4598c2ecf20Sopenharmony_ci if (buf[i] != check_buf[i]) { 4608c2ecf20Sopenharmony_ci first = i; 4618c2ecf20Sopenharmony_ci break; 4628c2ecf20Sopenharmony_ci } 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci while (i < offset + len) { 4658c2ecf20Sopenharmony_ci if (buf[i] != check_buf[i]) { 4668c2ecf20Sopenharmony_ci (*bytesp)++; 4678c2ecf20Sopenharmony_ci bit = 1; 4688c2ecf20Sopenharmony_ci while (bit < 256) { 4698c2ecf20Sopenharmony_ci if ((buf[i] & bit) != (check_buf[i] & bit)) 4708c2ecf20Sopenharmony_ci (*bitsp)++; 4718c2ecf20Sopenharmony_ci bit <<= 1; 4728c2ecf20Sopenharmony_ci } 4738c2ecf20Sopenharmony_ci } 4748c2ecf20Sopenharmony_ci i++; 4758c2ecf20Sopenharmony_ci } 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci return first; 4788c2ecf20Sopenharmony_ci} 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Eraseblock torturing module"); 4818c2ecf20Sopenharmony_ciMODULE_AUTHOR("Artem Bityutskiy, Jarkko Lavinen, Adrian Hunter"); 4828c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 483