From mboxrd@z Thu Jan 1 00:00:00 1970 From: =?UTF-8?Q?Martin_Hundeb=c3=b8ll?= Date: Mon, 30 Jul 2018 22:45:22 +0200 Subject: [U-Boot] [RFC] cmd: add bootslot command to select/boot slot based on boot counts In-Reply-To: <89febb3a-429e-6066-0791-cd6559e31b41@prevas.dk> References: <20180713123413.4115-1-martin@geanix.com> <89febb3a-429e-6066-0791-cd6559e31b41@prevas.dk> Message-ID: <93b97bb7-cf31-80c5-e0c1-c29eb2db9c47@geanix.com> List-Id: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 8bit To: u-boot@lists.denx.de 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_" 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"): > 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 > 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" >> +); >>