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 664F2C4338F for ; Thu, 19 Aug 2021 03:50:18 +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 E5C4D6109F for ; Thu, 19 Aug 2021 03:50:17 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.4.1 mail.kernel.org E5C4D6109F 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 C9FCB83245; Thu, 19 Aug 2021 05:47: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=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="f28qNIv2"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id 76C1C831E7; Thu, 19 Aug 2021 05:46:46 +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 72C9A8315C for ; Thu, 19 Aug 2021 05:46:34 +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 w22-20020a056830411600b0048bcf4c6bd9so6984571ott.8 for ; Wed, 18 Aug 2021 20:46:34 -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=EOxSyqmU+7WuuzpPuXVKFgOLYH4wnCa16LAz1MzVVTo=; b=f28qNIv25j0WWZpbz/os5QXNmVE18P7+uiEHmUt/Z+uZk4JDjMrTtf5LYYadreQ8Qe b3ev1TVW+jbwxifZmekiqVHxZUswKplTUSnQcOx1WidqL7fHrKcmrFJxli3qFmVTO3jH FbQrdleP2WZsMLYYRwhF6S0mVT6IRBDzHgSc4= 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=EOxSyqmU+7WuuzpPuXVKFgOLYH4wnCa16LAz1MzVVTo=; b=oyH2z50+Kdd3w1Yr7t9qpI6C5Gsb1RGFweRGkziJEDly50PFSShF4rrX+YhTS0LbR5 JN7lnd6+EyC2t6pB+RIMT2eOP+eMwdKI65tfy7LP+kMFWx4FbdjarEOU1jaHOCYZU8pn 8Kg3AvjfBnbieGlCCteVklboR+ZQqQdN2ca1r0I38pKC6oeX5mlsilbLPM6gfpo53Hdd Wk0BNhCnuICJt8nFLoaLX1DRpueYSEuf2/Qet7VaaZwxmKrc7D7n2IZ6Orh9BZbDtw3q mnvsDa/+Q/Yy27HyVZmEZ10X1iHlBdwlmDfNzKwRNi4EtKSGvJYWJUGsMBvw21gy81gk 1zGQ== X-Gm-Message-State: AOAM532UItwtnFT3GC9HYq4PEXi/QGH+Ia5EjfNfcfRWSuMDLoOa42ps Uh4ZZjycs8RDR5OUFja2M48k6dTHRvVImw== X-Google-Smtp-Source: ABdhPJwWBMrHXMnWl+/wKauBf80ARaC0mJtEMroKIEa5h4SWj5TceXJ+zm1/fcP64V3jo4mUHdETvQ== X-Received: by 2002:a05:6830:14d5:: with SMTP id t21mr9675546otq.271.1629344792722; Wed, 18 Aug 2021 20:46:32 -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.31 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 18 Aug 2021 20:46:32 -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 19/28] bootmethod: Add the uclass and core implementation Date: Wed, 18 Aug 2021 21:45:52 -0600 Message-Id: <20210818214547.19.I3b5bc09d034cd72b2a9c604a0907c10abc05956a@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 uclass for bootmethod and the various helpers needed to make it work. Signed-off-by: Simon Glass --- MAINTAINERS | 6 + boot/Kconfig | 9 + boot/Makefile | 1 + boot/bootmethod.c | 445 +++++++++++++++++++++++++++++++++++++++++ include/bootmethod.h | 355 ++++++++++++++++++++++++++++++++ include/dm/uclass-id.h | 1 + 6 files changed, 817 insertions(+) create mode 100644 boot/bootmethod.c create mode 100644 include/bootmethod.h diff --git a/MAINTAINERS b/MAINTAINERS index 776ff703b9b..d977cee562b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -630,6 +630,12 @@ M: Simon Glass S: Maintained F: tools/binman/ +BOOTMETHOD +M: Simon Glass +S: Maintained +F: boot/bootmethod.c +F: include/bootmethod.h + BTRFS M: Marek Behun R: Qu Wenruo diff --git a/boot/Kconfig b/boot/Kconfig index 0d4c38402c1..90f716c3ef1 100644 --- a/boot/Kconfig +++ b/boot/Kconfig @@ -290,6 +290,15 @@ endif # SPL endif # FIT +config BOOTMETHOD + bool "Bootmethod support" + default y + help + A bootmethod is a standard way of locating something to boot, + typically an Operating System such as Linux. Enable this to support + iterating through available bootmethods to find a bootflow suitable + for booting. + config LEGACY_IMAGE_FORMAT bool "Enable support for the legacy image format" default y if !FIT_SIGNATURE diff --git a/boot/Makefile b/boot/Makefile index e8f984248e6..10d427e115c 100644 --- a/boot/Makefile +++ b/boot/Makefile @@ -22,6 +22,7 @@ endif obj-y += image.o obj-$(CONFIG_ANDROID_AB) += android_ab.o obj-$(CONFIG_ANDROID_BOOT_IMAGE) += image-android.o image-android-dt.o +obj-$(CONFIG_$(SPL_TPL_)BOOTMETHOD) += bootmethod.o obj-$(CONFIG_$(SPL_TPL_)OF_LIBFDT) += image-fdt.o obj-$(CONFIG_$(SPL_TPL_)FIT_SIGNATURE) += fdt_region.o obj-$(CONFIG_$(SPL_TPL_)FIT) += image-fit.o diff --git a/boot/bootmethod.c b/boot/bootmethod.c new file mode 100644 index 00000000000..8e4157c6a47 --- /dev/null +++ b/boot/bootmethod.c @@ -0,0 +1,445 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2021 Google LLC + * Written by Simon Glass + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +enum { + /* + * Set some sort of limit on the number of bootflows a bootmethod can + * return. Note that for disks this limits the partitions numbers that + * are scanned to 1..MAX_BOOTFLOWS_PER_BOOTMETHOD + */ + MAX_BOOTFLOWS_PER_BOOTMETHOD = 20, +}; + +static const char *const bootmethod_state[BOOTFLOWST_COUNT] = { + "base", + "media", + "part", + "fs", + "file", + "loaded", +}; + +static const char *const bootmethod_type[BOOTFLOWT_COUNT] = { +}; + +int bootmethod_get_state(struct bootflow_state **statep) +{ + struct uclass *uc; + int ret; + + ret = uclass_get(UCLASS_BOOTMETHOD, &uc); + if (ret) + return ret; + *statep = uclass_get_priv(uc); + + return 0; +} + +const char *bootmethod_state_get_name(enum bootflow_state_t state) +{ + if (state < 0 || state >= BOOTFLOWST_COUNT) + return "?"; + + return bootmethod_state[state]; +} + +const char *bootmethod_type_get_name(enum bootflow_type_t type) +{ + if (type < 0 || type >= BOOTFLOWT_COUNT) + return "?"; + + return bootmethod_type[type]; +} + +void bootflow_free(struct bootflow *bflow) +{ + free(bflow->name); + free(bflow->subdir); + free(bflow->fname); + free(bflow->buf); +} + +void bootflow_remove(struct bootflow *bflow) +{ + list_del(&bflow->bm_node); + list_del(&bflow->glob_node); + + bootflow_free(bflow); + free(bflow); +} + +void bootmethod_clear_bootflows(struct udevice *dev) +{ + struct bootmethod_uc_plat *ucp = dev_get_uclass_plat(dev); + + while (!list_empty(&ucp->bootflow_head)) { + struct bootflow *bflow; + + bflow = list_first_entry(&ucp->bootflow_head, struct bootflow, + bm_node); + bootflow_remove(bflow); + } +} + +void bootmethod_clear_glob(void) +{ + struct bootflow_state *state; + + if (bootmethod_get_state(&state)) + return; + + while (!list_empty(&state->glob_head)) { + struct bootflow *bflow; + + bflow = list_first_entry(&state->glob_head, struct bootflow, + glob_node); + bootflow_remove(bflow); + } +} + +int bootmethod_add_bootflow(struct bootflow *bflow) +{ + struct bootmethod_uc_plat *ucp = dev_get_uclass_plat(bflow->dev); + struct bootflow_state *state; + struct bootflow *new; + int ret; + + assert(bflow->dev); + ret = bootmethod_get_state(&state); + if (ret) + return ret; + + new = malloc(sizeof(*bflow)); + if (!new) + return log_msg_ret("bflow", -ENOMEM); + memcpy(new, bflow, sizeof(*bflow)); + + list_add_tail(&new->glob_node, &state->glob_head); + list_add_tail(&new->bm_node, &ucp->bootflow_head); + + return 0; +} + +int bootmethod_first_bootflow(struct udevice *dev, struct bootflow **bflowp) +{ + struct bootmethod_uc_plat *ucp = dev_get_uclass_plat(dev); + + if (list_empty(&ucp->bootflow_head)) + return -ENOENT; + + *bflowp = list_first_entry(&ucp->bootflow_head, struct bootflow, + bm_node); + + return 0; +} + +int bootmethod_next_bootflow(struct bootflow **bflowp) +{ + struct bootflow *bflow = *bflowp; + struct bootmethod_uc_plat *ucp = dev_get_uclass_plat(bflow->dev); + + *bflowp = NULL; + + if (list_is_last(&bflow->bm_node, &ucp->bootflow_head)) + return -ENOENT; + + *bflowp = list_entry(bflow->bm_node.next, struct bootflow, bm_node); + + return 0; +} + +int bootflow_first_glob(struct bootflow **bflowp) +{ + struct bootflow_state *state; + int ret; + + ret = bootmethod_get_state(&state); + if (ret) + return ret; + + if (list_empty(&state->glob_head)) + return -ENOENT; + + *bflowp = list_first_entry(&state->glob_head, struct bootflow, + glob_node); + + return 0; +} + +int bootflow_next_glob(struct bootflow **bflowp) +{ + struct bootflow_state *state; + struct bootflow *bflow = *bflowp; + int ret; + + ret = bootmethod_get_state(&state); + if (ret) + return ret; + + *bflowp = NULL; + + if (list_is_last(&bflow->glob_node, &state->glob_head)) + return -ENOENT; + + *bflowp = list_entry(bflow->glob_node.next, struct bootflow, glob_node); + + return 0; +} + +int bootmethod_get_bootflow(struct udevice *dev, int seq, + struct bootflow *bflow) +{ + const struct bootmethod_ops *ops = bootmethod_get_ops(dev); + + if (!ops->get_bootflow) + return -ENOSYS; + memset(bflow, '\0', sizeof(*bflow)); + bflow->dev = dev; + bflow->seq = seq; + + return ops->get_bootflow(dev, seq, bflow); +} + +static int next_bootflow(struct udevice *dev, int seq, struct bootflow *bflow) +{ + int ret; + + ret = bootmethod_get_bootflow(dev, seq, bflow); + if (ret) + return ret; + + return 0; +} + +static void bootmethod_iter_set_dev(struct bootmethod_iter *iter, + struct udevice *dev) +{ + iter->dev = dev; + if (iter->flags & BOOTFLOWF_SHOW) { + if (dev) + printf("Scanning bootmethod '%s':\n", dev->name); + else + printf("No more bootmethods\n"); + } +} + +int bootmethod_scan_first_bootflow(struct bootmethod_iter *iter, int flags, + struct bootflow *bflow) +{ + struct udevice *dev; + int ret; + + iter->flags = flags; + iter->seq = 0; + ret = uclass_first_device_err(UCLASS_BOOTMETHOD, &dev); + if (ret) + return ret; + bootmethod_iter_set_dev(iter, dev); + + ret = bootmethod_scan_next_bootflow(iter, bflow); + if (ret) + return ret; + + return 0; +} + +int bootmethod_scan_next_bootflow(struct bootmethod_iter *iter, + struct bootflow *bflow) +{ + struct udevice *dev; + int ret; + + do { + dev = iter->dev; + ret = next_bootflow(dev, iter->seq, bflow); + + /* If we got a valid bootflow, return it */ + if (!ret) { + log_debug("Bootmethod '%s' seq %d: Found bootflow\n", + dev->name, iter->seq); + iter->seq++; + return 0; + } + + /* + * Unless there are no more partitions or no bootflow support, + * try the next partition + */ + else if (ret != -ESHUTDOWN && ret != -ENOSYS) { + log_debug("Bootmethod '%s' seq %d: Error %d\n", + dev->name, iter->seq, ret); + if ((iter->seq++ != MAX_BOOTFLOWS_PER_BOOTMETHOD) && + (iter->flags & BOOTFLOWF_ALL)) + return log_msg_ret("all", ret); + } + + /* we got to the end of that bootmethod, try the next */ + ret = uclass_next_device_err(&dev); + bootmethod_iter_set_dev(iter, dev); + + /* if there are no more bootmethods, give up */ + if (ret) + return ret; + + /* start at the beginning of this bootmethod */ + iter->seq = 0; + } while (1); +} + +int bootmethod_bind(struct udevice *parent, const char *drv_name, + const char *name, struct udevice **devp) +{ + struct udevice *dev; + char dev_name[30]; + char *str; + int ret; + + snprintf(dev_name, sizeof(dev_name), "%s.%s", parent->name, name); + str = strdup(dev_name); + if (!str) + return -ENOMEM; + ret = device_bind_driver(parent, drv_name, str, &dev); + if (ret) + return ret; + *devp = dev; + + return 0; +} + +int bootmethod_find_in_blk(struct udevice *dev, struct udevice *blk, int seq, + struct bootflow *bflow) +{ + struct blk_desc *desc = dev_get_uclass_plat(blk); + struct disk_partition info; + char name[60]; + int partnum = seq + 1; + int ret; + + if (seq >= MAX_BOOTFLOWS_PER_BOOTMETHOD) + return -ESHUTDOWN; + + bflow->blk = blk; + bflow->seq = seq; + snprintf(name, sizeof(name), "%s.part_%x", dev->name, partnum); + bflow->name = strdup(name); + if (!bflow->name) + return log_msg_ret("name", -ENOMEM); + + bflow->state = BOOTFLOWST_BASE; + ret = part_get_info(desc, partnum, &info); + + /* This error indicates the media is not present */ + if (ret != -EOPNOTSUPP) + bflow->state = BOOTFLOWST_MEDIA; + if (ret) + return log_msg_ret("part", ret); + + bflow->state = BOOTFLOWST_PART; + bflow->part = partnum; + ret = fs_set_blk_dev_with_part(desc, partnum); +#ifdef CONFIG_DOS_PARTITION + log_debug("%s: Found partition %x type %x fstype %d\n", blk->name, + partnum, info.sys_ind, ret ? -1 : fs_get_type()); +#endif + if (ret) + return log_msg_ret("fs", ret); + + bflow->state = BOOTFLOWST_FS; + + return 0; +} + +int bootflow_boot(struct bootflow *bflow) +{ + bool done = false; + int ret; + + if (bflow->state != BOOTFLOWST_LOADED) + return log_msg_ret("load", -EPROTO); + + switch (bflow->type) { + case BOOTFLOWT_COUNT: + break; + } + + if (!done) + return log_msg_ret("type", -ENOSYS); + + if (ret) + return log_msg_ret("boot", ret); + + /* + * internal error, should not get here since we should have booted + * something or returned an error + */ + + return log_msg_ret("end", -EFAULT); +} + +int bootmethod_setup_for_dev(struct udevice *parent, const char *drv_name) +{ + struct udevice *bm; + int ret; + + if (!CONFIG_IS_ENABLED(BOOTMETHOD)) + return 0; + + ret = device_find_first_child_by_uclass(parent, UCLASS_BOOTMETHOD, + &bm); + if (ret) { + if (ret != -ENODEV) { + log_debug("Cannot access bootmethod device\n"); + return ret; + } + + ret = bootmethod_bind(parent, drv_name, "bootmethod", &bm); + if (ret) { + log_debug("Cannot create bootmethod device\n"); + return ret; + } + } + + return 0; +} + +static int bootmethod_init(struct uclass *uc) +{ + struct bootflow_state *state = uclass_get_priv(uc); + + INIT_LIST_HEAD(&state->glob_head); + + return 0; +} + +static int bootmethod_post_bind(struct udevice *dev) +{ + struct bootmethod_uc_plat *ucp = dev_get_uclass_plat(dev); + + INIT_LIST_HEAD(&ucp->bootflow_head); + + return 0; +} + +UCLASS_DRIVER(bootmethod) = { + .id = UCLASS_BOOTMETHOD, + .name = "bootmethod", + .flags = DM_UC_FLAG_SEQ_ALIAS, + .priv_auto = sizeof(struct bootflow_state), + .per_device_plat_auto = sizeof(struct bootmethod_uc_plat), + .init = bootmethod_init, + .post_bind = bootmethod_post_bind, +}; diff --git a/include/bootmethod.h b/include/bootmethod.h new file mode 100644 index 00000000000..a45897b0c0c --- /dev/null +++ b/include/bootmethod.h @@ -0,0 +1,355 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2021 Google LLC + * Written by Simon Glass + */ + +#ifndef __bootmethod_h +#define __bootmethod_h + +#include + +/** + * enum bootflow_state_t - states that a particular bootflow can be in + */ +enum bootflow_state_t { + BOOTFLOWST_BASE, /**< Nothing known yet */ + BOOTFLOWST_MEDIA, /**< Media exists */ + BOOTFLOWST_PART, /**< Partition exists */ + BOOTFLOWST_FS, /**< Filesystem exists */ + BOOTFLOWST_FILE, /**< Bootflow file exists */ + BOOTFLOWST_LOADED, /**< Bootflow file loaded */ + + BOOTFLOWST_COUNT +}; + +enum bootflow_type_t { + BOOTFLOWT_COUNT, +}; + +/** + * struct bootflow_state - information about available bootflows, etc. + * + * This is attached to the bootmethod uclass so there is only one of them. It + * provides overall information about bootmethods and bootflows. + * + * @cur_bootmethod: Currently selected bootmethod (for commands) + * @cur_bootflow: Currently selected bootflow (for commands) + * @glob_head: Head for the global list of all bootmethods across all bootflows + */ +struct bootflow_state { + struct udevice *cur_bootmethod; + struct bootflow *cur_bootflow; + struct list_head glob_head; +}; + +/** + * struct bootmethod_uc_plat - uclass information about a bootmethod + * + * This is attached to each device in the bootmethod uclass and accessible via + * dev_get_uclass_plat(dev) + * + * @bootflows: List of available bootflows for this bootmethod + */ +struct bootmethod_uc_plat { + struct list_head bootflow_head; +}; + +extern struct bootflow_cmds g_bootflow_cmds; + +/** + * struct bootflow - information about a bootflow + * + * This is connected into two separate linked lists: + * + * bm_sibling - links all bootflows in the same bootmethod + * glob_sibling - links all bootflows in all bootmethods + * + * @bm_node: Points to siblings in the same bootmethod + * @glob_node: Points to siblings in the global list (all bootmethod) + * @dev: Bootmethod device which produced this bootflow + * @blk: Block device which contains this bootflow, NULL if this is a network + * device + * @seq: Sequence number of bootflow within its bootmethod, typically the + * partition number (0...) + * @name: Name of bootflow (allocated) + * @type: Bootflow type (enum bootflow_type_t) + * @state: Current state (enum bootflow_state_t) + * @part: Partition number + * @subdir: Subdirectory to fetch files from (with trailing /), or NULL if none + * @fname: Filename of bootflow file (allocated) + * @buf: Bootflow file contents (allocated) + * @size: Size of bootflow file in bytes + * @err: Error number received (0 if OK) + */ +struct bootflow { + struct list_head bm_node; + struct list_head glob_node; + struct udevice *dev; + struct udevice *blk; + int seq; + char *name; + enum bootflow_type_t type; + enum bootflow_state_t state; + int part; + char *subdir; + char *fname; + char *buf; + int size; + int err; +}; + +/** + * enum bootflow_flags_t - flags for the bootflow + * + * @BOOTFLOWF_FIXED: Only used fixed/internal media + * @BOOTFLOWF_SHOW: Show each bootmethod before scanning it + * @BOOTFLOWF_ALL: Return bootflows with errors as well + */ +enum bootflow_flags_t { + BOOTFLOWF_FIXED = 1 << 0, + BOOTFLOWF_SHOW = 1 << 1, + BOOTFLOWF_ALL = 1 << 2, +}; + +/** + * struct bootmethod_iter - state for iterating through bootflows + * + * @flags: Flags to use (see enum bootflow_flags_t) + * @dev: Current bootmethod + * @seq: Current sequence number within that bootmethod (determines partition + * number, for example) + */ +struct bootmethod_iter { + int flags; + struct udevice *dev; + int seq; +}; + +/** + * struct bootmethod_ops - Operations for the Platform Controller Hub + * + * Consider using ioctl() to add rarely used or driver-specific operations. + */ +struct bootmethod_ops { + /** + * get_bootflow() - get a bootflow + * + * @dev: Bootflow device to check + * @seq: Sequence number of bootflow to read (0 for first) + * @bflow: Returns bootflow if found + * @return 0 if OK, -ESHUTDOWN if there are no more bootflows on this + * device, -ENOSYS if this device doesn't support bootflows, + * other -ve value on other error + */ + int (*get_bootflow)(struct udevice *dev, int seq, + struct bootflow *bflow); +}; + +#define bootmethod_get_ops(dev) ((struct bootmethod_ops *)(dev)->driver->ops) + +/** + * bootmethod_get_bootflow() - get a bootflow + * + * @dev: Bootflow device to check + * @seq: Sequence number of bootflow to read (0 for first) + * @bflow: Returns bootflow if found + * @return 0 if OK, -ESHUTDOWN if there are no more bootflows on this device, + * -ENOSYS if this device doesn't support bootflows, other -ve value on + * other error + */ +int bootmethod_get_bootflow(struct udevice *dev, int seq, + struct bootflow *bflow); + +/** + * bootmethod_scan_first_bootflow() - find the first bootflow + * + * This works through the available bootmethod devices until it finds one that + * can supply a bootflow. It then returns that + * + * If @flags includes BOOTFLOWF_ALL then bootflows with errors are returned too + * + * @iter: Place to store private info (inited by this call) + * @flags: Flags for bootmethod (enum bootflow_flags_t) + * @bflow: Place to put the bootflow if found + * @return 0 if found, -ESHUTDOWN if no more bootflows, other -ve on error + */ +int bootmethod_scan_first_bootflow(struct bootmethod_iter *iter, int flags, + struct bootflow *bflow); + +/** + * bootmethod_scan_next_bootflow() - find the next bootflow + * + * This works through the available bootmethod devices until it finds one that + * can supply a bootflow. It then returns that bootflow + * + * @iter: Private info (as set up by bootmethod_scan_first_bootflow()) + * @bflow: Place to put the bootflow if found + * @return 0 if found, -ESHUTDOWN if no more bootflows, -ve on error + */ +int bootmethod_scan_next_bootflow(struct bootmethod_iter *iter, + struct bootflow *bflow); + +/** + * bootmethod_bind() - Bind a new named bootmethod device + * + * @parent: Parent of the new device + * @drv_name: Driver name to use for the bootmethod device + * @name: Name for the device (parent name is prepended) + * @devp: the new device (which has not been probed) + */ +int bootmethod_bind(struct udevice *parent, const char *drv_name, + const char *name, struct udevice **devp); + +/** + * bootmethod_find_in_blk() - Find a bootmethod in a block device + * + * @dev: Bootflow device associated with this block device + * @blk: Block device to search + * @seq: Sequence number within block device, used as the partition number, + * after adding 1 + * @bflow: Returns bootflow if found + * @return 0 if found, -ESHUTDOWN if no more bootflows, other -ve on error + */ +int bootmethod_find_in_blk(struct udevice *dev, struct udevice *blk, int seq, + struct bootflow *bflow); + +/** + * bootmethod_list() - List all available bootmethods + * + * @probe: true to probe devices, false to leave them as is + */ +void bootmethod_list(bool probe); + +/** + * bootmethod_state_get_name() - Get the name of a bootflow state + * + * @state: State to check + * @return name, or "?" if invalid + */ +const char *bootmethod_state_get_name(enum bootflow_state_t state); + +/** + * bootmethod_type_get_name() - Get the name of a bootflow state + * + * @type: Type to check + * @return name, or "?" if invalid + */ +const char *bootmethod_type_get_name(enum bootflow_type_t type); + +/** + * bootmethod_get_state() - Get the (single) state for the bootmethod system + * + * The state holds a global list of all bootflows that have been found. + * + * @return 0 if OK, -ve if the uclass does not exist + */ +int bootmethod_get_state(struct bootflow_state **statep); + +/** + * bootmethod_clear_bootflows() - Clear bootflows from a bootmethod + * + * Each bootmethod maintains a list of discovered bootflows. This provides a + * way to clear it. These bootflows are removed from the global list too. + * + * @dev: bootmethod device to update + */ +void bootmethod_clear_bootflows(struct udevice *dev); + +/** + * bootmethod_clear_glob() - Clear the global list of bootflows + * + * This removes all bootflows globally and across all bootmethods. + */ +void bootmethod_clear_glob(void); + +/** + * bootmethod_add_bootflow() - Add a bootflow to the bootmethod's list + * + * All fields in @bflow must be set up. Note that @bflow->dev is used to add the + * bootflow to that device. + * + * @dev: Bootmethod device to add to + * @bflow: Bootflow to add. Note that fields within bflow must be allocated + * since this function takes over ownership of these. This functions makes + * a copy of @bflow itself (without allocating its fields again), so the + * caller must dispose of the memory used by the @bflow pointer itself + * @return 0 if OK, -ENOMEM if out of memory + */ +int bootmethod_add_bootflow(struct bootflow *bflow); + +/** + * bootmethod_first_bootflow() - Get the first bootflow from a bootmethod + * + * Returns the first bootflow attached to a bootmethod + * + * @dev: bootmethod device + * @bflowp: Returns a pointer to the bootflow + * @return 0 if found, -ENOENT if there are no bootflows + */ +int bootmethod_first_bootflow(struct udevice *dev, struct bootflow **bflowp); + +/** + * bootmethod_next_bootflow() - Get the next bootflow from a bootmethod + * + * Returns the next bootflow attached to a bootmethod + * + * @bflowp: On entry, the last bootflow returned , e.g. from + * bootmethod_first_bootflow() + * @return 0 if found, -ENOENT if there are no more bootflows + */ +int bootmethod_next_bootflow(struct bootflow **bflowp); + +/** + * bootflow_first_glob() - Get the first bootflow from the global list + * + * Returns the first bootflow in the global list, no matter what bootflow it is + * attached to + * + * @bflowp: Returns a pointer to the bootflow + * @return 0 if found, -ENOENT if there are no bootflows + */ +int bootflow_first_glob(struct bootflow **bflowp); + +/** + * bootflow_next_glob() - Get the next bootflow from the global list + * + * Returns the next bootflow in the global list, no matter what bootflow it is + * attached to + * + * @bflowp: On entry, the last bootflow returned , e.g. from + * bootflow_first_glob() + * @return 0 if found, -ENOENT if there are no more bootflows + */ +int bootflow_next_glob(struct bootflow **bflowp); + +/** + * bootflow_free() - Free memory used by a bootflow + * + * This frees fields within @bflow, but not the @bflow pointer itself + */ +void bootflow_free(struct bootflow *bflow); + +/** + * bootflow_boot() - boot a bootflow + * + * @bflow: Bootflow to boot + * @return -EPROTO if bootflow has not been loaded, -ENOSYS if the bootflow + * type is not supported, -EFAULT if the boot returned without an error + * when we are expecting it to boot + */ +int bootflow_boot(struct bootflow *bflow); + +/** + * bootmethod_setup_for_dev() - Bind a new bootmethod device + * + * Creates a bootmethod device as a child of @parent. This should be called from + * the driver's bind() method or its uclass' post_bind() method. + * + * @parent: Parent device (e.g. MMC or Ethernet) + * @drv_name: Name of bootmethod driver to bind + * @return 0 if OK, -ve on error + */ +int bootmethod_setup_for_dev(struct udevice *parent, const char *drv_name); + +#endif diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index e7edd409f30..116d2f02f26 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -38,6 +38,7 @@ enum uclass_id { UCLASS_AXI, /* AXI bus */ UCLASS_BLK, /* Block device */ UCLASS_BOOTCOUNT, /* Bootcount backing store */ + UCLASS_BOOTMETHOD, /* Bootmethod for locating an OS to boot*/ UCLASS_BUTTON, /* Button */ UCLASS_CACHE, /* Cache controller */ UCLASS_CLK, /* Clock source, e.g. used by peripherals */ -- 2.33.0.rc1.237.g0d66db33f3-goog