summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/Makefile1
-rw-r--r--drivers/mtd/ubispl/Makefile1
-rw-r--r--drivers/mtd/ubispl/ubi-wrapper.h106
-rw-r--r--drivers/mtd/ubispl/ubispl.c926
-rw-r--r--drivers/mtd/ubispl/ubispl.h136
5 files changed, 1170 insertions, 0 deletions
diff --git a/drivers/Makefile b/drivers/Makefile
index b3ac8a6..ad5cbae 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -27,6 +27,7 @@ obj-$(CONFIG_SPL_MTD_SUPPORT) += mtd/
obj-$(CONFIG_SPL_NAND_SUPPORT) += mtd/nand/
obj-$(CONFIG_SPL_ONENAND_SUPPORT) += mtd/onenand/
obj-$(CONFIG_SPL_SPI_FLASH_SUPPORT) += mtd/spi/
+obj-$(CONFIG_SPL_UBI) += mtd/ubispl/
obj-$(CONFIG_SPL_DMA_SUPPORT) += dma/
obj-$(CONFIG_SPL_ETH_SUPPORT) += net/
obj-$(CONFIG_SPL_ETH_SUPPORT) += net/phy/
diff --git a/drivers/mtd/ubispl/Makefile b/drivers/mtd/ubispl/Makefile
new file mode 100644
index 0000000..740dbed
--- /dev/null
+++ b/drivers/mtd/ubispl/Makefile
@@ -0,0 +1 @@
+obj-y += ubispl.o ../ubi/crc32.o
diff --git a/drivers/mtd/ubispl/ubi-wrapper.h b/drivers/mtd/ubispl/ubi-wrapper.h
new file mode 100644
index 0000000..72b9dcb
--- /dev/null
+++ b/drivers/mtd/ubispl/ubi-wrapper.h
@@ -0,0 +1,106 @@
+/*
+ * The parts taken from the kernel implementation are:
+ *
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * UBISPL specific defines:
+ *
+ * Copyright (c) Thomas Gleixner <tglx@linutronix.de>
+ *
+ * SPDX-License-Identifier: GPL 2.0+ BSD-3-Clause
+ */
+
+/*
+ * Contains various defines copy&pasted from ubi.h and ubi-user.h to make
+ * the upstream fastboot code happy.
+ */
+#ifndef __UBOOT_UBI_WRAPPER_H
+#define __UBOOT_UBI_WRAPPER_H
+
+/*
+ * Error codes returned by the I/O sub-system.
+ *
+ * UBI_IO_FF: the read region of flash contains only 0xFFs
+ * UBI_IO_FF_BITFLIPS: the same as %UBI_IO_FF, but also also there was a data
+ * integrity error reported by the MTD driver
+ * (uncorrectable ECC error in case of NAND)
+ * UBI_IO_BAD_HDR: the EC or VID header is corrupted (bad magic or CRC)
+ * UBI_IO_BAD_HDR_EBADMSG: the same as %UBI_IO_BAD_HDR, but also there was a
+ * data integrity error reported by the MTD driver
+ * (uncorrectable ECC error in case of NAND)
+ * UBI_IO_BITFLIPS: bit-flips were detected and corrected
+ *
+ * UBI_FASTMAP_ANCHOR: u-boot SPL add on to tell the caller that the fastmap
+ * anchor block has been found
+ *
+ * Note, it is probably better to have bit-flip and ebadmsg as flags which can
+ * be or'ed with other error code. But this is a big change because there are
+ * may callers, so it does not worth the risk of introducing a bug
+ */
+enum {
+ UBI_IO_FF = 1,
+ UBI_IO_FF_BITFLIPS,
+ UBI_IO_BAD_HDR,
+ UBI_IO_BAD_HDR_EBADMSG,
+ UBI_IO_BITFLIPS,
+ UBI_FASTMAP_ANCHOR,
+};
+
+/*
+ * UBI volume type constants.
+ *
+ * @UBI_DYNAMIC_VOLUME: dynamic volume
+ * @UBI_STATIC_VOLUME: static volume
+ */
+enum {
+ UBI_DYNAMIC_VOLUME = 3,
+ UBI_STATIC_VOLUME = 4,
+};
+
+/*
+ * Return codes of the fastmap sub-system
+ *
+ * UBI_NO_FASTMAP: No fastmap super block was found
+ * UBI_BAD_FASTMAP: A fastmap was found but it's unusable
+ */
+enum {
+ UBI_NO_FASTMAP = 1,
+ UBI_BAD_FASTMAP,
+};
+
+/**
+ * struct ubi_fastmap_layout - in-memory fastmap data structure.
+ * @e: PEBs used by the current fastmap
+ * @to_be_tortured: if non-zero tortured this PEB
+ * @used_blocks: number of used PEBs
+ * @max_pool_size: maximal size of the user pool
+ * @max_wl_pool_size: maximal size of the pool used by the WL sub-system
+ */
+struct ubi_fastmap_layout {
+ struct ubi_wl_entry *e[UBI_FM_MAX_BLOCKS];
+ int to_be_tortured[UBI_FM_MAX_BLOCKS];
+ int used_blocks;
+ int max_pool_size;
+ int max_wl_pool_size;
+};
+
+/**
+ * struct ubi_fm_pool - in-memory fastmap pool
+ * @pebs: PEBs in this pool
+ * @used: number of used PEBs
+ * @size: total number of PEBs in this pool
+ * @max_size: maximal size of the pool
+ *
+ * A pool gets filled with up to max_size.
+ * If all PEBs within the pool are used a new fastmap will be written
+ * to the flash and the pool gets refilled with empty PEBs.
+ *
+ */
+struct ubi_fm_pool {
+ int pebs[UBI_FM_MAX_POOL_SIZE];
+ int used;
+ int size;
+ int max_size;
+};
+
+#endif
diff --git a/drivers/mtd/ubispl/ubispl.c b/drivers/mtd/ubispl/ubispl.c
new file mode 100644
index 0000000..a81a8e7
--- /dev/null
+++ b/drivers/mtd/ubispl/ubispl.c
@@ -0,0 +1,926 @@
+/*
+ * Copyright (c) Thomas Gleixner <tglx@linutronix.de>
+ *
+ * The parts taken from the kernel implementation are:
+ *
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * SPDX-License-Identifier: GPL 2.0+ BSD-3-Clause
+ */
+
+#include <common.h>
+#include <errno.h>
+#include <ubispl.h>
+
+#include <linux/crc32.h>
+
+#include "ubispl.h"
+
+/**
+ * ubi_calc_fm_size - calculates the fastmap size in bytes for an UBI device.
+ * @ubi: UBI device description object
+ */
+static size_t ubi_calc_fm_size(struct ubi_scan_info *ubi)
+{
+ size_t size;
+
+ size = sizeof(struct ubi_fm_sb) +
+ sizeof(struct ubi_fm_hdr) +
+ sizeof(struct ubi_fm_scan_pool) +
+ sizeof(struct ubi_fm_scan_pool) +
+ (ubi->peb_count * sizeof(struct ubi_fm_ec)) +
+ (sizeof(struct ubi_fm_eba) +
+ (ubi->peb_count * sizeof(__be32))) +
+ sizeof(struct ubi_fm_volhdr) * UBI_MAX_VOLUMES;
+ return roundup(size, ubi->leb_size);
+}
+
+static int ubi_io_read(struct ubi_scan_info *ubi, void *buf, int pnum,
+ unsigned long from, unsigned long len)
+{
+ return ubi->read(pnum + ubi->peb_offset, from, len, buf);
+}
+
+static int ubi_io_is_bad(struct ubi_scan_info *ubi, int peb)
+{
+ return peb >= ubi->peb_count || peb < 0;
+}
+
+static int ubi_io_read_vid_hdr(struct ubi_scan_info *ubi, int pnum,
+ struct ubi_vid_hdr *vh, int unused)
+{
+ u32 magic;
+ int res;
+
+ /* No point in rescanning a corrupt block */
+ if (test_bit(pnum, ubi->corrupt))
+ return UBI_IO_BAD_HDR;
+ /*
+ * If the block has been scanned already, no need to rescan
+ */
+ if (test_and_set_bit(pnum, ubi->scanned))
+ return 0;
+
+ res = ubi_io_read(ubi, vh, pnum, ubi->vid_offset, sizeof(*vh));
+
+ /*
+ * Bad block, unrecoverable ECC error, skip the block
+ */
+ if (res) {
+ ubi_dbg("Skipping bad or unreadable block %d", pnum);
+ vh->magic = 0;
+ generic_set_bit(pnum, ubi->corrupt);
+ return res;
+ }
+
+ /* Magic number available ? */
+ magic = be32_to_cpu(vh->magic);
+ if (magic != UBI_VID_HDR_MAGIC) {
+ generic_set_bit(pnum, ubi->corrupt);
+ if (magic == 0xffffffff)
+ return UBI_IO_FF;
+ ubi_msg("Bad magic in block 0%d %08x", pnum, magic);
+ return UBI_IO_BAD_HDR;
+ }
+
+ /* Header CRC correct ? */
+ if (crc32(UBI_CRC32_INIT, vh, UBI_VID_HDR_SIZE_CRC) !=
+ be32_to_cpu(vh->hdr_crc)) {
+ ubi_msg("Bad CRC in block 0%d", pnum);
+ generic_set_bit(pnum, ubi->corrupt);
+ return UBI_IO_BAD_HDR;
+ }
+
+ ubi_dbg("RV: pnum: %i sqnum %llu", pnum, be64_to_cpu(vh->sqnum));
+
+ return 0;
+}
+
+static int ubi_rescan_fm_vid_hdr(struct ubi_scan_info *ubi,
+ struct ubi_vid_hdr *vh,
+ u32 fm_pnum, u32 fm_vol_id, u32 fm_lnum)
+{
+ int res;
+
+ if (ubi_io_is_bad(ubi, fm_pnum))
+ return -EINVAL;
+
+ res = ubi_io_read_vid_hdr(ubi, fm_pnum, vh, 0);
+ if (!res) {
+ /* Check volume id, volume type and lnum */
+ if (be32_to_cpu(vh->vol_id) == fm_vol_id &&
+ vh->vol_type == UBI_VID_STATIC &&
+ be32_to_cpu(vh->lnum) == fm_lnum)
+ return 0;
+ ubi_dbg("RS: PEB %u vol: %u : %u typ %u lnum %u %u",
+ fm_pnum, fm_vol_id, vh->vol_type,
+ be32_to_cpu(vh->vol_id),
+ fm_lnum, be32_to_cpu(vh->lnum));
+ }
+ return res;
+}
+
+/* Insert the logic block into the volume info */
+static int ubi_add_peb_to_vol(struct ubi_scan_info *ubi,
+ struct ubi_vid_hdr *vh, u32 vol_id,
+ u32 pnum, u32 lnum)
+{
+ struct ubi_vol_info *vi = ubi->volinfo + vol_id;
+ u32 *ltp;
+
+ /*
+ * If the volume is larger than expected, yell and give up :(
+ */
+ if (lnum >= UBI_MAX_VOL_LEBS) {
+ ubi_warn("Vol: %u LEB %d > %d", vol_id, lnum, UBI_MAX_VOL_LEBS);
+ return -EINVAL;
+ }
+
+ ubi_dbg("SC: Add PEB %u to Vol %u as LEB %u fnd %d sc %d",
+ pnum, vol_id, lnum, !!test_bit(lnum, vi->found),
+ !!test_bit(pnum, ubi->scanned));
+
+ /* Points to the translation entry */
+ ltp = vi->lebs_to_pebs + lnum;
+
+ /* If the block is already assigned, check sqnum */
+ if (__test_and_set_bit(lnum, vi->found)) {
+ u32 cur_pnum = *ltp;
+ struct ubi_vid_hdr *cur = ubi->blockinfo + cur_pnum;
+
+ /*
+ * If the current block hase not yet been scanned, we
+ * need to do that. The other block might be stale or
+ * the current block corrupted and the FM not yet
+ * updated.
+ */
+ if (!test_bit(cur_pnum, ubi->scanned)) {
+ /*
+ * If the scan fails, we use the valid block
+ */
+ if (ubi_rescan_fm_vid_hdr(ubi, cur, cur_pnum, vol_id,
+ lnum)) {
+ *ltp = pnum;
+ return 0;
+ }
+ }
+
+ /*
+ * Should not happen ....
+ */
+ if (test_bit(cur_pnum, ubi->corrupt)) {
+ *ltp = pnum;
+ return 0;
+ }
+
+ ubi_dbg("Vol %u LEB %u PEB %u->sqnum %llu NPEB %u->sqnum %llu",
+ vol_id, lnum, cur_pnum, be64_to_cpu(cur->sqnum), pnum,
+ be64_to_cpu(vh->sqnum));
+
+ /*
+ * Compare sqnum and take the newer one
+ */
+ if (be64_to_cpu(cur->sqnum) < be64_to_cpu(vh->sqnum))
+ *ltp = pnum;
+ } else {
+ *ltp = pnum;
+ if (lnum > vi->last_block)
+ vi->last_block = lnum;
+ }
+
+ return 0;
+}
+
+static int ubi_scan_vid_hdr(struct ubi_scan_info *ubi, struct ubi_vid_hdr *vh,
+ u32 pnum)
+{
+ u32 vol_id, lnum;
+ int res;
+
+ if (ubi_io_is_bad(ubi, pnum))
+ return -EINVAL;
+
+ res = ubi_io_read_vid_hdr(ubi, pnum, vh, 0);
+ if (res)
+ return res;
+
+ /* Get volume id */
+ vol_id = be32_to_cpu(vh->vol_id);
+
+ /* If this is the fastmap anchor, return right away */
+ if (vol_id == UBI_FM_SB_VOLUME_ID)
+ return ubi->fm_enabled ? UBI_FASTMAP_ANCHOR : 0;
+
+ /* We only care about static volumes with an id < UBI_SPL_VOL_IDS */
+ if (vol_id >= UBI_SPL_VOL_IDS || vh->vol_type != UBI_VID_STATIC)
+ return 0;
+
+ /* We are only interested in the volumes to load */
+ if (!test_bit(vol_id, ubi->toload))
+ return 0;
+
+ lnum = be32_to_cpu(vh->lnum);
+ return ubi_add_peb_to_vol(ubi, vh, vol_id, pnum, lnum);
+}
+
+static int assign_aeb_to_av(struct ubi_scan_info *ubi, u32 pnum, u32 lnum,
+ u32 vol_id, u32 vol_type, u32 used)
+{
+ struct ubi_vid_hdr *vh;
+
+ if (ubi_io_is_bad(ubi, pnum))
+ return -EINVAL;
+
+ ubi->fastmap_pebs++;
+
+ if (vol_id >= UBI_SPL_VOL_IDS || vol_type != UBI_STATIC_VOLUME)
+ return 0;
+
+ /* We are only interested in the volumes to load */
+ if (!test_bit(vol_id, ubi->toload))
+ return 0;
+
+ vh = ubi->blockinfo + pnum;
+
+ return ubi_scan_vid_hdr(ubi, vh, pnum);
+}
+
+static int scan_pool(struct ubi_scan_info *ubi, __be32 *pebs, int pool_size)
+{
+ struct ubi_vid_hdr *vh;
+ u32 pnum;
+ int i;
+
+ ubi_dbg("Scanning pool size: %d", pool_size);
+
+ for (i = 0; i < pool_size; i++) {
+ pnum = be32_to_cpu(pebs[i]);
+
+ if (ubi_io_is_bad(ubi, pnum)) {
+ ubi_err("FM: Bad PEB in fastmap pool! %u", pnum);
+ return UBI_BAD_FASTMAP;
+ }
+
+ vh = ubi->blockinfo + pnum;
+ /*
+ * We allow the scan to fail here. The loader will notice
+ * and look for a replacement.
+ */
+ ubi_scan_vid_hdr(ubi, vh, pnum);
+ }
+ return 0;
+}
+
+/*
+ * Fastmap code is stolen from Linux kernel and this stub structure is used
+ * to make it happy.
+ */
+struct ubi_attach_info {
+ int i;
+};
+
+static int ubi_attach_fastmap(struct ubi_scan_info *ubi,
+ struct ubi_attach_info *ai,
+ struct ubi_fastmap_layout *fm)
+{
+ struct ubi_fm_hdr *fmhdr;
+ struct ubi_fm_scan_pool *fmpl1, *fmpl2;
+ struct ubi_fm_ec *fmec;
+ struct ubi_fm_volhdr *fmvhdr;
+ struct ubi_fm_eba *fm_eba;
+ int ret, i, j, pool_size, wl_pool_size;
+ size_t fm_pos = 0, fm_size = ubi->fm_size;
+ void *fm_raw = ubi->fm_buf;
+
+ memset(ubi->fm_used, 0, sizeof(ubi->fm_used));
+
+ fm_pos += sizeof(struct ubi_fm_sb);
+ if (fm_pos >= fm_size)
+ goto fail_bad;
+
+ fmhdr = (struct ubi_fm_hdr *)(fm_raw + fm_pos);
+ fm_pos += sizeof(*fmhdr);
+ if (fm_pos >= fm_size)
+ goto fail_bad;
+
+ if (be32_to_cpu(fmhdr->magic) != UBI_FM_HDR_MAGIC) {
+ ubi_err("bad fastmap header magic: 0x%x, expected: 0x%x",
+ be32_to_cpu(fmhdr->magic), UBI_FM_HDR_MAGIC);
+ goto fail_bad;
+ }
+
+ fmpl1 = (struct ubi_fm_scan_pool *)(fm_raw + fm_pos);
+ fm_pos += sizeof(*fmpl1);
+ if (fm_pos >= fm_size)
+ goto fail_bad;
+ if (be32_to_cpu(fmpl1->magic) != UBI_FM_POOL_MAGIC) {
+ ubi_err("bad fastmap pool magic: 0x%x, expected: 0x%x",
+ be32_to_cpu(fmpl1->magic), UBI_FM_POOL_MAGIC);
+ goto fail_bad;
+ }
+
+ fmpl2 = (struct ubi_fm_scan_pool *)(fm_raw + fm_pos);
+ fm_pos += sizeof(*fmpl2);
+ if (fm_pos >= fm_size)
+ goto fail_bad;
+ if (be32_to_cpu(fmpl2->magic) != UBI_FM_POOL_MAGIC) {
+ ubi_err("bad fastmap pool magic: 0x%x, expected: 0x%x",
+ be32_to_cpu(fmpl2->magic), UBI_FM_POOL_MAGIC);
+ goto fail_bad;
+ }
+
+ pool_size = be16_to_cpu(fmpl1->size);
+ wl_pool_size = be16_to_cpu(fmpl2->size);
+ fm->max_pool_size = be16_to_cpu(fmpl1->max_size);
+ fm->max_wl_pool_size = be16_to_cpu(fmpl2->max_size);
+
+ if (pool_size > UBI_FM_MAX_POOL_SIZE || pool_size < 0) {
+ ubi_err("bad pool size: %i", pool_size);
+ goto fail_bad;
+ }
+
+ if (wl_pool_size > UBI_FM_MAX_POOL_SIZE || wl_pool_size < 0) {
+ ubi_err("bad WL pool size: %i", wl_pool_size);
+ goto fail_bad;
+ }
+
+ if (fm->max_pool_size > UBI_FM_MAX_POOL_SIZE ||
+ fm->max_pool_size < 0) {
+ ubi_err("bad maximal pool size: %i", fm->max_pool_size);
+ goto fail_bad;
+ }
+
+ if (fm->max_wl_pool_size > UBI_FM_MAX_POOL_SIZE ||
+ fm->max_wl_pool_size < 0) {
+ ubi_err("bad maximal WL pool size: %i", fm->max_wl_pool_size);
+ goto fail_bad;
+ }
+
+ /* read EC values from free list */
+ for (i = 0; i < be32_to_cpu(fmhdr->free_peb_count); i++) {
+ fmec = (struct ubi_fm_ec *)(fm_raw + fm_pos);
+ fm_pos += sizeof(*fmec);
+ if (fm_pos >= fm_size)
+ goto fail_bad;
+ }
+
+ /* read EC values from used list */
+ for (i = 0; i < be32_to_cpu(fmhdr->used_peb_count); i++) {
+ fmec = (struct ubi_fm_ec *)(fm_raw + fm_pos);
+ fm_pos += sizeof(*fmec);
+ if (fm_pos >= fm_size)
+ goto fail_bad;
+
+ generic_set_bit(be32_to_cpu(fmec->pnum), ubi->fm_used);
+ }
+
+ /* read EC values from scrub list */
+ for (i = 0; i < be32_to_cpu(fmhdr->scrub_peb_count); i++) {
+ fmec = (struct ubi_fm_ec *)(fm_raw + fm_pos);
+ fm_pos += sizeof(*fmec);
+ if (fm_pos >= fm_size)
+ goto fail_bad;
+ }
+
+ /* read EC values from erase list */
+ for (i = 0; i < be32_to_cpu(fmhdr->erase_peb_count); i++) {
+ fmec = (struct ubi_fm_ec *)(fm_raw + fm_pos);
+ fm_pos += sizeof(*fmec);
+ if (fm_pos >= fm_size)
+ goto fail_bad;
+ }
+
+ /* Iterate over all volumes and read their EBA table */
+ for (i = 0; i < be32_to_cpu(fmhdr->vol_count); i++) {
+ u32 vol_id, vol_type, used, reserved;
+
+ fmvhdr = (struct ubi_fm_volhdr *)(fm_raw + fm_pos);
+ fm_pos += sizeof(*fmvhdr);
+ if (fm_pos >= fm_size)
+ goto fail_bad;
+
+ if (be32_to_cpu(fmvhdr->magic) != UBI_FM_VHDR_MAGIC) {
+ ubi_err("bad fastmap vol header magic: 0x%x, " \
+ "expected: 0x%x",
+ be32_to_cpu(fmvhdr->magic), UBI_FM_VHDR_MAGIC);
+ goto fail_bad;
+ }
+
+ vol_id = be32_to_cpu(fmvhdr->vol_id);
+ vol_type = fmvhdr->vol_type;
+ used = be32_to_cpu(fmvhdr->used_ebs);
+
+ fm_eba = (struct ubi_fm_eba *)(fm_raw + fm_pos);
+ fm_pos += sizeof(*fm_eba);
+ fm_pos += (sizeof(__be32) * be32_to_cpu(fm_eba->reserved_pebs));
+ if (fm_pos >= fm_size)
+ goto fail_bad;
+
+ if (be32_to_cpu(fm_eba->magic) != UBI_FM_EBA_MAGIC) {
+ ubi_err("bad fastmap EBA header magic: 0x%x, " \
+ "expected: 0x%x",
+ be32_to_cpu(fm_eba->magic), UBI_FM_EBA_MAGIC);
+ goto fail_bad;
+ }
+
+ reserved = be32_to_cpu(fm_eba->reserved_pebs);
+ ubi_dbg("FA: vol %u used %u res: %u", vol_id, used, reserved);
+ for (j = 0; j < reserved; j++) {
+ int pnum = be32_to_cpu(fm_eba->pnum[j]);
+
+ if ((int)be32_to_cpu(fm_eba->pnum[j]) < 0)
+ continue;
+
+ if (!__test_and_clear_bit(pnum, ubi->fm_used))
+ continue;
+
+ /*
+ * We only handle static volumes so used_ebs
+ * needs to be handed in. And we do not assign
+ * the reserved blocks
+ */
+ if (j >= used)
+ continue;
+
+ ret = assign_aeb_to_av(ubi, pnum, j, vol_id,
+ vol_type, used);
+ if (!ret)
+ continue;
+
+ /*
+ * Nasty: The fastmap claims that the volume
+ * has one block more than it, but that block
+ * is always empty and the other blocks have
+ * the correct number of total LEBs in the
+ * headers. Deal with it.
+ */
+ if (ret != UBI_IO_FF && j != used - 1)
+ goto fail_bad;
+ ubi_dbg("FA: Vol: %u Ignoring empty LEB %d of %d",
+ vol_id, j, used);
+ }
+ }
+
+ ret = scan_pool(ubi, fmpl1->pebs, pool_size);
+ if (ret)
+ goto fail;
+
+ ret = scan_pool(ubi, fmpl2->pebs, wl_pool_size);
+ if (ret)
+ goto fail;
+
+#ifdef CHECKME
+ /*
+ * If fastmap is leaking PEBs (must not happen), raise a
+ * fat warning and fall back to scanning mode.
+ * We do this here because in ubi_wl_init() it's too late
+ * and we cannot fall back to scanning.
+ */
+ if (WARN_ON(count_fastmap_pebs(ai) != ubi->peb_count -
+ ai->bad_peb_count - fm->used_blocks))
+ goto fail_bad;
+#endif
+
+ return 0;
+
+fail_bad:
+ ret = UBI_BAD_FASTMAP;
+fail:
+ return ret;
+}
+
+static int ubi_scan_fastmap(struct ubi_scan_info *ubi,
+ struct ubi_attach_info *ai,
+ int fm_anchor)
+{
+ struct ubi_fm_sb *fmsb, *fmsb2;
+ struct ubi_vid_hdr *vh;
+ struct ubi_fastmap_layout *fm;
+ int i, used_blocks, pnum, ret = 0;
+ size_t fm_size;
+ __be32 crc, tmp_crc;
+ unsigned long long sqnum = 0;
+
+ fmsb = &ubi->fm_sb;
+ fm = &ubi->fm_layout;
+
+ ret = ubi_io_read(ubi, fmsb, fm_anchor, ubi->leb_start, sizeof(*fmsb));
+ if (ret && ret != UBI_IO_BITFLIPS)
+ goto free_fm_sb;
+ else if (ret == UBI_IO_BITFLIPS)
+ fm->to_be_tortured[0] = 1;
+
+ if (be32_to_cpu(fmsb->magic) != UBI_FM_SB_MAGIC) {
+ ubi_err("bad super block magic: 0x%x, expected: 0x%x",
+ be32_to_cpu(fmsb->magic), UBI_FM_SB_MAGIC);
+ ret = UBI_BAD_FASTMAP;
+ goto free_fm_sb;
+ }
+
+ if (fmsb->version != UBI_FM_FMT_VERSION) {
+ ubi_err("bad fastmap version: %i, expected: %i",
+ fmsb->version, UBI_FM_FMT_VERSION);
+ ret = UBI_BAD_FASTMAP;
+ goto free_fm_sb;
+ }
+
+ used_blocks = be32_to_cpu(fmsb->used_blocks);
+ if (used_blocks > UBI_FM_MAX_BLOCKS || used_blocks < 1) {
+ ubi_err("number of fastmap blocks is invalid: %i", used_blocks);
+ ret = UBI_BAD_FASTMAP;
+ goto free_fm_sb;
+ }
+
+ fm_size = ubi->leb_size * used_blocks;
+ if (fm_size != ubi->fm_size) {
+ ubi_err("bad fastmap size: %zi, expected: %zi", fm_size,
+ ubi->fm_size);
+ ret = UBI_BAD_FASTMAP;
+ goto free_fm_sb;
+ }
+
+ vh = &ubi->fm_vh;
+
+ for (i = 0; i < used_blocks; i++) {
+ pnum = be32_to_cpu(fmsb->block_loc[i]);
+
+ if (ubi_io_is_bad(ubi, pnum)) {
+ ret = UBI_BAD_FASTMAP;
+ goto free_hdr;
+ }
+
+#ifdef LATER
+ int image_seq;
+ ret = ubi_io_read_ec_hdr(ubi, pnum, ech, 0);
+ if (ret && ret != UBI_IO_BITFLIPS) {
+ ubi_err("unable to read fastmap block# %i EC (PEB: %i)",
+ i, pnum);
+ if (ret > 0)
+ ret = UBI_BAD_FASTMAP;
+ goto free_hdr;
+ } else if (ret == UBI_IO_BITFLIPS)
+ fm->to_be_tortured[i] = 1;
+
+ image_seq = be32_to_cpu(ech->image_seq);
+ if (!ubi->image_seq)
+ ubi->image_seq = image_seq;
+ /*
+ * Older UBI implementations have image_seq set to zero, so
+ * we shouldn't fail if image_seq == 0.
+ */
+ if (image_seq && (image_seq != ubi->image_seq)) {
+ ubi_err("wrong image seq:%d instead of %d",
+ be32_to_cpu(ech->image_seq), ubi->image_seq);
+ ret = UBI_BAD_FASTMAP;
+ goto free_hdr;
+ }
+#endif
+ ret = ubi_io_read_vid_hdr(ubi, pnum, vh, 0);
+ if (ret && ret != UBI_IO_BITFLIPS) {
+ ubi_err("unable to read fastmap block# %i (PEB: %i)",
+ i, pnum);
+ goto free_hdr;
+ }
+
+ /*
+ * Mainline code rescans the anchor header. We've done
+ * that already so we merily copy it over.
+ */
+ if (pnum == fm_anchor)
+ memcpy(vh, ubi->blockinfo + pnum, sizeof(*fm));
+
+ if (i == 0) {
+ if (be32_to_cpu(vh->vol_id) != UBI_FM_SB_VOLUME_ID) {
+ ubi_err("bad fastmap anchor vol_id: 0x%x," \
+ " expected: 0x%x",
+ be32_to_cpu(vh->vol_id),
+ UBI_FM_SB_VOLUME_ID);
+ ret = UBI_BAD_FASTMAP;
+ goto free_hdr;
+ }
+ } else {
+ if (be32_to_cpu(vh->vol_id) != UBI_FM_DATA_VOLUME_ID) {
+ ubi_err("bad fastmap data vol_id: 0x%x," \
+ " expected: 0x%x",
+ be32_to_cpu(vh->vol_id),
+ UBI_FM_DATA_VOLUME_ID);
+ ret = UBI_BAD_FASTMAP;
+ goto free_hdr;
+ }
+ }
+
+ if (sqnum < be64_to_cpu(vh->sqnum))
+ sqnum = be64_to_cpu(vh->sqnum);
+
+ ret = ubi_io_read(ubi, ubi->fm_buf + (ubi->leb_size * i), pnum,
+ ubi->leb_start, ubi->leb_size);
+ if (ret && ret != UBI_IO_BITFLIPS) {
+ ubi_err("unable to read fastmap block# %i (PEB: %i, " \
+ "err: %i)", i, pnum, ret);
+ goto free_hdr;
+ }
+ }
+
+ fmsb2 = (struct ubi_fm_sb *)(ubi->fm_buf);
+ tmp_crc = be32_to_cpu(fmsb2->data_crc);
+ fmsb2->data_crc = 0;
+ crc = crc32(UBI_CRC32_INIT, ubi->fm_buf, fm_size);
+ if (crc != tmp_crc) {
+ ubi_err("fastmap data CRC is invalid");
+ ubi_err("CRC should be: 0x%x, calc: 0x%x", tmp_crc, crc);
+ ret = UBI_BAD_FASTMAP;
+ goto free_hdr;
+ }
+
+ fmsb2->sqnum = sqnum;
+
+ fm->used_blocks = used_blocks;
+
+ ret = ubi_attach_fastmap(ubi, ai, fm);
+ if (ret) {
+ if (ret > 0)
+ ret = UBI_BAD_FASTMAP;
+ goto free_hdr;
+ }
+
+ ubi->fm = fm;
+ ubi->fm_pool.max_size = ubi->fm->max_pool_size;
+ ubi->fm_wl_pool.max_size = ubi->fm->max_wl_pool_size;
+ ubi_msg("attached by fastmap %uMB %u blocks",
+ ubi->fsize_mb, ubi->peb_count);
+ ubi_dbg("fastmap pool size: %d", ubi->fm_pool.max_size);
+ ubi_dbg("fastmap WL pool size: %d", ubi->fm_wl_pool.max_size);
+
+out:
+ if (ret)
+ ubi_err("Attach by fastmap failed, doing a full scan!");
+ return ret;
+
+free_hdr:
+free_fm_sb:
+ goto out;
+}
+
+/*
+ * Scan the flash and attempt to attach via fastmap
+ */
+static void ipl_scan(struct ubi_scan_info *ubi)
+{
+ unsigned int pnum;
+ int res;
+
+ /*
+ * Scan first for the fastmap super block
+ */
+ for (pnum = 0; pnum < UBI_FM_MAX_START; pnum++) {
+ res = ubi_scan_vid_hdr(ubi, ubi->blockinfo + pnum, pnum);
+ /*
+ * We ignore errors here as we are meriliy scanning
+ * the headers.
+ */
+ if (res != UBI_FASTMAP_ANCHOR)
+ continue;
+
+ /*
+ * If fastmap is disabled, continue scanning. This
+ * might happen because the previous attempt failed or
+ * the caller disabled it right away.
+ */
+ if (!ubi->fm_enabled)
+ continue;
+
+ /*
+ * Try to attach the fastmap, if that fails continue
+ * scanning.
+ */
+ if (!ubi_scan_fastmap(ubi, NULL, pnum))
+ return;
+ /*
+ * Fastmap failed. Clear everything we have and start
+ * over. We are paranoid and do not trust anything.
+ */
+ memset(ubi->volinfo, 0, sizeof(ubi->volinfo));
+ pnum = 0;
+ break;
+ }
+
+ /*
+ * Continue scanning, ignore errors, we might find what we are
+ * looking for,
+ */
+ for (; pnum < ubi->peb_count; pnum++)
+ ubi_scan_vid_hdr(ubi, ubi->blockinfo + pnum, pnum);
+}
+
+/*
+ * Load a logical block of a volume into memory
+ */
+static int ubi_load_block(struct ubi_scan_info *ubi, uint8_t *laddr,
+ struct ubi_vol_info *vi, u32 vol_id, u32 lnum,
+ u32 last)
+{
+ struct ubi_vid_hdr *vh, *vrepl;
+ u32 pnum, crc, dlen;
+
+retry:
+ /*
+ * If this is a fastmap run, we try to rescan full, otherwise
+ * we simply give up.
+ */
+ if (!test_bit(lnum, vi->found)) {
+ ubi_warn("LEB %d of %d is missing", lnum, last);
+ return -EINVAL;
+ }
+
+ pnum = vi->lebs_to_pebs[lnum];
+
+ ubi_dbg("Load vol %u LEB %u PEB %u", vol_id, lnum, pnum);
+
+ if (ubi_io_is_bad(ubi, pnum)) {
+ ubi_warn("Corrupted mapping block %d PB %d\n", lnum, pnum);
+ return -EINVAL;
+ }
+
+ if (test_bit(pnum, ubi->corrupt))
+ goto find_other;
+
+ /*
+ * Lets try to read that block
+ */
+ vh = ubi->blockinfo + pnum;
+
+ if (!test_bit(pnum, ubi->scanned)) {
+ ubi_warn("Vol: %u LEB %u PEB %u not yet scanned", vol_id,
+ lnum, pnum);
+ if (ubi_rescan_fm_vid_hdr(ubi, vh, pnum, vol_id, lnum))
+ goto find_other;
+ }
+
+ /*
+ * Check, if the total number of blocks is correct
+ */
+ if (be32_to_cpu(vh->used_ebs) != last) {
+ ubi_dbg("Block count missmatch.");
+ ubi_dbg("vh->used_ebs: %d nrblocks: %d",
+ be32_to_cpu(vh->used_ebs), last);
+ generic_set_bit(pnum, ubi->corrupt);
+ goto find_other;
+ }
+
+ /*
+ * Get the data length of this block.
+ */
+ dlen = be32_to_cpu(vh->data_size);
+
+ /*
+ * Read the data into RAM. We ignore the return value
+ * here as the only thing which might go wrong are
+ * bitflips. Try nevertheless.
+ */
+ ubi_io_read(ubi, laddr, pnum, ubi->leb_start, dlen);
+
+ /* Calculate CRC over the data */
+ crc = crc32(UBI_CRC32_INIT, laddr, dlen);
+
+ if (crc != be32_to_cpu(vh->data_crc)) {
+ ubi_warn("Vol: %u LEB %u PEB %u data CRC failure", vol_id,
+ lnum, pnum);
+ generic_set_bit(pnum, ubi->corrupt);
+ goto find_other;
+ }
+
+ /* We are good. Return the data length we read */
+ return dlen;
+
+find_other:
+ ubi_dbg("Find replacement for LEB %u PEB %u", lnum, pnum);
+ generic_clear_bit(lnum, vi->found);
+ vrepl = NULL;
+
+ for (pnum = 0; pnum < ubi->peb_count; pnum++) {
+ struct ubi_vid_hdr *tmp = ubi->blockinfo + pnum;
+ u32 t_vol_id = be32_to_cpu(tmp->vol_id);
+ u32 t_lnum = be32_to_cpu(tmp->lnum);
+
+ if (test_bit(pnum, ubi->corrupt))
+ continue;
+
+ if (t_vol_id != vol_id || t_lnum != lnum)
+ continue;
+
+ if (!test_bit(pnum, ubi->scanned)) {
+ ubi_warn("Vol: %u LEB %u PEB %u not yet scanned",
+ vol_id, lnum, pnum);
+ if (ubi_rescan_fm_vid_hdr(ubi, tmp, pnum, vol_id, lnum))
+ continue;
+ }
+
+ /*
+ * We found one. If its the first, assign it otherwise
+ * compare the sqnum
+ */
+ generic_set_bit(lnum, vi->found);
+
+ if (!vrepl) {
+ vrepl = tmp;
+ continue;
+ }
+
+ if (be64_to_cpu(vrepl->sqnum) < be64_to_cpu(tmp->sqnum))
+ vrepl = tmp;
+ }
+
+ if (vrepl) {
+ /* Update the vi table */
+ pnum = vrepl - ubi->blockinfo;
+ vi->lebs_to_pebs[lnum] = pnum;
+ ubi_dbg("Trying PEB %u for LEB %u", pnum, lnum);
+ vh = vrepl;
+ }
+ goto retry;
+}
+
+/*
+ * Load a volume into RAM
+ */
+static int ipl_load(struct ubi_scan_info *ubi, const u32 vol_id, uint8_t *laddr)
+{
+ struct ubi_vol_info *vi;
+ u32 lnum, last, len;
+
+ if (vol_id >= UBI_SPL_VOL_IDS)
+ return -EINVAL;
+
+ len = 0;
+ vi = ubi->volinfo + vol_id;
+ last = vi->last_block + 1;
+
+ /* Read the blocks to RAM, check CRC */
+ for (lnum = 0 ; lnum < last; lnum++) {
+ int res = ubi_load_block(ubi, laddr, vi, vol_id, lnum, last);
+
+ if (res < 0) {
+ ubi_warn("Failed to load volume %u", vol_id);
+ return res;
+ }
+ /* res is the data length of the read block */
+ laddr += res;
+ len += res;
+ }
+ return len;
+}
+
+int ubispl_load_volumes(struct ubispl_info *info, struct ubispl_load *lvols,
+ int nrvols)
+{
+ struct ubi_scan_info *ubi = info->ubi;
+ int res, i, fastmap = info->fastmap;
+ u32 fsize;
+
+retry:
+ /*
+ * We do a partial initializiation of @ubi. Cleaning fm_buf is
+ * not necessary.
+ */
+ memset(ubi, 0, offsetof(struct ubi_scan_info, fm_buf));
+
+ ubi->read = info->read;
+
+ /* Precalculate the offsets */
+ ubi->vid_offset = info->vid_offset;
+ ubi->leb_start = info->leb_start;
+ ubi->leb_size = info->peb_size - ubi->leb_start;
+ ubi->peb_count = info->peb_count;
+ ubi->peb_offset = info->peb_offset;
+
+ fsize = info->peb_size * info->peb_count;
+ ubi->fsize_mb = fsize >> 20;
+
+ /* Fastmap init */
+ ubi->fm_size = ubi_calc_fm_size(ubi);
+ ubi->fm_enabled = fastmap;
+
+ for (i = 0; i < nrvols; i++) {
+ struct ubispl_load *lv = lvols + i;
+
+ generic_set_bit(lv->vol_id, ubi->toload);
+ }
+
+ ipl_scan(ubi);
+
+ for (i = 0; i < nrvols; i++) {
+ struct ubispl_load *lv = lvols + i;
+
+ ubi_msg("Loading VolId #%d", lv->vol_id);
+ res = ipl_load(ubi, lv->vol_id, lv->load_addr);
+ if (res < 0) {
+ if (fastmap) {
+ fastmap = 0;
+ goto retry;
+ }
+ ubi_warn("Failed");
+ return res;
+ }
+ }
+ return 0;
+}
diff --git a/drivers/mtd/ubispl/ubispl.h b/drivers/mtd/ubispl/ubispl.h
new file mode 100644
index 0000000..9227881
--- /dev/null
+++ b/drivers/mtd/ubispl/ubispl.h
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) Thomas Gleixner <tglx@linutronix.de>
+ *
+ * SPDX-License-Identifier: GPL 2.0+ BSD-3-Clause
+ */
+
+#ifndef _UBOOT_MTD_UBISPL_H
+#define _UBOOT_MTD_UBISPL_H
+
+#include "../ubi/ubi-media.h"
+#include "ubi-wrapper.h"
+
+/*
+ * The maximum number of volume ids we scan. So you can load volume id
+ * 0 to (CONFIG_SPL_UBI_VOL_ID_MAX - 1)
+ */
+#define UBI_SPL_VOL_IDS CONFIG_SPL_UBI_VOL_IDS
+/*
+ * The size of the read buffer for the fastmap blocks. In theory up to
+ * UBI_FM_MAX_BLOCKS * CONFIG_SPL_MAX_PEB_SIZE. In practice today
+ * one or two blocks.
+ */
+#define UBI_FM_BUF_SIZE (UBI_FM_MAX_BLOCKS*CONFIG_SPL_UBI_MAX_PEB_SIZE)
+/*
+ * The size of the bitmaps for the attach/ scan
+ */
+#define UBI_FM_BM_SIZE ((CONFIG_SPL_UBI_MAX_PEBS / BITS_PER_LONG) + 1)
+/*
+ * The maximum number of logical erase blocks per loadable volume
+ */
+#define UBI_MAX_VOL_LEBS CONFIG_SPL_UBI_MAX_VOL_LEBS
+/*
+ * The bitmap size for the above to denote the found blocks inside the volume
+ */
+#define UBI_VOL_BM_SIZE ((UBI_MAX_VOL_LEBS / BITS_PER_LONG) + 1)
+
+/**
+ * struct ubi_vol_info - UBISPL internal volume represenation
+ * @last_block: The last block (highest LEB) found for this volume
+ * @found: Bitmap to mark found LEBS
+ * @lebs_to_pebs: LEB to PEB translation table
+ */
+struct ubi_vol_info {
+ u32 last_block;
+ unsigned long found[UBI_VOL_BM_SIZE];
+ u32 lebs_to_pebs[UBI_MAX_VOL_LEBS];
+};
+
+/**
+ * struct ubi_scan_info - UBISPL internal data for FM attach and full scan
+ *
+ * @read: Read function to access the flash provided by the caller
+ * @peb_count: Number of physical erase blocks in the UBI FLASH area
+ * aka MTD partition.
+ * @peb_offset: Offset of PEB0 in the UBI FLASH area (aka MTD partition)
+ * to the real start of the FLASH in erase blocks.
+ * @fsize_mb: Size of the scanned FLASH area in MB (stats only)
+ * @vid_offset: Offset from the start of a PEB to the VID header
+ * @leb_start: Offset from the start of a PEB to the data area
+ * @leb_size: Size of the data area
+ *
+ * @fastmap_pebs: Counter of PEBs "attached" by fastmap
+ * @fastmap_anchor: The anchor PEB of the fastmap
+ * @fm_sb: The fastmap super block data
+ * @fm_vh: The fastmap VID header
+ * @fm: Pointer to the fastmap layout
+ * @fm_layout: The fastmap layout itself
+ * @fm_pool: The pool of PEBs to scan at fastmap attach time
+ * @fm_wl_pool: The pool of PEBs scheduled for wearleveling
+ *
+ * @fm_enabled: Indicator whether fastmap attachment is enabled.
+ * @fm_used: Bitmap to indicate the PEBS covered by fastmap
+ * @scanned: Bitmap to indicate the PEBS of which the VID header
+ * hase been physically scanned.
+ * @corrupt: Bitmap to indicate corrupt blocks
+ * @toload: Bitmap to indicate the volumes which should be loaded
+ *
+ * @blockinfo: The vid headers of the scanned blocks
+ * @volinfo: The volume information of the interesting (toload)
+ * volumes
+ *
+ * @fm_buf: The large fastmap attach buffer
+ */
+struct ubi_scan_info {
+ ubispl_read_flash read;
+ unsigned int fsize_mb;
+ unsigned int peb_count;
+ unsigned int peb_offset;
+
+ unsigned long vid_offset;
+ unsigned long leb_start;
+ unsigned long leb_size;
+
+ /* Fastmap: The upstream required fields */
+ int fastmap_pebs;
+ int fastmap_anchor;
+ size_t fm_size;
+ struct ubi_fm_sb fm_sb;
+ struct ubi_vid_hdr fm_vh;
+ struct ubi_fastmap_layout *fm;
+ struct ubi_fastmap_layout fm_layout;
+ struct ubi_fm_pool fm_pool;
+ struct ubi_fm_pool fm_wl_pool;
+
+ /* Fastmap: UBISPL specific data */
+ int fm_enabled;
+ unsigned long fm_used[UBI_FM_BM_SIZE];
+ unsigned long scanned[UBI_FM_BM_SIZE];
+ unsigned long corrupt[UBI_FM_BM_SIZE];
+ unsigned long toload[UBI_FM_BM_SIZE];
+
+ /* Data for storing the VID and volume information */
+ struct ubi_vol_info volinfo[UBI_SPL_VOL_IDS];
+ struct ubi_vid_hdr blockinfo[CONFIG_SPL_UBI_MAX_PEBS];
+
+ /* The large buffer for the fastmap */
+ uint8_t fm_buf[UBI_FM_BUF_SIZE];
+};
+
+#ifdef CFG_DEBUG
+#define ubi_dbg(fmt, ...) printf("UBI: debug:" fmt "\n", ##__VA_ARGS__)
+#else
+#define ubi_dbg(fmt, ...)
+#endif
+
+#ifdef CONFIG_UBI_SILENCE_MSG
+#define ubi_msg(fmt, ...)
+#else
+#define ubi_msg(fmt, ...) printf("UBI: " fmt "\n", ##__VA_ARGS__)
+#endif
+/* UBI warning messages */
+#define ubi_warn(fmt, ...) printf("UBI warning: " fmt "\n", ##__VA_ARGS__)
+/* UBI error messages */
+#define ubi_err(fmt, ...) printf("UBI error: " fmt "\n", ##__VA_ARGS__)
+
+#endif