Commit ebf53f42 authored by Artem Bityutskiy's avatar Artem Bityutskiy

UBI: fix NOR flash recovery

This commit fixes NOR flash recovery issues observed with Spansion
S29GL512N NOR.

When NOR erases, it first fills PEBs with zeroes, then sets all bytes
to 0xFF. Filling with zeroes starts from the end of the PEB. And when
power is cut, this results in PEBs containing correct EC and VID headers
but corrupted with zeros at the end. This confuses UBI and it mistakinly
accepts these PEBs and associate them with LEBs.

Fis this issue by zeroing EC and VID magics before erasing PEBs, to
make UBI later refuse zem.
Signed-off-by: default avatarArtem Bityutskiy <Artem.Bityutskiy@nokia.com>
parent fe96efc1
...@@ -657,6 +657,11 @@ static int io_init(struct ubi_device *ubi) ...@@ -657,6 +657,11 @@ static int io_init(struct ubi_device *ubi)
if (ubi->mtd->block_isbad && ubi->mtd->block_markbad) if (ubi->mtd->block_isbad && ubi->mtd->block_markbad)
ubi->bad_allowed = 1; ubi->bad_allowed = 1;
if (ubi->mtd->type == MTD_NORFLASH) {
ubi_assert(ubi->mtd->writesize == 1);
ubi->nor_flash = 1;
}
ubi->min_io_size = ubi->mtd->writesize; ubi->min_io_size = ubi->mtd->writesize;
ubi->hdrs_min_io_size = ubi->mtd->writesize >> ubi->mtd->subpage_sft; ubi->hdrs_min_io_size = ubi->mtd->writesize >> ubi->mtd->subpage_sft;
......
...@@ -266,8 +266,8 @@ int ubi_io_write(struct ubi_device *ubi, const void *buf, int pnum, int offset, ...@@ -266,8 +266,8 @@ int ubi_io_write(struct ubi_device *ubi, const void *buf, int pnum, int offset,
addr = (loff_t)pnum * ubi->peb_size + offset; addr = (loff_t)pnum * ubi->peb_size + offset;
err = ubi->mtd->write(ubi->mtd, addr, len, &written, buf); err = ubi->mtd->write(ubi->mtd, addr, len, &written, buf);
if (err) { if (err) {
ubi_err("error %d while writing %d bytes to PEB %d:%d, written" ubi_err("error %d while writing %d bytes to PEB %d:%d, written "
" %zd bytes", err, len, pnum, offset, written); "%zd bytes", err, len, pnum, offset, written);
ubi_dbg_dump_stack(); ubi_dbg_dump_stack();
} else } else
ubi_assert(written == len); ubi_assert(written == len);
...@@ -453,6 +453,54 @@ static int torture_peb(struct ubi_device *ubi, int pnum) ...@@ -453,6 +453,54 @@ static int torture_peb(struct ubi_device *ubi, int pnum)
return err; return err;
} }
/**
* nor_erase_prepare - prepare a NOR flash PEB for erasure.
* @ubi: UBI device description object
* @pnum: physical eraseblock number to prepare
*
* NOR flash, or at least some of them, have peculiar embedded PEB erasure
* algorithm: the PEB is first filled with zeroes, then it is erased. And
* filling with zeroes starts from the end of the PEB. This was observed with
* Spansion S29GL512N NOR flash.
*
* This means that in case of a power cut we may end up with intact data at the
* beginning of the PEB, and all zeroes at the end of PEB. In other words, the
* EC and VID headers are OK, but a large chunk of data at the end of PEB is
* zeroed. This makes UBI mistakenly treat this PEB as used and associate it
* with an LEB, which leads to subsequent failures (e.g., UBIFS fails).
*
* This function is called before erasing NOR PEBs and it zeroes out EC and VID
* magic numbers in order to invalidate them and prevent the failures. Returns
* zero in case of success and a negative error code in case of failure.
*/
static int nor_erase_prepare(struct ubi_device *ubi, int pnum)
{
int err;
size_t written;
loff_t addr;
uint32_t data = 0;
addr = (loff_t)pnum * ubi->peb_size;
err = ubi->mtd->write(ubi->mtd, addr, 4, &written, &data);
if (err) {
ubi_err("error %d while writing 4 bytes to PEB %d:0, written "
"%zd bytes", err, pnum, 0, written);
ubi_dbg_dump_stack();
return err;
}
addr += ubi->vid_hdr_aloffset;
err = ubi->mtd->write(ubi->mtd, addr, 4, &written, &data);
if (err) {
ubi_err("error %d while writing 4 bytes to PEB %d:%d, written "
"%zd bytes", err, pnum, ubi->vid_hdr_aloffset, written);
ubi_dbg_dump_stack();
return err;
}
return 0;
}
/** /**
* ubi_io_sync_erase - synchronously erase a physical eraseblock. * ubi_io_sync_erase - synchronously erase a physical eraseblock.
* @ubi: UBI device description object * @ubi: UBI device description object
...@@ -484,6 +532,12 @@ int ubi_io_sync_erase(struct ubi_device *ubi, int pnum, int torture) ...@@ -484,6 +532,12 @@ int ubi_io_sync_erase(struct ubi_device *ubi, int pnum, int torture)
return -EROFS; return -EROFS;
} }
if (ubi->nor_flash) {
err = nor_erase_prepare(ubi, pnum);
if (err)
return err;
}
if (torture) { if (torture) {
ret = torture_peb(ubi, pnum); ret = torture_peb(ubi, pnum);
if (ret < 0) if (ret < 0)
......
...@@ -373,6 +373,7 @@ struct ubi_wl_entry; ...@@ -373,6 +373,7 @@ struct ubi_wl_entry;
* @vid_hdr_shift: contains @vid_hdr_offset - @vid_hdr_aloffset * @vid_hdr_shift: contains @vid_hdr_offset - @vid_hdr_aloffset
* @bad_allowed: whether the MTD device admits of bad physical eraseblocks or * @bad_allowed: whether the MTD device admits of bad physical eraseblocks or
* not * not
* @nor_flash: non-zero if working on top of NOR flash
* @mtd: MTD device descriptor * @mtd: MTD device descriptor
* *
* @peb_buf1: a buffer of PEB size used for different purposes * @peb_buf1: a buffer of PEB size used for different purposes
...@@ -454,7 +455,8 @@ struct ubi_device { ...@@ -454,7 +455,8 @@ struct ubi_device {
int vid_hdr_offset; int vid_hdr_offset;
int vid_hdr_aloffset; int vid_hdr_aloffset;
int vid_hdr_shift; int vid_hdr_shift;
int bad_allowed; unsigned int bad_allowed:1;
unsigned int nor_flash:1;
struct mtd_info *mtd; struct mtd_info *mtd;
void *peb_buf1; void *peb_buf1;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment