static int fio_mtd_open_file(struct thread_data *td, struct fio_file *f) { struct fio_mtd_data *fmd; int ret; ret = generic_open_file(td, f); if (ret) return ret; fmd = calloc(1, sizeof(*fmd)); if (!fmd) goto err_close; ret = mtd_get_dev_info(desc, f->file_name, &fmd->info); if (ret != 0) { td_verror(td, errno, "mtd_get_dev_info"); goto err_free; } FILE_SET_ENG_DATA(f, fmd); return 0; err_free: free(fmd); err_close: { int fio_unused __ret; __ret = generic_close_file(td, f); return 1; } }
static libmtd_t _libmtd_init( struct mtd_dev_info *info, const char * dev ) { int err; libmtd_t ret = libmtd_open(); if ( NULL == ret ) { if ( 0 == errno ) { bc_log( LOG_ERR, "No MTD support available on this system " "(libmtd_open returned NULL, errno is 0).\n" ); } else { bc_log( LOG_ERR, "Error %d initialising libmtd: %s.\n", errno, strerror(errno) ); } exit(1); } err = mtd_get_dev_info( ret, dev, info ); if ( 0 > err ) { bc_log( LOG_ERR, "Error %d reading device info of %s: %s.\n", errno, dev, strerror(errno) ); exit(1); } return ret; }
int fio_mtd_get_file_size(struct thread_data *td, struct fio_file *f) { struct mtd_dev_info info; int ret = mtd_get_dev_info(desc, f->file_name, &info); if (ret != 0) { td_verror(td, errno, "mtd_get_dev_info"); return errno; } f->real_file_size = info.size; return 0; }
static int translate_dev(libmtd_t libmtd, const char *node) { int err; struct mtd_dev_info mtd; err = mtd_get_dev_info(libmtd, node, &mtd); if (err) { if (errno == ENODEV) return errmsg("\"%s\" does not correspond to any " "existing MTD device", node); return sys_errmsg("cannot get information about MTD " "device \"%s\"", node); } return mtd.mtd_num; }
int do_ubiformat(int argc, char *argv[]) { int err, verbose; struct mtd_dev_info mtd; struct ubigen_info ui; struct ubi_scan_info *si; err = parse_opt(argc, argv); if (err) return err; err = mtd_get_dev_info(args.node, &mtd); if (err) { sys_errmsg("cannot get information about \"%s\"", args.node); goto out_close_mtd; } if (!is_power_of_2(mtd.min_io_size)) { errmsg("min. I/O size is %d, but should be power of 2", mtd.min_io_size); goto out_close_mtd; } if (args.subpage_size && args.subpage_size != mtd.subpage_size) { mtd.subpage_size = args.subpage_size; args.manual_subpage = 1; } if (args.manual_subpage) { /* Do some sanity check */ if (args.subpage_size > mtd.min_io_size) { errmsg("sub-page cannot be larger than min. I/O unit"); goto out_close_mtd; } if (mtd.min_io_size % args.subpage_size) { errmsg("min. I/O unit size should be multiple of " "sub-page size"); goto out_close_mtd; } } args.node_fd = open(args.node, O_RDWR); if (args.node_fd < 0) { sys_errmsg("cannot open \"%s\"", args.node); goto out_close_mtd; } /* Validate VID header offset if it was specified */ if (args.vid_hdr_offs != 0) { if (args.vid_hdr_offs % 8) { errmsg("VID header offset has to be multiple of min. I/O unit size"); goto out_close; } if (args.vid_hdr_offs + (int)UBI_VID_HDR_SIZE > mtd.eb_size) { errmsg("bad VID header offset"); goto out_close; } } if (!mtd.writable) { errmsg("%s (%s) is a read-only device", mtd.node, args.node); goto out_close; } /* Make sure this MTD device is not attached to UBI */ /* FIXME! Find a proper way to do this in barebox! */ if (!args.quiet) { normsg_cont("%s (%s), size %lld bytes (%s)", mtd.node, mtd.type_str, mtd.size, size_human_readable(mtd.size)); printf(", %d eraseblocks of %d bytes (%s)", mtd.eb_cnt, mtd.eb_size, size_human_readable(mtd.eb_size)); printf(", min. I/O size %d bytes\n", mtd.min_io_size); } if (args.quiet) verbose = 0; else if (args.verbose) verbose = 2; else verbose = 1; err = libscan_ubi_scan(&mtd, args.node_fd, &si, verbose); if (err) { errmsg("failed to scan %s (%s)", mtd.node, args.node); goto out_close; } if (si->good_cnt == 0) { errmsg("all %d eraseblocks are bad", si->bad_cnt); goto out_free; } if (si->good_cnt < 2 && (!args.novtbl || args.image)) { errmsg("too few non-bad eraseblocks (%d) on %s", si->good_cnt, mtd.node); goto out_free; } if (!args.quiet) { if (si->ok_cnt) normsg("%d eraseblocks have valid erase counter, mean value is %lld", si->ok_cnt, si->mean_ec); if (si->empty_cnt) normsg("%d eraseblocks are supposedly empty", si->empty_cnt); if (si->corrupted_cnt) normsg("%d corrupted erase counters", si->corrupted_cnt); print_bad_eraseblocks(&mtd, si); } if (si->alien_cnt) { if (!args.quiet) warnmsg("%d of %d eraseblocks contain non-ubifs data", si->alien_cnt, si->good_cnt); if (!args.yes && !args.quiet) warnmsg("use '-y' to force erasing"); if (!args.yes) goto out_free; } if (!args.override_ec && si->empty_cnt < si->good_cnt) { int percent = (si->ok_cnt * 100) / si->good_cnt; /* * Make sure the majority of eraseblocks have valid * erase counters. */ if (percent < 50) { if (!args.quiet) { warnmsg("only %d of %d eraseblocks have valid erase counter", si->ok_cnt, si->good_cnt); if (args.yes) { normsg("erase counter 0 will be used for all eraseblocks"); normsg("note, arbitrary erase counter value may be specified using -e option"); } else { warnmsg("use '-y' to force erase counters"); } } if (!args.yes) goto out_free; args.ec = 0; args.override_ec = 1; } else if (percent < 95) { if (!args.quiet) { warnmsg("only %d of %d eraseblocks have valid erase counter", si->ok_cnt, si->good_cnt); if (args.yes) normsg("mean erase counter %lld will be used for the rest of eraseblock", si->mean_ec); else warnmsg("use '-y' to force erase counters"); } if (!args.yes) goto out_free; args.ec = si->mean_ec; args.override_ec = 1; } } if (!args.quiet && args.override_ec) normsg("use erase counter %lld for all eraseblocks", args.ec); ubigen_info_init(&ui, mtd.eb_size, mtd.min_io_size, mtd.subpage_size, args.vid_hdr_offs, args.ubi_ver, args.image_seq); if (si->vid_hdr_offs != -1 && ui.vid_hdr_offs != si->vid_hdr_offs) { /* * Hmm, what we read from flash and what we calculated using * min. I/O unit size and sub-page size differs. */ if (!args.quiet) { warnmsg("VID header and data offsets on flash are %d and %d, " "which is different to requested offsets %d and %d", si->vid_hdr_offs, si->data_offs, ui.vid_hdr_offs, ui.data_offs); normsg("using offsets %d and %d", ui.vid_hdr_offs, ui.data_offs); } } if (args.image) { err = flash_image(&mtd, &ui, si); if (err < 0) goto out_free; err = format(&mtd, &ui, si, err, 1); if (err) goto out_free; } else { err = format(&mtd, &ui, si, 0, args.novtbl); if (err) goto out_free; } libscan_ubi_scan_free(si); close(args.node_fd); return 0; out_free: libscan_ubi_scan_free(si); out_close: close(args.node_fd); out_close_mtd: return 1; }
int main(int argc, char * const argv[]) { int err, verbose; libmtd_t libmtd; struct mtd_info mtd_info; struct mtd_dev_info mtd; libubi_t libubi; struct ubigen_info ui; struct ubi_scan_info *si; libmtd = libmtd_open(); if (!libmtd) return errmsg("MTD subsystem is not present"); err = parse_opt(argc, argv); if (err) goto out_close_mtd; err = mtd_get_info(libmtd, &mtd_info); if (err) { if (errno == ENODEV) errmsg("MTD is not present"); sys_errmsg("cannot get MTD information"); goto out_close_mtd; } err = mtd_get_dev_info(libmtd, args.node, &mtd); if (err) { sys_errmsg("cannot get information about \"%s\"", args.node); goto out_close_mtd; } if (!is_power_of_2(mtd.min_io_size)) { errmsg("min. I/O size is %d, but should be power of 2", mtd.min_io_size); goto out_close; } if (!mtd_info.sysfs_supported) { /* * Linux kernels older than 2.6.30 did not support sysfs * interface, and it is impossible to find out sub-page * size in these kernels. This is why users should * provide -s option. */ if (args.subpage_size == 0) { warnmsg("your MTD system is old and it is impossible " "to detect sub-page size. Use -s to get rid " "of this warning"); normsg("assume sub-page to be %d", mtd.subpage_size); } else { mtd.subpage_size = args.subpage_size; args.manual_subpage = 1; } } else if (args.subpage_size && args.subpage_size != mtd.subpage_size) { mtd.subpage_size = args.subpage_size; args.manual_subpage = 1; } if (args.manual_subpage) { /* Do some sanity check */ if (args.subpage_size > mtd.min_io_size) { errmsg("sub-page cannot be larger than min. I/O unit"); goto out_close; } if (mtd.min_io_size % args.subpage_size) { errmsg("min. I/O unit size should be multiple of " "sub-page size"); goto out_close; } } args.node_fd = open(args.node, O_RDWR); if (args.node_fd == -1) { sys_errmsg("cannot open \"%s\"", args.node); goto out_close_mtd; } /* Validate VID header offset if it was specified */ if (args.vid_hdr_offs != 0) { if (args.vid_hdr_offs % 8) { errmsg("VID header offset has to be multiple of min. I/O unit size"); goto out_close; } if (args.vid_hdr_offs + (int)UBI_VID_HDR_SIZE > mtd.eb_size) { errmsg("bad VID header offset"); goto out_close; } } if (!mtd.writable) { errmsg("mtd%d (%s) is a read-only device", mtd.mtd_num, args.node); goto out_close; } /* Make sure this MTD device is not attached to UBI */ libubi = libubi_open(); if (libubi) { int ubi_dev_num; err = mtd_num2ubi_dev(libubi, mtd.mtd_num, &ubi_dev_num); libubi_close(libubi); if (!err) { errmsg("please, first detach mtd%d (%s) from ubi%d", mtd.mtd_num, args.node, ubi_dev_num); goto out_close; } } if (!args.quiet) { normsg_cont("mtd%d (%s), size ", mtd.mtd_num, mtd.type_str); ubiutils_print_bytes(mtd.size, 1); printf(", %d eraseblocks of ", mtd.eb_cnt); ubiutils_print_bytes(mtd.eb_size, 1); printf(", min. I/O size %d bytes\n", mtd.min_io_size); } if (args.quiet) verbose = 0; else if (args.verbose) verbose = 2; else verbose = 1; err = ubi_scan(&mtd, args.node_fd, &si, verbose); if (err) { errmsg("failed to scan mtd%d (%s)", mtd.mtd_num, args.node); goto out_close; } if (si->good_cnt == 0) { errmsg("all %d eraseblocks are bad", si->bad_cnt); goto out_free; } if (si->good_cnt < 2 && (!args.novtbl || args.image)) { errmsg("too few non-bad eraseblocks (%d) on mtd%d", si->good_cnt, mtd.mtd_num); goto out_free; } if (!args.quiet) { if (si->ok_cnt) normsg("%d eraseblocks have valid erase counter, mean value is %lld", si->ok_cnt, si->mean_ec); if (si->empty_cnt) normsg("%d eraseblocks are supposedly empty", si->empty_cnt); if (si->corrupted_cnt) normsg("%d corrupted erase counters", si->corrupted_cnt); print_bad_eraseblocks(&mtd, si); } if (si->alien_cnt) { if (!args.yes || !args.quiet) warnmsg("%d of %d eraseblocks contain non-ubifs data", si->alien_cnt, si->good_cnt); if (!args.yes && want_exit()) { if (args.yes && !args.quiet) printf("yes\n"); goto out_free; } } if (!args.override_ec && si->empty_cnt < si->good_cnt) { int percent = ((double)si->ok_cnt)/si->good_cnt * 100; /* * Make sure the majority of eraseblocks have valid * erase counters. */ if (percent < 50) { if (!args.yes || !args.quiet) warnmsg("only %d of %d eraseblocks have valid erase counter", si->ok_cnt, si->good_cnt); normsg("erase counter 0 will be used for all eraseblocks"); normsg("note, arbitrary erase counter value may be specified using -e option"); if (!args.yes && want_exit()) { if (args.yes && !args.quiet) printf("yes\n"); goto out_free; } args.ec = 0; args.override_ec = 1; } else if (percent < 95) { if (!args.yes || !args.quiet) warnmsg("only %d of %d eraseblocks have valid erase counter", si->ok_cnt, si->good_cnt); normsg("mean erase counter %lld will be used for the rest of eraseblock", si->mean_ec); if (!args.yes && want_exit()) { if (args.yes && !args.quiet) printf("yes\n"); goto out_free; } args.ec = si->mean_ec; args.override_ec = 1; } } if (!args.quiet && args.override_ec) normsg("use erase counter %lld for all eraseblocks", args.ec); ubigen_info_init(&ui, mtd.eb_size, mtd.min_io_size, mtd.subpage_size, args.vid_hdr_offs, args.ubi_ver, args.image_seq); if (si->vid_hdr_offs != -1 && ui.vid_hdr_offs != si->vid_hdr_offs) { /* * Hmm, what we read from flash and what we calculated using * min. I/O unit size and sub-page size differs. */ if (!args.yes || !args.quiet) { warnmsg("VID header and data offsets on flash are %d and %d, " "which is different to requested offsets %d and %d", si->vid_hdr_offs, si->data_offs, ui.vid_hdr_offs, ui.data_offs); normsg_cont("use new offsets %d and %d? (yes/no) ", ui.vid_hdr_offs, ui.data_offs); } if (args.yes || answer_is_yes()) { if (args.yes && !args.quiet) printf("yes\n"); } else ubigen_info_init(&ui, mtd.eb_size, mtd.min_io_size, 0, si->vid_hdr_offs, args.ubi_ver, args.image_seq); normsg("use offsets %d and %d", ui.vid_hdr_offs, ui.data_offs); } if (args.image) { err = flash_image(libmtd, &mtd, &ui, si); if (err < 0) goto out_free; err = format(libmtd, &mtd, &ui, si, err, 1); if (err) goto out_free; } else { err = format(libmtd, &mtd, &ui, si, 0, args.novtbl); if (err) goto out_free; } ubi_scan_free(si); close(args.node_fd); libmtd_close(libmtd); return 0; out_free: ubi_scan_free(si); out_close: close(args.node_fd); out_close_mtd: libmtd_close(libmtd); return -1; }
/* * 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 main(int argc, char * const argv[]) { int devfd, datafd, num_blocks, block; off_t file_size; libmtd_t mtd_desc; struct mtd_dev_info devinfo; uint8_t *blockbuf; char response[8]; if (argc != 3) { printf("usage: %s <image file> <mtd dev node>\n", argv[0]); return -EINVAL; } mtd_desc = libmtd_open(); if (mtd_desc == NULL) { int errsv = errno; fprintf(stderr, "can't initialize libmtd\n"); return -errsv; } /* open the spl image file and mtd device */ datafd = open(argv[1], O_RDONLY); if (datafd == -1) { int errsv = errno; perror(argv[1]); return -errsv; } devfd = open(argv[2], O_WRONLY); if (devfd == -1) { int errsv = errno; perror(argv[2]); return -errsv; } if (mtd_get_dev_info(mtd_desc, argv[2], &devinfo) < 0) { int errsv = errno; perror(argv[2]); return -errsv; } /* determine the number of blocks needed by the image */ file_size = lseek(datafd, 0, SEEK_END); if (file_size == (off_t)-1) { int errsv = errno; perror("lseek"); return -errsv; } num_blocks = (file_size + RELIABLE_BLOCKSIZE - 1) / RELIABLE_BLOCKSIZE; file_size = lseek(datafd, 0, SEEK_SET); if (file_size == (off_t)-1) { int errsv = errno; perror("lseek"); return -errsv; } printf("The mtd partition contains %d blocks\n", devinfo.eb_cnt); printf("U-Boot will occupy %d blocks\n", num_blocks); if (num_blocks > devinfo.eb_cnt) { fprintf(stderr, "Insufficient blocks on partition\n"); return -EINVAL; } printf("IMPORTANT: These blocks must be in an erased state!\n"); printf("Do you want to proceed?\n"); scanf("%s", response); if ((response[0] != 'y') && (response[0] != 'Y')) { printf("Exiting\n"); close(devfd); close(datafd); return 0; } blockbuf = calloc(RELIABLE_BLOCKSIZE, 1); if (blockbuf == NULL) { int errsv = errno; perror("calloc"); return -errsv; } for (block = 0; block < num_blocks; block++) { int ofs, page; uint8_t *pagebuf = blockbuf, *buf = blockbuf; uint8_t *oobbuf = page0_oob; /* magic num in oob of 1st page */ size_t len = RELIABLE_BLOCKSIZE; int ret; /* read data for one block from file */ while (len) { ssize_t read_ret = read(datafd, buf, len); if (read_ret == -1) { int errsv = errno; if (errno == EINTR) continue; perror("read"); return -errsv; } else if (read_ret == 0) { break; /* EOF */ } len -= read_ret; buf += read_ret; } printf("Block %d: writing\r", block + 1); fflush(stdout); for (page = 0, ofs = 0; page < PAGES_PER_BLOCK; page++, ofs += PAGESIZE) { if (page & 0x04) /* Odd-numbered 2k page */ continue; /* skipped in reliable mode */ ret = mtd_write(mtd_desc, &devinfo, devfd, block, ofs, pagebuf, PAGESIZE, oobbuf, OOBSIZE, MTD_OPS_PLACE_OOB); if (ret) { fprintf(stderr, "\nmtd_write returned %d on block %d, ofs %x\n", ret, block + 1, ofs); return -EIO; } oobbuf = ff_oob; /* oob for subsequent pages */ if (page & 0x01) /* odd-numbered subpage */ pagebuf += PAGESIZE; } } printf("\nDone\n"); close(devfd); close(datafd); free(blockbuf); return 0; }
/* * Main program */ int nandwrite_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; long long ofg_imglen = 1; process_options(argc, argv); /* Open the device */ if ((fd = open(mtd_device, O_RDWR)) == -1) { sys_errmsg("%s", mtd_device); return -1; } mtd_desc = libmtd_open(); if (!mtd_desc) { errmsg("can't initialize libmtd"); return -1; } /* Fill in MTD device capability structure */ if (mtd_get_dev_info(mtd_desc, mtd_device, &mtd) < 0) { errmsg("mtd_get_dev_info failed"); return -1; } /* * 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("The start address is not page-aligned !\n" "The pagesize of this NAND Flash is 0x%x.\n", mtd.min_io_size); return -1; } /* 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("ioctl MTDFILEMODE is missing"); return -1; default: sys_errmsg("MTDFILEMODE"); return -1; } } } /* 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 {
/** * @brief NAND FLASH的读写测试,把DDR中一段数据写到NAND FLASH中,接着 读到DDR的另一空间,进行比较。每个block单独测试。 * @retval S_OK表示SPI FLASH测试成功. * @retval E_FAIL表示硬件底层操作出错. */ HRESULT CTester::TestFlash() { /* 读写NAND FLASH 的block,进行测试: ① 待测的NAND FLASH容量为512K字节(FLASH总容量为512MB,测试保留区域), 测试时将测试区域划分为4个block,每个block为64K字(128KB); ② 先对被测block进行擦除操作, 然后往block中的每个单元(16位,每个block有64K个16位单元)写入其对应的地址偏移量。 写完一个block后,把block内的每个单元读出来跟写入的数据进行比较。 */ struct mtd_dev_info mtd = { 0,0,0,0,"","",0,0,0,0,0,0,0,0,0 }; libmtd_t mtd_desc; static const char *mtd_device = "/dev/mtd11"; //测试专用分区 int fd = -1; int ret = S_OK; unsigned char write_mode = MTD_OPS_RAW; //const DWORD dwTotalSize = 256*1024*1024; const int pagelen = 2048; //?定值? const DWORD blockSize = pagelen * 64; //const DWORD dwTestblockstart = 0xFD80000; //硬件地址? //const DWORD dwTestAddr = 0xFD80000; const DWORD dwTestblockstart = 0x0; //分区开始 const DWORD dwTestAddr = 0x0; const DWORD dwTestSize = 512*1024;//512KByte //DWORD dwLen = dwTestSize; DWORD dwblockCount = dwTestSize/blockSize; DWORD mtdoffset = 0; //unsigned char writebuf[128 *1024]; //unsigned char readbuf[128 *1024]; unsigned char *writebuf = (unsigned char *)swpa_mem_alloc(128 *1024); if (NULL == writebuf) { SW_TRACE_NORMAL("Err: malloc write buf failed!"); return E_OUTOFMEMORY; } unsigned char *readbuf = (unsigned char *)swpa_mem_alloc(128 *1024); if (NULL == readbuf) { SW_TRACE_NORMAL("Err: malloc read buf failed!"); SAFE_MEM_FREE(writebuf); return E_OUTOFMEMORY; } unsigned short *wr_ptr16 = (unsigned short* )(writebuf); //unsigned short *rd_ptr16 = (unsigned short* )(readbuf); unsigned int i = 0,j=0; //create test data for(i = 0; i < ( 64 * 1024); i++) { wr_ptr16[i] = i; } SW_TRACE_NORMAL("Info: Testing NAND Flash...............\n"); /* Open the device */ if ((fd = open(mtd_device, O_RDWR)) < 0) { SW_TRACE_NORMAL("%s\n", mtd_device); //close(fd); SAFE_MEM_FREE(readbuf); SAFE_MEM_FREE(writebuf); return E_FAIL; } mtd_desc = libmtd_open(); if (!mtd_desc) { SW_TRACE_NORMAL("can't initialize libmtd\n"); //libmtd_close(mtd_desc); close(fd); SAFE_MEM_FREE(readbuf); SAFE_MEM_FREE(writebuf); return E_FAIL; } /* Fill in MTD device capability structure */ if (mtd_get_dev_info(mtd_desc, mtd_device, &mtd) < 0) { SW_TRACE_NORMAL("mtd_get_dev_info failed"); libmtd_close(mtd_desc); close(fd); SAFE_MEM_FREE(readbuf); SAFE_MEM_FREE(writebuf); return E_FAIL; } SW_TRACE_NORMAL("Info: mtd oobsize %d,subpagesize %d,ebsize %d,miniosize %d\n", mtd.oob_size,mtd.subpage_size,mtd.eb_size,mtd.min_io_size); //erase block //@eb_size: eraseblock size for (i = dwTestblockstart; i < dwTestblockstart + dwTestSize; i += mtd.eb_size) { //skip bad? if (mtd_erase(mtd_desc, &mtd, fd, i / mtd.eb_size)) { SW_TRACE_NORMAL("%s: MTD Erase failure\n", mtd_device); ret = E_FAIL; goto closeall; } } // begin test NAND FLASH write and read. for(i = 0 ; i < dwblockCount; i++) { if (GetForceStopFlag()) { return S_OK; } /* write test data to a block*/ j = 0; mtdoffset = dwTestAddr + blockSize * i; while ( j < blockSize) { //@min_io_size: minimum input/output unit size if( mtd_write(mtd_desc, &mtd, fd, mtdoffset / mtd.eb_size, mtdoffset % mtd.eb_size,&writebuf[j],mtd.min_io_size,NULL,0,write_mode) ) { SW_TRACE_NORMAL("%s: MTD write failure\n", mtd_device); ret = E_FAIL; goto closeall; } mtdoffset += mtd.min_io_size; j += pagelen; } /* read test data from a block*/ memset(readbuf, 0x00, blockSize); j = 0; mtdoffset = dwTestAddr + blockSize * i; while ( j < blockSize) { if( mtd_read(&mtd, fd, mtdoffset / mtd.eb_size, mtdoffset % mtd.eb_size, &readbuf[j], mtd.min_io_size)) { SW_TRACE_NORMAL("MTD read failure.\n"); ret = E_FAIL; goto closeall; } mtdoffset += mtd.min_io_size; j += pagelen; } if (GetForceStopFlag()) { return S_OK; } /* cheeck the data */ for(j = 0 ; j < blockSize ; j++) { if( readbuf[j] != writebuf[j] ) { SW_TRACE_NORMAL("data check failure.\n"); ret = E_FAIL; goto closeall; } } } closeall: libmtd_close(mtd_desc); close(fd); SAFE_MEM_FREE(readbuf); SAFE_MEM_FREE(writebuf); swpa_utils_shell("flash_eraseall /dev/mtd11", NULL); if(ret == E_FAIL) { SW_TRACE_NORMAL("Info: NAND Flash Test -- NG!\n"); }else{ SW_TRACE_NORMAL("Info: NAND Flash Test -- OK!\n"); } return ret; }
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; }