UBI: try to reveal buggy MTD drivers
[deliverable/linux.git] / drivers / mtd / ubi / io.c
index c2960ac9f39c9e863216c91b607e157c0ff65271..339a74f11c0b94a92b742d039d598637e0fe38d7 100644 (file)
@@ -146,6 +146,28 @@ int ubi_io_read(const struct ubi_device *ubi, void *buf, int pnum, int offset,
        if (err)
                return err;
 
+       /*
+        * Deliberately corrupt the buffer to improve robustness. Indeed, if we
+        * do not do this, the following may happen:
+        * 1. The buffer contains data from previous operation, e.g., read from
+        *    another PEB previously. The data looks like expected, e.g., if we
+        *    just do not read anything and return - the caller would not
+        *    notice this. E.g., if we are reading a VID header, the buffer may
+        *    contain a valid VID header from another PEB.
+        * 2. The driver is buggy and returns us success or -EBADMSG or
+        *    -EUCLEAN, but it does not actually put any data to the buffer.
+        *
+        * This may confuse UBI or upper layers - they may think the buffer
+        * contains valid data while in fact it is just old data. This is
+        * especially possible because UBI (and UBIFS) relies on CRC, and
+        * treats data as correct even in case of ECC errors if the CRC is
+        * correct.
+        *
+        * Try to prevent this situation by changing the first byte of the
+        * buffer.
+        */
+       *((uint8_t *)buf) ^= 0xFF;
+
        addr = (loff_t)pnum * ubi->peb_size + offset;
 retry:
        err = ubi->mtd->read(ubi->mtd, addr, len, &read, buf);
@@ -480,12 +502,26 @@ static int nor_erase_prepare(struct ubi_device *ubi, int pnum)
        size_t written;
        loff_t addr;
        uint32_t data = 0;
+       /*
+        * Note, we cannot generally define VID header buffers on stack,
+        * because of the way we deal with these buffers (see the header
+        * comment in this file). But we know this is a NOR-specific piece of
+        * code, so we can do this. But yes, this is error-prone and we should
+        * (pre-)allocate VID header buffer instead.
+        */
        struct ubi_vid_hdr vid_hdr;
 
-       addr = (loff_t)pnum * ubi->peb_size + ubi->vid_hdr_aloffset;
+       /*
+        * It is important to first invalidate the EC header, and then the VID
+        * header. Otherwise a power cut may lead to valid EC header and
+        * invalid VID header, in which case UBI will treat this PEB as
+        * corrupted and will try to preserve it, and print scary warnings (see
+        * the header comment in scan.c for more information).
+        */
+       addr = (loff_t)pnum * ubi->peb_size;
        err = ubi->mtd->write(ubi->mtd, addr, 4, &written, (void *)&data);
        if (!err) {
-               addr -= ubi->vid_hdr_aloffset;
+               addr += ubi->vid_hdr_aloffset;
                err = ubi->mtd->write(ubi->mtd, addr, 4, &written,
                                      (void *)&data);
                if (!err)
@@ -494,18 +530,26 @@ static int nor_erase_prepare(struct ubi_device *ubi, int pnum)
 
        /*
         * We failed to write to the media. This was observed with Spansion
-        * S29GL512N NOR flash. Most probably the eraseblock erasure was
-        * interrupted at a very inappropriate moment, so it became unwritable.
-        * In this case we probably anyway have garbage in this PEB.
+        * S29GL512N NOR flash. Most probably the previously eraseblock erasure
+        * was interrupted at a very inappropriate moment, so it became
+        * unwritable. In this case we probably anyway have garbage in this
+        * PEB.
         */
        err1 = ubi_io_read_vid_hdr(ubi, pnum, &vid_hdr, 0);
-       if (err1 == UBI_IO_BAD_HDR_EBADMSG || err1 == UBI_IO_BAD_HDR)
-               /*
-                * The VID header is corrupted, so we can safely erase this
-                * PEB and not afraid that it will be treated as a valid PEB in
-                * case of an unclean reboot.
-                */
-               return 0;
+       if (err1 == UBI_IO_BAD_HDR_EBADMSG || err1 == UBI_IO_BAD_HDR ||
+           err1 == UBI_IO_FF) {
+               struct ubi_ec_hdr ec_hdr;
+
+               err1 = ubi_io_read_ec_hdr(ubi, pnum, &ec_hdr, 0);
+               if (err1 == UBI_IO_BAD_HDR_EBADMSG || err1 == UBI_IO_BAD_HDR ||
+                   err1 == UBI_IO_FF)
+                       /*
+                        * Both VID and EC headers are corrupted, so we can
+                        * safely erase this PEB and not afraid that it will be
+                        * treated as a valid PEB in case of an unclean reboot.
+                        */
+                       return 0;
+       }
 
        /*
         * The PEB contains a valid VID header, but we cannot invalidate it.
This page took 0.025445 seconds and 5 git commands to generate.