bootstd: Add a bootflow command
Add a 'bootflow' command to handle listing and selection of bootflow. Signed-off-by: Simon Glass <sjg@chromium.org>
This commit is contained in:
parent
5d053cca38
commit
2d653f686b
18
cmd/Kconfig
18
cmd/Kconfig
|
|
@ -221,6 +221,24 @@ config CMD_BOOTDEV
|
|||
|
||||
This command is not necessary for bootstd to work.
|
||||
|
||||
config CMD_BOOTFLOW
|
||||
bool "bootflow"
|
||||
depends on BOOTSTD
|
||||
default y
|
||||
help
|
||||
Support scanning for bootflows available with the bootdevs. The
|
||||
bootflows can optionally be booted.
|
||||
|
||||
config CMD_BOOTFLOW_FULL
|
||||
bool "bootflow - extract subcommands"
|
||||
depends on BOOTSTD_FULL
|
||||
default y if BOOTSTD_FULL
|
||||
help
|
||||
Add the ability to list the available bootflows, select one and obtain
|
||||
information about it.
|
||||
|
||||
This command is not necessary for bootstd to work.
|
||||
|
||||
config BOOTM_EFI
|
||||
bool "Support booting UEFI FIT images"
|
||||
depends on CMD_BOOTEFI && CMD_BOOTM && FIT
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ obj-$(CONFIG_CMD_ADC) += adc.o
|
|||
obj-$(CONFIG_CMD_ARMFLASH) += armflash.o
|
||||
obj-$(CONFIG_HAVE_BLOCK_DEVICE) += blk_common.o
|
||||
obj-$(CONFIG_CMD_BOOTDEV) += bootdev.o
|
||||
obj-$(CONFIG_CMD_BOOTFLOW) += bootflow.o
|
||||
obj-$(CONFIG_CMD_SOURCE) += source.o
|
||||
obj-$(CONFIG_CMD_BCB) += bcb.o
|
||||
obj-$(CONFIG_CMD_BDI) += bdinfo.o
|
||||
|
|
|
|||
|
|
@ -0,0 +1,404 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* 'bootflow' command
|
||||
*
|
||||
* Copyright 2021 Google LLC
|
||||
* Written by Simon Glass <sjg@chromium.org>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <bootdev.h>
|
||||
#include <bootflow.h>
|
||||
#include <bootstd.h>
|
||||
#include <command.h>
|
||||
#include <console.h>
|
||||
#include <dm.h>
|
||||
#include <mapmem.h>
|
||||
|
||||
/**
|
||||
* report_bootflow_err() - Report where a bootflow failed
|
||||
*
|
||||
* When a bootflow does not make it to the 'loaded' state, something went wrong.
|
||||
* Print a helpful message if there is an error
|
||||
*
|
||||
* @bflow: Bootflow to process
|
||||
* @err: Error code (0 if none)
|
||||
*/
|
||||
static void report_bootflow_err(struct bootflow *bflow, int err)
|
||||
{
|
||||
if (!err)
|
||||
return;
|
||||
|
||||
/* Indent out to 'Method' */
|
||||
printf(" ** ");
|
||||
|
||||
switch (bflow->state) {
|
||||
case BOOTFLOWST_BASE:
|
||||
printf("No media/partition found");
|
||||
break;
|
||||
case BOOTFLOWST_MEDIA:
|
||||
printf("No partition found");
|
||||
break;
|
||||
case BOOTFLOWST_PART:
|
||||
printf("No filesystem found");
|
||||
break;
|
||||
case BOOTFLOWST_FS:
|
||||
printf("File not found");
|
||||
break;
|
||||
case BOOTFLOWST_FILE:
|
||||
printf("File cannot be loaded");
|
||||
break;
|
||||
case BOOTFLOWST_READY:
|
||||
printf("Ready");
|
||||
break;
|
||||
case BOOTFLOWST_COUNT:
|
||||
break;
|
||||
}
|
||||
|
||||
printf(", err=%d\n", err);
|
||||
}
|
||||
|
||||
/**
|
||||
* show_bootflow() - Show the status of a bootflow
|
||||
*
|
||||
* @seq: Bootflow index
|
||||
* @bflow: Bootflow to show
|
||||
* @errors: True to show the error received, if any
|
||||
*/
|
||||
static void show_bootflow(int index, struct bootflow *bflow, bool errors)
|
||||
{
|
||||
printf("%3x %-11s %-6s %-9.9s %4x %-25.25s %s\n", index,
|
||||
bflow->method->name, bootflow_state_get_name(bflow->state),
|
||||
dev_get_uclass_name(dev_get_parent(bflow->dev)), bflow->part,
|
||||
bflow->name, bflow->fname);
|
||||
if (errors)
|
||||
report_bootflow_err(bflow, bflow->err);
|
||||
}
|
||||
|
||||
static void show_header(void)
|
||||
{
|
||||
printf("Seq Method State Uclass Part Name Filename\n");
|
||||
printf("--- ----------- ------ -------- ---- ------------------------ ----------------\n");
|
||||
}
|
||||
|
||||
static void show_footer(int count, int num_valid)
|
||||
{
|
||||
printf("--- ----------- ------ -------- ---- ------------------------ ----------------\n");
|
||||
printf("(%d bootflow%s, %d valid)\n", count, count != 1 ? "s" : "",
|
||||
num_valid);
|
||||
}
|
||||
|
||||
static int do_bootflow_scan(struct cmd_tbl *cmdtp, int flag, int argc,
|
||||
char *const argv[])
|
||||
{
|
||||
struct bootstd_priv *std;
|
||||
struct bootflow_iter iter;
|
||||
struct udevice *dev;
|
||||
struct bootflow bflow;
|
||||
bool all = false, boot = false, errors = false, list = false;
|
||||
int num_valid = 0;
|
||||
bool has_args;
|
||||
int ret, i;
|
||||
int flags;
|
||||
|
||||
ret = bootstd_get_priv(&std);
|
||||
if (ret)
|
||||
return CMD_RET_FAILURE;
|
||||
dev = std->cur_bootdev;
|
||||
|
||||
has_args = argc > 1 && *argv[1] == '-';
|
||||
if (IS_ENABLED(CONFIG_CMD_BOOTFLOW_FULL)) {
|
||||
if (has_args) {
|
||||
all = strchr(argv[1], 'a');
|
||||
boot = strchr(argv[1], 'b');
|
||||
errors = strchr(argv[1], 'e');
|
||||
list = strchr(argv[1], 'l');
|
||||
argc--;
|
||||
argv++;
|
||||
}
|
||||
if (argc > 1) {
|
||||
const char *label = argv[1];
|
||||
|
||||
if (bootdev_find_by_any(label, &dev))
|
||||
return CMD_RET_FAILURE;
|
||||
}
|
||||
} else {
|
||||
if (has_args) {
|
||||
printf("Flags not supported: enable CONFIG_BOOTFLOW_FULL\n");
|
||||
return CMD_RET_USAGE;
|
||||
}
|
||||
boot = true;
|
||||
}
|
||||
|
||||
std->cur_bootflow = NULL;
|
||||
|
||||
flags = 0;
|
||||
if (list)
|
||||
flags |= BOOTFLOWF_SHOW;
|
||||
if (all)
|
||||
flags |= BOOTFLOWF_ALL;
|
||||
|
||||
/*
|
||||
* If we have a device, just scan for bootflows attached to that device
|
||||
*/
|
||||
if (IS_ENABLED(CONFIG_CMD_BOOTFLOW_FULL) && dev) {
|
||||
if (list) {
|
||||
printf("Scanning for bootflows in bootdev '%s'\n",
|
||||
dev->name);
|
||||
show_header();
|
||||
}
|
||||
bootdev_clear_bootflows(dev);
|
||||
for (i = 0,
|
||||
ret = bootflow_scan_bootdev(dev, &iter, flags, &bflow);
|
||||
i < 1000 && ret != -ENODEV;
|
||||
i++, ret = bootflow_scan_next(&iter, &bflow)) {
|
||||
bflow.err = ret;
|
||||
if (!ret)
|
||||
num_valid++;
|
||||
ret = bootdev_add_bootflow(&bflow);
|
||||
if (ret) {
|
||||
printf("Out of memory\n");
|
||||
return CMD_RET_FAILURE;
|
||||
}
|
||||
if (list)
|
||||
show_bootflow(i, &bflow, errors);
|
||||
if (boot && !bflow.err)
|
||||
bootflow_run_boot(&iter, &bflow);
|
||||
}
|
||||
} else {
|
||||
if (list) {
|
||||
printf("Scanning for bootflows in all bootdevs\n");
|
||||
show_header();
|
||||
}
|
||||
bootstd_clear_glob();
|
||||
|
||||
for (i = 0,
|
||||
ret = bootflow_scan_first(&iter, flags, &bflow);
|
||||
i < 1000 && ret != -ENODEV;
|
||||
i++, ret = bootflow_scan_next(&iter, &bflow)) {
|
||||
bflow.err = ret;
|
||||
if (!ret)
|
||||
num_valid++;
|
||||
ret = bootdev_add_bootflow(&bflow);
|
||||
if (ret) {
|
||||
printf("Out of memory\n");
|
||||
return CMD_RET_FAILURE;
|
||||
}
|
||||
if (list)
|
||||
show_bootflow(i, &bflow, errors);
|
||||
if (boot && !bflow.err)
|
||||
bootflow_run_boot(&iter, &bflow);
|
||||
}
|
||||
}
|
||||
bootflow_iter_uninit(&iter);
|
||||
if (list)
|
||||
show_footer(i, num_valid);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CMD_BOOTFLOW_FULL
|
||||
static int do_bootflow_list(struct cmd_tbl *cmdtp, int flag, int argc,
|
||||
char *const argv[])
|
||||
{
|
||||
struct bootstd_priv *std;
|
||||
struct udevice *dev;
|
||||
struct bootflow *bflow;
|
||||
int num_valid = 0;
|
||||
bool errors = false;
|
||||
int ret, i;
|
||||
|
||||
if (argc > 1 && *argv[1] == '-')
|
||||
errors = strchr(argv[1], 'e');
|
||||
|
||||
ret = bootstd_get_priv(&std);
|
||||
if (ret)
|
||||
return CMD_RET_FAILURE;
|
||||
dev = std->cur_bootdev;
|
||||
|
||||
/* If we have a device, just list bootflows attached to that device */
|
||||
if (dev) {
|
||||
printf("Showing bootflows for bootdev '%s'\n", dev->name);
|
||||
show_header();
|
||||
for (ret = bootdev_first_bootflow(dev, &bflow), i = 0;
|
||||
!ret;
|
||||
ret = bootdev_next_bootflow(&bflow), i++) {
|
||||
num_valid += bflow->state == BOOTFLOWST_READY;
|
||||
show_bootflow(i, bflow, errors);
|
||||
}
|
||||
} else {
|
||||
printf("Showing all bootflows\n");
|
||||
show_header();
|
||||
for (ret = bootflow_first_glob(&bflow), i = 0;
|
||||
!ret;
|
||||
ret = bootflow_next_glob(&bflow), i++) {
|
||||
num_valid += bflow->state == BOOTFLOWST_READY;
|
||||
show_bootflow(i, bflow, errors);
|
||||
}
|
||||
}
|
||||
show_footer(i, num_valid);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_bootflow_select(struct cmd_tbl *cmdtp, int flag, int argc,
|
||||
char *const argv[])
|
||||
{
|
||||
struct bootstd_priv *std;
|
||||
struct bootflow *bflow, *found;
|
||||
struct udevice *dev;
|
||||
const char *name;
|
||||
char *endp;
|
||||
int seq, i;
|
||||
int ret;
|
||||
|
||||
ret = bootstd_get_priv(&std);
|
||||
if (ret)
|
||||
return CMD_RET_FAILURE;
|
||||
;
|
||||
if (argc < 2) {
|
||||
std->cur_bootflow = NULL;
|
||||
return 0;
|
||||
}
|
||||
dev = std->cur_bootdev;
|
||||
|
||||
name = argv[1];
|
||||
seq = simple_strtol(name, &endp, 16);
|
||||
found = NULL;
|
||||
|
||||
/*
|
||||
* If we have a bootdev device, only allow selection of bootflows
|
||||
* attached to that device
|
||||
*/
|
||||
if (dev) {
|
||||
for (ret = bootdev_first_bootflow(dev, &bflow), i = 0;
|
||||
!ret;
|
||||
ret = bootdev_next_bootflow(&bflow), i++) {
|
||||
if (*endp ? !strcmp(bflow->name, name) : i == seq) {
|
||||
found = bflow;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (ret = bootflow_first_glob(&bflow), i = 0;
|
||||
!ret;
|
||||
ret = bootflow_next_glob(&bflow), i++) {
|
||||
if (*endp ? !strcmp(bflow->name, name) : i == seq) {
|
||||
found = bflow;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
printf("Cannot find bootflow '%s' ", name);
|
||||
if (dev)
|
||||
printf("in bootdev '%s' ", dev->name);
|
||||
printf("(err=%d)\n", ret);
|
||||
return CMD_RET_FAILURE;
|
||||
}
|
||||
std->cur_bootflow = found;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_bootflow_info(struct cmd_tbl *cmdtp, int flag, int argc,
|
||||
char *const argv[])
|
||||
{
|
||||
struct bootstd_priv *std;
|
||||
struct bootflow *bflow;
|
||||
bool dump = false;
|
||||
int ret;
|
||||
|
||||
if (argc > 1 && *argv[1] == '-')
|
||||
dump = strchr(argv[1], 'd');
|
||||
|
||||
ret = bootstd_get_priv(&std);
|
||||
if (ret)
|
||||
return CMD_RET_FAILURE;
|
||||
|
||||
if (!std->cur_bootflow) {
|
||||
printf("No bootflow selected\n");
|
||||
return CMD_RET_FAILURE;
|
||||
}
|
||||
bflow = std->cur_bootflow;
|
||||
|
||||
printf("Name: %s\n", bflow->name);
|
||||
printf("Device: %s\n", bflow->dev->name);
|
||||
printf("Block dev: %s\n", bflow->blk ? bflow->blk->name : "(none)");
|
||||
printf("Method: %s\n", bflow->method->name);
|
||||
printf("State: %s\n", bootflow_state_get_name(bflow->state));
|
||||
printf("Partition: %d\n", bflow->part);
|
||||
printf("Subdir: %s\n", bflow->subdir ? bflow->subdir : "(none)");
|
||||
printf("Filename: %s\n", bflow->fname);
|
||||
printf("Buffer: %lx\n", (ulong)map_to_sysmem(bflow->buf));
|
||||
printf("Size: %x (%d bytes)\n", bflow->size, bflow->size);
|
||||
printf("Error: %d\n", bflow->err);
|
||||
if (dump && bflow->buf) {
|
||||
/* Set some sort of maximum on the size */
|
||||
int size = min(bflow->size, 10 << 10);
|
||||
int i;
|
||||
|
||||
printf("Contents:\n\n");
|
||||
for (i = 0; i < size; i++) {
|
||||
putc(bflow->buf[i]);
|
||||
if (!(i % 128) && ctrlc()) {
|
||||
printf("...interrupted\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_bootflow_boot(struct cmd_tbl *cmdtp, int flag, int argc,
|
||||
char *const argv[])
|
||||
{
|
||||
struct bootstd_priv *std;
|
||||
struct bootflow *bflow;
|
||||
int ret;
|
||||
|
||||
ret = bootstd_get_priv(&std);
|
||||
if (ret)
|
||||
return CMD_RET_FAILURE;
|
||||
|
||||
/*
|
||||
* Require a current bootflow. Users can use 'bootflow scan -b' to
|
||||
* automatically scan and boot, if needed.
|
||||
*/
|
||||
if (!std->cur_bootflow) {
|
||||
printf("No bootflow selected\n");
|
||||
return CMD_RET_FAILURE;
|
||||
}
|
||||
bflow = std->cur_bootflow;
|
||||
ret = bootflow_run_boot(NULL, bflow);
|
||||
if (ret)
|
||||
return CMD_RET_FAILURE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_CMD_BOOTFLOW_FULL */
|
||||
|
||||
#ifdef CONFIG_SYS_LONGHELP
|
||||
static char bootflow_help_text[] =
|
||||
#ifdef CONFIG_CMD_BOOTFLOW_FULL
|
||||
"scan [-abel] [bdev] - scan for valid bootflows (-l list, -a all, -e errors, -b boot)\n"
|
||||
"bootflow list [-e] - list scanned bootflows (-e errors)\n"
|
||||
"bootflow select [<num>|<name>] - select a bootflow\n"
|
||||
"bootflow info [-d] - show info on current bootflow (-d dump bootflow)\n"
|
||||
"bootflow boot - boot current bootflow (or first available if none selected)";
|
||||
#else
|
||||
"scan - boot first available bootflow\n";
|
||||
#endif
|
||||
#endif /* CONFIG_SYS_LONGHELP */
|
||||
|
||||
U_BOOT_CMD_WITH_SUBCMDS(bootflow, "Boot flows", bootflow_help_text,
|
||||
U_BOOT_SUBCMD_MKENT(scan, 3, 1, do_bootflow_scan),
|
||||
#ifdef CONFIG_CMD_BOOTFLOW_FULL
|
||||
U_BOOT_SUBCMD_MKENT(list, 2, 1, do_bootflow_list),
|
||||
U_BOOT_SUBCMD_MKENT(select, 2, 1, do_bootflow_select),
|
||||
U_BOOT_SUBCMD_MKENT(info, 2, 1, do_bootflow_info),
|
||||
U_BOOT_SUBCMD_MKENT(boot, 1, 1, do_bootflow_boot)
|
||||
#endif
|
||||
);
|
||||
Loading…
Reference in New Issue