
Hi,
On 2018-07-13 14:34, Martin Hundebøll wrote:
The existing bootcount feature is targeted systems with a primary, and a rescue boot setup, where the number of boot tries to the primary boot is tracked. If the number exceeds the limit, the alternative/rescue is booted.
This patch adds support for a more sophisticated setup, where more than two boot slots can exist, and the order of slots can be configured.
The 'bootcommand' command reads the configured slots (and their priority/order) from a configured environment variable ("bootslots" by default). For each conifgured slot, a remaining boot count is maintained in an evnironment variable ("bootcount_<slot>" by default). If the first boot slot has positive boot count, it is booted using the slot specific boot command ("bootcmd_<slot>" by default). Otherwise the next slot is checked.
An example environment when using the bootslot command with two slots ("a" and "b"):
With RAUC bootslot's is specified with uppercase letters, uppercase is not preserved. We end up with BOOT_b_LEFT=2... botocmd_* is with lowercase, just to make things easier.
/Sean
bootslots=a b bootcount_a=3 bootcount_b=3 bootcmd_a=setenv bootargs $bootargs root=/dev/mmcblk0p1; booti $loadaddr bootcmd_b=setenv bootargs $bootargs root=/dev/mmcblk0p2; booti $loadaddr
Once linux is booted, it resets the bootcount variable for the booted slot using "fw_setenv":
fw_setenv bootcount_a 3
When the non-booted slot is updated, the order is updated by setting the bootslots variable with "fw_setenv":
fw_setenv bootslots=b a
Signed-off-by: Martin Hundebøll martin@geanix.com
Tested-by: Sean Nyekjaer sean.nyekjaer@prevas.dk
cmd/Kconfig | 42 +++++++++ cmd/Makefile | 1 + cmd/bootslot.c | 225 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 268 insertions(+) create mode 100644 cmd/bootslot.c
diff --git a/cmd/Kconfig b/cmd/Kconfig index aec209006d..3919606e74 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -1277,6 +1277,48 @@ config CMD_BOOTCOUNT Enable the bootcount command, which allows interrogation and reset of the bootcounter.
+config CMD_BOOTSLOT
- bool "Enable support for multiple boot slots"
- help
Parses an ordered list of configured boot slot names (e.g. "a b")
and selects a corresponding boot command based on the current
boot counter for each slot.
+config CMD_BOOTSLOT_ENV_SLOTS
- string "Environment variable to read bootslots from"
- depends on CMD_BOOTSLOT
- default "bootslots"
- help
Configures the environment variable to read out when looking for a
list of available boot sloots.
+config CMD_BOOTSLOT_ENV_COUNT
- string "Environment variable format string to read/write slot boot count from/to"
- depends on CMD_BOOTSLOT
- default "bootcount_%s"
- help
Configures the prefix to use when reading the boot count for a
specific slot. The prefix is concatenated with the slot name, so
that the boot count for slot "a" is read and saved to "<prefix>a".
+config CMD_BOOTSLOT_ENV_CMD
- string "Environment variable format string to read slot boot command from"
- depends on CMD_BOOTSLOT
- default "bootcmd_%s"
- help
Configures the prefix to use when reading the boot command for
specific slot. The prefix is concatenated with the slot name, so
that the boot command for slot "a" is read from "<prefix>a".
+config CMD_BOOTSLOT_DEFAULT_COUNT
- int "Default boot count for each configured slot"
- depends on CMD_BOOTSLOT
- default 3
- help
The default number of times a slot is tried before proceeding to the
slot. The default is used when a slot has no count yet, or when it
is reset with the "bootslot reset" command.
- config CMD_BSP bool "Enable board-specific commands" help
diff --git a/cmd/Makefile b/cmd/Makefile index 323f1fd2c7..68c8e50c91 100644 --- a/cmd/Makefile +++ b/cmd/Makefile @@ -26,6 +26,7 @@ obj-$(CONFIG_CMD_BOOTCOUNT) += bootcount.o obj-$(CONFIG_CMD_BOOTEFI) += bootefi.o obj-$(CONFIG_CMD_BOOTMENU) += bootmenu.o obj-$(CONFIG_CMD_BOOTSTAGE) += bootstage.o +obj-$(CONFIG_CMD_BOOTSLOT) += bootslot.o obj-$(CONFIG_CMD_BOOTZ) += bootz.o obj-$(CONFIG_CMD_BOOTI) += booti.o obj-$(CONFIG_CMD_BTRFS) += btrfs.o diff --git a/cmd/bootslot.c b/cmd/bootslot.c new file mode 100644 index 0000000000..03897b1f60 --- /dev/null +++ b/cmd/bootslot.c @@ -0,0 +1,225 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- Copyright (c) 2018, Geanix, All rights reserved.
- */
+#include <common.h> +#include <environment.h> +#include <stdlib.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/compat.h> +#include <vsprintf.h>
+static char *bootslot_envname(const char *fmt, const char *slot) +{
- int len = strlen(fmt) + strlen(slot);
- char *envname = malloc(len + 1);
- sprintf(envname, fmt, slot);
- return envname;
+}
+static unsigned long bootslot_get_count(const char *slot) +{
- char *envname;
- unsigned long count;
- envname = bootslot_envname(CONFIG_CMD_BOOTSLOT_ENV_COUNT, slot);
- count = env_get_ulong(envname, 10, CONFIG_CMD_BOOTSLOT_DEFAULT_COUNT);
- free(envname);
- return count;
+}
+static void bootslot_set_count(const char *slot, unsigned long count) +{
- char *envname;
- envname = bootslot_envname(CONFIG_CMD_BOOTSLOT_ENV_COUNT, slot);
- env_set_ulong(envname, count);
- free(envname);
+}
+static const char *bootslot_get_cmd(const char *slot) +{
- char *envname;
- char *cmd;
- envname = bootslot_envname(CONFIG_CMD_BOOTSLOT_ENV_CMD, slot);
- cmd = env_get(envname);
- free(envname);
- return cmd;
+}
+static char *bootslot_get_slots(int argc, char * const argv[]) +{
- char *slots = NULL;
- int len = 1; /* make room for terminating \0 */
- int i;
- /* read boot slots from environment if no args are given, or
* duplicate the argument if a single argument is given */
- if (argc == 1)
return strdup(env_get(CONFIG_CMD_BOOTSLOT_ENV_SLOTS));
- else if (argc == 2)
return strdup(argv[1]);
- /* compute the string length of the list of slots */
- for (i = 1; i < argc; i++)
len += strlen(argv[i]) + 1; /* add room for space separator */
- /* allocate the string buffer and copy each slot argument to it */
- slots = kzalloc(len, 0);
- strcpy(slots, argv[1]);
- for (i = 2; i < argc; i++) {
strcat(slots, " ");
strcat(slots, argv[i]);
- }
- return slots;
+}
+static int do_bootslot_list(cmd_tbl_t *cmdtp, int flag, int argc,
char * const argv[])
+{
- char *mem = bootslot_get_slots(argc, argv);
- char *slots = mem;
- char *slot;
- if (slots == NULL)
return CMD_RET_USAGE;
- printf("slot\tcount\tbootcmd\n");
- while ((slot = strsep(&slots, " ")) != NULL) {
unsigned long count;
const char *bootcmd;
if (strlen(slot) == 0)
continue;
count = bootslot_get_count(slot);
bootcmd = bootslot_get_cmd(slot);
if (bootcmd)
printf("%s\t%lu\t%s\n", slot, count, bootcmd);
else
printf("%s\t%lu\t<not defined>\n", slot, count);
- }
- free(mem);
- return CMD_RET_SUCCESS;
+}
+static int do_bootslot_reset(cmd_tbl_t *cmdtp, int flag, int argc,
char * const argv[])
+{
- char *mem = bootslot_get_slots(argc, argv);
- char *slots = mem;
- char *slot;
- if (slots == NULL)
return CMD_RET_USAGE;
- while ((slot = strsep(&slots, " ")) != NULL) {
if (strlen(slot) == 0)
continue;
bootslot_set_count(slot, CONFIG_CMD_BOOTSLOT_DEFAULT_COUNT);
- }
- free(mem);
- return CMD_RET_SUCCESS;
+}
+static int do_bootslot_boot(cmd_tbl_t *cmdtp, int flag, int argc,
char * const argv[])
+{
- char *mem = bootslot_get_slots(argc, argv);
- char *slots = mem;
- char *slot;
- bool found_valid = false;
- if (slots == NULL)
return CMD_RET_USAGE;
- while ((slot = strsep(&slots, " ")) != NULL) {
const char *bootcmd;
unsigned long count;
if (strlen(slot) == 0)
continue;
count = bootslot_get_count(slot);
if (count == 0) {
printf("slot %s bootcount is zero; trying next...\n",
slot);
continue;
}
bootcmd = bootslot_get_cmd(slot);
if (bootcmd == NULL) {
printf("slot %s bootcmd not found; trying next...\n",
slot);
continue;
}
printf("slot %s has %lu boot tries remaining; booting...\n",
slot, count);
found_valid = true;
bootslot_set_count(slot, --count);
env_save();
run_command_list(bootcmd, -1, CMD_FLAG_ENV);
break;
- }
- free(mem);
- if (found_valid == false) {
printf("no valid bootslot found; resetting counters\n");
run_command("bootslot reset", 0);
return CMD_RET_FAILURE;
- }
- return CMD_RET_SUCCESS;
+}
+static cmd_tbl_t cmd_bootslot_sub[] = {
- U_BOOT_CMD_MKENT(boot, INT_MAX, 0, do_bootslot_boot, "", ""),
- U_BOOT_CMD_MKENT(list, INT_MAX, 1, do_bootslot_list, "", ""),
- U_BOOT_CMD_MKENT(reset, INT_MAX, 1, do_bootslot_reset, "", ""),
+};
+/*
- Process a bootslots sub-command
- */
+static int do_bootslot(cmd_tbl_t *cmdtp, int flag, int argc,
char * const argv[])
+{
- cmd_tbl_t *c;
- /* Strip off leading 'bootslot' command argument */
- argc--;
- argv++;
- c = find_cmd_tbl(argv[0], cmd_bootslot_sub,
ARRAY_SIZE(cmd_bootslot_sub));
- if (c)
return c->cmd(cmdtp, flag, argc, argv);
- else
return CMD_RET_USAGE;
+}
+U_BOOT_CMD(bootslot, INT_MAX, 1, do_bootslot,
- "Bootslot command",
- " - select and boot slot based on counters\n"
- "boot [<slot>] - Boot the passed or first valid slot in $bootslots\n"
- "list [<slot>] - List remaining boot tries for passed or all slots in $bootslots\n"
- "reset [<slot>] - Reset remaining boot tries for all or passed slot\n"
+);