[U-Boot] [patch 2/2] spl: Lightweight UBI and UBI fastmap support

Thomas Gleixner tglx at linutronix.de
Sat Jul 5 11:48:13 CEST 2014


Booting a payload out of NAND FLASH from the SPL is a crux today, as
it requires hard partioned FLASH. Not a brilliant idea with the
reliability of todays NAND FLASH chips.

The upstream UBI + UBI fastmap implementation which is about to
brought to u-boot is too heavy weight for SPLs as it provides way more
functionality than needed for a SPL and does not even fit into the
restricted SPL areas which are loaded from the SoC boot ROM.

So this provides a fast and lightweight implementation of UBI scanning
and UBI fastmap attach. The scan and logical to physical block mapping
code is developed from scratch, while the fastmap implementation is
lifted from the linux kernel source and stripped down to fit the SPL
needs.

The text foot print on the board which I used for development is:

 6854	0	0	6854	1abd	drivers/mtd/ubispl/built-in.o

Attaching a NAND chip with 4096 physical eraseblocks (4 blocks are
reserved for the SPL) takes:

In full scan mode:	1172ms
In fastmap mode:	  95ms

The code requires quite some storage. The largest and unknown part of
it is the number of fastmap blocks to read. Therefor the data
structure is not put into the BSS. The code requires a pointer to free
memory handed in which is initialized by the UBI attach code itself.

See doc/README.ubispl for further information on how to use it.

This shares the crc32 implementation of drivers/mtd/ubi and it should
share ubi-media.h once u-boot is updated to the 3.14 ubi code. For now
I keep a private copy of ubi-media.h as ubispl needs all the new fancy
things from there. There is no way to share the fastmap code, as
UBISPL only utilizes the slightly modified functions
ubi_attach_fastmap() and ubi_scan_fastmap() from the original kernel
ubi fastmap implementation.

Signed-off-by: Thomas Gleixner <tglx at linutronix.de>
---
 README                           |    4 
 doc/README.ubispl                |  144 ++++++
 drivers/mtd/ubispl/Makefile      |    1 
 drivers/mtd/ubispl/ubi-media.h   |  503 +++++++++++++++++++++
 drivers/mtd/ubispl/ubi-wrapper.h |  104 ++++
 drivers/mtd/ubispl/ubispl.c      |  920 +++++++++++++++++++++++++++++++++++++++
 drivers/mtd/ubispl/ubispl.h      |  136 +++++
 include/ubispl.h                 |   90 +++
 scripts/Makefile.spl             |    1 
 9 files changed, 1903 insertions(+)

Index: u-boot/README
===================================================================
--- u-boot.orig/README
+++ u-boot/README
@@ -3456,6 +3456,10 @@ FIT uImage format:
 		Support for NAND boot using simple NAND drivers that
 		expose the cmd_ctrl() interface.
 
+		CONFIG_SPL_UBI
+		Support for a lightweight UBI (fastmap) scanner and
+		loader
+
 		CONFIG_SPL_MTD_SUPPORT
 		Support for the MTD subsystem within SPL.  Useful for
 		environment on NAND support within SPL.
