All of lore.kernel.org
 help / color / mirror / Atom feed
* [U-Boot] [RFC] cmd: add bootslot command to select/boot slot based on boot counts
@ 2018-07-13 12:34 Martin Hundebøll
  2018-07-16  6:05 ` Sean Nyekjær
                   ` (2 more replies)
  0 siblings, 3 replies; 6+ messages in thread
From: Martin Hundebøll @ 2018-07-13 12:34 UTC (permalink / raw)
  To: u-boot

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@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

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [U-Boot] [RFC] cmd: add bootslot command to select/boot slot based on boot counts
  2018-07-13 12:34 [U-Boot] [RFC] cmd: add bootslot command to select/boot slot based on boot counts Martin Hundebøll
@ 2018-07-16  6:05 ` Sean Nyekjær
  2018-07-30 20:45   ` Martin Hundebøll
  2019-12-11  8:16 ` Sean Nyekjaer
  2020-02-16 15:33 ` Wolfgang Denk
  2 siblings, 1 reply; 6+ messages in thread
From: Sean Nyekjær @ 2018-07-16  6:05 UTC (permalink / raw)
  To: u-boot

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"
> +);
> 

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [U-Boot] [RFC] cmd: add bootslot command to select/boot slot based on boot counts
  2018-07-16  6:05 ` Sean Nyekjær
@ 2018-07-30 20:45   ` Martin Hundebøll
  2018-08-24 11:13     ` Sean Nyekjær
  0 siblings, 1 reply; 6+ messages in thread
From: Martin Hundebøll @ 2018-07-30 20:45 UTC (permalink / raw)
  To: u-boot

Hi,

On 2018-07-16 08:05, Sean Nyekjær wrote:
> 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.

I cannot reproduce the lowercase issue. Can you send me your (def)config 
and environment?

% grep BOOTSLOT .config
CONFIG_CMD_BOOTSLOT=y
CONFIG_CMD_BOOTSLOT_ENV_SLOTS="BOOTORDER"
CONFIG_CMD_BOOTSLOT_ENV_COUNT="BOOT_%s_LEFT"
CONFIG_CMD_BOOTSLOT_ENV_CMD="BOOTCMD_%s"
CONFIG_CMD_BOOTSLOT_DEFAULT_COUNT=3

% grep -i boot board/raspberrypi/rpi/env.txt
BOOTORDER=A B
bootargs_all=console=ttyS0,115200n8 rootfstype=squashfs rootwait
bootargs_a=root=/dev/mmcblk0p2 bootslot=A
bootargs_b=root=/dev/mmcblk0p3 bootslot=B
set_bootargs_a=setenv bootargs $bootargs_all $bootargs_a
set_bootargs_b=setenv bootargs $bootargs_all $bootargs_b
bootcmd_common=run load_kernel; booti $loadaddr - $fdtcontroladdr
BOOTCMD_A=run set_bootargs_a bootcmd_common
BOOTCMD_B=run set_bootargs_b bootcmd_common
bootcmd=bootslot boot

// Martin

> /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"
>> +);
>>

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [U-Boot] [RFC] cmd: add bootslot command to select/boot slot based on boot counts
  2018-07-30 20:45   ` Martin Hundebøll
