From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-19.5 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 7E7DAC4338F for ; Thu, 19 Aug 2021 03:50:43 +0000 (UTC) Received: from phobos.denx.de (phobos.denx.de [85.214.62.61]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 063626108B for ; Thu, 19 Aug 2021 03:50:43 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.4.1 mail.kernel.org 063626108B Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=chromium.org Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=lists.denx.de Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id 50CC683255; Thu, 19 Aug 2021 05:47:43 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=chromium.org Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=pass (1024-bit key; unprotected) header.d=chromium.org header.i=@chromium.org header.b="fc/QXlNq"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id E24C0831A0; Thu, 19 Aug 2021 05:46:49 +0200 (CEST) Received: from mail-ot1-x331.google.com (mail-ot1-x331.google.com [IPv6:2607:f8b0:4864:20::331]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits)) (No client certificate requested) by phobos.denx.de (Postfix) with ESMTPS id B8BB28311F for ; Thu, 19 Aug 2021 05:46:37 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=chromium.org Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=sjg@chromium.org Received: by mail-ot1-x331.google.com with SMTP id v33-20020a0568300921b0290517cd06302dso6949467ott.13 for ; Wed, 18 Aug 2021 20:46:37 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=CO1LBtNMcQbP1Hn5ItEaaDVe4b2W6ZQTfDq4ghEonIA=; b=fc/QXlNqxeVuRrQdbo+7rmkPjWTndneMaFeWbncbu+5hu4QIphbdhYPxunaVc7tu6D 2VMosy0ihVaqEYqKTK/tlEjfXFejCibtvo/GpJVH7X+eIArHKbKRlqfVDU2AUSfJVMdk oin3l26PilcP+Le8naOTkHGiX7FWYhyqlLRjw= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=CO1LBtNMcQbP1Hn5ItEaaDVe4b2W6ZQTfDq4ghEonIA=; b=YV688whTDgjj+//dlJiruS15zJs3w/N3ewGcSkq1K1m6pOYuqNcdSssTLzUQ2L9D3Y dG8i2DhvsNJdEyIdD3loy592GnjfJO3zHkn+c5NFtKH5a7+pUdr4AegXA+zsRCikgBeT 6VO8JpvoVc0dXajBxNk1/a7S3gO9UihKWOAS9A69dxC/k9oIZcl9Y/QS84xQm/1OiegK AYQauJot3ULGSgWjlq+HTe/AycvEKE9JN3HGac5VmU39MWjl0VwouDsDJ4UcuWWVqX8l Go5D/3nZJ92C3DWJt7VqjB+TpIePKGOUV5n4KfvnxkVcySW9VYwgUIeTpozj9n4Gb5Xn hnmA== X-Gm-Message-State: AOAM531D95vCNcKcb/5YhEDMjcJGA/xehEmnkDNCu+uov/wFHNz60VOV IydvSKl9UoARo37TLaN+ylXpB2L64uPZUg== X-Google-Smtp-Source: ABdhPJw86O4fGx1xBDT3IsWno6mBDPpSYjIby4XeaUtn0BI0JZ1K/x3Q4+Gluuns+ebE7Wm7FfOxOA== X-Received: by 2002:a05:6830:4388:: with SMTP id s8mr9853920otv.167.1629344796196; Wed, 18 Aug 2021 20:46:36 -0700 (PDT) Received: from kiwi.bld.corp.google.com (c-67-190-101-114.hsd1.co.comcast.net. [67.190.101.114]) by smtp.gmail.com with ESMTPSA id q3sm370025ooa.13.2021.08.18.20.46.35 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 18 Aug 2021 20:46:35 -0700 (PDT) From: Simon Glass To: U-Boot Mailing List Cc: Ilias Apalodimas , Steffen Jaeckel , Michal Simek , Tom Rini , Dennis Gilmore , Daniel Schwierzeck , Lukas Auer , Simon Glass Subject: [PATCH 22/28] bootflow: Add a command Date: Wed, 18 Aug 2021 21:45:55 -0600 Message-Id: <20210818214547.22.I6f3d772aa3671d526b66580c7989ae6c27c8538a@changeid> X-Mailer: git-send-email 2.33.0.rc1.237.g0d66db33f3-goog In-Reply-To: <20210819034601.1618773-1-sjg@chromium.org> References: <20210819034601.1618773-1-sjg@chromium.org> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.34 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" X-Virus-Scanned: clamav-milter 0.103.2 at phobos.denx.de X-Virus-Status: Clean Add a 'bootflow' command to handle listing and selection of bootflow. Signed-off-by: Simon Glass --- 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 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 + */ + +#include +#include +#include +#include +#include +#include + +/** + * 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