linux/drivers/mtd/tests/mtd_nandecctest.c
<<
>>
Prefs
   1#define pr_fmt(fmt)     KBUILD_MODNAME ": " fmt
   2
   3#include <linux/kernel.h>
   4#include <linux/module.h>
   5#include <linux/list.h>
   6#include <linux/random.h>
   7#include <linux/string.h>
   8#include <linux/bitops.h>
   9#include <linux/slab.h>
  10#include <linux/mtd/nand_ecc.h>
  11
  12/*
  13 * Test the implementation for software ECC
  14 *
  15 * No actual MTD device is needed, So we don't need to warry about losing
  16 * important data by human error.
  17 *
  18 * This covers possible patterns of corruption which can be reliably corrected
  19 * or detected.
  20 */
  21
  22#if IS_ENABLED(CONFIG_MTD_NAND)
  23
  24struct nand_ecc_test {
  25        const char *name;
  26        void (*prepare)(void *, void *, void *, void *, const size_t);
  27        int (*verify)(void *, void *, void *, const size_t);
  28};
  29
  30/*
  31 * The reason for this __change_bit_le() instead of __change_bit() is to inject
  32 * bit error properly within the region which is not a multiple of
  33 * sizeof(unsigned long) on big-endian systems
  34 */
  35#ifdef __LITTLE_ENDIAN
  36#define __change_bit_le(nr, addr) __change_bit(nr, addr)
  37#elif defined(__BIG_ENDIAN)
  38#define __change_bit_le(nr, addr) \
  39                __change_bit((nr) ^ ((BITS_PER_LONG - 1) & ~0x7), addr)
  40#else
  41#error "Unknown byte order"
  42#endif
  43
  44static void single_bit_error_data(void *error_data, void *correct_data,
  45                                size_t size)
  46{
  47        unsigned int offset = prandom_u32() % (size * BITS_PER_BYTE);
  48
  49        memcpy(error_data, correct_data, size);
  50        __change_bit_le(offset, error_data);
  51}
  52
  53static void double_bit_error_data(void *error_data, void *correct_data,
  54                                size_t size)
  55{
  56        unsigned int offset[2];
  57
  58        offset[0] = prandom_u32() % (size * BITS_PER_BYTE);
  59        do {
  60                offset[1] = prandom_u32() % (size * BITS_PER_BYTE);
  61        } while (offset[0] == offset[1]);
  62
  63        memcpy(error_data, correct_data, size);
  64
  65        __change_bit_le(offset[0], error_data);
  66        __change_bit_le(offset[1], error_data);
  67}
  68
  69static unsigned int random_ecc_bit(size_t size)
  70{
  71        unsigned int offset = prandom_u32() % (3 * BITS_PER_BYTE);
  72
  73        if (size == 256) {
  74                /*
  75                 * Don't inject a bit error into the insignificant bits (16th
  76                 * and 17th bit) in ECC code for 256 byte data block
  77                 */
  78                while (offset == 16 || offset == 17)
  79                        offset = prandom_u32() % (3 * BITS_PER_BYTE);
  80        }
  81
  82        return offset;
  83}
  84
  85static void single_bit_error_ecc(void *error_ecc, void *correct_ecc,
  86                                size_t size)
  87{
  88        unsigned int offset = random_ecc_bit(size);
  89
  90        memcpy(error_ecc, correct_ecc, 3);
  91        __change_bit_le(offset, error_ecc);
  92}
  93
  94static void double_bit_error_ecc(void *error_ecc, void *correct_ecc,
  95                                size_t size)
  96{
  97        unsigned int offset[2];
  98
  99        offset[0] = random_ecc_bit(size);
 100        do {
 101                offset[1] = random_ecc_bit(size);
 102        } while (offset[0] == offset[1]);
 103
 104        memcpy(error_ecc, correct_ecc, 3);
 105        __change_bit_le(offset[0], error_ecc);
 106        __change_bit_le(offset[1], error_ecc);
 107}
 108
 109static void no_bit_error(void *error_data, void *error_ecc,
 110                void *correct_data, void *correct_ecc, const size_t size)
 111{
 112        memcpy(error_data, correct_data, size);
 113        memcpy(error_ecc, correct_ecc, 3);
 114}
 115
 116static int no_bit_error_verify(void *error_data, void *error_ecc,
 117                                void *correct_data, const size_t size)
 118{
 119        unsigned char calc_ecc[3];
 120        int ret;
 121
 122        __nand_calculate_ecc(error_data, size, calc_ecc);
 123        ret = __nand_correct_data(error_data, error_ecc, calc_ecc, size);
 124        if (ret == 0 && !memcmp(correct_data, error_data, size))
 125                return 0;
 126
 127        return -EINVAL;
 128}
 129
 130static void single_bit_error_in_data(void *error_data, void *error_ecc,
 131                void *correct_data, void *correct_ecc, const size_t size)
 132{
 133        single_bit_error_data(error_data, correct_data, size);
 134        memcpy(error_ecc, correct_ecc, 3);
 135}
 136
 137static void single_bit_error_in_ecc(void *error_data, void *error_ecc,
 138                void *correct_data, void *correct_ecc, const size_t size)
 139{
 140        memcpy(error_data, correct_data, size);
 141        single_bit_error_ecc(error_ecc, correct_ecc, size);
 142}
 143
 144static int single_bit_error_correct(void *error_data, void *error_ecc,
 145                                void *correct_data, const size_t size)
 146{
 147        unsigned char calc_ecc[3];
 148        int ret;
 149
 150        __nand_calculate_ecc(error_data, size, calc_ecc);
 151        ret = __nand_correct_data(error_data, error_ecc, calc_ecc, size);
 152        if (ret == 1 && !memcmp(correct_data, error_data, size))
 153                return 0;
 154
 155        return -EINVAL;
 156}
 157
 158static void double_bit_error_in_data(void *error_data, void *error_ecc,
 159                void *correct_data, void *correct_ecc, const size_t size)
 160{
 161        double_bit_error_data(error_data, correct_data, size);
 162        memcpy(error_ecc, correct_ecc, 3);
 163}
 164
 165static void single_bit_error_in_data_and_ecc(void *error_data, void *error_ecc,
 166                void *correct_data, void *correct_ecc, const size_t size)
 167{
 168        single_bit_error_data(error_data, correct_data, size);
 169        single_bit_error_ecc(error_ecc, correct_ecc, size);
 170}
 171
 172static void double_bit_error_in_ecc(void *error_data, void *error_ecc,
 173                void *correct_data, void *correct_ecc, const size_t size)
 174{
 175        memcpy(error_data, correct_data, size);
 176        double_bit_error_ecc(error_ecc, correct_ecc, size);
 177}
 178
 179static int double_bit_error_detect(void *error_data, void *error_ecc,
 180                                void *correct_data, const size_t size)
 181{
 182        unsigned char calc_ecc[3];
 183        int ret;
 184
 185        __nand_calculate_ecc(error_data, size, calc_ecc);
 186        ret = __nand_correct_data(error_data, error_ecc, calc_ecc, size);
 187
 188        return (ret == -1) ? 0 : -EINVAL;
 189}
 190
 191static const struct nand_ecc_test nand_ecc_test[] = {
 192        {
 193                .name = "no-bit-error",
 194                .prepare = no_bit_error,
 195                .verify = no_bit_error_verify,
 196        },
 197        {
 198                .name = "single-bit-error-in-data-correct",
 199                .prepare = single_bit_error_in_data,
 200                .verify = single_bit_error_correct,
 201        },
 202        {
 203                .name = "single-bit-error-in-ecc-correct",
 204                .prepare = single_bit_error_in_ecc,
 205                .verify = single_bit_error_correct,
 206        },
 207        {
 208                .name = "double-bit-error-in-data-detect",
 209                .prepare = double_bit_error_in_data,
 210                .verify = double_bit_error_detect,
 211        },
 212        {
 213                .name = "single-bit-error-in-data-and-ecc-detect",
 214                .prepare = single_bit_error_in_data_and_ecc,
 215                .verify = double_bit_error_detect,
 216        },
 217        {
 218                .name = "double-bit-error-in-ecc-detect",
 219                .prepare = double_bit_error_in_ecc,
 220                .verify = double_bit_error_detect,
 221        },
 222};
 223
 224static void dump_data_ecc(void *error_data, void *error_ecc, void *correct_data,
 225                        void *correct_ecc, const size_t size)
 226{
 227        pr_info("hexdump of error data:\n");
 228        print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4,
 229                        error_data, size, false);
 230        print_hex_dump(KERN_INFO, "hexdump of error ecc: ",
 231                        DUMP_PREFIX_NONE, 16, 1, error_ecc, 3, false);
 232
 233        pr_info("hexdump of correct data:\n");
 234        print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4,
 235                        correct_data, size, false);
 236        print_hex_dump(KERN_INFO, "hexdump of correct ecc: ",
 237                        DUMP_PREFIX_NONE, 16, 1, correct_ecc, 3, false);
 238}
 239
 240static int nand_ecc_test_run(const size_t size)
 241{
 242        int i;
 243        int err = 0;
 244        void *error_data;
 245        void *error_ecc;
 246        void *correct_data;
 247        void *correct_ecc;
 248
 249        error_data = kmalloc(size, GFP_KERNEL);
 250        error_ecc = kmalloc(3, GFP_KERNEL);
 251        correct_data = kmalloc(size, GFP_KERNEL);
 252        correct_ecc = kmalloc(3, GFP_KERNEL);
 253
 254        if (!error_data || !error_ecc || !correct_data || !correct_ecc) {
 255                err = -ENOMEM;
 256                goto error;
 257        }
 258
 259        prandom_bytes(correct_data, size);
 260        __nand_calculate_ecc(correct_data, size, correct_ecc);
 261
 262        for (i = 0; i < ARRAY_SIZE(nand_ecc_test); i++) {
 263                nand_ecc_test[i].prepare(error_data, error_ecc,
 264                                correct_data, correct_ecc, size);
 265                err = nand_ecc_test[i].verify(error_data, error_ecc,
 266                                                correct_data, size);
 267
 268                if (err) {
 269                        pr_err("not ok - %s-%zd\n",
 270                                nand_ecc_test[i].name, size);
 271                        dump_data_ecc(error_data, error_ecc,
 272                                correct_data, correct_ecc, size);
 273                        break;
 274                }
 275                pr_info("ok - %s-%zd\n",
 276                        nand_ecc_test[i].name, size);
 277        }
 278error:
 279        kfree(error_data);
 280        kfree(error_ecc);
 281        kfree(correct_data);
 282        kfree(correct_ecc);
 283
 284        return err;
 285}
 286
 287#else
 288
 289static int nand_ecc_test_run(const size_t size)
 290{
 291        return 0;
 292}
 293
 294#endif
 295
 296static int __init ecc_test_init(void)
 297{
 298        int err;
 299
 300        err = nand_ecc_test_run(256);
 301        if (err)
 302                return err;
 303
 304        return nand_ecc_test_run(512);
 305}
 306
 307static void __exit ecc_test_exit(void)
 308{
 309}
 310
 311module_init(ecc_test_init);
 312module_exit(ecc_test_exit);
 313
 314MODULE_DESCRIPTION("NAND ECC function test module");
 315MODULE_AUTHOR("Akinobu Mita");
 316MODULE_LICENSE("GPL");
 317