Exemplo n.º 1
0
static int write_eraseblock(int ebnum)
{
	int i;
	struct mtd_oob_ops ops;
	int err = 0;
	loff_t addr = (loff_t)ebnum * mtd->erasesize;

	prandom_bytes_state(&rnd_state, writebuf, use_len_max * pgcnt);
	for (i = 0; i < pgcnt; ++i, addr += mtd->writesize) {
		ops.mode      = MTD_OPS_AUTO_OOB;
		ops.len       = 0;
		ops.retlen    = 0;
		ops.ooblen    = use_len;
		ops.oobretlen = 0;
		ops.ooboffs   = use_offset;
		ops.datbuf    = NULL;
		ops.oobbuf    = writebuf + (use_len_max * i) + use_offset;
		err = mtd_write_oob(mtd, addr, &ops);
		if (err || ops.oobretlen != use_len) {
			pr_err("error: writeoob failed at %#llx\n",
			       (long long)addr);
			pr_err("error: use_len %d, use_offset %d\n",
			       use_len, use_offset);
			errcnt += 1;
			return err ? err : -1;
		}
		if (vary_offset)
			do_vary_offset();
	}

	return err;
}
Exemplo n.º 2
0
static int sm_block_markbad(struct mtd_info *mtd, loff_t ofs)
{
	struct mtd_oob_ops ops;
	struct sm_oob oob;
	int ret, error = 0;

	memset(&oob, -1, SM_OOB_SIZE);
	oob.block_status = 0x0F;

	/* As long as this function is called on erase block boundaries
		it will work correctly for 256 byte nand */
	ops.mode = MTD_OPS_PLACE_OOB;
	ops.ooboffs = 0;
	ops.ooblen = mtd->oobsize;
	ops.oobbuf = (void *)&oob;
	ops.datbuf = NULL;


	ret = mtd_write_oob(mtd, ofs, &ops);
	if (ret < 0 || ops.oobretlen != SM_OOB_SIZE) {
		printk(KERN_NOTICE
			"sm_common: can't mark sector at %i as bad\n",
								(int)ofs);
		error = -EIO;
	} else
		mtd->ecc_stats.badblocks++;

	return error;
}
Exemplo n.º 3
0
static int sm_block_markbad(struct nand_chip *chip, loff_t ofs)
{
	struct mtd_info *mtd = nand_to_mtd(chip);
	struct mtd_oob_ops ops;
	struct sm_oob oob;
	int ret;

	memset(&oob, -1, SM_OOB_SIZE);
	oob.block_status = 0x0F;

	/* As long as this function is called on erase block boundaries
		it will work correctly for 256 byte nand */
	ops.mode = MTD_OPS_PLACE_OOB;
	ops.ooboffs = 0;
	ops.ooblen = mtd->oobsize;
	ops.oobbuf = (void *)&oob;
	ops.datbuf = NULL;


	ret = mtd_write_oob(mtd, ofs, &ops);
	if (ret < 0 || ops.oobretlen != SM_OOB_SIZE) {
		pr_notice("sm_common: can't mark sector at %i as bad\n",
			  (int)ofs);
		return -EIO;
	}

	return 0;
}
Exemplo n.º 4
0
static 	int yaffs_mtd_write(struct yaffs_dev *dev, int nand_chunk,
				   const u8 *data, int data_len,
				   const u8 *oob, int oob_len)
{
	struct mtd_info *mtd = yaffs_dev_to_mtd(dev);
	loff_t addr;
	struct mtd_oob_ops ops;
	int retval;

	addr = ((loff_t) nand_chunk) * dev->param.total_bytes_per_chunk;
	memset(&ops, 0, sizeof(ops));
	ops.mode = MTD_OPS_AUTO_OOB;
	ops.len = (data) ? data_len : 0;
	ops.ooblen = oob_len;
	ops.datbuf = (u8 *)data;
	ops.oobbuf = (u8 *)oob;

#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 3, 0))
	retval = mtd_write_oob(mtd, addr, &ops);
#else
	retval = mtd->write_oob(mtd, addr, &ops);
#endif
	if (retval) {
		yaffs_trace(YAFFS_TRACE_MTD,
			"write_oob failed, chunk %d, mtd error %d",
			nand_chunk, retval);
	}
	return retval ? YAFFS_FAIL : YAFFS_OK;
}
Exemplo n.º 5
0
/* Write one page with oob one time */
int ubi_io_write_oob(const struct ubi_device *ubi, void *databuf, void *oobbuf,
                int pnum, int offset)
{
        int err;
        loff_t addr;
        struct mtd_oob_ops ops;

        dbg_io("read from PEB %d:%d", pnum, offset);

        ubi_assert(pnum >= 0 && pnum < ubi->peb_count);
        ubi_assert(offset >= 0 && offset + ubi->mtd->writesize <= ubi->peb_size);

        addr = (loff_t)pnum * ubi->peb_size + offset;

        ops.mode = MTD_OPS_AUTO_OOB;
        ops.ooblen = ubi->mtd->oobavail;
        ops.oobbuf = oobbuf;
        ops.ooboffs = 0;
        ops.len = ubi->mtd->writesize;
        ops.datbuf = databuf;
        ops.retlen = ops.oobretlen = 0;

        err = mtd_write_oob(ubi->mtd, addr, &ops);
        if (err) {
                ubi_err("error %d while writing to addr %lld peb%d:0x%x, written ",
                        err, addr, pnum, offset);
                dump_stack();
        } else
                ubi_assert(ops.retlen == ops.len);

        return err;
}
Exemplo n.º 6
0
int nandmtd_WriteChunkToNAND(struct yaffs_dev *dev, int chunkInNAND,
			     const u8 *data, const struct yaffs_spare *spare)
{
	struct mtd_info *mtd = (struct mtd_info *)(dev->driver_context);
	struct mtd_oob_ops ops;
	size_t dummy;
	int retval = 0;
	loff_t addr = ((loff_t) chunkInNAND) * dev->data_bytes_per_chunk;
	u8 spareAsBytes[8]; /* OOB */

	if (data && !spare)
		retval = mtd_write(mtd, addr, dev->data_bytes_per_chunk,
				&dummy, data);
	else if (spare) {
		if (dev->param.use_nand_ecc) {
			translate_spare2oob(spare, spareAsBytes);
			ops.mode = MTD_OPS_AUTO_OOB;
			ops.ooblen = 8; /* temp hack */
		} else {
			ops.mode = MTD_OPS_RAW;
			ops.ooblen = YAFFS_BYTES_PER_SPARE;
		}
		ops.len = data ? dev->data_bytes_per_chunk : ops.ooblen;
		ops.datbuf = (u8 *)data;
		ops.ooboffs = 0;
		ops.oobbuf = spareAsBytes;
		retval = mtd_write_oob(mtd, addr, &ops);
	}

	if (retval == 0)
		return YAFFS_OK;
	else
		return YAFFS_FAIL;
}
static int write_eraseblock(int ebnum)
{
    int i;
    struct mtd_oob_ops ops;
    int err = 0;
    loff_t addr = ebnum * mtd->erasesize;

    for (i = 0; i < pgcnt; ++i, addr += mtd->writesize) {
        set_random_data(writebuf, use_len);
        ops.mode      = MTD_OPS_AUTO_OOB;
        ops.len       = 0;
        ops.retlen    = 0;
        ops.ooblen    = use_len;
        ops.oobretlen = 0;
        ops.ooboffs   = use_offset;
        ops.datbuf    = NULL;
        ops.oobbuf    = writebuf;
        err = mtd_write_oob(mtd, addr, &ops);
        if (err || ops.oobretlen != use_len) {
            printk(PRINT_PREF "error: writeoob failed at %#llx\n",
                   (long long)addr);
            printk(PRINT_PREF "error: use_len %d, use_offset %d\n",
                   use_len, use_offset);
            errcnt += 1;
            return err ? err : -1;
        }
        if (vary_offset)
            do_vary_offset();
    }

    return err;
}
Exemplo n.º 8
0
Arquivo: mtdpart.c Projeto: DFE/u-boot
static int part_write_oob(struct mtd_info *mtd, loff_t to,
		struct mtd_oob_ops *ops)
{
	struct mtd_part *part = PART(mtd);

