summaryrefslogtreecommitdiff
path: root/nand-utils/nanddump.c
diff options
context:
space:
mode:
Diffstat (limited to 'nand-utils/nanddump.c')
-rw-r--r--nand-utils/nanddump.c490
1 files changed, 490 insertions, 0 deletions
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);
+}