mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2025-01-11 07:30:16 +00:00
c092b43906
Currently inject_single_bit_error() is used to inject single bit error into randomly selected bit position of the 256 or 512 bytes data block. Later change will add tests which inject bit errors into the ecc code. Unfortunately, inject_single_bit_error() doesn't work for the ecc code which is not a multiple of sizeof(unsigned long). Because bit fliping at random position is done by __change_bit(). For example, flipping bit position 0 by __change_bit(0, addr) modifies 3rd byte (32bit) or 7th byte (64bit) on big-endian systems. Using little-endian version of bitops can fix this issue. But little-endian version of __change_bit is not yet available. So this defines __change_bit_le() locally in a similar fashion to asm-generic/bitops/le.h and use it. Signed-off-by: Akinobu Mita <akinobu.mita@gmail.com> Signed-off-by: Artem Bityutskiy <artem.bityutskiy@linux.intel.com> Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
126 lines
3.0 KiB
C
126 lines
3.0 KiB
C
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/list.h>
|
|
#include <linux/random.h>
|
|
#include <linux/string.h>
|
|
#include <linux/bitops.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/mtd/nand_ecc.h>
|
|
|
|
#if defined(CONFIG_MTD_NAND) || defined(CONFIG_MTD_NAND_MODULE)
|
|
|
|
/*
|
|
* The reason for this __change_bit_le() instead of __change_bit() is to inject
|
|
* bit error properly within the region which is not a multiple of
|
|
* sizeof(unsigned long) on big-endian systems
|
|
*/
|
|
#ifdef __LITTLE_ENDIAN
|
|
#define __change_bit_le(nr, addr) __change_bit(nr, addr)
|
|
#elif defined(__BIG_ENDIAN)
|
|
#define __change_bit_le(nr, addr) \
|
|
__change_bit((nr) ^ ((BITS_PER_LONG - 1) & ~0x7), addr)
|
|
#else
|
|
#error "Unknown byte order"
|
|
#endif
|
|
|
|
static void inject_single_bit_error(void *data, size_t size)
|
|
{
|
|
unsigned int offset = random32() % (size * BITS_PER_BYTE);
|
|
|
|
__change_bit_le(offset, data);
|
|
}
|
|
|
|
static void dump_data_ecc(void *error_data, void *error_ecc, void *correct_data,
|
|
void *correct_ecc, const size_t size)
|
|
{
|
|
pr_info("hexdump of error data:\n");
|
|
print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4,
|
|
error_data, size, false);
|
|
print_hex_dump(KERN_INFO, "hexdump of error ecc: ",
|
|
DUMP_PREFIX_NONE, 16, 1, error_ecc, 3, false);
|
|
|
|
pr_info("hexdump of correct data:\n");
|
|
print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4,
|
|
correct_data, size, false);
|
|
print_hex_dump(KERN_INFO, "hexdump of correct ecc: ",
|
|
DUMP_PREFIX_NONE, 16, 1, correct_ecc, 3, false);
|
|
}
|
|
|
|
static int nand_ecc_test(const size_t size)
|
|
{
|
|
int err = 0;
|
|
void *error_data;
|
|
void *error_ecc;
|
|
void *correct_data;
|
|
void *correct_ecc;
|
|
char testname[30];
|
|
|
|
error_data = kmalloc(size, GFP_KERNEL);
|
|
error_ecc = kmalloc(3, GFP_KERNEL);
|
|
correct_data = kmalloc(size, GFP_KERNEL);
|
|
correct_ecc = kmalloc(3, GFP_KERNEL);
|
|
|
|
if (!error_data || !error_ecc || !correct_data || !correct_ecc) {
|
|
err = -ENOMEM;
|
|
goto error;
|
|
}
|
|
|
|
sprintf(testname, "nand-ecc-%zu", size);
|
|
|
|
get_random_bytes(correct_data, size);
|
|
|
|
memcpy(error_data, correct_data, size);
|
|
inject_single_bit_error(error_data, size);
|
|
|
|
__nand_calculate_ecc(correct_data, size, correct_ecc);
|
|
__nand_calculate_ecc(error_data, size, error_ecc);
|
|
__nand_correct_data(error_data, correct_ecc, error_ecc, size);
|
|
|
|
if (memcmp(correct_data, error_data, size)) {
|
|
pr_err("mtd_nandecctest: not ok - %s\n", testname);
|
|
dump_data_ecc(error_data, error_ecc, correct_data, correct_ecc,
|
|
size);
|
|
err = -EINVAL;
|
|
goto error;
|
|
}
|
|
pr_info("mtd_nandecctest: ok - %s\n", testname);
|
|
error:
|
|
kfree(error_data);
|
|
kfree(error_ecc);
|
|
kfree(correct_data);
|
|
kfree(correct_ecc);
|
|
|
|
return err;
|
|
}
|
|
|
|
#else
|
|
|
|
static int nand_ecc_test(const size_t size)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
#endif
|
|
|
|
static int __init ecc_test_init(void)
|
|
{
|
|
int err;
|
|
|
|
err = nand_ecc_test(256);
|
|
if (err)
|
|
return err;
|
|
|
|
return nand_ecc_test(512);
|
|
}
|
|
|
|
static void __exit ecc_test_exit(void)
|
|
{
|
|
}
|
|
|
|
module_init(ecc_test_init);
|
|
module_exit(ecc_test_exit);
|
|
|
|
MODULE_DESCRIPTION("NAND ECC function test module");
|
|
MODULE_AUTHOR("Akinobu Mita");
|
|
MODULE_LICENSE("GPL");
|