[RFC PATCH 20/20] boot: bootmeth: openwrt: add slot configuration from environment

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


Add configurable multi-slot boot with slot names and boot order
defined via environment variables. This enables production/recovery
dual-boot (or any number of named slots).

New environment variables:

- openwrt_slot_<name>=<location> -- defines a named slot. <name> is
  an arbitrary label (e.g. "production", "recovery"). <location> is
  a GPT partition label, MTD partition name, or UBI volume name.

- openwrt_boot_order=<name1> <name2> ... -- space-separated list of
  slot names that are eligible for booting. Only partitions/volumes
  whose name matches a configured slot's location are probed.

Without openwrt_boot_order, all partitions/volumes are probed
(backward-compatible with the Milestone 1/2 behavior). When set,
only partitions matching a defined slot pass the filter.

The matched slot name is stored in bflow->os_name for later use by
boot-loop avoidance (Milestone 4) and boot menu display.

The MTD and UBI bootdevs now delegate to bootmeth_read_bootflow()
instead of setting BOOTFLOWST_READY directly, so that slot filtering
is applied uniformly across all storage backends.

Example configuration:

  openwrt_slot_production=firmware
  openwrt_slot_recovery=recovery
  openwrt_boot_order=production recovery

Signed-off-by: Daniel Golle <daniel at makrotopia.org>
---
 boot/bootmeth_openwrt.c | 130 ++++++++++++++++++++++++++++++++--------
 1 file changed, 104 insertions(+), 26 deletions(-)

diff --git a/boot/bootmeth_openwrt.c b/boot/bootmeth_openwrt.c
index d448697fe08..6e90a203ed6 100644
--- a/boot/bootmeth_openwrt.c
+++ b/boot/bootmeth_openwrt.c
@@ -24,6 +24,63 @@
 #include <linux/libfdt.h>
 #include <linux/sizes.h>
 
+/**
+ * openwrt_match_slot() - check if a partition name matches a configured slot
+ * @part_name: GPT label, MTD partition name, or UBI volume name
+ * @slot_namep: set to strdup'd slot name on match (caller must free)
+ *
+ * When ``openwrt_boot_order`` is set in the environment, only partitions
+ * whose name matches one of the ``openwrt_slot_<name>`` locations are
+ * accepted. Without ``openwrt_boot_order``, all partitions pass.
+ *
+ * Return: 0 if accepted, -ENOENT if filtered out
+ */
+static int openwrt_match_slot(const char *part_name, char **slot_namep)
+{
+	const char *order, *p;
+
+	*slot_namep = NULL;
+
+	order = env_get("openwrt_boot_order");
+	if (!order)
+		return 0;
+
+	if (!part_name)
+		return -ENOENT;
+
+	p = order;
+	while (*p) {
+		char name[64], var[80];
+		const char *location, *end;
+		int len;
+
+		while (*p == ' ')
+			p++;
+		if (!*p)
+			break;
+
+		end = p;
+		while (*end && *end != ' ')
+			end++;
+
+		len = end - p;
+		if (len >= sizeof(name))
+			len = sizeof(name) - 1;
+		memcpy(name, p, len);
+		name[len] = '\0';
+		p = end;
+
+		snprintf(var, sizeof(var), "openwrt_slot_%s", name);
+		location = env_get(var);
+		if (location && !strcmp(part_name, location)) {
+			*slot_namep = strdup(name);
+			return 0;
+		}
+	}
+
+	return -ENOENT;
+}
+
 static int openwrt_check(struct udevice *dev, struct bootflow_iter *iter)
 {
 	if (bootflow_iter_check_blk(iter))
@@ -34,40 +91,61 @@ static int openwrt_check(struct udevice *dev, struct bootflow_iter *iter)
 
 static int openwrt_read_bootflow(struct udevice *dev, struct bootflow *bflow)
 {
-	struct blk_desc *desc = dev_get_uclass_plat(bflow->blk);
 	const char *part_name = NULL;
-	struct disk_partition info;
-	void *buf;
+	char *slot_name = NULL;
 	int ret;
 
-	/* Get partition geometry */
-	ret = part_get_info(desc, bflow->part, &info);
-	if (ret)
-		return log_msg_ret("part", ret);
-
-	part_name = (const char *)info.name;
-
-	/* Read first block to probe for an FDT/FIT header */
-	buf = memalign(SZ_1K, desc->blksz);
-	if (!buf)
-		return log_msg_ret("mem", -ENOMEM);
+	if (bflow->blk) {
+		struct blk_desc *desc = dev_get_uclass_plat(bflow->blk);
+		struct disk_partition info;
+		void *buf;
+
+		ret = part_get_info(desc, bflow->part, &info);
+		if (ret)
+			return log_msg_ret("part", ret);
+
+		part_name = (const char *)info.name;
+
+		/* Check slot filter before expensive I/O */
+		ret = openwrt_match_slot(part_name, &slot_name);
+		if (ret)
+			return -ENOENT;
+
+		/* Read first block to probe for an FDT/FIT header */
+		buf = memalign(SZ_1K, desc->blksz);
+		if (!buf) {
+			free(slot_name);
+			return log_msg_ret("mem", -ENOMEM);
+		}
+
+		ret = blk_read(bflow->blk, info.start, 1, buf);
+		if (ret != 1) {
+			free(buf);
+			free(slot_name);
+			return log_msg_ret("rd", -EIO);
+		}
+
+		if (fdt_check_header(buf)) {
+			free(buf);
+			free(slot_name);
+			return -ENOENT;
+		}
 
-	ret = blk_read(bflow->blk, info.start, 1, buf);
-	if (ret != 1) {
 		free(buf);
-		return log_msg_ret("rd", -EIO);
-	}
 
-	/* Must start with a valid FDT header */
-	if (fdt_check_header(buf)) {
-		free(buf);
-		return -ENOENT;
-	}
+		/* Show the GPT partition label as Filename */
+		bflow->fname = strdup(part_name);
+	} else {
+		/* MTD or UBI — partition/volume name in bootmeth_priv */
+		part_name = bflow->bootmeth_priv;
 
-	free(buf);
+		ret = openwrt_match_slot(part_name, &slot_name);
+		if (ret)
+			return -ENOENT;
+	}
 
-	/* Show the GPT partition label as Filename */
-	bflow->fname = strdup(part_name);
+	if (slot_name)
+		bflow->os_name = slot_name;
 
 	bflow->state = BOOTFLOWST_READY;
 
-- 
2.53.0


More information about the U-Boot mailing list