The 'bootstd' device provides the central information about U-Boot
standard boot.

Add a uclass for bootstd and the various helpers needed to make it
work. Also add a binding file.

Signed-off-by: Simon Glass <s...@chromium.org>
---

Changes in v5:
- Disable bootstd for EFI app builds, since they use dm_scan_other()

Changes in v4:
- NULL-terminate the default prefix list
- Use new Return style in function comments

Changes in v3:
- Add CONFIG_BOOTSTD_FULL to enable the full feature set
- Support bootdev-order and filename-prefixes only with CONFIG_BOOTSTD_FULL

 MAINTAINERS                          |   2 +
 boot/Kconfig                         |  34 ++++++
 boot/Makefile                        |   3 +
 boot/bootstd-uclass.c                | 152 +++++++++++++++++++++++++++
 configs/efi-x86_app32_defconfig      |   1 +
 configs/efi-x86_app64_defconfig      |   1 +
 doc/device-tree-bindings/bootstd.txt |  28 +++++
 include/bootstd.h                    |  80 ++++++++++++++
 include/dm/uclass-id.h               |   1 +
 9 files changed, 302 insertions(+)
 create mode 100644 boot/bootstd-uclass.c
 create mode 100644 doc/device-tree-bindings/bootstd.txt
 create mode 100644 include/bootstd.h

diff --git a/MAINTAINERS b/MAINTAINERS
index d6d45573fdc..dd099d6240f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -696,6 +696,8 @@ F:  tools/binman/
 BOOTDEVICE
 M:     Simon Glass <s...@chromium.org>
 S:     Maintained
+F:     boot/bootstd.c
+F:     include/bootstd.h
 F:     include/bootflow.h
 
 BTRFS
diff --git a/boot/Kconfig b/boot/Kconfig
index ec5b956490d..9faa55a5418 100644
--- a/boot/Kconfig
+++ b/boot/Kconfig
@@ -292,6 +292,40 @@ endif # SPL
 
 endif # FIT
 
+config BOOTSTD
+       bool "Standard boot support"
+       default y
+       depends on DM && OF_CONTROL && BLK
+       help
+         U-Boot supports a standard way of locating something to boot,
+         typically an Operating System such as Linux, provided by a distro such
+         as Arch Linux or Debian. Enable this to support iterating through
+         available bootdevs and using bootmeths to find bootflows suitable for
+         booting.
+
+         Standard boot is not a standard way of booting, just a framework
+         within U-Boot for supporting all the different ways that exist.
+
+         Terminology:
+
+           - bootdev - a device which can hold a distro (e.g. MMC)
+           - bootmeth - a method to scan a bootdev to find bootflows (owned by
+               U-Boot)
+           - bootflow - a description of how to boot (owned by the distro)
+
+config BOOTSTD_FULL
+       bool "Enhanced features for standard boot"
+       default y if SANDBOX
+       help
+         This enables various useful features for standard boot, which are not
+         essential for operation:
+
+         - bootdev, bootmeth commands
+         - extra features in the bootflow command
+         - support for selecting the ordering of bootmeths ("bootmeth order")
+         - support for selecting the ordering of bootdevs using the devicetree
+           as well as the "boot_targets" environment variable
+
 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 1b99e6ee33f..21dcf6a6056 100644
--- a/boot/Makefile
+++ b/boot/Makefile
@@ -18,6 +18,9 @@ endif
 obj-y += image.o image-board.o
 obj-$(CONFIG_ANDROID_AB) += android_ab.o
 obj-$(CONFIG_ANDROID_BOOT_IMAGE) += image-android.o image-android-dt.o
