18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci#include <linux/kernel.h> 58c2ecf20Sopenharmony_ci#include <linux/module.h> 68c2ecf20Sopenharmony_ci#include <linux/list.h> 78c2ecf20Sopenharmony_ci#include <linux/random.h> 88c2ecf20Sopenharmony_ci#include <linux/string.h> 98c2ecf20Sopenharmony_ci#include <linux/bitops.h> 108c2ecf20Sopenharmony_ci#include <linux/slab.h> 118c2ecf20Sopenharmony_ci#include <linux/mtd/nand_ecc.h> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include "mtd_test.h" 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci/* 168c2ecf20Sopenharmony_ci * Test the implementation for software ECC 178c2ecf20Sopenharmony_ci * 188c2ecf20Sopenharmony_ci * No actual MTD device is needed, So we don't need to warry about losing 198c2ecf20Sopenharmony_ci * important data by human error. 208c2ecf20Sopenharmony_ci * 218c2ecf20Sopenharmony_ci * This covers possible patterns of corruption which can be reliably corrected 228c2ecf20Sopenharmony_ci * or detected. 238c2ecf20Sopenharmony_ci */ 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_MTD_RAW_NAND) 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistruct nand_ecc_test { 288c2ecf20Sopenharmony_ci const char *name; 298c2ecf20Sopenharmony_ci void (*prepare)(void *, void *, void *, void *, const size_t); 308c2ecf20Sopenharmony_ci int (*verify)(void *, void *, void *, const size_t); 318c2ecf20Sopenharmony_ci}; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci/* 348c2ecf20Sopenharmony_ci * The reason for this __change_bit_le() instead of __change_bit() is to inject 358c2ecf20Sopenharmony_ci * bit error properly within the region which is not a multiple of 368c2ecf20Sopenharmony_ci * sizeof(unsigned long) on big-endian systems 378c2ecf20Sopenharmony_ci */ 388c2ecf20Sopenharmony_ci#ifdef __LITTLE_ENDIAN 398c2ecf20Sopenharmony_ci#define __change_bit_le(nr, addr) __change_bit(nr, addr) 408c2ecf20Sopenharmony_ci#elif defined(__BIG_ENDIAN) 418c2ecf20Sopenharmony_ci#define __change_bit_le(nr, addr) \ 428c2ecf20Sopenharmony_ci __change_bit((nr) ^ ((BITS_PER_LONG - 1) & ~0x7), addr) 438c2ecf20Sopenharmony_ci#else 448c2ecf20Sopenharmony_ci#error "Unknown byte order" 458c2ecf20Sopenharmony_ci#endif 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistatic void single_bit_error_data(void *error_data, void *correct_data, 488c2ecf20Sopenharmony_ci size_t size) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci unsigned int offset = prandom_u32() % (size * BITS_PER_BYTE); 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci memcpy(error_data, correct_data, size); 538c2ecf20Sopenharmony_ci __change_bit_le(offset, error_data); 548c2ecf20Sopenharmony_ci} 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic void double_bit_error_data(void *error_data, void *correct_data, 578c2ecf20Sopenharmony_ci size_t size) 588c2ecf20Sopenharmony_ci{ 598c2ecf20Sopenharmony_ci unsigned int offset[2]; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci offset[0] = prandom_u32() % (size * BITS_PER_BYTE); 628c2ecf20Sopenharmony_ci do { 638c2ecf20Sopenharmony_ci offset[1] = prandom_u32() % (size * BITS_PER_BYTE); 648c2ecf20Sopenharmony_ci } while (offset[0] == offset[1]); 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci memcpy(error_data, correct_data, size); 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci __change_bit_le(offset[0], error_data); 698c2ecf20Sopenharmony_ci __change_bit_le(offset[1], error_data); 708c2ecf20Sopenharmony_ci} 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistatic unsigned int random_ecc_bit(size_t size) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci unsigned int offset = prandom_u32() % (3 * BITS_PER_BYTE); 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci if (size == 256) { 778c2ecf20Sopenharmony_ci /* 788c2ecf20Sopenharmony_ci * Don't inject a bit error into the insignificant bits (16th 798c2ecf20Sopenharmony_ci * and 17th bit) in ECC code for 256 byte data block 808c2ecf20Sopenharmony_ci */ 818c2ecf20Sopenharmony_ci while (offset == 16 || offset == 17) 828c2ecf20Sopenharmony_ci offset = prandom_u32() % (3 * BITS_PER_BYTE); 838c2ecf20Sopenharmony_ci } 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci return offset; 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic void single_bit_error_ecc(void *error_ecc, void *correct_ecc, 898c2ecf20Sopenharmony_ci size_t size) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci unsigned int offset = random_ecc_bit(size); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci memcpy(error_ecc, correct_ecc, 3); 948c2ecf20Sopenharmony_ci __change_bit_le(offset, error_ecc); 958c2ecf20Sopenharmony_ci} 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cistatic void double_bit_error_ecc(void *error_ecc, void *correct_ecc, 988c2ecf20Sopenharmony_ci size_t size) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci unsigned int offset[2]; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci offset[0] = random_ecc_bit(size); 1038c2ecf20Sopenharmony_ci do { 1048c2ecf20Sopenharmony_ci offset[1] = random_ecc_bit(size); 1058c2ecf20Sopenharmony_ci } while (offset[0] == offset[1]); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci memcpy(error_ecc, correct_ecc, 3); 1088c2ecf20Sopenharmony_ci __change_bit_le(offset[0], error_ecc); 1098c2ecf20Sopenharmony_ci __change_bit_le(offset[1], error_ecc); 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic void no_bit_error(void *error_data, void *error_ecc, 1138c2ecf20Sopenharmony_ci void *correct_data, void *correct_ecc, const size_t size) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci memcpy(error_data, correct_data, size); 1168c2ecf20Sopenharmony_ci memcpy(error_ecc, correct_ecc, 3); 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_cistatic int no_bit_error_verify(void *error_data, void *error_ecc, 1208c2ecf20Sopenharmony_ci void *correct_data, const size_t size) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci unsigned char calc_ecc[3]; 1238c2ecf20Sopenharmony_ci int ret; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci __nand_calculate_ecc(error_data, size, calc_ecc, 1268c2ecf20Sopenharmony_ci IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC)); 1278c2ecf20Sopenharmony_ci ret = __nand_correct_data(error_data, error_ecc, calc_ecc, size, 1288c2ecf20Sopenharmony_ci IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC)); 1298c2ecf20Sopenharmony_ci if (ret == 0 && !memcmp(correct_data, error_data, size)) 1308c2ecf20Sopenharmony_ci return 0; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci return -EINVAL; 1338c2ecf20Sopenharmony_ci} 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_cistatic void single_bit_error_in_data(void *error_data, void *error_ecc, 1368c2ecf20Sopenharmony_ci void *correct_data, void *correct_ecc, const size_t size) 1378c2ecf20Sopenharmony_ci{ 1388c2ecf20Sopenharmony_ci single_bit_error_data(error_data, correct_data, size); 1398c2ecf20Sopenharmony_ci memcpy(error_ecc, correct_ecc, 3); 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic void single_bit_error_in_ecc(void *error_data, void *error_ecc, 1438c2ecf20Sopenharmony_ci void *correct_data, void *correct_ecc, const size_t size) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci memcpy(error_data, correct_data, size); 1468c2ecf20Sopenharmony_ci single_bit_error_ecc(error_ecc, correct_ecc, size); 1478c2ecf20Sopenharmony_ci} 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_cistatic int single_bit_error_correct(void *error_data, void *error_ecc, 1508c2ecf20Sopenharmony_ci void *correct_data, const size_t size) 1518c2ecf20Sopenharmony_ci{ 1528c2ecf20Sopenharmony_ci unsigned char calc_ecc[3]; 1538c2ecf20Sopenharmony_ci int ret; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci __nand_calculate_ecc(error_data, size, calc_ecc, 1568c2ecf20Sopenharmony_ci IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC)); 1578c2ecf20Sopenharmony_ci ret = __nand_correct_data(error_data, error_ecc, calc_ecc, size, 1588c2ecf20Sopenharmony_ci IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC)); 1598c2ecf20Sopenharmony_ci if (ret == 1 && !memcmp(correct_data, error_data, size)) 1608c2ecf20Sopenharmony_ci return 0; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci return -EINVAL; 1638c2ecf20Sopenharmony_ci} 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_cistatic void double_bit_error_in_data(void *error_data, void *error_ecc, 1668c2ecf20Sopenharmony_ci void *correct_data, void *correct_ecc, const size_t size) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci double_bit_error_data(error_data, correct_data, size); 1698c2ecf20Sopenharmony_ci memcpy(error_ecc, correct_ecc, 3); 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cistatic void single_bit_error_in_data_and_ecc(void *error_data, void *error_ecc, 1738c2ecf20Sopenharmony_ci void *correct_data, void *correct_ecc, const size_t size) 1748c2ecf20Sopenharmony_ci{ 1758c2ecf20Sopenharmony_ci single_bit_error_data(error_data, correct_data, size); 1768c2ecf20Sopenharmony_ci single_bit_error_ecc(error_ecc, correct_ecc, size); 1778c2ecf20Sopenharmony_ci} 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_cistatic void double_bit_error_in_ecc(void *error_data, void *error_ecc, 1808c2ecf20Sopenharmony_ci void *correct_data, void *correct_ecc, const size_t size) 1818c2ecf20Sopenharmony_ci{ 1828c2ecf20Sopenharmony_ci memcpy(error_data, correct_data, size); 1838c2ecf20Sopenharmony_ci double_bit_error_ecc(error_ecc, correct_ecc, size); 1848c2ecf20Sopenharmony_ci} 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_cistatic int double_bit_error_detect(void *error_data, void *error_ecc, 1878c2ecf20Sopenharmony_ci void *correct_data, const size_t size) 1888c2ecf20Sopenharmony_ci{ 1898c2ecf20Sopenharmony_ci unsigned char calc_ecc[3]; 1908c2ecf20Sopenharmony_ci int ret; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci __nand_calculate_ecc(error_data, size, calc_ecc, 1938c2ecf20Sopenharmony_ci IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC)); 1948c2ecf20Sopenharmony_ci ret = __nand_correct_data(error_data, error_ecc, calc_ecc, size, 1958c2ecf20Sopenharmony_ci IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC)); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci return (ret == -EBADMSG) ? 0 : -EINVAL; 1988c2ecf20Sopenharmony_ci} 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_cistatic const struct nand_ecc_test nand_ecc_test[] = { 2018c2ecf20Sopenharmony_ci { 2028c2ecf20Sopenharmony_ci .name = "no-bit-error", 2038c2ecf20Sopenharmony_ci .prepare = no_bit_error, 2048c2ecf20Sopenharmony_ci .verify = no_bit_error_verify, 2058c2ecf20Sopenharmony_ci }, 2068c2ecf20Sopenharmony_ci { 2078c2ecf20Sopenharmony_ci .name = "single-bit-error-in-data-correct", 2088c2ecf20Sopenharmony_ci .prepare = single_bit_error_in_data, 2098c2ecf20Sopenharmony_ci .verify = single_bit_error_correct, 2108c2ecf20Sopenharmony_ci }, 2118c2ecf20Sopenharmony_ci { 2128c2ecf20Sopenharmony_ci .name = "single-bit-error-in-ecc-correct", 2138c2ecf20Sopenharmony_ci .prepare = single_bit_error_in_ecc, 2148c2ecf20Sopenharmony_ci .verify = single_bit_error_correct, 2158c2ecf20Sopenharmony_ci }, 2168c2ecf20Sopenharmony_ci { 2178c2ecf20Sopenharmony_ci .name = "double-bit-error-in-data-detect", 2188c2ecf20Sopenharmony_ci .prepare = double_bit_error_in_data, 2198c2ecf20Sopenharmony_ci .verify = double_bit_error_detect, 2208c2ecf20Sopenharmony_ci }, 2218c2ecf20Sopenharmony_ci { 2228c2ecf20Sopenharmony_ci .name = "single-bit-error-in-data-and-ecc-detect", 2238c2ecf20Sopenharmony_ci .prepare = single_bit_error_in_data_and_ecc, 2248c2ecf20Sopenharmony_ci .verify = double_bit_error_detect, 2258c2ecf20Sopenharmony_ci }, 2268c2ecf20Sopenharmony_ci { 2278c2ecf20Sopenharmony_ci .name = "double-bit-error-in-ecc-detect", 2288c2ecf20Sopenharmony_ci .prepare = double_bit_error_in_ecc, 2298c2ecf20Sopenharmony_ci .verify = double_bit_error_detect, 2308c2ecf20Sopenharmony_ci }, 2318c2ecf20Sopenharmony_ci}; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_cistatic void dump_data_ecc(void *error_data, void *error_ecc, void *correct_data, 2348c2ecf20Sopenharmony_ci void *correct_ecc, const size_t size) 2358c2ecf20Sopenharmony_ci{ 2368c2ecf20Sopenharmony_ci pr_info("hexdump of error data:\n"); 2378c2ecf20Sopenharmony_ci print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4, 2388c2ecf20Sopenharmony_ci error_data, size, false); 2398c2ecf20Sopenharmony_ci print_hex_dump(KERN_INFO, "hexdump of error ecc: ", 2408c2ecf20Sopenharmony_ci DUMP_PREFIX_NONE, 16, 1, error_ecc, 3, false); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci pr_info("hexdump of correct data:\n"); 2438c2ecf20Sopenharmony_ci print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4, 2448c2ecf20Sopenharmony_ci correct_data, size, false); 2458c2ecf20Sopenharmony_ci print_hex_dump(KERN_INFO, "hexdump of correct ecc: ", 2468c2ecf20Sopenharmony_ci DUMP_PREFIX_NONE, 16, 1, correct_ecc, 3, false); 2478c2ecf20Sopenharmony_ci} 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_cistatic int nand_ecc_test_run(const size_t size) 2508c2ecf20Sopenharmony_ci{ 2518c2ecf20Sopenharmony_ci int i; 2528c2ecf20Sopenharmony_ci int err = 0; 2538c2ecf20Sopenharmony_ci void *error_data; 2548c2ecf20Sopenharmony_ci void *error_ecc; 2558c2ecf20Sopenharmony_ci void *correct_data; 2568c2ecf20Sopenharmony_ci void *correct_ecc; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci error_data = kmalloc(size, GFP_KERNEL); 2598c2ecf20Sopenharmony_ci error_ecc = kmalloc(3, GFP_KERNEL); 2608c2ecf20Sopenharmony_ci correct_data = kmalloc(size, GFP_KERNEL); 2618c2ecf20Sopenharmony_ci correct_ecc = kmalloc(3, GFP_KERNEL); 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci if (!error_data || !error_ecc || !correct_data || !correct_ecc) { 2648c2ecf20Sopenharmony_ci err = -ENOMEM; 2658c2ecf20Sopenharmony_ci goto error; 2668c2ecf20Sopenharmony_ci } 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci prandom_bytes(correct_data, size); 2698c2ecf20Sopenharmony_ci __nand_calculate_ecc(correct_data, size, correct_ecc, 2708c2ecf20Sopenharmony_ci IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC)); 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(nand_ecc_test); i++) { 2738c2ecf20Sopenharmony_ci nand_ecc_test[i].prepare(error_data, error_ecc, 2748c2ecf20Sopenharmony_ci correct_data, correct_ecc, size); 2758c2ecf20Sopenharmony_ci err = nand_ecc_test[i].verify(error_data, error_ecc, 2768c2ecf20Sopenharmony_ci correct_data, size); 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci if (err) { 2798c2ecf20Sopenharmony_ci pr_err("not ok - %s-%zd\n", 2808c2ecf20Sopenharmony_ci nand_ecc_test[i].name, size); 2818c2ecf20Sopenharmony_ci dump_data_ecc(error_data, error_ecc, 2828c2ecf20Sopenharmony_ci correct_data, correct_ecc, size); 2838c2ecf20Sopenharmony_ci break; 2848c2ecf20Sopenharmony_ci } 2858c2ecf20Sopenharmony_ci pr_info("ok - %s-%zd\n", 2868c2ecf20Sopenharmony_ci nand_ecc_test[i].name, size); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci err = mtdtest_relax(); 2898c2ecf20Sopenharmony_ci if (err) 2908c2ecf20Sopenharmony_ci break; 2918c2ecf20Sopenharmony_ci } 2928c2ecf20Sopenharmony_cierror: 2938c2ecf20Sopenharmony_ci kfree(error_data); 2948c2ecf20Sopenharmony_ci kfree(error_ecc); 2958c2ecf20Sopenharmony_ci kfree(correct_data); 2968c2ecf20Sopenharmony_ci kfree(correct_ecc); 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci return err; 2998c2ecf20Sopenharmony_ci} 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci#else 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_cistatic int nand_ecc_test_run(const size_t size) 3048c2ecf20Sopenharmony_ci{ 3058c2ecf20Sopenharmony_ci return 0; 3068c2ecf20Sopenharmony_ci} 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci#endif 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_cistatic int __init ecc_test_init(void) 3118c2ecf20Sopenharmony_ci{ 3128c2ecf20Sopenharmony_ci int err; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci err = nand_ecc_test_run(256); 3158c2ecf20Sopenharmony_ci if (err) 3168c2ecf20Sopenharmony_ci return err; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci return nand_ecc_test_run(512); 3198c2ecf20Sopenharmony_ci} 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_cistatic void __exit ecc_test_exit(void) 3228c2ecf20Sopenharmony_ci{ 3238c2ecf20Sopenharmony_ci} 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_cimodule_init(ecc_test_init); 3268c2ecf20Sopenharmony_cimodule_exit(ecc_test_exit); 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("NAND ECC function test module"); 3298c2ecf20Sopenharmony_ciMODULE_AUTHOR("Akinobu Mita"); 3308c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 331