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 <mar...@geanix.com>
Tested-by: Sean Nyekjaer <sean.nyekj...@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"
+);

_______________________________________________
U-Boot mailing list
U-Boot@lists.denx.de
https://lists.denx.de/listinfo/u-boot

Reply via email to