Index: u-boot/doc/README.ubispl
===================================================================
--- /dev/null
+++ u-boot/doc/README.ubispl
@@ -0,0 +1,144 @@
+Lightweight UBI and UBI fastmap support
+
+# Copyright (C) Thomas Gleixner <tglx at linutronix.de>
+#
+# SPDX-License-Identifier:	GPL-2.0+
+
+Scans the UBI information and loads the requested static volumes into
+memory.
+
+Configuration Options:
+
+   CONFIG_SPL_UBI
+     Enables the SPL UBI support
+
+   CONFIG_SPL_UBI_MAX_VOL_LEBS
+     The maximum number of logical eraseblocks which a static volume
+     to load can contain. Used for sizing the scan data structure
+
+   CONFIG_SPL_UBI_MAX_PEB_SIZE
+     The maximum physical erase block size. Either a compile time
+     constant or runtime detection. Used for sizing the scan data
+     structure
+
+   CONFIG_SPL_UBI_MAX_PEBS
+     The maximum physical erase block count. Either a compile time
+     constant or runtime detection. Used for sizing the scan data
+     structure
+
+   CONFIG_SPL_UBI_VOL_IDS
+     The maximum volume ids which can be loaded. Used for sizing the
+     scan data structure.
+
+Usage notes:
+
+In the board config file define for example:
+
+#define CONFIG_SPL_UBI
+#define CONFIG_SPL_UBI_MAX_VOL_LEBS	256
+#define CONFIG_SPL_UBI_MAX_PEB_SIZE	(256*1024)
+#define CONFIG_SPL_UBI_MAX_PEBS		4096
+#define CONFIG_SPL_UBI_VOL_IDS		8
+
+The size requirement is roughly as follows:
+
+    2k for the basic data structure
+  + CONFIG_SPL_UBI_VOL_IDS * CONFIG_SPL_UBI_MAX_VOL_LEBS * 8
+  + CONFIG_SPL_UBI_MAX_PEBS * 64
+  + CONFIG_SPL_UBI_MAX_PEB_SIZE * UBI_FM_MAX_BLOCKS
+
+The last one is big, but I really don't care in that stage. Real world
+implementations only use the first couple of blocks, but the code
+handles up to UBI_FM_MAX_BLOCKS.
+
+Given the above configuration example the requirement is about 5M
+which is usually not a problem to reserve in the RAM along with the
+other areas like the kernel/dts load address.
+
+So something like this will do the trick:
+
+#define SPL_FINFO_ADDR			0x80800000
+#define SPL_DTB_LOAD_ADDR		0x81800000
+#define SPL_KERNEL_LOAD_ADDR		0x82000000
+
+In the board file, implement the following:
+
+static struct ubispl_load myvolumes[] = {
+	{
+		.name		= "Kernel",
+		.vol_id		= 0,
+		.load_addr	= (void *)SPL_KERNEL_LOAD_ADDR,
+	},
+	{
+		.name		= "DTB",
+		.vol_id		= 1,
+		.load_addr	= (void *)SPL_DTB_LOAD_ADDR,
+	}
+};
+
+int spl_start_uboot(void)
+{
+	struct ubispl_info info;
+
+	info.ubi = (struct ubi_scan_info *) SPL_FINFO_ADDR;
+	info.fastmap = 1;
+	info.read = nand_spl_read_flash;
+
+#if COMPILE_TIME_DEFINED
+	/*
+	 * MY_NAND_NR_SPL_PEBS is the number of physical erase blocks
+	 * in the FLASH which are reserved for the SPL. Think about
+	 * mtd partitions:
+	 *
+	 * part_spl { .start = 0, .end = 4 }
+	 * part_ubi { .start = 4, .end = NR_PEBS }
+	 */
+	info.peb_offset = MY_NAND_NR_SPL_PEBS;
+	info.peb_size = CONFIG_SYS_NAND_BLOCK_SIZE;
+	info.vid_offset = MY_NAND_UBI_VID_OFFS;
+	info.leb_start = MY_NAND_UBI_DATA_OFFS;
+	info.peb_count = MY_NAND_UBI_NUM_PEBS;
+#else
+	get_flash_info(&flash_info);
+	info.peb_offset = MY_NAND_NR_SPL_PEBS;
+	info.peb_size = flash_info.peb_size;
+
+	/*
+	 * The VID and Data offset depend on the capability of the
+	 * FLASH chip to do subpage writes.
+	 *
+	 * If the flash chip supports subpage writes, then the VID
+	 * header starts at the second subpage. So for 2k pages size
+	 * with 4 subpages the VID offset is 512. The DATA offset is 2k.
+	 *
+	 * If the flash chip does not support subpage writes then the
+	 * VID offset is FLASH_PAGE_SIZE and the DATA offset
+	 * 2 * FLASH_PAGE_SIZE
+	 */
+	info.vid_offset = flash_info.vid_offset;
+	info.leb_start = flash_info.data_offset;
+
+	/*
+	 * The flash reports the total number of erase blocks, so
+	 * we need to subtract the number of blocks which are reserved
+	 * for the SPL itself and not managed by UBI.
+	 */
+	info.peb_count = flash_info.peb_count - MY_NAND_NR_SPL_PEBS;
+#endif
+
+	ret = ubispl_load_volumes(&info, myvolumes, ARRAY_SIZE(myvolumes);
+
+	....
+
+}
+
+Note: you can load any payload that way. You can even load u-boot from
+UBI, so the only non UBI managed FLASH area is the one which is
+reserved for the SPL itself and read from the SoC ROM.
+
+And you can do fallback scenarios:
+
+    if (ubispl_load_volumes(&info, volumes0, ARRAY_SIZE(volumes0)))
+        if (ubispl_load_volumes(&info, volumes1, ARRAY_SIZE(volumes1)))
+	    ubispl_load_volumes(&info, vol_uboot, ARRAY_SIZE(vol_uboot));
+
Index: u-boot/drivers/mtd/ubispl/Makefile
===================================================================
--- /dev/null
+++ u-boot/drivers/mtd/ubispl/Makefile
@@ -0,0 +1 @@
+obj-y += ubispl.o ../ubi/crc32.o
Index: u-boot/drivers/mtd/ubispl/ubi-media.h
===================================================================
--- /dev/null
+++ u-boot/drivers/mtd/ubispl/ubi-media.h
@@ -0,0 +1,503 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ *
+ * Authors: Artem Bityutskiy (БОтюцкОй АртёЌ)
+ *          Thomas Gleixner
+ *          Frank Haverkamp
+ *          Oliver Lohmann
+ *          Andreas Arnez
+ */
+
+/*
+ * This file defines the layout of UBI headers and all the other UBI on-flash
+ * data structures.
+ */
+
+#ifndef __UBI_MEDIA_H__
+#define __UBI_MEDIA_H__
+
+#include <asm/byteorder.h>
+
+/* The version of UBI images supported by this implementation */
+#define UBI_VERSION 1
+
+/* The highest erase counter value supported by this implementation */
+#define UBI_MAX_ERASECOUNTER 0x7FFFFFFF
+
+/* The initial CRC32 value used when calculating CRC checksums */
+#define UBI_CRC32_INIT 0xFFFFFFFFU
+
+/* Erase counter header magic number (ASCII "UBI#") */
+#define UBI_EC_HDR_MAGIC  0x55424923
+/* Volume identifier header magic number (ASCII "UBI!") */
+#define UBI_VID_HDR_MAGIC 0x55424921
+
+/*
+ * Volume type constants used in the volume identifier header.
+ *
+ * @UBI_VID_DYNAMIC: dynamic volume
+ * @UBI_VID_STATIC: static volume
+ */
+enum {
+	UBI_VID_DYNAMIC = 1,
+	UBI_VID_STATIC  = 2
+};
+
+/*
+ * Volume flags used in the volume table record.
+ *
+ * @UBI_VTBL_AUTORESIZE_FLG: auto-resize this volume
+ *
+ * %UBI_VTBL_AUTORESIZE_FLG flag can be set only for one volume in the volume
+ * table. UBI automatically re-sizes the volume which has this flag and makes
+ * the volume to be of largest possible size. This means that if after the
+ * initialization UBI finds out that there are available physical eraseblocks
+ * present on the device, it automatically appends all of them to the volume
+ * (the physical eraseblocks reserved for bad eraseblocks handling and other
+ * reserved physical eraseblocks are not taken). So, if there is a volume with
+ * the %UBI_VTBL_AUTORESIZE_FLG flag set, the amount of available logical
+ * eraseblocks will be zero after UBI is loaded, because all of them will be
+ * reserved for this volume. Note, the %UBI_VTBL_AUTORESIZE_FLG bit is cleared
+ * after the volume had been initialized.
+ *
+ * The auto-resize feature is useful for device production purposes. For
+ * example, different NAND flash chips may have different amount of initial bad
+ * eraseblocks, depending of particular chip instance. Manufacturers of NAND
+ * chips usually guarantee that the amount of initial bad eraseblocks does not
+ * exceed certain percent, e.g. 2%. When one creates an UBI image which will be
+ * flashed to the end devices in production, he does not know the exact amount
+ * of good physical eraseblocks the NAND chip on the device will have, but this
+ * number is required to calculate the volume sized and put them to the volume
+ * table of the UBI image. In this case, one of the volumes (e.g., the one
+ * which will store the root file system) is marked as "auto-resizable", and
+ * UBI will adjust its size on the first boot if needed.
+ *
+ * Note, first UBI reserves some amount of physical eraseblocks for bad
+ * eraseblock handling, and then re-sizes the volume, not vice-versa. This
+ * means that the pool of reserved physical eraseblocks will always be present.
+ */
+enum {
+	UBI_VTBL_AUTORESIZE_FLG = 0x01,
+};
+
+/*
+ * Compatibility constants used by internal volumes.
+ *
+ * @UBI_COMPAT_DELETE: delete this internal volume before anything is written
+ *                     to the flash
+ * @UBI_COMPAT_RO: attach this device in read-only mode
+ * @UBI_COMPAT_PRESERVE: preserve this internal volume - do not touch its
+ *                       physical eraseblocks, don't allow the wear-leveling
+ *                       sub-system to move them
+ * @UBI_COMPAT_REJECT: reject this UBI image
+ */
+enum {
+	UBI_COMPAT_DELETE   = 1,
+	UBI_COMPAT_RO       = 2,
+	UBI_COMPAT_PRESERVE = 4,
+	UBI_COMPAT_REJECT   = 5
+};
+
+/* Sizes of UBI headers */
+#define UBI_EC_HDR_SIZE  sizeof(struct ubi_ec_hdr)
+#define UBI_VID_HDR_SIZE sizeof(struct ubi_vid_hdr)
+
+/* Sizes of UBI headers without the ending CRC */
+#define UBI_EC_HDR_SIZE_CRC  (UBI_EC_HDR_SIZE  - sizeof(__be32))
+#define UBI_VID_HDR_SIZE_CRC (UBI_VID_HDR_SIZE - sizeof(__be32))
+
+/**
+ * struct ubi_ec_hdr - UBI erase counter header.
+ * @magic: erase counter header magic number (%UBI_EC_HDR_MAGIC)
+ * @version: version of UBI implementation which is supposed to accept this
+ *           UBI image
+ * @padding1: reserved for future, zeroes
+ * @ec: the erase counter
+ * @vid_hdr_offset: where the VID header starts
+ * @data_offset: where the user data start
+ * @image_seq: image sequence number
+ * @padding2: reserved for future, zeroes
+ * @hdr_crc: erase counter header CRC checksum
+ *
+ * The erase counter header takes 64 bytes and has a plenty of unused space for
+ * future usage. The unused fields are zeroed. The @version field is used to
+ * indicate the version of UBI implementation which is supposed to be able to
+ * work with this UBI image. If @version is greater than the current UBI
+ * version, the image is rejected. This may be useful in future if something
+ * is changed radically. This field is duplicated in the volume identifier
+ * header.
+ *
+ * The @vid_hdr_offset and @data_offset fields contain the offset of the the
+ * volume identifier header and user data, relative to the beginning of the
+ * physical eraseblock. These values have to be the same for all physical
+ * eraseblocks.
+ *
+ * The @image_seq field is used to validate a UBI image that has been prepared
+ * for a UBI device. The @image_seq value can be any value, but it must be the
+ * same on all eraseblocks. UBI will ensure that all new erase counter headers
+ * also contain this value, and will check the value when attaching the flash.
+ * One way to make use of @image_seq is to increase its value by one every time
+ * an image is flashed over an existing image, then, if the flashing does not
+ * complete, UBI will detect the error when attaching the media.
+ */
+struct ubi_ec_hdr {
+	__be32  magic;
+	__u8    version;
+	__u8    padding1[3];
+	__be64  ec; /* Warning: the current limit is 31-bit anyway! */
+	__be32  vid_hdr_offset;
+	__be32  data_offset;
+	__be32  image_seq;
+	__u8    padding2[32];
+	__be32  hdr_crc;
+} __packed;
+
+/**
+ * struct ubi_vid_hdr - on-flash UBI volume identifier header.
+ * @magic: volume identifier header magic number (%UBI_VID_HDR_MAGIC)
+ * @version: UBI implementation version which is supposed to accept this UBI
+ *           image (%UBI_VERSION)
+ * @vol_type: volume type (%UBI_VID_DYNAMIC or %UBI_VID_STATIC)
+ * @copy_flag: if this logical eraseblock was copied from another physical
+ *             eraseblock (for wear-leveling reasons)
+ * @compat: compatibility of this volume (%0, %UBI_COMPAT_DELETE,
+ *          %UBI_COMPAT_IGNORE, %UBI_COMPAT_PRESERVE, or %UBI_COMPAT_REJECT)
+ * @vol_id: ID of this volume
+ * @lnum: logical eraseblock number
+ * @padding1: reserved for future, zeroes
+ * @data_size: how many bytes of data this logical eraseblock contains
+ * @used_ebs: total number of used logical eraseblocks in this volume
+ * @data_pad: how many bytes at the end of this physical eraseblock are not
+ *            used
+ * @data_crc: CRC checksum of the data stored in this logical eraseblock
+ * @padding2: reserved for future, zeroes
+ * @sqnum: sequence number
+ * @padding3: reserved for future, zeroes
+ * @hdr_crc: volume identifier header CRC checksum
+ *
+ * The @sqnum is the value of the global sequence counter at the time when this
+ * VID header was created. The global sequence counter is incremented each time
+ * UBI writes a new VID header to the flash, i.e. when it maps a logical
+ * eraseblock to a new physical eraseblock. The global sequence counter is an
+ * unsigned 64-bit integer and we assume it never overflows. The @sqnum
+ * (sequence number) is used to distinguish between older and newer versions of
+ * logical eraseblocks.
+ *
+ * There are 2 situations when there may be more than one physical eraseblock
+ * corresponding to the same logical eraseblock, i.e., having the same @vol_id
+ * and @lnum values in the volume identifier header. Suppose we have a logical
+ * eraseblock L and it is mapped to the physical eraseblock P.
+ *
+ * 1. Because UBI may erase physical eraseblocks asynchronously, the following
+ * situation is possible: L is asynchronously erased, so P is scheduled for
+ * erasure, then L is written to,i.e. mapped to another physical eraseblock P1,
+ * so P1 is written to, then an unclean reboot happens. Result - there are 2
+ * physical eraseblocks P and P1 corresponding to the same logical eraseblock
+ * L. But P1 has greater sequence number, so UBI picks P1 when it attaches the
+ * flash.
+ *
+ * 2. From time to time UBI moves logical eraseblocks to other physical
+ * eraseblocks for wear-leveling reasons. If, for example, UBI moves L from P
+ * to P1, and an unclean reboot happens before P is physically erased, there
+ * are two physical eraseblocks P and P1 corresponding to L and UBI has to
+ * select one of them when the flash is attached. The @sqnum field says which
+ * PEB is the original (obviously P will have lower @sqnum) and the copy. But
+ * it is not enough to select the physical eraseblock with the higher sequence
+ * number, because the unclean reboot could have happen in the middle of the
+ * copying process, so the data in P is corrupted. It is also not enough to
+ * just select the physical eraseblock with lower sequence number, because the
+ * data there may be old (consider a case if more data was added to P1 after
+ * the copying). Moreover, the unclean reboot may happen when the erasure of P
+ * was just started, so it result in unstable P, which is "mostly" OK, but
+ * still has unstable bits.
+ *
+ * UBI uses the @copy_flag field to indicate that this logical eraseblock is a
+ * copy. UBI also calculates data CRC when the data is moved and stores it at
+ * the @data_crc field of the copy (P1). So when UBI needs to pick one physical
+ * eraseblock of two (P or P1), the @copy_flag of the newer one (P1) is
+ * examined. If it is cleared, the situation* is simple and the newer one is
+ * picked. If it is set, the data CRC of the copy (P1) is examined. If the CRC
+ * checksum is correct, this physical eraseblock is selected (P1). Otherwise
+ * the older one (P) is selected.
+ *
+ * There are 2 sorts of volumes in UBI: user volumes and internal volumes.
+ * Internal volumes are not seen from outside and are used for various internal
+ * UBI purposes. In this implementation there is only one internal volume - the
+ * layout volume. Internal volumes are the main mechanism of UBI extensions.
+ * For example, in future one may introduce a journal internal volume. Internal
+ * volumes have their own reserved range of IDs.
+ *
+ * The @compat field is only used for internal volumes and contains the "degree
+ * of their compatibility". It is always zero for user volumes. This field
+ * provides a mechanism to introduce UBI extensions and to be still compatible
+ * with older UBI binaries. For example, if someone introduced a journal in
+ * future, he would probably use %UBI_COMPAT_DELETE compatibility for the
+ * journal volume.  And in this case, older UBI binaries, which know nothing
+ * about the journal volume, would just delete this volume and work perfectly
+ * fine. This is similar to what Ext2fs does when it is fed by an Ext3fs image
+ * - it just ignores the Ext3fs journal.
+ *
+ * The @data_crc field contains the CRC checksum of the contents of the logical
+ * eraseblock if this is a static volume. In case of dynamic volumes, it does
+ * not contain the CRC checksum as a rule. The only exception is when the
+ * data of the physical eraseblock was moved by the wear-leveling sub-system,
+ * then the wear-leveling sub-system calculates the data CRC and stores it in
+ * the @data_crc field. And of course, the @copy_flag is %in this case.
+ *
+ * The @data_size field is used only for static volumes because UBI has to know
+ * how many bytes of data are stored in this eraseblock. For dynamic volumes,
+ * this field usually contains zero. The only exception is when the data of the
+ * physical eraseblock was moved to another physical eraseblock for
+ * wear-leveling reasons. In this case, UBI calculates CRC checksum of the
+ * contents and uses both @data_crc and @data_size fields. In this case, the
+ * @data_size field contains data size.
+ *
+ * The @used_ebs field is used only for static volumes and indicates how many
+ * eraseblocks the data of the volume takes. For dynamic volumes this field is
+ * not used and always contains zero.
+ *
+ * The @data_pad is calculated when volumes are created using the alignment
+ * parameter. So, effectively, the @data_pad field reduces the size of logical
+ * eraseblocks of this volume. This is very handy when one uses block-oriented
+ * software (say, cramfs) on top of the UBI volume.
+ */
+struct ubi_vid_hdr {
+	__be32  magic;
+	__u8    version;
+	__u8    vol_type;
+	__u8    copy_flag;
+	__u8    compat;
+	__be32  vol_id;
+	__be32  lnum;
+	__u8    padding1[4];
+	__be32  data_size;
+	__be32  used_ebs;
+	__be32  data_pad;
+	__be32  data_crc;
+	__u8    padding2[4];
+	__be64  sqnum;
+	__u8    padding3[12];
+	__be32  hdr_crc;
+} __packed;
+
+/* Internal UBI volumes count */
+#define UBI_INT_VOL_COUNT 1
+
+/*
+ * Starting ID of internal volumes: 0x7fffefff.
+ * There is reserved room for 4096 internal volumes.
+ */
+#define UBI_INTERNAL_VOL_START (0x7FFFFFFF - 4096)
+
+/* The layout volume contains the volume table */
+
+#define UBI_LAYOUT_VOLUME_ID     UBI_INTERNAL_VOL_START
+#define UBI_LAYOUT_VOLUME_TYPE   UBI_VID_DYNAMIC
+#define UBI_LAYOUT_VOLUME_ALIGN  1
+#define UBI_LAYOUT_VOLUME_EBS    2
+#define UBI_LAYOUT_VOLUME_NAME   "layout volume"
+#define UBI_LAYOUT_VOLUME_COMPAT UBI_COMPAT_REJECT
+
+/* The maximum number of volumes per one UBI device */
+#define UBI_MAX_VOLUMES 128
+
+/* The maximum volume name length */
+#define UBI_VOL_NAME_MAX 127
+
+/* Size of the volume table record */
+#define UBI_VTBL_RECORD_SIZE sizeof(struct ubi_vtbl_record)
+
+/* Size of the volume table record without the ending CRC */
+#define UBI_VTBL_RECORD_SIZE_CRC (UBI_VTBL_RECORD_SIZE - sizeof(__be32))
+
+/**
+ * struct ubi_vtbl_record - a record in the volume table.
+ * @reserved_pebs: how many physical eraseblocks are reserved for this volume
+ * @alignment: volume alignment
+ * @data_pad: how many bytes are unused at the end of the each physical
+ * eraseblock to satisfy the requested alignment
+ * @vol_type: volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME)
+ * @upd_marker: if volume update was started but not finished
+ * @name_len: volume name length
+ * @name: the volume name
+ * @flags: volume flags (%UBI_VTBL_AUTORESIZE_FLG)
+ * @padding: reserved, zeroes
+ * @crc: a CRC32 checksum of the record
+ *
+ * The volume table records are stored in the volume table, which is stored in
+ * the layout volume. The layout volume consists of 2 logical eraseblock, each
+ * of which contains a copy of the volume table (i.e., the volume table is
+ * duplicated). The volume table is an array of &struct ubi_vtbl_record
+ * objects indexed by the volume ID.
+ *
+ * If the size of the logical eraseblock is large enough to fit
+ * %UBI_MAX_VOLUMES records, the volume table contains %UBI_MAX_VOLUMES
+ * records. Otherwise, it contains as many records as it can fit (i.e., size of
+ * logical eraseblock divided by sizeof(struct ubi_vtbl_record)).
+ *
+ * The @upd_marker flag is used to implement volume update. It is set to %1
+ * before update and set to %0 after the update. So if the update operation was
+ * interrupted, UBI knows that the volume is corrupted.
+ *
+ * The @alignment field is specified when the volume is created and cannot be
+ * later changed. It may be useful, for example, when a block-oriented file
+ * system works on top of UBI. The @data_pad field is calculated using the
+ * logical eraseblock size and @alignment. The alignment must be multiple to the
+ * minimal flash I/O unit. If @alignment is 1, all the available space of
+ * the physical eraseblocks is used.
+ *
+ * Empty records contain all zeroes and the CRC checksum of those zeroes.
+ */
+struct ubi_vtbl_record {
+	__be32  reserved_pebs;
+	__be32  alignment;
+	__be32  data_pad;
+	__u8    vol_type;
+	__u8    upd_marker;
+	__be16  name_len;
+	__u8    name[UBI_VOL_NAME_MAX+1];
+	__u8    flags;
+	__u8    padding[23];
+	__be32  crc;
+} __packed;
+
+/* UBI fastmap on-flash data structures */
+
+#define UBI_FM_SB_VOLUME_ID	(UBI_LAYOUT_VOLUME_ID + 1)
+#define UBI_FM_DATA_VOLUME_ID	(UBI_LAYOUT_VOLUME_ID + 2)
+
+/* fastmap on-flash data structure format version */
+#define UBI_FM_FMT_VERSION	1
+
+#define UBI_FM_SB_MAGIC		0x7B11D69F
+#define UBI_FM_HDR_MAGIC	0xD4B82EF7
+#define UBI_FM_VHDR_MAGIC	0xFA370ED1
+#define UBI_FM_POOL_MAGIC	0x67AF4D08
+#define UBI_FM_EBA_MAGIC	0xf0c040a8
+
+/* A fastmap supber block can be located between PEB 0 and
+ * UBI_FM_MAX_START */
+#define UBI_FM_MAX_START	64
+
+/* A fastmap can use up to UBI_FM_MAX_BLOCKS PEBs */
+#define UBI_FM_MAX_BLOCKS	32
+
+/* 5% of the total number of PEBs have to be scanned while attaching
+ * from a fastmap.
+ * But the size of this pool is limited to be between UBI_FM_MIN_POOL_SIZE and
+ * UBI_FM_MAX_POOL_SIZE */
+#define UBI_FM_MIN_POOL_SIZE	8
+#define UBI_FM_MAX_POOL_SIZE	256
+
+#define UBI_FM_WL_POOL_SIZE	25
+
+/**
+ * struct ubi_fm_sb - UBI fastmap super block
+ * @magic: fastmap super block magic number (%UBI_FM_SB_MAGIC)
+ * @version: format version of this fastmap
+ * @data_crc: CRC over the fastmap data
+ * @used_blocks: number of PEBs used by this fastmap
+ * @block_loc: an array containing the location of all PEBs of the fastmap
+ * @block_ec: the erase counter of each used PEB
+ * @sqnum: highest sequence number value at the time while taking the fastmap
+ *
+ */
+struct ubi_fm_sb {
+	__be32 magic;
+	__u8 version;
+	__u8 padding1[3];
+	__be32 data_crc;
+	__be32 used_blocks;
+	__be32 block_loc[UBI_FM_MAX_BLOCKS];
+	__be32 block_ec[UBI_FM_MAX_BLOCKS];
+	__be64 sqnum;
+	__u8 padding2[32];
+} __packed;
+
+/**
+ * struct ubi_fm_hdr - header of the fastmap data set
+ * @magic: fastmap header magic number (%UBI_FM_HDR_MAGIC)
+ * @free_peb_count: number of free PEBs known by this fastmap
+ * @used_peb_count: number of used PEBs known by this fastmap
+ * @scrub_peb_count: number of to be scrubbed PEBs known by this fastmap
+ * @bad_peb_count: number of bad PEBs known by this fastmap
+ * @erase_peb_count: number of bad PEBs which have to be erased
+ * @vol_count: number of UBI volumes known by this fastmap
+ */
+struct ubi_fm_hdr {
+	__be32 magic;
+	__be32 free_peb_count;
+	__be32 used_peb_count;
+	__be32 scrub_peb_count;
+	__be32 bad_peb_count;
+	__be32 erase_peb_count;
+	__be32 vol_count;
+	__u8 padding[4];
+} __packed;
+
+/* struct ubi_fm_hdr is followed by two struct ubi_fm_scan_pool */
+
+/**
+ * struct ubi_fm_scan_pool - Fastmap pool PEBs to be scanned while attaching
+ * @magic: pool magic numer (%UBI_FM_POOL_MAGIC)
+ * @size: current pool size
+ * @max_size: maximal pool size
+ * @pebs: an array containing the location of all PEBs in this pool
+ */
+struct ubi_fm_scan_pool {
+	__be32 magic;
+	__be16 size;
+	__be16 max_size;
+	__be32 pebs[UBI_FM_MAX_POOL_SIZE];
+	__be32 padding[4];
+} __packed;
+
+/* ubi_fm_scan_pool is followed by nfree+nused struct ubi_fm_ec records */
+
+/**
+ * struct ubi_fm_ec - stores the erase counter of a PEB
+ * @pnum: PEB number
+ * @ec: ec of this PEB
+ */
+struct ubi_fm_ec {
+	__be32 pnum;
+	__be32 ec;
+} __packed;
+
+/**
+ * struct ubi_fm_volhdr - Fastmap volume header
+ * it identifies the start of an eba table
+ * @magic: Fastmap volume header magic number (%UBI_FM_VHDR_MAGIC)
+ * @vol_id: volume id of the fastmapped volume
+ * @vol_type: type of the fastmapped volume
+ * @data_pad: data_pad value of the fastmapped volume
+ * @used_ebs: number of used LEBs within this volume
+ * @last_eb_bytes: number of bytes used in the last LEB
+ */
+struct ubi_fm_volhdr {
+	__be32 magic;
+	__be32 vol_id;
+	__u8 vol_type;
+	__u8 padding1[3];
+	__be32 data_pad;
+	__be32 used_ebs;
+	__be32 last_eb_bytes;
+	__u8 padding2[8];
+} __packed;
+
+/* struct ubi_fm_volhdr is followed by one struct ubi_fm_eba records */
+
+/**
+ * struct ubi_fm_eba - denotes an association beween a PEB and LEB
+ * @magic: EBA table magic number
+ * @reserved_pebs: number of table entries
+ * @pnum: PEB number of LEB (LEB is the index)
+ */
+struct ubi_fm_eba {
+	__be32 magic;
+	__be32 reserved_pebs;
+	__be32 pnum[0];
+} __packed;
+#endif /* !__UBI_MEDIA_H__ */
Index: u-boot/drivers/mtd/ubispl/ubi-wrapper.h
===================================================================
--- /dev/null
+++ u-boot/drivers/mtd/ubispl/ubi-wrapper.h
@@ -0,0 +1,104 @@
+/*
+ * The parts taken from the kernel implementation are:
+ *
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * UBISPL specific wrappers and defines:
+ *
+ * Copyright (c) Thomas Gleixner <tglx at linutronix.de>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+/*
+ * Contains various defines, wrappers 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,
+};
+
+#define UBI_BAD_FASTMAP		(-1)
+
+/*
+ * Stub structure so the fastmap code is happy.
+ */
+struct ubi_attach_info {
+	int i;
+};
+
+/**
+ * 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
Index: u-boot/drivers/mtd/ubispl/ubispl.c
===================================================================
--- /dev/null
+++ u-boot/drivers/mtd/ubispl/ubispl.c
@@ -0,0 +1,920 @@
+/*
+ * Copyright (c) Thomas Gleixner <tglx at linutronix.de>
+ *
+ * The parts taken from the kernel implementation are:
+ *
+ * Copyright (c) International Business Machines Corp., 2006
+
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.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_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 -1;
+	/*
+	 * 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 -1;
+
+	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 -1;
+}
+
+/* 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 vol_type, u32 pnum, u32 lnum)
+{
+	struct ubi_vol_info *vi = ubi->volinfo + vol_id;
+	u32 *ltp;
+
+	/*
+	 * We only care about static volumes with an id <
+	 * UBI_SPL_VOL_IDS.
+	 */
+	if (vol_id >= UBI_SPL_VOL_IDS || vol_type != UBI_VID_STATIC)
+		return 0;
+
+	/*
+	 * 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 -1;
+	}
+
+	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 -1;
+
+	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 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, vh->vol_type, 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 -1;
+
+	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;
+}
+
+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;
+	unsigned int fsize, nblocks;
+	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;
+	nblocks = ubi->peb_count;
+	fsize = ubi->fsize_mb;
+	ubi_msg("attached by fastmap %uMB %u blocks", fsize, nblocks);
+	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 -1;
+	}
+
+	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 -1;
+	}
+
+	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;
+
+	if (vol_id >= UBI_SPL_VOL_IDS)
+		return -1;
+
+	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;
+	}
+	return 0;
+}
+
+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("Load %s (#%d)", lv->name, lv->vol_id);
+		res = ipl_load(ubi, lv->vol_id, lv->load_addr);
+		if (res) {
+			if (fastmap) {
+				fastmap = 0;
+				goto retry;
+			}
+			ubi_warn("Failed \n");
+			return res;
+		}
+	}
+	return 0;
+}
Index: u-boot/drivers/mtd/ubispl/ubispl.h
===================================================================
--- /dev/null
+++ u-boot/drivers/mtd/ubispl/ubispl.h
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) Thomas Gleixner <tglx at linutronix.de>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#ifndef _UBOOT_MTD_UBISPL_H
+#define _UBOOT_MTD_UBISPL_H
+
+#include "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 {
+	int				(*read)(u32 pnum, u32 offset, u32 len, void *dest);
+	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
Index: u-boot/include/ubispl.h
===================================================================
--- /dev/null
+++ u-boot/include/ubispl.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) Thomas Gleixner <tglx at linutronix.de>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+#ifndef __UBOOT_UBISPL_H
+#define __UBOOT_UBISPL_H
+
+/*
+ * The following CONFIG options are relevant for UBISPL
+ *
+ * #define CONFIG_SPL_UBI_MAX_VOL_LEBS		256
+ *
+ * Defines the maximum number of logical erase blocks per loadable
+ * (static) volume to size the ubispl internal arrays.
+ *
+ * #define CONFIG_SPL_UBI_MAX_PEB_SIZE		(256*1024)
+ *
+ * Defines the maximum physical erase block size to size the fastmap
+ * buffer for ubispl.
+ *
+ * #define CONFIG_SPL_UBI_MAX_PEBS		4096
+ *
+ * Define the maximum number of physical erase blocks to size the
+ * ubispl internal arrays.
+ *
+ * #define CONFIG_SPL_UBI_VOL_IDS		8
+ *
+ * Defines the maximum number of volumes in which UBISPL is
+ * interested. Limits the amount of memory for the scan data and
+ * speeds up the scan process as we simply ignore stuff which we dont
+ * want to load from the SPL anyway. So the volumes which can be
+ * loaded in the above example are ids 0 - 7
+ */
+
+/*
+ * The struct definition is in drivers/mtd/ubispl/ubispl.h. It does
+ * not fit into the BSS due to the large buffer requirement of the
+ * upstream fastmap code. So the caller of ubispl_load_volumes needs
+ * to hand in a pointer to a free memory area where ubispl will place
+ * its data. The area is not required to be initialized.
+ */
+struct ubi_scan_info;
+
+/**
+ * struct ubispl_info - description structure for fast ubi scan
+ * @ubi:		Pointer to memory space for ubi scan info structure
+ * @peb_size:		Physical erase block size
+ * @vid_offset:		Offset of the VID header
+ * @leb_start:		Start of the logical erase block, i.e. offset of data
+ * @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.
+ * @fastmap:		Enable fastmap attachment
+ * @read:		Read function to access the flash
+ */
+struct ubispl_info {
+	struct ubi_scan_info	*ubi;
+	u32			peb_size;
+	u32			vid_offset;
+	u32			leb_start;
+	u32			peb_count;
+	u32			peb_offset;
+	int			fastmap;
+	int			(*read)(u32 pnum, u32 offset, u32 len, void *dest);
+};
+
+/**
+ * struct ubispl_load - structure to describe a volume to load
+ * @name:	Name of the volume
+ * @vol_id:	Volume id
+ * @load_addr:	Load address of the volume
+ */
+struct ubispl_load {
+	char		*name;
+	int		vol_id;
+	void		*load_addr;
+};
+
+/**
+ * ubispl_load_volumes - Scan flash and load volumes
+ * @info:	Pointer to the ubi scan info structure
+ * @lovls:	Pointer to array of volumes to load
+ * @nrvols:	Array size of @lovls
+ */
+extern int ubispl_load_volumes(struct ubispl_info *info,
+			       struct ubispl_load *lvols, int nrvols);
+
+#endif
Index: u-boot/scripts/Makefile.spl
===================================================================
--- u-boot.orig/scripts/Makefile.spl
+++ u-boot/scripts/Makefile.spl
@@ -105,6 +105,7 @@ libs-y += fs/
 libs-$(CONFIG_SPL_LIBGENERIC_SUPPORT) += lib/
 libs-$(CONFIG_SPL_POWER_SUPPORT) += drivers/power/ drivers/power/pmic/
 libs-$(CONFIG_SPL_MTD_SUPPORT) += drivers/mtd/
+libs-$(CONFIG_SPL_UBI) += drivers/mtd/ubispl/
 libs-$(if $(CONFIG_CMD_NAND),$(CONFIG_SPL_NAND_SUPPORT)) += drivers/mtd/nand/
 libs-$(CONFIG_SPL_DRIVERS_MISC_SUPPORT) += drivers/misc/
 libs-$(CONFIG_SPL_ONENAND_SUPPORT) += drivers/mtd/onenand/




More information about the U-Boot mailing list