diff options
Diffstat (limited to 'nandwrite.c')
-rw-r--r-- | nandwrite.c | 578 |
1 files changed, 0 insertions, 578 deletions
diff --git a/nandwrite.c b/nandwrite.c deleted file mode 100644 index 9c3fe8f..0000000 --- a/nandwrite.c +++ /dev/null @@ -1,578 +0,0 @@ -/* - * 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; -} |