summaryrefslogtreecommitdiff
path: root/ubi-utils/sort-me-out/nand2bin.c
diff options
context:
space:
mode:
Diffstat (limited to 'ubi-utils/sort-me-out/nand2bin.c')
-rw-r--r--ubi-utils/sort-me-out/nand2bin.c493
1 files changed, 493 insertions, 0 deletions
diff --git a/ubi-utils/sort-me-out/nand2bin.c b/ubi-utils/sort-me-out/nand2bin.c
new file mode 100644
index 0000000..8c95b27
--- /dev/null
+++ b/ubi-utils/sort-me-out/nand2bin.c
@@ -0,0 +1,493 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006, 2007
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Frank Haverkamp
+ *
+ * An utility to decompose NAND images and strip OOB off. Not yet finished ...
+ *
+ * 1.2 Removed argp because we want to use uClibc.
+ * 1.3 Minor cleanup
+ * 1.4 Fixed OOB output file
+ * 1.5 Added verbose output and option to set blocksize.
+ * Added split block mode for more convenient analysis.
+ * 1.6 Fixed ECC error detection and correction.
+ * 1.7 Made NAND ECC layout configurable, the holes which were previously
+ * filled with 0x00 are untouched now and will be 0xff just like MTD
+ * behaves when writing the oob (haver)
+ */
+
+#include <config.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <getopt.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "config.h"
+#include "nandecc.h"
+#include "ecclayouts.h"
+
+#define PROGRAM_VERSION "1.7"
+
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+#define MAXPATH 1024
+#define MIN(x,y) ((x)<(y)?(x):(y))
+
+struct args {
+ const char *oob_file;
+ const char *output_file;
+ size_t pagesize;
+ size_t oobsize;
+ int bad_marker_offs_in_oob;
+ size_t blocksize;
+ int split_blocks;
+ size_t in_len; /* size of input file */
+ int correct_ecc;
+ struct nand_ecclayout *nand_oob;
+
+ /* special stuff needed to get additional arguments */
+ char *arg1;
+ char **options; /* [STRING...] */
+};
+
+static struct args myargs = {
+ .output_file = "data.bin",
+ .oob_file = "oob.bin",
+ .pagesize = 2048,
+ .blocksize = 128 * 1024,
+ .nand_oob = &ibm_nand_oob_64,
+ .in_len = 0,
+ .split_blocks = 0,
+ .correct_ecc = 0,
+ .arg1 = NULL,
+ .options = NULL,
+};
+
+static char doc[] = "\nVersion: " PROGRAM_VERSION "\n"
+ "nand2bin - split data and OOB.\n";
+
+static const char *optionsstr =
+" -l, --ecc-placement=<MTD,IBM> OOB placement scheme (default is IBM).\n"
+" -o, --output=<output> Data output file\n"
+" -O, --oob=<oob> OOB output file\n"
+" -p, --pagesize=<pagesize> NAND pagesize\n"
+" -b, --blocksize=<blocksize> NAND blocksize\n"
+" -s, --split-blocks generate binaries for each block\n"
+" -e, --correct-ecc Correct data according to ECC info\n"
+" -v, --verbose verbose output\n"
+" -?, --help Give this help list\n"
+" --usage Give a short usage message\n";
+
+static const char *usage =
+"Usage: nand2bin [-?] [-o <output>] [-O <oob>] [-p <pagesize>]\n"
+" [--output=<output>] [--oob=<oob>] [--pagesize=<pagesize>] [--help]\n"
+" [--usage] input.mif\n";
+
+static int verbose = 0;
+
+static struct option long_options[] = {
+ { .name = "ecc-layout", .has_arg = 1, .flag = NULL, .val = 'l' },
+ { .name = "output", .has_arg = 1, .flag = NULL, .val = 'o' },
+ { .name = "oob", .has_arg = 1, .flag = NULL, .val = 'O' },
+ { .name = "pagesize", .has_arg = 1, .flag = NULL, .val = 'p' },
+ { .name = "blocksize", .has_arg = 1, .flag = NULL, .val = 'b' },
+ { .name = "split-blocks", .has_arg = 0, .flag = NULL, .val = 's' },
+ { .name = "correct-ecc", .has_arg = 0, .flag = NULL, .val = 'e' },
+ { .name = "verbose", .has_arg = 0, .flag = NULL, .val = 'v' },
+ { .name = "help", .has_arg = 0, .flag = NULL, .val = '?' },
+ { .name = "usage", .has_arg = 0, .flag = NULL, .val = 0 },
+ { NULL, 0, NULL, 0}
+};
+
+/*
+ * str_to_num - Convert string into number and cope with endings like
+ * k, K, kib, KiB for kilobyte
+ * m, M, mib, MiB for megabyte
+ */
+static uint32_t str_to_num(char *str)
+{
+ char *s = str;
+ ulong num = strtoul(s, &s, 0);
+
+ if (*s != '\0') {
+ if (strcmp(s, "KiB") == 0)
+ num *= 1024;
+ else if (strcmp(s, "MiB") == 0)
+ num *= 1024*1024;
+ else {
+ fprintf(stderr, "WARNING: Wrong number format "
+ "\"%s\", check your paramters!\n", str);
+ }
+ }
+ return num;
+}
+
+/*
+ * @brief Parse the arguments passed into the test case.
+ *
+ * @param argc The number of arguments
+ * @param argv The argument list
+ * @param args Pointer to program args structure
+ *
+ * @return error
+ *
+ */
+static int parse_opt(int argc, char **argv, struct args *args)
+{
+ unsigned int i, oob_idx = 0;
+ const char *ecc_layout = NULL;
+
+ while (1) {
+ int key;
+
+ key = getopt_long(argc, argv, "b:el:o:O:p:sv?", long_options, NULL);
+ if (key == -1)
+ break;
+
+ switch (key) {
+ case 'b': /* --blocksize<blocksize> */
+ args->blocksize = str_to_num(optarg);
+ break;
+ case 'e': /* --correct-ecc */
+ args->correct_ecc = 1;
+ break;
+ case 'l': /* --ecc-layout=<...> */
+ ecc_layout = optarg;
+ break;
+ case 'o': /* --output=<output.bin> */
+ args->output_file = optarg;
+ break;
+ case 'O': /* --oob=<oob.bin> */
+ args->oob_file = optarg;
+ break;
+ case 'p': /* --pagesize<pagesize> */
+ args->pagesize = str_to_num(optarg);
+ break;
+ case 's': /* --split-blocks */
+ args->split_blocks = 1;
+ break;
+ case 'v': /* --verbose */
+ verbose++;
+ break;
+ case 'V':
+ printf("%s\n", PROGRAM_VERSION);
+ exit(0);
+ break;
+ case '?': /* help */
+ printf("Usage: nand2bin [OPTION...] input.mif\n");
+ printf("%s%s", doc, optionsstr);
+ printf("\nReport bugs to %s\n",
+ PACKAGE_BUGREPORT);
+ exit(0);
+ break;
+ default:
+ printf("%s", usage);
+ exit(-1);
+ }
+ }
+
+ if (optind < argc)
+ args->arg1 = argv[optind++];
+
+ switch (args->pagesize) {
+ case 512:
+ args->oobsize = 16;
+ args->bad_marker_offs_in_oob = 5;
+ oob_idx = 0;
+ break;
+ case 2048:
+ args->oobsize = 64;
+ args->bad_marker_offs_in_oob = 0;
+ oob_idx = 1;
+ break;
+ default:
+ fprintf(stderr, "Unsupported page size: %d\n", args->pagesize);
+ return -EINVAL;
+ }
+
+ /* Figure out correct oob layout if it differs from default */
+ if (ecc_layout) {
+ for (i = 0; i < ARRAY_SIZE(oob_placement); i++)
+ if (strcmp(ecc_layout, oob_placement[i].name) == 0)
+ args->nand_oob =
+ oob_placement[i].nand_oob[oob_idx];
+ }
+ return 0;
+}
+
+/*
+ * We must only compare the relevant bytes in the OOB area. All other
+ * bytes can be ignored. The information we need to do this is in
+ * nand_oob.
+ */
+static int oob_cmp(struct nand_ecclayout *nand_oob, uint8_t *oob,
+ uint8_t *calc_oob)
+{
+ unsigned int i;
+ for (i = 0; i < nand_oob->eccbytes; i++)
+ if (oob[nand_oob->eccpos[i]] != calc_oob[nand_oob->eccpos[i]])
+ return 1;
+ return 0;
+}
+
+static inline void hexdump(FILE *fp, const uint8_t *buf, ssize_t size)
+{
+ int k;
+
+ for (k = 0; k < size; k++) {
+ fprintf(fp, "%02x ", buf[k]);
+ if ((k & 15) == 15)
+ fprintf(fp, "\n");
+ }
+}
+
+static int process_page(struct args *args, uint8_t *buf, uint8_t *oobbuf)
+{
+ size_t i, j;
+ int eccpoi;
+ uint8_t ecc_code[3] = { 0, }; /* temp */
+
+ /* Calculate ECC */
+ memset(oobbuf, 0xff, args->oobsize);
+ for (eccpoi = 0, i = 0; i < args->pagesize; i += 256, eccpoi += 3) {
+ nand_calculate_ecc(&buf[i], ecc_code);
+ for (j = 0; j < 3; j++)
+ oobbuf[args->nand_oob->eccpos[eccpoi + j]] = ecc_code[j];
+ }
+ return 0;
+}
+
+static int decompose_image(struct args *args, FILE *in_fp,
+ FILE *bin_fp, FILE *oob_fp)
+{
+ unsigned int i, eccpoi;
+ int read, rc, page = 0;
+ uint8_t *buf = malloc(args->pagesize);
+ uint8_t *oob = malloc(args->oobsize);
+ uint8_t *calc_oob = malloc(args->oobsize);
+ uint8_t *calc_buf = malloc(args->pagesize);
+ uint8_t *page_buf;
+ int pages_per_block = args->blocksize / args->pagesize;
+ int badpos = args->bad_marker_offs_in_oob;
+ uint8_t ecc_code[3] = { 0, }; /* temp */
+ uint8_t calc_ecc_code[3] = { 0, }; /* temp */
+
+ if (!buf || !oob || !calc_oob || !calc_buf)
+ exit(EXIT_FAILURE);
+
+ while (!feof(in_fp)) {
+ /* read page by page */
+ read = fread(buf, 1, args->pagesize, in_fp);
+ if (ferror(in_fp)) {
+ fprintf(stderr, "I/O Error.");
+ exit(EXIT_FAILURE);
+ }
+ if (read != (ssize_t)args->pagesize)
+ break;
+
+ read = fread(oob, 1, args->oobsize, in_fp);
+ if (ferror(in_fp)) {
+ fprintf(stderr, "I/O Error.");
+ exit(EXIT_FAILURE);
+ }
+
+ page_buf = buf; /* default is unmodified data */
+
+ if ((page == 0 || page == 1) && (oob[badpos] != 0xff)) {
+ if (verbose)
+ printf("Block %d is bad\n",
+ page / pages_per_block);
+ goto write_data;
+ }
+ if (args->correct_ecc)
+ page_buf = calc_buf;
+
+ process_page(args, buf, calc_oob);
+ memcpy(calc_buf, buf, args->pagesize);
+
+ if (verbose && oob_cmp(args->nand_oob, oob, calc_oob) != 0) {
+ printf("\nECC compare mismatch found at block %d page %d!\n",
+ page / pages_per_block, page % pages_per_block);
+
+ printf("Read out OOB Data:\n");
+ hexdump(stdout, oob, args->oobsize);
+
+ printf("Calculated OOB Data:\n");
+ hexdump(stdout, calc_oob, args->oobsize);
+ }
+
+ /* Do correction on subpage base */
+ for (i = 0, eccpoi = 0; i < args->pagesize; i += 256, eccpoi += 3) {
+ int j;
+
+ for (j = 0; j < 3; j++) {
+ ecc_code[j] = oob[args->nand_oob->eccpos[eccpoi + j]];
+ calc_ecc_code[j] =
+ calc_oob[args->nand_oob->eccpos[eccpoi + j]];
+ }
+ rc = nand_correct_data(calc_buf + i, ecc_code,
+ calc_ecc_code);
+ if (rc == -1)
+ fprintf(stdout, "Uncorrectable ECC error at "
+ "block %d page %d/%d\n",
+ page / pages_per_block,
+ page % pages_per_block, i / 256);
+ else if (rc > 0)
+ fprintf(stdout, "Correctable ECC error at "
+ "block %d page %d/%d\n",
+ page / pages_per_block,
+ page % pages_per_block, i / 256);
+ }
+
+ write_data:
+ rc = fwrite(page_buf, 1, args->pagesize, bin_fp);
+ if (ferror(bin_fp)) {
+ fprintf(stderr, "I/O Error.");
+ exit(EXIT_FAILURE);
+ }
+ rc = fwrite(oob, 1, args->oobsize, oob_fp);
+ if (ferror(bin_fp)) {
+ fprintf(stderr, "I/O Error.");
+ exit(EXIT_FAILURE);
+ }
+
+ page++;
+ }
+ free(calc_buf);
+ free(calc_oob);
+ free(oob);
+ free(buf);
+ return 0;
+}
+
+static int split_blocks(struct args *args, FILE *in_fp)
+{
+ uint8_t *buf;
+ int pages_per_block = args->blocksize / args->pagesize;
+ int block_len = pages_per_block * (args->pagesize + args->oobsize);
+ int blocks = args->in_len / block_len;
+ char bname[256] = { 0, };
+ int badpos = args->bad_marker_offs_in_oob;
+ int bad_blocks = 0, i, bad_block = 0;
+ ssize_t rc;
+ FILE *b;
+
+ buf = malloc(block_len);
+ if (!buf) {
+ perror("Not enough memory");
+ exit(EXIT_FAILURE);
+ }
+
+ for (i = 0; i < blocks; i++) {
+ rc = fread(buf, 1, block_len, in_fp);
+ if (rc != block_len) {
+ fprintf(stderr, "cannot read enough data!\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* do block analysis */
+ bad_block = 0;
+ if ((buf[args->pagesize + badpos] != 0xff) ||
+ (buf[2 * args->pagesize + args->oobsize + badpos] != 0xff)) {
+ bad_blocks++;
+ bad_block = 1;
+ }
+ if ((verbose && bad_block) || (verbose > 1)) {
+ printf("-- (block %d oob of page 0 and 1)\n", i);
+ hexdump(stdout, buf + args->pagesize, args->oobsize);
+ printf("--\n");
+ hexdump(stdout, buf + 2 * args->pagesize +
+ args->oobsize, args->oobsize);
+ }
+
+ /* write complete block out */
+ snprintf(bname, sizeof(bname) - 1, "%s.%d", args->arg1, i);
+ b = fopen(bname, "w+");
+ if (!b) {
+ perror("Cannot open file");
+ exit(EXIT_FAILURE);
+ }
+ rc = fwrite(buf, 1, block_len, b);
+ if (rc != block_len) {
+ fprintf(stderr, "could not write all data!\n");
+ exit(EXIT_FAILURE);
+ }
+ fclose(b);
+ }
+
+ free(buf);
+ if (bad_blocks || verbose)
+ fprintf(stderr, "%d blocks, %d bad blocks\n",
+ blocks, bad_blocks);
+ return 0;
+}
+
+int
+main(int argc, char *argv[])
+{
+ FILE *in, *bin = NULL, *oob = NULL;
+ struct stat file_info;
+
+ parse_opt(argc, argv, &myargs);
+
+ if (!myargs.arg1) {
+ fprintf(stderr, "Please specify input file!\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (lstat(myargs.arg1, &file_info) != 0) {
+ perror("Cannot fetch file size from input file.\n");
+ exit(EXIT_FAILURE);
+ }
+ myargs.in_len = file_info.st_size;
+
+ in = fopen(myargs.arg1, "r");
+ if (!in) {
+ perror("Cannot open file");
+ exit(EXIT_FAILURE);
+ }
+
+ if (myargs.split_blocks) {
+ split_blocks(&myargs, in);
+ goto out;
+ }
+
+ bin = fopen(myargs.output_file, "w+");
+ if (!bin) {
+ perror("Cannot open file");
+ exit(EXIT_FAILURE);
+ }
+ oob = fopen(myargs.oob_file, "w+");
+ if (!oob) {
+ perror("Cannot open file");
+ exit(EXIT_FAILURE);
+ }
+ decompose_image(&myargs, in, bin, oob);
+
+ out:
+ if (in) fclose(in);
+ if (bin) fclose(bin);
+ if (oob) fclose(oob);
+ exit(EXIT_SUCCESS);
+}