mirror of
https://source.denx.de/u-boot/u-boot.git
synced 2026-06-02 09:46:37 +03:00
bootstd: Add implementation for bootmeth rauc
Add a bootmeth driver which supports booting A/B system with RAUC as their update client. Signed-off-by: Martin Schwan <m.schwan@phytec.de> Tested-by: Wadim Egorov <w.egorov@phytec.de>
This commit is contained in:
51
boot/Kconfig
51
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
|
||||
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
|
||||
|
||||
@@ -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
|
||||
|
||||
432
boot/bootmeth_rauc.c
Normal file
432
boot/bootmeth_rauc.c
Normal file
@@ -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@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,
|
||||
};
|
||||
Reference in New Issue
Block a user