aboutsummaryrefslogtreecommitdiff
path: root/nand-utils
diff options
context:
space:
mode:
Diffstat (limited to 'nand-utils')
-rw-r--r--nand-utils/Makemodule.am18
-rw-r--r--nand-utils/nanddump.c2
-rw-r--r--nand-utils/nandflipbits.c326
-rw-r--r--nand-utils/nandtest.c49
-rw-r--r--nand-utils/nandwrite.c13
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;
}