Hi Daniel,

On Mon, 16 Feb 2026 at 14:24, Daniel Golle <[email protected]> wrote:
>
> 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;
> +               }

The code above would benefit from being in its own function.

>
> -       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

Regards,
SIMon

Reply via email to