[PATCH v2 1/2] bootstd: Add implementation for bootmeth rauc

Wadim Egorov w.egorov at phytec.de
Thu Aug 14 10:46:36 CEST 2025



On 8/14/25 11:44 AM, Wadim Egorov wrote:
> Hi Tom,
> 
> On 6/2/25 2:31 PM, Martin Schwan wrote:
>> Add a bootmeth driver which supports booting A/B system with RAUC as
>> their update client.
>>
>> Signed-off-by: Martin Schwan <m.schwan at phytec.de>
>> ---
>>   boot/Kconfig         |  51 ++++++
>>   boot/Makefile        |   1 +
>>   boot/bootmeth_rauc.c | 432 +++++++++++++++++++++++++++++++++++++++++ 
>> ++++++++++
>>   3 files changed, 484 insertions(+)
>>
>> diff --git a/boot/Kconfig b/boot/Kconfig
>> index 
>> 30eb5b328d7ad8a73a2e84812dd7853b66039164..a610cf9faf99cd5480f94dcac89c3401cca394ae 100644
>> --- a/boot/Kconfig
>> +++ b/boot/Kconfig
>> @@ -855,6 +855,57 @@ config EXPO
>>         The expo can be presented in graphics form using a vidconsole, 
>> or in
>>         text form on a serial console.
>> +config BOOTMETH_RAUC
>> +    bool "Bootdev support for RAUC A/B systems"
>> +    default y if BOOTSTD_FULL
> 
> I noticed that the merge 009d7722ffd7 ("Merge patch series "bootstd: New 
> bootmeth for RAUC A/B systems"") deleted the above line.
> 
> Was it on purpose?