+
+obj-$(CONFIG_$(SPL_TPL_)BOOTSTD) += bootstd-uclass.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/bootstd-uclass.c b/boot/bootstd-uclass.c
new file mode 100644
index 00000000000..615cd89bf0b
--- /dev/null
+++ b/boot/bootstd-uclass.c
@@ -0,0 +1,152 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Uclass implementation for standard boot
+ *
+ * Copyright 2021 Google LLC
+ * Written by Simon Glass <s...@chromium.org>
+ */
+
+#include <common.h>
+#include <bootflow.h>
+#include <bootstd.h>
+#include <dm.h>
+#include <log.h>
+#include <malloc.h>
+#include <dm/device-internal.h>
+#include <dm/lists.h>
+#include <dm/read.h>
+#include <dm/uclass-internal.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/* These are used if filename-prefixes is not present */
+const char *const default_prefixes[] = {"/", "/boot/", NULL};
+
+static int bootstd_of_to_plat(struct udevice *dev)
+{
+       struct bootstd_priv *priv = dev_get_priv(dev);
+       int ret;
+
+       if (IS_ENABLED(CONFIG_BOOTSTD_FULL)) {
+               /* Don't check errors since livetree and flattree are different 
*/
+               ret = dev_read_string_list(dev, "filename-prefixes",
+                                          &priv->prefixes);
+               dev_read_string_list(dev, "bootdev-order",
+                                    &priv->bootdev_order);
+       }
+
+       return 0;
+}
+
+static void bootstd_clear_glob_(struct bootstd_priv *priv)
+{
+       while (!list_empty(&priv->glob_head)) {
+               struct bootflow *bflow;
+
+               bflow = list_first_entry(&priv->glob_head, struct bootflow,
+                                        glob_node);
+               /* add later bootflow_remove(bflow); */
+       }
+}
+
+void bootstd_clear_glob(void)
+{
+       struct bootstd_priv *std;
+
+       if (bootstd_get_priv(&std))
+               return;
+
+       bootstd_clear_glob_(std);
+}
+
+static int bootstd_remove(struct udevice *dev)
+{
+       struct bootstd_priv *priv = dev_get_priv(dev);
+
+       free(priv->prefixes);
+       free(priv->bootdev_order);
+       bootstd_clear_glob_(priv);
+
+       return 0;
+}
+
+const char *const *const bootstd_get_bootdev_order(struct udevice *dev)
+{
+       struct bootstd_priv *std = dev_get_priv(dev);
+
+       return std->bootdev_order;
+}
+
+const char *const *const bootstd_get_prefixes(struct udevice *dev)
+{
+       struct bootstd_priv *std = dev_get_priv(dev);
+
+       return std->prefixes ? std->prefixes : default_prefixes;
+}
+
+int bootstd_get_priv(struct bootstd_priv **stdp)
+{
+       struct udevice *dev;
+       int ret;
+
+       ret = uclass_first_device_err(UCLASS_BOOTSTD, &dev);
+       if (ret)
+               return ret;
+       *stdp = dev_get_priv(dev);
+
+       return 0;
+}
+
+static int bootstd_probe(struct udevice *dev)
+{
+       struct bootstd_priv *std = dev_get_priv(dev);
+
+       INIT_LIST_HEAD(&std->glob_head);
+
+       return 0;
+}
+
+/* For now, bind the boormethod device if none are found in the devicetree */
+int dm_scan_other(bool pre_reloc_only)
+{
+       struct udevice *bootstd;
+       int ret;
+
+       /* These are not needed before relocation */
+       if (!(gd->flags & GD_FLG_RELOC))
+               return 0;
+
+       /* Create a bootstd device if needed */
+       uclass_find_first_device(UCLASS_BOOTSTD, &bootstd);
+       if (!bootstd) {
+               ret = device_bind_driver(gd->dm_root, "bootstd_drv", "bootstd",
+                                        &bootstd);
+               if (ret)
+                       return log_msg_ret("bootstd", ret);
+       }
+
+       return 0;
+}
+
+static const struct udevice_id bootstd_ids[] = {
+       { .compatible = "u-boot,boot-std" },
+       { }
+};
+
+U_BOOT_DRIVER(bootstd_drv) = {
+       .id             = UCLASS_BOOTSTD,
+       .name           = "bootstd_drv",
+       .of_to_plat     = bootstd_of_to_plat,
+       .probe          = bootstd_probe,
+       .remove         = bootstd_remove,
+       .of_match       = bootstd_ids,
+       .priv_auto      = sizeof(struct bootstd_priv),
+};
+
+UCLASS_DRIVER(bootstd) = {
+       .id             = UCLASS_BOOTSTD,
+       .name           = "bootstd",
+#if CONFIG_IS_ENABLED(OF_REAL)
+       .post_bind      = dm_scan_fdt_dev,
+#endif
+};
diff --git a/configs/efi-x86_app32_defconfig b/configs/efi-x86_app32_defconfig
index 228643a1bd6..7a723c136c2 100644
--- a/configs/efi-x86_app32_defconfig
+++ b/configs/efi-x86_app32_defconfig
@@ -8,6 +8,7 @@ CONFIG_VENDOR_EFI=y
 CONFIG_TARGET_EFI_APP32=y
 CONFIG_DEBUG_UART=y
 CONFIG_FIT=y
+# CONFIG_BOOTSTD is not set
 CONFIG_SHOW_BOOT_PROGRESS=y
 CONFIG_USE_BOOTARGS=y
 CONFIG_BOOTARGS="root=/dev/sdb3 init=/sbin/init rootwait ro"
diff --git a/configs/efi-x86_app64_defconfig b/configs/efi-x86_app64_defconfig
index 1ed2f130509..98f91d81164 100644
--- a/configs/efi-x86_app64_defconfig
+++ b/configs/efi-x86_app64_defconfig
@@ -8,6 +8,7 @@ CONFIG_VENDOR_EFI=y
 CONFIG_TARGET_EFI_APP64=y
 CONFIG_DEBUG_UART=y
 CONFIG_FIT=y
