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

Simon Glass sjg at chromium.org
Mon Apr 14 13:32:07 CEST 2025


Hi Martin,

On Wed, 29 Jan 2025 at 07:25, Martin Schwan <m.schwan at phytec.de> 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         |  11 ++
>  boot/Makefile        |   1 +
>  boot/bootmeth_rauc.c | 408 +++++++++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 420 insertions(+)

This looks fine to me. Just some nits below.

But this bootmeth does need documentation in doc/develop/bootstd/ and
also a test, e.g. in test/boot/

>
> diff --git a/boot/Kconfig b/boot/Kconfig
> index 20935a269c60dad53ce11ac7e58247dc23cf617e..966573d8eb7675d36f299c8c2fa6509bac147cae 100644
> --- a/boot/Kconfig
> +++ b/boot/Kconfig
> @@ -782,6 +782,17 @@ 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
> +       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.
> +
>  config BOOTMETH_SANDBOX
>         def_bool y
>         depends on SANDBOX
> diff --git a/boot/Makefile b/boot/Makefile
> index c2753de8163cf59b1abdc01149a856e2847960e6..427898449a8727747137ab1c7c571c473148c8c3 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..fcb7e8df4556710b160349f1dcc54cdda9b59c9d
> --- /dev/null
> +++ b/boot/bootmeth_rauc.c
> @@ -0,0 +1,408 @@
> +// 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
> +#define LOG_DEBUG
> +
> +#include <asm/cache.h>

That should go last.

> +#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>
> +
> +static const char * const script_names[] = { "boot.scr.uimg", "boot.scr", NULL };
> +static const char * const rauc_files[] = { "/usr/bin/rauc", "/etc/rauc/system.conf", NULL };
> +
> +/**
> + * struct distro_rauc_slot - Slot information
> + *
> + * @name       The slot name
> + * @left       The number of boot tries left
> + * @boot_part  The boot partition number on disk
> + * @root_part  The boot partition number on disk
> + */
> +struct distro_rauc_slot {
> +       char *name;
> +       ulong left;
> +       int boot_part;
> +       int root_part;
> +};
> +
> +/**
> + * struct distro_rauc_priv - Private data
> + *
> + * @slots      All slots of the device in default order
> + * @boot_order The current boot order string containing the active slot names
> + */
> +struct distro_rauc_priv {
> +       struct distro_rauc_slot *slots;
> +       char *boot_order;
> +};
> +
> +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 != 0)

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;
> +       int exists;
> +       int i;
> +       int j;
> +       int ret;
> +
> +       desc = dev_get_uclass_plat(bflow->blk);
> +       if (!desc)
> +               return log_msg_ret("desc", -ENOMEM);

That can't happen as the driver would never be bound in this case,
please remove.