Nvm, missed your comment

   [trini: Don't enable by default].

What would be wrong about it to enable it by default?

> 
> Regards,
> Wadim
> 
>> +    depends on CMDLINE
>> +    select BOOTMETH_GLOBAL
>> +    select HUSH_PARSER
>> +    help
>> +      Enables support for booting RAUC A/B systems from MMC devices. 
>> This
>> +      makes the bootdevs look for a 'boot.scr.uimg' or 'boot.scr' in the
>> +      respective boot partitions, describing how to boot the distro.
>> +
>> +if BOOTMETH_RAUC
>> +
>> +config BOOTMETH_RAUC_BOOT_ORDER
>> +    string "RAUC boot order"
>> +    default "A B"
>> +    help
>> +      Sets the default boot order. This must be list of space-separated
>> +      strings naming the individual boot slots. Each entry in this 
>> string
>> +      should correspond to an existing slot on the target's flash 
>> device.
>> +
>> +config BOOTMETH_RAUC_PARTITIONS
>> +    string "RAUC boot and root partitions indexes"
>> +    default "1,2 3,4"
>> +    help
>> +      Sets the partition indexes of boot and root slots. This must be 
>> a list
>> +      of comma-separated pair values, which in turn are separated by 
>> spaces.
>> +      The first value in pair is for the boot partition and second 
>> for the
>> +      root partition.
>> +
>> +config BOOTMETH_RAUC_DEFAULT_TRIES
>> +    int "RAUC slot default tries"
>> +    default 3
>> +    help
>> +      Sets how many times a slot should be tried booting, before 
>> considering
>> +      it to be bad.
>> +
>> +config BOOTMETH_RAUC_RESET_ALL_ZERO_TRIES
>> +    bool "Reset slot tries when all RAUC slots have zero tries left"
>> +    default y
>> +    help
>> +      When all slots have zero tries left or no valid slot was found, 
>> reset
>> +      to the default boot order set by BOOTMETH_RAUC_BOOT_ORDER and 
>> set the
>> +      slot tries to their default value specified by
>> +      BOOTMETH_RAUC_DEFAULT_TRIES.
>> +
>> +      This prevents a system from remaining in an unbootable state, 
>> after
>> +      all slot tries were decremented to zero.
>> +
>> +endif # BOOTMETH_RAUC
>> +
>>   config BOOTMETH_SANDBOX
>>       def_bool y
>>       depends on SANDBOX
>> diff --git a/boot/Makefile b/boot/Makefile
>> index 
>> 71dafaefa76bca7896741dd896829575bd9e8f10..46f27e71856c598a866424cb93ee01444ea6a19a 100644
>> --- a/boot/Makefile
>> +++ b/boot/Makefile
>> @@ -32,6 +32,7 @@ obj-$(CONFIG_$(PHASE_)BOOTMETH_EXTLINUX_PXE) += 
>> bootmeth_pxe.o
>>   obj-$(CONFIG_$(PHASE_)BOOTMETH_EFILOADER) += bootmeth_efi.o
>>   obj-$(CONFIG_$(PHASE_)BOOTMETH_CROS) += bootm.o bootm_os.o 
>> bootmeth_cros.o
>>   obj-$(CONFIG_$(PHASE_)BOOTMETH_QFW) += bootmeth_qfw.o
>> +obj-$(CONFIG_$(PHASE_)BOOTMETH_RAUC) += bootmeth_rauc.o
>>   obj-$(CONFIG_$(PHASE_)BOOTMETH_SANDBOX) += bootmeth_sandbox.o
>>   obj-$(CONFIG_$(PHASE_)BOOTMETH_SCRIPT) += bootmeth_script.o
>>   obj-$(CONFIG_$(PHASE_)CEDIT) += cedit.o
>> diff --git a/boot/bootmeth_rauc.c b/boot/bootmeth_rauc.c
>> new file mode 100644
>> index 
>> 0000000000000000000000000000000000000000..4d48d643c0a727d5ebd5f3514c173f0dd824b7cd
>> --- /dev/null
>> +++ b/boot/bootmeth_rauc.c
>> @@ -0,0 +1,432 @@
>> +// SPDX-License-Identifier: GPL-2.0+
>> +/*
>> + * Bootmethod for distro boot with RAUC
>> + *
>> + * Copyright 2025 PHYTEC Messtechnik GmbH
>> + * Written by Martin Schwan <m.schwan at phytec.de>
>> + */
>> +
>> +#define LOG_CATEGORY UCLASS_BOOTSTD
>> +
>> +#include <blk.h>
>> +#include <bootflow.h>
>> +#include <bootmeth.h>
>> +#include <bootstd.h>
>> +#include <dm.h>
>> +#include <env.h>
>> +#include <fs.h>
>> +#include <malloc.h>
>> +#include <mapmem.h>
>> +#include <string.h>
>> +#include <asm/cache.h>
>> +
>> +/* Length of env var "BOOT_*_LEFT" */
>> +#define BOOT_LEFT_LEN    (5 + 32 + 5)
>> +
>> +static const char * const script_names[] = { "boot.scr", 
>> "boot.scr.uimg", NULL };
>> +
>> +/**
>> + * struct distro_rauc_slot - Slot information
>> + *
>> + * A slot describes the unit of a bootable system consisting of one 
>> or multiple
>> + * partitions. This usually includes a root filesystem, kernel and 
>> potentially other
>> + * files, like device trees and boot scripts for that particular 
>> distribution.
>> + *
>> + * @name    The slot name
>> + * @boot_part    The boot partition number on disk
>> + * @root_part    The root partition number on disk
>> + */
>> +struct distro_rauc_slot {
>> +    char *name;
>> +    int boot_part;
>> +    int root_part;
>> +};
>> +
>> +/**
>> + * struct distro_rauc_priv - Private data
>> + *
>> + * @slots    All slots of the device in default order
>> + * @boot_order    String of the current boot order containing the 
>> active slot names
>> + */
>> +struct distro_rauc_priv {
>> +    struct distro_rauc_slot **slots;
>> +};
>> +
>> +static struct distro_rauc_slot *get_slot(struct distro_rauc_priv *priv,
>> +                     const char *slot_name)
>> +{
>> +    int i;
>> +
>> +    for (i = 0; priv->slots[i]->name; i++) {
>> +        if (!strcmp(priv->slots[i]->name, slot_name))
>> +            return priv->slots[i];
>> +    }
>> +
>> +    return NULL;
>> +}
>> +
>> +static int distro_rauc_check(struct udevice *dev, struct 
>> bootflow_iter *iter)
>> +{
>> +    /*
>> +     * This distro only works on whole MMC devices, as multiple 
>> partitions
>> +     * are needed for an A/B system.
>> +     */
>> +    if (bootflow_iter_check_mmc(iter))
>> +        return log_msg_ret("mmc", -EOPNOTSUPP);
>> +    if (iter->part)
>> +        return log_msg_ret("part", -EOPNOTSUPP);
>> +
>> +    return 0;
>> +}
>> +
>> +static int distro_rauc_scan_boot_part(struct bootflow *bflow)
>> +{
>> +    struct blk_desc *desc;
>> +    struct distro_rauc_priv *priv;
>> +    char *boot_order;
>> +    const char **boot_order_list;
>> +    bool exists;
>> +    int ret;
>> +    int i;
>> +    int j;
>> +
>> +    desc = dev_get_uclass_plat(bflow->blk);
>> +
>> +    priv = bflow->bootmeth_priv;
>> +    if (!priv || !priv->slots)
>> +        return log_msg_ret("priv", -EINVAL);
>> +
>> +    boot_order = env_get("BOOT_ORDER");
>> +    boot_order_list = str_to_list(boot_order);
>> +    for (i = 0; boot_order_list[i]; i++) {
>> +        exists = false;
>> +        for (j = 0; script_names[j]; j++) {
>> +            const struct distro_rauc_slot *slot;
>> +
>> +            slot = get_slot(priv, boot_order_list[i]);
>> +            if (!slot)
>> +                return log_msg_ret("env", -ENOENT);
>> +            ret = fs_set_blk_dev_with_part(desc, slot->boot_part);
>> +            if (ret)
>> +                return log_msg_ret("blk", ret);
>> +            exists |= fs_exists(script_names[j]);
>> +        }
>> +        if (!exists)
>> +            return log_msg_ret("fs", -ENOENT);
>> +    }
>> +    str_free_list(boot_order_list);
>> +
>> +    return 0;
>> +}
>> +
>> +static int distro_rauc_read_bootflow(struct udevice *dev, struct 
>> bootflow *bflow)
>> +{
>> +    struct distro_rauc_priv *priv;
>> +    int ret;
>> +    char *slot;
>> +    int i;
>> +    char *partitions;
>> +    char *boot_order;
>> +    const char *default_boot_order;
>> +    const char **default_boot_order_list;
>> +    char *boot_order_copy;
>> +    char boot_left[BOOT_LEFT_LEN];
>> +    char *parts;
>> +
>> +    /* Get RAUC variables or set their default values */
>> +    boot_order = env_get("BOOT_ORDER");
>> +    if (!boot_order) {
>> +        log_debug("BOOT_ORDER did not exist yet, setting default 
>> value\n");
>> +        if (env_set("BOOT_ORDER", CONFIG_BOOTMETH_RAUC_BOOT_ORDER))
>> +            return log_msg_ret("env", -EPERM);
>> +        boot_order = CONFIG_BOOTMETH_RAUC_BOOT_ORDER;
>> +    }
>> +    default_boot_order = CONFIG_BOOTMETH_RAUC_BOOT_ORDER;
>> +    default_boot_order_list = str_to_list(default_boot_order);
>> +    for (i = 0; default_boot_order_list[i]; i++) {
>> +        sprintf(boot_left, "BOOT_%s_LEFT", default_boot_order_list[i]);
>> +        if (!env_get(boot_left)) {
>> +            log_debug("%s did not exist yet, setting default value\n",
>> +                  boot_left);
>> +            if (!env_set_ulong(boot_left, 
>> CONFIG_BOOTMETH_RAUC_DEFAULT_TRIES))
>> +                return log_msg_ret("env", -EPERM);
>> +        }
>> +    }
>> +    str_free_list(default_boot_order_list);
>> +
>> +    priv = calloc(1, sizeof(struct distro_rauc_priv));
>> +    if (!priv)
>> +        return log_msg_ret("buf", -ENOMEM);
>> +    priv->slots = calloc(1, sizeof(struct distro_rauc_slot));
>> +
>> +    /* Copy default boot_order, so we can leave the original 
>> unmodified */
>> +    boot_order_copy = strdup(default_boot_order);
>> +    partitions = strdup(CONFIG_BOOTMETH_RAUC_PARTITIONS);
>> +
>> +    for (i = 1;
>> +         (parts = strsep(&partitions, " ")) &&
>> +         (slot = strsep(&boot_order_copy, " "));
>> +         i++) {
>> +        struct distro_rauc_slot *s;
>> +
>> +        s = calloc(1, sizeof(struct distro_rauc_slot));
>> +        s->name = strdup(slot);
>> +        s->boot_part = simple_strtoul(strsep(&parts, ","), NULL, 10);
>> +        s->root_part = simple_strtoul(strsep(&parts, ","), NULL, 10);
>> +        priv->slots = realloc(priv->slots, (i + 1) *
>> +                      sizeof(struct distro_rauc_slot));
>> +        priv->slots[i - 1] = s;
>> +        priv->slots[i]->name = NULL;
>> +    }
>> +
>> +    bflow->bootmeth_priv = priv;
>> +
>> +    ret = distro_rauc_scan_boot_part(bflow);
>> +    if (ret < 0) {
>> +        for (i = 0; priv->slots[i]->name; i++) {
>> +            free(priv->slots[i]->name);
>> +            free(priv->slots[i]);
>> +        }
>> +        free(priv);
>> +        free(boot_order_copy);
>> +        bflow->bootmeth_priv = NULL;
>> +        return ret;
>> +    }
>> +
>> +    bflow->state = BOOTFLOWST_READY;
>> +
>> +    return 0;
>> +}
>> +
>> +static int distro_rauc_read_file(struct udevice *dev, struct bootflow 
>> *bflow,
>> +                 const char *file_path, ulong addr,
>> +                 enum bootflow_img_t type, ulong *sizep)
>> +{
>> +    /*
>> +     * Reading individual files is not supported since we only 
>> operate on
>> +     * whole MMC devices (because we require multiple partitions).
>> +     */
>> +    return log_msg_ret("Unsupported", -ENOSYS);
>> +}
>> +
>> +static int distro_rauc_load_boot_script(struct bootflow *bflow,
>> +                    const struct distro_rauc_slot *slot)
>> +{
>> +    struct blk_desc *desc;
>> +    struct distro_rauc_priv *priv;
>> +    struct udevice *bootstd;
>> +    const char *const *prefixes;
>> +    int ret;
>> +    int i;
>> +    int j;
>> +
>> +    ret = uclass_first_device_err(UCLASS_BOOTSTD, &bootstd);
>> +    if (ret)
>> +        return log_msg_ret("std", ret);
>> +    prefixes = bootstd_get_prefixes(bootstd);
>> +
>> +    desc = dev_get_uclass_plat(bflow->blk);
>> +    priv = bflow->bootmeth_priv;
>> +    if (!priv || !priv->slots)
>> +        return log_msg_ret("priv", -EINVAL);
>> +
>> +    bflow->part = slot->boot_part;
>> +    if (!bflow->part)
>> +        return log_msg_ret("part", -ENOENT);
>> +
>> +    ret = bootmeth_setup_fs(bflow, desc);
>> +    if (ret)
>> +        return log_msg_ret("set", ret);
>> +
>> +    for (i = 0; prefixes[i] && bflow->state != BOOTFLOWST_FILE; i++) {
>> +        for (j = 0; script_names[j] && bflow->state != 
>> BOOTFLOWST_FILE; j++) {
>> +            if (!bootmeth_try_file(bflow, desc, prefixes[i], 
>> script_names[j])) {
>> +                log_debug("Found file '%s%s' in %s.part_%x\n",
>> +                      prefixes[i], script_names[j],
>> +                      bflow->dev->name, bflow->part);
>> +                bflow->subdir = strdup(prefixes[i]);
>> +            }
>> +        }
>> +    }
>> +    if (bflow->state != BOOTFLOWST_FILE)
>> +        return log_msg_ret("file", -ENOENT);
>> +
>> +    ret = bootmeth_alloc_file(bflow, 0x10000, ARCH_DMA_MINALIGN,
>> +                  (enum bootflow_img_t)IH_TYPE_SCRIPT);
>> +    if (ret)
>> +        return log_msg_ret("read", ret);
>> +
>> +    return 0;
>> +}
>> +
>> +static int find_active_slot(char **slot_name, ulong *slot_tries)
>> +{
>> +    ulong tries;
>> +    char boot_left[BOOT_LEFT_LEN];
>> +    char *boot_order;
>> +    const char **boot_order_list;
>> +    bool slot_found = false;
>> +    int ret;
>> +    int i;
>> +
>> +    boot_order = env_get("BOOT_ORDER");
>> +    if (!boot_order)
>> +        return log_msg_ret("env", -ENOENT);
>> +    boot_order_list = str_to_list(boot_order);
>> +    for (i = 0; boot_order_list[i] && !slot_found; i++) {
>> +        sprintf(boot_left, "BOOT_%s_LEFT", boot_order_list[i]);
>> +        tries = env_get_ulong(boot_left, 10, ULONG_MAX);
>> +        if (tries == ULONG_MAX)
>> +            return log_msg_ret("env", -ENOENT);
>> +
>> +        if (tries) {
>> +            ret = env_set_ulong(boot_left, tries - 1);
>> +            if (ret)
>> +                return log_msg_ret("env", ret);
>> +            *slot_name = strdup(boot_order_list[i]);
>> +            *slot_tries = tries;
>> +            slot_found = true;
>> +        }
>> +    }
>> +    str_free_list(boot_order_list);
>> +
>> +    if (!slot_found) {
>> +        if (IS_ENABLED(CONFIG_BOOTMETH_RAUC_RESET_ALL_ZERO_TRIES)) {
>> +            log_warning("WARNING: No valid slot found\n");
>> +            log_info("INFO: Resetting boot order and all slot tries\n");
>> +            boot_order_list = 
>> str_to_list(CONFIG_BOOTMETH_RAUC_BOOT_ORDER);
>> +            for (i = 0; boot_order_list[i]; i++) {
>> +                sprintf(boot_left, "BOOT_%s_LEFT", boot_order_list[i]);
>> +                ret = env_set_ulong(boot_left, 
>> CONFIG_BOOTMETH_RAUC_DEFAULT_TRIES);
>> +                if (ret)
>> +                    return log_msg_ret("env", ret);
>> +            }
>> +            str_free_list(boot_order_list);
>> +            ret = env_save();
>> +            if (ret)
>> +                return log_msg_ret("env", ret);
>> +            do_reset(NULL, 0, 0, NULL);
>> +        }
>> +        log_err("ERROR: No valid slot found\n");
>> +        return -EINVAL;
>> +    }
>> +
>> +    return 0;
>> +}
>> +
>> +static int distro_rauc_boot(struct udevice *dev, struct bootflow *bflow)
>> +{
>> +    struct blk_desc *desc;
>> +    struct distro_rauc_priv *priv;
>> +    const struct distro_rauc_slot *slot;
>> +    char *boot_order;
>> +    const char **boot_order_list;
>> +    char *active_slot;
>> +    ulong active_slot_tries;
>> +    char raucargs[64];
>> +    char boot_left[BOOT_LEFT_LEN];
>> +    ulong addr;
>> +    int ret = 0;
>> +    int i;
>> +
>> +    desc = dev_get_uclass_plat(bflow->blk);
>> +    if (desc->uclass_id != UCLASS_MMC)
>> +        return log_msg_ret("blk", -EINVAL);
>> +    priv = bflow->bootmeth_priv;
>> +
>> +    /* Device info variables */
>> +    ret = env_set("devtype", blk_get_devtype(bflow->blk));
>> +    if (ret)
>> +        return log_msg_ret("env", ret);
>> +
>> +    ret = env_set_hex("devnum", desc->devnum);
>> +    if (ret)
>> +        return log_msg_ret("env", ret);
>> +
>> +    /* Find active, valid slot */
>> +    ret = find_active_slot(&active_slot, &active_slot_tries);
>> +    if (ret)
>> +        return log_msg_ret("env", ret);
>> +
>> +    /* Kernel command line arguments */
>> +    sprintf(raucargs, "rauc.slot=%s", active_slot);
>> +    ret = env_set("raucargs", raucargs);
>> +    if (ret)
>> +        return log_msg_ret("env", ret);
>> +
>> +    /* Active slot info */
>> +    slot = get_slot(priv, active_slot);
>> +    if (!slot)
>> +        return log_msg_ret("env", -ENOENT);
>> +    ret = env_set_hex("distro_bootpart", slot->boot_part);
>> +    if (ret)
>> +        return log_msg_ret("env", ret);
>> +    ret = env_set_hex("distro_rootpart", slot->root_part);
>> +    if (ret)
>> +        return log_msg_ret("env", ret);
>> +    ret = env_save();
>> +    if (ret)
>> +        return log_msg_ret("env", ret);
>> +
>> +    /* Load distro boot script */
>> +    ret = distro_rauc_load_boot_script(bflow, slot);
>> +    if (ret)
>> +        return log_msg_ret("load", ret);
>> +
>> +    log_info("INFO: Booting slot %s, %lu of %d tries left\n",
>> +         active_slot, active_slot_tries, 
>> CONFIG_BOOTMETH_RAUC_DEFAULT_TRIES);
>> +
>> +    log_debug("devtype: %s\n", env_get("devtype"));
>> +    log_debug("devnum: %s\n", env_get("devnum"));
>> +    log_debug("distro_bootpart: %s\n", env_get("distro_bootpart"));
>> +    log_debug("distro_rootpart: %s\n", env_get("distro_rootpart"));
>> +    log_debug("raucargs: %s\n", env_get("raucargs"));
>> +    boot_order = env_get("BOOT_ORDER");
>> +    if (!boot_order)
>> +        return log_msg_ret("env", -EPERM);
>> +    log_debug("BOOT_ORDER: %s\n", boot_order);
>> +    boot_order_list = str_to_list(boot_order);
>> +    for (i = 0; boot_order_list[i]; i++) {
>> +        sprintf(boot_left, "BOOT_%s_LEFT", boot_order_list[i]);
>> +        log_debug("%s: %s\n", boot_left, env_get(boot_left));
>> +    }
>> +    str_free_list(boot_order_list);
>> +
>> +    /* Run distro boot script */
>> +    addr = map_to_sysmem(bflow->buf);
>> +    ret = cmd_source_script(addr, NULL, NULL);
>> +    if (ret)
>> +        return log_msg_ret("boot", ret);
>> +
>> +    return 0;
>> +}
>> +
>> +static int distro_rauc_bootmeth_bind(struct udevice *dev)
>> +{
>> +    struct bootmeth_uc_plat *plat = dev_get_uclass_plat(dev);
>> +
>> +    plat->desc = "RAUC distro boot from MMC";
>> +    plat->flags = BOOTMETHF_GLOBAL;
>> +
>> +    return 0;
>> +}
>> +
>> +static struct bootmeth_ops distro_rauc_bootmeth_ops = {
>> +    .check        = distro_rauc_check,
>> +    .read_bootflow    = distro_rauc_read_bootflow,
>> +    .read_file    = distro_rauc_read_file,
>> +    .boot        = distro_rauc_boot,
>> +};
>> +
>> +static const struct udevice_id distro_rauc_bootmeth_ids[] = {
>> +    { .compatible = "u-boot,distro-rauc" },
>> +    { }
>> +};
>> +
>> +U_BOOT_DRIVER(bootmeth_rauc) = {
>> +    .name        = "bootmeth_rauc",
>> +    .id        = UCLASS_BOOTMETH,
>> +    .of_match    = distro_rauc_bootmeth_ids,
>> +    .ops        = &distro_rauc_bootmeth_ops,
>> +    .bind        = distro_rauc_bootmeth_bind,
>> +};
>>
> 



More information about the U-Boot mailing list