Merge tag 'ti-v2020.07-rc3' of https://gitlab.denx.de/u-boot/custodians/u-boot-ti
[oweals/u-boot.git] / drivers / mtd / ubi / vtbl.c
index e6c8f5bbe0e00f54d32b180a95f1a9f28b7633b7..a2b5352cb2da4d5c4545343fc43c6ce1eda2fd7d 100644 (file)
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0+
 /*
  * Copyright (c) International Business Machines Corp., 2006
  * Copyright (c) Nokia Corporation, 2006, 2007
  *
- * SPDX-License-Identifier:    GPL-2.0+
- *
  * Author: Artem Bityutskiy (Битюцкий Артём)
  */
 
  * eraseblock stores one volume table copy, i.e. LEB 0 and LEB 1 duplicate each
  * other. This redundancy guarantees robustness to unclean reboots. The volume
  * table is basically an array of volume table records. Each record contains
- * full information about the volume and protected by a CRC checksum.
+ * full information about the volume and protected by a CRC checksum. Note,
+ * nowadays we use the atomic LEB change operation when updating the volume
+ * table, so we do not really need 2 LEBs anymore, but we preserve the older
+ * design for the backward compatibility reasons.
  *
- * The volume table is changed, it is first changed in RAM. Then LEB 0 is
+ * When the volume table is changed, it is first changed in RAM. Then LEB 0 is
  * erased, and the updated volume table is written back to LEB 0. Then same for
  * LEB 1. This scheme guarantees recoverability from unclean reboots.
  *
  * damaged.
  */
 
-#define __UBOOT__
 #ifndef __UBOOT__
+#include <log.h>
+#include <dm/devres.h>
 #include <linux/crc32.h>
 #include <linux/err.h>
 #include <linux/slab.h>
 #include <asm/div64.h>
+#include <u-boot/crc.h>
 #else
 #include <ubi_uboot.h>
+#include <linux/bug.h>
 #endif
 
 #include <linux/err.h>
@@ -61,6 +66,26 @@ static void self_vtbl_check(const struct ubi_device *ubi);
 /* Empty volume table record */
 static struct ubi_vtbl_record empty_vtbl_record;
 
