Пример #1
0
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");
	}
}
Пример #2
0
Файл: mtd.c Проект: rainmeng/fio
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;
}
Пример #3
0
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;
}
Пример #4
0
/* 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;
}
Пример #5
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;
}
Пример #6
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;
}
Пример #7
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);
}
Пример #8
0
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;
}
Пример #9
0
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;
}
Пример #10
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;
}
Пример #11
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;
}
Пример #12
0
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;
}