[PATCH 19/28] bootmethod: Add the uclass and core implementation
Simon Glass
sjg at chromium.org
Thu Aug 19 05:45:52 CEST 2021
Add a uclass for bootmethod and the various helpers needed to make it
work.
Signed-off-by: Simon Glass <sjg at chromium.org>
---
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 <sjg at chromium.org>
S: Maintained
F: tools/binman/
+BOOTMETHOD
+M: Simon Glass <sjg at chromium.org>
+S: Maintained
+F: boot/bootmethod.c
+F: include/bootmethod.h
+
BTRFS
M: Marek Behun <marek.behun at nic.cz>
R: Qu Wenruo <wqu at suse.com>
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 <sjg at chromium.org>
+ */
+
+#include <common.h>
+#include <blk.h>
+#include <bootmethod.h>
+#include <dm.h>
+#include <fs.h>
+#include <log.h>
+#include <malloc.h>
+#include <part.h>
+#include <dm/lists.h>
+#include <dm/uclass-internal.h>
+
+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 <sjg at chromium.org>
+ */
+
+#ifndef __bootmethod_h
+#define __bootmethod_h
+
+#include <linux/list.h>
+
+/**
+ * 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
More information about the U-Boot
mailing list