	if (to >= mtd->size)
		return -EINVAL;
	if (ops->datbuf && to + ops->len > mtd->size)
		return -EINVAL;
	return mtd_write_oob(part->master, to + part->offset, ops);
}
/* Write a chunk (page) of data to NAND.
 *
 * Caller always provides ExtendedTags data which are converted to a more
 * compact (packed) form for storage in NAND.  A mini-ECC runs over the
 * contents of the tags meta-data; used to valid the tags when read.
 *
 *  - Pack ExtendedTags to packed_tags1 form
 *  - Compute mini-ECC for packed_tags1
 *  - Write data and packed tags to NAND.
 *
 * Note: Due to the use of the packed_tags1 meta-data which does not include
 * a full sequence number (as found in the larger packed_tags2 form) it is
 * necessary for Yaffs to re-write a chunk/page (just once) to mark it as
 * discarded and dirty.  This is not ideal: newer NAND parts are supposed
 * to be written just once.  When Yaffs performs this operation, this
 * function is called with a NULL data pointer -- calling MTD write_oob
 * without data is valid usage (2.6.17).
 *
 * Any underlying MTD error results in YAFFS_FAIL.
 * Returns YAFFS_OK or YAFFS_FAIL.
 */
int nandmtd1_write_chunk_tags(struct yaffs_dev *dev,
			      int nand_chunk, const u8 * data,
			      const struct yaffs_ext_tags *etags)
{
	struct mtd_info *mtd = yaffs_dev_to_mtd(dev);
	int chunk_bytes = dev->data_bytes_per_chunk;
	loff_t addr = ((loff_t) nand_chunk) * chunk_bytes;
	struct mtd_oob_ops ops;
	struct yaffs_packed_tags1 pt1;
	int retval;

	/* we assume that packed_tags1 and struct yaffs_tags are compatible */
	compile_time_assertion(sizeof(struct yaffs_packed_tags1) == 12);
	compile_time_assertion(sizeof(struct yaffs_tags) == 8);

	yaffs_pack_tags1(&pt1, etags);
	yaffs_calc_tags_ecc((struct yaffs_tags *)&pt1);

	/* When deleting a chunk, the upper layer provides only skeletal
	 * etags, one with is_deleted set.  However, we need to update the
	 * tags, not erase them completely.  So we use the NAND write property
	 * that only zeroed-bits stick and set tag bytes to all-ones and
	 * zero just the (not) deleted bit.
	 */
#ifndef CONFIG_YAFFS_9BYTE_TAGS
	if (etags->is_deleted) {
		memset(&pt1, 0xff, 8);
		/* clear delete status bit to indicate deleted */
		pt1.deleted = 0;
	}
#else
	((u8 *) & pt1)[8] = 0xff;
	if (etags->is_deleted) {
		memset(&pt1, 0xff, 8);
		/* zero page_status byte to indicate deleted */
		((u8 *) & pt1)[8] = 0;
	}
#endif

	memset(&ops, 0, sizeof(ops));
	ops.mode =  MTD_OPS_AUTO_OOB;
	ops.len = (data) ? chunk_bytes : 0;
	ops.ooblen = YTAG1_SIZE;
	ops.datbuf = (u8 *) data;
	ops.oobbuf = (u8 *) & pt1;

	retval = mtd_write_oob(mtd, addr, &ops);
	if (retval) {
		yaffs_trace(YAFFS_TRACE_MTD,
			"write_oob failed, chunk %d, mtd error %d",
			nand_chunk, retval);
	}
	return retval ? YAFFS_FAIL : YAFFS_OK;
}
/* NB For use with inband tags....
 * We assume that the data buffer is of size total_bytes_per_chunk so that we can also
 * use it to load the tags.
 */
