123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620 |
- /*
- * 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"
- " -k, --skip-all-ffs Skip pages that contain only 0xff bytes\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"
- " --skip-bad-blocks-to-start"
- " Skip bad blocks when seeking to the start address\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"
- " -V, --version Output version information and exit\n"
- );
- exit(status);
- }
- static void display_version(void)
- {
- common_print_version();
- printf("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 skipallffs = false;
- static bool noskipbad = false;
- static bool pad = false;
- static bool skip_bad_blocks_to_start = 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:akV";
- static const struct option long_options[] = {
- /* Order of these args with val==0 matters; see option_index. */
- {"version", no_argument, 0, 'V'},
- {"input-skip", required_argument, 0, 0},
- {"input-size", required_argument, 0, 0},
- {"skip-bad-blocks-to-start", no_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'},
- {"skip-all-ffs", no_argument, 0, 'k'},
- {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 1: /* --input-skip */
- inputskip = simple_strtoll(optarg, &error);
- break;
- case 2: /* --input-size */
- inputsize = simple_strtoll(optarg, &error);
- break;
- case 3: /* --skip-bad-blocks-to-start */
- skip_bad_blocks_to_start = true;
- break;
- }
- break;
- case 'V':
- display_version();
- 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 'k':
- skipallffs = 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 or zero blockalign with "
- "option -b: %d", blockalign);
- if (!is_power_of_2(blockalign))
- errmsg_die("Can't specify a non-power-of-two 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);
- }
- static int is_virt_block_bad(struct mtd_dev_info *mtd, int fd,
- long long offset)
- {
- int i, ret = 0;
- for (i = 0; i < blockalign; ++i) {
- ret = mtd_is_bad(mtd, fd, offset / mtd->eb_size + i);
- if (ret)
- break;
- }
- return ret;
- }
- /*
- * Main program
- */
- int main(int argc, char * const argv[])
- {
- int fd = -1;
- int ifd = -1;
- int pagelen;
- long long imglen = 0;
- long long blockstart = -1;
- struct mtd_dev_info mtd;
- 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;
- }
- /* Skip bad blocks on the way to the start address if necessary */
- if (skip_bad_blocks_to_start) {
- long long bbs_offset = 0;
- while (bbs_offset < mtdoffset) {
- ret = is_virt_block_bad(&mtd, fd, bbs_offset);
- if (ret < 0) {
- sys_errmsg("%s: MTD get bad block failed", mtd_device);
- goto closeall;
- } else if (ret == 1) {
- if (!quiet)
- fprintf(stderr, "Bad block at %llx, %u block(s) "
- "from %llx will be skipped\n",
- bbs_offset, blockalign, bbs_offset);
- mtdoffset += ebsize_aligned;
- }
- bbs_offset += ebsize_aligned;
- }
- }
- /* 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);
- /*
- * 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;
- }
- if (!quiet)
- fprintf(stdout, "Writing data to block %lld at offset 0x%llx\n",
- blockstart / ebsize_aligned, blockstart);
- if (noskipbad)
- continue;
- ret = is_virt_block_bad(&mtd, fd, blockstart);
- if (ret < 0) {
- sys_errmsg("%s: MTD get bad block failed", mtd_device);
- goto closeall;
- } else if (ret == 1) {
- if (!quiet)
- fprintf(stderr,
- "Bad block at %llx, %u block(s) "
- "will be skipped\n",
- blockstart, blockalign);
- mtdoffset = blockstart + ebsize_aligned;
- if (mtdoffset > mtd.size) {
- errmsg("too many bad blocks, cannot complete request");
- goto closeall;
- }
- }
- }
- /* 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;
- }
- }
- }
- ret = 0;
- if (!skipallffs || !buffer_check_pattern(writebuf, mtd.min_io_size, 0xff)) {
- /* 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) {
- 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);
- if (mtd_erase_multi(mtd_desc, &mtd, fd,
- blockstart / mtd.eb_size, blockalign)) {
- 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:
- if (ifd > 0 && ifd != STDIN_FILENO)
- 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;
- }
|