[PATCH 22/28] bootflow: Add a command

Simon Glass sjg at chromium.org
Thu Aug 19 05:45:55 CEST 2021


Add a 'bootflow' command to handle listing and selection of bootflow.

Signed-off-by: Simon Glass <sjg at chromium.org>
---

 MAINTAINERS       |   1 +
 boot/bootmethod.c |  26 +++
 cmd/Makefile      |   1 +
 cmd/bootflow.c    | 399 ++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 427 insertions(+)
 create mode 100644 cmd/bootflow.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 655746265f8..367193358b7 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -635,6 +635,7 @@ M:	Simon Glass <sjg at chromium.org>
 S:	Maintained
 F:	boot/bootmethod.c
 F:	boot/distro.c
+F:	cmd/bootflow.c
 F:	cmd/bootmethod.c
 F:	include/bootmethod.h
 F:	include/distro.h
diff --git a/boot/bootmethod.c b/boot/bootmethod.c
index be9d4aa02cc..b9752f75e54 100644
--- a/boot/bootmethod.c
+++ b/boot/bootmethod.c
@@ -404,6 +404,32 @@ int bootflow_boot(struct bootflow *bflow)
 	return log_msg_ret("end", -EFAULT);
 }
 
+void bootmethod_list(bool probe)
+{
+	struct udevice *dev;
+	int ret;
+	int i;
+
+	printf("Seq  Probed  Status  Uclass    Name\n");
+	printf("---  ------  ------  --------  ------------------\n");
+	if (probe)
+		ret = uclass_first_device_err(UCLASS_BOOTMETHOD, &dev);
+	else
+		ret = uclass_find_first_device(UCLASS_BOOTMETHOD, &dev);
+	for (i = 0; dev; i++) {
+		printf("%3x   [ %c ]  %6s  %-9.9s %s\n", dev_seq(dev),
+		       device_active(dev) ? '+' : ' ',
+		       ret ? simple_itoa(ret) : "OK",
+		       dev_get_uclass_name(dev_get_parent(dev)), dev->name);
+		if (probe)
+			ret = uclass_next_device_err(&dev);
+		else
+			ret = uclass_find_next_device(&dev);
+	}
+	printf("---  ------  ------  --------  ------------------\n");
+	printf("(%d device%s)\n", i, i != 1 ? "s" : "");
+}
+
 int bootmethod_setup_for_dev(struct udevice *parent, const char *drv_name)
 {
 	struct udevice *bm;
diff --git a/cmd/Makefile b/cmd/Makefile
index 891819ae0f6..f6ad648c94e 100644
--- a/cmd/Makefile
+++ b/cmd/Makefile
@@ -19,6 +19,7 @@ obj-$(CONFIG_CMD_AB_SELECT) += ab_select.o
 obj-$(CONFIG_CMD_ADC) += adc.o
 obj-$(CONFIG_CMD_ARMFLASH) += armflash.o
 obj-$(CONFIG_HAVE_BLOCK_DEVICE) += blk_common.o
+obj-$(CONFIG_CMD_BOOTMETHOD) += bootmethod.o bootflow.o
 obj-$(CONFIG_CMD_SOURCE) += source.o
 obj-$(CONFIG_CMD_BCB) += bcb.o
 obj-$(CONFIG_CMD_BDI) += bdinfo.o
diff --git a/cmd/bootflow.c b/cmd/bootflow.c
new file mode 100644
index 00000000000..4c619f0063b
--- /dev/null
+++ b/cmd/bootflow.c
@@ -0,0 +1,399 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * 'bootflow' command
+ *
+ * Copyright 2021 Google LLC
+ * Written by Simon Glass <sjg at chromium.org>
+ */
+
+#include <common.h>
+#include <bootmethod.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 'Type' */
+	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_LOADED:
+		printf("File loaded");
+		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,
+	       bootmethod_type_get_name(bflow->type),
+	       bootmethod_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  Type         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_list(struct cmd_tbl *cmdtp, int flag, int argc,
+			    char *const argv[])
+{
+	struct bootflow_state *state;
+	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 = bootmethod_get_state(&state);
+	if (ret)
+		return CMD_RET_FAILURE;
+	dev = state->cur_bootmethod;
+
+	/* If we have a device, just list bootflows attached to that device */
+	if (dev) {
+		printf("Showing bootflows for bootmethod '%s'\n", dev->name);
+		show_header();
+		for (ret = bootmethod_first_bootflow(dev, &bflow), i = 0;
+		     !ret;
+		     ret = bootmethod_next_bootflow(&bflow), i++) {
+			num_valid += bflow->state == BOOTFLOWST_LOADED;
+			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_LOADED;
+			show_bootflow(i, bflow, errors);
+		}
+	}
+	show_footer(i, num_valid);
+
+	return 0;
+}
+
+static int bootflow_run_boot(struct bootflow *bflow)
+{
+	int ret;
+
+	printf("** Booting bootflow '%s'\n", bflow->name);
+	ret = bootflow_boot(bflow);
+	switch (ret) {
+	case -EPROTO:
+		printf("Bootflow not loaded (state '%s')\n",
+		       bootmethod_state_get_name(bflow->state));
+		break;
+	case -ENOSYS:
+		printf("Boot type '%s' not supported\n",
+		       bootmethod_type_get_name(bflow->type));
+		break;
+	default:
+		printf("Boot failed (err=%d)\n", ret);
+		break;
+	}
+
+	return 0;
+}
+
+static int do_bootflow_scan(struct cmd_tbl *cmdtp, int flag, int argc,
+			    char *const argv[])
+{
+	struct bootflow_state *state;
+	struct bootmethod_iter iter;
+	struct udevice *dev;
+	struct bootflow bflow;
+	bool all = false, boot = false, errors = false, list = false;
+	int num_valid = 0;
+	int ret, i;
+
+	if (argc > 1 && *argv[1] == '-') {
+		all = strchr(argv[1], 'a');
+		boot = strchr(argv[1], 'b');
+		errors = strchr(argv[1], 'e');
+		list = strchr(argv[1], 'l');
+	}
+
+	ret = bootmethod_get_state(&state);
+	if (ret)
+		return CMD_RET_FAILURE;
+	dev = state->cur_bootmethod;
+	state->cur_bootflow = NULL;
+
+	/*
+	 * If we have a device, just scan for bootflows attached to that device
+	 */
+	if (dev) {
+		if (list) {
+			printf("Scanning for bootflows in bootmethod '%s'\n",
+			       dev->name);
+			show_header();
+		}
+		bootmethod_clear_bootflows(dev);
+		for (i = 0, ret = 0; i < 100 && ret != -ESHUTDOWN; i++) {
+			ret = bootmethod_get_bootflow(dev, i, &bflow);
+			if ((ret && !all) || ret == -ESHUTDOWN) {
+				bootflow_free(&bflow);
+				continue;
+			}
+			bflow.err = ret;
+			ret = bootmethod_add_bootflow(&bflow);
+			if (ret) {
+				printf("Out of memory\n");
+				return CMD_RET_FAILURE;
+			}
+			num_valid++;
+			if (list)
+				show_bootflow(i, &bflow, errors);
+			if (boot && !bflow.err)
+				bootflow_run_boot(&bflow);
+		}
+	} else {
+		int flags = 0;
+
+		if (list) {
+			printf("Scanning for bootflows in all bootmethods\n");
+			show_header();
+		}
+		bootmethod_clear_glob();
+		if (list)
+			flags |= BOOTFLOWF_SHOW;
+		if (all)
+			flags |= BOOTFLOWF_ALL;
+		for (i = 0,
+		     ret = bootmethod_scan_first_bootflow(&iter, flags, &bflow);
+		     i < 1000 && ret != -ENODEV;
+		     i++, ret = bootmethod_scan_next_bootflow(&iter, &bflow)) {
+			bflow.err = ret;
+			if (!ret)
+				num_valid++;
+			ret = bootmethod_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(&bflow);
+		}
+	}
+	if (list)
+		show_footer(i, num_valid);
+
+	return 0;
+}
+
+static int do_bootflow_select(struct cmd_tbl *cmdtp, int flag, int argc,
+			      char *const argv[])
+{
+	struct bootflow_state *state;
+	struct bootflow *bflow, *found;
+	struct udevice *dev;
+	const char *name;
+	char *endp;
+	int seq, i;
+	int ret;
+
+	ret = bootmethod_get_state(&state);
+	if (ret)
+		return CMD_RET_FAILURE;
+;
+	if (argc < 2) {
+		state->cur_bootflow = NULL;
+		return 0;
+	}
+	dev = state->cur_bootmethod;
+
+	name = argv[1];
+	seq = simple_strtol(name, &endp, 16);
+	found = NULL;
+
+	/*
+	 * If we have a bootmethod device, only allow selection of bootflows
+	 * attached to that device
+	 */
+	if (dev) {
+		for (ret = bootmethod_first_bootflow(dev, &bflow), i = 0;
+		     !ret;
+		     ret = bootmethod_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 bootmethod '%s' ", dev->name);
+		printf("(err=%d)\n", ret);
+		return CMD_RET_FAILURE;
+	}
+	state->cur_bootflow = found;
+
+	return 0;
+}
+
+static int do_bootflow_info(struct cmd_tbl *cmdtp, int flag, int argc,
+			    char *const argv[])
+{
+	struct bootflow_state *state;
+	struct bootflow *bflow;
+	bool dump = false;
+	int ret;
+
+	if (argc > 1 && *argv[1] == '-')
+		dump = strchr(argv[1], 'd');
+
+	ret = bootmethod_get_state(&state);
+	if (ret)
+		return CMD_RET_FAILURE;
+
+	if (!state->cur_bootflow) {
+		printf("No bootflow selected\n");
+		return CMD_RET_FAILURE;
+	}
+	bflow = state->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("Sequence:  %d\n", bflow->seq);
+	printf("Type:      %s\n", bootmethod_type_get_name(bflow->type));
+	printf("State:     %s\n", bootmethod_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 bootflow_state *state;
+	struct bootflow *bflow;
+	int ret;
+
+	ret = bootmethod_get_state(&state);
+	if (ret)
+		return CMD_RET_FAILURE;
+
+	/*
+	 * Require a current bootflow. Users can use 'bootflow scan -b' to
+	 * automatically scan and boot, if needed.
+	 */
+	if (!state->cur_bootflow) {
+		printf("No bootflow selected\n");
+		return CMD_RET_FAILURE;
+	}
+	bflow = state->cur_bootflow;
+	ret = bootflow_run_boot(bflow);
+	if (ret) {
+		printf("Boot failed (err %d)n", ret);
+		return CMD_RET_FAILURE;
+	}
+
+	return 0;
+}
+
+#ifdef CONFIG_SYS_LONGHELP
+static char bootflow_help_text[] =
+	"scan [-abel] - scan for valid bootflows (-l list, -a all, -e errors, -b boot)\n"
+	"bootflow list [-e]    - list scanned bootflows (-e errors)\n"
+	"bootflow select       - 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)";
+#endif
+
+U_BOOT_CMD_WITH_SUBCMDS(bootflow, "Bootflows", bootflow_help_text,
+	U_BOOT_SUBCMD_MKENT(scan, 2, 1, do_bootflow_scan),
+	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));
-- 
2.33.0.rc1.237.g0d66db33f3-goog



More information about the U-Boot mailing list