All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Martin Hundebøll" <martin@geanix.com>
To: u-boot@lists.denx.de
Subject: [U-Boot] [RFC] cmd: add bootslot command to select/boot slot based on boot counts
Date: Mon, 30 Jul 2018 22:45:22 +0200	[thread overview]
Message-ID: <93b97bb7-cf31-80c5-e0c1-c29eb2db9c47@geanix.com> (raw)
In-Reply-To: <89febb3a-429e-6066-0791-cd6559e31b41@prevas.dk>

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

  reply	other threads:[~2018-07-30 20:45 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
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 [this message]
2018-08-24 11:13     ` Sean Nyekjær
2019-12-11  8:16 ` Sean Nyekjaer
2020-02-16 15:33 ` Wolfgang Denk

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=93b97bb7-cf31-80c5-e0c1-c29eb2db9c47@geanix.com \
    --to=martin@geanix.com \
    --cc=u-boot@lists.denx.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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.