+/**
+ * ubi_update_layout_vol - helper for updatting layout volumes on flash
+ * @ubi: UBI device description object
+ */
+static int ubi_update_layout_vol(struct ubi_device *ubi)
+{
+       struct ubi_volume *layout_vol;
+       int i, err;
+
+       layout_vol = ubi->volumes[vol_id2idx(ubi, UBI_LAYOUT_VOLUME_ID)];
+       for (i = 0; i < UBI_LAYOUT_VOLUME_EBS; i++) {
+               err = ubi_eba_atomic_leb_change(ubi, layout_vol, i, ubi->vtbl,
+                                               ubi->vtbl_size);
+               if (err)
+                       return err;
+       }
+
+       return 0;
+}
+
 /**
  * ubi_change_vtbl_record - change volume table record.
  * @ubi: UBI device description object
@@ -75,12 +100,10 @@ static struct ubi_vtbl_record empty_vtbl_record;
 int ubi_change_vtbl_record(struct ubi_device *ubi, int idx,
                           struct ubi_vtbl_record *vtbl_rec)
 {
-       int i, err;
+       int err;
        uint32_t crc;
-       struct ubi_volume *layout_vol;
 
        ubi_assert(idx >= 0 && idx < ubi->vtbl_slots);
-       layout_vol = ubi->volumes[vol_id2idx(ubi, UBI_LAYOUT_VOLUME_ID)];
 
        if (!vtbl_rec)
                vtbl_rec = &empty_vtbl_record;
@@ -90,19 +113,10 @@ int ubi_change_vtbl_record(struct ubi_device *ubi, int idx,
        }
 
        memcpy(&ubi->vtbl[idx], vtbl_rec, sizeof(struct ubi_vtbl_record));
-       for (i = 0; i < UBI_LAYOUT_VOLUME_EBS; i++) {
-               err = ubi_eba_unmap_leb(ubi, layout_vol, i);
-               if (err)
-                       return err;
-
-               err = ubi_eba_write_leb(ubi, layout_vol, i, ubi->vtbl, 0,
-                                       ubi->vtbl_size);
-               if (err)
-                       return err;
-       }
+       err = ubi_update_layout_vol(ubi);
 
        self_vtbl_check(ubi);
-       return 0;
+       return err ? err : 0;
 }
 
 /**
@@ -117,9 +131,7 @@ int ubi_change_vtbl_record(struct ubi_device *ubi, int idx,
 int ubi_vtbl_rename_volumes(struct ubi_device *ubi,
                            struct list_head *rename_list)
 {
-       int i, err;
        struct ubi_rename_entry *re;
-       struct ubi_volume *layout_vol;
 
        list_for_each_entry(re, rename_list, list) {
                uint32_t crc;
@@ -141,19 +153,7 @@ int ubi_vtbl_rename_volumes(struct ubi_device *ubi,
                vtbl_rec->crc = cpu_to_be32(crc);
        }
 
-       layout_vol = ubi->volumes[vol_id2idx(ubi, UBI_LAYOUT_VOLUME_ID)];
-       for (i = 0; i < UBI_LAYOUT_VOLUME_EBS; i++) {
-               err = ubi_eba_unmap_leb(ubi, layout_vol, i);
-               if (err)
-                       return err;
-
-               err = ubi_eba_write_leb(ubi, layout_vol, i, ubi->vtbl, 0,
-                                       ubi->vtbl_size);
-               if (err)
-                       return err;
-       }
-
-       return 0;
+       return ubi_update_layout_vol(ubi);
 }
 
 /**
@@ -185,7 +185,7 @@ static int vtbl_check(const struct ubi_device *ubi,
 
                crc = crc32(UBI_CRC32_INIT, &vtbl[i], UBI_VTBL_RECORD_SIZE_CRC);
                if (be32_to_cpu(vtbl[i].crc) != crc) {
-                       ubi_err("bad CRC at record %u: %#08x, not %#08x",
+                       ubi_err(ubi, "bad CRC at record %u: %#08x, not %#08x",
                                 i, crc, be32_to_cpu(vtbl[i].crc));
                        ubi_dump_vtbl_record(&vtbl[i], i);
                        return 1;
@@ -219,7 +219,7 @@ static int vtbl_check(const struct ubi_device *ubi,
 
                n = ubi->leb_size % alignment;
                if (data_pad != n) {
-                       ubi_err("bad data_pad, has to be %d", n);
+                       ubi_err(ubi, "bad data_pad, has to be %d", n);
                        err = 6;
                        goto bad;
                }
@@ -235,7 +235,7 @@ static int vtbl_check(const struct ubi_device *ubi,
                }
 
                if (reserved_pebs > ubi->good_peb_count) {
-                       ubi_err("too large reserved_pebs %d, good PEBs %d",
+                       ubi_err(ubi, "too large reserved_pebs %d, good PEBs %d",
                                reserved_pebs, ubi->good_peb_count);
                        err = 9;
                        goto bad;
@@ -269,7 +269,7 @@ static int vtbl_check(const struct ubi_device *ubi,
 #else
                            !strncmp((char *)vtbl[i].name, vtbl[n].name, len1)) {
 #endif
-                               ubi_err("volumes %d and %d have the same name \"%s\"",
+                               ubi_err(ubi, "volumes %d and %d have the same name \"%s\"",
                                        i, n, vtbl[i].name);
                                ubi_dump_vtbl_record(&vtbl[i], i);
                                ubi_dump_vtbl_record(&vtbl[n], n);
@@ -281,7 +281,7 @@ static int vtbl_check(const struct ubi_device *ubi,
        return 0;
 
 bad:
-       ubi_err("volume table check failed: record %d, error %d", i, err);
+       ubi_err(ubi, "volume table check failed: record %d, error %d", i, err);
        ubi_dump_vtbl_record(&vtbl[i], i);
        return -EINVAL;
 }
@@ -445,11 +445,11 @@ static struct ubi_vtbl_record *process_lvol(struct ubi_device *ubi,
                        leb_corrupted[1] = memcmp(leb[0], leb[1],
                                                  ubi->vtbl_size);
                if (leb_corrupted[1]) {
-                       ubi_warn("volume table copy #2 is corrupted");
+                       ubi_warn(ubi, "volume table copy #2 is corrupted");
                        err = create_vtbl(ubi, ai, 1, leb[0]);
                        if (err)
                                goto out_free;
-                       ubi_msg("volume table was restored");
+                       ubi_msg(ubi, "volume table was restored");
                }
 
                /* Both LEB 1 and LEB 2 are OK and consistent */
@@ -464,15 +464,15 @@ static struct ubi_vtbl_record *process_lvol(struct ubi_device *ubi,
                }
                if (leb_corrupted[1]) {
                        /* Both LEB 0 and LEB 1 are corrupted */
-                       ubi_err("both volume tables are corrupted");
+                       ubi_err(ubi, "both volume tables are corrupted");
                        goto out_free;
                }
 
-               ubi_warn("volume table copy #1 is corrupted");
+               ubi_warn(ubi, "volume table copy #1 is corrupted");
                err = create_vtbl(ubi, ai, 0, leb[1]);
                if (err)
                        goto out_free;
-               ubi_msg("volume table was restored");
+               ubi_msg(ubi, "volume table was restored");
 
                vfree(leb[0]);
                return leb[1];
