[U-Boot] [RFC] cmd: add bootslot command to select/boot slot based on boot counts
Martin Hundebøll
martin at geanix.com
Fri Jul 13 12:34:13 UTC 2018
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"):
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 at geanix.com>
---
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"
+);
--
2.18.0
More information about the U-Boot
mailing list