From 59b5a2ad83dfefae75011a839e94d4880ff02933 Mon Sep 17 00:00:00 2001 From: Peter Tyser Date: Tue, 3 Feb 2015 11:58:12 -0600 Subject: nand: Add verification functions Add nand_verify() and nand_verify_page_oob(). nand_verify() verifies NAND contents against an arbitrarily sized buffer using ECC while nand_verify_page_oob() verifies a NAND page's contents and OOB. Signed-off-by: Peter Tyser Tested-by: Heiko Schocher Acked-by: Heiko Schocher --- drivers/mtd/nand/nand_util.c | 97 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 96 insertions(+), 1 deletion(-) (limited to 'drivers/mtd/nand/nand_util.c') diff --git a/drivers/mtd/nand/nand_util.c b/drivers/mtd/nand/nand_util.c index afdd160d816..f4877560710 100644 --- a/drivers/mtd/nand/nand_util.c +++ b/drivers/mtd/nand/nand_util.c @@ -463,6 +463,87 @@ static size_t drop_ffs(const nand_info_t *nand, const u_char *buf, } #endif +/** + * nand_verify_page_oob: + * + * Verify a page of NAND flash, including the OOB. + * Reads page of NAND and verifies the contents and OOB against the + * values in ops. + * + * @param nand NAND device + * @param ops MTD operations, including data to verify + * @param ofs offset in flash + * @return 0 in case of success + */ +int nand_verify_page_oob(nand_info_t *nand, struct mtd_oob_ops *ops, loff_t ofs) +{ + int rval; + struct mtd_oob_ops vops; + size_t verlen = nand->writesize + nand->oobsize; + + memcpy(&vops, ops, sizeof(vops)); + + vops.datbuf = malloc(verlen); + + if (!vops.datbuf) + return -ENOMEM; + + vops.oobbuf = vops.datbuf + nand->writesize; + + rval = mtd_read_oob(nand, ofs, &vops); + if (!rval) + rval = memcmp(ops->datbuf, vops.datbuf, vops.len); + if (!rval) + rval = memcmp(ops->oobbuf, vops.oobbuf, vops.ooblen); + + free(vops.datbuf); + + return rval ? -EIO : 0; +} + +/** + * nand_verify: + * + * Verify a region of NAND flash. + * Reads NAND in page-sized chunks and verifies the contents against + * the contents of a buffer. The offset into the NAND must be + * page-aligned, and the function doesn't handle skipping bad blocks. + * + * @param nand NAND device + * @param ofs offset in flash + * @param len buffer length + * @param buf buffer to read from + * @return 0 in case of success + */ +int nand_verify(nand_info_t *nand, loff_t ofs, size_t len, u_char *buf) +{ + int rval = 0; + size_t verofs; + size_t verlen = nand->writesize; + uint8_t *verbuf = malloc(verlen); + + if (!verbuf) + return -ENOMEM; + + /* Read the NAND back in page-size groups to limit malloc size */ + for (verofs = ofs; verofs < ofs + len; + verofs += verlen, buf += verlen) { + verlen = min(nand->writesize, (uint32_t)(ofs + len - verofs)); + rval = nand_read(nand, verofs, &verlen, verbuf); + if (!rval || (rval == -EUCLEAN)) + rval = memcmp(buf, verbuf, verlen); + + if (rval) + break; + } + + free(verbuf); + + return rval ? -EIO : 0; +} + + + /** * nand_write_skip_bad: * @@ -501,7 +582,7 @@ int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length, #ifdef CONFIG_CMD_NAND_YAFFS if (flags & WITH_YAFFS_OOB) { - if (flags & ~WITH_YAFFS_OOB) + if (flags & (~WITH_YAFFS_OOB & ~WITH_WR_VERIFY)) return -EINVAL; int pages; @@ -554,6 +635,10 @@ int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length, if (!need_skip && !(flags & WITH_DROP_FFS)) { rval = nand_write(nand, offset, length, buffer); + + if ((flags & WITH_WR_VERIFY) && !rval) + rval = nand_verify(nand, offset, *length, buffer); + if (rval == 0) return 0; @@ -601,6 +686,11 @@ int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length, ops.oobbuf = ops.datbuf + pagesize; rval = mtd_write_oob(nand, offset, &ops); + + if ((flags & WITH_WR_VERIFY) && !rval) + rval = nand_verify_page_oob(nand, + &ops, offset); + if (rval != 0) break; @@ -620,6 +710,11 @@ int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length, rval = nand_write(nand, offset, &truncated_write_size, p_buffer); + + if ((flags & WITH_WR_VERIFY) && !rval) + rval = nand_verify(nand, offset, + truncated_write_size, p_buffer); + offset += write_size; p_buffer += write_size; } -- cgit v1.2.3 From 004a1fdb45fb06ee2faf6e50945ceb79d43a2f41 Mon Sep 17 00:00:00 2001 From: Peter Tyser Date: Tue, 3 Feb 2015 11:58:16 -0600 Subject: nand: yaffs: Remove the "nand write.yaffs" command This command is only enabled by one board, complicates the NAND code, and doesn't appear to have been functioning properly for several years. If there are no bad blocks in the NAND region being written nand_write_skip_bad() will take the shortcut of calling nand_write() which bypasses the special yaffs handling. This causes invalid YAFFS data to be written. See http://lists.denx.de/pipermail/u-boot/2011-September/102830.html for an example and a potential workaround. U-Boot still retains the ability to mount and access YAFFS partitions via CONFIG_YAFFS2. Signed-off-by: Peter Tyser --- drivers/mtd/nand/nand_util.c | 77 +++++++------------------------------------- 1 file changed, 12 insertions(+), 65 deletions(-) (limited to 'drivers/mtd/nand/nand_util.c') diff --git a/drivers/mtd/nand/nand_util.c b/drivers/mtd/nand/nand_util.c index f4877560710..12dd26a33fa 100644 --- a/drivers/mtd/nand/nand_util.c +++ b/drivers/mtd/nand/nand_util.c @@ -580,24 +580,7 @@ int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length, if (actual) *actual = 0; -#ifdef CONFIG_CMD_NAND_YAFFS - if (flags & WITH_YAFFS_OOB) { - if (flags & (~WITH_YAFFS_OOB & ~WITH_WR_VERIFY)) - return -EINVAL; - - int pages; - pages = nand->erasesize / nand->writesize; - blocksize = (pages * nand->oobsize) + nand->erasesize; - if (*length % (nand->writesize + nand->oobsize)) { - printf("Attempt to write incomplete page" - " in yaffs mode\n"); - return -EINVAL; - } - } else -#endif - { - blocksize = nand->erasesize; - } + blocksize = nand->erasesize; /* * nand_write() handles unaligned, partial page writes. @@ -666,58 +649,22 @@ int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length, else write_size = blocksize - block_offset; -#ifdef CONFIG_CMD_NAND_YAFFS - if (flags & WITH_YAFFS_OOB) { - int page, pages; - size_t pagesize = nand->writesize; - size_t pagesize_oob = pagesize + nand->oobsize; - struct mtd_oob_ops ops; - - ops.len = pagesize; - ops.ooblen = nand->oobsize; - ops.mode = MTD_OPS_AUTO_OOB; - ops.ooboffs = 0; - - pages = write_size / pagesize_oob; - for (page = 0; page < pages; page++) { - WATCHDOG_RESET(); - - ops.datbuf = p_buffer; - ops.oobbuf = ops.datbuf + pagesize; - - rval = mtd_write_oob(nand, offset, &ops); - - if ((flags & WITH_WR_VERIFY) && !rval) - rval = nand_verify_page_oob(nand, - &ops, offset); - - if (rval != 0) - break; - - offset += pagesize; - p_buffer += pagesize_oob; - } - } - else -#endif - { - truncated_write_size = write_size; + truncated_write_size = write_size; #ifdef CONFIG_CMD_NAND_TRIMFFS - if (flags & WITH_DROP_FFS) - truncated_write_size = drop_ffs(nand, p_buffer, - &write_size); + if (flags & WITH_DROP_FFS) + truncated_write_size = drop_ffs(nand, p_buffer, + &write_size); #endif - rval = nand_write(nand, offset, &truncated_write_size, - p_buffer); + rval = nand_write(nand, offset, &truncated_write_size, + p_buffer); - if ((flags & WITH_WR_VERIFY) && !rval) - rval = nand_verify(nand, offset, - truncated_write_size, p_buffer); + if ((flags & WITH_WR_VERIFY) && !rval) + rval = nand_verify(nand, offset, + truncated_write_size, p_buffer); - offset += write_size; - p_buffer += write_size; - } + offset += write_size; + p_buffer += write_size; if (rval != 0) { printf("NAND write to offset %llx failed %d\n", -- cgit v1.2.3