aboutsummaryrefslogtreecommitdiff
path: root/nand-utils
diff options
context:
space:
mode:
authorDongsheng Yang <yangds.fnst@cn.fujitsu.com>2015-10-31 11:12:01 +0800
committerBrian Norris <computersforpeace@gmail.com>2015-11-11 14:38:40 -0800
commit7d81790ced345585b1e647ca9d0f6678e7062fa4 (patch)
tree02f61270c7a0fff7bb6b2e28f247a3d2fd6ff490 /nand-utils
parent344753f2aacb94d98ce238f81fc4a4b6ef6adea9 (diff)
mtd-utils: Restructure the mtd-utils source.
* There is no code modification in this commit, only moving * the files to proper place. The user tools looks a little messy as we place almost the all tools in the root directory of mtd-utils. To make it more clear, I propose to introduce the following structure for our source code. mtd-utils/ |-- lib |-- include |-- misc-utils |-- jffsX-utils |-- nand-utils |-- nor-utils |-- ubi-utils |-- ubifs-utils `-- tests Signed-off-by: Dongsheng Yang <yangds.fnst@cn.fujitsu.com> Signed-off-by: Brian Norris <computersforpeace@gmail.com>
Diffstat (limited to 'nand-utils')
-rwxr-xr-xnand-utils/load_nandsim.sh127
-rw-r--r--nand-utils/nanddump.c490
-rw-r--r--nand-utils/nandtest.c317
-rw-r--r--nand-utils/nandwrite.c578
-rw-r--r--nand-utils/nftl_format.c422
-rw-r--r--nand-utils/nftldump.c278
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);
+}