> +
> +       priv = bflow->bootmeth_priv;
> +       if (!priv || !priv->slots || !priv->boot_order)
> +               return log_msg_ret("priv", -EINVAL);
> +
> +       for (i = 0; priv->slots[i].name != NULL; i++) {
> +               exists = 0;
> +               for (j = 0; script_names[j] != NULL; j++) {
> +                       ret = fs_set_blk_dev_with_part(desc, priv->slots[i].boot_part);
> +                       if (ret)
> +                               return log_msg_ret("blk", ret);
> +                       exists |= fs_exists(script_names[j]);
> +               }
> +               if (!exists)
> +                       return log_msg_ret("fs", -ENOENT);
> +       }
> +
> +       return 0;
> +}
> +
> +static int distro_rauc_scan_root_part(struct bootflow *bflow)
> +{
> +       struct blk_desc *desc;
> +       struct distro_rauc_priv *priv;
> +       int exists;
> +       int i;
> +       int j;
> +       int ret;
> +
> +       desc = dev_get_uclass_plat(bflow->blk);
> +       if (!desc)
> +               return log_msg_ret("desc", -ENOMEM);
> +
> +       priv = bflow->bootmeth_priv;
> +       if (!priv || !priv->slots || !priv->boot_order)
> +               return log_msg_ret("priv", -EINVAL);
> +
> +       for (i = 0; priv->slots[i].name != NULL; i++) {
> +               exists = 0;

bool exists = false; ?

> +               for (j = 0; rauc_files[j] != NULL; j++) {
> +                       ret = fs_set_blk_dev_with_part(desc,
> +                                       priv->slots[i].root_part);
> +                       if (ret)
> +                               return log_msg_ret("set", ret);
> +                       if (!fs_exists(rauc_files[j]))
> +                               return log_msg_ret("fs", -ENOENT);
> +               }
> +       }
> +
> +       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 *boot_order;
> +       char *partitions;
> +       const char **partitions_list;
> +       char *slots;
> +       const char **slots_list;
> +       char boot_left[32];
> +       char *parts;
> +       ulong default_tries;
> +
> +       bflow->state = BOOTFLOWST_MEDIA;
> +
> +       priv = malloc(sizeof(struct distro_rauc_priv));
> +       if (!priv)
> +               return log_msg_ret("buf", -ENOMEM);
> +

Please run checkpatch or patman to check this patch.

> +
> +       partitions = env_get("rauc_partitions");
> +       if (!partitions)
> +               return log_msg_ret("env", -ENOENT);
> +       partitions_list = str_to_list(partitions);
> +
> +       slots = env_get("rauc_slots");
> +       if (!slots)
> +               return log_msg_ret("env", -ENOENT);
> +       slots_list = str_to_list(slots);
> +
> +       boot_order = env_get("BOOT_ORDER");

Environment variables should be in lower case.

> +       if (!boot_order) {
> +               if (env_set("BOOT_ORDER", slots))
> +                       return log_msg_ret("env", -EPERM);
> +       }
> +       priv->boot_order = strdup(boot_order);
> +
> +       default_tries = env_get_ulong("rauc_slot_default_tries", 10, 3);
> +       for (i = 0; slots_list[i] != NULL; i++) {
> +               sprintf(boot_left, "BOOT_%s_LEFT", slots_list[i]);

same here

> +               if (!env_get(boot_left))
> +                       env_set_ulong(boot_left, default_tries);

check error

> +       }
> +
> +       for (i = 0; slots_list[i] != NULL && partitions_list[i] != NULL; i++);
> +       priv->slots = malloc((i + 1) * sizeof(struct distro_rauc_slot));

calloc?

> +       if (!priv->slots)
> +               return log_msg_ret("priv", -ENOMEM);
> +       for (i = 0; slots_list[i] != NULL && partitions_list[i] != NULL; i++) {
> +               priv->slots[i].name = strdup(slots_list[i]);
> +               sprintf(boot_left, "BOOT_%s_LEFT", slot);
> +               priv->slots[i].left = env_get_ulong(boot_left, 10, default_tries);
> +               parts = strdup(partitions_list[i]);
> +               priv->slots[i].boot_part = simple_strtoul(strsep(&parts, ","), NULL, 10);
> +               priv->slots[i].root_part = simple_strtoul(strsep(&parts, ","), NULL, 10);
> +               free(parts);
> +               parts = NULL;
> +       }
> +       priv->slots[i].name = NULL;
> +       priv->slots[i].left = 0;
> +       priv->slots[i].boot_part = 0;
> +       priv->slots[i].root_part = 0;
> +
> +       bflow->bootmeth_priv = priv;
> +       bflow->state = BOOTFLOWST_FS;
> +
> +       ret = distro_rauc_scan_boot_part(bflow);
> +       if (ret < 0)
> +               goto free_priv;
> +       ret = distro_rauc_scan_root_part(bflow);
> +       if (ret < 0)
> +               goto free_priv;
> +
> +       bflow->state = BOOTFLOWST_READY;
> +
> +       return 0;
> +
> +free_priv:
> +       for (i = 0; priv->slots[i].name != NULL; i++)
> +               free(priv->slots[i].name);
> +       free(priv->slots);
> +       free(priv);
> +       bflow->bootmeth_priv = NULL;

blank line here

> +       return ret;
> +}
> +
> +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 char *slot)

please as a comment, as I'm not sure what a slot is