int nandmtd2_write_chunk_tags(struct yaffs_dev *dev, int nand_chunk,
			      const u8 * data,
			      const struct yaffs_ext_tags *tags)
{
	struct mtd_info *mtd = yaffs_dev_to_mtd(dev);
	struct mtd_oob_ops ops;
	int retval = 0;

	loff_t addr;

	struct yaffs_packed_tags2 pt;

	int packed_tags_size =
	    dev->param.no_tags_ecc ? sizeof(pt.t) : sizeof(pt);
	void *packed_tags_ptr =
	    dev->param.no_tags_ecc ? (void *)&pt.t : (void *)&pt;

	yaffs_trace(YAFFS_TRACE_MTD,
		"nandmtd2_write_chunk_tags chunk %d data %p tags %p",
		nand_chunk, data, tags);

	addr = ((loff_t) nand_chunk) * dev->param.total_bytes_per_chunk;

	/* For yaffs2 writing there must be both data and tags.
	 * If we're using inband tags, then the tags are stuffed into
	 * the end of the data buffer.
	 */
	if (!data || !tags)
		BUG();
	else if (dev->param.inband_tags) {
		struct yaffs_packed_tags2_tags_only *pt2tp;
		pt2tp =
		    (struct yaffs_packed_tags2_tags_only *)(data +
							    dev->
							    data_bytes_per_chunk);
		yaffs_pack_tags2_tags_only(pt2tp, tags);
	} else {
		yaffs_pack_tags2(&pt, tags, !dev->param.no_tags_ecc);
        }

	ops.mode = MTD_OPS_AUTO_OOB;
	ops.ooblen = (dev->param.inband_tags) ? 0 : packed_tags_size;
	ops.len = dev->param.total_bytes_per_chunk;
	ops.ooboffs = 0;
	ops.datbuf = (u8 *) data;
	ops.oobbuf = (dev->param.inband_tags) ? NULL : packed_tags_ptr;
	retval = mtd_write_oob(mtd, addr, &ops);

	if (retval == 0)
		return YAFFS_OK;
	else
		return YAFFS_FAIL;
}
Exemplo n.º 11
0
/* basically the same as above but write calls */
static void test_mtd_write_oob(void **state)
{
	struct libmtd *lib = mock_libmtd_open();
	struct mtd_dev_info mtd;
	struct mtd_oob_buf64 oob64;
	struct mtd_oob_buf oob;
	int mock_fd = 4;
	uint64_t start = 0, length = 64;
	char buf[64];
	memset(buf, 0xCD, 64);
	memset(&oob, 0, sizeof(oob));
	memset(&oob64, 0, sizeof(oob64));
	memset(&mtd, 0, sizeof(mtd));
	mtd.bb_allowed = 1;
	mtd.eb_cnt = 1024;
	mtd.eb_size = 128;
	mtd.subpage_size = 64;
	mtd.oob_size = 128;
	oob64.start = start;
	oob64.length = length;
	oob64.usr_ptr = (uint64_t)(unsigned long)buf;
	oob.start = oob64.start;
	oob.length = oob64.length;
	oob.ptr = buf;

	lib->offs64_ioctls = OFFS64_IOCTLS_NOT_SUPPORTED;
	expect_ioctl(MEMWRITEOOB, 0, &oob);
	int r = mtd_write_oob(lib, &mtd, mock_fd, start, length, buf);
	assert_int_equal(r, 0);

	lib->offs64_ioctls = OFFS64_IOCTLS_SUPPORTED;
	expect_ioctl(MEMWRITEOOB64, 0, &oob64);
	r = mtd_write_oob(lib, &mtd, mock_fd, start, length, buf);
	assert_int_equal(r, 0);

	libmtd_close(lib);
	(void)state;
}
Exemplo n.º 12
0
static ssize_t raw_write_page(struct mtd_info *mtd, void *buf, loff_t offset)
{
	struct mtd_oob_ops ops;
	ssize_t ret;

	ops.mode = MTD_OPS_RAW;
	ops.ooboffs = 0;
	ops.datbuf = buf;
	ops.len = mtd->writesize;
	ops.oobbuf = buf + mtd->writesize;
	ops.ooblen = mtd->oobsize;
	ret = mtd_write_oob(mtd, offset, &ops);

        return ret;
}
Exemplo n.º 13
0
/*
 * Write oob data to flash
 */
