diff options
Diffstat (limited to 'nand-utils')
-rw-r--r-- | nand-utils/Makemodule.am | 18 | ||||
-rw-r--r-- | nand-utils/nanddump.c | 2 | ||||
-rw-r--r-- | nand-utils/nandflipbits.c | 326 | ||||
-rw-r--r-- | nand-utils/nandtest.c | 49 | ||||
-rw-r--r-- | nand-utils/nandwrite.c | 13 |
5 files changed, 385 insertions, 23 deletions
diff --git a/nand-utils/Makemodule.am b/nand-utils/Makemodule.am index d75b0cb..cee6777 100644 --- a/nand-utils/Makemodule.am +++ b/nand-utils/Makemodule.am @@ -7,26 +7,24 @@ nandwrite_LDADD = libmtd.a nandtest_SOURCES = nand-utils/nandtest.c nandtest_LDADD = libmtd.a -nftldump_SOURCES = nand-utils/nftldump.c +nftldump_SOURCES = nand-utils/nftldump.c include/mtd_swab.h +nftldump_SOURCES += include/mtd/nftl-user.h include/mtd/ftl-user.h nftldump_LDADD = libmtd.a -nftl_format_SOURCES = nand-utils/nftl_format.c +nftl_format_SOURCES = nand-utils/nftl_format.c include/mtd_swab.h +nftl_format_SOURCES += include/mtd/nftl-user.h include/mtd/ftl-user.h nftl_format_LDADD = libmtd.a -NAND_BINS = \ - nanddump nandwrite nandtest nftldump nftl_format +nandflipbits_SOURCES = nand-utils/nandflipbits.c +nandflipbits_LDADD = libmtd.a NAND_SH = \ nand-utils/load_nandsim.sh EXTRA_DIST += $(NAND_SH) -sbin_PROGRAMS += $(NAND_BINS) +sbin_PROGRAMS += nanddump nandwrite nandtest nftldump nftl_format nandflipbits if BUILD_TESTS -if INSTALL_TESTS -pkglibexec_SCRIPTS += $(NAND_SH) -else -noinst_SCRIPTS += $(NAND_SH) -endif +test_SCRIPTS += $(NAND_SH) endif diff --git a/nand-utils/nanddump.c b/nand-utils/nanddump.c index d7fc320..47539f5 100644 --- a/nand-utils/nanddump.c +++ b/nand-utils/nanddump.c @@ -499,7 +499,7 @@ int main(int argc, char * const argv[]) } } else { /* Write requested length if oob is omitted */ - size_t size_left = end_addr - ofs; + long long size_left = end_addr - ofs; if (omitoob && (size_left < bs)) err = ofd_write(ofd, readbuf, size_left); else diff --git a/nand-utils/nandflipbits.c b/nand-utils/nandflipbits.c new file mode 100644 index 0000000..7066408 --- /dev/null +++ b/nand-utils/nandflipbits.c @@ -0,0 +1,326 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2014 Bootlin + * + * Authors: Boris Brezillon <boris.brezillon@collabora.com> + * Miquel Raynal <miquel.raynal@bootlin.com> + * + * Overview: + * This utility manually flips specified bits in a NAND flash. + */ + +#define PROGRAM_NAME "nandflipbits" + +#include <mtd/mtd-user.h> +#include <sys/ioctl.h> +#include <unistd.h> +#include <string.h> +#include <stdlib.h> +#include <libmtd.h> +#include <getopt.h> +#include <stdio.h> +#include <fcntl.h> + +#include "common.h" + +struct bit_flip { + uint32_t block; + uint64_t offset; + int bit; + bool done; +}; + +static void usage(int status) +{ + fprintf(status == EXIT_SUCCESS ? stdout : stderr, +"Usage: "PROGRAM_NAME" [OPTIONS] <device> <bit>@<address> [<bit>@<address>...]\n" +"\n" +" Test ECC engines, see if they match the specified correction strength:\n" +" * Reads in raw mode the data from an MTD device\n" +" * Flips the indicated bit(s)\n" +" * Write it back in raw mode.\n" +"\n" +" -h, --help Display this help and exit\n" +" -o, --oob Provided addresses take OOB area into account\n" +" -q, --quiet Don't display progress messages\n" +"\n" + ); + exit(status); +} + +static const char *mtd_device; +static bool quiet = false; +static bool oob_mode = false; +static struct bit_flip *bits_to_flip; +static int nbits_to_flip = 0; + +static void process_options(int argc, char * const argv[]) +{ + int error = 0; + int i; + + for (;;) { + int option_index = 0; + static const char short_options[] = "hoq"; + static const struct option long_options[] = { + {"help", no_argument, 0, 'h'}, + {"oob", no_argument, 0, 'o'}, + {"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 'q': + quiet = true; + break; + case 'o': + oob_mode = true; + break; + case 'h': + usage(EXIT_SUCCESS); + break; + case '?': + default: + error++; + break; + } + } + + argc -= optind; + argv += optind; + + /* + * There must be at least the MTD device node path argument remaining + * and a list of minimum one 'bits-to-flip'. + */ + + if (argc < 2 || error) + usage(EXIT_FAILURE); + + /* MTD device */ + mtd_device = argv[0]; + argc--; + argv++; + + /* Parse the bits to flip */ + nbits_to_flip = argc; + bits_to_flip = malloc(sizeof(*bits_to_flip) * nbits_to_flip); + if (!bits_to_flip) + exit(EXIT_FAILURE); + + for (i = 0; i < nbits_to_flip; i++) { + struct bit_flip *bit_to_flip = &bits_to_flip[i]; + char *desc = argv[i]; + + bit_to_flip->bit = strtol(desc, &desc, 0); + if (errno || bit_to_flip->bit > 7) + goto free_bits; + + if (!desc || *desc++ != '@') + goto free_bits; + + bit_to_flip->offset = strtol(desc, &desc, 0); + if (errno) + goto free_bits; + } + + return; + +free_bits: + free(bits_to_flip); + + fprintf(stderr, "Invalid bit description\n"); + + exit(EXIT_FAILURE); +} + +int main(int argc, char **argv) +{ + struct mtd_dev_info mtd; + libmtd_t mtd_desc; + uint64_t mtdlen; + uint32_t pagelen, pages_per_blk, blklen; + uint8_t *buffer; + int fd, ret, i; + + process_options(argc, argv); + + /* Open the libmtd */ + mtd_desc = libmtd_open(); + if (!mtd_desc) { + fprintf(stderr, "Cannot initialize libmtd\n"); + ret = EXIT_FAILURE; + goto free_bits; + } + + /* Fill in MTD device capability structure */ + ret = mtd_get_dev_info(mtd_desc, mtd_device, &mtd); + if (ret < 0) { + fprintf(stderr, "Cannot retrieve MTD device information\n"); + ret = EXIT_FAILURE; + goto close_lib; + } + + /* Verify we are using a NAND device */ + if (mtd.type != MTD_NANDFLASH && mtd.type != MTD_MLCNANDFLASH) { + fprintf(stderr, "%s is not a NAND flash\n", mtd_device); + ret = EXIT_FAILURE; + goto close_lib; + } + + /* Open the MTD device */ + fd = open(mtd_device, O_RDWR); + if (fd < 0) { + fprintf(stderr, "Cannot open %s\n", mtd_device); + ret = EXIT_FAILURE; + goto close_lib; + } + + /* Select raw mode */ + ret = ioctl(fd, MTDFILEMODE, MTD_FILE_MODE_RAW); + if (ret) { + fprintf(stderr, "Unavailable raw mode ioctl\n"); + ret = EXIT_FAILURE; + goto close_fd; + } + + pagelen = mtd.min_io_size + (oob_mode ? mtd.oob_size : 0); + pages_per_blk = mtd.eb_size / mtd.min_io_size; + blklen = pages_per_blk * pagelen; + mtdlen = (uint64_t)blklen * (uint64_t)mtd.eb_cnt; + buffer = malloc((mtd.min_io_size + mtd.oob_size) * pages_per_blk); + if (!buffer) { + ret = EXIT_FAILURE; + goto close_fd; + } + + for (i = 0; i < nbits_to_flip; i++) { + int page; + + if (bits_to_flip[i].offset >= mtdlen) { + fprintf(stderr, "Invalid byte offset %" PRId64 + " (max %" PRId64 ")\n", + bits_to_flip[i].offset, mtdlen); + ret = EXIT_FAILURE; + goto free_buf; + } + + bits_to_flip[i].block = bits_to_flip[i].offset / blklen; + bits_to_flip[i].offset %= blklen; + page = bits_to_flip[i].offset / pagelen; + bits_to_flip[i].offset = (page * + (mtd.min_io_size + mtd.oob_size)) + + (bits_to_flip[i].offset % pagelen); + } + + while (1) { + struct bit_flip *bit_to_flip = NULL; + int blkoffs; + int bufoffs; + + /* Look for the next bitflip to insert */ + for (i = 0; i < nbits_to_flip; i++) { + if (bits_to_flip[i].done == false) { + bit_to_flip = &bits_to_flip[i]; + break; + } + } + + if (!bit_to_flip) { + ret = EXIT_SUCCESS; + break; + } + + /* Read the content of all the pages of a block */ + blkoffs = 0; + bufoffs = 0; + for (i = 0; i < pages_per_blk; i++) { + ret = mtd_read(&mtd, fd, bit_to_flip->block, blkoffs, + buffer + bufoffs, mtd.min_io_size); + if (ret) { + fprintf(stderr, "MTD read failure\n"); + ret = EXIT_FAILURE; + goto free_buf; + } + + bufoffs += mtd.min_io_size; + + ret = mtd_read_oob(mtd_desc, &mtd, fd, + bit_to_flip->block * mtd.eb_size + + blkoffs, + mtd.oob_size, buffer + bufoffs); + if (ret) { + fprintf(stderr, "MTD OOB read failure\n"); + ret = EXIT_FAILURE; + goto free_buf; + } + + bufoffs += mtd.oob_size; + blkoffs += mtd.min_io_size; + } + + /* Flip all bits that are located in this particular block */ + for (i = 0; i < nbits_to_flip; i++) { + unsigned char val, mask; + + if (bits_to_flip[i].block != bit_to_flip->block) + continue; + + mask = 1 << bits_to_flip[i].bit; + val = buffer[bits_to_flip[i].offset] & mask; + if (val) + buffer[bits_to_flip[i].offset] &= ~mask; + else + buffer[bits_to_flip[i].offset] |= mask; + } + + /* Erase the block */ + ret = mtd_erase(mtd_desc, &mtd, fd, bit_to_flip->block); + if (ret) { + fprintf(stderr, "MTD erase failure\n"); + ret = EXIT_FAILURE; + goto free_buf; + } + + /* Rewrite the pages, still in raw mode, with the bitflips */ + blkoffs = 0; + bufoffs = 0; + for (i = 0; i < pages_per_blk; i++) { + ret = mtd_write(mtd_desc, &mtd, fd, bit_to_flip->block, + blkoffs, buffer + bufoffs, mtd.min_io_size, + buffer + bufoffs + mtd.min_io_size, + mtd.oob_size, + MTD_OPS_RAW); + if (ret) { + fprintf(stderr, "MTD write failure\n"); + ret = EXIT_FAILURE; + goto free_buf; + } + + blkoffs += mtd.min_io_size; + bufoffs += mtd.min_io_size + mtd.oob_size; + } + + /* Mark the added bitflips as done */ + for (i = 0; i < nbits_to_flip; i++) { + if (bits_to_flip[i].block == bit_to_flip->block) + bits_to_flip[i].done = true; + } + } + +free_buf: + free(buffer); +close_fd: + close(fd); +close_lib: + libmtd_close(mtd_desc); +free_bits: + free(bits_to_flip); + + exit(ret); +} diff --git a/nand-utils/nandtest.c b/nand-utils/nandtest.c index 06dec25..cac0dde 100644 --- a/nand-utils/nandtest.c +++ b/nand-utils/nandtest.c @@ -8,6 +8,7 @@ #include <string.h> #include <time.h> #include <unistd.h> +#include <limits.h> #include <sys/stat.h> #include <sys/ioctl.h> #include <sys/types.h> @@ -144,6 +145,26 @@ static int erase_and_write(loff_t ofs, unsigned char *data, unsigned char *rbuf, return 0; } +static uint64_t get_mem_size(const char* device) +{ + const char* p = strrchr(device, '/'); + char path[PATH_MAX]; + int fd; + + snprintf(path, sizeof(path), "/sys/class/mtd/%s/size", p); + fd = open(path, O_RDONLY); + if (fd >= 0) { + char buffer[32]; + ssize_t n = read(fd, buffer, sizeof(buffer)); + close(fd); + if (n > 0) { + return strtoull(buffer, NULL, 0); + } + } + + fprintf(stderr, "Can't read size from %s\n", path); + exit(1); +} /* * Main program @@ -156,8 +177,9 @@ int main(int argc, char **argv) int nr_passes = 1; int nr_reads = 4; int keep_contents = 0; - uint32_t offset = 0; - uint32_t length = -1; + uint64_t offset = 0; + uint64_t length = -1; + uint64_t mem_size = 0; int error = 0; seed = time(NULL); @@ -212,11 +234,11 @@ int main(int argc, char **argv) break; case 'o': - offset = simple_strtoul(optarg, &error); + offset = simple_strtoull(optarg, &error); break; case 'l': - length = simple_strtoul(optarg, &error); + length = simple_strtoull(optarg, &error); break; } @@ -238,29 +260,34 @@ int main(int argc, char **argv) exit(1); } + mem_size = get_mem_size(argv[optind]); + if (length == -1) - length = meminfo.size; + length = mem_size; if (offset % meminfo.erasesize) { - fprintf(stderr, "Offset %x not multiple of erase size %x\n", + fprintf(stderr, "Offset %" PRIx64 + " 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", + fprintf(stderr, "Length %" PRIx64 + " 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); + if (length + offset > mem_size) { + fprintf(stderr, "Length %" PRIx64 " + offset %" PRIx64 + " exceeds device size %" PRIx64 "\n", + length, offset, mem_size); exit(1); } wbuf = malloc(meminfo.erasesize * 3); if (!wbuf) { fprintf(stderr, "Could not allocate %d bytes for buffer\n", - meminfo.erasesize * 2); + meminfo.erasesize * 3); exit(1); } rbuf = wbuf + meminfo.erasesize; diff --git a/nand-utils/nandwrite.c b/nand-utils/nandwrite.c index e8a210c..cd53a17 100644 --- a/nand-utils/nandwrite.c +++ b/nand-utils/nandwrite.c @@ -280,6 +280,7 @@ int main(int argc, char * const argv[]) libmtd_t mtd_desc; int ebsize_aligned; uint8_t write_mode; + size_t all_ffs_cnt = 0; process_options(argc, argv); @@ -417,6 +418,8 @@ int main(int argc, char * const argv[]) */ while ((imglen > 0 || writebuf < filebuf + filebuf_len) && mtdoffset < mtd.size) { + bool allffs; + /* * New eraseblock, check for bad block(s) * Stay in the loop to be sure that, if mtdoffset changes because @@ -555,7 +558,8 @@ int main(int argc, char * const argv[]) } ret = 0; - if (!skipallffs || !buffer_check_pattern(writebuf, mtd.min_io_size, 0xff)) { + allffs = buffer_check_pattern(writebuf, mtd.min_io_size, 0xff); + if (!allffs || !skipallffs) { /* Write out data */ ret = mtd_write(mtd_desc, &mtd, fd, mtdoffset / mtd.eb_size, mtdoffset % mtd.eb_size, @@ -564,6 +568,8 @@ int main(int argc, char * const argv[]) writeoob ? oobbuf : NULL, writeoob ? mtd.oob_size : 0, write_mode); + if (!ret && allffs) + all_ffs_cnt++; } if (ret) { @@ -615,6 +621,11 @@ closeall: || (writebuf < filebuf + filebuf_len)) sys_errmsg_die("Data was only partially written due to error"); + if (all_ffs_cnt) { + fprintf(stderr, "Written %zu blocks containing only 0xff bytes\n", all_ffs_cnt); + fprintf(stderr, "Those block may be incorrectly treated as empty!\n"); + } + /* Return happy */ return EXIT_SUCCESS; } |