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