[RFC PATCH 17/20] boot: bootdev: add UBI boot device

Daniel Golle daniel at makrotopia.org
Mon Feb 16 22:24:00 CET 2026


Add a boot device driver for UBI (Unsorted Block Images) that enables
bootstd to scan UBI volumes for FIT images.

The driver:
- Has a hunt callback that auto-attaches UBI: walks the DT for the
  first partition with compatible = "linux,ubi", finds the matching
  MTD device, and calls ubi_part_from_mtd() to attach
- Binds directly via bootdev_bind() as a child of the top-level MTD
  device, using a distinct name ("ubibootdev") to coexist with the
  MTD bootdev
- Iterates UBI volumes using iter->part as an index
- Reads the first bytes of each volume and checks for a valid FDT
  header via fdt_check_header()
- Stores the UBI volume name in bflow->bootmeth_priv for the
  bootmeth's boot() to pass to image_loader_init_ubi()
- Returns -ESHUTDOWN when all volumes are exhausted

Like the MTD bootdev, calls bootmeth_check() first so that only
compatible bootmeths produce bootflows. Inert until bootmeth_openwrt
is extended to accept UBI bootdevs.

Signed-off-by: Daniel Golle <daniel at makrotopia.org>
---
 boot/Kconfig       |   9 +++
 boot/Makefile      |   1 +
 boot/ubi_bootdev.c | 180 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 190 insertions(+)
 create mode 100644 boot/ubi_bootdev.c

diff --git a/boot/Kconfig b/boot/Kconfig
index 63e373cc62d..75d40744e69 100644
--- a/boot/Kconfig
+++ b/boot/Kconfig
@@ -594,6 +594,15 @@ config BOOTDEV_MTD
 	  This scans MTD partitions for uImage.FIT firmware images,
 	  enabling raw-flash boot via the OpenWrt boot method.
 
+config BOOTDEV_UBI
+	bool "UBI bootdev support"
+	depends on CMD_UBI
+	depends on BOOTSTD
+	help
+	  Enable a boot device for UBI (Unsorted Block Images).
+	  This scans UBI volumes for uImage.FIT firmware images,
+	  enabling raw-flash boot via the OpenWrt boot method.
+
 config BOOTMETH_OPENWRT
 	bool "Bootdev support for OpenWrt"
 	depends on FIT
diff --git a/boot/Makefile b/boot/Makefile
index feeed4924dd..aa7968c3932 100644
--- a/boot/Makefile
+++ b/boot/Makefile
@@ -73,6 +73,7 @@ obj-$(CONFIG_$(PHASE_)BOOTMETH_VBE_SIMPLE_OS) += vbe_simple_os.o
 
 obj-$(CONFIG_$(PHASE_)BOOTMETH_ANDROID) += bootmeth_android.o
 obj-$(CONFIG_$(PHASE_)BOOTDEV_MTD) += mtd_bootdev.o
+obj-$(CONFIG_$(PHASE_)BOOTDEV_UBI) += ubi_bootdev.o
 obj-$(CONFIG_$(PHASE_)BOOTMETH_OPENWRT) += bootmeth_openwrt.o
 
 obj-$(CONFIG_IMAGE_LOADER) += image-loader.o
