diff options
Diffstat (limited to 'nand-utils')
| -rwxr-xr-x | nand-utils/load_nandsim.sh | 127 | ||||
| -rw-r--r-- | nand-utils/nanddump.c | 490 | ||||
| -rw-r--r-- | nand-utils/nandtest.c | 317 | ||||
| -rw-r--r-- | nand-utils/nandwrite.c | 578 | ||||
| -rw-r--r-- | nand-utils/nftl_format.c | 422 | ||||
| -rw-r--r-- | nand-utils/nftldump.c | 278 | 
6 files changed, 2212 insertions, 0 deletions
| diff --git a/nand-utils/load_nandsim.sh b/nand-utils/load_nandsim.sh new file mode 100755 index 0000000..4d9f0cb --- /dev/null +++ b/nand-utils/load_nandsim.sh @@ -0,0 +1,127 @@ +#!/bin/sh -euf + +# +# This script inserts NAND simulator module to emulate NAND flash of specified +# size. +# +# Author: Artem Bityutskiy +# + +fatal() +{ +        echo "Error: $1" 1>&2 +        exit 1 +} + +usage() +{ +	cat 1>&2 <<EOF +Load NAND simulator to simulate flash of a specified size. + +Usage: ${0##*/} <size in MiB> <eraseblock size in KiB> \\ +       <page size (512 or 2048)> + +Only the first parameter is mandatory. Default eraseblock size +is 16KiB, default NAND page size is 512 bytes. + +Only the following combinations are supported: +-------------------------------------------------- +| size (MiB) | EB size (KiB) | Page size (bytes) | +-------------------------------------------------- +| 16         | 16            | 512               | +| 32         | 16            | 512               | +| 64         | 16            | 512               | +| 128        | 16            | 512               | +| 256        | 16            | 512               | +| 64         | 64            | 2048              | +| 64         | 128           | 2048              | +| 64         | 256           | 2048              | +| 64         | 512           | 2048              | +| 128        | 64            | 2048              | +| 128        | 128           | 2048              | +| 128        | 256           | 2048              | +| 128        | 512           | 2048              | +| 256        | 64            | 2048              | +| 256        | 128           | 2048              | +| 256        | 256           | 2048              | +| 256        | 512           | 2048              | +| 512        | 64            | 2048              | +| 512        | 128           | 2048              | +| 512        | 256           | 2048              | +| 512        | 512           | 2048              | +| 1024       | 64            | 2048              | +| 1024       | 128           | 2048              | +| 1024       | 256           | 2048              | +| 1024       | 512           | 2048              | +-------------------------------------------------- +EOF +} + +if grep -q "NAND simulator" /proc/mtd; then +	fatal "nandsim is already loaded" +fi + +if [ "$#" -lt "1" ]; then +	usage +	exit 1 +fi + +size="$1" +eb_size="$2" +page_size="$3" +if [ "$#" = "1" ]; then +	eb_size="16" +	page_size="512" +elif [ "$#" = "2" ]; then +	page_size="512" +fi + +if [ "$page_size" -eq 512 ] && [ "$eb_size" -ne "16" ]; then +	fatal "only 16KiB eraseblocks are possible in case of 512 bytes page" +fi + +first= +second= +third= +fourth= + +if [ "$page_size" -eq "512" ]; then +	first="0x20" +	case "$size" in +	16)  second=0x33 ;; +	32)  second=0x35 ;; +	64)  second=0x36 ;; +	128) second=0x78 ;; +	256) second=0x71 ;; +	*) fatal "flash size ${size}MiB is not supported, try 16, 32, 64 or 256" +	esac +elif [ "$page_size" -eq "2048" ]; then +	case "$eb_size" in +	64)  fourth="0x05" ;; +	128) fourth="0x15" ;; +	256) fourth="0x25" ;; +	512) fourth="0x35" ;; +	*)   fatal "eraseblock ${eb_size}KiB is not supported" +	esac + + +	case "$size" in +	64)   first="0x20"; second="0xa2"; third="0x00 ";; +	128)  first="0xec"; second="0xa1"; third="0x00 ";; +	256)  first="0x20"; second="0xaa"; third="0x00 ";; +	512)  first="0x20"; second="0xac"; third="0x00 ";; +	1024) first="0xec"; second="0xd3"; third="0x51 ";; +	*) fatal "unable to emulate ${size}MiB flash with ${eb_size}KiB eraseblock" +	esac +else +	fatal "bad NAND page size ${page_size}KiB, it has to be either 512 or 2048" +fi + +first="first_id_byte=$first" +second="second_id_byte=$second" +[ -z "$third" ]  || third="third_id_byte=$third" +[ -z "$fourth" ] || fourth="fourth_id_byte=$fourth" + +modprobe nandsim "$first" "$second" $third $fourth + +echo "Loaded NAND simulator (${size}MiB, ${eb_size}KiB eraseblock, $page_size bytes NAND page)" diff --git a/nand-utils/nanddump.c b/nand-utils/nanddump.c new file mode 100644 index 0000000..4ee7ed4 --- /dev/null +++ b/nand-utils/nanddump.c @@ -0,0 +1,490 @@ +/* + *  nanddump.c + * + *  Copyright (C) 2000 David Woodhouse (dwmw2@infradead.org) + *                     Steven J. Hill (sjhill@realitydiluted.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 dumps the contents of raw NAND chips or NAND + *   chips contained in DoC devices. + */ + +#define PROGRAM_NAME "nanddump" + +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <getopt.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include <asm/types.h> +#include <mtd/mtd-user.h> +#include "common.h" +#include <libmtd.h> + +static void display_help(int status) +{ +	fprintf(status == EXIT_SUCCESS ? stdout : stderr, +"Usage: %s [OPTIONS] MTD-device\n" +"Dumps the contents of a nand mtd partition.\n" +"\n" +"-h         --help               Display this help and exit\n" +"           --version            Output version information and exit\n" +"           --bb=METHOD          Choose bad block handling method (see below).\n" +"-a         --forcebinary        Force printing of binary data to tty\n" +"-c         --canonicalprint     Print canonical Hex+ASCII dump\n" +"-f file    --file=file          Dump to file\n" +"-l length  --length=length      Length\n" +"-n         --noecc              Read without error correction\n" +"           --omitoob            Omit OOB data (default)\n" +"-o         --oob                Dump OOB data\n" +"-p         --prettyprint        Print nice (hexdump)\n" +"-q         --quiet              Don't display progress and status messages\n" +"-s addr    --startaddress=addr  Start address\n" +"\n" +"--bb=METHOD, where METHOD can be `padbad', `dumpbad', or `skipbad':\n" +"    padbad:  dump flash data, substituting 0xFF for any bad blocks\n" +"    dumpbad: dump flash data, including any bad blocks\n" +"    skipbad: dump good data, completely skipping any bad blocks (default)\n", +	PROGRAM_NAME); +	exit(status); +} + +static void display_version(void) +{ +	printf("%1$s " VERSION "\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); +} + +// Option variables + +static bool			pretty_print = false;	// print nice +static bool			noecc = false;		// don't error correct +static bool			omitoob = true;		// omit oob data +static long long		start_addr;		// start address +static long long		length;			// dump length +static const char		*mtddev;		// mtd device name +static const char		*dumpfile;		// dump file name +static bool			quiet = false;		// suppress diagnostic output +static bool			canonical = false;	// print nice + ascii +static bool			forcebinary = false;	// force printing binary to tty + +static enum { +	padbad,   // dump flash data, substituting 0xFF for any bad blocks +	dumpbad,  // dump flash data, including any bad blocks +	skipbad,  // dump good data, completely skipping any bad blocks +} bb_method = skipbad; + +static void process_options(int argc, char * const argv[]) +{ +	int error = 0; +	bool oob_default = true; + +	for (;;) { +		int option_index = 0; +		static const char short_options[] = "hs:f:l:opqnca"; +		static const struct option long_options[] = { +			{"version", no_argument, 0, 0}, +			{"bb", required_argument, 0, 0}, +			{"omitoob", no_argument, 0, 0}, +			{"help", no_argument, 0, 'h'}, +			{"forcebinary", no_argument, 0, 'a'}, +			{"canonicalprint", no_argument, 0, 'c'}, +			{"file", required_argument, 0, 'f'}, +			{"oob", no_argument, 0, 'o'}, +			{"prettyprint", no_argument, 0, 'p'}, +			{"startaddress", required_argument, 0, 's'}, +			{"length", required_argument, 0, 'l'}, +			{"noecc", no_argument, 0, 'n'}, +			{"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: +				switch (option_index) { +					case 0: +						display_version(); +						break; +					case 1: +						/* Handle --bb=METHOD */ +						if (!strcmp(optarg, "padbad")) +							bb_method = padbad; +						else if (!strcmp(optarg, "dumpbad")) +							bb_method = dumpbad; +						else if (!strcmp(optarg, "skipbad")) +							bb_method = skipbad; +						else +							error++; +						break; +					case 2: /* --omitoob */ +						if (oob_default) { +							oob_default = false; +							omitoob = true; +						} else { +							errmsg_die("--oob and --oomitoob are mutually exclusive"); +						} +						break; +				} +				break; +			case 's': +				start_addr = simple_strtoll(optarg, &error); +				break; +			case 'f': +				dumpfile = xstrdup(optarg); +				break; +			case 'l': +				length = simple_strtoll(optarg, &error); +				break; +			case 'o': +				if (oob_default) { +					oob_default = false; +					omitoob = false; +				} else { +					errmsg_die("--oob and --oomitoob are mutually exclusive"); +				} +				break; +			case 'a': +				forcebinary = true; +				break; +			case 'c': +				canonical = true; +			case 'p': +				pretty_print = true; +				break; +			case 'q': +				quiet = true; +				break; +			case 'n': +				noecc = true; +				break; +			case 'h': +				display_help(EXIT_SUCCESS); +				break; +			case '?': +				error++; +				break; +		} +	} + +	if (start_addr < 0) +		errmsg_die("Can't specify negative offset with option -s: %lld", +				start_addr); + +	if (length < 0) +		errmsg_die("Can't specify negative length with option -l: %lld", length); + +	if (quiet && pretty_print) { +		fprintf(stderr, "The quiet and pretty print options are mutually-\n" +				"exclusive. Choose one or the other.\n"); +		exit(EXIT_FAILURE); +	} + +	if (forcebinary && pretty_print) { +		fprintf(stderr, "The forcebinary and pretty print options are\n" +				"mutually-exclusive. Choose one or the " +				"other.\n"); +		exit(EXIT_FAILURE); +	} + +	if ((argc - optind) != 1 || error) +		display_help(EXIT_FAILURE); + +	mtddev = argv[optind]; +} + +#define PRETTY_ROW_SIZE 16 +#define PRETTY_BUF_LEN 80 + +/** + * pretty_dump_to_buffer - formats a blob of data to "hex ASCII" in memory + * @buf: data blob to dump + * @len: number of bytes in the @buf + * @linebuf: where to put the converted data + * @linebuflen: total size of @linebuf, including space for terminating NULL + * @pagedump: true - dumping as page format; false - dumping as OOB format + * @ascii: dump ascii formatted data next to hexdump + * @prefix: address to print before line in a page dump, ignored if !pagedump + * + * pretty_dump_to_buffer() works on one "line" of output at a time, i.e., + * PRETTY_ROW_SIZE bytes of input data converted to hex + ASCII output. + * + * Given a buffer of unsigned char data, pretty_dump_to_buffer() converts the + * input data to a hex/ASCII dump at the supplied memory location. A prefix + * is included based on whether we are dumping page or OOB data. The converted + * output is always NULL-terminated. + * + * e.g. + *   pretty_dump_to_buffer(data, data_len, prettybuf, linelen, true, + *                         false, 256); + * produces: + *   0x00000100: 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f + * NOTE: This function was adapted from linux kernel, "lib/hexdump.c" + */ +static void pretty_dump_to_buffer(const unsigned char *buf, size_t len, +		char *linebuf, size_t linebuflen, bool pagedump, bool ascii, +		unsigned long long prefix) +{ +	static const char hex_asc[] = "0123456789abcdef"; +	unsigned char ch; +	unsigned int j, lx = 0, ascii_column; + +	if (pagedump) +		lx += sprintf(linebuf, "0x%.8llx: ", prefix); +	else +		lx += sprintf(linebuf, "  OOB Data: "); + +	if (!len) +		goto nil; +	if (len > PRETTY_ROW_SIZE)	/* limit to one line at a time */ +		len = PRETTY_ROW_SIZE; + +	for (j = 0; (j < len) && (lx + 3) <= linebuflen; j++) { +		ch = buf[j]; +		linebuf[lx++] = hex_asc[(ch & 0xf0) >> 4]; +		linebuf[lx++] = hex_asc[ch & 0x0f]; +		linebuf[lx++] = ' '; +	} +	if (j) +		lx--; + +	ascii_column = 3 * PRETTY_ROW_SIZE + 14; + +	if (!ascii) +		goto nil; + +	/* Spacing between hex and ASCII - ensure at least one space */ +	lx += sprintf(linebuf + lx, "%*s", +			MAX((int)MIN(linebuflen, ascii_column) - 1 - lx, 1), +			" "); + +	linebuf[lx++] = '|'; +	for (j = 0; (j < len) && (lx + 2) < linebuflen; j++) +		linebuf[lx++] = (isascii(buf[j]) && isprint(buf[j])) ? buf[j] +			: '.'; +	linebuf[lx++] = '|'; +nil: +	linebuf[lx++] = '\n'; +	linebuf[lx++] = '\0'; +} + + +/* + * Main program + */ +int main(int argc, char * const argv[]) +{ +	long long ofs, end_addr = 0; +	long long blockstart = 1; +	int i, fd, ofd = 0, bs, badblock = 0; +	struct mtd_dev_info mtd; +	char pretty_buf[PRETTY_BUF_LEN]; +	int firstblock = 1; +	struct mtd_ecc_stats stat1, stat2; +	bool eccstats = false; +	unsigned char *readbuf = NULL, *oobbuf = NULL; +	libmtd_t mtd_desc; + +	process_options(argc, argv); + +	/* Initialize libmtd */ +	mtd_desc = libmtd_open(); +	if (!mtd_desc) +		return errmsg("can't initialize libmtd"); + +	/* Open MTD device */ +	if ((fd = open(mtddev, O_RDONLY)) == -1) { +		perror(mtddev); +		exit(EXIT_FAILURE); +	} + +	/* Fill in MTD device capability structure */ +	if (mtd_get_dev_info(mtd_desc, mtddev, &mtd) < 0) +		return errmsg("mtd_get_dev_info failed"); + +	/* Allocate buffers */ +	oobbuf = xmalloc(sizeof(oobbuf) * mtd.oob_size); +	readbuf = xmalloc(sizeof(readbuf) * mtd.min_io_size); + +	if (noecc)  { +		if (ioctl(fd, MTDFILEMODE, MTD_FILE_MODE_RAW) != 0) { +				perror("MTDFILEMODE"); +				goto closeall; +		} +	} else { +		/* check if we can read ecc stats */ +		if (!ioctl(fd, ECCGETSTATS, &stat1)) { +			eccstats = true; +			if (!quiet) { +				fprintf(stderr, "ECC failed: %d\n", stat1.failed); +				fprintf(stderr, "ECC corrected: %d\n", stat1.corrected); +				fprintf(stderr, "Number of bad blocks: %d\n", stat1.badblocks); +				fprintf(stderr, "Number of bbt blocks: %d\n", stat1.bbtblocks); +			} +		} else +			perror("No ECC status information available"); +	} + +	/* Open output file for writing. If file name is "-", write to standard +	 * output. */ +	if (!dumpfile) { +		ofd = STDOUT_FILENO; +	} else if ((ofd = open(dumpfile, O_WRONLY | O_TRUNC | O_CREAT, 0644))== -1) { +		perror(dumpfile); +		goto closeall; +	} + +	if (!pretty_print && !forcebinary && isatty(ofd)) { +		fprintf(stderr, "Not printing binary garbage to tty. Use '-a'\n" +				"or '--forcebinary' to override.\n"); +		goto closeall; +	} + +	/* Initialize start/end addresses and block size */ +	if (start_addr & (mtd.min_io_size - 1)) { +		fprintf(stderr, "the start address (-s parameter) is not page-aligned!\n" +				"The pagesize of this NAND Flash is 0x%x.\n", +				mtd.min_io_size); +		goto closeall; +	} +	if (length) +		end_addr = start_addr + length; +	if (!length || end_addr > mtd.size) +		end_addr = mtd.size; + +	bs = mtd.min_io_size; + +	/* Print informative message */ +	if (!quiet) { +		fprintf(stderr, "Block size %d, page size %d, OOB size %d\n", +				mtd.eb_size, mtd.min_io_size, mtd.oob_size); +		fprintf(stderr, +				"Dumping data starting at 0x%08llx and ending at 0x%08llx...\n", +				start_addr, end_addr); +	} + +	/* Dump the flash contents */ +	for (ofs = start_addr; ofs < end_addr; ofs += bs) { +		/* Check for bad block */ +		if (bb_method == dumpbad) { +			badblock = 0; +		} else if (blockstart != (ofs & (~mtd.eb_size + 1)) || +				firstblock) { +			blockstart = ofs & (~mtd.eb_size + 1); +			firstblock = 0; +			if ((badblock = mtd_is_bad(&mtd, fd, ofs / mtd.eb_size)) < 0) { +				errmsg("libmtd: mtd_is_bad"); +				goto closeall; +			} +		} + +		if (badblock) { +			/* skip bad block, increase end_addr */ +			if (bb_method == skipbad) { +				end_addr += mtd.eb_size; +				ofs += mtd.eb_size - bs; +				if (end_addr > mtd.size) +					end_addr = mtd.size; +				continue; +			} +			memset(readbuf, 0xff, bs); +		} else { +			/* Read page data and exit on failure */ +			if (mtd_read(&mtd, fd, ofs / mtd.eb_size, ofs % mtd.eb_size, readbuf, bs)) { +				errmsg("mtd_read"); +				goto closeall; +			} +		} + +		/* ECC stats available ? */ +		if (eccstats) { +			if (ioctl(fd, ECCGETSTATS, &stat2)) { +				perror("ioctl(ECCGETSTATS)"); +				goto closeall; +			} +			if (stat1.failed != stat2.failed) +				fprintf(stderr, "ECC: %d uncorrectable bitflip(s)" +						" at offset 0x%08llx\n", +						stat2.failed - stat1.failed, ofs); +			if (stat1.corrected != stat2.corrected) +				fprintf(stderr, "ECC: %d corrected bitflip(s) at" +						" offset 0x%08llx\n", +						stat2.corrected - stat1.corrected, ofs); +			stat1 = stat2; +		} + +		/* Write out page data */ +		if (pretty_print) { +			for (i = 0; i < bs; i += PRETTY_ROW_SIZE) { +				pretty_dump_to_buffer(readbuf + i, PRETTY_ROW_SIZE, +						pretty_buf, PRETTY_BUF_LEN, true, canonical, ofs + i); +				write(ofd, pretty_buf, strlen(pretty_buf)); +			} +		} else +			write(ofd, readbuf, bs); + +		if (omitoob) +			continue; + +		if (badblock) { +			memset(oobbuf, 0xff, mtd.oob_size); +		} else { +			/* Read OOB data and exit on failure */ +			if (mtd_read_oob(mtd_desc, &mtd, fd, ofs, mtd.oob_size, oobbuf)) { +				errmsg("libmtd: mtd_read_oob"); +				goto closeall; +			} +		} + +		/* Write out OOB data */ +		if (pretty_print) { +			for (i = 0; i < mtd.oob_size; i += PRETTY_ROW_SIZE) { +				pretty_dump_to_buffer(oobbuf + i, mtd.oob_size - i, +						pretty_buf, PRETTY_BUF_LEN, false, canonical, 0); +				write(ofd, pretty_buf, strlen(pretty_buf)); +			} +		} else +			write(ofd, oobbuf, mtd.oob_size); +	} + +	/* Close the output file and MTD device, free memory */ +	close(fd); +	close(ofd); +	free(oobbuf); +	free(readbuf); + +	/* Exit happy */ +	return EXIT_SUCCESS; + +closeall: +	close(fd); +	close(ofd); +	free(oobbuf); +	free(readbuf); +	exit(EXIT_FAILURE); +} diff --git a/nand-utils/nandtest.c b/nand-utils/nandtest.c new file mode 100644 index 0000000..2ef7cc8 --- /dev/null +++ b/nand-utils/nandtest.c @@ -0,0 +1,317 @@ +#define PROGRAM_NAME "nandtest" + +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#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" + +void usage(int status) +{ +	fprintf(status ? stderr : stdout, +		"usage: %s [OPTIONS] <device>\n\n" +		"  -h, --help           Display this help output\n" +		"  -m, --markbad        Mark blocks bad if they appear so\n" +		"  -s, --seed           Supply random seed\n" +		"  -p, --passes         Number of passes\n" +		"  -r <n>, --reads=<n>  Read & check <n> times per pass\n" +		"  -o, --offset         Start offset on flash\n" +		"  -l, --length         Length of flash to test\n" +		"  -k, --keep           Restore existing contents after test\n", +		PROGRAM_NAME); +	exit(status); +} + +struct mtd_info_user meminfo; +struct mtd_ecc_stats oldstats, newstats; +int fd; +int markbad=0; +int seed; + +int read_and_compare(loff_t ofs, unsigned char *data, unsigned char *rbuf) +{ +	ssize_t len; +	int i; + +	len = pread(fd, rbuf, meminfo.erasesize, ofs); +	if (len < meminfo.erasesize) { +		printf("\n"); +		if (len) +			fprintf(stderr, "Short read (%zd bytes)\n", len); +		else +			perror("read"); +		exit(1); +	} + +	if (ioctl(fd, ECCGETSTATS, &newstats)) { +		printf("\n"); +		perror("ECCGETSTATS"); +		close(fd); +		exit(1); +	} + +	if (newstats.corrected > oldstats.corrected) { +		printf("\n %d bit(s) ECC corrected at %08x\n", +				newstats.corrected - oldstats.corrected, +				(unsigned) ofs); +		oldstats.corrected = newstats.corrected; +	} +	if (newstats.failed > oldstats.failed) { +		printf("\nECC failed at %08x\n", (unsigned) ofs); +		oldstats.failed = newstats.failed; +	} + +	printf("\r%08x: checking...", (unsigned)ofs); +	fflush(stdout); + +	if (memcmp(data, rbuf, meminfo.erasesize)) { +		printf("\n"); +		fprintf(stderr, "compare failed. seed %d\n", seed); +		for (i=0; i<meminfo.erasesize; i++) { +			if (data[i] != rbuf[i]) +				printf("Byte 0x%x is %02x should be %02x\n", +				       i, rbuf[i], data[i]); +		} +		return 1; +	} +	return 0; +} + +int erase_and_write(loff_t ofs, unsigned char *data, unsigned char *rbuf, int nr_reads) +{ +	struct erase_info_user er; +	ssize_t len; +	int i, read_errs = 0; + +	printf("\r%08x: erasing... ", (unsigned)ofs); +	fflush(stdout); + +	er.start = ofs; +	er.length = meminfo.erasesize; + +	if (ioctl(fd, MEMERASE, &er)) { +		perror("MEMERASE"); +		if (markbad) { +			printf("Mark block bad at %08lx\n", (long)ofs); +			ioctl(fd, MEMSETBADBLOCK, &ofs); +		} +		return 1; +	} + +	printf("\r%08x: writing...", (unsigned)ofs); +	fflush(stdout); + +	len = pwrite(fd, data, meminfo.erasesize, ofs); +	if (len < 0) { +		printf("\n"); +		perror("write"); +		if (markbad) { +			printf("Mark block bad at %08lx\n", (long)ofs); +			ioctl(fd, MEMSETBADBLOCK, &ofs); +		} +		return 1; +	} +	if (len < meminfo.erasesize) { +		printf("\n"); +		fprintf(stderr, "Short write (%zd bytes)\n", len); +		exit(1); +	} + +	for (i=1; i<=nr_reads; i++) { +		printf("\r%08x: reading (%d of %d)...", (unsigned)ofs, i, nr_reads); +		fflush(stdout); +		if (read_and_compare(ofs, data, rbuf)) +			read_errs++; +	} +	if (read_errs) { +		fprintf(stderr, "read/check %d of %d failed. seed %d\n", read_errs, nr_reads, seed); +		return 1; +	} +	return 0; +} + + +/* + * Main program + */ +int main(int argc, char **argv) +{ +	int i; +	unsigned char *wbuf, *rbuf, *kbuf; +	int pass; +	int nr_passes = 1; +	int nr_reads = 4; +	int keep_contents = 0; +	uint32_t offset = 0; +	uint32_t length = -1; +	int error = 0; + +	seed = time(NULL); + +	for (;;) { +		static const char short_options[] = "hkl:mo:p:r:s:"; +		static const struct option long_options[] = { +			{ "help", no_argument, 0, 'h' }, +			{ "markbad", no_argument, 0, 'm' }, +			{ "seed", required_argument, 0, 's' }, +			{ "passes", required_argument, 0, 'p' }, +			{ "offset", required_argument, 0, 'o' }, +			{ "length", required_argument, 0, 'l' }, +			{ "reads", required_argument, 0, 'r' }, +			{ "keep", no_argument, 0, 'k' }, +			{0, 0, 0, 0}, +		}; +		int option_index = 0; +		int c = getopt_long(argc, argv, short_options, long_options, &option_index); +		if (c == EOF) +			break; + +		switch (c) { +		case 'h': +			usage(0); +			break; + +		case '?': +			usage(1); +			break; + +		case 'm': +			markbad = 1; +			break; + +		case 'k': +			keep_contents = 1; +			break; + +		case 's': +			seed = atol(optarg); +			break; + +		case 'p': +			nr_passes = atol(optarg); +			break; + +		case 'r': +			nr_reads = atol(optarg); +			break; + +		case 'o': +			offset = simple_strtoul(optarg, &error); +			break; + +		case 'l': +			length = simple_strtoul(optarg, &error); +			break; + +		} +	} +	if (argc - optind != 1) +		usage(1); +	if (error) +		errmsg_die("Try --help for more information"); + +	fd = open(argv[optind], O_RDWR); +	if (fd < 0) { +		perror("open"); +		exit(1); +	} + +	if (ioctl(fd, MEMGETINFO, &meminfo)) { +		perror("MEMGETINFO"); +		close(fd); +		exit(1); +	} + +	if (length == -1) +		length = meminfo.size; + +	if (offset % meminfo.erasesize) { +		fprintf(stderr, "Offset %x not multiple of erase size %x\n", +			offset, meminfo.erasesize); +		exit(1); +	} +	if (length % meminfo.erasesize) { +		fprintf(stderr, "Length %x not multiple of erase size %x\n", +			length, meminfo.erasesize); +		exit(1); +	} +	if (length + offset > meminfo.size) { +		fprintf(stderr, "Length %x + offset %x exceeds device size %x\n", +			length, offset, meminfo.size); +		exit(1); +	} + +	wbuf = malloc(meminfo.erasesize * 3); +	if (!wbuf) { +		fprintf(stderr, "Could not allocate %d bytes for buffer\n", +			meminfo.erasesize * 2); +		exit(1); +	} +	rbuf = wbuf + meminfo.erasesize; +	kbuf = rbuf + meminfo.erasesize; + +	if (ioctl(fd, ECCGETSTATS, &oldstats)) { +		perror("ECCGETSTATS"); +		close(fd); +		exit(1); +	} + +	printf("ECC corrections: %d\n", oldstats.corrected); +	printf("ECC failures   : %d\n", oldstats.failed); +	printf("Bad blocks     : %d\n", oldstats.badblocks); +	printf("BBT blocks     : %d\n", oldstats.bbtblocks); + +	srand(seed); + +	for (pass = 0; pass < nr_passes; pass++) { +		loff_t test_ofs; + +		for (test_ofs = offset; test_ofs < offset+length; test_ofs += meminfo.erasesize) { +			ssize_t len; + +			seed = rand(); +			srand(seed); + +			if (ioctl(fd, MEMGETBADBLOCK, &test_ofs)) { +				printf("\rBad block at 0x%08x\n", (unsigned)test_ofs); +				continue; +			} + +			for (i=0; i<meminfo.erasesize; i++) +				wbuf[i] = rand(); + +			if (keep_contents) { +				printf("\r%08x: reading... ", (unsigned)test_ofs); +				fflush(stdout); + +				len = pread(fd, kbuf, meminfo.erasesize, test_ofs); +				if (len < meminfo.erasesize) { +					printf("\n"); +					if (len) +						fprintf(stderr, "Short read (%zd bytes)\n", len); +					else +						perror("read"); +					exit(1); +				} +			} +			if (erase_and_write(test_ofs, wbuf, rbuf, nr_reads)) +				continue; +			if (keep_contents) +				erase_and_write(test_ofs, kbuf, rbuf, 1); +		} +		printf("\nFinished pass %d successfully\n", pass+1); +	} +	/* Return happy */ +	return 0; +} diff --git a/nand-utils/nandwrite.c b/nand-utils/nandwrite.c new file mode 100644 index 0000000..9c3fe8f --- /dev/null +++ b/nand-utils/nandwrite.c @@ -0,0 +1,578 @@ +/* + *  nandwrite.c + * + *  Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) + *		  2003 Thomas Gleixner (tglx@linutronix.de) + * + * 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 writes a binary image directly to a NAND flash + *   chip or NAND chips contained in DoC devices. This is the + *   "inverse operation" of nanddump. + * + * tglx: Major rewrite to handle bad blocks, write data with or without ECC + *	 write oob data only on request + * + * Bug/ToDo: + */ + +#define PROGRAM_NAME "nandwrite" + +#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> +#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> + +static void display_help(int status) +{ +	fprintf(status == EXIT_SUCCESS ? stdout : stderr, +"Usage: nandwrite [OPTION] MTD_DEVICE [INPUTFILE|-]\n" +"Writes to the specified MTD device.\n" +"\n" +"  -a, --autoplace         Use auto OOB layout\n" +"  -m, --markbad           Mark blocks bad if write fails\n" +"  -n, --noecc             Write without ecc\n" +"  -N, --noskipbad         Write without bad block skipping\n" +"  -o, --oob               Input contains oob data\n" +"  -O, --onlyoob           Input contains oob data and only write the oob part\n" +"  -s addr, --start=addr   Set output start address (default is 0)\n" +"  -p, --pad               Pad writes to page size\n" +"  -b, --blockalign=1|2|4  Set multiple of eraseblocks to align to\n" +"      --input-skip=length Skip |length| bytes of the input file\n" +"      --input-size=length Only read |length| bytes of the input file\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) 2003 Thomas Gleixner \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	*standard_input = "-"; +static const char	*mtd_device, *img; +static long long	mtdoffset = 0; +static long long	inputskip = 0; +static long long	inputsize = 0; +static bool		quiet = false; +static bool		writeoob = false; +static bool		onlyoob = false; +static bool		markbad = false; +static bool		noecc = false; +static bool		autoplace = false; +static bool		noskipbad = false; +static bool		pad = false; +static int		blockalign = 1; /* default to using actual block size */ + +static void process_options(int argc, char * const argv[]) +{ +	int error = 0; + +	for (;;) { +		int option_index = 0; +		static const char short_options[] = "hb:mnNoOpqs:a"; +		static const struct option long_options[] = { +			/* Order of these args with val==0 matters; see option_index. */ +			{"version", no_argument, 0, 0}, +			{"input-skip", required_argument, 0, 0}, +			{"input-size", required_argument, 0, 0}, +			{"help", no_argument, 0, 'h'}, +			{"blockalign", required_argument, 0, 'b'}, +			{"markbad", no_argument, 0, 'm'}, +			{"noecc", no_argument, 0, 'n'}, +			{"noskipbad", no_argument, 0, 'N'}, +			{"oob", no_argument, 0, 'o'}, +			{"onlyoob", no_argument, 0, 'O'}, +			{"pad", no_argument, 0, 'p'}, +			{"quiet", no_argument, 0, 'q'}, +			{"start", required_argument, 0, 's'}, +			{"autoplace", no_argument, 0, 'a'}, +			{0, 0, 0, 0}, +		}; + +		int c = getopt_long(argc, argv, short_options, +				long_options, &option_index); +		if (c == EOF) +			break; + +		switch (c) { +		case 0: +			switch (option_index) { +			case 0: /* --version */ +				display_version(); +				break; +			case 1: /* --input-skip */ +				inputskip = simple_strtoll(optarg, &error); +				break; +			case 2: /* --input-size */ +				inputsize = simple_strtoll(optarg, &error); +				break; +			} +			break; +		case 'q': +			quiet = true; +			break; +		case 'n': +			noecc = true; +			break; +		case 'N': +			noskipbad = true; +			break; +		case 'm': +			markbad = true; +			break; +		case 'o': +			writeoob = true; +			break; +		case 'O': +			writeoob = true; +			onlyoob = true; +			break; +		case 'p': +			pad = true; +			break; +		case 's': +			mtdoffset = simple_strtoll(optarg, &error); +			break; +		case 'b': +			blockalign = atoi(optarg); +			break; +		case 'a': +			autoplace = true; +			break; +		case 'h': +			display_help(EXIT_SUCCESS); +			break; +		case '?': +			error++; +			break; +		} +	} + +	if (mtdoffset < 0) +		errmsg_die("Can't specify negative device offset with option" +				" -s: %lld", mtdoffset); + +	if (blockalign < 0) +		errmsg_die("Can't specify negative blockalign with option -b:" +				" %d", blockalign); + +	if (autoplace && noecc) +		errmsg_die("Autoplacement and no-ECC are mutually exclusive"); + +	if (!onlyoob && (pad && writeoob)) +		errmsg_die("Can't pad when oob data is present"); + +	argc -= optind; +	argv += optind; + +	/* +	 * There must be at least the MTD device node positional +	 * argument remaining and, optionally, the input file. +	 */ + +	if (argc < 1 || argc > 2 || error) +		display_help(EXIT_FAILURE); + +	mtd_device = argv[0]; + +	/* +	 * Standard input may be specified either explictly as "-" or +	 * implicity by simply omitting the second of the two +	 * positional arguments. +	 */ + +	img = ((argc == 2) ? argv[1] : standard_input); +} + +static void erase_buffer(void *buffer, size_t size) +{ +	const uint8_t kEraseByte = 0xff; + +	if (buffer != NULL && size > 0) +		memset(buffer, kEraseByte, size); +} + +/* + * Main program + */ +int main(int argc, char * const argv[]) +{ +	int fd = -1; +	int ifd = -1; +	int pagelen; +	long long imglen = 0; +	bool baderaseblock = false; +	long long blockstart = -1; +	struct mtd_dev_info mtd; +	long long offs; +	int ret; +	bool failed = true; +	/* contains all the data read from the file so far for the current eraseblock */ +	unsigned char *filebuf = NULL; +	size_t filebuf_max = 0; +	size_t filebuf_len = 0; +	/* points to the current page inside filebuf */ +	unsigned char *writebuf = NULL; +	/* points to the OOB for the current page in filebuf */ +	unsigned char *oobbuf = NULL; +	libmtd_t mtd_desc; +	int ebsize_aligned; +	uint8_t write_mode; + +	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"); + +	/* Fill in MTD device capability structure */ +	if (mtd_get_dev_info(mtd_desc, mtd_device, &mtd) < 0) +		errmsg_die("mtd_get_dev_info failed"); + +	/* +	 * Pretend erasesize is specified number of blocks - to match jffs2 +	 *   (virtual) block size +	 * Use this value throughout unless otherwise necessary +	 */ +	ebsize_aligned = mtd.eb_size * blockalign; + +	if (mtdoffset & (mtd.min_io_size - 1)) +		errmsg_die("The start address is not page-aligned !\n" +			   "The pagesize of this NAND Flash is 0x%x.\n", +			   mtd.min_io_size); + +	/* Select OOB write mode */ +	if (noecc) +		write_mode = MTD_OPS_RAW; +	else if (autoplace) +		write_mode = MTD_OPS_AUTO_OOB; +	else +		write_mode = MTD_OPS_PLACE_OOB; + +	if (noecc)  { +		ret = ioctl(fd, MTDFILEMODE, MTD_FILE_MODE_RAW); +		if (ret) { +			switch (errno) { +			case ENOTTY: +				errmsg_die("ioctl MTDFILEMODE is missing"); +			default: +				sys_errmsg_die("MTDFILEMODE"); +			} +		} +	} + +	/* Determine if we are reading from standard input or from a file. */ +	if (strcmp(img, standard_input) == 0) +		ifd = STDIN_FILENO; +	else +		ifd = open(img, O_RDONLY); + +	if (ifd == -1) { +		perror(img); +		goto closeall; +	} + +	pagelen = mtd.min_io_size + ((writeoob) ? mtd.oob_size : 0); + +	if (ifd == STDIN_FILENO) { +		imglen = inputsize ? : pagelen; +		if (inputskip) { +			errmsg("seeking stdin not supported"); +			goto closeall; +		} +	} else { +		if (!inputsize) { +			struct stat st; +			if (fstat(ifd, &st)) { +				sys_errmsg("unable to stat input image"); +				goto closeall; +			} +			imglen = st.st_size - inputskip; +		} else +			imglen = inputsize; + +		if (inputskip && lseek(ifd, inputskip, SEEK_CUR) == -1) { +			sys_errmsg("lseek input by %lld failed", inputskip); +			goto closeall; +		} +	} + +	/* Check, if file is page-aligned */ +	if (!pad && (imglen % pagelen) != 0) { +		fprintf(stderr, "Input file is not page-aligned. Use the padding " +				 "option.\n"); +		goto closeall; +	} + +	/* Check, if length fits into device */ +	if ((imglen / pagelen) * mtd.min_io_size > mtd.size - mtdoffset) { +		fprintf(stderr, "Image %lld bytes, NAND page %d bytes, OOB area %d" +				" bytes, device size %lld bytes\n", +				imglen, pagelen, mtd.oob_size, mtd.size); +		sys_errmsg("Input file does not fit into device"); +		goto closeall; +	} + +	/* +	 * Allocate a buffer big enough to contain all the data (OOB included) +	 * for one eraseblock. The order of operations here matters; if ebsize +	 * and pagelen are large enough, then "ebsize_aligned * pagelen" could +	 * overflow a 32-bit data type. +	 */ +	filebuf_max = ebsize_aligned / mtd.min_io_size * pagelen; +	filebuf = xmalloc(filebuf_max); +	erase_buffer(filebuf, filebuf_max); + +	/* +	 * Get data from input and write to the device while there is +	 * still input to read and we are still within the device +	 * bounds. Note that in the case of standard input, the input +	 * length is simply a quasi-boolean flag whose values are page +	 * length or zero. +	 */ +	while ((imglen > 0 || writebuf < filebuf + filebuf_len) +		&& mtdoffset < mtd.size) { +		/* +		 * New eraseblock, check for bad block(s) +		 * Stay in the loop to be sure that, if mtdoffset changes because +		 * of a bad block, the next block that will be written to +		 * is also checked. Thus, we avoid errors if the block(s) after the +		 * skipped block(s) is also bad (number of blocks depending on +		 * the blockalign). +		 */ +		while (blockstart != (mtdoffset & (~ebsize_aligned + 1))) { +			blockstart = mtdoffset & (~ebsize_aligned + 1); +			offs = blockstart; + +			/* +			 * if writebuf == filebuf, we are rewinding so we must +			 * not reset the buffer but just replay it +			 */ +			if (writebuf != filebuf) { +				erase_buffer(filebuf, filebuf_len); +				filebuf_len = 0; +				writebuf = filebuf; +			} + +			baderaseblock = false; +			if (!quiet) +				fprintf(stdout, "Writing data to block %lld at offset 0x%llx\n", +						 blockstart / ebsize_aligned, blockstart); + +			/* Check all the blocks in an erase block for bad blocks */ +			if (noskipbad) +				continue; + +			do { +				ret = mtd_is_bad(&mtd, fd, offs / ebsize_aligned); +				if (ret < 0) { +					sys_errmsg("%s: MTD get bad block failed", mtd_device); +					goto closeall; +				} else if (ret == 1) { +					baderaseblock = true; +					if (!quiet) +						fprintf(stderr, "Bad block at %llx, %u block(s) " +								"from %llx will be skipped\n", +								offs, blockalign, blockstart); +				} + +				if (baderaseblock) { +					mtdoffset = blockstart + ebsize_aligned; + +					if (mtdoffset > mtd.size) { +						errmsg("too many bad blocks, cannot complete request"); +						goto closeall; +					} +				} + +				offs +=  ebsize_aligned / blockalign; +			} while (offs < blockstart + ebsize_aligned); + +		} + +		/* Read more data from the input if there isn't enough in the buffer */ +		if (writebuf + mtd.min_io_size > filebuf + filebuf_len) { +			size_t readlen = mtd.min_io_size; +			size_t alreadyread = (filebuf + filebuf_len) - writebuf; +			size_t tinycnt = alreadyread; +			ssize_t cnt = 0; + +			while (tinycnt < readlen) { +				cnt = read(ifd, writebuf + tinycnt, readlen - tinycnt); +				if (cnt == 0) { /* EOF */ +					break; +				} else if (cnt < 0) { +					perror("File I/O error on input"); +					goto closeall; +				} +				tinycnt += cnt; +			} + +			/* No padding needed - we are done */ +			if (tinycnt == 0) { +				/* +				 * For standard input, set imglen to 0 to signal +				 * the end of the "file". For nonstandard input, +				 * leave it as-is to detect an early EOF. +				 */ +				if (ifd == STDIN_FILENO) +					imglen = 0; + +				break; +			} + +			/* Padding */ +			if (tinycnt < readlen) { +				if (!pad) { +					fprintf(stderr, "Unexpected EOF. Expecting at least " +							"%zu more bytes. Use the padding option.\n", +							readlen - tinycnt); +					goto closeall; +				} +				erase_buffer(writebuf + tinycnt, readlen - tinycnt); +			} + +			filebuf_len += readlen - alreadyread; +			if (ifd != STDIN_FILENO) { +				imglen -= tinycnt - alreadyread; +			} else if (cnt == 0) { +				/* No more bytes - we are done after writing the remaining bytes */ +				imglen = 0; +			} +		} + +		if (writeoob) { +			oobbuf = writebuf + mtd.min_io_size; + +			/* Read more data for the OOB from the input if there isn't enough in the buffer */ +			if (oobbuf + mtd.oob_size > filebuf + filebuf_len) { +				size_t readlen = mtd.oob_size; +				size_t alreadyread = (filebuf + filebuf_len) - oobbuf; +				size_t tinycnt = alreadyread; +				ssize_t cnt; + +				while (tinycnt < readlen) { +					cnt = read(ifd, oobbuf + tinycnt, readlen - tinycnt); +					if (cnt == 0) { /* EOF */ +						break; +					} else if (cnt < 0) { +						perror("File I/O error on input"); +						goto closeall; +					} +					tinycnt += cnt; +				} + +				if (tinycnt < readlen) { +					fprintf(stderr, "Unexpected EOF. Expecting at least " +							"%zu more bytes for OOB\n", readlen - tinycnt); +					goto closeall; +				} + +				filebuf_len += readlen - alreadyread; +				if (ifd != STDIN_FILENO) { +					imglen -= tinycnt - alreadyread; +				} else if (cnt == 0) { +					/* No more bytes - we are done after writing the remaining bytes */ +					imglen = 0; +				} +			} +		} + +		/* Write out data */ +		ret = mtd_write(mtd_desc, &mtd, fd, mtdoffset / mtd.eb_size, +				mtdoffset % mtd.eb_size, +				onlyoob ? NULL : writebuf, +				onlyoob ? 0 : mtd.min_io_size, +				writeoob ? oobbuf : NULL, +				writeoob ? mtd.oob_size : 0, +				write_mode); +		if (ret) { +			long long i; +			if (errno != EIO) { +				sys_errmsg("%s: MTD write failure", mtd_device); +				goto closeall; +			} + +			/* Must rewind to blockstart if we can */ +			writebuf = filebuf; + +			fprintf(stderr, "Erasing failed write from %#08llx to %#08llx\n", +				blockstart, blockstart + ebsize_aligned - 1); +			for (i = blockstart; i < blockstart + ebsize_aligned; i += mtd.eb_size) { +				if (mtd_erase(mtd_desc, &mtd, fd, i / mtd.eb_size)) { +					int errno_tmp = errno; +					sys_errmsg("%s: MTD Erase failure", mtd_device); +					if (errno_tmp != EIO) +						goto closeall; +				} +			} + +			if (markbad) { +				fprintf(stderr, "Marking block at %08llx bad\n", +						mtdoffset & (~mtd.eb_size + 1)); +				if (mtd_mark_bad(&mtd, fd, mtdoffset / mtd.eb_size)) { +					sys_errmsg("%s: MTD Mark bad block failure", mtd_device); +					goto closeall; +				} +			} +			mtdoffset = blockstart + ebsize_aligned; + +			continue; +		} +		mtdoffset += mtd.min_io_size; +		writebuf += pagelen; +	} + +	failed = false; + +closeall: +	close(ifd); +	libmtd_close(mtd_desc); +	free(filebuf); +	close(fd); + +	if (failed || (ifd != STDIN_FILENO && imglen > 0) +		   || (writebuf < filebuf + filebuf_len)) +		sys_errmsg_die("Data was only partially written due to error"); + +	/* Return happy */ +	return EXIT_SUCCESS; +} diff --git a/nand-utils/nftl_format.c b/nand-utils/nftl_format.c new file mode 100644 index 0000000..1fc3b36 --- /dev/null +++ b/nand-utils/nftl_format.c @@ -0,0 +1,422 @@ +/* + * nftl_format.c: Creating a NFTL/INFTL partition on an MTD device + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA + * + * ToDo: + *	1. UnitSizeFactor != 0xFF cases + *	2. test, test, and test !!! + */ + +#define PROGRAM_NAME "nftl_format" + +#define _XOPEN_SOURCE 500 /* for pread/pwrite */ +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <fcntl.h> +#include <time.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <sys/mount.h> +#include <errno.h> +#include <string.h> + +#include <asm/types.h> +#include <mtd/mtd-user.h> +#include <mtd/nftl-user.h> +#include <mtd/inftl-user.h> +#include <mtd_swab.h> + +unsigned char BadUnitTable[MAX_ERASE_ZONES]; +unsigned char *readbuf; +unsigned char *writebuf[4]; + +mtd_info_t meminfo; +erase_info_t erase; +int fd; +struct NFTLMediaHeader *NFTLhdr; +struct INFTLMediaHeader *INFTLhdr; + +static int do_oobcheck = 1; +static int do_rwecheck = 1; + +static unsigned char check_block_1(unsigned long block) +{ +	unsigned char oobbuf[16]; +	struct mtd_oob_buf oob = { 0, 16, oobbuf }; + +	oob.start = block * meminfo.erasesize; +	if (ioctl(fd, MEMREADOOB, &oob)) +		return ZONE_BAD_ORIGINAL; + +	if(oobbuf[5] == 0) +		return ZONE_BAD_ORIGINAL; + +	oob.start = block * meminfo.erasesize + 512 /* FIXME */; +	if (ioctl(fd, MEMREADOOB, &oob)) +		return ZONE_BAD_ORIGINAL; + +	if(oobbuf[5] == 0) +		return ZONE_BAD_ORIGINAL; + +	return ZONE_GOOD; +} + +static unsigned char check_block_2(unsigned long block) +{ +	unsigned long ofs = block * meminfo.erasesize; +	unsigned long blockofs; + +	/* Erase test */ +	erase.start = ofs; + +	for (blockofs = 0; blockofs < meminfo.erasesize; blockofs += 512) { +		pread(fd, readbuf, 512, ofs + blockofs); +		if (memcmp(readbuf, writebuf[0], 512)) { +			/* Block wasn't 0xff after erase */ +			printf(": Block not 0xff after erase\n"); +			return ZONE_BAD_ORIGINAL; +		} + +		pwrite(fd, writebuf[1], 512, blockofs + ofs); +		pread(fd, readbuf, 512, blockofs + ofs); +		if (memcmp(readbuf, writebuf[1], 512)) { +			printf(": Block not zero after clearing\n"); +			return ZONE_BAD_ORIGINAL; +		} +	} + +	/* Write test */ +	if (ioctl(fd, MEMERASE, &erase) != 0) { +		printf(": Second erase failed (%s)\n", strerror(errno)); +		return ZONE_BAD_ORIGINAL; +	} +	for (blockofs = 0; blockofs < meminfo.erasesize; blockofs += 512) { +		pwrite(fd, writebuf[2], 512, blockofs + ofs); +		pread(fd, readbuf, 512, blockofs + ofs); +		if (memcmp(readbuf, writebuf[2], 512)) { +			printf(": Block not 0x5a after writing\n"); +			return ZONE_BAD_ORIGINAL; +		} +	} + +	if (ioctl(fd, MEMERASE, &erase) != 0) { +		printf(": Third erase failed (%s)\n", strerror(errno)); +		return ZONE_BAD_ORIGINAL; +	} +	for (blockofs = 0; blockofs < meminfo.erasesize; blockofs += 512) { +		pwrite(fd, writebuf[3], 512, blockofs + ofs); +		pread(fd, readbuf, 512, blockofs + ofs); +		if (memcmp(readbuf, writebuf[3], 512)) { +			printf(": Block not 0xa5 after writing\n"); +			return ZONE_BAD_ORIGINAL; +		} +	} +	if (ioctl(fd, MEMERASE, &erase) != 0) { +		printf(": Fourth erase failed (%s)\n", strerror(errno)); +		return ZONE_BAD_ORIGINAL; +	} +	return ZONE_GOOD; +} + +static unsigned char erase_block(unsigned long block) +{ +	unsigned char status; +	int ret; + +	status = (do_oobcheck) ? check_block_1(block) : ZONE_GOOD; +	erase.start = block * meminfo.erasesize; + +	if (status != ZONE_GOOD) { +		printf("\rSkipping bad zone (factory marked) #%ld @ 0x%x\n", block, erase.start); +		fflush(stdout); +		return status; +	} + +	printf("\r\t Erasing Zone #%ld @ 0x%x", block, erase.start); +	fflush(stdout); + +	if ((ret=ioctl(fd, MEMERASE, &erase)) != 0) { +		printf(": Erase failed (%s)\n", strerror(errno)); +		return ZONE_BAD_ORIGINAL; +	} + +	if (do_rwecheck) { +		printf("\r\tChecking Zone #%ld @ 0x%x", block, erase.start); +		fflush(stdout); +		status = check_block_2(block); +		if (status != ZONE_GOOD) { +			printf("\rSkipping bad zone (RWE test failed) #%ld @ 0x%x\n", block, erase.start); +			fflush(stdout); +		} +	} +	return status; +} + +static int checkbbt(void) +{ +	unsigned char bbt[512]; +	unsigned char bits; +	int i, addr; + +	if (pread(fd, bbt, 512, 0x800) < 0) { +		printf("%s: failed to read BBT, errno=%d\n", PROGRAM_NAME, errno); +		return (-1); +	} + + +	for (i = 0; (i < 512); i++) { +		addr = i / 4; +		bits = 0x3 << ((i % 4) * 2); +		if ((bbt[addr] & bits) == 0) { +			BadUnitTable[i] = ZONE_BAD_ORIGINAL; +		} +	} + +	return (0); +} + +void usage(int rc) +{ +	fprintf(stderr, "Usage: %s [-ib] <mtddevice> [<start offset> [<size>]]\n", PROGRAM_NAME); +	exit(rc); +} + +int main(int argc, char **argv) +{ +	unsigned long startofs = 0, part_size = 0; +	unsigned long ezones = 0, ezone = 0, bad_zones = 0; +	unsigned char unit_factor = 0xFF; +	long MediaUnit1 = -1, MediaUnit2 = -1; +	long MediaUnitOff1 = 0, MediaUnitOff2 = 0; +	unsigned char oobbuf[16]; +	struct mtd_oob_buf oob = {0, 16, oobbuf}; +	char *mtddevice; +	const char *nftl; +	int c, do_inftl = 0, do_bbt = 0; + + +	printf("version 1.24 2005/11/07 11:15:13 gleixner\n"); + +	if (argc < 2) +		usage(1); + +	nftl = "NFTL"; + +	while ((c = getopt(argc, argv, "?hib")) > 0) { +		switch (c) { +			case 'i': +				nftl = "INFTL"; +				do_inftl = 1; +				break; +			case 'b': +				do_bbt = 1; +				break; +			case 'h': +			case '?': +				usage(0); +				break; +			default: +				usage(1); +				break; +		} +	} + +	mtddevice = argv[optind++]; +	if (argc > optind) { +		startofs = strtoul(argv[optind++], NULL, 0); +	} +	if (argc > optind) { +		part_size = strtoul(argv[optind++], NULL, 0); +	} + +	// Open and size the device +	if ((fd = open(mtddevice, O_RDWR)) < 0) { +		perror("Open flash device"); +		return 1; +	} + +	if (ioctl(fd, MEMGETINFO, &meminfo) != 0) { +		perror("ioctl(MEMGETINFO)"); +		close(fd); +		return 1; +	} + +	switch (meminfo.erasesize) { +		case 0x1000: +		case 0x2000: +		case 0x4000: +		case 0x8000: +			break; +		default: +			printf("Unrecognized Erase size, 0x%x - I'm confused\n", +					meminfo.erasesize); +			close(fd); +			return 1; +	} +	writebuf[0] = malloc(meminfo.erasesize * 5); +	if (!writebuf[0]) { +		printf("Malloc failed\n"); +		close(fd); +		return 1; +	} +	writebuf[1] = writebuf[0] + meminfo.erasesize; +	writebuf[2] = writebuf[1] + meminfo.erasesize; +	writebuf[3] = writebuf[2] + meminfo.erasesize; +	readbuf = writebuf[3] + meminfo.erasesize; +	memset(writebuf[0], 0xff, meminfo.erasesize); +	memset(writebuf[1], 0x00, meminfo.erasesize); +	memset(writebuf[2], 0x5a, meminfo.erasesize); +	memset(writebuf[3], 0xa5, meminfo.erasesize); +	memset(BadUnitTable, ZONE_GOOD, MAX_ERASE_ZONES); + +	if (part_size == 0 || (part_size > meminfo.size - startofs)) +		/* the user doest not or incorrectly specify NFTL partition size */ +		part_size = meminfo.size - startofs; + +	erase.length = meminfo.erasesize; +	ezones = part_size / meminfo.erasesize; + +	if (ezones > MAX_ERASE_ZONES) { +		/* Ought to change the UnitSizeFactor. But later. */ +		part_size = meminfo.erasesize * MAX_ERASE_ZONES; +		ezones = MAX_ERASE_ZONES; +		unit_factor = 0xFF; +	} + +	/* If using device BBT then parse that now */ +	if (do_bbt) { +		checkbbt(); +		do_oobcheck = 0; +		do_rwecheck = 0; +	} + +	/* Phase 1. Erasing and checking each erase zones in the NFTL partition. +	   N.B. Erase Zones not used by the NFTL partition are untouched and marked ZONE_GOOD */ +	printf("Phase 1. Checking and erasing Erase Zones from 0x%08lx to 0x%08lx\n", +			startofs, startofs + part_size); +	for (ezone = startofs / meminfo.erasesize; +			ezone < (ezones + startofs / meminfo.erasesize); ezone++) { +		if (BadUnitTable[ezone] != ZONE_GOOD) +			continue; +		if ((BadUnitTable[ezone] = erase_block(ezone)) == ZONE_GOOD) { +			if (MediaUnit1 == -1) { +				MediaUnit1 = ezone; +			} else if (MediaUnit2 == -1) { +				MediaUnit2 = ezone; +			} +		} else { +			bad_zones++; +		} +	} +	printf("\n"); + +	/* N.B. from dump of M-System original chips, NumEraseUnits counts the 2 Erase Unit used +	   by MediaHeader and the FirstPhysicalEUN starts from the MediaHeader */ +	if (do_inftl) { +		unsigned long maxzones, pezstart, pezend, numvunits; + +		INFTLhdr = (struct INFTLMediaHeader *) (writebuf[0]); +		strcpy(INFTLhdr->bootRecordID, "BNAND"); +		INFTLhdr->NoOfBootImageBlocks = cpu_to_le32(0); +		INFTLhdr->NoOfBinaryPartitions = cpu_to_le32(0); +		INFTLhdr->NoOfBDTLPartitions = cpu_to_le32(1); +		INFTLhdr->BlockMultiplierBits = cpu_to_le32(0); +		INFTLhdr->FormatFlags = cpu_to_le32(0); +		INFTLhdr->OsakVersion = cpu_to_le32(OSAK_VERSION); +		INFTLhdr->PercentUsed = cpu_to_le32(PERCENTUSED); +		/* +		 * Calculate number of virtual units we will have to work +		 * with. I am calculating out the known bad units here, not +		 * sure if that is what M-Systems do... +		 */ +		MediaUnit2 = MediaUnit1; +		MediaUnitOff2 = 4096; +		maxzones = meminfo.size / meminfo.erasesize; +		pezstart = startofs / meminfo.erasesize + 1; +		pezend = startofs / meminfo.erasesize + ezones - 1; +		numvunits = (ezones - 2) * PERCENTUSED / 100; +		for (ezone = pezstart; ezone < maxzones; ezone++) { +			if (BadUnitTable[ezone] != ZONE_GOOD) { +				if (numvunits > 1) +					numvunits--; +			} +		} + +		INFTLhdr->Partitions[0].virtualUnits = cpu_to_le32(numvunits); +		INFTLhdr->Partitions[0].firstUnit = cpu_to_le32(pezstart); +		INFTLhdr->Partitions[0].lastUnit = cpu_to_le32(pezend); +		INFTLhdr->Partitions[0].flags = cpu_to_le32(INFTL_BDTL); +		INFTLhdr->Partitions[0].spareUnits = cpu_to_le32(0); +		INFTLhdr->Partitions[0].Reserved0 = INFTLhdr->Partitions[0].firstUnit; +		INFTLhdr->Partitions[0].Reserved1 = cpu_to_le32(0); + +	} else { + +		NFTLhdr = (struct NFTLMediaHeader *) (writebuf[0]); +		strcpy(NFTLhdr->DataOrgID, "ANAND"); +		NFTLhdr->NumEraseUnits = cpu_to_le16(part_size / meminfo.erasesize); +		NFTLhdr->FirstPhysicalEUN = cpu_to_le16(MediaUnit1); +		/* N.B. we reserve 2 more Erase Units for "folding" of Virtual Unit Chain */ +		NFTLhdr->FormattedSize = cpu_to_le32(part_size - ( (5+bad_zones) * meminfo.erasesize)); +		NFTLhdr->UnitSizeFactor = unit_factor; +	} + +	/* Phase 2. Writing NFTL Media Headers and Bad Unit Table */ +	printf("Phase 2.a Writing %s Media Header and Bad Unit Table\n", nftl); +	pwrite(fd, writebuf[0], 512, MediaUnit1 * meminfo.erasesize + MediaUnitOff1); +	for (ezone = 0; ezone < (meminfo.size / meminfo.erasesize); ezone += 512) { +		pwrite(fd, BadUnitTable + ezone, 512, +				(MediaUnit1 * meminfo.erasesize) + 512 * (1 + ezone / 512)); +	} + +#if 0 +	printf("  MediaHeader contents:\n"); +	printf("    NumEraseUnits: %d\n", le16_to_cpu(NFTLhdr->NumEraseUnits)); +	printf("    FirstPhysicalEUN: %d\n", le16_to_cpu(NFTLhdr->FirstPhysicalEUN)); +	printf("    FormattedSize: %d (%d sectors)\n", le32_to_cpu(NFTLhdr->FormattedSize), +			le32_to_cpu(NFTLhdr->FormattedSize)/512); +#endif +	printf("Phase 2.b Writing Spare %s Media Header and Spare Bad Unit Table\n", nftl); +	pwrite(fd, writebuf[0], 512, MediaUnit2 * meminfo.erasesize + MediaUnitOff2); +	for (ezone = 0; ezone < (meminfo.size / meminfo.erasesize); ezone += 512) { +		pwrite(fd, BadUnitTable + ezone, 512, +				(MediaUnit2 * meminfo.erasesize + MediaUnitOff2) + 512 * (1 + ezone / 512)); +	} + +	/* UCI #1 for newly erased Erase Unit */ +	memset(oobbuf, 0xff, 16); +	oobbuf[11] = oobbuf[10] = oobbuf[9] = 0; +	oobbuf[8]  = (do_inftl) ? 0x00 : 0x03; +	oobbuf[12] = oobbuf[14] = 0x69; +	oobbuf[13] = oobbuf[15] = 0x3c; + +	/* N.B. The Media Header and Bad Erase Unit Table are considered as Free Erase Unit +	   by M-System i.e. their Virtual Unit Number == 0xFFFF in the Unit Control Information #0, +	   but their Block Status is BLOCK_USED (0x5555) in their Block Control Information */ +	/* Phase 3. Writing Unit Control Information for each Erase Unit */ +	printf("Phase 3. Writing Unit Control Information to each Erase Unit\n"); +	for (ezone = MediaUnit1; ezone < (ezones + startofs / meminfo.erasesize); ezone++) { +		/* write UCI #1 to each Erase Unit */ +		if (BadUnitTable[ezone] != ZONE_GOOD) +			continue; +		oob.start = (ezone * meminfo.erasesize) + 512 + (do_inftl * 512); +		if (ioctl(fd, MEMWRITEOOB, &oob)) +			printf("MEMWRITEOOB at %lx: %s\n", (unsigned long)oob.start, strerror(errno)); +	} + +	exit(0); +} diff --git a/nand-utils/nftldump.c b/nand-utils/nftldump.c new file mode 100644 index 0000000..32f4f2f --- /dev/null +++ b/nand-utils/nftldump.c @@ -0,0 +1,278 @@ +/* + * nftldump.c: Dumping the content of NFTL partitions on a "Physical Disk" + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA + * + * ToDo: + *	1. UnitSizeFactor != 0xFF cases + *	2. test, test, and test !!! + */ + +#define PROGRAM_NAME "nftldump" + +#define _XOPEN_SOURCE 500 /* For pread */ + +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <errno.h> + +#include <sys/ioctl.h> +#include <asm/types.h> +#include <mtd/mtd-user.h> +#include <mtd/nftl-user.h> +#include <mtd_swab.h> + +static struct NFTLMediaHeader MedHead[2]; +static mtd_info_t meminfo; + +static struct nftl_oob oobbuf; +static struct mtd_oob_buf oob = {0, 16, (unsigned char *)&oobbuf}; + +static int fd, ofd = -1;; +static int NumMedHeads; + +static unsigned char BadUnitTable[MAX_ERASE_ZONES]; + +#define SWAP16(x) do { x = le16_to_cpu(x); } while(0) +#define SWAP32(x) do { x = le32_to_cpu(x); } while(0) + +/* VUCtable, store the Erase Unit Number of the first Erase Unit in the chain */ +static unsigned short *VUCtable; + +/* FixMe: make this dynamic allocated */ +#define ERASESIZE 0x2000 +#define NUMVUNITS ((40*1024*1024) / ERASESIZE) +static union nftl_uci UCItable[NUMVUNITS][3]; + +static unsigned short nextEUN(unsigned short curEUN) +{ +	return UCItable[curEUN][0].a.ReplUnitNum; +} + +static unsigned int find_media_headers(void) +{ +	int i; +	static unsigned long ofs = 0; + +	NumMedHeads = 0; +	while (ofs < meminfo.size) { +		pread(fd, &MedHead[NumMedHeads], sizeof(struct NFTLMediaHeader), ofs); +		if (!strncmp(MedHead[NumMedHeads].DataOrgID, "ANAND", 6)) { +			SWAP16(MedHead[NumMedHeads].NumEraseUnits); +			SWAP16(MedHead[NumMedHeads].FirstPhysicalEUN); +			SWAP32(MedHead[NumMedHeads].FormattedSize); + +			if (NumMedHeads == 0) { +				printf("NFTL Media Header found at offset 0x%08lx:\n", ofs); +				printf("NumEraseUnits:    %d\n", +						MedHead[NumMedHeads].NumEraseUnits); +				printf("FirstPhysicalEUN: %d\n", +						MedHead[NumMedHeads].FirstPhysicalEUN); +				printf("Formatted Size:   %d\n", +						MedHead[NumMedHeads].FormattedSize); +				printf("UnitSizeFactor:   0x%x\n", +						MedHead[NumMedHeads].UnitSizeFactor); + +				/* read BadUnitTable, I don't know why pread() does not work for +				   larger (7680 bytes) chunks */ +				for (i = 0; i < MAX_ERASE_ZONES; i += 512) +					pread(fd, &BadUnitTable[i], 512, ofs + 512 + i); +			} else +				printf("Second NFTL Media Header found at offset 0x%08lx\n",ofs); +			NumMedHeads++; +		} + +		ofs += meminfo.erasesize; +		if (NumMedHeads == 2) { +			if (strncmp((char *)&MedHead[0], (char *)&MedHead[1], sizeof(struct NFTLMediaHeader)) != 0) { +				printf("warning: NFTL Media Header is not consistent with " +						"Spare NFTL Media Header\n"); +			} +			break; +		} +	} + +	/* allocate Virtual Unit Chain table for this NFTL partition */ +	VUCtable = calloc(MedHead[0].NumEraseUnits, sizeof(unsigned short)); +	return NumMedHeads; +} + +static void dump_erase_units(void) +{ +	int i, j; +	unsigned long ofs; + +	for (i = MedHead[0].FirstPhysicalEUN; i < MedHead[0].FirstPhysicalEUN + +			MedHead[0].NumEraseUnits; i++) { +		/* For each Erase Unit */ +		ofs = i * meminfo.erasesize; + +		/* read the Unit Control Information */ +		for (j = 0; j < 3; j++) { +			oob.start = ofs + (j * 512); +			if (ioctl(fd, MEMREADOOB, &oob)) +				printf("MEMREADOOB at %lx: %s\n", +						(unsigned long) oob.start, strerror(errno)); +			memcpy(&UCItable[i][j], &oobbuf.u, 8); +		} +		if (UCItable[i][1].b.EraseMark != cpu_to_le16(0x3c69)) { +			printf("EraseMark not present in unit %d: %x\n", +					i, UCItable[i][1].b.EraseMark); +		} else { +			/* a properly formatted unit */ +			SWAP16(UCItable[i][0].a.VirtUnitNum); +			SWAP16(UCItable[i][0].a.ReplUnitNum); +			SWAP16(UCItable[i][0].a.SpareVirtUnitNum); +			SWAP16(UCItable[i][0].a.SpareReplUnitNum); +			SWAP32(UCItable[i][1].b.WearInfo); +			SWAP16(UCItable[i][1].b.EraseMark); +			SWAP16(UCItable[i][1].b.EraseMark1); +			SWAP16(UCItable[i][2].c.FoldMark); +			SWAP16(UCItable[i][2].c.FoldMark1); + +			if (!(UCItable[i][0].a.VirtUnitNum & 0x8000)) { +				/* If this is the first in a chain, store the EUN in the VUC table */ +				if (VUCtable[UCItable[i][0].a.VirtUnitNum & 0x7fff]) { +					printf("Duplicate start of chain for VUC %d: " +							"Unit %d replaces Unit %d\n", +							UCItable[i][0].a.VirtUnitNum & 0x7fff, +							i, VUCtable[UCItable[i][0].a.VirtUnitNum & 0x7fff]); +				} +				VUCtable[UCItable[i][0].a.VirtUnitNum & 0x7fff] = i; +			} +		} + +		switch (BadUnitTable[i]) { +			case ZONE_BAD_ORIGINAL: +				printf("Unit %d is marked as ZONE_BAD_ORIGINAL\n", i); +				continue; +			case ZONE_BAD_MARKED: +				printf("Unit %d is marked as ZONE_BAD_MARKED\n", i); +				continue; +		} + +		/* ZONE_GOOD */ +		if (UCItable[i][0].a.VirtUnitNum == 0xffff) +			printf("Unit %d is free\n", i); +		else +			printf("Unit %d is in chain %d and %s a replacement\n", i, +					UCItable[i][0].a.VirtUnitNum & 0x7fff, +					UCItable[i][0].a.VirtUnitNum & 0x8000 ? "is" : "is not"); +	} +} + +static void dump_virtual_units(void) +{ +	int i, j; +	char readbuf[512]; + +	for (i = 0; i < (MedHead[0].FormattedSize / meminfo.erasesize); i++) { +		unsigned short curEUN = VUCtable[i]; + +		printf("Virtual Unit #%d: ", i); +		if (!curEUN) { +			printf("Not present\n"); +			continue; +		} +		printf("%d", curEUN); + +		/* walk through the Virtual Unit Chain */ +		while ((curEUN = nextEUN(curEUN)) != 0xffff) { +			printf(", %d", curEUN & 0x7fff); +		} +		printf("\n"); + +		if (ofd != -1) { +			/* Actually write out the data */ +			for (j = 0; j < meminfo.erasesize / 512; j++) { +				/* For each sector in the block */ +				unsigned short lastgoodEUN = 0xffff, thisEUN = VUCtable[i]; +				unsigned int status; + +				if (thisEUN == 0xffff) thisEUN = 0; + +				while (thisEUN && (thisEUN & 0x7fff) != 0x7fff) { +					oob.start = (thisEUN * ERASESIZE) + (j * 512); +					ioctl(fd, MEMREADOOB, &oob); +					status = oobbuf.b.Status | oobbuf.b.Status1; + +					switch (status) { +						case SECTOR_FREE: +							/* This is still free. Don't look any more */ +							thisEUN = 0; +							break; + +						case SECTOR_USED: +							/* SECTOR_USED. This is a good one. */ +							lastgoodEUN = thisEUN; +							break; +					} + +					/* Find the next erase unit in this chain, if any */ +					if (thisEUN) +						thisEUN = nextEUN(thisEUN) & 0x7fff; +				} + +				if (lastgoodEUN == 0xffff) +					memset(readbuf, 0, 512); +				else +					pread(fd, readbuf, 512, +							(lastgoodEUN * ERASESIZE) + (j * 512)); + +				write(ofd, readbuf, 512); +			} + +		} +	} +} + +int main(int argc, char **argv) +{ +	if (argc < 2) { +		printf("Usage: %s <device> [<outfile>]\n", PROGRAM_NAME); +		exit(1); +	} +	fd = open(argv[1], O_RDONLY); +	if (fd == -1) { +		perror("open flash"); +		exit (1); +	} + +	if (argc > 2) { +		ofd = open(argv[2], O_WRONLY | O_TRUNC | O_CREAT, 0644); +		if (ofd == -1) +			perror ("open outfile"); +	} + +	/* get size information of the MTD device */ +	if (ioctl(fd, MEMGETINFO, &meminfo) != 0) { +		perror("ioctl(MEMGETINFO)"); +		close(fd); +		return 1; +	} + +	while (find_media_headers() != 0) { +		dump_erase_units(); +		dump_virtual_units(); +		free(VUCtable); +	} + +	exit(0); +} | 
