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

