/* TODO: we should actually torture the PEB before marking it as bad */ static int mark_bad(const struct mtd_dev_info *mtd, struct ubi_scan_info *si, int eb) { int err; if (!args.yes) { normsg_cont("mark it as bad? Continue (yes/no) "); if (!answer_is_yes()) return -1; } if (!args.quiet) normsg_cont("marking block %d bad", eb); if (!args.quiet) printf("\n"); if (!mtd->bb_allowed) { if (!args.quiet) printf("\n"); return errmsg("bad blocks not supported by this flash"); } err = mtd_mark_bad(mtd, args.node_fd, eb); if (err) return err; si->bad_cnt += 1; si->ec[eb] = EB_BAD; return consecutive_bad_check(eb); }
static int fio_mtd_maybe_mark_bad(struct thread_data *td, struct fio_mtd_data *fmd, struct io_u *io_u, int eb) { int ret; if (errno == EIO) { ret = mtd_mark_bad(&fmd->info, io_u->file->fd, eb); if (ret != 0) { io_u->error = errno; td_verror(td, errno, "mtd_mark_bad"); return -1; } } return 0; }
static void test_mtd_mark_bad(void **state) { struct mtd_dev_info mtd; loff_t seek; int eb = 12; 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(MEMSETBADBLOCK, 0, &seek); int r = mtd_mark_bad(&mtd, 4, eb); assert_int_equal(r, 0); (void) state; }
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[]) { 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; }