summaryrefslogtreecommitdiff
path: root/drivers/mtd/ubi/io.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mtd/ubi/io.c')
-rw-r--r--drivers/mtd/ubi/io.c133
1 files changed, 104 insertions, 29 deletions
diff --git a/drivers/mtd/ubi/io.c b/drivers/mtd/ubi/io.c
index 0e3a76a9e2f8..915eb64cb001 100644
--- a/drivers/mtd/ubi/io.c
+++ b/drivers/mtd/ubi/io.c
@@ -1,21 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) International Business Machines Corp., 2006
* Copyright (c) Nokia Corporation, 2006, 2007
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
- * the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
* Author: Artem Bityutskiy (Битюцкий Артём)
*/
@@ -208,7 +195,19 @@ retry:
if (ubi_dbg_is_bitflip(ubi)) {
dbg_gen("bit-flip (emulated)");
- err = UBI_IO_BITFLIPS;
+ return UBI_IO_BITFLIPS;
+ }
+
+ if (ubi_dbg_is_read_failure(ubi, MASK_READ_FAILURE)) {
+ ubi_warn(ubi, "cannot read %d bytes from PEB %d:%d (emulated)",
+ len, pnum, offset);
+ return -EIO;
+ }
+
+ if (ubi_dbg_is_eccerr(ubi)) {
+ ubi_warn(ubi, "ECC error (emulated) while reading %d bytes from PEB %d:%d, read %zd bytes",
+ len, pnum, offset, read);
+ return -EBADMSG;
}
}
@@ -548,7 +547,14 @@ int ubi_io_sync_erase(struct ubi_device *ubi, int pnum, int torture)
return -EROFS;
}
- if (ubi->nor_flash) {
+ /*
+ * If the flash is ECC-ed then we have to erase the ECC block before we
+ * can write to it. But the write is in preparation to an erase in the
+ * first place. This means we cannot zero out EC and VID before the
+ * erase and we just have to hope the flash starts erasing from the
+ * start of the page.
+ */
+ if (ubi->nor_flash && ubi->mtd->writesize == 1) {
err = nor_erase_prepare(ubi, pnum);
if (err)
return err;
@@ -788,7 +794,36 @@ int ubi_io_read_ec_hdr(struct ubi_device *ubi, int pnum,
* If there was %-EBADMSG, but the header CRC is still OK, report about
* a bit-flip to force scrubbing on this PEB.
*/
- return read_err ? UBI_IO_BITFLIPS : 0;
+ if (read_err)
+ return UBI_IO_BITFLIPS;
+
+ if (ubi_dbg_is_read_failure(ubi, MASK_READ_FAILURE_EC)) {
+ ubi_warn(ubi, "cannot read EC header from PEB %d (emulated)",
+ pnum);
+ return -EIO;
+ }
+
+ if (ubi_dbg_is_ff(ubi, MASK_IO_FF_EC)) {
+ ubi_warn(ubi, "bit-all-ff (emulated)");
+ return UBI_IO_FF;
+ }
+
+ if (ubi_dbg_is_ff_bitflips(ubi, MASK_IO_FF_BITFLIPS_EC)) {
+ ubi_warn(ubi, "bit-all-ff with error reported by MTD driver (emulated)");
+ return UBI_IO_FF_BITFLIPS;
+ }
+
+ if (ubi_dbg_is_bad_hdr(ubi, MASK_BAD_HDR_EC)) {
+ ubi_warn(ubi, "bad_hdr (emulated)");
+ return UBI_IO_BAD_HDR;
+ }
+
+ if (ubi_dbg_is_bad_hdr_ebadmsg(ubi, MASK_BAD_HDR_EBADMSG_EC)) {
+ ubi_warn(ubi, "bad_hdr with ECC error (emulated)");
+ return UBI_IO_BAD_HDR_EBADMSG;
+ }
+
+ return 0;
}
/**
@@ -827,8 +862,13 @@ int ubi_io_write_ec_hdr(struct ubi_device *ubi, int pnum,
if (err)
return err;
- if (ubi_dbg_power_cut(ubi, POWER_CUT_EC_WRITE))
+ if (ubi_dbg_is_power_cut(ubi, MASK_POWER_CUT_EC)) {
+ ubi_warn(ubi, "emulating a power cut when writing EC header");
+ ubi_ro_mode(ubi);
return -EROFS;
+ }
+
+ memset((char *)ec_hdr + UBI_EC_HDR_SIZE, 0xFF, ubi->ec_hdr_alsize - UBI_EC_HDR_SIZE);
err = ubi_io_write(ubi, ec_hdr, pnum, 0, ubi->ec_hdr_alsize);
return err;
@@ -919,12 +959,7 @@ static int validate_vid_hdr(const struct ubi_device *ubi,
ubi_err(ubi, "bad data_size");
goto bad;
}
- } else if (lnum == used_ebs - 1) {
- if (data_size == 0) {
- ubi_err(ubi, "bad data_size at last LEB");
- goto bad;
- }
- } else {
+ } else if (lnum > used_ebs - 1) {
ubi_err(ubi, "too high lnum");
goto bad;
}
@@ -1040,7 +1075,36 @@ int ubi_io_read_vid_hdr(struct ubi_device *ubi, int pnum,
return -EINVAL;
}
- return read_err ? UBI_IO_BITFLIPS : 0;
+ if (read_err)
+ return UBI_IO_BITFLIPS;
+
+ if (ubi_dbg_is_read_failure(ubi, MASK_READ_FAILURE_VID)) {
+ ubi_warn(ubi, "cannot read VID header from PEB %d (emulated)",
+ pnum);
+ return -EIO;
+ }
+
+ if (ubi_dbg_is_ff(ubi, MASK_IO_FF_VID)) {
+ ubi_warn(ubi, "bit-all-ff (emulated)");
+ return UBI_IO_FF;
+ }
+
+ if (ubi_dbg_is_ff_bitflips(ubi, MASK_IO_FF_BITFLIPS_VID)) {
+ ubi_warn(ubi, "bit-all-ff with error reported by MTD driver (emulated)");
+ return UBI_IO_FF_BITFLIPS;
+ }
+
+ if (ubi_dbg_is_bad_hdr(ubi, MASK_BAD_HDR_VID)) {
+ ubi_warn(ubi, "bad_hdr (emulated)");
+ return UBI_IO_BAD_HDR;
+ }
+
+ if (ubi_dbg_is_bad_hdr_ebadmsg(ubi, MASK_BAD_HDR_EBADMSG_VID)) {
+ ubi_warn(ubi, "bad_hdr with ECC error (emulated)");
+ return UBI_IO_BAD_HDR_EBADMSG;
+ }
+
+ return 0;
}
/**
@@ -1082,8 +1146,19 @@ int ubi_io_write_vid_hdr(struct ubi_device *ubi, int pnum,
if (err)
return err;
- if (ubi_dbg_power_cut(ubi, POWER_CUT_VID_WRITE))
+ if (ubi_dbg_is_power_cut(ubi, MASK_POWER_CUT_VID)) {
+ ubi_warn(ubi, "emulating a power cut when writing VID header");
+ ubi_ro_mode(ubi);
return -EROFS;
+ }
+
+ if (ubi->vid_hdr_shift) {
+ memset((char *)p, 0xFF, ubi->vid_hdr_shift);
+ memset((char *)p + ubi->vid_hdr_shift + UBI_VID_HDR_SIZE, 0xFF,
+ ubi->vid_hdr_alsize - (ubi->vid_hdr_shift + UBI_VID_HDR_SIZE));
+ } else {
+ memset((char *)p + UBI_VID_HDR_SIZE, 0xFF, ubi->vid_hdr_alsize - UBI_VID_HDR_SIZE);
+ }
err = ubi_io_write(ubi, p, pnum, ubi->vid_hdr_aloffset,
ubi->vid_hdr_alsize);
@@ -1158,7 +1233,7 @@ fail:
* @ubi: UBI device description object
* @pnum: the physical eraseblock number to check
*
- * This function returns zero if the erase counter header is all right and and
+ * This function returns zero if the erase counter header is all right and
* a negative error code if not or if an error occurred.
*/
static int self_check_peb_ec_hdr(const struct ubi_device *ubi, int pnum)
@@ -1310,7 +1385,7 @@ static int self_check_write(struct ubi_device *ubi, const void *buf, int pnum,
if (!ubi_dbg_chk_io(ubi))
return 0;
- buf1 = __vmalloc(len, GFP_NOFS, PAGE_KERNEL);
+ buf1 = __vmalloc(len, GFP_NOFS);
if (!buf1) {
ubi_err(ubi, "cannot allocate memory to check writes");
return 0;
@@ -1374,7 +1449,7 @@ int ubi_self_check_all_ff(struct ubi_device *ubi, int pnum, int offset, int len)
if (!ubi_dbg_chk_io(ubi))
return 0;
- buf = __vmalloc(len, GFP_NOFS, PAGE_KERNEL);
+ buf = __vmalloc(len, GFP_NOFS);
if (!buf) {
ubi_err(ubi, "cannot allocate memory to check for 0xFFs");
return 0;