[U-Boot] [ubiblock PATCH] Read-only block driver on top of UBI volumes

Juha Sarlin jsub at sarlin.mobi
Thu Nov 21 00:40:19 UTC 2019


Add "ubi block" command to create a block device on top of a UBI volume.
This block device can be used with some U-Boot commands, eg: fstype, ls
and load.

Example use:
    => ubi part fs
    => ubi block root
    root dev=0
    => fstype ubi 0
    squashfs

ubiblock_read() is based on drivers/mtd/ubi/block.c from Linux 4.19.75.

Signed-off-by: Juha Sarlin <jsub at sarlin.mobi>
---

 cmd/ubi.c                    |  36 ++++++++++
 disk/part.c                  |  22 ------
 drivers/block/blk-uclass.c   |   4 +-
 drivers/mtd/ubi/Kconfig      |  15 ++++
 drivers/mtd/ubi/Makefile     |   2 +
 drivers/mtd/ubi/block.c      | 134 +++++++++++++++++++++++++++++++++++
 drivers/mtd/ubi/build.c      |   1 +
 drivers/mtd/ubi/ubi-uclass.c |  74 +++++++++++++++++++
 drivers/mtd/ubi/ubi.h        |  19 +++--
 include/blk.h                |   1 +
 include/dm/uclass-id.h       |   1 +
 include/linux/mtd/ubi.h      |   2 +-
 include/ubi_uboot.h          |   2 -
 13 files changed, 276 insertions(+), 37 deletions(-)
 create mode 100644 drivers/mtd/ubi/block.c
 create mode 100644 drivers/mtd/ubi/ubi-uclass.c

diff --git a/cmd/ubi.c b/cmd/ubi.c
index 22ba5b1a2c..ee5738a9f2 100644
--- a/cmd/ubi.c
+++ b/cmd/ubi.c
@@ -102,6 +102,34 @@ static int ubi_check(char *name)
 	return 1;
 }
 
+/**
+ * Create BLK device on UBI volume.
+ *
+ * @name: [[ubi]dev]:volume
+ * @return CMD_RET_SUCCESS or CMD_RET_FAILURE
+ */
+static int ubi_block(char *name)
+{
+	const char *device, *volume;
+	int ret, ubi_num = 0;
+
+	volume = strchr(name, ':');
+	if (volume) {
+		volume++;
+		device = strncmp(name, "ubi", 3) ? name : name + 3;
+		ubi_num = simple_strtoul(device, NULL, 0);
+	} else {
+		volume = name;
+	}
+	ret = ubiblock_create(ubi_num, volume);
+	if (ret < 0) {
+		pr_err("Can't create ubi block device: %s\n", errno_str(ret));
+		return CMD_RET_FAILURE;
+	}
+	printf("%s dev=%d\n", name, ret);
+	return CMD_RET_SUCCESS;
+}
+
 static int verify_mkvol_req(const struct ubi_device *ubi,
 			    const struct ubi_mkvol_req *req)
 {
@@ -541,6 +569,13 @@ static int do_ubi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
 		return ubi_info(layout);
 	}
 