int inftl_write_oob(struct mtd_info *mtd, loff_t offs, size_t len,
		    size_t *retlen, uint8_t *buf)
{
	struct mtd_oob_ops ops;
	int res;

	ops.mode = MTD_OPS_PLACE_OOB;
	ops.ooboffs = offs & (mtd->writesize - 1);
	ops.ooblen = len;
	ops.oobbuf = buf;
	ops.datbuf = NULL;

	res = mtd_write_oob(mtd, offs & ~(mtd->writesize - 1), &ops);
	*retlen = ops.oobretlen;
	return res;
}
Exemplo n.º 14
0
static ssize_t mtdraw_blkwrite(struct mtd_info *mtd, const void *buf,
			       ulong offset)
{
	struct mtd_oob_ops ops;
	int ret;

	ops.mode = MTD_OPS_RAW;
	ops.ooboffs = 0;
	ops.datbuf = (void *)buf;
	ops.len = mtd->writesize;
	ops.oobbuf = (void *)buf + mtd->writesize;
	ops.ooblen = mtd->oobsize;
	ret = mtd_write_oob(mtd, offset, &ops);
	if (!ret)
		ret = ops.retlen + ops.oobretlen;
	return ret;
}
Exemplo n.º 15
0
/*
 * Write data and oob to flash
 */
