[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