| From f722013ee9fd24623df31dec9a91a6d02c3e2f2f Mon Sep 17 00:00:00 2001 |
| From: "THOMSON, Adam (Adam)" <adam.thomson@alcatel-lucent.com> |
| Date: Tue, 14 Jun 2011 16:52:38 +0200 |
| Subject: mtd: nand_base: always initialise oob_poi before writing OOB data |
| |
| From: "THOMSON, Adam (Adam)" <adam.thomson@alcatel-lucent.com> |
| |
| commit f722013ee9fd24623df31dec9a91a6d02c3e2f2f upstream. |
| |
| In nand_do_write_ops() code it is possible for a caller to provide |
| ops.oobbuf populated and ops.mode == MTD_OOB_AUTO, which currently |
| means that the chip->oob_poi buffer isn't initialised to all 0xFF. |
| The nand_fill_oob() method then carries out the task of copying |
| the provided OOB data to oob_poi, but with MTD_OOB_AUTO it skips |
| areas marked as unavailable by the layout struct, including the |
| bad block marker bytes. |
| |
| An example of this causing issues is when the last OOB data read |
| was from the start of a bad block where the markers are not 0xFF, |
| and the caller wishes to write new OOB data at the beginning of |
| another block. In this scenario the caller would provide OOB data, |
| but nand_fill_oob() would skip the bad block marker bytes in |
| oob_poi before copying the OOB data provided by the caller. |
| This means that when the OOB data is written back to NAND, |
| the block is inadvertently marked as bad without the caller knowing. |
| This has been witnessed when using YAFFS2 where tags are stored |
| in the OOB. |
| |
| To avoid this oob_poi is always initialised to 0xFF to make sure |
| no left over data is inadvertently written back to the OOB area. |
| |
| Credits to Brian Norris <computersforpeace@gmail.com> for fixing this |
| patch. |
| |
| Signed-off-by: Adam Thomson <adam.thomson@alcatel-lucent.com> |
| Signed-off-by: Artem Bityutskiy <dedekind1@gmail.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> |
| |
| |
| --- |
| drivers/mtd/nand/nand_base.c | 27 ++++++++++++++++----------- |
| 1 file changed, 16 insertions(+), 11 deletions(-) |
| |
| --- a/drivers/mtd/nand/nand_base.c |
| +++ b/drivers/mtd/nand/nand_base.c |
| @@ -2097,14 +2097,22 @@ static int nand_write_page(struct mtd_in |
| |
| /** |
| * nand_fill_oob - [Internal] Transfer client buffer to oob |
| - * @chip: nand chip structure |
| + * @mtd: MTD device structure |
| * @oob: oob data buffer |
| * @len: oob data write length |
| * @ops: oob ops structure |
| */ |
| -static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob, size_t len, |
| - struct mtd_oob_ops *ops) |
| +static uint8_t *nand_fill_oob(struct mtd_info *mtd, uint8_t *oob, size_t len, |
| + struct mtd_oob_ops *ops) |
| { |
| + struct nand_chip *chip = mtd->priv; |
| + |
| + /* |
| + * Initialise to all 0xFF, to avoid the possibility of left over OOB |
| + * data from a previous OOB read. |
| + */ |
| + memset(chip->oob_poi, 0xff, mtd->oobsize); |
| + |
| switch (ops->mode) { |
| |
| case MTD_OOB_PLACE: |
| @@ -2201,10 +2209,6 @@ static int nand_do_write_ops(struct mtd_ |
| (chip->pagebuf << chip->page_shift) < (to + ops->len)) |
| chip->pagebuf = -1; |
| |
| - /* If we're not given explicit OOB data, let it be 0xFF */ |
| - if (likely(!oob)) |
| - memset(chip->oob_poi, 0xff, mtd->oobsize); |
| - |
| /* Don't allow multipage oob writes with offset */ |
| if (oob && ops->ooboffs && (ops->ooboffs + ops->ooblen > oobmaxlen)) |
| return -EINVAL; |
| @@ -2226,8 +2230,11 @@ static int nand_do_write_ops(struct mtd_ |
| |
| if (unlikely(oob)) { |
| size_t len = min(oobwritelen, oobmaxlen); |
| - oob = nand_fill_oob(chip, oob, len, ops); |
| + oob = nand_fill_oob(mtd, oob, len, ops); |
| oobwritelen -= len; |
| + } else { |
| + /* We still need to erase leftover OOB data */ |
| + memset(chip->oob_poi, 0xff, mtd->oobsize); |
| } |
| |
| ret = chip->write_page(mtd, chip, wbuf, page, cached, |
| @@ -2401,10 +2408,8 @@ static int nand_do_write_oob(struct mtd_ |
| if (page == chip->pagebuf) |
| chip->pagebuf = -1; |
| |
| - memset(chip->oob_poi, 0xff, mtd->oobsize); |
| - nand_fill_oob(chip, ops->oobbuf, ops->ooblen, ops); |
| + nand_fill_oob(mtd, ops->oobbuf, ops->ooblen, ops); |
| status = chip->ecc.write_oob(mtd, chip, page & chip->pagemask); |
| - memset(chip->oob_poi, 0xff, mtd->oobsize); |
| |
| if (status) |
| return status; |