@ 2018-08-24 11:13     ` Sean Nyekjær
  0 siblings, 0 replies; 6+ messages in thread
From: Sean Nyekjær @ 2018-08-24 11:13 UTC (permalink / raw)
  To: u-boot

>> 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.
> 
> I cannot reproduce the lowercase issue. Can you send me your (def)config 
> and environment?
> 
> % grep BOOTSLOT .config
> CONFIG_CMD_BOOTSLOT=y
> CONFIG_CMD_BOOTSLOT_ENV_SLOTS="BOOTORDER"
> CONFIG_CMD_BOOTSLOT_ENV_COUNT="BOOT_%s_LEFT"
> CONFIG_CMD_BOOTSLOT_ENV_CMD="BOOTCMD_%s"
> CONFIG_CMD_BOOTSLOT_DEFAULT_COUNT=3
> 
> % grep -i boot board/raspberrypi/rpi/env.txt
> BOOTORDER=A B
> bootargs_all=console=ttyS0,115200n8 rootfstype=squashfs rootwait
> bootargs_a=root=/dev/mmcblk0p2 bootslot=A
> bootargs_b=root=/dev/mmcblk0p3 bootslot=B
> set_bootargs_a=setenv bootargs $bootargs_all $bootargs_a
> set_bootargs_b=setenv bootargs $bootargs_all $bootargs_b
> bootcmd_common=run load_kernel; booti $loadaddr - $fdtcontroladdr
> BOOTCMD_A=run set_bootargs_a bootcmd_common
> BOOTCMD_B=run set_bootargs_b bootcmd_common
> bootcmd=bootslot boot

Hi,

I'm not able to reproduce the lowercase issue, I must have got the 
config wrong :-)

Please submit a [PATCH] for this as it could be very useful.

/Sean

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [U-Boot] [RFC] cmd: add bootslot command to select/boot slot based on boot counts
  2018-07-13 12:34 [U-Boot] [RFC] cmd: add bootslot command to select/boot slot based on boot counts Martin Hundebøll
  2018-07-16  6:05 ` Sean Nyekjær
@ 2019-12-11  8:16 ` Sean Nyekjaer
  2020-02-16 15:33 ` Wolfgang Denk
  2 siblings, 0 replies; 6+ messages in thread
From: Sean Nyekjaer @ 2019-12-11  8:16 UTC (permalink / raw)
  To: u-boot



On 13/07/2018 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"):
> 
> 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>

We have used this for the past 1.5 years.
Will you post this as a patch?

/Sean

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [U-Boot] [RFC] cmd: add bootslot command to select/boot slot based on boot counts
  2018-07-13 12:34 [U-Boot] [RFC] cmd: add bootslot command to select/boot slot based on boot counts Martin Hundebøll
  2018-07-16  6:05 ` Sean Nyekjær
  2019-12-11  8:16 ` Sean Nyekjaer
@ 2020-02-16 15:33 ` Wolfgang Denk
  2 siblings, 0 replies; 6+ messages in thread
From: Wolfgang Denk @ 2020-02-16 15:33 UTC (permalink / raw)
  To: u-boot

Dear Martin,

In message <20180713123413.4115-1-martin@geanix.com> you 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 is not a correct summary of the current implementation.  If
the boot count exceeds the limit, some _alternative_boot_command_ is
executed.

The trivial case is to boot another image / partion or such.

Butt nothing prevents you to run any more sophisticated scripts
here.

> 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.

This can easily implemented with a few lines of shell code as well.

>  3 files changed, 268 insertions(+)

I think it makes no sense to add a lot of code that can be
implemented as a few lines of shell scripts as well, especially as
this code is neither time critical nor has the potential of becoming
widely used.

NAKed-by: Wolfgang Denk <wd@denx.de>


Best regards,

Wolfgang Denk

-- 
DENX Software Engineering GmbH,      Managing Director: Wolfgang Denk
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Phone: (+49)-8142-66989-10 Fax: (+49)-8142-66989-80 Email: wd at denx.de
There are two ways of constructing a software design. One way  is  to
make  it  so  simple that there are obviously no deficiencies and the
other is to make it so complicated that there are  no  obvious  defi-
ciencies.                             - Charles Anthony Richard Hoare

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2020-02-16 15:33 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-07-13 12:34 [U-Boot] [RFC] cmd: add bootslot command to select/boot slot based on boot counts Martin Hundebøll
2018-07-16  6:05 ` Sean Nyekjær
2018-07-30 20:45   ` Martin Hundebøll
2018-08-24 11:13     ` Sean Nyekjær
2019-12-11  8:16 ` Sean Nyekjaer
2020-02-16 15:33 ` Wolfgang Denk

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.