brintos

brintos / linux-shallow public Read only

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