From patchwork Fri Jul 13 12:34:13 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Martin_Hundeb=C3=B8ll?= X-Patchwork-Id: 943549 X-Patchwork-Delegate: trini@ti.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.denx.de (client-ip=81.169.180.215; helo=lists.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=geanix.com Received: from lists.denx.de (dione.denx.de [81.169.180.215]) by ozlabs.org (Postfix) with ESMTP id 41RtGV1jKkz9ryt for ; Fri, 13 Jul 2018 23:01:06 +1000 (AEST) Received: by lists.denx.de (Postfix, from userid 105) id D94A9C22068; Fri, 13 Jul 2018 13:01:03 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on lists.denx.de X-Spam-Level: X-Spam-Status: No, score=0.0 required=5.0 tests=none autolearn=unavailable autolearn_force=no version=3.4.0 Received: from lists.denx.de (localhost [IPv6:::1]) by lists.denx.de (Postfix) with ESMTP id CEF9FC22022; Fri, 13 Jul 2018 13:00:59 +0000 (UTC) Received: by lists.denx.de (Postfix, from userid 105) id E5701C22022; Fri, 13 Jul 2018 12:34:20 +0000 (UTC) Received: from mail.hundeboll.net (mail.hundeboll.net [88.198.92.70]) by lists.denx.de (Postfix) with ESMTP id 6461DC22012 for ; Fri, 13 Jul 2018 12:34:16 +0000 (UTC) Received: from hundeboll.local (unknown [85.184.147.232]) by mail.hundeboll.net (Postfix) with ESMTPSA id CBCE21C098A; Fri, 13 Jul 2018 14:34:15 +0200 (CEST) From: =?utf-8?q?Martin_Hundeb=C3=B8ll?= To: u-boot@lists.denx.de Date: Fri, 13 Jul 2018 14:34:13 +0200 Message-Id: <20180713123413.4115-1-martin@geanix.com> X-Mailer: git-send-email 2.18.0 MIME-Version: 1.0 X-Mailman-Approved-At: Fri, 13 Jul 2018 13:00:59 +0000 Subject: [U-Boot] [RFC] cmd: add bootslot command to select/boot slot based on boot counts X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.18 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "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_" by default). If the first boot slot has positive boot count, it is booted using the slot specific boot command ("bootcmd_" 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 Tested-by: Sean Nyekjaer --- 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 "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 "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 +#include +#include +#include +#include +#include +#include + +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\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 [] - Boot the passed or first valid slot in $bootslots\n" + "list [] - List remaining boot tries for passed or all slots in $bootslots\n" + "reset [] - Reset remaining boot tries for all or passed slot\n" +);