> +{
> +       struct blk_desc *desc;
> +       struct distro_rauc_priv *priv;
> +       const char *prefix = "/";
> +       int ret;
> +       int i;
> +
> +       if (!slot)
> +               return log_msg_ret("slot", -EINVAL);
> +
> +       desc = dev_get_uclass_plat(bflow->blk);
> +       priv = bflow->bootmeth_priv;
> +
> +       bflow->part = 0;

Isn't it already 0 ?

> +       for (i = 0; priv->slots[i].name != NULL; i++) {
> +               if (strcmp(priv->slots[i].name, slot) == 0) {
> +                       bflow->part = priv->slots[i].boot_part;
> +                       break;
> +               }
> +       }
> +       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; script_names[i] != NULL; i++) {

Drop the '!= NULL' here and above

> +               if (!bootmeth_try_file(bflow, desc, prefix, script_names[i]))
> +                       break;
> +       }
> +       if (bflow->state != BOOTFLOWST_FILE)
> +               return log_msg_ret("file", -ENOENT);
> +
> +       bflow->subdir = strdup(prefix);
> +
> +       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 decrement_slot_tries(const char *slot)
> +{
> +       ulong tries;
> +       char boot_left[32];
> +
> +       sprintf(boot_left, "BOOT_%s_LEFT", slot);

lower case

> +       tries = env_get_ulong(boot_left, 10, ULONG_MAX);
> +       if (tries == ULONG_MAX)
> +               return log_msg_ret("env", -ENOENT);
> +       if (tries <= 0)
> +               return 0;
> +
> +       return env_set_ulong(boot_left, tries - 1);
> +}
> +
> +static int distro_rauc_boot(struct udevice *dev, struct bootflow *bflow)
> +{
> +       struct blk_desc *desc;
> +       struct distro_rauc_priv *priv;
> +       char *boot_order;
> +       const char **boot_order_list;
> +       const char *active_slot;
> +       char raucargs[32];
> +       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;
> +
> +       /* RAUC variables */
> +       boot_order = env_get("BOOT_ORDER");
> +       if (!boot_order)
> +               return log_msg_ret("env", -ENOENT);
> +       boot_order_list = str_to_list(boot_order);
> +
> +       /* 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);
> +
> +       /* Kernel command line arguments */
> +       active_slot = strdup(boot_order_list[0]);
> +       if (!active_slot)
> +               return log_msg_ret("env", -ENOENT);
> +       sprintf(raucargs, "rauc.slot=%s", active_slot);
> +       ret = env_set("raucargs", raucargs);
> +       if (ret)
> +               return log_msg_ret("env", ret);
> +
> +       /* TODO: If active_slot is in boot_order, but has 0 tries left, remove

/*
 * TODO: ...

> +        * it from boot_order. Then, set active_slot to other, if available.
> +        */
> +
> +       /* Partition info variables */
> +       for (i = 0; priv->slots[i].name != NULL; i++) {
> +               if (strcmp(priv->slots[i].name, active_slot) == 0) {
> +                       ret = env_set_hex("mmcroot", priv->slots[i].root_part);
> +                       if (ret)
> +                               return log_msg_ret("env", ret);
> +                       break;
> +               }
> +       }
> +
> +       /* Load distro boot script */
> +       ret = distro_rauc_load_boot_script(bflow, active_slot);
> +       if (ret)
> +               return log_msg_ret("load", ret);
> +       ret = env_set_hex("distro_bootpart", bflow->part);
> +       if (ret)
> +               return log_msg_ret("env", ret);
> +
> +       ret = decrement_slot_tries(active_slot);
> +       if (ret)
> +               return log_msg_ret("env", ret);
> +       ret = env_save();
> +       if (ret)
> +               return log_msg_ret("env", ret);
> +
> +       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("mmcroot: %s\n", env_get("mmcroot"));
> +       log_debug("raucargs: %s\n", env_get("raucargs"));
> +       log_debug("rauc_slots: %s\n", env_get("rauc_slots"));
> +       log_debug("rauc_partitions: %s\n", env_get("rauc_partitions"));
> +       log_debug("rauc_slot_default_tries: %s\n", env_get("rauc_slot_default_tries"));
> +       log_debug("BOOT_ORDER: %s\n", env_get("BOOT_ORDER"));
> +       for (i = 0; priv->slots[i].name != NULL; i++) {
> +               log_debug("BOOT_%s_LEFT: %ld\n", priv->slots[i].name,
> +                         priv->slots[i].left);
> +       }
> +
> +       /* 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_ANY_PART;
> +
> +       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,
> +};
>
> --
> 2.48.1
>

Regards,
SImon


More information about the U-Boot mailing list