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 <[email protected]>
---
 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

Reply via email to