static int inftl_write(struct mtd_info *mtd, loff_t offs, size_t len,
		       size_t *retlen, uint8_t *buf, uint8_t *oob)
{
	struct mtd_oob_ops ops;
	int res;

	ops.mode = MTD_OPS_PLACE_OOB;
	ops.ooboffs = offs;
	ops.ooblen = mtd->oobsize;
	ops.oobbuf = oob;
	ops.datbuf = buf;
	ops.len = len;

	res = mtd_write_oob(mtd, offs & ~(mtd->writesize - 1), &ops);
	*retlen = ops.retlen;
	return res;
}
Exemplo n.º 16
0
static int
concat_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops)
{
	struct mtd_concat *concat = CONCAT(mtd);
	struct mtd_oob_ops devops = *ops;
	int i, err;

	if (!(mtd->flags & MTD_WRITEABLE))
		return -EROFS;

	ops->retlen = ops->oobretlen = 0;

	for (i = 0; i < concat->num_subdev; i++) {
		struct mtd_info *subdev = concat->subdev[i];

		if (to >= subdev->size) {
			to -= subdev->size;
			continue;
		}

		/* partial write ? */
		if (to + devops.len > subdev->size)
			devops.len = subdev->size - to;

		err = mtd_write_oob(subdev, to, &devops);
		ops->retlen += devops.oobretlen;
		if (err)
			return err;

		if (devops.datbuf) {
			devops.len = ops->len - ops->retlen;
			if (!devops.len)
				return 0;
			devops.datbuf += devops.retlen;
		}
		if (devops.oobbuf) {
			devops.ooblen = ops->ooblen - ops->oobretlen;
			if (!devops.ooblen)
				return 0;
			devops.oobbuf += devops.oobretlen;
		}
		to = 0;
	}
	return -EINVAL;
}
Exemplo n.º 17
0
static 	int yaffs_mtd_write(struct yaffs_dev *dev, int nand_chunk,
				   const u8 *data, int data_len,
				   const u8 *oob, int oob_len)
{
	struct mtd_info *mtd = yaffs_dev_to_mtd(dev);
	loff_t addr;
	struct mtd_oob_ops ops;
	int retval;

	yaffs_trace(YAFFS_TRACE_MTD,
			"yaffs_mtd_write(%p, %d, %p, %d, %p, %d)\n",
			dev, nand_chunk, data, data_len, oob, oob_len);

	if (!data || !data_len) {
		data = NULL;
		data_len = 0;
	}

	if (!oob || !oob_len) {
		oob = NULL;
		oob_len = 0;
	}

	addr = ((loff_t) nand_chunk) * dev->param.total_bytes_per_chunk;
	memset(&ops, 0, sizeof(ops));
	ops.mode = MTD_OPS_AUTO_OOB;
	ops.len = (data) ? data_len : 0;
	ops.ooblen = oob_len;
	ops.datbuf = (u8 *)data;
	ops.oobbuf = (u8 *)oob;

	retval = mtd_write_oob(mtd, addr, &ops);
	if (retval) {
		yaffs_trace(YAFFS_TRACE_MTD,
			"write_oob failed, chunk %d, mtd error %d",
			nand_chunk, retval);
	}
	return retval ? YAFFS_FAIL : YAFFS_OK;
}
Exemplo n.º 18
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;
}
Exemplo n.º 19
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;
}
Exemplo n.º 20
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;
}
Exemplo n.º 21
0
static int __init mtd_oobtest_init(void)
{
    int err = 0;
    unsigned int i;
    uint64_t tmp;
    struct mtd_oob_ops ops;
    loff_t addr = 0, addr0;

    printk(KERN_INFO "\n");
    printk(KERN_INFO "=================================================\n");

    if (dev < 0) {
        printk(PRINT_PREF "Please specify a valid mtd-device via module paramter\n");
        printk(KERN_CRIT "CAREFUL: This test wipes all data on the specified MTD device!\n");
        return -EINVAL;
    }

    printk(PRINT_PREF "MTD device: %d\n", dev);

    mtd = get_mtd_device(NULL, dev);
    if (IS_ERR(mtd)) {
        err = PTR_ERR(mtd);
        printk(PRINT_PREF "error: cannot get MTD device\n");
        return err;
    }

    if (mtd->type != MTD_NANDFLASH) {
        printk(PRINT_PREF "this test requires NAND flash\n");
        goto out;
    }

    tmp = mtd->size;
    do_div(tmp, mtd->erasesize);
    ebcnt = tmp;
    pgcnt = mtd->erasesize / mtd->writesize;

    printk(PRINT_PREF "MTD device size %llu, eraseblock size %u, "
           "page size %u, count of eraseblocks %u, pages per "
           "eraseblock %u, OOB size %u\n",
           (unsigned long long)mtd->size, mtd->erasesize,
           mtd->writesize, ebcnt, pgcnt, mtd->oobsize);

    err = -ENOMEM;
    readbuf = kmalloc(mtd->erasesize, GFP_KERNEL);
    if (!readbuf) {
        printk(PRINT_PREF "error: cannot allocate memory\n");
        goto out;
    }
    writebuf = kmalloc(mtd->erasesize, GFP_KERNEL);
    if (!writebuf) {
        printk(PRINT_PREF "error: cannot allocate memory\n");
        goto out;
    }

    err = scan_for_bad_eraseblocks();
    if (err)
        goto out;

    use_offset = 0;
    use_len = mtd->ecclayout->oobavail;
    use_len_max = mtd->ecclayout->oobavail;
    vary_offset = 0;

    /* First test: write all OOB, read it back and verify */
    printk(PRINT_PREF "test 1 of 5\n");

    err = erase_whole_device();
    if (err)
        goto out;

    simple_srand(1);
    err = write_whole_device();
    if (err)
        goto out;

    simple_srand(1);
    err = verify_all_eraseblocks();
    if (err)
        goto out;

    /*
     * Second test: write all OOB, a block at a time, read it back and
     * verify.
     */
    printk(PRINT_PREF "test 2 of 5\n");

    err = erase_whole_device();
    if (err)
        goto out;

    simple_srand(3);
    err = write_whole_device();
    if (err)
        goto out;

    /* Check all eraseblocks */
    simple_srand(3);
    printk(PRINT_PREF "verifying all eraseblocks\n");
    for (i = 0; i < ebcnt; ++i) {
        if (bbt[i])
            continue;
        err = verify_eraseblock_in_one_go(i);
        if (err)
            goto out;
        if (i % 256 == 0)
            printk(PRINT_PREF "verified up to eraseblock %u\n", i);
        cond_resched();
    }
    printk(PRINT_PREF "verified %u eraseblocks\n", i);

    /*
     * Third test: write OOB at varying offsets and lengths, read it back
     * and verify.
     */
    printk(PRINT_PREF "test 3 of 5\n");

    err = erase_whole_device();
    if (err)
        goto out;

    /* Write all eraseblocks */
    use_offset = 0;
    use_len = mtd->ecclayout->oobavail;
    use_len_max = mtd->ecclayout->oobavail;
    vary_offset = 1;
    simple_srand(5);

    err = write_whole_device();
    if (err)
        goto out;

    /* Check all eraseblocks */
    use_offset = 0;
    use_len = mtd->ecclayout->oobavail;
    use_len_max = mtd->ecclayout->oobavail;
    vary_offset = 1;
    simple_srand(5);
    err = verify_all_eraseblocks();
    if (err)
        goto out;

    use_offset = 0;
    use_len = mtd->ecclayout->oobavail;
    use_len_max = mtd->ecclayout->oobavail;
    vary_offset = 0;

    /* Fourth test: try to write off end of device */
    printk(PRINT_PREF "test 4 of 5\n");

    err = erase_whole_device();
    if (err)
        goto out;

    addr0 = 0;
    for (i = 0; i < ebcnt && bbt[i]; ++i)
        addr0 += mtd->erasesize;

    /* Attempt to write off end of OOB */
    ops.mode      = MTD_OPS_AUTO_OOB;
    ops.len       = 0;
    ops.retlen    = 0;
    ops.ooblen    = 1;
    ops.oobretlen = 0;
    ops.ooboffs   = mtd->ecclayout->oobavail;
    ops.datbuf    = NULL;
    ops.oobbuf    = writebuf;
    printk(PRINT_PREF "attempting to start write past end of OOB\n");
    printk(PRINT_PREF "an error is expected...\n");
    err = mtd_write_oob(mtd, addr0, &ops);
    if (err) {
        printk(PRINT_PREF "error occurred as expected\n");
        err = 0;
    } else {
        printk(PRINT_PREF "error: can write past end of OOB\n");
        errcnt += 1;
    }

    /* Attempt to read off end of OOB */
    ops.mode      = MTD_OPS_AUTO_OOB;
    ops.len       = 0;
    ops.retlen    = 0;
    ops.ooblen    = 1;
    ops.oobretlen = 0;
    ops.ooboffs   = mtd->ecclayout->oobavail;
    ops.datbuf    = NULL;
    ops.oobbuf    = readbuf;
    printk(PRINT_PREF "attempting to start read past end of OOB\n");
    printk(PRINT_PREF "an error is expected...\n");
    err = mtd_read_oob(mtd, addr0, &ops);
    if (err) {
        printk(PRINT_PREF "error occurred as expected\n");
        err = 0;
    } else {
        printk(PRINT_PREF "error: can read past end of OOB\n");
        errcnt += 1;
    }

    if (bbt[ebcnt - 1])
        printk(PRINT_PREF "skipping end of device tests because last "
               "block is bad\n");
    else {
        /* Attempt to write off end of device */
        ops.mode      = MTD_OPS_AUTO_OOB;
        ops.len       = 0;
        ops.retlen    = 0;
        ops.ooblen    = mtd->ecclayout->oobavail + 1;
        ops.oobretlen = 0;
        ops.ooboffs   = 0;
        ops.datbuf    = NULL;
        ops.oobbuf    = writebuf;
        printk(PRINT_PREF "attempting to write past end of device\n");
        printk(PRINT_PREF "an error is expected...\n");
        err = mtd_write_oob(mtd, mtd->size - mtd->writesize, &ops);
        if (err) {
            printk(PRINT_PREF "error occurred as expected\n");
            err = 0;
        } else {
            printk(PRINT_PREF "error: wrote past end of device\n");
            errcnt += 1;
        }

        /* Attempt to read off end of device */
        ops.mode      = MTD_OPS_AUTO_OOB;
        ops.len       = 0;
        ops.retlen    = 0;
        ops.ooblen    = mtd->ecclayout->oobavail + 1;
        ops.oobretlen = 0;
        ops.ooboffs   = 0;
        ops.datbuf    = NULL;
        ops.oobbuf    = readbuf;
        printk(PRINT_PREF "attempting to read past end of device\n");
        printk(PRINT_PREF "an error is expected...\n");
        err = mtd_read_oob(mtd, mtd->size - mtd->writesize, &ops);
        if (err) {
            printk(PRINT_PREF "error occurred as expected\n");
            err = 0;
        } else {
            printk(PRINT_PREF "error: read past end of device\n");
            errcnt += 1;
        }

        err = erase_eraseblock(ebcnt - 1);
        if (err)
            goto out;

        /* Attempt to write off end of device */
        ops.mode      = MTD_OPS_AUTO_OOB;
        ops.len       = 0;
        ops.retlen    = 0;
        ops.ooblen    = mtd->ecclayout->oobavail;
        ops.oobretlen = 0;
        ops.ooboffs   = 1;
        ops.datbuf    = NULL;
        ops.oobbuf    = writebuf;
        printk(PRINT_PREF "attempting to write past end of device\n");
        printk(PRINT_PREF "an error is expected...\n");
        err = mtd_write_oob(mtd, mtd->size - mtd->writesize, &ops);
        if (err) {
            printk(PRINT_PREF "error occurred as expected\n");
            err = 0;
        } else {
            printk(PRINT_PREF "error: wrote past end of device\n");
            errcnt += 1;
        }

        /* Attempt to read off end of device */
        ops.mode      = MTD_OPS_AUTO_OOB;
        ops.len       = 0;
        ops.retlen    = 0;
        ops.ooblen    = mtd->ecclayout->oobavail;
        ops.oobretlen = 0;
        ops.ooboffs   = 1;
        ops.datbuf    = NULL;
        ops.oobbuf    = readbuf;
        printk(PRINT_PREF "attempting to read past end of device\n");
        printk(PRINT_PREF "an error is expected...\n");
        err = mtd_read_oob(mtd, mtd->size - mtd->writesize, &ops);
        if (err) {
            printk(PRINT_PREF "error occurred as expected\n");
            err = 0;
        } else {
            printk(PRINT_PREF "error: read past end of device\n");
            errcnt += 1;
        }
    }

    /* Fifth test: write / read across block boundaries */
    printk(PRINT_PREF "test 5 of 5\n");

    /* Erase all eraseblocks */
    err = erase_whole_device();
    if (err)
        goto out;

    /* Write all eraseblocks */
    simple_srand(11);
    printk(PRINT_PREF "writing OOBs of whole device\n");
    for (i = 0; i < ebcnt - 1; ++i) {
        int cnt = 2;
        int pg;
        size_t sz = mtd->ecclayout->oobavail;
        if (bbt[i] || bbt[i + 1])
            continue;
        addr = (i + 1) * mtd->erasesize - mtd->writesize;
        for (pg = 0; pg < cnt; ++pg) {
            set_random_data(writebuf, sz);
            ops.mode      = MTD_OPS_AUTO_OOB;
            ops.len       = 0;
            ops.retlen    = 0;
            ops.ooblen    = sz;
            ops.oobretlen = 0;
            ops.ooboffs   = 0;
            ops.datbuf    = NULL;
            ops.oobbuf    = writebuf;
            err = mtd_write_oob(mtd, addr, &ops);
            if (err)
                goto out;
            if (i % 256 == 0)
                printk(PRINT_PREF "written up to eraseblock "
                       "%u\n", i);
            cond_resched();
            addr += mtd->writesize;
        }
    }
    printk(PRINT_PREF "written %u eraseblocks\n", i);

    /* Check all eraseblocks */
    simple_srand(11);
    printk(PRINT_PREF "verifying all eraseblocks\n");
    for (i = 0; i < ebcnt - 1; ++i) {
        if (bbt[i] || bbt[i + 1])
            continue;
        set_random_data(writebuf, mtd->ecclayout->oobavail * 2);
        addr = (i + 1) * mtd->erasesize - mtd->writesize;
        ops.mode      = MTD_OPS_AUTO_OOB;
        ops.len       = 0;
        ops.retlen    = 0;
        ops.ooblen    = mtd->ecclayout->oobavail * 2;
        ops.oobretlen = 0;
        ops.ooboffs   = 0;
        ops.datbuf    = NULL;
        ops.oobbuf    = readbuf;
        err = mtd_read_oob(mtd, addr, &ops);
        if (err)
            goto out;
        if (memcmp(readbuf, writebuf, mtd->ecclayout->oobavail * 2)) {
            printk(PRINT_PREF "error: verify failed at %#llx\n",
                   (long long)addr);
            errcnt += 1;
            if (errcnt > 1000) {
                printk(PRINT_PREF "error: too many errors\n");
                goto out;
            }
        }
        if (i % 256 == 0)
            printk(PRINT_PREF "verified up to eraseblock %u\n", i);
        cond_resched();
    }
    printk(PRINT_PREF "verified %u eraseblocks\n", i);

    printk(PRINT_PREF "finished with %d errors\n", errcnt);
out:
    kfree(bbt);
    kfree(writebuf);
    kfree(readbuf);
    put_mtd_device(mtd);
    if (err)
        printk(PRINT_PREF "error %d occurred\n", err);
    printk(KERN_INFO "=================================================\n");
    return err;
}