+	if (strcmp(argv[1], "block") == 0) {
+		if (argc > 2)
+			return ubi_block(argv[2]);
+		printf("No volume name\n");
+		return CMD_RET_FAILURE;
+	}
+
 	if (strcmp(argv[1], "check") == 0) {
 		if (argc > 2)
 			return ubi_check(argv[2]);
@@ -688,6 +723,7 @@ U_BOOT_CMD(
 		" - Write part of a volume from address\n"
 	"ubi read[vol] address volume [size]"
 		" - Read volume to address with size\n"
+	"ubi block volume - Create block device on UBI volume\n"
 	"ubi remove[vol] volume"
 		" - Remove volume\n"
 	"ubi skipcheck volume on/off - Set or clear skip_check flag in volume header\n"
diff --git a/disk/part.c b/disk/part.c
index 8982ef3bae..f168311e9a 100644
--- a/disk/part.c
+++ b/disk/part.c
@@ -466,28 +466,6 @@ int blk_get_device_part_str(const char *ifname, const char *dev_part_str,
 	}
 #endif
 
-#ifdef CONFIG_CMD_UBIFS
-	/*
-	 * Special-case ubi, ubi goes through a mtd, rather than through
-	 * a regular block device.
-	 */
-	if (0 == strcmp(ifname, "ubi")) {
-		if (!ubifs_is_mounted()) {
-			printf("UBIFS not mounted, use ubifsmount to mount volume first!\n");
-			return -1;
-		}
-
-		*dev_desc = NULL;
-		memset(info, 0, sizeof(*info));
-		strcpy((char *)info->type, BOOT_PART_TYPE);
-		strcpy((char *)info->name, "UBI");
-#if CONFIG_IS_ENABLED(PARTITION_UUIDS)
-		info->uuid[0] = 0;
-#endif
-		return 0;
-	}
-#endif
-
 	/* If no dev_part_str, use bootdevice environment variable */
 	if (!dev_part_str || !strlen(dev_part_str) ||
 	    !strcmp(dev_part_str, "-"))
diff --git a/drivers/block/blk-uclass.c b/drivers/block/blk-uclass.c
index ca8978f0e1..433ea16db4 100644
--- a/drivers/block/blk-uclass.c
+++ b/drivers/block/blk-uclass.c
@@ -24,6 +24,7 @@ static const char *if_typename_str[IF_TYPE_COUNT] = {
 	[IF_TYPE_NVME]		= "nvme",
 	[IF_TYPE_EFI]		= "efi",
 	[IF_TYPE_VIRTIO]	= "virtio",
+	[IF_TYPE_UBI]		= "ubi",
 };
 
 static enum uclass_id if_type_uclass_id[IF_TYPE_COUNT] = {
@@ -39,6 +40,7 @@ static enum uclass_id if_type_uclass_id[IF_TYPE_COUNT] = {
 	[IF_TYPE_NVME]		= UCLASS_NVME,
 	[IF_TYPE_EFI]		= UCLASS_EFI,
 	[IF_TYPE_VIRTIO]	= UCLASS_VIRTIO,
+	[IF_TYPE_UBI]		= UCLASS_UBI,
 };
 
 static enum if_type if_typename_to_iftype(const char *if_typename)
@@ -112,7 +114,7 @@ struct blk_desc *blk_get_devnum_by_typename(const char *if_typename, int devnum)
 
 		debug("%s: if_type=%d, devnum=%d: %s, %d, %d\n", __func__,
 		      if_type, devnum, dev->name, desc->if_type, desc->devnum);
-		if (desc->devnum != devnum)
+		if (desc->if_type != if_type || desc->devnum != devnum)
 			continue;
 
 		/* Find out the parent device uclass */
diff --git a/drivers/mtd/ubi/Kconfig b/drivers/mtd/ubi/Kconfig
index a78fd51ba7..008494db1a 100644
--- a/drivers/mtd/ubi/Kconfig
+++ b/drivers/mtd/ubi/Kconfig
@@ -95,6 +95,21 @@ config MTD_UBI_FASTMAP_AUTOCONVERT
 	  Set this parameter to enable fastmap automatically on images
 	  without a fastmap.
 
+config MTD_UBI_BLOCK
+	bool "Read-only block devices on top of UBI volumes"
+	default n
+	help
+	   This option enables read-only UBI block devices support. UBI block
+	   devices will be layered on top of UBI volumes, which means that the
+	   UBI driver will transparently handle things like bad eraseblocks and
+	   bit-flips. You can put any block-oriented file system on top of UBI
+	   volumes in read-only mode (e.g., ext4), but it is probably most
+	   practical for read-only file systems, like squashfs.
+
+	   When selected, this feature will be built in the UBI driver.
+
+	   If in doubt, say "N".
+
 config MTD_UBI_FM_DEBUG
 	int "Enable UBI fastmap debug"
 	depends on MTD_UBI_FASTMAP
diff --git a/drivers/mtd/ubi/Makefile b/drivers/mtd/ubi/Makefile
index 30d00fbdfe..92ea1fa0d1 100644
--- a/drivers/mtd/ubi/Makefile
+++ b/drivers/mtd/ubi/Makefile
@@ -4,6 +4,8 @@
 # Wolfgang Denk, DENX Software Engineering, wd at denx.de.
 
 obj-y += attach.o build.o vtbl.o vmt.o upd.o kapi.o eba.o io.o wl.o crc32.o
+obj-$(CONFIG_MTD_UBI) += ubi-uclass.o
+obj-$(CONFIG_MTD_UBI_BLOCK) += block.o
 obj-$(CONFIG_MTD_UBI_FASTMAP) += fastmap.o
 obj-y += misc.o
 obj-y += debug.o
diff --git a/drivers/mtd/ubi/block.c b/drivers/mtd/ubi/block.c
new file mode 100644
index 0000000000..b46cd32404
--- /dev/null
+++ b/drivers/mtd/ubi/block.c
@@ -0,0 +1,134 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2019 Juha Sarlin
+ * Copyright (c) 2014 Ezequiel Garcia
+ * Copyright (c) 2011 Free Electrons
+ */
+
+/*
+ * Read-only block devices on top of UBI volumes
+ *
+ * A simple implementation to allow a block device to be layered on top of a
+ * UBI volume. The implementation is provided by creating a static 1-to-1
+ * mapping between the block device and the UBI volume.
+ *
+ * The addressed byte is obtained from the addressed block sector, which is
+ * mapped linearly into the corresponding LEB:
+ *
+ *   LEB number = addressed byte / LEB size
+ */
+
+#include <div64.h>
+#include <dm.h>
+#include <dm/device-internal.h>
+#include <ubi_uboot.h>
+#include "ubi.h"
+
+static struct udevice *ubiblock_find(int ubi_num, const char *volume)
+{
+	struct udevice *dev;
+	char name[UBI_VOL_NAME_MAX + 8];
+
+	snprintf(name, sizeof(name), "ubi%d.%s", ubi_num, volume);
+	for (blk_first_device(IF_TYPE_UBI, &dev); dev; blk_next_device(&dev)) {
+		if (!strcmp(dev->name, name))
+			return dev;
+	}
+	return NULL;
+}
+
+static unsigned long ubiblock_read(struct udevice *dev, lbaint_t start,
+				   lbaint_t blkcnt, void *buffer)
+{
+	struct ubi_volume_desc *desc = dev->priv;
+	struct ubi_volume *vol = desc->vol;
+	struct blk_desc *blk = dev_get_uclass_platdata(dev);
+	int ret, leb, offset, bytes_left, to_read;
+	int leb_size = vol->ubi->leb_size;
+	int blksz = blk->blksz;
+	u64 pos;
+
+	to_read = blkcnt * blksz;
+	pos = start * blksz;
+
+	/* Get LEB:offset address to read from */
+	offset = do_div(pos, leb_size);
+	leb = pos;
+	bytes_left = to_read;
+
+	while (bytes_left) {
+		/*
+		 * We can only read one LEB at a time. Therefore if the read
+		 * length is larger than one LEB size, we split the operation.
+		 */
+		if (offset + to_read > leb_size)
+			to_read = leb_size - offset;
+
+		ret = ubi_read(desc, leb, buffer, offset, to_read);
+		if (ret < 0) {
+			pr_err("%s: ubi_read failed (%d)\n", __func__, ret);
+			return ret;
+		}
+		bytes_left -= to_read;
+		to_read = bytes_left;
+		leb += 1;
+		offset = 0;
+	}
+	return blkcnt;
+}
+
+/**
+ * Create BLK device on UBI volume.
+ *
+ * @return devnum or -errno
+ */
+int ubiblock_create(int ubi_num, const char *volume)
+{
+	struct udevice *dev, *parent;
+	struct ubi_volume_desc *desc;
+	struct blk_desc *blk;
+	struct ubi_volume *vol;
+	int ret, leb_size, blksz, size;
+
+	ret = ubi_get(ubi_num, &parent);
+	if (ret)
+		return ret;
+	desc = ubi_open_volume_nm(ubi_num, volume, UBI_READONLY);
+	if (IS_ERR(desc))
+		return PTR_ERR(desc);
+	dev = ubiblock_find(ubi_num, volume);
+	if (!dev) {
+		vol = desc->vol;
+		leb_size = vol->ubi->leb_size;
+		/* Block size must be power of 2 */
+		blksz = leb_size & ~(leb_size - 1);
+		size = vol->reserved_pebs * (leb_size / blksz);
+		ret = blk_create_devicef(parent, "ubiblock", volume,
+					 IF_TYPE_UBI, -1, blksz, size, &dev);
+		if (ret)
+			return ret;
+		dev->priv = desc;
+	}
+	blk = dev_get_uclass_platdata(dev);
+	return blk->devnum;
+}
+
+static int ubiblock_unbind(struct udevice *dev)
+{
+	if (dev->priv) {
+		ubi_close_volume(dev->priv);
+		dev->priv = NULL;
+	}
+	return 0;
+}
+
+static const struct blk_ops ubiblock_ops = {
+	.read	= ubiblock_read,
+};
+
+U_BOOT_DRIVER(ubiblock) = {
+	.name			= "ubiblock",
+	.id			= UCLASS_BLK,
+	.unbind			= ubiblock_unbind,
+	.ops			= &ubiblock_ops,
+};
diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c
index 42c5270c7f..0c15057a8d 100644
--- a/drivers/mtd/ubi/build.c
+++ b/drivers/mtd/ubi/build.c
@@ -1112,6 +1112,7 @@ int ubi_detach_mtd_dev(int ubi_num, int anyway)
 	if (!ubi)
 		return -EINVAL;
 
+	ubi_put(ubi_num);
 	spin_lock(&ubi_devices_lock);
 	put_device(&ubi->dev);
 	ubi->ref_count -= 1;
diff --git a/drivers/mtd/ubi/ubi-uclass.c b/drivers/mtd/ubi/ubi-uclass.c
new file mode 100644
index 0000000000..a13aa7512f
--- /dev/null
+++ b/drivers/mtd/ubi/ubi-uclass.c
@@ -0,0 +1,74 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2019 Juha Sarlin
+ */
+#include <common.h>
+#include <dm.h>
+#include <dm/lists.h>
+#include <dm/uclass-internal.h>
+#include <dm/device-internal.h>
+#include <dm/root.h>
+#include "ubi.h"
+
+/** Get udevice for a UBI device. */
+int ubi_get(int ubi_num, struct udevice **devp)
+{
+	struct ubi_device *ubi;
+	struct udevice *dev;
+	int ret;
+
+	ubi = ubi_get_device(ubi_num);
+	if (!ubi)
+		return -ENODEV;
+	ret = uclass_find_device_by_name(UCLASS_UBI, ubi->ubi_name, &dev);
+	if (ret) {
+		ret = device_bind_driver(dm_root(), "ubi",
+					 ubi->ubi_name, &dev);
+		if (ret) {
+			ubi_put_device(ubi);
+			return ret;
+		}
+		dev->priv = ubi;
+	} else {
+		ubi_put_device(ubi);
+	}
+	*devp = dev;
+	return 0;
+}
+
+/** Remove udevice for a UBI device. */
+int ubi_put(int ubi_num)
+{
+	struct ubi_device *ubi;
+	struct udevice *dev;
+	int ret;
+
+	ubi = ubi_get_device(ubi_num);
+	if (!ubi)
+		return 0;
+	ubi_put_device(ubi);
+	ret = uclass_find_device_by_name(UCLASS_UBI, ubi->ubi_name, &dev);
+	if (!ret)
+		device_unbind(dev);
+	return 0;
+}
+
+static int ubi_unbind(struct udevice *dev)
+{
+	if (dev->priv) {
+		ubi_put_device(dev->priv);
+		dev->priv = NULL;
+	}
+	return 0;
+}
+
+U_BOOT_DRIVER(ubi) = {
+	.id		= UCLASS_UBI,
+	.name		= "ubi",
+	.unbind		= ubi_unbind,
+};
+
+UCLASS_DRIVER(ubi) = {
+	.id		= UCLASS_UBI,
+	.name		= "ubi",
+};
diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h
index f44960186b..c0acb67c23 100644
--- a/drivers/mtd/ubi/ubi.h
+++ b/drivers/mtd/ubi/ubi.h
@@ -922,6 +922,10 @@ void ubi_do_get_volume_info(struct ubi_device *ubi, struct ubi_volume *vol,
 int ubi_compare_lebs(struct ubi_device *ubi, const struct ubi_ainf_peb *aeb,
 		      int pnum, const struct ubi_vid_hdr *vid_hdr);
 
+/* ubi-uclass.c */
+int ubi_get(int ubi_num, struct udevice **devp);
+int ubi_put(int ubi_num);
+
 /* fastmap.c */
 #ifdef CONFIG_MTD_UBI_FASTMAP
 size_t ubi_calc_fm_size(struct ubi_device *ubi);
@@ -933,19 +937,12 @@ static inline int ubi_update_fastmap(struct ubi_device *ubi) { return 0; }
 #endif
 
 /* block.c */
-#ifdef CONFIG_MTD_UBI_BLOCK
-int ubiblock_init(void);
-void ubiblock_exit(void);
-int ubiblock_create(struct ubi_volume_info *vi);
-int ubiblock_remove(struct ubi_volume_info *vi);
-#else
 static inline int ubiblock_init(void) { return 0; }
 static inline void ubiblock_exit(void) {}
-static inline int ubiblock_create(struct ubi_volume_info *vi)
-{
-	return -ENOSYS;
-}
-static inline int ubiblock_remove(struct ubi_volume_info *vi)
+#ifdef CONFIG_MTD_UBI_BLOCK
+int ubiblock_create(int ubi_num, const char *volume);
+#else
+static inline int ubiblock_create(int ubi_num, const char *volume)
 {
 	return -ENOSYS;
 }
diff --git a/include/blk.h b/include/blk.h
index d0c033aece..b4c5c35248 100644
--- a/include/blk.h
+++ b/include/blk.h
@@ -34,6 +34,7 @@ enum if_type {
 	IF_TYPE_NVME,
 	IF_TYPE_EFI,
 	IF_TYPE_VIRTIO,
+	IF_TYPE_UBI,
 
 	IF_TYPE_COUNT,			/* Number of interface types */
 };
diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h
index 0c563d898b..b8bc400661 100644
--- a/include/dm/uclass-id.h
+++ b/include/dm/uclass-id.h
@@ -102,6 +102,7 @@ enum uclass_id {
 	UCLASS_THERMAL,		/* Thermal sensor */
 	UCLASS_TIMER,		/* Timer device */
 	UCLASS_TPM,		/* Trusted Platform Module TIS interface */
+	UCLASS_UBI,		/* Unsorted Block Images */
 	UCLASS_UFS,		/* Universal Flash Storage */
 	UCLASS_USB,		/* USB bus */
 	UCLASS_USB_DEV_GENERIC,	/* USB generic device */
diff --git a/include/linux/mtd/ubi.h b/include/linux/mtd/ubi.h
index badf6a0c6c..6c7f22a42b 100644
--- a/include/linux/mtd/ubi.h
+++ b/include/linux/mtd/ubi.h
@@ -271,7 +271,7 @@ int ubi_flush(int ubi_num, int vol_id, int lnum);
  * This function is the same as the 'ubi_leb_read()' function, but it does not
  * provide the checking capability.
  */
-static inline int ubi_read(struct ubi_volume_desc *desc, int lnum, char *buf,
+static inline int ubi_read(struct ubi_volume_desc *desc, int lnum, void *buf,
 			   int offset, int len)
 {
 	return ubi_leb_read(desc, lnum, buf, offset, len, 0);
diff --git a/include/ubi_uboot.h b/include/ubi_uboot.h
index 0770228cd8..7a602c766c 100644
--- a/include/ubi_uboot.h
+++ b/include/ubi_uboot.h
@@ -46,8 +46,6 @@
 #undef CONFIG_MTD_UBI_DEBUG_MSG_IO
 #undef CONFIG_MTD_UBI_DEBUG_MSG_BLD
 
-#undef CONFIG_MTD_UBI_BLOCK
-
 /* ubi_init() disables returning error codes when built into the Linux
  * kernel so that it doesn't hang the Linux kernel boot process.  Since
  * the U-Boot driver code depends on getting valid error codes from this
-- 
2.20.1.98.gecbdaf0899



More information about the U-Boot mailing list