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 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id A6260C433F5 for ; Sat, 23 Oct 2021 23:30:28 +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 2F4C260F22 for ; Sat, 23 Oct 2021 23:30:28 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.4.1 mail.kernel.org 2F4C260F22 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 8CCE383571; Sun, 24 Oct 2021 01:29:46 +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="hUG+fz4i"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id A8F808331C; Sun, 24 Oct 2021 01:27:52 +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 0B4A783544 for ; Sun, 24 Oct 2021 01:27:14 +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 b4-20020a9d7544000000b00552ab826e3aso9338533otl.4 for ; Sat, 23 Oct 2021 16:27:13 -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=SKnq6hdmiy7laJlm+EcHvIYlo+rFOkyIqVxmVLIVzXk=; b=hUG+fz4i5zDzfjnSpdFy+slthXlN1l8hh4z+n1N1TAA13dZif2lgi9JYKPDTLsDPdW pzm2V9gkXkgpSLR0MyVA/M/McK2ec8yhWV+98h7VOsQDcwad66BFSfvFlF482LBeChEn ECy2nnIdpze3n8F70tYZ7gTWEKeDsmvsTjMKQ= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=SKnq6hdmiy7laJlm+EcHvIYlo+rFOkyIqVxmVLIVzXk=; b=cqhrZpmW9vG3vKTwko2yL4Be0XAAXHhGWanWRfwcUAdhvE3KW23XJ5x+EWxpnXKuqJ LxF3QU0ILaop6bZe7x5T0ssEbKT1PI5WIKz0Nb94tze85oIhxQg/8f33JlefVA+VBtyz 6vc7+cPWcaxlcUl160kDP53/EXim31CIJzXx1EI/X0/kDDwFcEuinVQp8+ZbHXD8KeOC x2zZ5KH7pd2IjR7hotBOnPeLgTparhrZa0BuuX/I1L8YTAZuv4bS7VQs0OZfA+1imwQm mzzCe5Hr8E7KTagLb04qrcjkrfqvFtMLQ+Gq/AajdFAV2urRo5/BR1ryk9axn/M8Pyka WkLg== X-Gm-Message-State: AOAM531i+8xXu3xM1w5sgCo5rsRv0awavO0YrGYVJM1UJLcyfACqx6fu k01n9lDfaBihB/h/4IlTjzuoEVxZ9DLcbg== X-Google-Smtp-Source: ABdhPJxeyHZWyWunJkyad24Tk6FfZ92tcTsJmOPYaFkzZMeKUM+5CDTfLULtmNyKVmkju+AM9bVXbg== X-Received: by 2002:a9d:ea9:: with SMTP id 38mr6948568otj.144.1635031632365; Sat, 23 Oct 2021 16:27:12 -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 l24sm2253885oop.4.2021.10.23.16.27.11 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 23 Oct 2021 16:27:12 -0700 (PDT) From: Simon Glass To: U-Boot Mailing List Cc: Michal Simek , Heinrich Schuchardt , Tom Rini , Ilias Apalodimas , Daniel Schwierzeck , Steffen Jaeckel , =?UTF-8?q?Marek=20Beh=C3=BAn?= , Lukas Auer , Dennis Gilmore , Simon Glass Subject: [PATCH v2 25/41] bootstd: Add a bootflow ommand Date: Sat, 23 Oct 2021 17:26:19 -0600 Message-Id: <20211023172618.v2.25.I6f3d772aa3671d526b66580c7989ae6c27c8538a@changeid> X-Mailer: git-send-email 2.33.0.1079.g6e70778dc9-goog In-Reply-To: <20211023232635.9195-1-sjg@chromium.org> References: <20211023232635.9195-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 --- (no changes since v1) cmd/Kconfig | 10 +- cmd/Makefile | 2 +- cmd/bootflow.c | 386 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 395 insertions(+), 3 deletions(-) create mode 100644 cmd/bootflow.c diff --git a/cmd/Kconfig b/cmd/Kconfig index bb6b881660c..1b6900d7f52 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -195,12 +195,18 @@ config CMD_BOOTM Boot an application image from the memory. config CMD_BOOTSTD - bool "bootdev" + bool "bootdev, bootflow" depends on BOOTSTD default y help Support working with standard boot, including listing available - bootdevs and showing information about a particular one. + bootdevs and showing information about a particular one. It also + allows scanning for bootflows available with the bootdev. + + This enables the following commands: + + bootdev + bootflow config BOOTM_EFI bool "Support booting UEFI FIT images" diff --git a/cmd/Makefile b/cmd/Makefile index a8fce22d6c6..1b4ff861871 100644 --- a/cmd/Makefile +++ b/cmd/Makefile @@ -19,7 +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_BOOTSTD) += bootdev.o +obj-$(CONFIG_CMD_BOOTSTD) += bootdev.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..84b9970bd65 --- /dev/null +++ b/cmd/bootflow.c @@ -0,0 +1,386 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * 'bootflow' command + * + * Copyright 2021 Google LLC + * Written by Simon Glass + */ + +#include +#include +#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 '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_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_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; + int ret, i; + int flags; + + ret = bootstd_get_priv(&std); + if (ret) + return CMD_RET_FAILURE; + dev = std->cur_bootdev; + + 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'); + argc--; + argv++; + } + if (argc > 1) { + const char *label = argv[1]; + + if (bootdev_find_by_any(label, &dev)) + return CMD_RET_FAILURE; + } + + 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 (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; +} + +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; +} + +#ifdef CONFIG_SYS_LONGHELP +static char bootflow_help_text[] = + "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 [|] - 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, "Boot flows", bootflow_help_text, + U_BOOT_SUBCMD_MKENT(scan, 3, 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.1079.g6e70778dc9-goog