1 #include <linux/kernel.h>
2 #include <linux/module.h>
3 #include <linux/list.h>
4 #include <linux/random.h>
5 #include <linux/string.h>
6 #include <linux/bitops.h>
7 #include <linux/slab.h>
8 #include <linux/mtd/nand_ecc.h>
9 
10 #if defined(CONFIG_MTD_NAND) || defined(CONFIG_MTD_NAND_MODULE)
11 
12 /*
13  * The reason for this __change_bit_le() instead of __change_bit() is to inject
14  * bit error properly within the region which is not a multiple of
15  * sizeof(unsigned long) on big-endian systems
16  */
17 #ifdef __LITTLE_ENDIAN
18 #define __change_bit_le(nr, addr) __change_bit(nr, addr)
19 #elif defined(__BIG_ENDIAN)
20 #define __change_bit_le(nr, addr) \
21 		__change_bit((nr) ^ ((BITS_PER_LONG - 1) & ~0x7), addr)
22 #else
23 #error "Unknown byte order"
24 #endif
25 
26 static void inject_single_bit_error(void *data, size_t size)
27 {
28 	unsigned int offset = random32() % (size * BITS_PER_BYTE);
29 
30 	__change_bit_le(offset, data);
31 }
32 
33 static void dump_data_ecc(void *error_data, void *error_ecc, void *correct_data,
34 			void *correct_ecc, const size_t size)
35 {
36 	pr_info("hexdump of error data:\n");
37 	print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4,
38 			error_data, size, false);
39 	print_hex_dump(KERN_INFO, "hexdump of error ecc: ",
40 			DUMP_PREFIX_NONE, 16, 1, error_ecc, 3, false);
41 
42 	pr_info("hexdump of correct data:\n");
43 	print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4,
44 			correct_data, size, false);
45 	print_hex_dump(KERN_INFO, "hexdump of correct ecc: ",
46 			DUMP_PREFIX_NONE, 16, 1, correct_ecc, 3, false);
47 }
48 
49 static int nand_ecc_test(const size_t size)
50 {
51 	int err = 0;
52 	void *error_data;
53 	void *error_ecc;
54 	void *correct_data;
55 	void *correct_ecc;
56 	char testname[30];
57 
58 	error_data = kmalloc(size, GFP_KERNEL);
59 	error_ecc = kmalloc(3, GFP_KERNEL);
60 	correct_data = kmalloc(size, GFP_KERNEL);
61 	correct_ecc = kmalloc(3, GFP_KERNEL);
62 
63 	if (!error_data || !error_ecc || !correct_data || !correct_ecc) {
64 		err = -ENOMEM;
65 		goto error;
66 	}
67 
68 	sprintf(testname, "nand-ecc-%zu", size);
69 
70 	get_random_bytes(correct_data, size);
71 
72 	memcpy(error_data, correct_data, size);
73 	inject_single_bit_error(error_data, size);
74 
75 	__nand_calculate_ecc(correct_data, size, correct_ecc);
76 	__nand_calculate_ecc(error_data, size, error_ecc);
77 	__nand_correct_data(error_data, correct_ecc, error_ecc, size);
78 
79 	if (memcmp(correct_data, error_data, size)) {
80 		pr_err("mtd_nandecctest: not ok - %s\n", testname);
81 		dump_data_ecc(error_data, error_ecc, correct_data, correct_ecc,
82 				size);
83 		err = -EINVAL;
84 		goto error;
85 	}
86 	pr_info("mtd_nandecctest: ok - %s\n", testname);
87 error:
88 	kfree(error_data);
89 	kfree(error_ecc);
90 	kfree(correct_data);
91 	kfree(correct_ecc);
92 
93 	return err;
94 }
95 
96 #else
97 
98 static int nand_ecc_test(const size_t size)
99 {
100 	return 0;
101 }
102 
103 #endif
104 
105 static int __init ecc_test_init(void)
106 {
107 	int err;
108 
109 	err = nand_ecc_test(256);
110 	if (err)
111 		return err;
112 
113 	return nand_ecc_test(512);
114 }
115 
116 static void __exit ecc_test_exit(void)
117 {
118 }
119 
120 module_init(ecc_test_init);
121 module_exit(ecc_test_exit);
122 
123 MODULE_DESCRIPTION("NAND ECC function test module");
124 MODULE_AUTHOR("Akinobu Mita");
125 MODULE_LICENSE("GPL");
126