diff --git a/boot/ubi_bootdev.c b/boot/ubi_bootdev.c
new file mode 100644
index 00000000000..03131a4ee1b
--- /dev/null
+++ b/boot/ubi_bootdev.c
@@ -0,0 +1,180 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * UBI boot device
+ *
+ * Copyright (C) 2026 Daniel Golle <daniel at makrotopia.org>
+ */
+
+#define LOG_CATEGORY UCLASS_BOOTSTD
+
+#include <bootdev.h>
+#include <bootflow.h>
+#include <bootmeth.h>
+#include <dm.h>
+#include <dm/ofnode.h>
+#include <malloc.h>
+#include <mtd.h>
+#include <ubi_uboot.h>
+#include <linux/libfdt.h>
+
+static int ubi_bootdev_get_bootflow(struct udevice *dev,
+				    struct bootflow_iter *iter,
+				    struct bootflow *bflow)
+{
+	struct ubi_device *ubi;
+	struct ubi_volume *vol;
+	struct mtd_info *ubi_mtd, *top_mtd, *part;
+	char buf[40];
+	char dname[60];
+	int ubi_part_idx = 0;
+	int n = 0;
+	int i, ret;
+
+	ret = bootmeth_check(bflow->method, iter);
+	if (ret)
+		return log_msg_ret("chk", ret);
+
+	ubi = ubi_devices[0];
+	if (!ubi)
+		return log_msg_ret("ubi", -ENODEV);
+
+	/* Count volumes so the scanning framework knows the bound */
+	for (i = 0; i < ubi->vtbl_slots; i++) {
+		if (ubi->volumes[i])
+			n++;
+	}
+	if (n)
+		iter->max_part = n - 1;
+
+	n = 0;
+
+	/* Walk to the iter->part'th UBI volume */
+	for (i = 0; i < ubi->vtbl_slots; i++) {
+		vol = ubi->volumes[i];
+		if (!vol)
+			continue;
+		if (n == iter->part)
+			goto found;
+		n++;
+	}
+	return -ESHUTDOWN;
+
+found:
+	ret = ubi_volume_read(vol->name, buf, 0, sizeof(buf));
+	if (ret)
+		return log_msg_ret("rd", -EIO);
+
+	if (fdt_check_header(buf))
+		return log_msg_ret("fdt", -ENOENT);
+
+	/*
+	 * Find the MTD partition index hosting UBI so we can display
+	 * a meaningful partition number in the bootflow listing.
+	 */
+	ubi_mtd = ubi->mtd;
+	top_mtd = ubi_mtd;
+	while (top_mtd->parent)
+		top_mtd = top_mtd->parent;
+
+	n = 0;
+	list_for_each_entry(part, &top_mtd->partitions, node) {
+		if (part == ubi_mtd) {
+			ubi_part_idx = n;
+			break;
+		}
+		n++;
+	}
+
+	/* Device-style name and partition index for bootflow list display */
+	snprintf(dname, sizeof(dname), "%s.part_%x", dev->name, ubi_part_idx);
+	bflow->name = strdup(dname);
+	bflow->part = ubi_part_idx;
+	bflow->fname = strdup(vol->name);
+	bflow->bootmeth_priv = strdup(vol->name);
+	bflow->state = BOOTFLOWST_MEDIA;
+
+	return bootmeth_read_bootflow(bflow->method, bflow);
+}
+
+static int ubi_bootdev_bind(struct udevice *dev)
+{
+	struct bootdev_uc_plat *ucp = dev_get_uclass_plat(dev);
+
+	ucp->prio = BOOTDEVP_4_SCAN_FAST;
+
+	return 0;
+}
+
+/**
+ * ubi_bootdev_hunt() - attach UBI and bind a bootdev for it
+ *
+ * Walk the DT for the first partition with compatible = "linux,ubi",
+ * find its MTD device, attach UBI via ubi_part_from_mtd(), then bind
+ * a ubi_bootdev as a child of the top-level MTD DM device.
+ */
+static int ubi_bootdev_hunt(struct bootdev_hunter *info, bool show)
+{
+	struct udevice *bdev;
+	struct mtd_info *mtd;
+	ofnode node;
+	int ret;
+
+	mtd_probe_devices();
+
+	if (!ubi_devices[0]) {
+		ofnode_for_each_compatible_node(node, "linux,ubi") {
+			mtd_for_each_device(mtd) {
+				if (ofnode_equal(mtd->flash_node, node))
+					goto found;
+			}
+		}
+		return -ENOENT;
+found:
+		ret = ubi_part_from_mtd(mtd);
+		if (ret)
+			return log_msg_ret("att", -ENOENT);
+	}
+
+	/* Find the top-level MTD device for the attached UBI */
+	mtd = ubi_devices[0]->mtd;
+	while (mtd->parent)
+		mtd = mtd->parent;
+
+	if (!mtd->dev)
+		return log_msg_ret("dev", -ENODEV);
+
+	/*
+	 * Bind directly — bootdev_setup_for_dev() cannot be used here
+	 * because the MTD bootdev may already occupy the single bootdev
+	 * child slot.
+	 */
+	ret = bootdev_bind(mtd->dev, "ubi_bootdev", "ubibootdev", &bdev);
+	if (ret)
+		return log_msg_ret("bd", ret);
+
+	return 0;
+}
+
+struct bootdev_ops ubi_bootdev_ops = {
+	.get_bootflow	= ubi_bootdev_get_bootflow,
+};
+
+static const struct udevice_id ubi_bootdev_ids[] = {
+	{ .compatible = "u-boot,bootdev-ubi" },
+	{ }
+};
+
+U_BOOT_DRIVER(ubi_bootdev) = {
+	.name		= "ubi_bootdev",
+	.id		= UCLASS_BOOTDEV,
+	.ops		= &ubi_bootdev_ops,
+	.bind		= ubi_bootdev_bind,
+	.of_match	= ubi_bootdev_ids,
+};
+
+BOOTDEV_HUNTER(ubi_bootdev_hunter) = {
+	.prio		= BOOTDEVP_4_SCAN_FAST,
+	.uclass		= UCLASS_MTD,
+	.drv		= DM_DRIVER_REF(ubi_bootdev),
+	.hunt		= ubi_bootdev_hunt,
+};
-- 
2.53.0


More information about the U-Boot mailing list