+# CONFIG_BOOTSTD is not set
 CONFIG_SHOW_BOOT_PROGRESS=y
 CONFIG_USE_BOOTARGS=y
 CONFIG_BOOTARGS="root=/dev/sdb3 init=/sbin/init rootwait ro"
diff --git a/doc/device-tree-bindings/bootstd.txt 
b/doc/device-tree-bindings/bootstd.txt
new file mode 100644
index 00000000000..f048b9dd327
--- /dev/null
+++ b/doc/device-tree-bindings/bootstd.txt
@@ -0,0 +1,28 @@
+U-Boot standard boot device (bootstd)
+=====================================
+
+This is the controlling device for U-Boot standard boot, providing a way to
+boot operating systems in a way that can be controlled by distros.
+
+Required properties:
+
+compatible: "u-boot,boot-std"
+
+Optional properties:
+
+filename-prefixes:
+   List of strings, each a directory to search for bootflow files
+
+bootdev-order:
+   List of bootdevs to check for bootflows, each a bootdev label (the media
+   uclass followed by the numeric sequence number of the media device)
+
+
+Example:
+
+       bootstd {
+               compatible = "u-boot,boot-std";
+
+               filename-prefixes = "/", "/boot/";
+               bootdev-order = "mmc2", "mmc1";
+       };
diff --git a/include/bootstd.h b/include/bootstd.h
new file mode 100644
index 00000000000..b002365f4f0
--- /dev/null
+++ b/include/bootstd.h
@@ -0,0 +1,80 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Standard U-Boot boot framework
+ *
+ * Copyright 2021 Google LLC
+ * Written by Simon Glass <s...@chromium.org>
+ */
+
+#ifndef __bootstd_h
+#define __bootstd_h
+
+struct udevice;
+
+/**
+ * struct bootstd_priv - priv data for the bootstd driver
+ *
+ * This is attached to the (only) bootstd device, so there is only one instance
+ * of this struct. It provides overall information about bootdevs and 
bootflows.
+ *
+ * @prefixes: NULL-terminated list of prefixes to use for bootflow filenames,
+ *     e.g. "/", "/boot/"; NULL if none
+ * @bootdev_order: Order to use for bootdevs (or NULL if none), with each item
+ *     being a bootdev label, e.g. "mmc2", "mmc1";
+ * @cur_bootdev: Currently selected bootdev (for commands)
+ * @cur_bootflow: Currently selected bootflow (for commands)
+ * @glob_head: Head for the global list of all bootflows across all bootdevs
+ * @bootmeth_count: Number of bootmeth devices in @bootmeth_order
+ * @bootmeth_order: List of bootmeth devices to use, in order, NULL-terminated
+ */
+struct bootstd_priv {
+       const char **prefixes;
+       const char **bootdev_order;
+       struct udevice *cur_bootdev;
+       struct bootflow *cur_bootflow;
+       struct list_head glob_head;
+       int bootmeth_count;
+       struct udevice **bootmeth_order;
+};
+
+/**
+ * bootstd_get_bootdev_order() - Get the boot-order list
+ *
+ * This reads the boot order, e.g. {"mmc0", "mmc2", NULL}
+ *
+ * The list is alloced by the bootstd driver so should not be freed. That is 
the
+ * reason for all the const stuff in the function signature
+ *
+ * Return: list of string points, terminated by NULL; or NULL if no boot order
+ */
+const char *const *const bootstd_get_bootdev_order(struct udevice *dev);
+
+/**
+ * bootstd_get_prefixes() - Get the filename-prefixes list
+ *
+ * This reads the prefixes, e.g. {"/", "/bpot", NULL}
+ *
+ * The list is alloced by the bootstd driver so should not be freed. That is 
the
+ * reason for all the const stuff in the function signature
+ *
+ * Return: list of string points, terminated by NULL; or NULL if no boot order
+ */
+const char *const *const bootstd_get_prefixes(struct udevice *dev);
+
+/**
+ * bootstd_get_priv() - Get the (single) state for the bootstd 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 bootstd_get_priv(struct bootstd_priv **stdp);
+
+/**
+ * bootstd_clear_glob() - Clear the global list of bootflows
+ *
+ * This removes all bootflows globally and across all bootdevs.
+ */
+void bootstd_clear_glob(void);
+
+#endif
diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h
index 0e26e1d1382..cece0626a11 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_BOOTSTD,         /* Standard boot driver */
        UCLASS_BUTTON,          /* Button */
        UCLASS_CACHE,           /* Cache controller */
        UCLASS_CLK,             /* Clock source, e.g. used by peripherals */
-- 
2.36.0.rc2.479.g8af0fa9b8e-goog

Reply via email to