[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