[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