@@ -558,10 +558,13 @@ static int init_volumes(struct ubi_device *ubi,
                vol->name[vol->name_len] = '\0';
                vol->vol_id = i;
 
+               if (vtbl[i].flags & UBI_VTBL_SKIP_CRC_CHECK_FLG)
+                       vol->skip_check = 1;
+
                if (vtbl[i].flags & UBI_VTBL_AUTORESIZE_FLG) {
                        /* Auto re-size flag may be set only for one volume */
                        if (ubi->autoresize_vol_id != -1) {
-                               ubi_err("more than one auto-resize volume (%d and %d)",
+                               ubi_err(ubi, "more than one auto-resize volume (%d and %d)",
                                        ubi->autoresize_vol_id, i);
                                kfree(vol);
                                return -EINVAL;
@@ -590,7 +593,7 @@ static int init_volumes(struct ubi_device *ubi,
 
                /* Static volumes only */
                av = ubi_find_av(ai, i);
-               if (!av) {
+               if (!av || !av->leb_count) {
                        /*
                         * No eraseblocks belonging to this volume found. We
                         * don't actually know whether this static volume is
@@ -607,7 +610,7 @@ static int init_volumes(struct ubi_device *ubi,
                         * We found a static volume which misses several
                         * eraseblocks. Treat it as corrupted.
                         */
-                       ubi_warn("static volume %d misses %d LEBs - corrupted",
+                       ubi_warn(ubi, "static volume %d misses %d LEBs - corrupted",
                                 av->vol_id, av->used_ebs - av->leb_count);
                        vol->corrupted = 1;
                        continue;
@@ -645,10 +648,10 @@ static int init_volumes(struct ubi_device *ubi,
        vol->ubi = ubi;
 
        if (reserved_pebs > ubi->avail_pebs) {
-               ubi_err("not enough PEBs, required %d, available %d",
+               ubi_err(ubi, "not enough PEBs, required %d, available %d",
                        reserved_pebs, ubi->avail_pebs);
                if (ubi->corr_peb_count)
-                       ubi_err("%d PEBs are corrupted and not used",
+                       ubi_err(ubi, "%d PEBs are corrupted and not used",
                                ubi->corr_peb_count);
        }
        ubi->rsvd_pebs += reserved_pebs;
@@ -693,7 +696,7 @@ static int check_av(const struct ubi_volume *vol,
        return 0;
 
 bad:
-       ubi_err("bad attaching information, error %d", err);
+       ubi_err(vol->ubi, "bad attaching information, error %d", err);
        ubi_dump_av(av);
        ubi_dump_vol_info(vol);
        return -EINVAL;
@@ -717,14 +720,15 @@ static int check_attaching_info(const struct ubi_device *ubi,
        struct ubi_volume *vol;
 
        if (ai->vols_found > UBI_INT_VOL_COUNT + ubi->vtbl_slots) {
-               ubi_err("found %d volumes while attaching, maximum is %d + %d",
+               ubi_err(ubi, "found %d volumes while attaching, maximum is %d + %d",
                        ai->vols_found, UBI_INT_VOL_COUNT, ubi->vtbl_slots);
                return -EINVAL;
        }
 
        if (ai->highest_vol_id >= ubi->vtbl_slots + UBI_INT_VOL_COUNT &&
            ai->highest_vol_id < UBI_INTERNAL_VOL_START) {
-               ubi_err("too large volume ID %d found", ai->highest_vol_id);
+               ubi_err(ubi, "too large volume ID %d found",
+                       ai->highest_vol_id);
                return -EINVAL;
        }
 
@@ -752,7 +756,7 @@ static int check_attaching_info(const struct ubi_device *ubi,
                         * reboot while the volume was being removed. Discard
                         * these eraseblocks.
                         */
-                       ubi_msg("finish volume %d removal", av->vol_id);
+                       ubi_msg(ubi, "finish volume %d removal", av->vol_id);
                        ubi_remove_av(ai, av);
                } else if (av) {
                        err = check_av(vol, av);
@@ -806,13 +810,13 @@ int ubi_read_volume_table(struct ubi_device *ubi, struct ubi_attach_info *ai)
                        if (IS_ERR(ubi->vtbl))
                                return PTR_ERR(ubi->vtbl);
                } else {
-                       ubi_err("the layout volume was not found");
+                       ubi_err(ubi, "the layout volume was not found");
                        return -EINVAL;
                }
        } else {
                if (av->leb_count > UBI_LAYOUT_VOLUME_EBS) {
                        /* This must not happen with proper UBI images */
-                       ubi_err("too many LEBs (%d) in layout volume",
+                       ubi_err(ubi, "too many LEBs (%d) in layout volume",
                                av->leb_count);
                        return -EINVAL;
                }
@@ -861,7 +865,7 @@ static void self_vtbl_check(const struct ubi_device *ubi)
                return;
 
        if (vtbl_check(ubi, ubi->vtbl)) {
-               ubi_err("self-check failed");
+               ubi_err(ubi, "self-check failed");
                BUG();
        }
 }