[PATCH] nand: add nandflipbits tool

Dongsheng Yang yangds.fnst at cn.fujitsu.com
Fri Jan 8 01:14:22 PST 2016


On 11/13/2015 05:04 AM, Brian Norris wrote:
> Hi Boris,

Hi Boris,
	do you have a time to update this patch, good
tool to us for debugging.

Yang
>
> On Thu, Nov 27, 2014 at 02:29:18PM +0100, Boris Brezillon wrote:
>
> ^^ This patch hasn't quite reached its anniversary! Sorry :(
>
>> The nandflipbits tool is intended to be used when one need to flip one or
>> several specific bits on a NAND media.
>> It can be useful to manually recover from an unexpected bit flip on a flash
>> device, though the main purpose of this tool is to provide a way to test
>> ECC algorithms robustness.
>>
>> One typical example I used this tool for is testing HW ECC engines behavior
>> when bitflips occur in an erased page: most HW engines do not correctly
>> handle this case, because, most of the time, ECC bits generated for an
>> empty page are not all 1s, and, empty page detection embedded in such
>> engines is only validating that all bits are set to 1s (which is not true
>> when a bit-flip has occurred).
>>
>> Signed-off-by: Boris Brezillon <boris.brezillon at free-electrons.com>
>> ---
>>   Makefile       |   2 +-
>>   nandflipbits.c | 332 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>
> If you're going to resubmit this, please move it to nand-utils now. Or
> would it be tests/?
>
>>   2 files changed, 333 insertions(+), 1 deletion(-)
>>   create mode 100644 nandflipbits.c
>>
>> diff --git a/Makefile b/Makefile
>> index eade234..55252f8 100644
>> --- a/Makefile
>> +++ b/Makefile
>> @@ -20,7 +20,7 @@ MTD_BINS = \
>>   	ftl_format flash_erase nanddump doc_loadbios \
>>   	ftl_check mkfs.jffs2 flash_lock flash_unlock \
>>   	flash_otp_info flash_otp_dump flash_otp_lock flash_otp_write \
>> -	mtd_debug flashcp nandwrite nandtest \
>> +	mtd_debug flashcp nandwrite nandtest nandflipbits \
>>   	jffs2dump \
>>   	nftldump nftl_format docfdisk \
>>   	rfddump rfdformat \
>> diff --git a/nandflipbits.c b/nandflipbits.c
>> new file mode 100644
>> index 0000000..9ec39cf
>> --- /dev/null
>> +++ b/nandflipbits.c
>> @@ -0,0 +1,332 @@
>> +/*
>> + *  nandflipbits.c
>> + *
>> + *  Copyright (C) 2014 Free Electrons
>> + *
>> + *  Author: Boris Brezillon <boris.brezillon at free-electrons.com>
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 as
>> + * published by the Free Software Foundation.
>> + *
>> + * Overview:
>> + *   This utility manually flip specified bits in a NAND flash.
>
> s/flip/flips/
>
>> + *
>> + */
>> +
>> +#define PROGRAM_NAME "nandwrite"
>
> s/nandwrite/nandflipbits/
>
>> +
>> +#include <ctype.h>
>> +#include <errno.h>
>> +#include <fcntl.h>
>> +#include <stdbool.h>
>> +#include <stddef.h>
>> +#include <stdint.h>
>> +#include <stdio.h>
>> +#include <stdlib.h>
>> +#include <string.h>
>> +#include <time.h>
>
> Don't need this header? I bet you copied this from nandwrite, which also
> probably doesn't need it.
>
>> +#include <unistd.h>
>> +#include <sys/stat.h>
>> +#include <sys/ioctl.h>
>> +#include <sys/types.h>
>> +#include <getopt.h>
>> +
>> +#include <asm/types.h>
>> +#include "mtd/mtd-user.h"
>> +#include "common.h"
>> +#include <libmtd.h>
>> +
>> +struct bit_flip {
>> +	int block;
>> +	off_t offset;
>> +	int bit;
>> +	bool done;
>> +};
>> +
>> +static void display_help(int status)
>> +{
>> +	fprintf(status == EXIT_SUCCESS ? stdout : stderr,
>> +"Usage: nandflipbits [OPTION] MTD_DEVICE <bit>@<address>[:<bit>@<address>]\n"
>
> Why is the ':' separation chosen? Couldn't we just take extra args
> (i.e., separated by ' ')? That might make the parsing a bit simpler too,
> since you could just use argc to determine how many 'bit_flip's to
> allocate.
>
>> +"Writes to the specified MTD device.\n"
>
> I have a few more related comments later on, but it seems like this help
> text deserves a bit more substance. The minimal comment at the top of
> the file is already more helpful than this text :) e.g.:
>
>   1) What does this do? It does more than "write to the specified MTD
>   device"...
>   2) It requires RAW support, and not every driver implements this
>   correctly
>   3) It may not leave a block in a great state, if you're trying to flip
>   bits in an erased block on a NOP=1 device, for testing purposes.
>   4) What is it really *intended* for? e.g., testing the ECC engine to
>   see if it matches the specified correction strength
>
> Random thought related to #4: perhaps a simple test or two could be
> derived from this. e.g., this could begin to replace
> drivers/mtd/tests/nandbiterrs.c, I think.
>
>> +"\n"
>> +"  -o, --oob               Provided addresses take OOB area into account\n"
>> +"  -q, --quiet             Don't display progress messages\n"
>> +"  -h, --help              Display this help and exit\n"
>> +"      --version           Output version information and exit\n"
>> +	);
>> +	exit(status);
>> +}
>> +
>> +static void display_version(void)
>> +{
>> +	printf("%1$s " VERSION "\n"
>> +			"\n"
>> +			"Copyright (C) 2014 Free Electrons \n"
>> +			"\n"
>> +			"%1$s comes with NO WARRANTY\n"
>> +			"to the extent permitted by law.\n"
>> +			"\n"
>> +			"You may redistribute copies of %1$s\n"
>> +			"under the terms of the GNU General Public Licence.\n"
>> +			"See the file `COPYING' for more information.\n",
>> +			PROGRAM_NAME);
>> +	exit(EXIT_SUCCESS);
>> +}
>> +
>> +static const char	*mtd_device;
>> +static bool		quiet = false;
>> +static bool		oob_mode = false;
>> +static struct bit_flip	*bits_to_flip;
>> +static int		nbits_to_flip = 0;
>> +
>> +static void process_options(int argc, char * const argv[])
>> +{
>> +	char *bits_to_flip_iter;
>> +	int bit_idx = 0;
>> +	int error = 0;
>> +
>> +	for (;;) {
>> +		int option_index = 0;
>> +		static const char short_options[] = "hoq";
>> +		static const struct option long_options[] = {
>> +			/* Order of these args with val==0 matters; see option_index. */
>> +			{"version", no_argument, 0, 0},
>
> I see you're borrowing this from nanddump/nandwrite, but it seems kinda
> hacky to rely on this being the first entry. Maybe you can put 'v' in
> the 4th field, so we can just have a 'case 'v':' below, even if we don't
> want to give it a shortopt of -v?
>
>> +			{"help", no_argument, 0, 'h'},
>> +			{"oob", no_argument, 0, 'o'},
>> +			{"quiet", no_argument, 0, 'q'},
>> +			{0, 0, 0, 0},
>> +		};
>> +
>> +		int c = getopt_long(argc, argv, short_options,
>> +				long_options, &option_index);
>> +		if (c == EOF)
>> +			break;
>> +
>> +		switch (c) {
>> +		case 0:
>
> ^^ Then this could be a case 'v' instead.
>
>> +			if (!option_index) {
>
> Or if we don't do that, it might be clearer to say:
>
> 			if (option_index == 0)
>
> To show that the value '0' means something more than just "nothing" (as
> in, NULL checking).
>
>> +				display_version();
>> +				break;
>> +			}
>> +			break;
>> +		case 'q':
>> +			quiet = true;
>> +			break;
>> +		case 'o':
>> +			oob_mode = true;
>> +			break;
>> +		case 'h':
>> +			display_help(EXIT_SUCCESS);
>> +			break;
>> +		case '?':
>
> Maybe put 'default:' here too, just in case we get some of the options
> flags wrong in the future? e.g., we add to short_options[] and/or
> long_options[] but forget to handle it in the switch/case?
>
>> +			error++;
>> +			break;
>> +		}
>> +	}
>> +
>> +	argc -= optind;
>> +	argv += optind;
>> +
>> +	/*
>> +	 * There must be at least the MTD device node path argument remaining
>> +	 * and the 'bits-to-flip' description.
>> +	 */
>> +
>> +	if (argc != 2 || error)
>> +		display_help(EXIT_FAILURE);
>> +
>> +	mtd_device = argv[0];
>> +	bits_to_flip_iter = argv[1];
>> +	do {
>> +		nbits_to_flip++;
>> +		bits_to_flip_iter = strstr(bits_to_flip_iter, ":");
>> +		if (bits_to_flip_iter)
>> +			bits_to_flip_iter++;
>> +	} while (bits_to_flip_iter);
>
> If you change the argument input to avoid the ':' separation, you don't
> need this loop. You can just compute the following xmalloc() size using
> argc.
>
>> +
>> +	bits_to_flip = xmalloc(sizeof(*bits_to_flip) * nbits_to_flip);
>> +	bits_to_flip_iter = argv[1];
>> +
>> +	do {
>
> And then this would be a for loop, not a do-while.
>
>> +		struct bit_flip	*bit_to_flip = &bits_to_flip[bit_idx++];
>> +
>> +		bit_to_flip->bit = strtol(bits_to_flip_iter,
>> +					  &bits_to_flip_iter, 0);
>> +		if (errno || bit_to_flip->bit > 7)
>
> Hmm, is it valid to check errno before checking the return value? That's
> not how I read the man page at least.
>
> Once this is a loop over argv[], it should be pretty easy to use
> strsep(arg, "@") and then simple_strtol() on each half. Makes the error
> handling a bit simpler and more obvious, I think.
>
>> +			goto err_invalid_bit_desc;
>> +
>> +		if (!bits_to_flip_iter || *bits_to_flip_iter++ != '@')
>> +			goto err_invalid_bit_desc;
>> +
>> +		bit_to_flip->offset = strtol(bits_to_flip_iter,
>> +					     &bits_to_flip_iter, 0);
>> +
>> +		if (!bits_to_flip_iter || errno)
>> +			goto err_invalid_bit_desc;
>> +
>> +		bits_to_flip_iter = strstr(bits_to_flip_iter, ":");
>> +		if (bits_to_flip_iter)
>> +			bits_to_flip_iter++;
>> +	} while (bits_to_flip_iter);
>> +
>> +	return;
>> +
>> +err_invalid_bit_desc:
>> +	fprintf(stderr, "Invalid bit description\n");
>> +	exit(EXIT_FAILURE);
>> +}
>
> Blank line here?
>
>> +int main(int argc, char **argv)
>> +{
>> +	libmtd_t mtd_desc;
>> +	struct mtd_dev_info mtd;
>> +	int pagelen;
>> +	int pages_per_blk;
>> +	size_t blklen;
>> +	long long mtdlen;
>> +	int fd;
>> +	int ret;
>> +	uint8_t *buffer;
>> +	int i;
>> +
>> +	process_options(argc, argv);
>> +
>> +	/* Open the device */
>> +	if ((fd = open(mtd_device, O_RDWR)) == -1)
>> +		sys_errmsg_die("%s", mtd_device);
>> +
>> +	mtd_desc = libmtd_open();
>> +	if (!mtd_desc)
>> +		errmsg_die("can't initialize libmtd");
>> +
>> +	ret = ioctl(fd, MTDFILEMODE, MTD_FILE_MODE_RAW);
>> +	if (ret) {
>> +		printf("Failed to set MTD in raw access mode\n");
>> +		return -errno;
>> +	}
>> +
>> +	/* Fill in MTD device capability structure */
>> +	if (mtd_get_dev_info(mtd_desc, mtd_device, &mtd) < 0)
>> +		errmsg_die("mtd_get_dev_info failed");
>> +
>> +	/* Select OOB write mode */
>> +	ret = ioctl(fd, MTDFILEMODE, MTD_FILE_MODE_RAW);
>
> Why do you need to do this twice?
>
> Also, would this be helpful to add this to libmtd? We could do the
> following error handling in one place (to handle for
> nanddump/nandwrite/nandflipbits).
>
>> +	if (ret) {
>> +		switch (errno) {
>> +		case ENOTTY:
>> +			errmsg_die("ioctl MTDFILEMODE is missing");
>> +		default:
>> +			sys_errmsg_die("MTDFILEMODE");
>> +		}
>> +	}
>> +
>> +	pagelen = mtd.min_io_size + (oob_mode ? mtd.oob_size : 0);
>> +	pages_per_blk = mtd.eb_size / mtd.min_io_size;
>> +	blklen = pages_per_blk * pagelen;
>> +	mtdlen = blklen * mtd.eb_cnt;
>> +	buffer = xmalloc((mtd.min_io_size + mtd.oob_size) * pages_per_blk);
>> +
>> +	for (i = 0; i < nbits_to_flip; i++) {
>> +		int page;
>> +
>> +		if (bits_to_flip[i].offset >= mtdlen)
>> +			errmsg_die("Invalid bit offset");
>> +
>> +		bits_to_flip[i].block = bits_to_flip[i].offset / blklen;
>> +		bits_to_flip[i].offset %= blklen;
>> +		page = bits_to_flip[i].offset / pagelen;
>> +		bits_to_flip[i].offset = (page *
>> +					  (mtd.min_io_size + mtd.oob_size)) +
>> +					 (bits_to_flip[i].offset % pagelen);
>> +
>> +	}
>> +
>> +	while (1) {
>> +		struct bit_flip	*bit_to_flip = NULL;
>> +		int blkoffs;
>> +		int bufoffs;
>> +
>> +		for (i = 0; i < nbits_to_flip; i++) {
>> +			if (bits_to_flip[i].done == false) {
>> +				bit_to_flip = &bits_to_flip[i];
>> +				break;
>> +			}
>> +		}
>> +
>> +		if (!bit_to_flip)
>> +			break;
>> +
>> +		blkoffs = 0;
>> +		bufoffs = 0;
>> +		for (i = 0; i < pages_per_blk; i++) {
>> +			ret = mtd_read(&mtd, fd, bit_to_flip->block, blkoffs,
>> +				       buffer + bufoffs, mtd.min_io_size);
>> +			if (ret) {
>> +				sys_errmsg("%s: MTD Read failure", mtd_device);
>> +				goto closeall;
>> +			}
>> +
>> +			bufoffs += mtd.min_io_size;
>> +
>> +			ret = mtd_read_oob(mtd_desc, &mtd, fd, blkoffs,
>> +					   mtd.oob_size, buffer + bufoffs);
>> +			if (ret) {
>> +				sys_errmsg("%s: MTD Read OOB failure", mtd_device);
>> +				goto closeall;
>> +			}
>> +
>> +			bufoffs += mtd.oob_size;
>> +			blkoffs += mtd.min_io_size;
>> +		}
>> +
>> +		for (i = 0; i < nbits_to_flip; i++) {
>> +			unsigned char val, mask;
>> +
>> +			if (bits_to_flip[i].block != bit_to_flip->block)
>> +				continue;
>> +
>> +			mask = 1 << bits_to_flip[i].bit;
>> +			val = buffer[bits_to_flip[i].offset] & mask;
>> +			if (val)
>> +				buffer[bits_to_flip[i].offset] &= ~mask;
>> +			else
>> +				buffer[bits_to_flip[i].offset] |= mask;
>> +		}
>> +
>> +		ret = mtd_erase(mtd_desc, &mtd, fd, bit_to_flip->block);
>> +		if (ret) {
>> +			sys_errmsg("%s: MTD Erase failure", mtd_device);
>> +			goto closeall;
>> +		}
>> +
>> +		blkoffs = 0;
>> +		bufoffs = 0;
>> +		for (i = 0; i < pages_per_blk; i++) {
>> +			ret = mtd_write(mtd_desc, &mtd, fd, bit_to_flip->block,
>> +					blkoffs, buffer + bufoffs, mtd.min_io_size,
>> +					buffer + bufoffs + mtd.min_io_size,
>> +					mtd.oob_size,
>> +					MTD_OPS_RAW);
>
> How effective is this in practice? You're doing a read/modify/write on
> the whole block, and that seems a bit error prone if you assume it can
> still allow, e.g., further writes to an MLC/NOP=1 device when you're
> flipping an erased block.
>
> Perhaps the caveats should be better documented in the help message.
> Don't want people getting the wrong idea about this tool.
>
> Regards,
> Brian
>
>> +			if (ret) {
>> +				sys_errmsg("%s: MTD Write failure", mtd_device);
>> +				goto closeall;
>> +			}
>> +
>> +			blkoffs += mtd.min_io_size;
>> +			bufoffs += mtd.min_io_size + mtd.oob_size;
>> +		}
>> +
>> +		for (i = 0; i < nbits_to_flip; i++) {
>> +			if (bits_to_flip[i].block == bit_to_flip->block)
>> +				bits_to_flip[i].done = true;
>> +		}
>> +	}
>> +
>> +	return 0;
>> +
>> +closeall:
>> +	libmtd_close(mtd_desc);
>> +	close(fd);
>> +	free(buffer);
>> +	exit(EXIT_FAILURE);
>> +}
>> --
>> 1.9.1
>>
>
> ______________________________________________________
> Linux MTD discussion mailing list
> http://lists.infradead.org/mailman/listinfo/linux-mtd/
> .
>






More information about the linux-mtd mailing list