static void print_region_map(const struct mtd_dev_info *mtd, int fd, const region_info_t *reginfo) { unsigned long start; int i, width; int ret_locked, errno_locked, ret_bad, errno_bad; printf("Eraseblock map:\n"); /* Figure out the number of spaces to pad w/out libm */ for (i = 1, width = 0; i < reginfo->numblocks; i *= 10, ++width) continue; /* If we don't have a fd to query, just show the bare map */ if (fd == -1) { ret_locked = ret_bad = -1; errno_locked = errno_bad = ENODEV; } else ret_locked = ret_bad = errno_locked = errno_bad = 0; for (i = 0; i < reginfo->numblocks; ++i) { start = reginfo->offset + i * reginfo->erasesize; printf(" %*i: %08lx ", width, i, start); if (ret_locked != -1) { ret_locked = mtd_is_locked(mtd, fd, i); if (ret_locked == 1) printf("RO "); else errno_locked = errno; } if (ret_locked != 1) printf(" "); if (ret_bad != -1) { ret_bad = mtd_is_bad(mtd, fd, i); if (ret_bad == 1) printf("BAD "); else errno_bad = errno; } if (ret_bad != 1) printf(" "); if (((i + 1) % 4) == 0) printf("\n"); } if (i % 4) printf("\n"); if (ret_locked == -1 && errno_locked != EOPNOTSUPP) { errno = errno_locked; sys_errmsg("could not read locked block info"); } if (mtd->bb_allowed && ret_bad == -1 && errno_bad != EOPNOTSUPP) { errno = errno_bad; sys_errmsg("could not read bad block info"); } }
static int fio_mtd_is_bad(struct thread_data *td, struct fio_mtd_data *fmd, struct io_u *io_u, int eb) { int ret = mtd_is_bad(&fmd->info, io_u->file->fd, eb); if (ret == -1) { io_u->error = errno; td_verror(td, errno, "mtd_is_bad"); } else if (ret == 1) io_u->error = EIO; /* Silent failure--don't flood stderr */ return ret; }
static void test_mtd_is_bad(void **state) { struct mtd_dev_info mtd; loff_t seek; int eb = 0x42; memset(&mtd, 0, sizeof(mtd)); mtd.bb_allowed = 1; mtd.eb_cnt = 1024; mtd.eb_size = 128; seek = (loff_t)eb * mtd.eb_size; expect_ioctl(MEMGETBADBLOCK, 0, &seek); int r = mtd_is_bad(&mtd, 4, eb); assert_int_equal(r, 0); (void) state; }
/* Returns -1 upon failure, 0 if block is OK, and 1 if the erase block is bad. */ static int _check_block( struct bootconfig * bc, unsigned int block_idx ) { int err; err = mtd_is_bad( &bc->info, bc->fd, block_idx ); if ( 0 > err ) { bc_log( LOG_ERR, "Error %d while bad block checking block %d on %s: %s.\n", errno, block_idx, bc->dev, strerror( errno )); return -1; } else if ( 0 < err ) { bc_log( LOG_WARNING, "Block %d on %s is bad. Sorry about that.\n", block_idx, bc->dev); memcpy ( bc->blocks[ block_idx ].magic, "BAD!", 4 ); return 1; } return 0; }
static int flash_erase(int mtdnum) { int fd; char mtd_device[LINESIZE]; struct mtd_dev_info *mtd; int noskipbad = 0; int unlock = 0; unsigned int eb, eb_start, eb_cnt; struct flash_description *flash = get_flash_info(); if (!mtd_dev_present(flash->libmtd, mtdnum)) { ERROR("MTD %d does not exist\n", mtdnum); return -ENODEV; } mtd = &flash->mtd_info[mtdnum].mtd; snprintf(mtd_device, sizeof(mtd_device), "/dev/mtd%d", mtdnum); if ((fd = open(mtd_device, O_RDWR)) < 0) { ERROR( "%s: %s: %s", __func__, mtd_device, strerror(errno)); return -ENODEV; } /* * prepare to erase all of the MTD partition, */ eb_start = 0; eb_cnt = (mtd->size / mtd->eb_size) - eb_start; for (eb = 0; eb < eb_start + eb_cnt; eb++) { /* Always skip bad sectors */ if (!noskipbad) { int ret = mtd_is_bad(mtd, fd, eb); if (ret > 0) { continue; } else if (ret < 0) { if (errno == EOPNOTSUPP) { noskipbad = 1; } else { ERROR("%s: MTD get bad block failed", mtd_device); return -EFAULT; } } } if (unlock) { if (mtd_unlock(mtd, fd, eb) != 0) { TRACE("%s: MTD unlock failure", mtd_device); continue; } } if (mtd_erase(flash->libmtd, mtd, fd, eb) != 0) { ERROR("%s: MTD Erase failure", mtd_device); return -EFAULT; } } close(fd); return 0; }
static int flash_write_nand(int mtdnum, struct img_type *img) { char mtd_device[LINESIZE]; struct flash_description *flash = get_flash_info(); struct mtd_dev_info *mtd = &flash->mtd_info[mtdnum].mtd; int pagelen; bool baderaseblock = false; long long imglen = 0; long long blockstart = -1; long long offs; unsigned char *filebuf = NULL; size_t filebuf_max = 0; size_t filebuf_len = 0; long long mtdoffset = 0; int ifd = img->fdin; int fd = -1; bool failed = true; int ret; unsigned char *writebuf = NULL; pagelen = mtd->min_io_size; imglen = img->size; snprintf(mtd_device, sizeof(mtd_device), "/dev/mtd%d", mtdnum); if ((imglen / pagelen) * mtd->min_io_size > mtd->size) { ERROR("Image %s does not fit into mtd%d\n", img->fname, mtdnum); return -EIO; } filebuf_max = mtd->eb_size / mtd->min_io_size * pagelen; filebuf = calloc(1, filebuf_max); erase_buffer(filebuf, filebuf_max); if ((fd = open(mtd_device, O_RDWR)) < 0) { ERROR( "%s: %s: %s", __func__, mtd_device, strerror(errno)); return -ENODEV; } /* * 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 */ while (blockstart != (mtdoffset & (~mtd->eb_size + 1))) { blockstart = mtdoffset & (~mtd->eb_size + 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; do { ret = mtd_is_bad(mtd, fd, offs / mtd->eb_size); if (ret < 0) { ERROR("mtd%d: MTD get bad block failed", mtdnum); goto closeall; } else if (ret == 1) { baderaseblock = true; } if (baderaseblock) { mtdoffset = blockstart + mtd->eb_size; if (mtdoffset > mtd->size) { ERROR("too many bad blocks, cannot complete request"); goto closeall; } } offs += mtd->eb_size; } while (offs < blockstart + mtd->eb_size); } /* 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) { ERROR("File I/O error on input"); goto closeall; } tinycnt += cnt; } /* No padding needed - we are done */ if (tinycnt == 0) { imglen = 0; break; } /* Padding */ if (tinycnt < readlen) { erase_buffer(writebuf + tinycnt, readlen - tinycnt); } filebuf_len += readlen - alreadyread; imglen -= tinycnt - alreadyread; } /* Write out data */ ret = mtd_write(flash->libmtd, mtd, fd, mtdoffset / mtd->eb_size, mtdoffset % mtd->eb_size, writebuf, mtd->min_io_size, NULL, 0, MTD_OPS_PLACE_OOB); if (ret) { long long i; if (errno != EIO) { ERROR("mtd%d: MTD write failure", mtdnum); goto closeall; } /* Must rewind to blockstart if we can */ writebuf = filebuf; for (i = blockstart; i < blockstart + mtd->eb_size; i += mtd->eb_size) { if (mtd_erase(flash->libmtd, mtd, fd, i / mtd->eb_size)) { int errno_tmp = errno; TRACE("mtd%d: MTD Erase failure", mtdnum); if (errno_tmp != EIO) goto closeall; } } TRACE("Marking block at %08llx bad\n", mtdoffset & (~mtd->eb_size + 1)); if (mtd_mark_bad(mtd, fd, mtdoffset / mtd->eb_size)) { ERROR("mtd%d: MTD Mark bad block failure", mtdnum); goto closeall; } mtdoffset = blockstart + mtd->eb_size; continue; } mtdoffset += mtd->min_io_size; writebuf += pagelen; } failed = false; closeall: free(filebuf); close(fd); if (failed) { ERROR("Installing image %s into mtd%d failed\n", img->fname, mtdnum); return -1; } return 0; }
/* * Main program */ int main(int argc, char * const argv[]) { long long ofs, end_addr = 0; long long blockstart = 1; int ret, i, fd, ofd = 0, bs, badblock = 0; struct mtd_dev_info mtd; char pretty_buf[PRETTY_BUF_LEN]; int oobinfochanged = 0, firstblock = 1; struct nand_oobinfo old_oobinfo; struct mtd_ecc_stats stat1, stat2; bool eccstats = false; unsigned char *readbuf = NULL, *oobbuf = NULL; libmtd_t mtd_desc; process_options(argc, argv); /* Initialize libmtd */ mtd_desc = libmtd_open(); if (!mtd_desc) return errmsg("can't initialize libmtd"); /* Open MTD device */ if ((fd = open(mtddev, O_RDONLY)) == -1) { perror(mtddev); exit(EXIT_FAILURE); } /* Fill in MTD device capability structure */ if (mtd_get_dev_info(mtd_desc, mtddev, &mtd) < 0) return errmsg("mtd_get_dev_info failed"); /* Allocate buffers */ oobbuf = xmalloc(sizeof(oobbuf) * mtd.oob_size); readbuf = xmalloc(sizeof(readbuf) * mtd.min_io_size); if (noecc) { ret = ioctl(fd, MTDFILEMODE, MTD_MODE_RAW); if (ret == 0) { oobinfochanged = 2; } else { switch (errno) { case ENOTTY: if (ioctl(fd, MEMGETOOBSEL, &old_oobinfo) != 0) { perror("MEMGETOOBSEL"); goto closeall; } if (ioctl(fd, MEMSETOOBSEL, &none_oobinfo) != 0) { perror("MEMSETOOBSEL"); goto closeall; } oobinfochanged = 1; break; default: perror("MTDFILEMODE"); goto closeall; } } } else { /* check if we can read ecc stats */ if (!ioctl(fd, ECCGETSTATS, &stat1)) { eccstats = true; if (!quiet) { fprintf(stderr, "ECC failed: %d\n", stat1.failed); fprintf(stderr, "ECC corrected: %d\n", stat1.corrected); fprintf(stderr, "Number of bad blocks: %d\n", stat1.badblocks); fprintf(stderr, "Number of bbt blocks: %d\n", stat1.bbtblocks); } } else perror("No ECC status information available"); } /* Open output file for writing. If file name is "-", write to standard * output. */ if (!dumpfile) { ofd = STDOUT_FILENO; } else if ((ofd = open(dumpfile, O_WRONLY | O_TRUNC | O_CREAT, 0644))== -1) { perror(dumpfile); goto closeall; } if (!pretty_print && !forcebinary && isatty(ofd)) { fprintf(stderr, "Not printing binary garbage to tty. Use '-a'\n" "or '--forcebinary' to override.\n"); goto closeall; } /* Initialize start/end addresses and block size */ if (start_addr & (mtd.min_io_size - 1)) { fprintf(stderr, "WARNING: The start address is not page-aligned !\n" "The pagesize of this NAND Flash is 0x%x.\n" "nandwrite doesn't allow writes starting at this location.\n" "Future versions of nanddump will fail here.\n", mtd.min_io_size); } if (length) end_addr = start_addr + length; if (!length || end_addr > mtd.size) end_addr = mtd.size; bs = mtd.min_io_size; /* Print informative message */ if (!quiet) { fprintf(stderr, "Block size %d, page size %d, OOB size %d\n", mtd.eb_size, mtd.min_io_size, mtd.oob_size); fprintf(stderr, "Dumping data starting at 0x%08llx and ending at 0x%08llx...\n", start_addr, end_addr); } /* Dump the flash contents */ for (ofs = start_addr; ofs < end_addr; ofs += bs) { /* Check for bad block */ if (noskipbad) { badblock = 0; } else if (blockstart != (ofs & (~mtd.eb_size + 1)) || firstblock) { blockstart = ofs & (~mtd.eb_size + 1); firstblock = 0; if ((badblock = mtd_is_bad(&mtd, fd, ofs / mtd.eb_size)) < 0) { errmsg("libmtd: mtd_is_bad"); goto closeall; } } if (badblock) { if (omitbad) continue; memset(readbuf, 0xff, bs); } else { /* Read page data and exit on failure */ if (mtd_read(&mtd, fd, ofs / mtd.eb_size, ofs % mtd.eb_size, readbuf, bs)) { errmsg("mtd_read"); goto closeall; } } /* ECC stats available ? */ if (eccstats) { if (ioctl(fd, ECCGETSTATS, &stat2)) { perror("ioctl(ECCGETSTATS)"); goto closeall; } if (stat1.failed != stat2.failed) fprintf(stderr, "ECC: %d uncorrectable bitflip(s)" " at offset 0x%08llx\n", stat2.failed - stat1.failed, ofs); if (stat1.corrected != stat2.corrected) fprintf(stderr, "ECC: %d corrected bitflip(s) at" " offset 0x%08llx\n", stat2.corrected - stat1.corrected, ofs); stat1 = stat2; } /* Write out page data */ if (pretty_print) { for (i = 0; i < bs; i += PRETTY_ROW_SIZE) { pretty_dump_to_buffer(readbuf + i, PRETTY_ROW_SIZE, pretty_buf, PRETTY_BUF_LEN, true, canonical, ofs + i); write(ofd, pretty_buf, strlen(pretty_buf)); } } else write(ofd, readbuf, bs); if (omitoob) continue; if (badblock) { memset(oobbuf, 0xff, mtd.oob_size); } else { /* Read OOB data and exit on failure */ if (mtd_read_oob(mtd_desc, &mtd, fd, ofs, mtd.oob_size, oobbuf)) { errmsg("libmtd: mtd_read_oob"); goto closeall; } } /* Write out OOB data */ if (pretty_print) { for (i = 0; i < mtd.oob_size; i += PRETTY_ROW_SIZE) { pretty_dump_to_buffer(oobbuf + i, mtd.oob_size - i, pretty_buf, PRETTY_BUF_LEN, false, canonical, 0); write(ofd, pretty_buf, strlen(pretty_buf)); } } else write(ofd, oobbuf, mtd.oob_size); } /* reset oobinfo */ if (oobinfochanged == 1) { if (ioctl(fd, MEMSETOOBSEL, &old_oobinfo) != 0) { perror("MEMSETOOBSEL"); close(fd); close(ofd); return EXIT_FAILURE; } } /* Close the output file and MTD device, free memory */ close(fd); close(ofd); free(oobbuf); free(readbuf); /* Exit happy */ return EXIT_SUCCESS; closeall: /* The new mode change is per file descriptor ! */ if (oobinfochanged == 1) { if (ioctl(fd, MEMSETOOBSEL, &old_oobinfo) != 0) { perror("MEMSETOOBSEL"); } } close(fd); close(ofd); free(oobbuf); free(readbuf); exit(EXIT_FAILURE); }
int ubi_scan(struct mtd_dev_info *mtd, int fd, struct ubi_scan_info **info, int verbose) { int eb, v = (verbose == 2), pr = (verbose == 1); struct ubi_scan_info *si; unsigned long long sum = 0; si = calloc(1, sizeof(struct ubi_scan_info)); if (!si) return sys_errmsg("cannot allocate %zd bytes of memory", sizeof(struct ubi_scan_info)); si->ec = calloc(mtd->eb_cnt, sizeof(uint32_t)); if (!si->ec) { sys_errmsg("cannot allocate %zd bytes of memory", sizeof(struct ubi_scan_info)); goto out_si; } si->vid_hdr_offs = si->data_offs = -1; verbose(v, "start scanning eraseblocks 0-%d", mtd->eb_cnt); for (eb = 0; eb < mtd->eb_cnt; eb++) { int ret; uint32_t crc; struct ubi_ec_hdr ech; unsigned long long ec; if (v) { normsg_cont("scanning eraseblock %d", eb); fflush(stdout); } if (pr) { printf("\r" PROGRAM_NAME ": scanning eraseblock %d -- %2lld %% complete ", eb, (long long)(eb + 1) * 100 / mtd->eb_cnt); fflush(stdout); } ret = mtd_is_bad(mtd, fd, eb); if (ret == -1) goto out_ec; if (ret) { si->bad_cnt += 1; si->ec[eb] = EB_BAD; if (v) printf(": bad\n"); continue; } ret = mtd_read(mtd, fd, eb, 0, &ech, sizeof(struct ubi_ec_hdr)); if (ret < 0) goto out_ec; if (be32_to_cpu(ech.magic) != UBI_EC_HDR_MAGIC) { if (all_ff(&ech, sizeof(struct ubi_ec_hdr))) { si->empty_cnt += 1; si->ec[eb] = EB_EMPTY; if (v) printf(": empty\n"); } else { si->alien_cnt += 1; si->ec[eb] = EB_ALIEN; if (v) printf(": alien\n"); } continue; } crc = mtd_crc32(UBI_CRC32_INIT, &ech, UBI_EC_HDR_SIZE_CRC); if (be32_to_cpu(ech.hdr_crc) != crc) { si->corrupted_cnt += 1; si->ec[eb] = EB_CORRUPTED; if (v) printf(": bad CRC %#08x, should be %#08x\n", crc, be32_to_cpu(ech.hdr_crc)); continue; } ec = be64_to_cpu(ech.ec); if (ec > EC_MAX) { if (pr) printf("\n"); errmsg("erase counter in EB %d is %llu, while this " "program expects them to be less than %u", eb, ec, EC_MAX); goto out_ec; } if (si->vid_hdr_offs == -1) { si->vid_hdr_offs = be32_to_cpu(ech.vid_hdr_offset); si->data_offs = be32_to_cpu(ech.data_offset); if (si->data_offs % mtd->min_io_size) { if (pr) printf("\n"); if (v) printf(": corrupted because of the below\n"); warnmsg("bad data offset %d at eraseblock %d (n" "of multiple of min. I/O unit size %d)", si->data_offs, eb, mtd->min_io_size); warnmsg("treat eraseblock %d as corrupted", eb); si->corrupted_cnt += 1; si->ec[eb] = EB_CORRUPTED; continue; } } else { if ((int)be32_to_cpu(ech.vid_hdr_offset) != si->vid_hdr_offs) { if (pr) printf("\n"); if (v) printf(": corrupted because of the below\n"); warnmsg("inconsistent VID header offset: was " "%d, but is %d in eraseblock %d", si->vid_hdr_offs, be32_to_cpu(ech.vid_hdr_offset), eb); warnmsg("treat eraseblock %d as corrupted", eb); si->corrupted_cnt += 1; si->ec[eb] = EB_CORRUPTED; continue; } if ((int)be32_to_cpu(ech.data_offset) != si->data_offs) { if (pr) printf("\n"); if (v) printf(": corrupted because of the below\n"); warnmsg("inconsistent data offset: was %d, but" " is %d in eraseblock %d", si->data_offs, be32_to_cpu(ech.data_offset), eb); warnmsg("treat eraseblock %d as corrupted", eb); si->corrupted_cnt += 1; si->ec[eb] = EB_CORRUPTED; continue; } } si->ok_cnt += 1; si->ec[eb] = ec; if (v) printf(": OK, erase counter %u\n", si->ec[eb]); } if (si->ok_cnt != 0) { /* Calculate mean erase counter */ for (eb = 0; eb < mtd->eb_cnt; eb++) { if (si->ec[eb] > EC_MAX) continue; sum += si->ec[eb]; } si->mean_ec = sum / si->ok_cnt; } si->good_cnt = mtd->eb_cnt - si->bad_cnt; verbose(v, "finished, mean EC %lld, %d OK, %d corrupted, %d empty, %d " "alien, bad %d", si->mean_ec, si->ok_cnt, si->corrupted_cnt, si->empty_cnt, si->alien_cnt, si->bad_cnt); *info = si; if (pr) printf("\n"); return 0; out_ec: free(si->ec); out_si: free(si); *info = NULL; return -1; }
int main (int argc, char *argv[]) { libmtd_t mtd_desc; struct mtd_dev_info mtd; int fd, clmpos = 0, clmlen = 8, eb; int isNAND, bbtest = 1; int error = 0; uint64_t offset = 0; exe_name = argv[0]; for (;;) { int option_index = 0; static const char *short_options = "jq"; static const struct option long_options[] = { {"help", no_argument, 0, 0}, {"version", no_argument, 0, 0}, {"jffs2", no_argument, 0, 'j'}, {"quiet", no_argument, 0, 'q'}, {"silent", 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 0: switch (option_index) { case 0: display_help(); return 0; case 1: display_version(); return 0; } break; case 'q': quiet = 1; break; case 'j': jffs2 = 1; break; case '?': error = 1; break; } } if (optind == argc) { fprintf(stderr, "%s: no MTD device specified\n", exe_name); error = 1; } if (error) { fprintf(stderr, "Try `%s --help' for more information.\n", exe_name); return 1; } mtd_device = argv[optind]; mtd_desc = libmtd_open(); if (mtd_desc == NULL) { fprintf(stderr, "%s: can't initialize libmtd\n", exe_name); return 1; } if ((fd = open(mtd_device, O_RDWR)) < 0) { fprintf(stderr, "%s: %s: %s\n", exe_name, mtd_device, strerror(errno)); return 1; } if (mtd_get_dev_info(mtd_desc, mtd_device, &mtd) < 0) { fprintf(stderr, "%s: mtd_get_dev_info failed\n", exe_name); return 1; } isNAND = mtd.type == MTD_NANDFLASH ? 1 : 0; if (jffs2) { cleanmarker.magic = cpu_to_je16 (JFFS2_MAGIC_BITMASK); cleanmarker.nodetype = cpu_to_je16 (JFFS2_NODETYPE_CLEANMARKER); if (!isNAND) cleanmarker.totlen = cpu_to_je32 (sizeof (struct jffs2_unknown_node)); else { struct nand_oobinfo oobinfo; if (ioctl(fd, MEMGETOOBSEL, &oobinfo) != 0) { fprintf(stderr, "%s: %s: unable to get NAND oobinfo\n", exe_name, mtd_device); return 1; } /* Check for autoplacement */ if (oobinfo.useecc == MTD_NANDECC_AUTOPLACE) { /* Get the position of the free bytes */ if (!oobinfo.oobfree[0][1]) { fprintf (stderr, " Eeep. Autoplacement selected and no empty space in oob\n"); return 1; } clmpos = oobinfo.oobfree[0][0]; clmlen = oobinfo.oobfree[0][1]; if (clmlen > 8) clmlen = 8; } else { /* Legacy mode */ switch (mtd.oob_size) { case 8: clmpos = 6; clmlen = 2; break; case 16: clmpos = 8; clmlen = 8; break; case 64: clmpos = 16; clmlen = 8; break; } } cleanmarker.totlen = cpu_to_je32(8); } cleanmarker.hdr_crc = cpu_to_je32 (mtd_crc32 (0, &cleanmarker, sizeof (struct jffs2_unknown_node) - 4)); } for (eb = 0; eb < (mtd.size / mtd.eb_size); eb++) { offset = eb * mtd.eb_size; if (bbtest) { int ret = mtd_is_bad(&mtd, fd, eb); if (ret > 0) { if (!quiet) printf ("\nSkipping bad block at 0x%08llx\n", (unsigned long long)offset); continue; } else if (ret < 0) { if (errno == EOPNOTSUPP) { bbtest = 0; if (isNAND) { fprintf(stderr, "%s: %s: Bad block check not available\n", exe_name, mtd_device); return 1; } } else { fprintf(stderr, "\n%s: %s: MTD get bad block failed: %s\n", exe_name, mtd_device, strerror(errno)); return 1; } } } if (!quiet) show_progress(&mtd, offset); if (mtd_erase(mtd_desc, &mtd, fd, eb) != 0) { fprintf(stderr, "\n%s: %s: MTD Erase failure: %s\n", exe_name, mtd_device, strerror(errno)); continue; } /* format for JFFS2 ? */ if (!jffs2) continue; /* write cleanmarker */ if (isNAND) { if (mtd_write_oob(mtd_desc, &mtd, fd, offset + clmpos, clmlen, &cleanmarker) != 0) { fprintf(stderr, "\n%s: %s: MTD writeoob failure: %s\n", exe_name, mtd_device, strerror(errno)); continue; } } else { if (lseek (fd, (loff_t)offset, SEEK_SET) < 0) { fprintf(stderr, "\n%s: %s: MTD lseek failure: %s\n", exe_name, mtd_device, strerror(errno)); continue; } if (write (fd , &cleanmarker, sizeof (cleanmarker)) != sizeof (cleanmarker)) { fprintf(stderr, "\n%s: %s: MTD write failure: %s\n", exe_name, mtd_device, strerror(errno)); continue; } } if (!quiet) printf (" Cleanmarker written at %llx.", (unsigned long long)offset); } if (!quiet) { show_progress(&mtd, offset); printf("\n"); } return 0; }
int main(int argc, char **argv) { int status = EXIT_SUCCESS, i, ret, blk; process_options(argc, argv); mtd_desc = libmtd_open(); if (!mtd_desc) return errmsg("can't initialize libmtd"); if (mtd_get_dev_info(mtd_desc, mtddev, &mtd) < 0) return errmsg("mtd_get_dev_info failed"); if (mtd.subpage_size == 1) { puts("not NAND flash, assume page size is 512 bytes."); pgsize = 512; } else { pgsize = mtd.subpage_size; } pgcnt = mtd.eb_size / pgsize; if (count < 0) count = mtd.eb_cnt; if (peb >= mtd.eb_cnt) return errmsg("Physical erase block %d is out of range!\n", peb); if ((peb + (count - 1)*(skip + 1)) >= mtd.eb_cnt) { return errmsg("Given block range exceeds block count of %d!\n", mtd.eb_cnt); } iobuf = xmalloc(mtd.eb_size); iobuf1 = xmalloc(mtd.eb_size); if ((fd = open(mtddev, O_RDWR)) == -1) { perror(mtddev); status = EXIT_FAILURE; goto out; } /* Read all eraseblocks 1 page at a time */ puts("testing page read"); for (i = 0; i < count; ++i) { blk = peb + i*(skip+1); if (mtd_is_bad(&mtd, fd, blk)) { printf("Skipping bad block %d\n", blk); continue; } ret = read_eraseblock_by_page(blk); if (ret && (flags & FLAG_VERBOSE)) { dump_eraseblock(blk); status = EXIT_FAILURE; } } out: free(iobuf); free(iobuf1); return status; }
/* * Main program */ int main(int argc, char * const argv[]) { int cnt = 0; int fd = -1; int ifd = -1; int imglen = 0, pagelen; bool baderaseblock = false; long long blockstart = -1; struct mtd_dev_info mtd; long long offs; int ret; int oobinfochanged = 0; struct nand_oobinfo old_oobinfo; 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 *oobreadbuf = NULL; unsigned char *oobbuf = NULL; libmtd_t mtd_desc; int ebsize_aligned; process_options(argc, argv); if (pad && writeoob) { fprintf(stderr, "Can't pad when oob data is present.\n"); exit(EXIT_FAILURE); } /* Open the device */ if ((fd = open(mtd_device, O_RDWR)) == -1) { perror(mtd_device); exit(EXIT_FAILURE); } mtd_desc = libmtd_open(); if (!mtd_desc) return errmsg("can't initialize libmtd"); /* Fill in MTD device capability structure */ if (mtd_get_dev_info(mtd_desc, mtd_device, &mtd) < 0) return errmsg("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)) { fprintf(stderr, "The start address is not page-aligned !\n" "The pagesize of this NAND Flash is 0x%x.\n", mtd.min_io_size); close(fd); exit(EXIT_FAILURE); } if (autoplace) { /* Read the current oob info */ if (ioctl(fd, MEMGETOOBSEL, &old_oobinfo) != 0) { perror("MEMGETOOBSEL"); close(fd); exit(EXIT_FAILURE); } // autoplace ECC ? if (old_oobinfo.useecc != MTD_NANDECC_AUTOPLACE) { if (ioctl(fd, MEMSETOOBSEL, &autoplace_oobinfo) != 0) { perror("MEMSETOOBSEL"); close(fd); exit(EXIT_FAILURE); } oobinfochanged = 1; } } if (noecc) { ret = ioctl(fd, MTDFILEMODE, MTD_MODE_RAW); if (ret == 0) { oobinfochanged = 2; } else { switch (errno) { case ENOTTY: if (ioctl(fd, MEMGETOOBSEL, &old_oobinfo) != 0) { perror("MEMGETOOBSEL"); close(fd); exit(EXIT_FAILURE); } if (ioctl(fd, MEMSETOOBSEL, &none_oobinfo) != 0) { perror("MEMSETOOBSEL"); close(fd); exit(EXIT_FAILURE); } oobinfochanged = 1; break; default: perror("MTDFILEMODE"); close(fd); exit(EXIT_FAILURE); } } } /* * force oob layout for jffs2 or yaffs ? * Legacy support */ if (forcejffs2 || forceyaffs) { struct nand_oobinfo *oobsel = forcejffs2 ? &jffs2_oobinfo : &yaffs_oobinfo; if (autoplace) { fprintf(stderr, "Autoplacement is not possible for legacy -j/-y options\n"); goto restoreoob; } if ((old_oobinfo.useecc == MTD_NANDECC_AUTOPLACE) && !forcelegacy) { fprintf(stderr, "Use -f option to enforce legacy placement on autoplacement enabled mtd device\n"); goto restoreoob; } if (mtd.oob_size == 8) { if (forceyaffs) { fprintf(stderr, "YAFSS cannot operate on 256 Byte page size"); goto restoreoob; } /* Adjust number of ecc bytes */ jffs2_oobinfo.eccbytes = 3; } if (ioctl(fd, MEMSETOOBSEL, oobsel) != 0) { perror("MEMSETOOBSEL"); goto restoreoob; } } /* 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 restoreoob; } pagelen = mtd.min_io_size + ((writeoob) ? mtd.oob_size : 0); /* * For the standard input case, the input size is merely an * invariant placeholder and is set to the write page * size. Otherwise, just use the input file size. * * TODO: Add support for the -l,--length=length option (see * previous discussion by Tommi Airikka <*****@*****.**> at * <http://lists.infradead.org/pipermail/linux-mtd/2008-September/ * 022913.html> */ if (ifd == STDIN_FILENO) { imglen = pagelen; } else { imglen = lseek(ifd, 0, SEEK_END); lseek(ifd, 0, SEEK_SET); } // 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 %d bytes, NAND page %d bytes, OOB area %d" " bytes, device size %lld bytes\n", imglen, pagelen, mtd.oob_size, mtd.size); perror("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); oobbuf = xmalloc(mtd.oob_size); erase_buffer(oobbuf, mtd.oob_size); /* * 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 { if ((ret = mtd_is_bad(&mtd, fd, offs / ebsize_aligned)) < 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; } 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)) { int readlen = mtd.min_io_size; int alreadyread = (filebuf + filebuf_len) - writebuf; int tinycnt = alreadyread; 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 " "%d 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) { oobreadbuf = writebuf + mtd.min_io_size; // Read more data for the OOB from the input if there isn't enough in the buffer if ((oobreadbuf + mtd.oob_size) > (filebuf + filebuf_len)) { int readlen = mtd.oob_size; int alreadyread = (filebuf + filebuf_len) - oobreadbuf; int tinycnt = alreadyread; while (tinycnt < readlen) { cnt = read(ifd, oobreadbuf + 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 " "%d 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; } } if (!noecc) { int i, start, len; int tags_pos = 0; /* * We use autoplacement and have the oobinfo with the autoplacement * information from the kernel available * * Modified to support out of order oobfree segments, * such as the layout used by diskonchip.c */ if (!oobinfochanged && (old_oobinfo.useecc == MTD_NANDECC_AUTOPLACE)) { for (i = 0; old_oobinfo.oobfree[i][1]; i++) { /* Set the reserved bytes to 0xff */ start = old_oobinfo.oobfree[i][0]; len = old_oobinfo.oobfree[i][1]; if (rawoob) memcpy(oobbuf + start, oobreadbuf + start, len); else memcpy(oobbuf + start, oobreadbuf + tags_pos, len); tags_pos += len; } } else { /* Set at least the ecc byte positions to 0xff */ start = old_oobinfo.eccbytes; len = mtd.oob_size - start; memcpy(oobbuf + start, oobreadbuf + start, len); } } /* Write OOB data first, as ecc will be placed in there */ if (mtd_write_oob(mtd_desc, &mtd, fd, mtdoffset, mtd.oob_size, noecc ? oobreadbuf : oobbuf)) { sys_errmsg("%s: MTD writeoob failure", mtd_device); goto closeall; } } /* Write out the Page data */ if (mtd_write(&mtd, fd, mtdoffset / mtd.eb_size, mtdoffset % mtd.eb_size, writebuf, mtd.min_io_size)) { int 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, 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); restoreoob: libmtd_close(mtd_desc); free(filebuf); free(oobbuf); if (oobinfochanged == 1) { if (ioctl(fd, MEMSETOOBSEL, &old_oobinfo) != 0) { perror("MEMSETOOBSEL"); close(fd); exit(EXIT_FAILURE); } } close(fd); if (failed || ((ifd != STDIN_FILENO) && (imglen > 0)) || (writebuf < (filebuf + filebuf_len))) { perror("Data was only partially written due to error\n"); exit(EXIT_FAILURE); } /* Return happy */ return EXIT_SUCCESS; }
int main(int argc, char *argv[]) { libmtd_t mtd_desc; struct mtd_dev_info mtd; int fd, clmpos = 0, clmlen = 8; unsigned long long start; unsigned int eb, eb_start, eb_cnt; int isNAND; int error = 0; off_t offset = 0; /* * Process user arguments */ for (;;) { int option_index = 0; static const char *short_options = "jNqu"; static const struct option long_options[] = { {"help", no_argument, 0, 0}, {"version", no_argument, 0, 0}, {"jffs2", no_argument, 0, 'j'}, {"noskipbad", no_argument, 0, 'N'}, {"quiet", no_argument, 0, 'q'}, {"silent", no_argument, 0, 'q'}, {"unlock", no_argument, 0, 'u'}, {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: display_help(); return 0; case 1: display_version(); return 0; } break; case 'j': jffs2 = 1; break; case 'N': noskipbad = 1; break; case 'q': quiet = 1; break; case 'u': unlock = 1; break; case '?': error = 1; break; } } switch (argc - optind) { case 3: mtd_device = argv[optind]; start = simple_strtoull(argv[optind + 1], &error); eb_cnt = simple_strtoul(argv[optind + 2], &error); break; default: case 0: errmsg("no MTD device specified"); case 1: errmsg("no start erase block specified"); case 2: errmsg("no erase block count specified"); error = 1; break; } if (error) return errmsg("Try `--help' for more information"); /* * Locate MTD and prepare for erasure */ mtd_desc = libmtd_open(); if (mtd_desc == NULL) return errmsg("can't initialize libmtd"); if ((fd = open(mtd_device, O_RDWR)) < 0) return sys_errmsg("%s", mtd_device); if (mtd_get_dev_info(mtd_desc, mtd_device, &mtd) < 0) return errmsg("mtd_get_dev_info failed"); eb_start = start / mtd.eb_size; isNAND = mtd.type == MTD_NANDFLASH ? 1 : 0; if (jffs2) { cleanmarker.magic = cpu_to_je16 (JFFS2_MAGIC_BITMASK); cleanmarker.nodetype = cpu_to_je16 (JFFS2_NODETYPE_CLEANMARKER); if (!isNAND) cleanmarker.totlen = cpu_to_je32(sizeof(cleanmarker)); else { struct nand_oobinfo oobinfo; if (ioctl(fd, MEMGETOOBSEL, &oobinfo) != 0) return sys_errmsg("%s: unable to get NAND oobinfo", mtd_device); /* Check for autoplacement */ if (oobinfo.useecc == MTD_NANDECC_AUTOPLACE) { /* Get the position of the free bytes */ if (!oobinfo.oobfree[0][1]) return errmsg(" Eeep. Autoplacement selected and no empty space in oob"); clmpos = oobinfo.oobfree[0][0]; clmlen = oobinfo.oobfree[0][1]; if (clmlen > 8) clmlen = 8; } else { /* Legacy mode */ switch (mtd.oob_size) { case 8: clmpos = 6; clmlen = 2; break; case 16: clmpos = 8; clmlen = 8; break; case 64: clmpos = 16; clmlen = 8; break; } } cleanmarker.totlen = cpu_to_je32(8); } cleanmarker.hdr_crc = cpu_to_je32(mtd_crc32(0, &cleanmarker, sizeof(cleanmarker) - 4)); } /* * Now do the actual erasing of the MTD device */ if (eb_cnt == 0) eb_cnt = (mtd.size / mtd.eb_size) - eb_start; for (eb = eb_start; eb < eb_start + eb_cnt; eb++) { offset = (off_t)eb * mtd.eb_size; if (!noskipbad) { int ret = mtd_is_bad(&mtd, fd, eb); if (ret > 0) { verbose(!quiet, "Skipping bad block at %08"PRIxoff_t, offset); continue; } else if (ret < 0) { if (errno == EOPNOTSUPP) { noskipbad = 1; if (isNAND) return errmsg("%s: Bad block check not available", mtd_device); } else return sys_errmsg("%s: MTD get bad block failed", mtd_device); } } show_progress(&mtd, offset, eb, eb_start, eb_cnt); if (unlock) { if (mtd_unlock(&mtd, fd, eb) != 0) { sys_errmsg("%s: MTD unlock failure", mtd_device); continue; } } if (mtd_erase(mtd_desc, &mtd, fd, eb) != 0) { sys_errmsg("%s: MTD Erase failure", mtd_device); continue; } /* format for JFFS2 ? */ if (!jffs2) continue; /* write cleanmarker */ if (isNAND) { if (mtd_write_oob(mtd_desc, &mtd, fd, (uint64_t)offset + clmpos, clmlen, &cleanmarker) != 0) { sys_errmsg("%s: MTD writeoob failure", mtd_device); continue; } } else { if (lseek(fd, offset, SEEK_SET) < 0) { sys_errmsg("%s: MTD lseek failure", mtd_device); continue; } if (write(fd, &cleanmarker, sizeof(cleanmarker)) != sizeof(cleanmarker)) { sys_errmsg("%s: MTD write failure", mtd_device); continue; } } verbose(!quiet, " Cleanmarker written at %"PRIxoff_t, offset); } show_progress(&mtd, offset, eb, eb_start, eb_cnt); bareverbose(!quiet, "\n"); return 0; }