All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v5 00/34] Initial implementation of standard boot
@ 2022-04-25  5:30 Simon Glass
  2022-04-25  5:30 ` [PATCH v5 01/34] lib: Move string tests to the string module Simon Glass
                   ` (31 more replies)
  0 siblings, 32 replies; 36+ messages in thread
From: Simon Glass @ 2022-04-25  5:30 UTC (permalink / raw)
  To: U-Boot Mailing List
  Cc: Dennis Gilmore, Lukas Auer, Daniel Schwierzeck, Tom Rini,
	Michal Simek, Ilias Apalodimas, Steffen Jaeckel, Simon Glass,
	Jaehoon Chung, Marek Vasut, Pavel Herrmann, Peng Fan


The bootflow feature provide a built-in way for U-Boot to automatically
boot an Operating System without custom scripting and other customisation.
This is called 'standard boot' since it provides a standard way for
U-Boot to boot a distro, without scripting.

It introduces the following concepts:

   - bootdev - a device which can hold a distro
   - 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)

This series provides an implementation of these, enabled to scan for
bootflows from MMC, USB and Ethernet. It supports the existing distro
boot as well as the EFI loader flow (bootefi/bootmgr). It works
similiarly to the existing script-based approach, but is native to
U-Boot.

With this we can boot on a Raspberry Pi 3 with just one command:

   bootflow scan -lb

which means to scan, listing (-l) each bootflow and trying to boot each
one (-b). The final patch shows this.

With a standard way to identify boot devices, booting become easier. It
also should be possible to support U-Boot scripts, for backwards
compatibility only.

This series relies on the PXE clean-up series, posted here:

   https://patchwork.ozlabs.org/project/uboot/list/?series=267078

For documentation, see the 'doc' patch.

For version 2, a new naming scheme is used as above:

   - bootdev is used instead of bootdevice, because 'device' is overused,
       is everywhere in U-Boot, can be confused with udevice
   - bootmeth - because 'method' is too vanilla, appears 1300 times in
       U-Boot

Also in version 2, drivers are introduced for the boot methods, to make
it more extensible. Booting a custom OS is simply a matter of creating a
bootmeth for it and implementing the read_file() and boot() methods.

Version 4 makes some minor improvements and leaves out the RFC patch for
rpi conversion, in the hope of getting the base support applied sooner
rather than later.

The design is described in these two documents:

https://drive.google.com/file/d/1ggW0KJpUOR__vBkj3l61L2dav4ZkNC12/view?usp=sharing

https://drive.google.com/file/d/1kTrflO9vvGlKp-ZH_jlgb9TY3WYG6FF9/view?usp=sharing

Overall size increment on rpi_3_32b is 6.75KB.

The series is available at u-boot-dm/bme-working

Sample log on rpi_3_32b:

U-Boot 2021.10-rc2-00043-gccd453aa918-dirty (Aug 28 2021 - 13:58:46 -0600)

DRAM:  992 MiB
RPI 3 Model B (0xa22082)
MMC:   mmc@7e202000: 0, sdhci@7e300000: 1
Loading Environment from FAT... Unable to read "uboot.env" from mmc0:1... In:    serial
Out:   vidconsole
Err:   vidconsole
Net:   No ethernet found.
starting USB...
Bus usb@7e980000: USB DWC2
scanning bus usb@7e980000 for devices... usb_kbd usb_kbd: Timeout poll on interrupt endpoint
Failed to get keyboard state from device 0c40:8000
4 USB Device(s) found
       scanning usb for storage devices... 0 Storage Device(s) found
Hit any key to stop autoboot:  0
Scanning for bootflows in all bootmethods
Seq  Type         State   Uclass    Part  Name                      Filename
---  -----------  ------  --------  ----  ------------------------  ----------------
Scanning bootmethod 'mmc@7e202000.bootmethod':
  0  efi-loader   loaded  mmc          1  mmc@7e202000.bootmethod.p efi/boot/bootarm.efi
** Booting bootflow 'mmc@7e202000.bootmethod.part_1'
Scanning disk mmc@7e202000.blk...
** Unrecognized filesystem type **
Card did not respond to voltage select! : -110
Scanning disk sdhci@7e300000.blk...
Disk sdhci@7e300000.blk not ready
Found 4 disks
No EFI system partition
Booting /efi\boot
Waiting for Ethernet connection... done.

      Fedora (5.11.12-300.fc34.armv7hl) 34 (Workstation Edition)
      UEFI Firmware Settings

      Use the ▲ and ▼ keys to change the selection.
      Press 'e' to edit the selected item, or 'c' for a command prompt. Press Escape to return to the previous menu.
   The selected entry will be started automatically in 0s.

Changes in v5:
- Add a Kconfig to select bootstd by default
- Disable bootstd for EFI app builds, since they use dm_scan_other()
- Rebase to master

Changes in v4:
- Add a new fs_type field for use with sandbox
- Add a test also
- Add new patch to correct comment formatting to avoid sphinx errors
- Add new patch with a function to set the filesystem type
- Allow use without a block device
- Correct bug in this function and add test
- Correct the commit tag
- Fix 'a' typo
- Improve debugging in efiload_read_file()
- Make CMD_BOOTSTD_FULL depend on BOOTSTD_FULL
- NULL-terminate the default prefix list
- Reword the commit message
- Show the bootmeth name when booting
- Split out the file setup into into its own function
- Update docs for typos that have been fixed
- Update tests for code changes
- Update the function comment to clarify its intert / limitations
- Use new Return style in function comments
- Use new sphynx Return style
- Warn when trying to use the full bootflow command but it is not enabled

Changes in v3:
- Add CONFIG_BOOTSTD_FULL to enable the full feature set
- Add a log category
- Add bootdev_get_from_blk() function
- Add new patch to rename uclass_get_by_name_len()
- Add new patch to support finding a uclass device by partial name
- Add some more common functions
- Add some tests, including one for 'abc123def456'
- Add support for script boot
- Add tests for the "bootmeths" env var
- Adjust for new blk_find_device() function
- Align the EFI load address
- Change the function to return a pointer to the first digit
- Explain why it is OK to use "?" as an unknown bootflow state
- Mention bootflow_state[] in enum bootflow_state_t
- Move bootdev ordering into the uclass
- Move bootmeth ordering into the uclass
- Move bootmeth/bootdev ordering into the uclass
- Point to the header file for bootflow_state[] docs
- Put some features behind CONFIG_BOOTFLOW_FULL
- Support "bootmeths" env var
- Support bootdev-order and filename-prefixes only with CONFIG_BOOTSTD_FULL
- Update bootdev_test_order() for boot_targets env var
- Update docs for "bootmeths" and "boot_targets" env vars
- Update for the new trailing_strtoln_end()
- Update test condition to use CMD_BOOTFLOW
- Update tests for the new 'bootmeth order' syntax
- Use a short name when BOOTSTD_FULL is not enabled
- Use blk_find_device() to find the block device for a media device
- Use common bootmeth functions
- Use new bootmeth_try_file() function
- Use separate Kconfig options for each command

Simon Glass (34):
  lib: Move string tests to the string module
  test: Add tests for trailing_strtol()
  lib: Correct comment formatting to avoid sphinx problems
  lib: Fix a few bugs in trailing_strtoln()
  lib: Add a way to find the postiion of a trailing number
  dm: core: Rename and fix uclass_get_by_name_len()
  dm: core: Allow finding a uclass device by partial name
  test: fastboot: Avoid using mmc1
  test: dm: Restart USB before assuming it is stopped
  dm: blk: Add a function to return the device type
  fs: Add a function to set the filesystem type
  bootstd: Add the concept of a bootflow
  bootstd: Add the bootstd uclass and core implementation
  bootstd: Add the bootdev uclass
  bootstd: Add the bootmeth uclass and helpers
  bootstd: Add support for bootflows
  bootstd: Add a bootdev command
  bootstd: Add a bootflow command
  bootstd: Add a bootmeth command
  bootstd: Add an implementation of distro boot
  bootstd: mmc: Add a bootdev driver
  bootstd: ethernet: Add a bootdev driver
  bootstd: Add an implementation of distro PXE boot
  bootstd: Add an implementation of EFI boot
  bootstd: Add a system bootdev for strange boot methods
  bootstd: Add an implementation of EFI bootmgr
  bootstd: Add a sandbox bootmeth driver
  bootstd: sandbox: Add a hostfs bootdev
  bootstd: Add an implementation of script boot
  bootstd: usb: Add a bootdev driver
  bootstd: Add tests for bootstd including all uclasses
  bootstd: Add setup for the bootflow tests
  bootstd: doc: Add documentation
  bootstd: Provide a default command

 MAINTAINERS                           |  21 +
 arch/sandbox/dts/sandbox.dts          |   4 +
 arch/sandbox/dts/sandbox.dtsi         |   5 +
 arch/sandbox/dts/test.dts             |  18 +
 boot/Kconfig                          | 115 ++++-
 boot/Makefile                         |  15 +
 boot/bootdev-uclass.c                 | 649 ++++++++++++++++++++++++++
 boot/bootflow.c                       | 411 ++++++++++++++++
 boot/bootmeth-uclass.c                | 333 +++++++++++++
 boot/bootmeth_distro.c                | 143 ++++++
 boot/bootmeth_efi.c                   | 188 ++++++++
 boot/bootmeth_efi_mgr.c               |  86 ++++
 boot/bootmeth_pxe.c                   | 186 ++++++++
 boot/bootmeth_sandbox.c               |  69 +++
 boot/bootmeth_script.c                | 139 ++++++
 boot/bootstd-uclass.c                 | 183 ++++++++
 boot/system_bootdev.c                 |  66 +++
 cmd/Kconfig                           |  39 ++
 cmd/Makefile                          |   3 +
 cmd/bootdev.c                         | 120 +++++
 cmd/bootflow.c                        | 404 ++++++++++++++++
 cmd/bootmeth.c                        | 113 +++++
 common/usb_storage.c                  |  11 +
 configs/efi-x86_app32_defconfig       |   1 +
 configs/efi-x86_app64_defconfig       |   1 +
 configs/rcar3_salvator-x_defconfig    |   1 +
 configs/sandbox_defconfig             |   3 +-
 configs/sandbox_flattree_defconfig    |   3 +-
 configs/tbs2910_defconfig             |   1 +
 doc/develop/bootstd.rst               | 638 +++++++++++++++++++++++++
 doc/develop/distro.rst                |   3 +
 doc/develop/index.rst                 |   1 +
 doc/device-tree-bindings/bootdev.txt  |  26 ++
 doc/device-tree-bindings/bootmeth.txt |  31 ++
 doc/device-tree-bindings/bootstd.txt  |  36 ++
 doc/usage/cmd/bootdev.rst             | 135 ++++++
 doc/usage/cmd/bootflow.rst            | 427 +++++++++++++++++
 doc/usage/cmd/bootmeth.rst            | 108 +++++
 doc/usage/index.rst                   |   3 +
 drivers/block/blk-uclass.c            |   7 +
 drivers/core/uclass.c                 |  20 +-
 drivers/mmc/Makefile                  |   5 +
 drivers/mmc/mmc-uclass.c              |  23 +
 drivers/mmc/mmc_bootdev.c             |  62 +++
 drivers/usb/host/Makefile             |   4 +
 drivers/usb/host/usb_bootdev.c        |  61 +++
 fs/Kconfig                            |   2 +
 fs/fs.c                               |   5 +
 fs/sandbox/Kconfig                    |   2 +
 fs/sandbox/Makefile                   |   1 +
 fs/sandbox/host_bootdev.c             |  56 +++
 include/blk.h                         |   8 +
 include/bootdev.h                     | 275 +++++++++++
 include/bootflow.h                    | 310 ++++++++++++
 include/bootmeth.h                    | 234 ++++++++++
 include/bootstd.h                     |  80 ++++
 include/distro.h                      |  24 +
 include/dm/device.h                   |   2 +-
 include/dm/uclass-id.h                |   3 +
 include/dm/uclass-internal.h          |  16 +
 include/dm/uclass.h                   |   6 +-
 include/env_callback.h                |   7 +
 include/fs.h                          |  11 +
 include/mmc.h                         |  12 +-
 include/test/suites.h                 |   2 +
 include/vsprintf.h                    | 117 +++--
 lib/strto.c                           |  23 +-
 net/Kconfig                           |   9 +
 net/Makefile                          |   1 +
 net/eth-uclass.c                      |   8 +
 net/eth_bootdev.c                     | 101 ++++
 test/Makefile                         |   1 +
 test/boot/Makefile                    |   5 +
 test/boot/bootdev.c                   | 223 +++++++++
 test/boot/bootflow.c                  | 400 ++++++++++++++++
 test/boot/bootmeth.c                  | 122 +++++
 test/boot/bootstd_common.c            |  35 ++
 test/boot/bootstd_common.h            |  27 ++
 test/cmd_ut.c                         |   7 +
 test/dm/blk.c                         |   6 +
 test/dm/core.c                        |  17 +
 test/dm/fastboot.c                    |   4 +-
 test/print_ut.c                       |  40 --
 test/py/tests/bootstd/mmc1.img.xz     | Bin 0 -> 4448 bytes
 test/py/tests/test_ut.py              | 103 ++++
 test/str_ut.c                         |  72 +++
 86 files changed, 7188 insertions(+), 110 deletions(-)
 create mode 100644 boot/bootdev-uclass.c
 create mode 100644 boot/bootflow.c
 create mode 100644 boot/bootmeth-uclass.c
 create mode 100644 boot/bootmeth_distro.c
 create mode 100644 boot/bootmeth_efi.c
 create mode 100644 boot/bootmeth_efi_mgr.c
 create mode 100644 boot/bootmeth_pxe.c
 create mode 100644 boot/bootmeth_sandbox.c
 create mode 100644 boot/bootmeth_script.c
 create mode 100644 boot/bootstd-uclass.c
 create mode 100644 boot/system_bootdev.c
 create mode 100644 cmd/bootdev.c
 create mode 100644 cmd/bootflow.c
 create mode 100644 cmd/bootmeth.c
 create mode 100644 doc/develop/bootstd.rst
 create mode 100644 doc/device-tree-bindings/bootdev.txt
 create mode 100644 doc/device-tree-bindings/bootmeth.txt
 create mode 100644 doc/device-tree-bindings/bootstd.txt
 create mode 100644 doc/usage/cmd/bootdev.rst
 create mode 100644 doc/usage/cmd/bootflow.rst
 create mode 100644 doc/usage/cmd/bootmeth.rst
 create mode 100644 drivers/mmc/mmc_bootdev.c
 create mode 100644 drivers/usb/host/usb_bootdev.c
 create mode 100644 fs/sandbox/Kconfig
 create mode 100644 fs/sandbox/host_bootdev.c
 create mode 100644 include/bootdev.h
 create mode 100644 include/bootflow.h
 create mode 100644 include/bootmeth.h
 create mode 100644 include/bootstd.h
 create mode 100644 include/distro.h
 create mode 100644 net/eth_bootdev.c
 create mode 100644 test/boot/Makefile
 create mode 100644 test/boot/bootdev.c
 create mode 100644 test/boot/bootflow.c
 create mode 100644 test/boot/bootmeth.c
 create mode 100644 test/boot/bootstd_common.c
 create mode 100644 test/boot/bootstd_common.h
 create mode 100644 test/py/tests/bootstd/mmc1.img.xz

-- 
2.36.0.rc2.479.g8af0fa9b8e-goog


^ permalink raw reply	[flat|nested] 36+ messages in thread

* [PATCH v5 01/34] lib: Move string tests to the string module
  2022-04-25  5:30 [PATCH v5 00/34] Initial implementation of standard boot Simon Glass
@ 2022-04-25  5:30 ` Simon Glass
  2022-04-25  5:30 ` [PATCH v5 02/34] test: Add tests for trailing_strtol() Simon Glass
                   ` (30 subsequent siblings)
  31 siblings, 0 replies; 36+ messages in thread
From: Simon Glass @ 2022-04-25  5:30 UTC (permalink / raw)
  To: U-Boot Mailing List
  Cc: Dennis Gilmore, Lukas Auer, Daniel Schwierzeck, Tom Rini,
	Michal Simek, Ilias Apalodimas, Steffen Jaeckel, Simon Glass

A few string tests were added to the print module by mistake. Move them.

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

(no changes since v4)

Changes in v4:
- Correct the commit tag

 test/print_ut.c | 40 ----------------------------------------
 test/str_ut.c   | 40 ++++++++++++++++++++++++++++++++++++++++
 2 files changed, 40 insertions(+), 40 deletions(-)

diff --git a/test/print_ut.c b/test/print_ut.c
index 247011f2db0..47a6ce57840 100644
--- a/test/print_ut.c
+++ b/test/print_ut.c
@@ -345,26 +345,6 @@ static int print_do_hex_dump(struct unit_test_state *uts)
 }
 PRINT_TEST(print_do_hex_dump, UT_TESTF_CONSOLE_REC);
 
-static int print_itoa(struct unit_test_state *uts)
-{
-	ut_asserteq_str("123", simple_itoa(123));
-	ut_asserteq_str("0", simple_itoa(0));
-	ut_asserteq_str("2147483647", simple_itoa(0x7fffffff));
-	ut_asserteq_str("4294967295", simple_itoa(0xffffffff));
-
-	/* Use #ifdef here to avoid a compiler warning on 32-bit machines */
-#ifdef CONFIG_PHYS_64BIT
-	if (sizeof(ulong) == 8) {
-		ut_asserteq_str("9223372036854775807",
-				simple_itoa((1UL << 63) - 1));
-		ut_asserteq_str("18446744073709551615", simple_itoa(-1));
-	}
-#endif /* CONFIG_PHYS_64BIT */
-
-	return 0;
-}
-PRINT_TEST(print_itoa, 0);
-
 static int snprint(struct unit_test_state *uts)
 {
 	char buf[10] = "xxxxxxxxx";
@@ -391,26 +371,6 @@ static int snprint(struct unit_test_state *uts)
 }
 PRINT_TEST(snprint, 0);
 
-static int print_xtoa(struct unit_test_state *uts)
-{
-	ut_asserteq_str("7f", simple_xtoa(127));
-	ut_asserteq_str("00", simple_xtoa(0));
-	ut_asserteq_str("7fffffff", simple_xtoa(0x7fffffff));
-	ut_asserteq_str("ffffffff", simple_xtoa(0xffffffff));
-
-	/* Use #ifdef here to avoid a compiler warning on 32-bit machines */
-#ifdef CONFIG_PHYS_64BIT
-	if (sizeof(ulong) == 8) {
-		ut_asserteq_str("7fffffffffffffff",
-				simple_xtoa((1UL << 63) - 1));
-		ut_asserteq_str("ffffffffffffffff", simple_xtoa(-1));
-	}
-#endif /* CONFIG_PHYS_64BIT */
-
-	return 0;
-}
-PRINT_TEST(print_xtoa, 0);
-
 int do_ut_print(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
 {
 	struct unit_test *tests = UNIT_TEST_SUITE_START(print_test);
diff --git a/test/str_ut.c b/test/str_ut.c
index d2840d51524..6c817f6f72f 100644
--- a/test/str_ut.c
+++ b/test/str_ut.c
@@ -202,6 +202,46 @@ static int str_dectoul(struct unit_test_state *uts)
 }
 STR_TEST(str_dectoul, 0);
 
+static int str_itoa(struct unit_test_state *uts)
+{
+	ut_asserteq_str("123", simple_itoa(123));
+	ut_asserteq_str("0", simple_itoa(0));
+	ut_asserteq_str("2147483647", simple_itoa(0x7fffffff));
+	ut_asserteq_str("4294967295", simple_itoa(0xffffffff));
+
+	/* Use #ifdef here to avoid a compiler warning on 32-bit machines */
+#ifdef CONFIG_PHYS_64BIT
+	if (sizeof(ulong) == 8) {
+		ut_asserteq_str("9223372036854775807",
+				simple_itoa((1UL << 63) - 1));
+		ut_asserteq_str("18446744073709551615", simple_itoa(-1));
+	}
+#endif /* CONFIG_PHYS_64BIT */
+
+	return 0;
+}
+STR_TEST(str_itoa, 0);
+
+static int str_xtoa(struct unit_test_state *uts)
+{
+	ut_asserteq_str("7f", simple_xtoa(127));
+	ut_asserteq_str("00", simple_xtoa(0));
+	ut_asserteq_str("7fffffff", simple_xtoa(0x7fffffff));
+	ut_asserteq_str("ffffffff", simple_xtoa(0xffffffff));
+
+	/* Use #ifdef here to avoid a compiler warning on 32-bit machines */
+#ifdef CONFIG_PHYS_64BIT
+	if (sizeof(ulong) == 8) {
+		ut_asserteq_str("7fffffffffffffff",
+				simple_xtoa((1UL << 63) - 1));
+		ut_asserteq_str("ffffffffffffffff", simple_xtoa(-1));
+	}
+#endif /* CONFIG_PHYS_64BIT */
+
+	return 0;
+}
+STR_TEST(str_xtoa, 0);
+
 int do_ut_str(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
 {
 	struct unit_test *tests = UNIT_TEST_SUITE_START(str_test);
-- 
2.36.0.rc2.479.g8af0fa9b8e-goog


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH v5 02/34] test: Add tests for trailing_strtol()
  2022-04-25  5:30 [PATCH v5 00/34] Initial implementation of standard boot Simon Glass
  2022-04-25  5:30 ` [PATCH v5 01/34] lib: Move string tests to the string module Simon Glass
@ 2022-04-25  5:30 ` Simon Glass
  2022-04-25  5:30 ` [PATCH v5 03/34] lib: Correct comment formatting to avoid sphinx problems Simon Glass
                   ` (29 subsequent siblings)
  31 siblings, 0 replies; 36+ messages in thread
From: Simon Glass @ 2022-04-25  5:30 UTC (permalink / raw)
  To: U-Boot Mailing List
  Cc: Dennis Gilmore, Lukas Auer, Daniel Schwierzeck, Tom Rini,
	Michal Simek, Ilias Apalodimas, Steffen Jaeckel, Simon Glass

This function currently has no tests. Add some.

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

(no changes since v4)

Changes in v4:
- Use new Return style in function comments

 include/vsprintf.h |  4 ++--
 test/str_ut.c      | 19 +++++++++++++++++++
 2 files changed, 21 insertions(+), 2 deletions(-)

diff --git a/include/vsprintf.h b/include/vsprintf.h
index 532ef3650bd..3d1f968df44 100644
--- a/include/vsprintf.h
+++ b/include/vsprintf.h
@@ -99,7 +99,7 @@ long long simple_strtoll(const char *cp, char **endp, unsigned int base);
  * For example, "abc123" would return 123.
  *
  * @str:	String to exxamine
- * Return: training number if found, else -1
+ * Return: trailing number if found, else -1
  */
 long trailing_strtol(const char *str);
 
@@ -114,7 +114,7 @@ long trailing_strtol(const char *str);
  * @str:	String to exxamine
  * @end:	Pointer to end of string to examine, or NULL to use the
  *		whole string
- * Return: training number if found, else -1
+ * Return: trailing number if found, else -1
  */
 long trailing_strtoln(const char *str, const char *end);
 
diff --git a/test/str_ut.c b/test/str_ut.c
index 6c817f6f72f..9674a59f2a6 100644
--- a/test/str_ut.c
+++ b/test/str_ut.c
@@ -242,6 +242,25 @@ static int str_xtoa(struct unit_test_state *uts)
 }
 STR_TEST(str_xtoa, 0);
 
+static int str_trailing(struct unit_test_state *uts)
+{
+	char str1[] = "abc123def";
+
+	ut_asserteq(-1, trailing_strtol(""));
+	ut_asserteq(-1, trailing_strtol("123"));
+	ut_asserteq(123, trailing_strtol("abc123"));
+	ut_asserteq(4, trailing_strtol("12c4"));
+	ut_asserteq(-1, trailing_strtol("abd"));
+	ut_asserteq(-1, trailing_strtol("abc123def"));
+
+	ut_asserteq(-1, trailing_strtoln(str1, NULL));
+	ut_asserteq(123, trailing_strtoln(str1, str1 + 6));
+	ut_asserteq(-1, trailing_strtoln(str1, str1 + 9));
+
+	return 0;
+}
+STR_TEST(str_trailing, 0);
+
 int do_ut_str(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
 {
 	struct unit_test *tests = UNIT_TEST_SUITE_START(str_test);
-- 
2.36.0.rc2.479.g8af0fa9b8e-goog


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH v5 03/34] lib: Correct comment formatting to avoid sphinx problems
  2022-04-25  5:30 [PATCH v5 00/34] Initial implementation of standard boot Simon Glass
  2022-04-25  5:30 ` [PATCH v5 01/34] lib: Move string tests to the string module Simon Glass
  2022-04-25  5:30 ` [PATCH v5 02/34] test: Add tests for trailing_strtol() Simon Glass
@ 2022-04-25  5:30 ` Simon Glass
  2022-04-25  5:30 ` [PATCH v5 04/34] lib: Fix a few bugs in trailing_strtoln() Simon Glass
                   ` (28 subsequent siblings)
  31 siblings, 0 replies; 36+ messages in thread
From: Simon Glass @ 2022-04-25  5:30 UTC (permalink / raw)
  To: U-Boot Mailing List
  Cc: Dennis Gilmore, Lukas Auer, Daniel Schwierzeck, Tom Rini,
	Michal Simek, Ilias Apalodimas, Steffen Jaeckel, Simon Glass

Tweak a few comments to kep sphinx happy, in case we want to include this
file one day.

Also fix the 'exxamine' typo.

Patch-notes:
This uses:

   sed -i 's/@param \(\S*\)\s*/@\1: /' include/vsprintf.h

to convert the @param to the new format.

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

(no changes since v4)

Changes in v4:
- Add new patch to correct comment formatting to avoid sphinx errors
- Correct the commit tag

 include/vsprintf.h | 92 +++++++++++++++++++++++-----------------------
 1 file changed, 46 insertions(+), 46 deletions(-)

diff --git a/include/vsprintf.h b/include/vsprintf.h
index 3d1f968df44..d4bf3211da4 100644
--- a/include/vsprintf.h
+++ b/include/vsprintf.h
@@ -13,9 +13,9 @@
 /**
  * simple_strtoul - convert a string to an unsigned long
  *
- * @param cp	The string to be converted
- * @param endp	Updated to point to the first character not converted
- * @param base	The number base to use (0 for the default)
+ * @cp: The string to be converted
+ * @endp: Updated to point to the first character not converted
+ * @base: The number base to use (0 for the default)
  * Return: value decoded from string (0 if invalid)
  *
  * Converts a string to an unsigned long. If there are invalid characters at
@@ -34,8 +34,8 @@ ulong simple_strtoul(const char *cp, char **endp, unsigned int base);
 /**
  * hex_strtoul - convert a string in hex to an unsigned long
  *
- * @param cp	The string to be converted
- * @param endp	Updated to point to the first character not converted
+ * @cp: The string to be converted
+ * @endp: Updated to point to the first character not converted
  * Return: value decoded from string (0 if invalid)
  *
  * Converts a hex string to an unsigned long. If there are invalid characters at
@@ -47,8 +47,8 @@ unsigned long hextoul(const char *cp, char **endp);
 /**
  * dec_strtoul - convert a string in decimal to an unsigned long
  *
- * @param cp	The string to be converted
- * @param endp	Updated to point to the first character not converted
+ * @cp: The string to be converted
+ * @endp: Updated to point to the first character not converted
  * Return: value decoded from string (0 if invalid)
  *
  * Converts a decimal string to an unsigned long. If there are invalid
@@ -59,11 +59,11 @@ unsigned long dectoul(const char *cp, char **endp);
 
 /**
  * strict_strtoul - convert a string to an unsigned long strictly
- * @param cp	The string to be converted
- * @param base	The number base to use (0 for the default)
- * @param res	The converted result value
- * Return: 0 if conversion is successful and *res is set to the converted
- * value, otherwise it returns -EINVAL and *res is set to 0.
+ * @cp: The string to be converted
+ * @base: The number base to use (0 for the default)
+ * @res: The converted result value
+ * Return: 0 if conversion is successful and `*res` is set to the converted
+ * value, otherwise it returns -EINVAL and `*res` is set to 0.
  *
  * strict_strtoul converts a string to an unsigned long only if the
  * string is really an unsigned long string, any string containing
@@ -98,7 +98,7 @@ long long simple_strtoll(const char *cp, char **endp, unsigned int base);
  * Given a string this finds a trailing number on the string and returns it.
  * For example, "abc123" would return 123.
  *
- * @str:	String to exxamine
+ * @str:	String to examine
  * Return: trailing number if found, else -1
  */
 long trailing_strtol(const char *str);
@@ -111,7 +111,7 @@ long trailing_strtol(const char *str);
  * characters between @str and @end - 1 are examined. If @end is NULL, it is
  * set to str + strlen(str).
  *
- * @str:	String to exxamine
+ * @str:	String to examine
  * @end:	Pointer to end of string to examine, or NULL to use the
  *		whole string
  * Return: trailing number if found, else -1
@@ -124,7 +124,7 @@ long trailing_strtoln(const char *str, const char *end);
  * Prints a message on the console(s) and then resets. If CONFIG_PANIC_HANG is
  * defined, then it will hang instead of resetting.
  *
- * @param fmt:	printf() format string for message, which should not include
+ * @fmt: printf() format string for message, which should not include
  *		\n, followed by arguments
  */
 void panic(const char *fmt, ...)
@@ -139,16 +139,16 @@ void panic(const char *fmt, ...)
  * This function can be used instead of panic() when your board does not
  * already use printf(), * to keep code size small.
  *
- * @param fmt:	string to display, which should not include \n
+ * @str: string to display, which should not include \n
  */
 void panic_str(const char *str) __attribute__ ((noreturn));
 
 /**
  * Format a string and place it in a buffer
  *
- * @param buf	The buffer to place the result into
- * @param fmt	The format string to use
- * @param ...	Arguments for the format string
+ * @buf: The buffer to place the result into
+ * @fmt: The format string to use
+ * @...: Arguments for the format string
  *
  * The function returns the number of characters written
  * into @buf.
@@ -161,9 +161,9 @@ int sprintf(char *buf, const char *fmt, ...)
 /**
  * Format a string and place it in a buffer (va_list version)
  *
- * @param buf	The buffer to place the result into
- * @param fmt	The format string to use
- * @param args	Arguments for the format string
+ * @buf: The buffer to place the result into
+ * @fmt: The format string to use
+ * @args: Arguments for the format string
  * Return: the number of characters which have been written into
  * the @buf not including the trailing '\0'.
  *
@@ -178,7 +178,7 @@ int vsprintf(char *buf, const char *fmt, va_list args);
  *
  * This returns a static string containing the decimal representation of the
  * given value. The returned value may be overwritten by other calls to other
- * simple_... functions, so should be used immediately
+ * simple... functions, so should be used immediately
  *
  * @val: Value to convert
  * Return: string containing the decimal representation of @val
@@ -190,9 +190,9 @@ char *simple_itoa(ulong val);
  *
  * This returns a static string containing the hexadecimal representation of the
  * given value. The returned value may be overwritten by other calls to other
- * simple_... functions, so should be used immediately
+ * simple... functions, so should be used immediately
  *
- * @val: Value to convert
+ * @num: Value to convert
  * Return: string containing the hexecimal representation of @val
  */
 char *simple_xtoa(ulong num);
@@ -200,10 +200,10 @@ char *simple_xtoa(ulong num);
 /**
  * Format a string and place it in a buffer
  *
- * @param buf	The buffer to place the result into
- * @param size	The size of the buffer, including the trailing null space
- * @param fmt	The format string to use
- * @param ...	Arguments for the format string
+ * @buf: The buffer to place the result into
+ * @size: The size of the buffer, including the trailing null space
+ * @fmt: The format string to use
+ * @...: Arguments for the format string
  * Return: the number of characters which would be
  * generated for the given input, excluding the trailing null,
  * as per ISO C99.  If the return is greater than or equal to
@@ -217,10 +217,10 @@ int snprintf(char *buf, size_t size, const char *fmt, ...)
 /**
  * Format a string and place it in a buffer
  *
- * @param buf	The buffer to place the result into
- * @param size	The size of the buffer, including the trailing null space
- * @param fmt	The format string to use
- * @param ...	Arguments for the format string
+ * @buf: The buffer to place the result into
+ * @size: The size of the buffer, including the trailing null space
+ * @fmt: The format string to use
+ * @...: Arguments for the format string
  *
  * The return value is the number of characters written into @buf not including
  * the trailing '\0'. If @size is == 0 the function returns 0.
@@ -233,10 +233,10 @@ int scnprintf(char *buf, size_t size, const char *fmt, ...)
 /**
  * Format a string and place it in a buffer (base function)
  *
- * @param buf	The buffer to place the result into
- * @param size	The size of the buffer, including the trailing null space
- * @param fmt	The format string to use
- * @param args	Arguments for the format string
+ * @buf: The buffer to place the result into
+ * @size: The size of the buffer, including the trailing null space
+ * @fmt: The format string to use
+ * @args: Arguments for the format string
  * Return: The number characters which would be generated for the given
  * input, excluding the trailing '\0', as per ISO C99. Note that fewer
  * characters may be written if this number of characters is >= size.
@@ -258,10 +258,10 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list args);
 /**
  * Format a string and place it in a buffer (va_list version)
  *
- * @param buf	The buffer to place the result into
- * @param size	The size of the buffer, including the trailing null space
- * @param fmt	The format string to use
- * @param args	Arguments for the format string
+ * @buf: The buffer to place the result into
+ * @size: The size of the buffer, including the trailing null space
+ * @fmt: The format string to use
+ * @args: Arguments for the format string
  * Return: the number of characters which have been written into
  * the @buf not including the trailing '\0'. If @size is == 0 the function
  * returns 0.
@@ -278,8 +278,8 @@ int vscnprintf(char *buf, size_t size, const char *fmt, va_list args);
  * This prints a value with grouped digits, like 12,345,678 to make it easier
  * to read.
  *
- * @val:	Value to print
- * @digits:	Number of digiits to print
+ * @int_val: Value to print
+ * @digits: Number of digiits to print
  */
 void print_grouped_ull(unsigned long long int_val, int digits);
 
@@ -309,9 +309,9 @@ void str_to_upper(const char *in, char *out, size_t len);
 
 /**
  * vsscanf - Unformat a buffer into a list of arguments
- * @buf:	input buffer
- * @fmt:	format of buffer
- * @args:	arguments
+ * @inp: input buffer
+ * @fmt0: format of buffer
+ * @ap: arguments
  */
 int vsscanf(const char *inp, char const *fmt0, va_list ap);
 
-- 
2.36.0.rc2.479.g8af0fa9b8e-goog


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH v5 04/34] lib: Fix a few bugs in trailing_strtoln()
  2022-04-25  5:30 [PATCH v5 00/34] Initial implementation of standard boot Simon Glass
                   ` (2 preceding siblings ...)
  2022-04-25  5:30 ` [PATCH v5 03/34] lib: Correct comment formatting to avoid sphinx problems Simon Glass
@ 2022-04-25  5:30 ` Simon Glass
  2022-04-25  5:30 ` [PATCH v5 05/34] lib: Add a way to find the postiion of a trailing number Simon Glass
                   ` (27 subsequent siblings)
  31 siblings, 0 replies; 36+ messages in thread
From: Simon Glass @ 2022-04-25  5:30 UTC (permalink / raw)
  To: U-Boot Mailing List
  Cc: Dennis Gilmore, Lukas Auer, Daniel Schwierzeck, Tom Rini,
	Michal Simek, Ilias Apalodimas, Steffen Jaeckel, Simon Glass

At present this has a minor bug in that it reads the byte before the
start of the string, if it is empty. Also it doesn't handle a
non-numeric prefix which is only one character long.

Fix these bugs with a reworked implementation. Add a test for the second
case. The first one is hard to test.

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

(no changes since v4)

Changes in v4:
- Correct the commit tag
- Reword the commit message
- Update the function comment to clarify its intert / limitations

 include/vsprintf.h |  3 +++
 lib/strto.c        | 11 ++++++-----
 test/str_ut.c      |  2 ++
 3 files changed, 11 insertions(+), 5 deletions(-)

diff --git a/include/vsprintf.h b/include/vsprintf.h
index d4bf3211da4..5172ceedec1 100644
--- a/include/vsprintf.h
+++ b/include/vsprintf.h
@@ -98,6 +98,9 @@ long long simple_strtoll(const char *cp, char **endp, unsigned int base);
  * Given a string this finds a trailing number on the string and returns it.
  * For example, "abc123" would return 123.
  *
+ * Note that this does not handle a string without a prefix. See dectoul() for
+ * that case.
+ *
  * @str:	String to examine
  * Return: trailing number if found, else -1
  */
diff --git a/lib/strto.c b/lib/strto.c
index f1918843765..b1d803a77d9 100644
--- a/lib/strto.c
+++ b/lib/strto.c
@@ -189,11 +189,12 @@ long trailing_strtoln(const char *str, const char *end)
 
 	if (!end)
 		end = str + strlen(str);
-	if (isdigit(end[-1])) {
-		for (p = end - 1; p > str; p--) {
-			if (!isdigit(*p))
-				return dectoul(p + 1, NULL);
-		}
+	p = end - 1;
+	if (p > str && isdigit(*p)) {
+		do {
+			if (!isdigit(p[-1]))
+				return dectoul(p, NULL);
+		} while (--p > str);
 	}
 
 	return -1;
diff --git a/test/str_ut.c b/test/str_ut.c
index 9674a59f2a6..058b3594379 100644
--- a/test/str_ut.c
+++ b/test/str_ut.c
@@ -257,6 +257,8 @@ static int str_trailing(struct unit_test_state *uts)
 	ut_asserteq(123, trailing_strtoln(str1, str1 + 6));
 	ut_asserteq(-1, trailing_strtoln(str1, str1 + 9));
 
+	ut_asserteq(3, trailing_strtol("a3"));
+
 	return 0;
 }
 STR_TEST(str_trailing, 0);
-- 
2.36.0.rc2.479.g8af0fa9b8e-goog


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH v5 05/34] lib: Add a way to find the postiion of a trailing number
  2022-04-25  5:30 [PATCH v5 00/34] Initial implementation of standard boot Simon Glass
                   ` (3 preceding siblings ...)
  2022-04-25  5:30 ` [PATCH v5 04/34] lib: Fix a few bugs in trailing_strtoln() Simon Glass
@ 2022-04-25  5:30 ` Simon Glass
  2022-04-25  5:30 ` [PATCH v5 06/34] dm: core: Rename and fix uclass_get_by_name_len() Simon Glass
                   ` (26 subsequent siblings)
  31 siblings, 0 replies; 36+ messages in thread
From: Simon Glass @ 2022-04-25  5:30 UTC (permalink / raw)
  To: U-Boot Mailing List
  Cc: Dennis Gilmore, Lukas Auer, Daniel Schwierzeck, Tom Rini,
	Michal Simek, Ilias Apalodimas, Steffen Jaeckel, Simon Glass

At present it is not possible to find out which part of the string is the
number part and which is before it. Add a new variant which provides this
feature, so we can separate the two in the caller.

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

(no changes since v4)

Changes in v4:
- Use new Return style in function comments

Changes in v3:
- Add some tests, including one for 'abc123def456'
- Change the function to return a pointer to the first digit

 include/vsprintf.h | 18 ++++++++++++++++++
 lib/strto.c        | 14 ++++++++++++--
 test/str_ut.c      | 13 ++++++++++++-
 3 files changed, 42 insertions(+), 3 deletions(-)

diff --git a/include/vsprintf.h b/include/vsprintf.h
index 5172ceedec1..e006af200fd 100644
--- a/include/vsprintf.h
+++ b/include/vsprintf.h
@@ -121,6 +121,24 @@ long trailing_strtol(const char *str);
  */
 long trailing_strtoln(const char *str, const char *end);
 
+/**
+ * trailing_strtoln_end() - extract trailing integer from a fixed-length string
+ *
+ * Given a fixed-length string this finds a trailing number on the string
+ * and returns it. For example, "abc123" would return 123. Only the
+ * characters between @str and @end - 1 are examined. If @end is NULL, it is
+ * set to str + strlen(str).
+ *
+ * @str:	String to examine
+ * @end:	Pointer to end of string to examine, or NULL to use the
+ *		whole string
+ * @endp:	If non-NULL, this is set to point to the character where the
+ *	number starts, e.g. for "mmc0" this would be point to the '0'; if no
+ *	trailing number is found, it is set to the end of the string
+ * Return: training number if found, else -1
+ */
+long trailing_strtoln_end(const char *str, const char *end, char const **endp);
+
 /**
  * panic() - Print a message and reset/hang
  *
diff --git a/lib/strto.c b/lib/strto.c
index b1d803a77d9..6462d4fddf1 100644
--- a/lib/strto.c
+++ b/lib/strto.c
@@ -183,7 +183,7 @@ long long simple_strtoll(const char *cp, char **endp, unsigned int base)
 	return simple_strtoull(cp, endp, base);
 }
 
-long trailing_strtoln(const char *str, const char *end)
+long trailing_strtoln_end(const char *str, const char *end, char const **endp)
 {
 	const char *p;
 
@@ -192,14 +192,24 @@ long trailing_strtoln(const char *str, const char *end)
 	p = end - 1;
 	if (p > str && isdigit(*p)) {
 		do {
-			if (!isdigit(p[-1]))
+			if (!isdigit(p[-1])) {
+				if (endp)
+					*endp = p;
 				return dectoul(p, NULL);
+			}
 		} while (--p > str);
 	}
+	if (endp)
+		*endp = end;
 
 	return -1;
 }
 
+long trailing_strtoln(const char *str, const char *end)
+{
+	return trailing_strtoln_end(str, end, NULL);
+}
+
 long trailing_strtol(const char *str)
 {
 	return trailing_strtoln(str, NULL);
diff --git a/test/str_ut.c b/test/str_ut.c
index 058b3594379..5a844347c2b 100644
--- a/test/str_ut.c
+++ b/test/str_ut.c
@@ -244,7 +244,9 @@ STR_TEST(str_xtoa, 0);
 
 static int str_trailing(struct unit_test_state *uts)
 {
-	char str1[] = "abc123def";
+	const char str1[] = "abc123def";
+	const char str2[] = "abc123def456";
+	const char *end;
 
 	ut_asserteq(-1, trailing_strtol(""));
 	ut_asserteq(-1, trailing_strtol("123"));
@@ -259,6 +261,15 @@ static int str_trailing(struct unit_test_state *uts)
 
 	ut_asserteq(3, trailing_strtol("a3"));
 
+	ut_asserteq(123, trailing_strtoln_end(str1, str1 + 6, &end));
+	ut_asserteq(3, end - str1);
+
+	ut_asserteq(-1, trailing_strtoln_end(str1, str1 + 7, &end));
+	ut_asserteq(7, end - str1);
+
+	ut_asserteq(456, trailing_strtoln_end(str2, NULL, &end));
+	ut_asserteq(9, end - str2);
+
 	return 0;
 }
 STR_TEST(str_trailing, 0);
-- 
2.36.0.rc2.479.g8af0fa9b8e-goog


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH v5 06/34] dm: core: Rename and fix uclass_get_by_name_len()
  2022-04-25  5:30 [PATCH v5 00/34] Initial implementation of standard boot Simon Glass
                   ` (4 preceding siblings ...)
  2022-04-25  5:30 ` [PATCH v5 05/34] lib: Add a way to find the postiion of a trailing number Simon Glass
@ 2022-04-25  5:30 ` Simon Glass
  2022-04-25  5:31 ` [PATCH v5 07/34] dm: core: Allow finding a uclass device by partial name Simon Glass
                   ` (25 subsequent siblings)
  31 siblings, 0 replies; 36+ messages in thread
From: Simon Glass @ 2022-04-25  5:30 UTC (permalink / raw)
  To: U-Boot Mailing List
  Cc: Dennis Gilmore, Lukas Auer, Daniel Schwierzeck, Tom Rini,
	Michal Simek, Ilias Apalodimas, Steffen Jaeckel, Simon Glass,
	Patrick Delaunay, Tim Harvey, Marek Vasut, Pavel Herrmann

It seems that namelen is more common in U-Boot. Rename this function to
fit in better. Also fix a bug where it breaks the operation of
uclass_get_by_name() and add a test.

Signed-off-by: Simon Glass <sjg@chromium.org>
Reported-by: Patrick Delaunay <patrick.delaunay@foss.st.com>
Reported-by: Tim Harvey <tharvey@gateworks.com>
---

(no changes since v4)

Changes in v4:
- Correct bug in this function and add test
- Use new sphynx Return style

Changes in v3:
- Add new patch to rename uclass_get_by_name_len()

 drivers/core/uclass.c | 7 ++++---
 include/dm/uclass.h   | 6 +++---
 test/dm/core.c        | 2 ++
 3 files changed, 9 insertions(+), 6 deletions(-)

diff --git a/drivers/core/uclass.c b/drivers/core/uclass.c
index 2578803b7a4..4b9b54f0f15 100644
--- a/drivers/core/uclass.c
+++ b/drivers/core/uclass.c
@@ -180,14 +180,15 @@ void uclass_set_priv(struct uclass *uc, void *priv)
 	uc->priv_ = priv;
 }
 
-enum uclass_id uclass_get_by_name_len(const char *name, int len)
+enum uclass_id uclass_get_by_namelen(const char *name, int len)
 {
 	int i;
 
 	for (i = 0; i < UCLASS_COUNT; i++) {
 		struct uclass_driver *uc_drv = lists_uclass_lookup(i);
 
-		if (uc_drv && !strncmp(uc_drv->name, name, len))
+		if (uc_drv && !strncmp(uc_drv->name, name, len) &&
+		    strlen(uc_drv->name) == len)
 			return i;
 	}
 
@@ -196,7 +197,7 @@ enum uclass_id uclass_get_by_name_len(const char *name, int len)
 
 enum uclass_id uclass_get_by_name(const char *name)
 {
-	return uclass_get_by_name_len(name, strlen(name));
+	return uclass_get_by_namelen(name, strlen(name));
 }
 
 int dev_get_uclass_index(struct udevice *dev, struct uclass **ucp)
diff --git a/include/dm/uclass.h b/include/dm/uclass.h
index aafe6522886..f6c0110b061 100644
--- a/include/dm/uclass.h
+++ b/include/dm/uclass.h
@@ -173,13 +173,13 @@ int uclass_get(enum uclass_id key, struct uclass **ucp);
 const char *uclass_get_name(enum uclass_id id);
 
 /**
- * uclass_get_by_name_len() - Look up a uclass by its partial driver name
+ * uclass_get_by_namelen() - Look up a uclass by its driver name
  *
  * @name: Name to look up
- * @len: Length of the partial name
+ * @len: Length of @name (the uclass driver name must have the same length)
  * Return: the associated uclass ID, or UCLASS_INVALID if not found
  */
-enum uclass_id uclass_get_by_name_len(const char *name, int len);
+enum uclass_id uclass_get_by_namelen(const char *name, int len);
 
 /**
  * uclass_get_by_name() - Look up a uclass by its driver name
diff --git a/test/dm/core.c b/test/dm/core.c
index 0ce0b3c4a2e..2c73ecf54a0 100644
--- a/test/dm/core.c
+++ b/test/dm/core.c
@@ -1161,6 +1161,8 @@ static int dm_test_uclass_names(struct unit_test_state *uts)
 	ut_asserteq_str("test", uclass_get_name(UCLASS_TEST));
 	ut_asserteq(UCLASS_TEST, uclass_get_by_name("test"));
 
+	ut_asserteq(UCLASS_SPI, uclass_get_by_name("spi"));
+
 	return 0;
 }
 DM_TEST(dm_test_uclass_names, UT_TESTF_SCAN_PDATA);
-- 
2.36.0.rc2.479.g8af0fa9b8e-goog


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH v5 07/34] dm: core: Allow finding a uclass device by partial name
  2022-04-25  5:30 [PATCH v5 00/34] Initial implementation of standard boot Simon Glass
                   ` (5 preceding siblings ...)
  2022-04-25  5:30 ` [PATCH v5 06/34] dm: core: Rename and fix uclass_get_by_name_len() Simon Glass
@ 2022-04-25  5:31 ` Simon Glass
  2022-04-25  5:31 ` [PATCH v5 08/34] test: fastboot: Avoid using mmc1 Simon Glass
                   ` (24 subsequent siblings)
  31 siblings, 0 replies; 36+ messages in thread
From: Simon Glass @ 2022-04-25  5:31 UTC (permalink / raw)
  To: U-Boot Mailing List
  Cc: Dennis Gilmore, Lukas Auer, Daniel Schwierzeck, Tom Rini,
	Michal Simek, Ilias Apalodimas, Steffen Jaeckel, Simon Glass,
	Marek Vasut, Pavel Herrmann

In some cases two devices are related and the only way to tell is to
check that the names partially patch. Add a way to check this without
needing to create a new string for the comparison.

Fix the comment for device_find_child_by_namelen() while we are here.

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

(no changes since v4)

Changes in v4:
- Add a test also
- Use new sphynx Return style

Changes in v3:
- Add new patch to support finding a uclass device by partial name

 drivers/core/uclass.c        | 13 ++++++++++---
 include/dm/device.h          |  2 +-
 include/dm/uclass-internal.h | 16 ++++++++++++++++
 test/dm/core.c               | 15 +++++++++++++++
 4 files changed, 42 insertions(+), 4 deletions(-)

diff --git a/drivers/core/uclass.c b/drivers/core/uclass.c
index 4b9b54f0f15..08d9ed82de2 100644
--- a/drivers/core/uclass.c
+++ b/drivers/core/uclass.c
@@ -274,8 +274,8 @@ int uclass_find_next_device(struct udevice **devp)
 	return 0;
 }
 
-int uclass_find_device_by_name(enum uclass_id id, const char *name,
-			       struct udevice **devp)
+int uclass_find_device_by_namelen(enum uclass_id id, const char *name, int len,
+				  struct udevice **devp)
 {
 	struct uclass *uc;
 	struct udevice *dev;
@@ -289,7 +289,8 @@ int uclass_find_device_by_name(enum uclass_id id, const char *name,
 		return ret;
 
 	uclass_foreach_dev(dev, uc) {
-		if (!strcmp(dev->name, name)) {
+		if (!strncmp(dev->name, name, len) &&
+		    strlen(dev->name) == len) {
 			*devp = dev;
 			return 0;
 		}
@@ -298,6 +299,12 @@ int uclass_find_device_by_name(enum uclass_id id, const char *name,
 	return -ENODEV;
 }
 
+int uclass_find_device_by_name(enum uclass_id id, const char *name,
+			       struct udevice **devp)
+{
+	return uclass_find_device_by_namelen(id, name, strlen(name), devp);
+}
+
 int uclass_find_next_free_seq(struct uclass *uc)
 {
 	struct udevice *dev;
diff --git a/include/dm/device.h b/include/dm/device.h
index e0f86f5df9f..b474888d025 100644
--- a/include/dm/device.h
+++ b/include/dm/device.h
@@ -799,7 +799,7 @@ int device_find_first_child_by_uclass(const struct udevice *parent,
 				      struct udevice **devp);
 
 /**
- * device_find_child_by_name() - Find a child by device name
+ * device_find_child_by_namelen() - Find a child by device name
  *
  * @parent:	Parent device to search
  * @name:	Name to look for
diff --git a/include/dm/uclass-internal.h b/include/dm/uclass-internal.h
index daf856c03cf..3ddcdd21439 100644
--- a/include/dm/uclass-internal.h
+++ b/include/dm/uclass-internal.h
@@ -154,6 +154,22 @@ int uclass_find_first_device(enum uclass_id id, struct udevice **devp);
  */
 int uclass_find_next_device(struct udevice **devp);
 
+/**
+ * uclass_find_device_by_namelen() - Find uclass device based on ID and name
+ *
+ * This searches for a device with the exactly given name.
+ *
+ * The device is NOT probed, it is merely returned.
+ *
+ * @id: ID to look up
+ * @name: name of a device to find
+ * @len: Length of @name (the uclass driver name must have the same length)
+ * @devp: Returns pointer to device (the first one with the name)
+ * Return: 0 if OK, -ve on error
+ */
+int uclass_find_device_by_namelen(enum uclass_id id, const char *name, int len,
+				  struct udevice **devp);
+
 /**
  * uclass_find_device_by_name() - Find uclass device based on ID and name
  *
diff --git a/test/dm/core.c b/test/dm/core.c
index 2c73ecf54a0..ebd504427d1 100644
--- a/test/dm/core.c
+++ b/test/dm/core.c
@@ -1260,3 +1260,18 @@ static int dm_test_get_stats(struct unit_test_state *uts)
 	return 0;
 }
 DM_TEST(dm_test_get_stats, UT_TESTF_SCAN_FDT);
+
+/* Test uclass_find_device_by_name() */
+static int dm_test_uclass_find_device(struct unit_test_state *uts)
+{
+	struct udevice *dev;
+
+	ut_assertok(uclass_find_device_by_name(UCLASS_I2C, "i2c@0", &dev));
+	ut_asserteq(-ENODEV,
+		    uclass_find_device_by_name(UCLASS_I2C, "i2c@0x", &dev));
+	ut_assertok(uclass_find_device_by_namelen(UCLASS_I2C, "i2c@0x", 5,
+						  &dev));
+
+	return 0;
+}
+DM_TEST(dm_test_uclass_find_device, UT_TESTF_SCAN_FDT);
-- 
2.36.0.rc2.479.g8af0fa9b8e-goog


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH v5 08/34] test: fastboot: Avoid using mmc1
  2022-04-25  5:30 [PATCH v5 00/34] Initial implementation of standard boot Simon Glass
                   ` (6 preceding siblings ...)
  2022-04-25  5:31 ` [PATCH v5 07/34] dm: core: Allow finding a uclass device by partial name Simon Glass
@ 2022-04-25  5:31 ` Simon Glass
  2022-04-25  5:31 ` [PATCH v5 09/34] test: dm: Restart USB before assuming it is stopped Simon Glass
                   ` (23 subsequent siblings)
  31 siblings, 0 replies; 36+ messages in thread
From: Simon Glass @ 2022-04-25  5:31 UTC (permalink / raw)
  To: U-Boot Mailing List
  Cc: Dennis Gilmore, Lukas Auer, Daniel Schwierzeck, Tom Rini,
	Michal Simek, Ilias Apalodimas, Steffen Jaeckel, Simon Glass

The bootflow tests need to use an MMC with an associated backing file
containing a filesystem. Update the fastboot tests to cope with this.

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

(no changes since v1)

 test/dm/fastboot.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/test/dm/fastboot.c b/test/dm/fastboot.c
index e7f8c362b85..758538d0e85 100644
--- a/test/dm/fastboot.c
+++ b/test/dm/fastboot.c
@@ -81,9 +81,9 @@ static int dm_test_fastboot_mmc_part(struct unit_test_state *uts)
 						  &part_info, response));
 	ut_asserteq(0, fastboot_mmc_get_part_info("0.0:0", &fb_dev_desc,
 						  &part_info, response));
-	ut_asserteq(0, fastboot_mmc_get_part_info("1", &fb_dev_desc,
+	ut_asserteq(0, fastboot_mmc_get_part_info("2", &fb_dev_desc,
 						  &part_info, response));
-	ut_asserteq(0, fastboot_mmc_get_part_info("1.0", &fb_dev_desc,
+	ut_asserteq(0, fastboot_mmc_get_part_info("2.0", &fb_dev_desc,
 						  &part_info, response));
 	ut_asserteq(1, fastboot_mmc_get_part_info(":1", &fb_dev_desc,
 						  &part_info, response));
-- 
2.36.0.rc2.479.g8af0fa9b8e-goog


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH v5 09/34] test: dm: Restart USB before assuming it is stopped
  2022-04-25  5:30 [PATCH v5 00/34] Initial implementation of standard boot Simon Glass
                   ` (7 preceding siblings ...)
  2022-04-25  5:31 ` [PATCH v5 08/34] test: fastboot: Avoid using mmc1 Simon Glass
@ 2022-04-25  5:31 ` Simon Glass
  2022-04-25  5:31 ` [PATCH v5 10/34] dm: blk: Add a function to return the device type Simon Glass
                   ` (22 subsequent siblings)
  31 siblings, 0 replies; 36+ messages in thread
From: Simon Glass @ 2022-04-25  5:31 UTC (permalink / raw)
  To: U-Boot Mailing List
  Cc: Dennis Gilmore, Lukas Auer, Daniel Schwierzeck, Tom Rini,
	Michal Simek, Ilias Apalodimas, Steffen Jaeckel, Simon Glass,
	Marek Vasut, Pavel Herrmann

Update the blk test to stop USB first, in case another test has started
it.

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

(no changes since v1)

 test/dm/blk.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/test/dm/blk.c b/test/dm/blk.c
index 8556cc7159c..85c3a3bd45c 100644
--- a/test/dm/blk.c
+++ b/test/dm/blk.c
@@ -15,6 +15,9 @@
 
 DECLARE_GLOBAL_DATA_PTR;
 
+/* Allow resetting the USB-started flag */
+extern char usb_started;
+
 /* Test that block devices can be created */
 static int dm_test_blk_base(struct unit_test_state *uts)
 {
@@ -66,8 +69,11 @@ static int dm_test_blk_usb(struct unit_test_state *uts)
 	struct udevice *usb_dev, *dev;
 	struct blk_desc *dev_desc;
 
+	usb_started = false;
+
 	/* Get a flash device */
 	state_set_skip_delays(true);
+	ut_assertok(usb_stop());
 	ut_assertok(usb_init());
 	ut_assertok(uclass_get_device(UCLASS_MASS_STORAGE, 0, &usb_dev));
 	ut_assertok(blk_get_device_by_str("usb", "0", &dev_desc));
-- 
2.36.0.rc2.479.g8af0fa9b8e-goog


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH v5 10/34] dm: blk: Add a function to return the device type
  2022-04-25  5:30 [PATCH v5 00/34] Initial implementation of standard boot Simon Glass
                   ` (8 preceding siblings ...)
  2022-04-25  5:31 ` [PATCH v5 09/34] test: dm: Restart USB before assuming it is stopped Simon Glass
@ 2022-04-25  5:31 ` Simon Glass
  2022-04-25  5:31 ` [PATCH v5 11/34] fs: Add a function to set the filesystem type Simon Glass
                   ` (21 subsequent siblings)
  31 siblings, 0 replies; 36+ messages in thread
From: Simon Glass @ 2022-04-25  5:31 UTC (permalink / raw)
  To: U-Boot Mailing List
  Cc: Dennis Gilmore, Lukas Auer, Daniel Schwierzeck, Tom Rini,
	Michal Simek, Ilias Apalodimas, Steffen Jaeckel, Simon Glass,
	Marek Vasut, Pavel Herrmann

Use the uclass name to get the device type for a block device.

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

(no changes since v4)

Changes in v4:
- Use new sphynx Return style

 drivers/block/blk-uclass.c | 7 +++++++
 include/blk.h              | 8 ++++++++
 2 files changed, 15 insertions(+)

diff --git a/drivers/block/blk-uclass.c b/drivers/block/blk-uclass.c
index f1e4a856467..29f355fa154 100644
--- a/drivers/block/blk-uclass.c
+++ b/drivers/block/blk-uclass.c
@@ -509,6 +509,13 @@ int blk_get_from_parent(struct udevice *parent, struct udevice **devp)
 	return 0;
 }
 
+const char *blk_get_devtype(struct udevice *dev)
+{
+	struct udevice *parent = dev_get_parent(dev);
+
+	return uclass_get_name(device_get_uclass_id(parent));
+};
+
 int blk_find_max_devnum(enum if_type if_type)
 {
 	struct udevice *dev;
diff --git a/include/blk.h b/include/blk.h
index dbe9ae219da..9503369db83 100644
--- a/include/blk.h
+++ b/include/blk.h
@@ -433,6 +433,14 @@ int blk_select_hwpart(struct udevice *dev, int hwpart);
  */
 int blk_get_from_parent(struct udevice *parent, struct udevice **devp);
 
+/**
+ * blk_get_devtype() - Get the device type of a block device
+ *
+ * @dev:	Block device to check
+ * Return: device tree, i.e. the uclass name of its parent, e.g. "mmc"
+ */
+const char *blk_get_devtype(struct udevice *dev);
+
 /**
  * blk_get_by_device() - Get the block device descriptor for the given device
  * @dev:	Instance of a storage device
-- 
2.36.0.rc2.479.g8af0fa9b8e-goog


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH v5 11/34] fs: Add a function to set the filesystem type
  2022-04-25  5:30 [PATCH v5 00/34] Initial implementation of standard boot Simon Glass
                   ` (9 preceding siblings ...)
  2022-04-25  5:31 ` [PATCH v5 10/34] dm: blk: Add a function to return the device type Simon Glass
@ 2022-04-25  5:31 ` Simon Glass
  2022-04-25  5:31 ` [PATCH v5 12/34] bootstd: Add the concept of a bootflow Simon Glass
                   ` (20 subsequent siblings)
  31 siblings, 0 replies; 36+ messages in thread
From: Simon Glass @ 2022-04-25  5:31 UTC (permalink / raw)
  To: U-Boot Mailing List
  Cc: Dennis Gilmore, Lukas Auer, Daniel Schwierzeck, Tom Rini,
	Michal Simek, Ilias Apalodimas, Steffen Jaeckel, Simon Glass

When sandbox is used with hostfs we won't have a block device, but still
must set up the filesystem type before any filesystem operation, such as
loading a file. Add a function to handle this.

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

(no changes since v4)

Changes in v4:
- Add new patch with a function to set the filesystem type

 fs/fs.c      |  5 +++++
 include/fs.h | 11 +++++++++++
 2 files changed, 16 insertions(+)

diff --git a/fs/fs.c b/fs/fs.c
index b812597ced9..6de1a3eb6d5 100644
--- a/fs/fs.c
+++ b/fs/fs.c
@@ -36,6 +36,11 @@ static int fs_dev_part;
 static struct disk_partition fs_partition;
 static int fs_type = FS_TYPE_ANY;
 
+void fs_set_type(int type)
+{
+	fs_type = type;
+}
+
 static inline int fs_probe_unsupported(struct blk_desc *fs_dev_desc,
 				      struct disk_partition *fs_partition)
 {
diff --git a/include/fs.h b/include/fs.h
index e2beba36b98..b43f16a692f 100644
--- a/include/fs.h
+++ b/include/fs.h
@@ -57,6 +57,17 @@ int do_ext2load(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]);
  */
 int fs_set_blk_dev(const char *ifname, const char *dev_part_str, int fstype);
 
+/**
+ * fs_set_type() - Tell fs layer which filesystem type is used
+ *
+ * This is needed when reading from a non-block device such as sandbox. It does
+ * a similar job to fs_set_blk_dev() but just sets the filesystem type instead
+ * of detecting it and loading it on the block device
+ *
+ * @type: Filesystem type to use (FS_TYPE...)
+ */
+void fs_set_type(int type);
+
 /*
  * fs_set_blk_dev_with_part - Set current block device + partition
  *
-- 
2.36.0.rc2.479.g8af0fa9b8e-goog


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH v5 12/34] bootstd: Add the concept of a bootflow
  2022-04-25  5:30 [PATCH v5 00/34] Initial implementation of standard boot Simon Glass
                   ` (10 preceding siblings ...)
  2022-04-25  5:31 ` [PATCH v5 11/34] fs: Add a function to set the filesystem type Simon Glass
@ 2022-04-25  5:31 ` Simon Glass
  2022-04-25  5:31 ` [PATCH v5 13/34] bootstd: Add the bootstd uclass and core implementation Simon Glass
                   ` (19 subsequent siblings)
  31 siblings, 0 replies; 36+ messages in thread
From: Simon Glass @ 2022-04-25  5:31 UTC (permalink / raw)
  To: U-Boot Mailing List
  Cc: Dennis Gilmore, Lukas Auer, Daniel Schwierzeck, Tom Rini,
	Michal Simek, Ilias Apalodimas, Steffen Jaeckel, Simon Glass

A bootflow encapsulates the process used to boot an operating system.
It typically has a control file (such as extlinux.conf) and information
about which 'bootdev' it came from.

Add the header file for this first, since it is needed by all other
files.

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

(no changes since v4)

Changes in v4:
- Add a new fs_type field for use with sandbox
- Use new Return style in function comments

Changes in v3:
- Mention bootflow_state[] in enum bootflow_state_t

 MAINTAINERS        |   5 +
 include/bootflow.h | 260 +++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 265 insertions(+)
 create mode 100644 include/bootflow.h

diff --git a/MAINTAINERS b/MAINTAINERS
index f7665fccf7a..d6d45573fdc 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -693,6 +693,11 @@ M:	Simon Glass <sjg@chromium.org>
 S:	Maintained
 F:	tools/binman/
 
+BOOTDEVICE
+M:	Simon Glass <sjg@chromium.org>
+S:	Maintained
+F:	include/bootflow.h
+
 BTRFS
 M:	Marek Behun <marek.behun@nic.cz>
 R:	Qu Wenruo <wqu@suse.com>
diff --git a/include/bootflow.h b/include/bootflow.h
new file mode 100644
index 00000000000..6e9a729a9a3
--- /dev/null
+++ b/include/bootflow.h
@@ -0,0 +1,260 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright 2021 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#ifndef __bootflow_h
+#define __bootflow_h
+
+#include <linux/list.h>
+
+/**
+ * enum bootflow_state_t - states that a particular bootflow can be in
+ *
+ * Only bootflows in state BOOTFLOWST_READY can be used to boot.
+ *
+ * See bootflow_state[] for the names for each of these
+ */
+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_READY,	/**< Bootflow file loaded */
+
+	BOOTFLOWST_COUNT
+};
+
+/**
+ * struct bootflow - information about a bootflow
+ *
+ * This is connected into two separate linked lists:
+ *
+ *   bm_sibling - links all bootflows in the same bootdev
+ *   glob_sibling - links all bootflows in all bootdevs
+ *
+ * @bm_node: Points to siblings in the same bootdev
+ * @glob_node: Points to siblings in the global list (all bootdev)
+ * @dev: Bootdevice device which produced this bootflow
+ * @blk: Block device which contains this bootflow, NULL if this is a network
+ *	device
+ * @part: Partition number (0 for whole device)
+ * @fs_type: Filesystem type (FS_TYPE...) if this is fixed by the media, else 0.
+ *	For example, the sandbox host-filesystem bootdev sets this to
+ *	FS_TYPE_SANDBOX
+ * @method: Bootmethod device used to perform the boot and read files
+ * @name: Name of bootflow (allocated)
+ * @state: Current state (enum bootflow_state_t)
+ * @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 part;
+	int fs_type;
+	struct udevice *method;
+	char *name;
+	enum bootflow_state_t state;
+	char *subdir;
+	char *fname;
+	char *buf;
+	int size;
+	int err;
+};
+
+/**
+ * enum bootflow_flags_t - flags for the bootflow iterator
+ *
+ * @BOOTFLOWF_FIXED: Only used fixed/internal media
+ * @BOOTFLOWF_SHOW: Show each bootdev before scanning it
+ * @BOOTFLOWF_ALL: Return bootflows with errors as well
+ * @BOOTFLOWF_SINGLE_DEV: Just scan one bootmeth
+ */
+enum bootflow_flags_t {
+	BOOTFLOWF_FIXED		= 1 << 0,
+	BOOTFLOWF_SHOW		= 1 << 1,
+	BOOTFLOWF_ALL		= 1 << 2,
+	BOOTFLOWF_SINGLE_DEV	= 1 << 3,
+};
+
+/**
+ * struct bootflow_iter - state for iterating through bootflows
+ *
+ * This starts at with the first bootdev/partition/bootmeth and can be used to
+ * iterate through all of them.
+ *
+ * Iteration starts with the bootdev. The first partition (0, i.e. whole device)
+ * is scanned first. For partition 0, it iterates through all the available
+ * bootmeths to see which one(s) can provide a bootflow. Then it moves to
+ * parition 1 (if there is one) and the process continues. Once all partitions
+ * are examined, it moves to the next bootdev.
+ *
+ * Initially @max_part is 0, meaning that only the whole device (@part=0) can be
+ * used. During scanning, if a partition table is found, then @max_part is
+ * updated to a larger value, no less than the number of available partitions.
+ * This ensures that iteration works through all partitions on the bootdev.
+ *
+ * @flags: Flags to use (see enum bootflow_flags_t)
+ * @dev: Current bootdev
+ * @part: Current partition number (0 for whole device)
+ * @method: Current bootmeth
+ * @max_part: Maximum hardware partition number in @dev, 0 if there is no
+ *	partition table
+ * @err: Error obtained from checking the last iteration. This is used to skip
+ *	forward (e.g. to skip the current partition because it is not valid)
+ *	-ESHUTDOWN: try next bootdev
+ * @num_devs: Number of bootdevs in @dev_order
+ * @cur_dev: Current bootdev number, an index into @dev_order[]
+ * @dev_order: List of bootdevs to scan, in order of priority. The scan starts
+ *	with the first one on the list
+ * @num_methods: Number of bootmeth devices in @method_order
+ * @cur_method: Current method number, an index into @method_order
+ * @method_order: List of bootmeth devices to use, in order
+ */
+struct bootflow_iter {
+	int flags;
+	struct udevice *dev;
+	int part;
+	struct udevice *method;
+	int max_part;
+	int err;
+	int num_devs;
+	int cur_dev;
+	struct udevice **dev_order;
+	int num_methods;
+	int cur_method;
+	struct udevice **method_order;
+};
+
+/**
+ * bootflow_iter_init() - Reset a bootflow iterator
+ *
+ * This sets everything to the starting point, ready for use.
+ *
+ * @iter: Place to store private info (inited by this call)
+ * @flags: Flags to use (see enum bootflow_flags_t)
+ */
+void bootflow_iter_init(struct bootflow_iter *iter, int flags);
+
+/**
+ * bootflow_iter_uninit() - Free memory used by an interator
+ *
+ * @iter:	Iterator to free
+ */
+void bootflow_iter_uninit(struct bootflow_iter *iter);
+
+/**
+ * bootflow_scan_bootdev() - find the first bootflow in a bootdev
+ *
+ * If @flags includes BOOTFLOWF_ALL then bootflows with errors are returned too
+ *
+ * @dev:	Boot device to scan, NULL to work through all of them until it
+ *	finds one that * can supply a bootflow
+ * @iter:	Place to store private info (inited by this call)
+ * @flags:	Flags for bootdev (enum bootflow_flags_t)
+ * @bflow:	Place to put the bootflow if found
+ * Return: 0 if found,  -ENODEV if no device, other -ve on other error
+ *	(iteration can continue)
+ */
+int bootflow_scan_bootdev(struct udevice *dev, struct bootflow_iter *iter,
+			  int flags, struct bootflow *bflow);
+
+/**
+ * bootflow_scan_first() - find the first bootflow
+ *
+ * This works through the available bootdev 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), with
+ * @flags:	Flags for bootdev (enum bootflow_flags_t)
+ * @bflow:	Place to put the bootflow if found
+ * Return: 0 if found, -ENODEV if no device, other -ve on other error (iteration
+ *	can continue)
+ */
+int bootflow_scan_first(struct bootflow_iter *iter, int flags,
+			struct bootflow *bflow);
+
+/**
+ * bootflow_scan_next() - find the next bootflow
+ *
+ * This works through the available bootdev devices until it finds one that
+ * can supply a bootflow. It then returns that bootflow
+ *
+ * @iter:	Private info (as set up by bootflow_scan_first())
+ * @bflow:	Place to put the bootflow if found
+ * Return: 0 if found, -ENODEV if no device, -ESHUTDOWN if no more bootflows,
+ *	other -ve on other error (iteration can continue)
+ */
+int bootflow_scan_next(struct bootflow_iter *iter, struct bootflow *bflow);
+
+/**
+ * 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, -ENOTSUPP if trying method resulted in
+ *	finding out that is not actually supported for this boot and should not
+ *	be tried again unless something changes
+ */
+int bootflow_boot(struct bootflow *bflow);
+
+/**
+ * bootflow_run_boot() - Try to boot a bootflow
+ *
+ * @iter: Current iteration (or NULL if none). Used to disable a bootmeth if the
+ *	boot returns -ENOTSUPP
+ * @bflow: Bootflow to boot
+ * Return: result of trying to boot
+ */
+int bootflow_run_boot(struct bootflow_iter *iter, struct bootflow *bflow);
+
+/**
+ * bootflow_state_get_name() - Get the name of a bootflow state
+ *
+ * @state: State to check
+ * Return: name, or "?" if invalid
+ */
+const char *bootflow_state_get_name(enum bootflow_state_t state);
+
+#endif
-- 
2.36.0.rc2.479.g8af0fa9b8e-goog


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH v5 13/34] bootstd: Add the bootstd uclass and core implementation
  2022-04-25  5:30 [PATCH v5 00/34] Initial implementation of standard boot Simon Glass
                   ` (11 preceding siblings ...)
  2022-04-25  5:31 ` [PATCH v5 12/34] bootstd: Add the concept of a bootflow Simon Glass
@ 2022-04-25  5:31 ` Simon Glass
  2022-04-25  5:31 ` [PATCH v5 14/34] bootstd: Add the bootdev uclass Simon Glass
                   ` (18 subsequent siblings)
  31 siblings, 0 replies; 36+ messages in thread
From: Simon Glass @ 2022-04-25  5:31 UTC (permalink / raw)
  To: U-Boot Mailing List
  Cc: Dennis Gilmore, Lukas Auer, Daniel Schwierzeck, Tom Rini,
	Michal Simek, Ilias Apalodimas, Steffen Jaeckel, Simon Glass

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 <sjg@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 <sjg@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 <sjg@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 <sjg@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


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH v5 14/34] bootstd: Add the bootdev uclass
  2022-04-25  5:30 [PATCH v5 00/34] Initial implementation of standard boot Simon Glass
                   ` (12 preceding siblings ...)
  2022-04-25  5:31 ` [PATCH v5 13/34] bootstd: Add the bootstd uclass and core implementation Simon Glass
@ 2022-04-25  5:31 ` Simon Glass
  2022-04-25  5:31 ` [PATCH v5 15/34] bootstd: Add the bootmeth uclass and helpers Simon Glass
                   ` (17 subsequent siblings)
  31 siblings, 0 replies; 36+ messages in thread
From: Simon Glass @ 2022-04-25  5:31 UTC (permalink / raw)
  To: U-Boot Mailing List
  Cc: Dennis Gilmore, Lukas Auer, Daniel Schwierzeck, Tom Rini,
	Michal Simek, Ilias Apalodimas, Steffen Jaeckel, Simon Glass

A 'bootdev' is a device which can be used to boot an operating system.
It is a child of the media device (e.g. MMC) which handles reading files
from that device, such as a bootflow file.

Add a uclass for bootdev and the various helpers needed to make it
work. Also add a binding file, empty for now.

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

(no changes since v4)

Changes in v4:
- Use new Return style in function comments

Changes in v3:
- Add bootdev_get_from_blk() function
- Move bootdev ordering into the uclass
- Update for the new trailing_strtoln_end()
- Use blk_find_device() to find the block device for a media device

 MAINTAINERS                          |   4 +-
 boot/Makefile                        |   1 +
 boot/bootdev-uclass.c                | 640 +++++++++++++++++++++++++++
 doc/device-tree-bindings/bootdev.txt |   8 +
 include/bootdev.h                    | 275 ++++++++++++
 include/dm/uclass-id.h               |   1 +
 6 files changed, 928 insertions(+), 1 deletion(-)
 create mode 100644 boot/bootdev-uclass.c
 create mode 100644 doc/device-tree-bindings/bootdev.txt
 create mode 100644 include/bootdev.h

diff --git a/MAINTAINERS b/MAINTAINERS
index dd099d6240f..78ec857d182 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -696,9 +696,11 @@ F:	tools/binman/
 BOOTDEVICE
 M:	Simon Glass <sjg@chromium.org>
 S:	Maintained
+F:	boot/bootdev*.c
 F:	boot/bootstd.c
-F:	include/bootstd.h
+F:	include/bootdev*.h
 F:	include/bootflow.h
+F:	include/bootstd.h
 
 BTRFS
 M:	Marek Behun <marek.behun@nic.cz>
diff --git a/boot/Makefile b/boot/Makefile
index 21dcf6a6056..301d75fb646 100644
--- a/boot/Makefile
+++ b/boot/Makefile
@@ -19,6 +19,7 @@ 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) += bootdev-uclass.o
 obj-$(CONFIG_$(SPL_TPL_)BOOTSTD) += bootstd-uclass.o
 
 obj-$(CONFIG_$(SPL_TPL_)OF_LIBFDT) += image-fdt.o
diff --git a/boot/bootdev-uclass.c b/boot/bootdev-uclass.c
new file mode 100644
index 00000000000..e0baeb82b14
--- /dev/null
+++ b/boot/bootdev-uclass.c
@@ -0,0 +1,640 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2021 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#define LOG_CATEGORY UCLASS_BOOTSTD
+
+#include <common.h>
+#include <dm.h>
+#include <bootdev.h>
+#include <bootflow.h>
+#include <bootstd.h>
+#include <env.h>
+#include <fs.h>
+#include <log.h>
+#include <malloc.h>
+#include <part.h>
+#include <sort.h>
+#include <dm/device-internal.h>
+#include <dm/lists.h>
+#include <dm/uclass-internal.h>
+
+enum {
+	/*
+	 * Set some sort of limit on the number of partitions a bootdev can
+	 * have. Note that for disks this limits the partitions numbers that
+	 * are scanned to 1..MAX_BOOTFLOWS_PER_BOOTDEV
+	 */
+	MAX_PART_PER_BOOTDEV	= 30,
+
+	/* Maximum supported length of the "boot_targets" env string */
+	BOOT_TARGETS_MAX_LEN	= 100,
+};
+
+int bootdev_add_bootflow(struct bootflow *bflow)
+{
+	struct bootdev_uc_plat *ucp = dev_get_uclass_plat(bflow->dev);
+	struct bootstd_priv *std;
+	struct bootflow *new;
+	int ret;
+
+	assert(bflow->dev);
+	ret = bootstd_get_priv(&std);
+	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, &std->glob_head);
+	list_add_tail(&new->bm_node, &ucp->bootflow_head);
+
+	return 0;
+}
+
+int bootdev_first_bootflow(struct udevice *dev, struct bootflow **bflowp)
+{
+	struct bootdev_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 bootdev_next_bootflow(struct bootflow **bflowp)
+{
+	struct bootflow *bflow = *bflowp;
+	struct bootdev_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 bootdev_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;
+	device_set_name_alloced(dev);
+	*devp = dev;
+
+	return 0;
+}
+
+int bootdev_find_in_blk(struct udevice *dev, struct udevice *blk,
+			struct bootflow_iter *iter, struct bootflow *bflow)
+{
+	struct blk_desc *desc = dev_get_uclass_plat(blk);
+	struct disk_partition info;
+	char partstr[20];
+	char name[60];
+	int ret;
+
+	/* Sanity check */
+	if (iter->part >= MAX_PART_PER_BOOTDEV)
+		return log_msg_ret("max", -ESHUTDOWN);
+
+	bflow->blk = blk;
+	if (iter->part)
+		snprintf(partstr, sizeof(partstr), "part_%x", iter->part);
+	else
+		strcpy(partstr, "whole");
+	snprintf(name, sizeof(name), "%s.%s", dev->name, partstr);
+	bflow->name = strdup(name);
+	if (!bflow->name)
+		return log_msg_ret("name", -ENOMEM);
+
+	bflow->part = iter->part;
+
+	/*
+	 * partition numbers start at 0 so this cannot succeed, but it can tell
+	 * us whether there is valid media there
+	 */
+	ret = part_get_info(desc, iter->part, &info);
+	if (!iter->part && ret == -ENOENT)
+		ret = 0;
+
+	/*
+	 * This error indicates the media is not present. Otherwise we just
+	 * blindly scan the next partition. We could be more intelligent here
+	 * and check which partition numbers actually exist.
+	 */
+	if (ret == -EOPNOTSUPP)
+		ret = -ESHUTDOWN;
+	else
+		bflow->state = BOOTFLOWST_MEDIA;
+	if (ret)
+		return log_msg_ret("part", ret);
+
+	/*
+	 * Currently we don't get the number of partitions, so just
+	 * assume a large number
+	 */
+	iter->max_part = MAX_PART_PER_BOOTDEV;
+
+	if (iter->part) {
+		ret = fs_set_blk_dev_with_part(desc, bflow->part);
+		bflow->state = BOOTFLOWST_PART;
+
+		/* Use an #ifdef due to info.sys_ind */
+#ifdef CONFIG_DOS_PARTITION
+		log_debug("%s: Found partition %x type %x fstype %d\n",
+			  blk->name, bflow->part, info.sys_ind,
+			  ret ? -1 : fs_get_type());
+#endif
+		if (ret)
+			return log_msg_ret("fs", ret);
+		bflow->state = BOOTFLOWST_FS;
+	}
+
+	return 0;
+}
+
+void bootdev_list(bool probe)
+{
+	struct udevice *dev;
+	int ret;
+	int i;
+
+	printf("Seq  Probed  Status  Uclass    Name\n");
+	printf("---  ------  ------  --------  ------------------\n");
+	if (probe)
+		ret = uclass_first_device_err(UCLASS_BOOTDEV, &dev);
+	else
+		ret = uclass_find_first_device(UCLASS_BOOTDEV, &dev);
+	for (i = 0; dev; i++) {
+		printf("%3x   [ %c ]  %6s  %-9.9s %s\n", dev_seq(dev),
+		       device_active(dev) ? '+' : ' ',
+		       ret ? simple_itoa(ret) : "OK",
+		       dev_get_uclass_name(dev_get_parent(dev)), dev->name);
+		if (probe)
+			ret = uclass_next_device_err(&dev);
+		else
+			ret = uclass_find_next_device(&dev);
+	}
+	printf("---  ------  ------  --------  ------------------\n");
+	printf("(%d bootdev%s)\n", i, i != 1 ? "s" : "");
+}
+
+int bootdev_setup_for_dev(struct udevice *parent, const char *drv_name)
+{
+	struct udevice *bdev;
+	int ret;
+
+	ret = device_find_first_child_by_uclass(parent, UCLASS_BOOTDEV,
+						&bdev);
+	if (ret) {
+		if (ret != -ENODEV) {
+			log_debug("Cannot access bootdev device\n");
+			return ret;
+		}
+
+		ret = bootdev_bind(parent, drv_name, "bootdev", &bdev);
+		if (ret) {
+			log_debug("Cannot create bootdev device\n");
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+int bootdev_setup_sibling_blk(struct udevice *blk, const char *drv_name)
+{
+	struct udevice *parent, *dev;
+	char dev_name[50];
+	int ret;
+
+	snprintf(dev_name, sizeof(dev_name), "%s.%s", blk->name, "bootdev");
+
+	parent = dev_get_parent(blk);
+	ret = device_find_child_by_name(parent, dev_name, &dev);
+	if (ret) {
+		char *str;
+
+		if (ret != -ENODEV) {
+			log_debug("Cannot access bootdev device\n");
+			return ret;
+		}
+		str = strdup(dev_name);
+		if (!str)
+			return -ENOMEM;
+
+		ret = device_bind_driver(parent, drv_name, str, &dev);
+		if (ret) {
+			log_debug("Cannot create bootdev device\n");
+			return ret;
+		}
+		device_set_name_alloced(dev);
+	}
+
+	return 0;
+}
+
+int bootdev_get_sibling_blk(struct udevice *dev, struct udevice **blkp)
+{
+	struct udevice *parent = dev_get_parent(dev);
+	struct udevice *blk;
+	int ret, len;
+	char *p;
+
+	if (device_get_uclass_id(dev) != UCLASS_BOOTDEV)
+		return -EINVAL;
+
+	/* This should always work if bootdev_setup_sibling_blk() was used */
+	p = strstr(dev->name, ".bootdev");
+	if (!p)
+		return log_msg_ret("str", -EINVAL);
+
+	len = p - dev->name;
+	ret = device_find_child_by_namelen(parent, dev->name, len, &blk);
+	if (ret)
+		return log_msg_ret("find", ret);
+	*blkp = blk;
+
+	return 0;
+}
+
+static int bootdev_get_from_blk(struct udevice *blk, struct udevice **bootdevp)
+{
+	struct udevice *parent = dev_get_parent(blk);
+	struct udevice *bootdev;
+	char dev_name[50];
+	int ret;
+
+	if (device_get_uclass_id(blk) != UCLASS_BLK)
+		return -EINVAL;
+
+	/* This should always work if bootdev_setup_sibling_blk() was used */
+	snprintf(dev_name, sizeof(dev_name), "%s.%s", blk->name, "bootdev");
+	ret = device_find_child_by_name(parent, dev_name, &bootdev);
+	if (ret)
+		return log_msg_ret("find", ret);
+	*bootdevp = bootdev;
+
+	return 0;
+}
+
+int bootdev_unbind_dev(struct udevice *parent)
+{
+	struct udevice *dev;
+	int ret;
+
+	ret = device_find_first_child_by_uclass(parent, UCLASS_BOOTDEV, &dev);
+	if (!ret) {
+		ret = device_remove(dev, DM_REMOVE_NORMAL);
+		if (ret)
+			return log_msg_ret("rem", ret);
+		ret = device_unbind(dev);
+		if (ret)
+			return log_msg_ret("unb", ret);
+	}
+
+	return 0;
+}
+
+/**
+ * bootdev_find_by_label() - Convert a label string to a bootdev device
+ *
+ * Looks up a label name to find the associated bootdev. For example, if the
+ * label name is "mmc2", this will find a bootdev for an mmc device whose
+ * sequence number is 2.
+ *
+ * @label: Label string to convert, e.g. "mmc2"
+ * @devp: Returns bootdev device corresponding to that boot label
+ * Return: 0 if OK, -EINVAL if the label name (e.g. "mmc") does not refer to a
+ *	uclass, -ENOENT if no bootdev for that media has the sequence number
+ *	(e.g. 2)
+ */
+int bootdev_find_by_label(const char *label, struct udevice **devp)
+{
+	struct udevice *media;
+	struct uclass *uc;
+	enum uclass_id id;
+	const char *end;
+	int seq;
+
+	seq = trailing_strtoln_end(label, NULL, &end);
+	id = uclass_get_by_namelen(label, end - label);
+	log_debug("find %s: seq=%d, id=%d/%s\n", label, seq, id,
+		  uclass_get_name(id));
+	if (id == UCLASS_INVALID) {
+		log_warning("Unknown uclass '%s' in label\n", label);
+		return -EINVAL;
+	}
+	if (id == UCLASS_USB)
+		id = UCLASS_MASS_STORAGE;
+
+	/* Iterate through devices in the media uclass (e.g. UCLASS_MMC) */
+	uclass_id_foreach_dev(id, media, uc) {
+		struct udevice *bdev, *blk;
+		int ret;
+
+		/* if there is no seq, match anything */
+		if (seq != -1 && dev_seq(media) != seq) {
+			log_debug("- skip, media seq=%d\n", dev_seq(media));
+			continue;
+		}
+
+		ret = device_find_first_child_by_uclass(media, UCLASS_BOOTDEV,
+							&bdev);
+		if (ret) {
+			log_debug("- looking via blk, seq=%d, id=%d\n", seq,
+				  id);
+			ret = blk_find_device(id, seq, &blk);
+			if (!ret) {
+				log_debug("- get from blk %s\n", blk->name);
+				ret = bootdev_get_from_blk(blk, &bdev);
+			}
+		}
+		if (!ret) {
+			log_debug("- found %s\n", bdev->name);
+			*devp = bdev;
+			return 0;
+		}
+		log_debug("- no device in %s\n", media->name);
+	}
+	log_warning("Unknown seq %d for label '%s'\n", seq, label);
+
+	return -ENOENT;
+}
+
+int bootdev_find_by_any(const char *name, struct udevice **devp)
+{
+	struct udevice *dev;
+	int ret, seq;
+	char *endp;
+
+	seq = simple_strtol(name, &endp, 16);
+
+	/* Select by name, label or number */
+	if (*endp) {
+		ret = uclass_get_device_by_name(UCLASS_BOOTDEV, name, &dev);
+		if (ret == -ENODEV) {
+			ret = bootdev_find_by_label(name, &dev);
+			if (ret) {
+				printf("Cannot find bootdev '%s' (err=%d)\n",
+				       name, ret);
+				return ret;
+			}
+			ret = device_probe(dev);
+		}
+		if (ret) {
+			printf("Cannot probe bootdev '%s' (err=%d)\n", name,
+			       ret);
+			return ret;
+		}
+	} else {
+		ret = uclass_get_device_by_seq(UCLASS_BOOTDEV, seq, &dev);
+	}
+	if (ret) {
+		printf("Cannot find '%s' (err=%d)\n", name, ret);
+		return ret;
+	}
+
+	*devp = dev;
+
+	return 0;
+}
+
+int bootdev_get_bootflow(struct udevice *dev, struct bootflow_iter *iter,
+			 struct bootflow *bflow)
+{
+	const struct bootdev_ops *ops = bootdev_get_ops(dev);
+
+	if (!ops->get_bootflow)
+		return -ENOSYS;
+	memset(bflow, '\0', sizeof(*bflow));
+	bflow->dev = dev;
+	bflow->method = iter->method;
+	bflow->state = BOOTFLOWST_BASE;
+
+	return ops->get_bootflow(dev, iter, bflow);
+}
+
+void bootdev_clear_bootflows(struct udevice *dev)
+{
+	struct bootdev_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);
+		/* later bootflow_remove(bflow); */
+	}
+}
+
+/**
+ * h_cmp_bootdev() - Compare two bootdevs to find out which should go first
+ *
+ * @v1: struct udevice * of first bootdev device
+ * @v2: struct udevice * of second bootdev device
+ * Return: sort order (<0 if dev1 < dev2, ==0 if equal, >0 if dev1 > dev2)
+ */
+static int h_cmp_bootdev(const void *v1, const void *v2)
+{
+	const struct udevice *dev1 = *(struct udevice **)v1;
+	const struct udevice *dev2 = *(struct udevice **)v2;
+	const struct bootdev_uc_plat *ucp1 = dev_get_uclass_plat(dev1);
+	const struct bootdev_uc_plat *ucp2 = dev_get_uclass_plat(dev2);
+	int diff;
+
+	/* Use priority first */
+	diff = ucp1->prio - ucp2->prio;
+	if (diff)
+		return diff;
+
+	/* Fall back to seq for devices of the same priority */
+	diff = dev_seq(dev1) - dev_seq(dev2);
+
+	return diff;
+}
+
+/**
+ * build_order() - Build the ordered list of bootdevs to use
+ *
+ * This builds an ordered list of devices by one of three methods:
+ * - using the boot_targets environment variable, if non-empty
+ * - using the bootdev-order devicetree property, if present
+ * - sorted by priority and sequence number
+ *
+ * @bootstd: BOOTSTD device to use
+ * @order: Bootdevs listed in default order
+ * @max_count: Number of entries in @order
+ * Return: number of bootdevs found in the ordering, or -E2BIG if the
+ * boot_targets string is too long, or -EXDEV if the ordering produced 0 results
+ */
+static int build_order(struct udevice *bootstd, struct udevice **order,
+		       int max_count)
+{
+	const char *overflow_target = NULL;
+	const char *const *labels;
+	struct udevice *dev;
+	const char *targets;
+	int i, ret, count;
+
+	targets = env_get("boot_targets");
+	labels = IS_ENABLED(CONFIG_BOOTSTD_FULL) ?
+		bootstd_get_bootdev_order(bootstd) : NULL;
+	if (targets) {
+		char str[BOOT_TARGETS_MAX_LEN];
+		char *target;
+
+		if (strlen(targets) >= BOOT_TARGETS_MAX_LEN)
+			return log_msg_ret("len", -E2BIG);
+
+		/* make a copy of the string, since strok() will change it */
+		strcpy(str, targets);
+		for (i = 0, target = strtok(str, " "); target;
+		     target = strtok(NULL, " ")) {
+			ret = bootdev_find_by_label(target, &dev);
+			if (!ret) {
+				if (i == max_count) {
+					overflow_target = target;
+					break;
+				}
+				order[i++] = dev;
+			}
+		}
+		count = i;
+	} else if (labels) {
+		int upto;
+
+		upto = 0;
+		for (i = 0; labels[i]; i++) {
+			ret = bootdev_find_by_label(labels[i], &dev);
+			if (!ret) {
+				if (upto == max_count) {
+					overflow_target = labels[i];
+					break;
+				}
+				order[upto++] = dev;
+			}
+		}
+		count = upto;
+	} else {
+		/* sort them into priority order */
+		count = max_count;
+		qsort(order, count, sizeof(struct udevice *), h_cmp_bootdev);
+	}
+
+	if (overflow_target) {
+		log_warning("Expected at most %d bootdevs, but overflowed with boot_target '%s'\n",
+			    max_count, overflow_target);
+	}
+
+	if (!count)
+		return log_msg_ret("targ", -EXDEV);
+
+	return count;
+}
+
+int bootdev_setup_iter_order(struct bootflow_iter *iter, struct udevice **devp)
+{
+	struct udevice *bootstd, *dev = *devp, **order;
+	int upto, i;
+	int count;
+	int ret;
+
+	ret = uclass_first_device_err(UCLASS_BOOTSTD, &bootstd);
+	if (ret) {
+		log_err("Missing bootstd device\n");
+		return log_msg_ret("std", ret);
+	}
+
+	/* Handle scanning a single device */
+	if (dev) {
+		iter->flags |= BOOTFLOWF_SINGLE_DEV;
+		return 0;
+	}
+
+	count = uclass_id_count(UCLASS_BOOTDEV);
+	if (!count)
+		return log_msg_ret("count", -ENOENT);
+
+	order = calloc(count, sizeof(struct udevice *));
+	if (!order)
+		return log_msg_ret("order", -ENOMEM);
+
+	/*
+	 * Get a list of bootdevs, in seq order (i.e. using aliases). There may
+	 * be gaps so try to count up high enough to find them all.
+	 */
+	for (i = 0, upto = 0; upto < count && i < 20 + count * 2; i++) {
+		ret = uclass_find_device_by_seq(UCLASS_BOOTDEV, i, &dev);
+		if (!ret)
+			order[upto++] = dev;
+	}
+	log_debug("Found %d bootdevs\n", count);
+	if (upto != count)
+		log_debug("Expected %d bootdevs, found %d using aliases\n",
+			  count, upto);
+
+	count = build_order(bootstd, order, upto);
+	if (count < 0) {
+		free(order);
+		return log_msg_ret("build", count);
+	}
+
+	iter->dev_order = order;
+	iter->num_devs = count;
+	iter->cur_dev = 0;
+
+	dev = *order;
+	ret = device_probe(dev);
+	if (ret)
+		return log_msg_ret("probe", ret);
+	*devp = dev;
+
+	return 0;
+}
+
+static int bootdev_post_bind(struct udevice *dev)
+{
+	struct bootdev_uc_plat *ucp = dev_get_uclass_plat(dev);
+
+	INIT_LIST_HEAD(&ucp->bootflow_head);
+
+	return 0;
+}
+
+static int bootdev_pre_unbind(struct udevice *dev)
+{
+	bootdev_clear_bootflows(dev);
+
+	return 0;
+}
+
+UCLASS_DRIVER(bootdev) = {
+	.id		= UCLASS_BOOTDEV,
+	.name		= "bootdev",
+	.flags		= DM_UC_FLAG_SEQ_ALIAS,
+	.per_device_plat_auto	= sizeof(struct bootdev_uc_plat),
+	.post_bind	= bootdev_post_bind,
+	.pre_unbind	= bootdev_pre_unbind,
+};
diff --git a/doc/device-tree-bindings/bootdev.txt b/doc/device-tree-bindings/bootdev.txt
new file mode 100644
index 00000000000..95b7fec8212
--- /dev/null
+++ b/doc/device-tree-bindings/bootdev.txt
@@ -0,0 +1,8 @@
+U-Boot boot device (bootdev)
+============================
+
+A bootdev provides a way to obtain a bootflow file from a device. It is a
+child of the media device (UCLASS_MMC, UCLASS_SPI_FLASH, etc.)
+
+The bootdev driver is provided by the media devices. The bindings for each
+are described in this file (to come).
diff --git a/include/bootdev.h b/include/bootdev.h
new file mode 100644
index 00000000000..9fc219839fe
--- /dev/null
+++ b/include/bootdev.h
@@ -0,0 +1,275 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright 2021 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#ifndef __bootdev_h
+#define __bootdev_h
+
+#include <linux/list.h>
+
+struct bootflow;
+struct bootflow_iter;
+struct udevice;
+
+/**
+ * enum bootdev_prio_t - priority of each bootdev
+ *
+ * These values are associated with each bootdev and set up by the driver.
+ *
+ * Smallest value is the highest priority. By default, bootdevs are scanned from
+ * highest to lowest priority
+ */
+enum bootdev_prio_t {
+	BOOTDEVP_0_INTERNAL_FAST	= 10,
+	BOOTDEVP_1_INTERNAL_SLOW	= 20,
+	BOOTDEVP_2_SCAN_FAST		= 30,
+	BOOTDEVP_3_SCAN_SLOW		= 40,
+	BOOTDEVP_4_NET_BASE		= 50,
+	BOOTDEVP_5_NET_FALLBACK		= 60,
+	BOOTDEVP_6_SYSTEM		= 70,
+
+	BOOTDEVP_COUNT,
+};
+
+/**
+ * struct bootdev_uc_plat - uclass information about a bootdev
+ *
+ * This is attached to each device in the bootdev uclass and accessible via
+ * dev_get_uclass_plat(dev)
+ *
+ * @bootflows: List of available bootflows for this bootdev
+ * @piro: Priority of this bootdev
+ */
+struct bootdev_uc_plat {
+	struct list_head bootflow_head;
+	enum bootdev_prio_t prio;
+};
+
+/** struct bootdev_ops - Operations for the bootdev uclass */
+struct bootdev_ops {
+	/**
+	 * get_bootflow() - get a bootflow
+	 *
+	 * @dev:	Bootflow device to check
+	 * @iter:	Provides current dev, part, method to get. Should update
+	 *	max_part if there is a partition table. Should update state,
+	 *	subdir, fname, buf, size according to progress
+	 * @bflow:	Updated 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, struct bootflow_iter *iter,
+			    struct bootflow *bflow);
+};
+
+#define bootdev_get_ops(dev)  ((struct bootdev_ops *)(dev)->driver->ops)
+
+/**
+ * bootdev_get_bootflow() - get a bootflow
+ *
+ * @dev:	Bootflow device to check
+ * @iter:	Provides current  part, method to get
+ * @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 bootdev_get_bootflow(struct udevice *dev, struct bootflow_iter *iter,
+			 struct bootflow *bflow);
+
+/**
+ * bootdev_bind() - Bind a new named bootdev device
+ *
+ * @parent:	Parent of the new device
+ * @drv_name:	Driver name to use for the bootdev device
+ * @name:	Name for the device (parent name is prepended)
+ * @devp:	the new device (which has not been probed)
+ */
+int bootdev_bind(struct udevice *parent, const char *drv_name, const char *name,
+		 struct udevice **devp);
+
+/**
+ * bootdev_find_in_blk() - Find a bootdev in a block device
+ *
+ * @dev: Bootflow device associated with this block device
+ * @blk: Block device to search
+ * @iter:	Provides current dev, part, method to get. Should update
+ *	max_part if there is a partition table
+ * @bflow: On entry, provides information about the partition and device to
+ *	check. On exit, returns bootflow if found
+ * Return: 0 if found, -ESHUTDOWN if no more bootflows, other -ve on error
+ */
+int bootdev_find_in_blk(struct udevice *dev, struct udevice *blk,
+			struct bootflow_iter *iter, struct bootflow *bflow);
+
+/**
+ * bootdev_list() - List all available bootdevs
+ *
+ * @probe: true to probe devices, false to leave them as is
+ */
+void bootdev_list(bool probe);
+
+/**
+ * bootdev_clear_bootflows() - Clear bootflows from a bootdev
+ *
+ * Each bootdev maintains a list of discovered bootflows. This provides a
+ * way to clear it. These bootflows are removed from the global list too.
+ *
+ * @dev: bootdev device to update
+ */
+void bootdev_clear_bootflows(struct udevice *dev);
+
+/**
+ * bootdev_add_bootflow() - Add a bootflow to the bootdev's list
+ *
+ * All fields in @bflow must be set up. Note that @bflow->dev is used to add the
+ * bootflow to that device.
+ *
+ * @dev: Bootdevice 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 bootdev_add_bootflow(struct bootflow *bflow);
+
+/**
+ * bootdev_first_bootflow() - Get the first bootflow from a bootdev
+ *
+ * Returns the first bootflow attached to a bootdev
+ *
+ * @dev: bootdev device
+ * @bflowp: Returns a pointer to the bootflow
+ * Return: 0 if found, -ENOENT if there are no bootflows
+ */
+int bootdev_first_bootflow(struct udevice *dev, struct bootflow **bflowp);
+
+/**
+ * bootdev_next_bootflow() - Get the next bootflow from a bootdev
+ *
+ * Returns the next bootflow attached to a bootdev
+ *
+ * @bflowp: On entry, the last bootflow returned , e.g. from
+ *	bootdev_first_bootflow()
+ * Return: 0 if found, -ENOENT if there are no more bootflows
+ */
+int bootdev_next_bootflow(struct bootflow **bflowp);
+
+/**
+ * bootdev_find_by_label() - Look up a bootdev by label
+ *
+ * Each bootdev has a label which contains the media-uclass name and a number,
+ * e.g. 'mmc2'. This looks up the label and returns the associated bootdev
+ *
+ * The lookup is performed based on the media device's sequence number. So for
+ * 'mmc2' this looks for a device in UCLASS_MMC with a dev_seq() of 2.
+ *
+ * @label: Label to look up (e.g. "mmc1" or "mmc0")
+ * @devp: Returns the bootdev device found, or NULL if none (note it does not
+ *	return the media device, but its bootdev child)
+ * Return: 0 if OK, -EINVAL if the uclass is not supported by this board,
+ *	-ENOENT if there is no device with that number
+ */
+int bootdev_find_by_label(const char *label, struct udevice **devp);
+
+/**
+ * bootdev_find_by_any() - Find a bootdev by name, label or sequence
+ *
+ * @name: name (e.g. "mmc2.bootdev"), label ("mmc2"), or sequence ("2") to find
+ * @devp: returns the device found, on success
+ * Return: 0 if OK, -ve on error
+ */
+int bootdev_find_by_any(const char *name, struct udevice **devp);
+
+/**
+ * bootdev_setup_iter_order() - Set up the ordering of bootdevs to scan
+ *
+ * This sets up the ordering information in @iter, based on the priority of each
+ * bootdev and the bootdev-order property in the bootstd node
+ *
+ * If a single device is requested, no ordering is needed
+ *
+ * @iter: Iterator to update with the order
+ * @devp: On entry, *devp is NULL to scan all, otherwise this is the (single)
+ *	device to scan. Returns the first device to use, which is the passed-in
+ *	@devp if it was non-NULL
+ * Return: 0 if OK, -ENOENT if no bootdevs, -ENOMEM if out of memory, other -ve
+ *	on other error
+ */
+int bootdev_setup_iter_order(struct bootflow_iter *iter, struct udevice **devp);
+
+#if CONFIG_IS_ENABLED(BOOTSTD)
+/**
+ * bootdev_setup_for_dev() - Bind a new bootdev device
+ *
+ * Creates a bootdev device as a child of @parent. This should be called from
+ * the driver's bind() method or its uclass' post_bind() method.
+ *
+ * If a child bootdev already exists, this function does nothing
+ *
+ * @parent: Parent device (e.g. MMC or Ethernet)
+ * @drv_name: Name of bootdev driver to bind
+ * Return: 0 if OK, -ve on error
+ */
+int bootdev_setup_for_dev(struct udevice *parent, const char *drv_name);
+
+/**
+ * bootdev_setup_for_blk() - Bind a new bootdev device for a blk device
+ *
+ * Creates a bootdev device as a sibling of @blk. This should be called from
+ * the driver's bind() method or its uclass' post_bind() method, at the same
+ * time as the bould device is bound
+ *
+ * If a device of the same name already exists, this function does nothing
+ *
+ * @parent: Parent device (e.g. MMC or Ethernet)
+ * @drv_name: Name of bootdev driver to bind
+ * Return: 0 if OK, -ve on error
+ */
+int bootdev_setup_sibling_blk(struct udevice *blk, const char *drv_name);
+
+/**
+ * bootdev_get_sibling_blk() - Locate the block device for a bootdev
+ *
+ * @dev: bootdev to check
+ * @blkp: returns associated block device
+ * Return: 0 if OK, -EINVAL if @dev is not a bootdev device, other -ve on other
+ *	error
+ */
+int bootdev_get_sibling_blk(struct udevice *dev, struct udevice **blkp);
+
+/**
+ * bootdev_unbind_dev() - Unbind a bootdev device
+ *
+ * Remove and unbind a bootdev device which is a child of @parent. This should
+ * be called from the driver's unbind() method or its uclass' post_bind()
+ * method.
+ *
+ * @parent: Parent device (e.g. MMC or Ethernet)
+ * Return: 0 if OK, -ve on error
+ */
+int bootdev_unbind_dev(struct udevice *parent);
+#else
+static inline int bootdev_setup_for_dev(struct udevice *parent,
+					const char *drv_name)
+{
+	return 0;
+}
+
+static inline int bootdev_setup_sibling_blk(struct udevice *blk,
+					    const char *drv_name)
+{
+	return 0;
+}
+
+static inline int bootdev_unbind_dev(struct udevice *parent)
+{
+	return 0;
+}
+#endif
+
+#endif
diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h
index cece0626a11..97ba4c02c42 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_BOOTDEV,		/* Boot device for locating an OS to boot */
 	UCLASS_BOOTSTD,		/* Standard boot driver */
 	UCLASS_BUTTON,		/* Button */
 	UCLASS_CACHE,		/* Cache controller */
-- 
2.36.0.rc2.479.g8af0fa9b8e-goog


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH v5 15/34] bootstd: Add the bootmeth uclass and helpers
  2022-04-25  5:30 [PATCH v5 00/34] Initial implementation of standard boot Simon Glass
                   ` (13 preceding siblings ...)
  2022-04-25  5:31 ` [PATCH v5 14/34] bootstd: Add the bootdev uclass Simon Glass
@ 2022-04-25  5:31 ` Simon Glass
  2022-04-25  5:31 ` [PATCH v5 16/34] bootstd: Add support for bootflows Simon Glass
                   ` (16 subsequent siblings)
  31 siblings, 0 replies; 36+ messages in thread
From: Simon Glass @ 2022-04-25  5:31 UTC (permalink / raw)
  To: U-Boot Mailing List
  Cc: Dennis Gilmore, Lukas Auer, Daniel Schwierzeck, Tom Rini,
	Michal Simek, Ilias Apalodimas, Steffen Jaeckel, Simon Glass

A bootmeth is a method of locating an operating system. For now, just
add the uclass itself. Drivers for particular bootmeths are added later.

If no bootmeths devices are included in the devicetree, create them
automatically. This avoids the need for boilerplate in the devicetree
files.

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

(no changes since v4)

Changes in v4:
- Split out the file setup into into its own function
- Use new Return style in function comments

Changes in v3:
- Add some more common functions
- Move bootmeth ordering into the uclass
- Support "bootmeths" env var

 MAINTAINERS            |   2 +
 boot/Makefile          |   1 +
 boot/bootmeth-uclass.c | 333 +++++++++++++++++++++++++++++++++++++++++
 boot/bootstd-uclass.c  |  29 +++-
 include/bootmeth.h     | 234 +++++++++++++++++++++++++++++
 include/dm/uclass-id.h |   1 +
 include/env_callback.h |   7 +
 7 files changed, 605 insertions(+), 2 deletions(-)
 create mode 100644 boot/bootmeth-uclass.c
 create mode 100644 include/bootmeth.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 78ec857d182..7b6aada33c4 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -696,10 +696,12 @@ F:	tools/binman/
 BOOTDEVICE
 M:	Simon Glass <sjg@chromium.org>
 S:	Maintained
+F:	boot/bootmeth*.c
 F:	boot/bootdev*.c
 F:	boot/bootstd.c
 F:	include/bootdev*.h
 F:	include/bootflow.h
+F:	include/bootmeth.h
 F:	include/bootstd.h
 
 BTRFS
diff --git a/boot/Makefile b/boot/Makefile
index 301d75fb646..1150051e737 100644
--- a/boot/Makefile
+++ b/boot/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_ANDROID_AB) += android_ab.o
 obj-$(CONFIG_ANDROID_BOOT_IMAGE) += image-android.o image-android-dt.o
 
 obj-$(CONFIG_$(SPL_TPL_)BOOTSTD) += bootdev-uclass.o
+obj-$(CONFIG_$(SPL_TPL_)BOOTSTD) += bootmeth-uclass.o
 obj-$(CONFIG_$(SPL_TPL_)BOOTSTD) += bootstd-uclass.o
 
 obj-$(CONFIG_$(SPL_TPL_)OF_LIBFDT) += image-fdt.o
diff --git a/boot/bootmeth-uclass.c b/boot/bootmeth-uclass.c
new file mode 100644
index 00000000000..c040d5f92b2
--- /dev/null
+++ b/boot/bootmeth-uclass.c
@@ -0,0 +1,333 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2021 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#define LOG_CATEGORY UCLASS_BOOTSTD
+
+#include <common.h>
+#include <blk.h>
+#include <bootflow.h>
+#include <bootmeth.h>
+#include <bootstd.h>
+#include <dm.h>
+#include <env_internal.h>
+#include <fs.h>
+#include <malloc.h>
+#include <mapmem.h>
+#include <dm/uclass-internal.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+int bootmeth_check(struct udevice *dev, struct bootflow_iter *iter)
+{
+	const struct bootmeth_ops *ops = bootmeth_get_ops(dev);
+
+	if (!ops->check)
+		return 0;
+
+	return ops->check(dev, iter);
+}
+
+int bootmeth_read_bootflow(struct udevice *dev, struct bootflow *bflow)
+{
+	const struct bootmeth_ops *ops = bootmeth_get_ops(dev);
+
+	if (!ops->read_bootflow)
+		return -ENOSYS;
+
+	return ops->read_bootflow(dev, bflow);
+}
+
+int bootmeth_boot(struct udevice *dev, struct bootflow *bflow)
+{
+	const struct bootmeth_ops *ops = bootmeth_get_ops(dev);
+
+	if (!ops->boot)
+		return -ENOSYS;
+
+	return ops->boot(dev, bflow);
+}
+
+int bootmeth_read_file(struct udevice *dev, struct bootflow *bflow,
+		       const char *file_path, ulong addr, ulong *sizep)
+{
+	const struct bootmeth_ops *ops = bootmeth_get_ops(dev);
+
+	if (!ops->read_file)
+		return -ENOSYS;
+
+	return ops->read_file(dev, bflow, file_path, addr, sizep);
+}
+
+/**
+ * bootmeth_setup_iter_order() - Set up the ordering of bootmeths to scan
+ *
+ * This sets up the ordering information in @iter, based on the selected
+ * ordering of the bootmethds in bootstd_priv->bootmeth_order. If there is no
+ * ordering there, then all bootmethods are added
+ *
+ * @iter: Iterator to update with the order
+ * Return: 0 if OK, -ENOENT if no bootdevs, -ENOMEM if out of memory, other -ve
+ *	on other error
+ */
+int bootmeth_setup_iter_order(struct bootflow_iter *iter)
+{
+	struct bootstd_priv *std;
+	struct udevice **order;
+	int count;
+	int ret;
+
+	ret = bootstd_get_priv(&std);
+	if (ret)
+		return ret;
+
+	/* Create an array large enough */
+	count = std->bootmeth_count ? std->bootmeth_count :
+		uclass_id_count(UCLASS_BOOTMETH);
+	if (!count)
+		return log_msg_ret("count", -ENOENT);
+
+	order = calloc(count, sizeof(struct udevice *));
+	if (!order)
+		return log_msg_ret("order", -ENOMEM);
+
+	/* If we have an ordering, copy it */
+	if (IS_ENABLED(CONFIG_BOOTSTD_FULL) && std->bootmeth_count) {
+		memcpy(order, std->bootmeth_order,
+		       count * sizeof(struct bootmeth *));
+	} else {
+		struct udevice *dev;
+		int i, upto;
+
+		/*
+		 * Get a list of bootmethods, in seq order (i.e. using aliases).
+		 * There may be gaps so try to count up high enough to find them
+		 * all.
+		 */
+		for (i = 0, upto = 0; upto < count && i < 20 + count * 2; i++) {
+			ret = uclass_get_device_by_seq(UCLASS_BOOTMETH, i,
+						       &dev);
+			if (!ret)
+				order[upto++] = dev;
+		}
+		count = upto;
+	}
+
+	iter->method_order = order;
+	iter->num_methods = count;
+	iter->cur_method = 0;
+
+	return 0;
+}
+
+int bootmeth_set_order(const char *order_str)
+{
+	struct bootstd_priv *std;
+	struct udevice **order;
+	int count, ret, i, len;
+	const char *s, *p;
+
+	ret = bootstd_get_priv(&std);
+	if (ret)
+		return ret;
+
+	if (!order_str) {
+		free(std->bootmeth_order);
+		std->bootmeth_order = NULL;
+		std->bootmeth_count = 0;
+		return 0;
+	}
+
+	/* Create an array large enough */
+	count = uclass_id_count(UCLASS_BOOTMETH);
+	if (!count)
+		return log_msg_ret("count", -ENOENT);
+
+	order = calloc(count + 1, sizeof(struct udevice *));
+	if (!order)
+		return log_msg_ret("order", -ENOMEM);
+
+	for (i = 0, s = order_str; *s && i < count; s = p + (*p == ' '), i++) {
+		struct udevice *dev;
+
+		p = strchrnul(s, ' ');
+		len = p - s;
+		ret = uclass_find_device_by_namelen(UCLASS_BOOTMETH, s, len,
+						    &dev);
+		if (ret) {
+			printf("Unknown bootmeth '%.*s'\n", len, s);
+			free(order);
+			return ret;
+		}
+		order[i] = dev;
+	}
+	order[i] = NULL;
+	free(std->bootmeth_order);
+	std->bootmeth_order = order;
+	std->bootmeth_count = i;
+
+	return 0;
+}
+
+/**
+ * setup_fs() - Set up read to read a file
+ *
+ * We must redo the setup before each filesystem operation. This function
+ * handles that, including setting the filesystem type if a block device is not
+ * being used
+ *
+ * @bflow: Information about file to try
+ * @desc: Block descriptor to read from (NULL if not a block device)
+ * Return: 0 if OK, -ve on error
+ */
+static int setup_fs(struct bootflow *bflow, struct blk_desc *desc)
+{
+	int ret;
+
+	if (desc) {
+		ret = fs_set_blk_dev_with_part(desc, bflow->part);
+		if (ret)
+			return log_msg_ret("set", ret);
+	} else if (IS_ENABLED(CONFIG_BOOTSTD_FULL) && bflow->fs_type) {
+		fs_set_type(bflow->fs_type);
+	}
+
+	return 0;
+}
+
+int bootmeth_try_file(struct bootflow *bflow, struct blk_desc *desc,
+		      const char *prefix, const char *fname)
+{
+	char path[200];
+	loff_t size;
+	int ret, ret2;
+
+	snprintf(path, sizeof(path), "%s%s", prefix ? prefix : "", fname);
+	log_debug("trying: %s\n", path);
+
+	free(bflow->fname);
+	bflow->fname = strdup(path);
+	if (!bflow->fname)
+		return log_msg_ret("name", -ENOMEM);
+
+	if (IS_ENABLED(CONFIG_BOOTSTD_FULL) && bflow->fs_type)
+		fs_set_type(bflow->fs_type);
+
+	ret = fs_size(path, &size);
+	log_debug("   %s - err=%d\n", path, ret);
+
+	/* Sadly FS closes the file after fs_size() so we must redo this */
+	ret2 = setup_fs(bflow, desc);
+	if (ret2)
+		return log_msg_ret("fs", ret2);
+
+	if (ret)
+		return log_msg_ret("size", ret);
+
+	bflow->size = size;
+	bflow->state = BOOTFLOWST_FILE;
+
+	return 0;
+}
+
+int bootmeth_alloc_file(struct bootflow *bflow, uint size_limit, uint align)
+{
+	loff_t bytes_read;
+	ulong addr;
+	char *buf;
+	uint size;
+	int ret;
+
+	size = bflow->size;
+	log_debug("   - script file size %x\n", size);
+	if (size > size_limit)
+		return log_msg_ret("chk", -E2BIG);
+
+	buf = memalign(align, size + 1);
+	if (!buf)
+		return log_msg_ret("buf", -ENOMEM);
+	addr = map_to_sysmem(buf);
+
+	ret = fs_read(bflow->fname, addr, 0, 0, &bytes_read);
+	if (ret) {
+		free(buf);
+		return log_msg_ret("read", ret);
+	}
+	if (size != bytes_read)
+		return log_msg_ret("bread", -EINVAL);
+	buf[size] = '\0';
+	bflow->state = BOOTFLOWST_READY;
+	bflow->buf = buf;
+
+	return 0;
+}
+
+int bootmeth_common_read_file(struct udevice *dev, struct bootflow *bflow,
+			      const char *file_path, ulong addr, ulong *sizep)
+{
+	struct blk_desc *desc = NULL;
+	loff_t len_read;
+	loff_t size;
+	int ret;
+
+	if (bflow->blk)
+		desc = dev_get_uclass_plat(bflow->blk);
+
+	ret = setup_fs(bflow, desc);
+	if (ret)
+		return log_msg_ret("fs", ret);
+
+	ret = fs_size(file_path, &size);
+	if (ret)
+		return log_msg_ret("size", ret);
+	if (size > *sizep)
+		return log_msg_ret("spc", -ENOSPC);
+
+	ret = setup_fs(bflow, desc);
+	if (ret)
+		return log_msg_ret("fs", ret);
+
+	ret = fs_read(file_path, addr, 0, 0, &len_read);
+	if (ret)
+		return ret;
+	*sizep = len_read;
+
+	return 0;
+}
+
+#ifdef CONFIG_BOOTSTD_FULL
+/**
+ * on_bootmeths() - Update the bootmeth order
+ *
+ * This will check for a valid baudrate and only apply it if valid.
+ */
+static int on_bootmeths(const char *name, const char *value, enum env_op op,
+			int flags)
+{
+	int ret;
+
+	switch (op) {
+	case env_op_create:
+	case env_op_overwrite:
+		ret = bootmeth_set_order(value);
+		if (ret)
+			return 1;
+		return 0;
+	case env_op_delete:
+		bootmeth_set_order(NULL);
+		fallthrough;
+	default:
+		return 0;
+	}
+}
+U_BOOT_ENV_CALLBACK(bootmeths, on_bootmeths);
+#endif /* CONFIG_BOOTSTD_FULL */
+
+UCLASS_DRIVER(bootmeth) = {
+	.id		= UCLASS_BOOTMETH,
+	.name		= "bootmeth",
+	.flags		= DM_UC_FLAG_SEQ_ALIAS,
+	.per_device_plat_auto	= sizeof(struct bootmeth_uc_plat),
+};
diff --git a/boot/bootstd-uclass.c b/boot/bootstd-uclass.c
index 615cd89bf0b..4c71c2829ef 100644
--- a/boot/bootstd-uclass.c
+++ b/boot/bootstd-uclass.c
@@ -109,8 +109,10 @@ static int bootstd_probe(struct udevice *dev)
 /* 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;
+	struct driver *drv = ll_entry_start(struct driver, driver);
+	const int n_ents = ll_entry_count(struct driver, driver);
+	struct udevice *dev, *bootstd;
+	int i, ret;
 
 	/* These are not needed before relocation */
 	if (!(gd->flags & GD_FLG_RELOC))
@@ -125,6 +127,29 @@ int dm_scan_other(bool pre_reloc_only)
 			return log_msg_ret("bootstd", ret);
 	}
 
+	/* If there are no bootmeth devices, create them */
+	uclass_find_first_device(UCLASS_BOOTMETH, &dev);
+	if (dev)
+		return 0;
+
+	for (i = 0; i < n_ents; i++, drv++) {
+		/*
+		 * Disable EFI Manager for now as no one uses it so it is
+		 * confusing
+		 */
+		if (drv->id == UCLASS_BOOTMETH &&
+		    strcmp("efi_mgr_bootmeth", drv->name)) {
+			const char *name = drv->name;
+
+			if (!strncmp("bootmeth_", name, 9))
+				name += 9;
+			ret = device_bind(bootstd, drv, name, 0, ofnode_null(),
+					  &dev);
+			if (ret)
+				return log_msg_ret("meth", ret);
+		}
+	}
+
 	return 0;
 }
 
diff --git a/include/bootmeth.h b/include/bootmeth.h
new file mode 100644
index 00000000000..484e503e338
--- /dev/null
+++ b/include/bootmeth.h
@@ -0,0 +1,234 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright 2021 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#ifndef __bootmeth_h
+#define __bootmeth_h
+
+struct blk_desc;
+struct bootflow;
+struct bootflow_iter;
+struct udevice;
+
+/**
+ * struct bootmeth_uc_plat - information the uclass keeps about each bootmeth
+ *
+ * @desc: A long description of the bootmeth
+ */
+struct bootmeth_uc_plat {
+	const char *desc;
+};
+
+/** struct bootmeth_ops - Operations for boot methods */
+struct bootmeth_ops {
+	/**
+	 * check_supported() - check if a bootmeth supports this bootflow
+	 *
+	 * This is optional. If not provided, the bootdev is assumed to be
+	 * supported
+	 *
+	 * The bootmeth can check the bootdev (e.g. to make sure it is a
+	 * network device) or the partition information. The following fields
+	 * in @iter are available:
+	 *
+	 *   name, dev, state, part
+	 *   max_part may be set if part != 0 (i.e. there is a valid partition
+	 *      table). Otherwise max_part is 0
+	 *   method is available but is the same as @dev
+	 *   the partition has not yet been read, nor has the filesystem been
+	 *   checked
+	 *
+	 * It may update only the flags in @iter
+	 *
+	 * @dev:	Bootmethod device to check against
+	 * @iter:	On entry, provides bootdev, hwpart, part
+	 * Return: 0 if OK, -ENOTSUPP if this bootdev is not supported
+	 */
+	int (*check)(struct udevice *dev, struct bootflow_iter *iter);
+
+	/**
+	 * read_bootflow() - read a bootflow for a device
+	 *
+	 * @dev:	Bootmethod device to use
+	 * @bflow:	On entry, provides dev, hwpart, part and method.
+	 *	Returns updated bootflow if found
+	 * Return: 0 if OK, -ve on error
+	 */
+	int (*read_bootflow)(struct udevice *dev, struct bootflow *bflow);
+
+	/**
+	 * read_file() - read a file needed for a bootflow
+	 *
+	 * Read a file from the same place as the bootflow came from
+	 *
+	 * @dev:	Bootmethod device to use
+	 * @bflow:	Bootflow providing info on where to read from
+	 * @file_path:	Path to file (may be absolute or relative)
+	 * @addr:	Address to load file
+	 * @sizep:	On entry provides the maximum permitted size; on exit
+	 *		returns the size of the file
+	 * Return: 0 if OK, -ENOSPC if the file is too large for @sizep, other
+	 *	-ve value if something else goes wrong
+	 */
+	int (*read_file)(struct udevice *dev, struct bootflow *bflow,
+			 const char *file_path, ulong addr, ulong *sizep);
+
+	/**
+	 * boot() - boot a bootflow
+	 *
+	 * @dev:	Bootmethod device to boot
+	 * @bflow:	Bootflow to boot
+	 * Return: does not return on success, since it should boot the
+	 *	Operating Systemn. Returns -EFAULT if that fails, -ENOTSUPP if
+	 *	trying method resulted in finding out that is not actually
+	 *	supported for this boot and should not be tried again unless
+	 *	something changes, other -ve on other error
+	 */
+	int (*boot)(struct udevice *dev, struct bootflow *bflow);
+};
+
+#define bootmeth_get_ops(dev)  ((struct bootmeth_ops *)(dev)->driver->ops)
+
+/**
+ * bootmeth_check() - check if a bootmeth supports this bootflow
+ *
+ * This is optional. If not provided, the bootdev is assumed to be
+ * supported
+ *
+ * The bootmeth can check the bootdev (e.g. to make sure it is a
+ * network device) or the partition information. The following fields
+ * in @iter are available:
+ *
+ *   name, dev, state, part
+ *   max_part may be set if part != 0 (i.e. there is a valid partition
+ *      table). Otherwise max_part is 0
+ *   method is available but is the same as @dev
+ *   the partition has not yet been read, nor has the filesystem been
+ *   checked
+ *
+ * It may update only the flags in @iter
+ *
+ * @dev:	Bootmethod device to check against
+ * @iter:	On entry, provides bootdev, hwpart, part
+ * Return: 0 if OK, -ENOTSUPP if this bootdev is not supported
+ */
+int bootmeth_check(struct udevice *dev, struct bootflow_iter *iter);
+
+/**
+ * bootmeth_read_bootflow() - set up a bootflow for a device
+ *
+ * @dev:	Bootmethod device to check
+ * @bflow:	On entry, provides dev, hwpart, part and method.
+ *	Returns updated bootflow if found
+ * Return: 0 if OK, -ve on error
+ */
+int bootmeth_read_bootflow(struct udevice *dev, struct bootflow *bflow);
+
+/**
+ * bootmeth_read_file() - read a file needed for a bootflow
+ *
+ * Read a file from the same place as the bootflow came from
+ *
+ * @dev:	Bootmethod device to use
+ * @bflow:	Bootflow providing info on where to read from
+ * @file_path:	Path to file (may be absolute or relative)
+ * @addr:	Address to load file
+ * @sizep:	On entry provides the maximum permitted size; on exit
+ *		returns the size of the file
+ * Return: 0 if OK, -ENOSPC if the file is too large for @sizep, other
+ *	-ve value if something else goes wrong
+ */
+int bootmeth_read_file(struct udevice *dev, struct bootflow *bflow,
+		       const char *file_path, ulong addr, ulong *sizep);
+
+/**
+ * bootmeth_boot() - boot a bootflow
+ *
+ * @dev:	Bootmethod device to boot
+ * @bflow:	Bootflow to boot
+ * Return: does not return on success, since it should boot the
+ *	Operating Systemn. Returns -EFAULT if that fails, other -ve on
+ *	other error
+ */
+int bootmeth_boot(struct udevice *dev, struct bootflow *bflow);
+
+/**
+ * bootmeth_setup_iter_order() - Set up the ordering of bootmeths to scan
+ *
+ * This sets up the ordering information in @iter, based on the selected
+ * ordering of the bootmethds in bootstd_priv->bootmeth_order. If there is no
+ * ordering there, then all bootmethods are added
+ *
+ * @iter: Iterator to update with the order
+ * Return: 0 if OK, -ENOENT if no bootdevs, -ENOMEM if out of memory, other -ve
+ *	on other error
+ */
+int bootmeth_setup_iter_order(struct bootflow_iter *iter);
+
+/**
+ * bootmeth_set_order() - Set the bootmeth order
+ *
+ * This selects the ordering to use for bootmeths
+ *
+ * @order_str: String containing the ordering. This is a comma-separate list of
+ * bootmeth-device names, e.g. "syslinux,efi". If empty then a default ordering
+ * is used, based on the sequence number of devices (i.e. using aliases)
+ * Return: 0 if OK, -ENODEV if an unknown bootmeth is mentioned, -ENOMEM if
+ * out of memory, -ENOENT if there are no bootmeth devices
+ */
+int bootmeth_set_order(const char *order_str);
+
+/**
+ * bootmeth_try_file() - See we can access a given file
+ *
+ * Check for a file with a given name. If found, the filename is allocated in
+ * @bflow
+ *
+ * Sets the state to BOOTFLOWST_FILE on success. It also calls
+ * fs_set_blk_dev_with_part() so that this does not need to be done by the
+ * caller before reading the file.
+ *
+ * @bflow: Information about file to try
+ * @desc: Block descriptor to read from
+ * @prefix: Filename prefix to prepend to @fname (NULL for none)
+ * @fname: Filename to read
+ * Return: 0 if OK, -ENOMEM if not enough memory to allocate bflow->fname,
+ * other -ve value on other error
+ */
+int bootmeth_try_file(struct bootflow *bflow, struct blk_desc *desc,
+		      const char *prefix, const char *fname);
+
+/**
+ * bootmeth_alloc_file() - Allocate and read a bootflow file
+ *
+ * Allocates memory for a bootflow file and reads it in. Sets the state to
+ * BOOTFLOWST_READY on success
+ *
+ * Note that fs_set_blk_dev_with_part() must have been called previously.
+ *
+ * @bflow: Information about file to read
+ * @size_limit: Maximum file size to permit
+ * @align: Allocation alignment (1 for unaligned)
+ * Return: 0 if OK, -E2BIG if file is too large, -ENOMEM if out of memory,
+ *	other -ve on other error
+ */
+int bootmeth_alloc_file(struct bootflow *bflow, uint size_limit, uint align);
+
+/**
+ * bootmeth_common_read_file() - Common handler for reading a file
+ *
+ * Reads a named file from the same location as the bootflow file.
+ *
+ * @dev: bootmeth device to read from
+ * @bflow: Bootflow information
+ * @file_path: Path to file
+ * @addr: Address to load file to
+ * @sizep: On entry, the maximum file size to accept, on exit the actual file
+ *	size read
+ */
+int bootmeth_common_read_file(struct udevice *dev, struct bootflow *bflow,
+			      const char *file_path, ulong addr, ulong *sizep);
+
+#endif
diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h
index 97ba4c02c42..5cee8bebf8e 100644
--- a/include/dm/uclass-id.h
+++ b/include/dm/uclass-id.h
@@ -39,6 +39,7 @@ enum uclass_id {
 	UCLASS_BLK,		/* Block device */
 	UCLASS_BOOTCOUNT,       /* Bootcount backing store */
 	UCLASS_BOOTDEV,		/* Boot device for locating an OS to boot */
+	UCLASS_BOOTMETH,	/* Bootmethod for booting an OS */
 	UCLASS_BOOTSTD,		/* Standard boot driver */
 	UCLASS_BUTTON,		/* Button */
 	UCLASS_CACHE,		/* Cache controller */
diff --git a/include/env_callback.h b/include/env_callback.h
index 05e9516a0f4..d5d2b2fcad6 100644
--- a/include/env_callback.h
+++ b/include/env_callback.h
@@ -57,6 +57,12 @@
 #define NET_CALLBACKS
 #endif
 
+#ifdef CONFIG_BOOTSTD
+#define BOOTSTD_CALLBACK	"bootmeths:bootmeths,"
+#else
+#define BOOTSTD_CALLBACK
+#endif
+
 /*
  * This list of callback bindings is static, but may be overridden by defining
  * a new association in the ".callbacks" environment variable.
@@ -65,6 +71,7 @@
 	ENV_DOT_ESCAPE ENV_FLAGS_VAR ":flags," \
 	"baudrate:baudrate," \
 	NET_CALLBACKS \
+	BOOTSTD_CALLBACK \
 	"loadaddr:loadaddr," \
 	SILENT_CALLBACK \
 	SPLASHIMAGE_CALLBACK \
-- 
2.36.0.rc2.479.g8af0fa9b8e-goog


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH v5 16/34] bootstd: Add support for bootflows
  2022-04-25  5:30 [PATCH v5 00/34] Initial implementation of standard boot Simon Glass
                   ` (14 preceding siblings ...)
  2022-04-25  5:31 ` [PATCH v5 15/34] bootstd: Add the bootmeth uclass and helpers Simon Glass
@ 2022-04-25  5:31 ` Simon Glass
  2022-04-25  5:31 ` [PATCH v5 17/34] bootstd: Add a bootdev command Simon Glass
                   ` (15 subsequent siblings)
  31 siblings, 0 replies; 36+ messages in thread
From: Simon Glass @ 2022-04-25  5:31 UTC (permalink / raw)
  To: U-Boot Mailing List
  Cc: Dennis Gilmore, Lukas Auer, Daniel Schwierzeck, Tom Rini,
	Michal Simek, Ilias Apalodimas, Steffen Jaeckel, Simon Glass

Add support for bootflows, including maintaining a list of them and
iterating to find them.

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

(no changes since v4)

Changes in v4:
- Show the bootmeth name when booting
- Use new Return style in function comments

Changes in v3:
- Explain why it is OK to use "?" as an unknown bootflow state
- Move bootmeth/bootdev ordering into the uclass
- Point to the header file for bootflow_state[] docs

 MAINTAINERS           |   3 +-
 boot/Makefile         |   1 +
 boot/bootdev-uclass.c |  11 +-
 boot/bootflow.c       | 411 ++++++++++++++++++++++++++++++++++++++++++
 boot/bootstd-uclass.c |   2 +-
 include/bootflow.h    |  50 +++++
 6 files changed, 475 insertions(+), 3 deletions(-)
 create mode 100644 boot/bootflow.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 7b6aada33c4..24e2ad75f82 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -696,8 +696,9 @@ F:	tools/binman/
 BOOTDEVICE
 M:	Simon Glass <sjg@chromium.org>
 S:	Maintained
-F:	boot/bootmeth*.c
 F:	boot/bootdev*.c
+F:	boot/bootflow.c
+F:	boot/bootmeth*.c
 F:	boot/bootstd.c
 F:	include/bootdev*.h
 F:	include/bootflow.h
diff --git a/boot/Makefile b/boot/Makefile
index 1150051e737..ac861fd035f 100644
--- a/boot/Makefile
+++ b/boot/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_ANDROID_AB) += android_ab.o
 obj-$(CONFIG_ANDROID_BOOT_IMAGE) += image-android.o image-android-dt.o
 
 obj-$(CONFIG_$(SPL_TPL_)BOOTSTD) += bootdev-uclass.o
+obj-$(CONFIG_$(SPL_TPL_)BOOTSTD) += bootflow.o
 obj-$(CONFIG_$(SPL_TPL_)BOOTSTD) += bootmeth-uclass.o
 obj-$(CONFIG_$(SPL_TPL_)BOOTSTD) += bootstd-uclass.o
 
diff --git a/boot/bootdev-uclass.c b/boot/bootdev-uclass.c
index e0baeb82b14..1ede933c2f2 100644
--- a/boot/bootdev-uclass.c
+++ b/boot/bootdev-uclass.c
@@ -10,6 +10,7 @@
 #include <dm.h>
 #include <bootdev.h>
 #include <bootflow.h>
+#include <bootmeth.h>
 #include <bootstd.h>
 #include <env.h>
 #include <fs.h>
@@ -130,6 +131,10 @@ int bootdev_find_in_blk(struct udevice *dev, struct udevice *blk,
 
 	bflow->part = iter->part;
 
+	ret = bootmeth_check(bflow->method, iter);
+	if (ret)
+		return log_msg_ret("check", ret);
+
 	/*
 	 * partition numbers start at 0 so this cannot succeed, but it can tell
 	 * us whether there is valid media there
@@ -171,6 +176,10 @@ int bootdev_find_in_blk(struct udevice *dev, struct udevice *blk,
 		bflow->state = BOOTFLOWST_FS;
 	}
 
+	ret = bootmeth_read_bootflow(bflow->method, bflow);
+	if (ret)
+		return log_msg_ret("method", ret);
+
 	return 0;
 }
 
@@ -445,7 +454,7 @@ void bootdev_clear_bootflows(struct udevice *dev)
 
 		bflow = list_first_entry(&ucp->bootflow_head, struct bootflow,
 					 bm_node);
-		/* later bootflow_remove(bflow); */
+		bootflow_remove(bflow);
 	}
 }
 
diff --git a/boot/bootflow.c b/boot/bootflow.c
new file mode 100644
index 00000000000..24ba3c34660
--- /dev/null
+++ b/boot/bootflow.c
@@ -0,0 +1,411 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2021 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#define LOG_CATEGORY UCLASS_BOOTSTD
+
+#include <common.h>
+#include <bootdev.h>
+#include <bootflow.h>
+#include <bootmeth.h>
+#include <bootstd.h>
+#include <dm.h>
+#include <malloc.h>
+#include <dm/device-internal.h>
+#include <dm/uclass-internal.h>
+
+/* error codes used to signal running out of things */
+enum {
+	BF_NO_MORE_PARTS	= -ESHUTDOWN,
+	BF_NO_MORE_DEVICES	= -ENODEV,
+};
+
+/**
+ * bootflow_state - name for each state
+ *
+ * See enum bootflow_state_t for what each of these means
+ */
+static const char *const bootflow_state[BOOTFLOWST_COUNT] = {
+	"base",
+	"media",
+	"part",
+	"fs",
+	"file",
+	"ready",
+};
+
+const char *bootflow_state_get_name(enum bootflow_state_t state)
+{
+	/* This doesn't need to be a useful name, since it will never occur */
+	if (state < 0 || state >= BOOTFLOWST_COUNT)
+		return "?";
+
+	return bootflow_state[state];
+}
+
+int bootflow_first_glob(struct bootflow **bflowp)
+{
+	struct bootstd_priv *std;
+	int ret;
+
+	ret = bootstd_get_priv(&std);
+	if (ret)
+		return ret;
+
+	if (list_empty(&std->glob_head))
+		return -ENOENT;
+
+	*bflowp = list_first_entry(&std->glob_head, struct bootflow,
+				   glob_node);
+
+	return 0;
+}
+
+int bootflow_next_glob(struct bootflow **bflowp)
+{
+	struct bootstd_priv *std;
+	struct bootflow *bflow = *bflowp;
+	int ret;
+
+	ret = bootstd_get_priv(&std);
+	if (ret)
+		return ret;
+
+	*bflowp = NULL;
+
+	if (list_is_last(&bflow->glob_node, &std->glob_head))
+		return -ENOENT;
+
+	*bflowp = list_entry(bflow->glob_node.next, struct bootflow, glob_node);
+
+	return 0;
+}
+
+void bootflow_iter_init(struct bootflow_iter *iter, int flags)
+{
+	memset(iter, '\0', sizeof(*iter));
+	iter->flags = flags;
+}
+
+void bootflow_iter_uninit(struct bootflow_iter *iter)
+{
+	free(iter->dev_order);
+	free(iter->method_order);
+}
+
+int bootflow_iter_drop_bootmeth(struct bootflow_iter *iter,
+				const struct udevice *bmeth)
+{
+	/* We only support disabling the current bootmeth */
+	if (bmeth != iter->method || iter->cur_method >= iter->num_methods ||
+	    iter->method_order[iter->cur_method] != bmeth)
+		return -EINVAL;
+
+	memmove(&iter->method_order[iter->cur_method],
+		&iter->method_order[iter->cur_method + 1],
+		(iter->num_methods - iter->cur_method - 1) * sizeof(void *));
+
+	iter->num_methods--;
+
+	return 0;
+}
+
+static void bootflow_iter_set_dev(struct bootflow_iter *iter,
+				  struct udevice *dev)
+{
+	iter->dev = dev;
+	if ((iter->flags & (BOOTFLOWF_SHOW | BOOTFLOWF_SINGLE_DEV)) ==
+	    BOOTFLOWF_SHOW) {
+		if (dev)
+			printf("Scanning bootdev '%s':\n", dev->name);
+		else
+			printf("No more bootdevs\n");
+	}
+}
+
+/**
+ * iter_incr() - Move to the next item (method, part, bootdev)
+ *
+ * Return: 0 if OK, BF_NO_MORE_DEVICES if there are no more bootdevs
+ */
+static int iter_incr(struct bootflow_iter *iter)
+{
+	struct udevice *dev;
+	int ret;
+
+	if (iter->err == BF_NO_MORE_DEVICES)
+		return BF_NO_MORE_DEVICES;
+
+	if (iter->err != BF_NO_MORE_PARTS) {
+		/* Get the next boothmethod */
+		if (++iter->cur_method < iter->num_methods) {
+			iter->method = iter->method_order[iter->cur_method];
+			return 0;
+		}
+	}
+
+	/* No more bootmeths; start at the first one, and... */
+	iter->cur_method = 0;
+	iter->method = iter->method_order[iter->cur_method];
+
+	if (iter->err != BF_NO_MORE_PARTS) {
+		/* ...select next partition  */
+		if (++iter->part <= iter->max_part)
+			return 0;
+	}
+
+	/* No more partitions; start at the first one and...*/
+	iter->part = 0;
+
+	/*
+	 * Note: as far as we know, there is no partition table on the next
+	 * bootdev, so set max_part to 0 until we discover otherwise. See
+	 * bootdev_find_in_blk() for where this is set.
+	 */
+	iter->max_part = 0;
+
+	/* ...select next bootdev */
+	if (iter->flags & BOOTFLOWF_SINGLE_DEV) {
+		ret = -ENOENT;
+	} else if (++iter->cur_dev == iter->num_devs) {
+		ret = -ENOENT;
+		bootflow_iter_set_dev(iter, NULL);
+	} else {
+		dev = iter->dev_order[iter->cur_dev];
+		ret = device_probe(dev);
+		if (!log_msg_ret("probe", ret))
+			bootflow_iter_set_dev(iter, dev);
+	}
+
+	/* if there are no more bootdevs, give up */
+	if (ret)
+		return log_msg_ret("incr", BF_NO_MORE_DEVICES);
+
+	return 0;
+}
+
+/**
+ * bootflow_check() - Check if a bootflow can be obtained
+ *
+ * @iter: Provides part, bootmeth to use
+ * @bflow: Bootflow to update on success
+ * Return: 0 if OK, -ENOSYS if there is no bootflow support on this device,
+ *	BF_NO_MORE_PARTS if there are no more partitions on bootdev
+ */
+static int bootflow_check(struct bootflow_iter *iter, struct bootflow *bflow)
+{
+	struct udevice *dev;
+	int ret;
+
+	dev = iter->dev;
+	ret = bootdev_get_bootflow(dev, iter, bflow);
+
+	/* If we got a valid bootflow, return it */
+	if (!ret) {
+		log_debug("Bootdevice '%s' part %d method '%s': Found bootflow\n",
+			  dev->name, iter->part, iter->method->name);
+		return 0;
+	}
+
+	/* Unless there is nothing more to try, move to the next device */
+	else if (ret != BF_NO_MORE_PARTS && ret != -ENOSYS) {
+		log_debug("Bootdevice '%s' part %d method '%s': Error %d\n",
+			  dev->name, iter->part, iter->method->name, ret);
+		/*
+		 * For 'all' we return all bootflows, even
+		 * those with errors
+		 */
+		if (iter->flags & BOOTFLOWF_ALL)
+			return log_msg_ret("all", ret);
+	}
+	if (ret)
+		return log_msg_ret("check", ret);
+
+	return 0;
+}
+
+int bootflow_scan_bootdev(struct udevice *dev, struct bootflow_iter *iter,
+			  int flags, struct bootflow *bflow)
+{
+	int ret;
+
+	bootflow_iter_init(iter, flags);
+
+	ret = bootdev_setup_iter_order(iter, &dev);
+	if (ret)
+		return log_msg_ret("obdev", -ENODEV);
+	bootflow_iter_set_dev(iter, dev);
+
+	ret = bootmeth_setup_iter_order(iter);
+	if (ret)
+		return log_msg_ret("obmeth", -ENODEV);
+
+	/* Find the first bootmeth (there must be at least one!) */
+	iter->method = iter->method_order[iter->cur_method];
+
+	ret = bootflow_check(iter, bflow);
+	if (ret) {
+		if (ret != BF_NO_MORE_PARTS && ret != -ENOSYS) {
+			if (iter->flags & BOOTFLOWF_ALL)
+				return log_msg_ret("all", ret);
+		}
+		iter->err = ret;
+		ret = bootflow_scan_next(iter, bflow);
+		if (ret)
+			return log_msg_ret("get", ret);
+	}
+
+	return 0;
+}
+
+int bootflow_scan_first(struct bootflow_iter *iter, int flags,
+			struct bootflow *bflow)
+{
+	int ret;
+
+	ret = bootflow_scan_bootdev(NULL, iter, flags, bflow);
+	if (ret)
+		return log_msg_ret("start", ret);
+
+	return 0;
+}
+
+int bootflow_scan_next(struct bootflow_iter *iter, struct bootflow *bflow)
+{
+	int ret;
+
+	do {
+		ret = iter_incr(iter);
+		if (ret == BF_NO_MORE_DEVICES)
+			return log_msg_ret("done", ret);
+
+		if (!ret) {
+			ret = bootflow_check(iter, bflow);
+			if (!ret)
+				return 0;
+			iter->err = ret;
+			if (ret != BF_NO_MORE_PARTS && ret != -ENOSYS) {
+				if (iter->flags & BOOTFLOWF_ALL)
+					return log_msg_ret("all", ret);
+			}
+		} else {
+			iter->err = ret;
+		}
+
+	} while (1);
+}
+
+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);
+}
+
+int bootflow_boot(struct bootflow *bflow)
+{
+	int ret;
+
+	if (bflow->state != BOOTFLOWST_READY)
+		return log_msg_ret("load", -EPROTO);
+
+	ret = bootmeth_boot(bflow->method, bflow);
+	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 bootflow_run_boot(struct bootflow_iter *iter, struct bootflow *bflow)
+{
+	int ret;
+
+	printf("** Booting bootflow '%s' with %s\n", bflow->name,
+	       bflow->method->name);
+	ret = bootflow_boot(bflow);
+	if (!IS_ENABLED(CONFIG_BOOTSTD_FULL)) {
+		printf("Boot failed (err=%d)\n", ret);
+		return ret;
+	}
+
+	switch (ret) {
+	case -EPROTO:
+		printf("Bootflow not loaded (state '%s')\n",
+		       bootflow_state_get_name(bflow->state));
+		break;
+	case -ENOSYS:
+		printf("Boot method '%s' not supported\n", bflow->method->name);
+		break;
+	case -ENOTSUPP:
+		/* Disable this bootflow for this iteration */
+		if (iter) {
+			int ret2;
+
+			ret2 = bootflow_iter_drop_bootmeth(iter, bflow->method);
+			if (!ret2) {
+				printf("Boot method '%s' failed and will not be retried\n",
+				       bflow->method->name);
+			}
+		}
+
+		break;
+	default:
+		printf("Boot failed (err=%d)\n", ret);
+		break;
+	}
+
+	return ret;
+}
+
+int bootflow_iter_uses_blk_dev(const struct bootflow_iter *iter)
+{
+	const struct udevice *media = dev_get_parent(iter->dev);
+	enum uclass_id id = device_get_uclass_id(media);
+
+	log_debug("uclass %d: %s\n", id, uclass_get_name(id));
+	if (id != UCLASS_ETH && id != UCLASS_BOOTSTD)
+		return 0;
+
+	return -ENOTSUPP;
+}
+
+int bootflow_iter_uses_network(const struct bootflow_iter *iter)
+{
+	const struct udevice *media = dev_get_parent(iter->dev);
+	enum uclass_id id = device_get_uclass_id(media);
+
+	log_debug("uclass %d: %s\n", id, uclass_get_name(id));
+	if (id == UCLASS_ETH)
+		return 0;
+
+	return -ENOTSUPP;
+}
+
+int bootflow_iter_uses_system(const struct bootflow_iter *iter)
+{
+	const struct udevice *media = dev_get_parent(iter->dev);
+	enum uclass_id id = device_get_uclass_id(media);
+
+	log_debug("uclass %d: %s\n", id, uclass_get_name(id));
+	if (id == UCLASS_BOOTSTD)
+		return 0;
+
+	return -ENOTSUPP;
+}
diff --git a/boot/bootstd-uclass.c b/boot/bootstd-uclass.c
index 4c71c2829ef..266bd7cb2e3 100644
--- a/boot/bootstd-uclass.c
+++ b/boot/bootstd-uclass.c
@@ -45,7 +45,7 @@ static void bootstd_clear_glob_(struct bootstd_priv *priv)
 
 		bflow = list_first_entry(&priv->glob_head, struct bootflow,
 					 glob_node);
-		/* add later bootflow_remove(bflow); */
+		bootflow_remove(bflow);
 	}
 }
 
diff --git a/include/bootflow.h b/include/bootflow.h
index 6e9a729a9a3..c30ba042a48 100644
--- a/include/bootflow.h
+++ b/include/bootflow.h
@@ -151,6 +151,18 @@ void bootflow_iter_init(struct bootflow_iter *iter, int flags);
  */
 void bootflow_iter_uninit(struct bootflow_iter *iter);
 
+/**
+ * bootflow_iter_drop_bootmeth() - Remove a bootmeth from an iterator
+ *
+ * Update the iterator so that the bootmeth will not be used again while this
+ * iterator is in use
+ *
+ * @iter: Iterator to update
+ * @bmeth: Boot method to remove
+ */
+int bootflow_iter_drop_bootmeth(struct bootflow_iter *iter,
+				const struct udevice *bmeth);
+
 /**
  * bootflow_scan_bootdev() - find the first bootflow in a bootdev
  *
@@ -257,4 +269,42 @@ int bootflow_run_boot(struct bootflow_iter *iter, struct bootflow *bflow);
  */
 const char *bootflow_state_get_name(enum bootflow_state_t state);
 
+/**
+ * bootflow_remove() - Remove a bootflow and free its memory
+ *
+ * This updates the linked lists containing the bootflow then frees it.
+ *
+ * @bflow: Bootflow to remove
+ */
+void bootflow_remove(struct bootflow *bflow);
+
+/**
+ * bootflow_iter_uses_blk_dev() - Check that a bootflow uses a block device
+ *
+ * This checks the bootdev in the bootflow to make sure it uses a block device
+ *
+ * Return: 0 if OK, -ENOTSUPP if some other device is used (e.g. ethernet)
+ */
+int bootflow_iter_uses_blk_dev(const struct bootflow_iter *iter);
+
+/**
+ * bootflow_iter_uses_network() - Check that a bootflow uses a network device
+ *
+ * This checks the bootdev in the bootflow to make sure it uses a network
+ * device
+ *
+ * Return: 0 if OK, -ENOTSUPP if some other device is used (e.g. MMC)
+ */
+int bootflow_iter_uses_network(const struct bootflow_iter *iter);
+
+/**
+ * bootflow_iter_uses_system() - Check that a bootflow uses the bootstd device
+ *
+ * This checks the bootdev in the bootflow to make sure it uses the bootstd
+ * device
+ *
+ * Return: 0 if OK, -ENOTSUPP if some other device is used (e.g. MMC)
+ */
+int bootflow_iter_uses_system(const struct bootflow_iter *iter);
+
 #endif
-- 
2.36.0.rc2.479.g8af0fa9b8e-goog


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH v5 17/34] bootstd: Add a bootdev command
  2022-04-25  5:30 [PATCH v5 00/34] Initial implementation of standard boot Simon Glass
                   ` (15 preceding siblings ...)
  2022-04-25  5:31 ` [PATCH v5 16/34] bootstd: Add support for bootflows Simon Glass
@ 2022-04-25  5:31 ` Simon Glass
  2022-04-25  5:31 ` [PATCH v5 18/34] bootstd: Add a bootflow command Simon Glass
                   ` (14 subsequent siblings)
  31 siblings, 0 replies; 36+ messages in thread
From: Simon Glass @ 2022-04-25  5:31 UTC (permalink / raw)
  To: U-Boot Mailing List
  Cc: Dennis Gilmore, Lukas Auer, Daniel Schwierzeck, Tom Rini,
	Michal Simek, Ilias Apalodimas, Steffen Jaeckel, Simon Glass

Add a 'bootdev' command to handle listing and selection of bootdevs.

Disable standard boot for a few boards which otherwise run out of space.

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

(no changes since v3)

Changes in v3:
- Use separate Kconfig options for each command

 MAINTAINERS                        |   1 +
 cmd/Kconfig                        |  10 +++
 cmd/Makefile                       |   1 +
 cmd/bootdev.c                      | 120 +++++++++++++++++++++++++++++
 configs/rcar3_salvator-x_defconfig |   1 +
 configs/tbs2910_defconfig          |   1 +
 6 files changed, 134 insertions(+)
 create mode 100644 cmd/bootdev.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 24e2ad75f82..5a48b841990 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -700,6 +700,7 @@ F:	boot/bootdev*.c
 F:	boot/bootflow.c
 F:	boot/bootmeth*.c
 F:	boot/bootstd.c
+F:	cmd/bootdev.c
 F:	include/bootdev*.h
 F:	include/bootflow.h
 F:	include/bootmeth.h
diff --git a/cmd/Kconfig b/cmd/Kconfig
index f580797d2d3..a5c69187911 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -211,6 +211,16 @@ config CMD_BOOTM_PRE_LOAD
 	 This stage allow to check or modify the image provided
 	 to the bootm command.
 
+config CMD_BOOTDEV
+	bool "bootdev"
+	depends on BOOTSTD
+	default y if BOOTSTD_FULL
+	help
+	  Support listing available bootdevs (boot devices) which can provide an
+	  OS to boot, as well as showing information about a particular one.
+
+	  This command is not necessary for bootstd to work.
+
 config BOOTM_EFI
 	bool "Support booting UEFI FIT images"
 	depends on CMD_BOOTEFI && CMD_BOOTM && FIT
diff --git a/cmd/Makefile b/cmd/Makefile
index ede634d731c..474f6d55f84 100644
--- a/cmd/Makefile
+++ b/cmd/Makefile
@@ -19,6 +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_BOOTDEV) += bootdev.o
 obj-$(CONFIG_CMD_SOURCE) += source.o
 obj-$(CONFIG_CMD_BCB) += bcb.o
 obj-$(CONFIG_CMD_BDI) += bdinfo.o
diff --git a/cmd/bootdev.c b/cmd/bootdev.c
new file mode 100644
index 00000000000..ecd797c0503
--- /dev/null
+++ b/cmd/bootdev.c
@@ -0,0 +1,120 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * 'bootdev' command
+ *
+ * Copyright 2021 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <common.h>
+#include <bootdev.h>
+#include <bootflow.h>
+#include <bootstd.h>
+#include <command.h>
+#include <dm.h>
+#include <dm/device-internal.h>
+#include <dm/uclass-internal.h>
+
+static int bootdev_check_state(struct bootstd_priv **stdp)
+{
+	struct bootstd_priv *std;
+	int ret;
+
+	ret = bootstd_get_priv(&std);
+	if (ret)
+		return ret;
+	if (!std->cur_bootdev) {
+		printf("Please use 'bootdev select' first\n");
+		return -ENOENT;
+	}
+	*stdp = std;
+
+	return 0;
+}
+
+static int do_bootdev_list(struct cmd_tbl *cmdtp, int flag, int argc,
+			   char *const argv[])
+{
+	bool probe;
+
+	probe = argc >= 2 && !strcmp(argv[1], "-p");
+	bootdev_list(probe);
+
+	return 0;
+}
+
+static int do_bootdev_select(struct cmd_tbl *cmdtp, int flag, int argc,
+			     char *const argv[])
+{
+	struct bootstd_priv *std;
+	struct udevice *dev;
+	int ret;
+
+	ret = bootstd_get_priv(&std);
+	if (ret)
+		return CMD_RET_FAILURE;
+	if (argc < 2) {
+		std->cur_bootdev = NULL;
+		return 0;
+	}
+	if (bootdev_find_by_any(argv[1], &dev))
+		return CMD_RET_FAILURE;
+
+	std->cur_bootdev = dev;
+
+	return 0;
+}
+
+static int do_bootdev_info(struct cmd_tbl *cmdtp, int flag, int argc,
+			   char *const argv[])
+{
+	struct bootstd_priv *priv;
+	struct bootflow *bflow;
+	int ret, i, num_valid;
+	struct udevice *dev;
+	bool probe;
+
+	probe = argc >= 2 && !strcmp(argv[1], "-p");
+
+	ret = bootdev_check_state(&priv);
+	if (ret)
+		return CMD_RET_FAILURE;
+
+	dev = priv->cur_bootdev;
+
+	/* Count the number of bootflows, including how many are valid*/
+	num_valid = 0;
+	for (ret = bootdev_first_bootflow(dev, &bflow), i = 0;
+	     !ret;
+	     ret = bootdev_next_bootflow(&bflow), i++)
+		num_valid += bflow->state == BOOTFLOWST_READY;
+
+	/*
+	 * Prove the device, if requested, otherwise assume that there is no
+	 * error
+	 */
+	ret = 0;
+	if (probe)
+		ret = device_probe(dev);
+
+	printf("Name:      %s\n", dev->name);
+	printf("Sequence:  %d\n", dev_seq(dev));
+	printf("Status:    %s\n", ret ? simple_itoa(ret) : device_active(dev) ?
+		"Probed" : "OK");
+	printf("Uclass:    %s\n", dev_get_uclass_name(dev_get_parent(dev)));
+	printf("Bootflows: %d (%d valid)\n", i, num_valid);
+
+	return 0;
+}
+
+#ifdef CONFIG_SYS_LONGHELP
+static char bootdev_help_text[] =
+	"list [-p]      - list all available bootdevs (-p to probe)\n"
+	"bootdev select <bd>    - select a bootdev by name | label | seq\n"
+	"bootdev info [-p]      - show information about a bootdev (-p to probe)";
+#endif
+
+U_BOOT_CMD_WITH_SUBCMDS(bootdev, "Boot devices", bootdev_help_text,
+	U_BOOT_SUBCMD_MKENT(list, 2, 1, do_bootdev_list),
+	U_BOOT_SUBCMD_MKENT(select, 2, 1, do_bootdev_select),
+	U_BOOT_SUBCMD_MKENT(info, 2, 1, do_bootdev_info));
diff --git a/configs/rcar3_salvator-x_defconfig b/configs/rcar3_salvator-x_defconfig
index f333291aa4b..6ef62ab4cbb 100644
--- a/configs/rcar3_salvator-x_defconfig
+++ b/configs/rcar3_salvator-x_defconfig
@@ -14,6 +14,7 @@ CONFIG_SYS_LOAD_ADDR=0x58000000
 CONFIG_LTO=y
 CONFIG_REMAKE_ELF=y
 CONFIG_FIT=y
+# CONFIG_BOOTSTD is not set
 CONFIG_SUPPORT_RAW_INITRD=y
 CONFIG_SYS_MONITOR_BASE=0x00000000
 CONFIG_USE_BOOTARGS=y
diff --git a/configs/tbs2910_defconfig b/configs/tbs2910_defconfig
index 60a816059a6..9f7642f25ca 100644
--- a/configs/tbs2910_defconfig
+++ b/configs/tbs2910_defconfig
@@ -17,6 +17,7 @@ CONFIG_SYS_MEMTEST_END=0x2f400000
 CONFIG_LTO=y
 CONFIG_HAS_BOARD_SIZE_LIMIT=y
 CONFIG_BOARD_SIZE_LIMIT=392192
+# CONFIG_BOOTSTD is not set
 CONFIG_SUPPORT_RAW_INITRD=y
 CONFIG_BOOTDELAY=3
 CONFIG_USE_BOOTCOMMAND=y
-- 
2.36.0.rc2.479.g8af0fa9b8e-goog


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH v5 18/34] bootstd: Add a bootflow command
  2022-04-25  5:30 [PATCH v5 00/34] Initial implementation of standard boot Simon Glass
                   ` (16 preceding siblings ...)
  2022-04-25  5:31 ` [PATCH v5 17/34] bootstd: Add a bootdev command Simon Glass
@ 2022-04-25  5:31 ` Simon Glass
  2022-04-25  5:31 ` [PATCH v5 19/34] bootstd: Add a bootmeth command Simon Glass
                   ` (13 subsequent siblings)
  31 siblings, 0 replies; 36+ messages in thread
From: Simon Glass @ 2022-04-25  5:31 UTC (permalink / raw)
  To: U-Boot Mailing List
  Cc: Dennis Gilmore, Lukas Auer, Daniel Schwierzeck, Tom Rini,
	Michal Simek, Ilias Apalodimas, Steffen Jaeckel, Simon Glass

Add a 'bootflow' command to handle listing and selection of bootflow.

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

(no changes since v4)

Changes in v4:
- Make CMD_BOOTSTD_FULL depend on BOOTSTD_FULL
- Warn when trying to use the full bootflow command but it is not enabled

Changes in v3:
- Put some features behind CONFIG_BOOTFLOW_FULL
- Use separate Kconfig options for each command

 cmd/Kconfig    |  18 +++
 cmd/Makefile   |   1 +
 cmd/bootflow.c | 404 +++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 423 insertions(+)
 create mode 100644 cmd/bootflow.c

diff --git a/cmd/Kconfig b/cmd/Kconfig
index a5c69187911..d2ffc5370a9 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -221,6 +221,24 @@ config CMD_BOOTDEV
 
 	  This command is not necessary for bootstd to work.
 
+config CMD_BOOTFLOW
+	bool "bootflow"
+	depends on BOOTSTD
+	default y
+	help
+	  Support scanning for bootflows available with the bootdevs. The
+	  bootflows can optionally be booted.
+
+config CMD_BOOTFLOW_FULL
+	bool "bootflow - extract subcommands"
+	depends on BOOTSTD_FULL
+	default y if BOOTSTD_FULL
+	help
+	  Add the ability to list the available bootflows, select one and obtain
+	  information about it.
+
+	  This command is not necessary for bootstd to work.
+
 config BOOTM_EFI
 	bool "Support booting UEFI FIT images"
 	depends on CMD_BOOTEFI && CMD_BOOTM && FIT
diff --git a/cmd/Makefile b/cmd/Makefile
index 474f6d55f84..addb8f02056 100644
--- a/cmd/Makefile
+++ b/cmd/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_CMD_ADC) += adc.o
 obj-$(CONFIG_CMD_ARMFLASH) += armflash.o
 obj-$(CONFIG_HAVE_BLOCK_DEVICE) += blk_common.o
 obj-$(CONFIG_CMD_BOOTDEV) += bootdev.o
+obj-$(CONFIG_CMD_BOOTFLOW) += 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..af4b9c37323
--- /dev/null
+++ b/cmd/bootflow.c
@@ -0,0 +1,404 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * 'bootflow' command
+ *
+ * Copyright 2021 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <common.h>
+#include <bootdev.h>
+#include <bootflow.h>
+#include <bootstd.h>
+#include <command.h>
+#include <console.h>
+#include <dm.h>
+#include <mapmem.h>
+
+/**
+ * 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_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;
+	bool has_args;
+	int ret, i;
+	int flags;
+
+	ret = bootstd_get_priv(&std);
+	if (ret)
+		return CMD_RET_FAILURE;
+	dev = std->cur_bootdev;
+
+	has_args = argc > 1 && *argv[1] == '-';
+	if (IS_ENABLED(CONFIG_CMD_BOOTFLOW_FULL)) {
+		if (has_args) {
+			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;
+		}
+	} else {
+		if (has_args) {
+			printf("Flags not supported: enable CONFIG_BOOTFLOW_FULL\n");
+			return CMD_RET_USAGE;
+		}
+		boot = true;
+	}
+
+	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 (IS_ENABLED(CONFIG_CMD_BOOTFLOW_FULL) && 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;
+}
+
+#ifdef CONFIG_CMD_BOOTFLOW_FULL
+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_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;
+}
+#endif /* CONFIG_CMD_BOOTFLOW_FULL */
+
+#ifdef CONFIG_SYS_LONGHELP
+static char bootflow_help_text[] =
+#ifdef CONFIG_CMD_BOOTFLOW_FULL
+	"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 [<num>|<name>] - 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)";
+#else
+	"scan - boot first available bootflow\n";
+#endif
+#endif /* CONFIG_SYS_LONGHELP */
+
+U_BOOT_CMD_WITH_SUBCMDS(bootflow, "Boot flows", bootflow_help_text,
+	U_BOOT_SUBCMD_MKENT(scan, 3, 1, do_bootflow_scan),
+#ifdef CONFIG_CMD_BOOTFLOW_FULL
+	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)
+#endif
+);
-- 
2.36.0.rc2.479.g8af0fa9b8e-goog


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH v5 19/34] bootstd: Add a bootmeth command
  2022-04-25  5:30 [PATCH v5 00/34] Initial implementation of standard boot Simon Glass
                   ` (17 preceding siblings ...)
  2022-04-25  5:31 ` [PATCH v5 18/34] bootstd: Add a bootflow command Simon Glass
@ 2022-04-25  5:31 ` Simon Glass
  2022-04-25  5:31 ` [PATCH v5 21/34] bootstd: mmc: Add a bootdev driver Simon Glass
                   ` (12 subsequent siblings)
  31 siblings, 0 replies; 36+ messages in thread
From: Simon Glass @ 2022-04-25  5:31 UTC (permalink / raw)
  To: U-Boot Mailing List
  Cc: Dennis Gilmore, Lukas Auer, Daniel Schwierzeck, Tom Rini,
	Michal Simek, Ilias Apalodimas, Steffen Jaeckel, Simon Glass

Add a 'bootmeth' command to handle listing and ordering of boot methods.

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

(no changes since v3)

Changes in v3:
- Move bootmeth ordering into the uclass

 cmd/Kconfig    |  11 +++++
 cmd/Makefile   |   1 +
 cmd/bootmeth.c | 113 +++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 125 insertions(+)
 create mode 100644 cmd/bootmeth.c

diff --git a/cmd/Kconfig b/cmd/Kconfig
index d2ffc5370a9..1205ef501e9 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -239,6 +239,17 @@ config CMD_BOOTFLOW_FULL
 
 	  This command is not necessary for bootstd to work.
 
+config CMD_BOOTMETH
+	bool "bootmeth"
+	depends on BOOTSTD
+	default y if BOOTSTD_FULL
+	help
+	  Support listing available bootmethds (methods used to boot an
+	  Operating System), as well as selecting the order that the bootmeths
+	  are used.
+
+	  This command is not necessary for bootstd to work.
+
 config BOOTM_EFI
 	bool "Support booting UEFI FIT images"
 	depends on CMD_BOOTEFI && CMD_BOOTM && FIT
diff --git a/cmd/Makefile b/cmd/Makefile
index addb8f02056..5e43a1e022e 100644
--- a/cmd/Makefile
+++ b/cmd/Makefile
@@ -21,6 +21,7 @@ obj-$(CONFIG_CMD_ARMFLASH) += armflash.o
 obj-$(CONFIG_HAVE_BLOCK_DEVICE) += blk_common.o
 obj-$(CONFIG_CMD_BOOTDEV) += bootdev.o
 obj-$(CONFIG_CMD_BOOTFLOW) += bootflow.o
+obj-$(CONFIG_CMD_BOOTMETH) += bootmeth.o
 obj-$(CONFIG_CMD_SOURCE) += source.o
 obj-$(CONFIG_CMD_BCB) += bcb.o
 obj-$(CONFIG_CMD_BDI) += bdinfo.o
diff --git a/cmd/bootmeth.c b/cmd/bootmeth.c
new file mode 100644
index 00000000000..c9a27fe8ac6
--- /dev/null
+++ b/cmd/bootmeth.c
@@ -0,0 +1,113 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * 'bootmeth' command
+ *
+ * Copyright 2021 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <common.h>
+#include <bootdev.h>
+#include <bootmeth.h>
+#include <bootstd.h>
+#include <command.h>
+#include <dm.h>
+#include <malloc.h>
+#include <dm/uclass-internal.h>
+
+static int do_bootmeth_list(struct cmd_tbl *cmdtp, int flag, int argc,
+			    char *const argv[])
+{
+	struct bootstd_priv *std;
+	struct udevice *dev;
+	bool use_order;
+	bool all = false;
+	int ret;
+	int i;
+
+	if (argc > 1 && *argv[1] == '-') {
+		all = strchr(argv[1], 'a');
+		argc--;
+		argv++;
+	}
+
+	ret = bootstd_get_priv(&std);
+	if (ret) {
+		printf("Cannot get bootstd (err=%d)\n", ret);
+		return CMD_RET_FAILURE;
+	}
+
+	printf("Order  Seq  Name                Description\n");
+	printf("-----  ---  ------------------  ------------------\n");
+
+	/*
+	 * Use the ordering if we have one, so long as we are not trying to list
+	 * all bootmethds
+	 */
+	use_order = std->bootmeth_count && !all;
+	if (use_order)
+		dev = std->bootmeth_order[0];
+	else
+		ret = uclass_find_first_device(UCLASS_BOOTMETH, &dev);
+
+	for (i = 0; dev;) {
+		struct bootmeth_uc_plat *ucp = dev_get_uclass_plat(dev);
+		int order = i;
+
+		/*
+		 * With the -a flag we may list bootdevs that are not in the
+		 * ordering. Find their place in the order
+		 */
+		if (all && std->bootmeth_count) {
+			int j;
+
+			/* Find the position of this bootmeth in the order */
+			order = -1;
+			for (j = 0; j < std->bootmeth_count; j++) {
+				if (std->bootmeth_order[j] == dev)
+					order = j;
+			}
+		}
+
+		if (order == -1)
+			printf("%5s", "-");
+		else
+			printf("%5x", order);
+		printf("  %3x  %-19.19s %s\n", dev_seq(dev), dev->name,
+		       ucp->desc);
+		i++;
+		if (use_order)
+			dev = std->bootmeth_order[i];
+		else
+			uclass_find_next_device(&dev);
+	}
+	printf("-----  ---  ------------------  ------------------\n");
+	printf("(%d bootmeth%s)\n", i, i != 1 ? "s" : "");
+
+	return 0;
+}
+
+static int do_bootmeth_order(struct cmd_tbl *cmdtp, int flag, int argc,
+			     char *const argv[])
+{
+	int ret;
+
+	ret = bootmeth_set_order(argv[1]);
+	if (ret) {
+		printf("Failed (err=%d)\n", ret);
+		return CMD_RET_FAILURE;
+	}
+	env_set("bootmeths", argv[1]);
+
+	return 0;
+}
+
+#ifdef CONFIG_SYS_LONGHELP
+static char bootmeth_help_text[] =
+	"list [-a]     - list available bootmeths (-a all)\n"
+	"bootmeth order [<bd> ...]  - select bootmeth order / subset to use";
+#endif
+
+U_BOOT_CMD_WITH_SUBCMDS(bootmeth, "Boot methods", bootmeth_help_text,
+	U_BOOT_SUBCMD_MKENT(list, 2, 1, do_bootmeth_list),
+	U_BOOT_SUBCMD_MKENT(order, CONFIG_SYS_MAXARGS, 1, do_bootmeth_order));
-- 
2.36.0.rc2.479.g8af0fa9b8e-goog


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH v5 21/34] bootstd: mmc: Add a bootdev driver
  2022-04-25  5:30 [PATCH v5 00/34] Initial implementation of standard boot Simon Glass
                   ` (18 preceding siblings ...)
  2022-04-25  5:31 ` [PATCH v5 19/34] bootstd: Add a bootmeth command Simon Glass
@ 2022-04-25  5:31 ` Simon Glass
  2022-04-25  5:31 ` [PATCH v5 22/34] bootstd: ethernet: " Simon Glass
                   ` (11 subsequent siblings)
  31 siblings, 0 replies; 36+ messages in thread
From: Simon Glass @ 2022-04-25  5:31 UTC (permalink / raw)
  To: U-Boot Mailing List
  Cc: Dennis Gilmore, Lukas Auer, Daniel Schwierzeck, Tom Rini,
	Michal Simek, Ilias Apalodimas, Steffen Jaeckel, Simon Glass,
	Jaehoon Chung, Peng Fan

Add a bootdev driver for MMC. It mostly just calls the bootdev helper
function.

Add a function to obtain the block device for an MMC controller.

Fix up the comment for mmc_get_blk_desc() while we are here.

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

(no changes since v4)

Changes in v4:
- Use new Return style in function comments

Changes in v3:
- Adjust for new blk_find_device() function

 MAINTAINERS               |  1 +
 drivers/mmc/Makefile      |  5 ++++
 drivers/mmc/mmc-uclass.c  | 23 +++++++++++++++
 drivers/mmc/mmc_bootdev.c | 62 +++++++++++++++++++++++++++++++++++++++
 include/mmc.h             | 12 +++++++-
 5 files changed, 102 insertions(+), 1 deletion(-)
 create mode 100644 drivers/mmc/mmc_bootdev.c

diff --git a/MAINTAINERS b/MAINTAINERS
index d9125fca850..239fa2eeb75 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -702,6 +702,7 @@ F:	boot/bootmeth*.c
 F:	boot/bootstd.c
 F:	cmd/bootdev.c
 F:	cmd/bootflow.c
+F:	drivers/mmc/mmc_bootdev.c
 F:	include/bootdev.h
 F:	include/bootflow.h
 F:	include/bootmeth.h
diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile
index 17ebc04203e..96275093022 100644
--- a/drivers/mmc/Makefile
+++ b/drivers/mmc/Makefile
@@ -5,6 +5,11 @@
 
 obj-y += mmc.o
 obj-$(CONFIG_$(SPL_)DM_MMC) += mmc-uclass.o
+
+ifdef CONFIG_$(SPL_TPL_)DM_MMC
+obj-$(CONFIG_$(SPL_TPL_)BOOTSTD) += mmc_bootdev.o
+endif
+
 obj-$(CONFIG_$(SPL_)MMC_WRITE) += mmc_write.o
 obj-$(CONFIG_MMC_PWRSEQ) += mmc-pwrseq.o
 obj-$(CONFIG_MMC_SDHCI_ADMA_HELPERS) += sdhci-adma.o
diff --git a/drivers/mmc/mmc-uclass.c b/drivers/mmc/mmc-uclass.c
index 57da788ad80..688bdc06d42 100644
--- a/drivers/mmc/mmc-uclass.c
+++ b/drivers/mmc/mmc-uclass.c
@@ -8,6 +8,7 @@
 #define LOG_CATEGORY UCLASS_MMC
 
 #include <common.h>
+#include <bootdev.h>
 #include <log.h>
 #include <mmc.h>
 #include <dm.h>
@@ -315,6 +316,20 @@ int mmc_get_next_devnum(void)
 	return blk_find_max_devnum(IF_TYPE_MMC);
 }
 
+int mmc_get_blk(struct udevice *dev, struct udevice **blkp)
+{
+	struct udevice *blk;
+	int ret;
+
+	device_find_first_child_by_uclass(dev, UCLASS_BLK, &blk);
+	ret = device_probe(blk);
+	if (ret)
+		return ret;
+	*blkp = blk;
+
+	return 0;
+}
+
 struct blk_desc *mmc_get_blk_desc(struct mmc *mmc)
 {
 	struct blk_desc *desc;
@@ -406,6 +421,10 @@ int mmc_bind(struct udevice *dev, struct mmc *mmc, const struct mmc_config *cfg)
 	mmc->cfg = cfg;
 	mmc->priv = dev;
 
+	ret = bootdev_setup_for_dev(dev, "mmc_bootdev");
+	if (ret)
+		return log_msg_ret("bootdev", ret);
+
 	/* the following chunk was from mmc_register() */
 
 	/* Setup dsr related values */
@@ -424,12 +443,16 @@ int mmc_bind(struct udevice *dev, struct mmc *mmc, const struct mmc_config *cfg)
 int mmc_unbind(struct udevice *dev)
 {
 	struct udevice *bdev;
+	int ret;
 
 	device_find_first_child_by_uclass(dev, UCLASS_BLK, &bdev);
 	if (bdev) {
 		device_remove(bdev, DM_REMOVE_NORMAL);
 		device_unbind(bdev);
 	}
+	ret = bootdev_unbind_dev(dev);
+	if (ret)
+		return log_msg_ret("bootdev", ret);
 
 	return 0;
 }
diff --git a/drivers/mmc/mmc_bootdev.c b/drivers/mmc/mmc_bootdev.c
new file mode 100644
index 00000000000..b4f41fb3a67
--- /dev/null
+++ b/drivers/mmc/mmc_bootdev.c
@@ -0,0 +1,62 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Bootdevice for MMC
+ *
+ * Copyright 2021 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <common.h>
+#include <bootdev.h>
+#include <dm.h>
+#include <mmc.h>
+
+static int mmc_get_bootflow(struct udevice *dev, struct bootflow_iter *iter,
+			    struct bootflow *bflow)
+{
+	struct udevice *mmc_dev = dev_get_parent(dev);
+	struct udevice *blk;
+	int ret;
+
+	ret = mmc_get_blk(mmc_dev, &blk);
+	/*
+	 * If there is no media, indicate that no more partitions should be
+	 * checked
+	 */
+	if (ret == -EOPNOTSUPP)
+		ret = -ESHUTDOWN;
+	if (ret)
+		return log_msg_ret("blk", ret);
+	assert(blk);
+	ret = bootdev_find_in_blk(dev, blk, iter, bflow);
+	if (ret)
+		return log_msg_ret("find", ret);
+
+	return 0;
+}
+
+static int mmc_bootdev_bind(struct udevice *dev)
+{
+	struct bootdev_uc_plat *ucp = dev_get_uclass_plat(dev);
+
+	ucp->prio = BOOTDEVP_0_INTERNAL_FAST;
+
+	return 0;
+}
+
+struct bootdev_ops mmc_bootdev_ops = {
+	.get_bootflow	= mmc_get_bootflow,
+};
+
+static const struct udevice_id mmc_bootdev_ids[] = {
+	{ .compatible = "u-boot,bootdev-mmc" },
+	{ }
+};
+
+U_BOOT_DRIVER(mmc_bootdev) = {
+	.name		= "mmc_bootdev",
+	.id		= UCLASS_BOOTDEV,
+	.ops		= &mmc_bootdev_ops,
+	.bind		= mmc_bootdev_bind,
+	.of_match	= mmc_bootdev_ids,
+};
diff --git a/include/mmc.h b/include/mmc.h
index 6bdcce881dd..9b4dc683110 100644
--- a/include/mmc.h
+++ b/include/mmc.h
@@ -956,10 +956,20 @@ int mmc_get_env_dev(void);
  * mmc_get_blk_desc() - Get the block descriptor for an MMC device
  *
  * @mmc:	MMC device
- * Return: block device if found, else NULL
+ * Return: block descriptor if found, else NULL
  */
 struct blk_desc *mmc_get_blk_desc(struct mmc *mmc);
 
+/**
+ * mmc_get_blk() - Get the block device for an MMC device
+ *
+ * @dev:	MMC device
+ * @blkp:	Returns pointer to probed block device on sucesss
+ *
+ * Return: 0 on success, -ve on error
+ */
+int mmc_get_blk(struct udevice *dev, struct udevice **blkp);
+
 /**
  * mmc_send_ext_csd() - read the extended CSD register
  *
-- 
2.36.0.rc2.479.g8af0fa9b8e-goog


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH v5 22/34] bootstd: ethernet: Add a bootdev driver
  2022-04-25  5:30 [PATCH v5 00/34] Initial implementation of standard boot Simon Glass
                   ` (19 preceding siblings ...)
  2022-04-25  5:31 ` [PATCH v5 21/34] bootstd: mmc: Add a bootdev driver Simon Glass
@ 2022-04-25  5:31 ` Simon Glass
  2022-04-25  5:31 ` [PATCH v5 23/34] bootstd: Add an implementation of distro PXE boot Simon Glass
                   ` (10 subsequent siblings)
  31 siblings, 0 replies; 36+ messages in thread
From: Simon Glass @ 2022-04-25  5:31 UTC (permalink / raw)
  To: U-Boot Mailing List
  Cc: Dennis Gilmore, Lukas Auer, Daniel Schwierzeck, Tom Rini,
	Michal Simek, Ilias Apalodimas, Steffen Jaeckel, Simon Glass

Add a bootdev driver for Ethernet. It can use the PXE boot mechanism to
locate a file, added later.

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

(no changes since v1)

 MAINTAINERS       |   1 +
 net/Kconfig       |   9 +++++
 net/Makefile      |   1 +
 net/eth-uclass.c  |   8 ++++
 net/eth_bootdev.c | 101 ++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 120 insertions(+)
 create mode 100644 net/eth_bootdev.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 239fa2eeb75..77a34082249 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -707,6 +707,7 @@ F:	include/bootdev.h
 F:	include/bootflow.h
 F:	include/bootmeth.h
 F:	include/bootstd.h
+F:	net/eth_bootdevice.c
 
 BTRFS
 M:	Marek Behun <marek.behun@nic.cz>
diff --git a/net/Kconfig b/net/Kconfig
index ef0aa161f73..964a4fe4999 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -25,6 +25,15 @@ config PROT_UDP
 	  Enable a generic udp framework that allows defining a custom
 	  handler for udp protocol.
 
+config BOOTDEV_ETH
+	bool "Enable bootdev for ethernet"
+	depends on BOOTSTD
+	default y
+	help
+	  Provide a bootdev for ethernet so that is it possible to boot
+	  an operationg system over the network, using the PXE (Preboot
+	  Execution Environment) protocol.
+
 config BOOTP_SEND_HOSTNAME
 	bool "Send hostname to DNS server"
 	help
diff --git a/net/Makefile b/net/Makefile
index fb3eba840ff..6c812502d3e 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_CMD_DNS)  += dns.o
 obj-$(CONFIG_DM_DSA)   += dsa-uclass.o
 ifdef CONFIG_DM_ETH
 obj-$(CONFIG_NET)      += eth-uclass.o
+obj-$(CONFIG_$(SPL_TPL_)BOOTDEV_ETH) += eth_bootdev.o
 else
 obj-$(CONFIG_NET)      += eth_legacy.o
 endif
diff --git a/net/eth-uclass.c b/net/eth-uclass.c
index 58c308f3327..bcefc54ded8 100644
--- a/net/eth-uclass.c
+++ b/net/eth-uclass.c
@@ -8,6 +8,7 @@
 #define LOG_CATEGORY UCLASS_ETH
 
 #include <common.h>
+#include <bootdev.h>
 #include <bootstage.h>
 #include <dm.h>
 #include <env.h>
@@ -473,6 +474,8 @@ int eth_initialize(void)
 
 static int eth_post_bind(struct udevice *dev)
 {
+	int ret;
+
 	if (strchr(dev->name, ' ')) {
 		printf("\nError: eth device name \"%s\" has a space!\n",
 		       dev->name);
@@ -482,6 +485,11 @@ static int eth_post_bind(struct udevice *dev)
 #ifdef CONFIG_DM_ETH_PHY
 	eth_phy_binds_nodes(dev);
 #endif
+	if (CONFIG_IS_ENABLED(BOOTDEV_ETH)) {
+		ret = bootdev_setup_for_dev(dev, "eth_bootdev");
+		if (ret)
+			return log_msg_ret("bootdev", ret);
+	}
 
 	return 0;
 }
diff --git a/net/eth_bootdev.c b/net/eth_bootdev.c
new file mode 100644
index 00000000000..b735966d2bc
--- /dev/null
+++ b/net/eth_bootdev.c
@@ -0,0 +1,101 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Bootdevice for ethernet (uses PXE)
+ *
+ * Copyright 2021 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <common.h>
+#include <bootdev.h>
+#include <bootflow.h>
+#include <command.h>
+#include <bootmeth.h>
+#include <distro.h>
+#include <dm.h>
+#include <log.h>
+#include <net.h>
+
+static int eth_get_bootflow(struct udevice *dev, struct bootflow_iter *iter,
+			    struct bootflow *bflow)
+{
+	char name[60];
+	int ret;
+
+	/* Must be an Ethernet device */
+	ret = bootflow_iter_uses_network(iter);
+	if (ret)
+		return log_msg_ret("net", ret);
+
+	ret = bootmeth_check(bflow->method, iter);
+	if (ret)
+		return log_msg_ret("check", ret);
+
+	/*
+	 * Like distro boot, this assumes there is only one Ethernet device.
+	 * In this case, that means that @eth is ignored
+	 */
+
+	snprintf(name, sizeof(name), "%s.%d", dev->name, iter->part);
+	bflow->name = strdup(name);
+	if (!bflow->name)
+		return log_msg_ret("name", -ENOMEM);
+
+	/*
+	 * There is not a direct interface to the network stack so run
+	 * everything through the command-line interpreter for now.
+	 *
+	 * Don't bother checking the result of dhcp. It can fail with:
+	 *
+	 * DHCP client bound to address 192.168.4.50 (4 ms)
+	 * *** Warning: no boot file name; using 'C0A80432.img'
+	 * Using smsc95xx_eth device
+	 * TFTP from server 192.168.4.1; our IP address is 192.168.4.50
+	 * Filename 'C0A80432.img'.
+	 * Load address: 0x200000
+	 * Loading: *
+	 * TFTP error: 'File not found' (1)
+	 *
+	 * This is not a real failure, since we don't actually care if the
+	 * boot file exists.
+	 */
+	log_debug("running dhcp\n");
+	run_command("dhcp", 0);
+	bflow->state = BOOTFLOWST_MEDIA;
+
+	/* See distro_pxe_read_bootflow() for the standard impl of this */
+	log_debug("dhcp complete - reading bootflow with method %s\n",
+		  bflow->method->name);
+	ret = bootmeth_read_bootflow(bflow->method, bflow);
+	log_debug("reading bootflow returned %d\n", ret);
+	if (ret)
+		return log_msg_ret("method", ret);
+
+	return 0;
+}
+
+static int eth_bootdev_bind(struct udevice *dev)
+{
+	struct bootdev_uc_plat *ucp = dev_get_uclass_plat(dev);
+
+	ucp->prio = BOOTDEVP_4_NET_BASE;
+
+	return 0;
+}
+
+struct bootdev_ops eth_bootdev_ops = {
+	.get_bootflow	= eth_get_bootflow,
+};
+
+static const struct udevice_id eth_bootdev_ids[] = {
+	{ .compatible = "u-boot,bootdev-eth" },
+	{ }
+};
+
+U_BOOT_DRIVER(eth_bootdev) = {
+	.name		= "eth_bootdev",
+	.id		= UCLASS_BOOTDEV,
+	.ops		= &eth_bootdev_ops,
+	.bind		= eth_bootdev_bind,
+	.of_match	= eth_bootdev_ids,
+};
-- 
2.36.0.rc2.479.g8af0fa9b8e-goog


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH v5 23/34] bootstd: Add an implementation of distro PXE boot
  2022-04-25  5:30 [PATCH v5 00/34] Initial implementation of standard boot Simon Glass
                   ` (20 preceding siblings ...)
  2022-04-25  5:31 ` [PATCH v5 22/34] bootstd: ethernet: " Simon Glass
@ 2022-04-25  5:31 ` Simon Glass
  2022-04-25  5:31 ` [PATCH v5 24/34] bootstd: Add an implementation of EFI boot Simon Glass
                   ` (9 subsequent siblings)
  31 siblings, 0 replies; 36+ messages in thread
From: Simon Glass @ 2022-04-25  5:31 UTC (permalink / raw)
  To: U-Boot Mailing List
  Cc: Dennis Gilmore, Lukas Auer, Daniel Schwierzeck, Tom Rini,
	Michal Simek, Ilias Apalodimas, Steffen Jaeckel, Simon Glass

Add a bootmeth driver which handles distro boot from a network device, so
we can boot a bootflow using this commonly used mechanism.

In effect, this provides the same functionality as the 'pxe' command
and shares the same code. But the interface into it is via a bootmeth.

For now this requires the 'pxe' command be enabled. Future work may tidy
this up so that it can be used without CONFIG_CMDLINE being enabled.

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

(no changes since v3)

Changes in v3:
- Add a log category
- Use a short name when BOOTSTD_FULL is not enabled

 boot/Kconfig        |  10 +++
 boot/Makefile       |   1 +
 boot/bootmeth_pxe.c | 186 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 197 insertions(+)
 create mode 100644 boot/bootmeth_pxe.c

diff --git a/boot/Kconfig b/boot/Kconfig
index d34c31303dc..9e02a7549af 100644
--- a/boot/Kconfig
+++ b/boot/Kconfig
@@ -339,6 +339,16 @@ config BOOTMETH_DISTRO
 
 	  This provides a way to try out standard boot on an existing boot flow.
 
+config BOOTMETH_DISTRO_PXE
+	bool "Bootdev support for distro boot over network"
+	depends on CMD_PXE && CMD_NET && DM_ETH
+	default y
+	help
+	  Enables support for distro boot using bootdevs. This makes the
+	  bootdevs look for a 'extlinux/extlinux.conf' on the tftp server.
+
+	  This provides a way to try out standard boot on an existing boot flow.
+
 endif
 
 config LEGACY_IMAGE_FORMAT
diff --git a/boot/Makefile b/boot/Makefile
index 6b7014b41ac..9c9613d22a1 100644
--- a/boot/Makefile
+++ b/boot/Makefile
@@ -25,6 +25,7 @@ obj-$(CONFIG_$(SPL_TPL_)BOOTSTD) += bootmeth-uclass.o
 obj-$(CONFIG_$(SPL_TPL_)BOOTSTD) += bootstd-uclass.o
 
 obj-$(CONFIG_$(SPL_TPL_)BOOTMETH_DISTRO) += bootmeth_distro.o
+obj-$(CONFIG_$(SPL_TPL_)BOOTMETH_DISTRO_PXE) += bootmeth_pxe.o
 
 obj-$(CONFIG_$(SPL_TPL_)OF_LIBFDT) += image-fdt.o
 obj-$(CONFIG_$(SPL_TPL_)FIT_SIGNATURE) += fdt_region.o
diff --git a/boot/bootmeth_pxe.c b/boot/bootmeth_pxe.c
new file mode 100644
index 00000000000..f1e2b4c7762
--- /dev/null
+++ b/boot/bootmeth_pxe.c
@@ -0,0 +1,186 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Bootmethod for distro boot using PXE (network boot)
+ *
+ * Copyright 2021 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#define LOG_CATEGORY UCLASS_BOOTSTD
+
+#include <common.h>
+#include <bootdev.h>
+#include <bootflow.h>
+#include <bootmeth.h>
+#include <command.h>
+#include <distro.h>
+#include <dm.h>
+#include <fs.h>
+#include <log.h>
+#include <malloc.h>
+#include <mapmem.h>
+#include <mmc.h>
+#include <net.h>
+#include <pxe_utils.h>
+
+static int disto_pxe_getfile(struct pxe_context *ctx, const char *file_path,
+			     char *file_addr, ulong *sizep)
+{
+	struct distro_info *info = ctx->userdata;
+	ulong addr;
+	int ret;
+
+	addr = simple_strtoul(file_addr, NULL, 16);
+	ret = bootmeth_read_file(info->dev, info->bflow, file_path, addr,
+				 sizep);
+	if (ret)
+		return log_msg_ret("read", ret);
+
+	return 0;
+}
+
+static int distro_pxe_check(struct udevice *dev, struct bootflow_iter *iter)
+{
+	int ret;
+
+	/* This only works on network devices */
+	ret = bootflow_iter_uses_network(iter);
+	if (ret)
+		return log_msg_ret("net", ret);
+
+	return 0;
+}
+
+static int distro_pxe_read_bootflow(struct udevice *dev, struct bootflow *bflow)
+{
+	const char *addr_str;
+	char fname[200];
+	char *bootdir;
+	ulong addr;
+	ulong size;
+	char *buf;
+	int ret;
+
+	addr_str = env_get("pxefile_addr_r");
+	if (!addr_str)
+		return log_msg_ret("pxeb", -EPERM);
+	addr = simple_strtoul(addr_str, NULL, 16);
+
+	log_debug("calling pxe_get()\n");
+	ret = pxe_get(addr, &bootdir, &size);
+	log_debug("pxe_get() returned %d\n", ret);
+	if (ret)
+		return log_msg_ret("pxeb", ret);
+	bflow->size = size;
+
+	/* Use the directory of the dhcp bootdir as our subdir, if provided */
+	if (bootdir) {
+		const char *last_slash;
+		int path_len;
+
+		last_slash = strrchr(bootdir, '/');
+		if (last_slash) {
+			path_len = (last_slash - bootdir) + 1;
+			bflow->subdir = malloc(path_len + 1);
+			memcpy(bflow->subdir, bootdir, path_len);
+			bflow->subdir[path_len] = '\0';
+		}
+	}
+	snprintf(fname, sizeof(fname), "%s%s",
+		 bflow->subdir ? bflow->subdir : "", DISTRO_FNAME);
+
+	bflow->fname = strdup(fname);
+	if (!bflow->fname)
+		return log_msg_ret("name", -ENOMEM);
+
+	bflow->state = BOOTFLOWST_READY;
+
+	/* Allocate the buffer, including the \0 byte added by get_pxe_file() */
+	buf = malloc(size + 1);
+	if (!buf)
+		return log_msg_ret("buf", -ENOMEM);
+	memcpy(buf, map_sysmem(addr, 0), size + 1);
+	bflow->buf = buf;
+
+	return 0;
+}
+
+static int distro_pxe_read_file(struct udevice *dev, struct bootflow *bflow,
+				const char *file_path, ulong addr, ulong *sizep)
+{
+	char *tftp_argv[] = {"tftp", NULL, NULL, NULL};
+	struct pxe_context *ctx = dev_get_priv(dev);
+	char file_addr[17];
+	ulong size;
+	int ret;
+
+	sprintf(file_addr, "%lx", addr);
+	tftp_argv[1] = file_addr;
+	tftp_argv[2] = (void *)file_path;
+
+	if (do_tftpb(ctx->cmdtp, 0, 3, tftp_argv))
+		return -ENOENT;
+	ret = pxe_get_file_size(&size);
+	if (ret)
+		return log_msg_ret("tftp", ret);
+	if (size > *sizep)
+		return log_msg_ret("spc", -ENOSPC);
+	*sizep = size;
+
+	return 0;
+}
+
+static int distro_pxe_boot(struct udevice *dev, struct bootflow *bflow)
+{
+	struct pxe_context *ctx = dev_get_priv(dev);
+	struct cmd_tbl cmdtp = {};	/* dummy */
+	struct distro_info info;
+	ulong addr;
+	int ret;
+
+	addr = map_to_sysmem(bflow->buf);
+	info.dev = dev;
+	info.bflow = bflow;
+	info.cmdtp = &cmdtp;
+	ret = pxe_setup_ctx(ctx, &cmdtp, disto_pxe_getfile, &info, false,
+			    bflow->subdir);
+	if (ret)
+		return log_msg_ret("ctx", -EINVAL);
+
+	ret = pxe_process(ctx, addr, false);
+	if (ret)
+		return log_msg_ret("bread", -EINVAL);
+
+	return 0;
+}
+
+static int distro_bootmeth_pxe_bind(struct udevice *dev)
+{
+	struct bootmeth_uc_plat *plat = dev_get_uclass_plat(dev);
+
+	plat->desc = IS_ENABLED(CONFIG_BOOTSTD_FULL) ?
+		"PXE boot from a network device" : "PXE";
+
+	return 0;
+}
+
+static struct bootmeth_ops distro_bootmeth_pxe_ops = {
+	.check		= distro_pxe_check,
+	.read_bootflow	= distro_pxe_read_bootflow,
+	.read_file	= distro_pxe_read_file,
+	.boot		= distro_pxe_boot,
+};
+
+static const struct udevice_id distro_bootmeth_pxe_ids[] = {
+	{ .compatible = "u-boot,distro-pxe" },
+	{ }
+};
+
+U_BOOT_DRIVER(bootmeth_pxe) = {
+	.name		= "bootmeth_pxe",
+	.id		= UCLASS_BOOTMETH,
+	.of_match	= distro_bootmeth_pxe_ids,
+	.ops		= &distro_bootmeth_pxe_ops,
+	.bind		= distro_bootmeth_pxe_bind,
+	.priv_auto	= sizeof(struct pxe_context),
+};
-- 
2.36.0.rc2.479.g8af0fa9b8e-goog


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH v5 24/34] bootstd: Add an implementation of EFI boot
  2022-04-25  5:30 [PATCH v5 00/34] Initial implementation of standard boot Simon Glass
                   ` (21 preceding siblings ...)
  2022-04-25  5:31 ` [PATCH v5 23/34] bootstd: Add an implementation of distro PXE boot Simon Glass
@ 2022-04-25  5:31 ` Simon Glass
  2022-04-25  5:31 ` [PATCH v5 26/34] bootstd: Add an implementation of EFI bootmgr Simon Glass
                   ` (8 subsequent siblings)
  31 siblings, 0 replies; 36+ messages in thread
From: Simon Glass @ 2022-04-25  5:31 UTC (permalink / raw)
  To: U-Boot Mailing List
  Cc: Dennis Gilmore, Lukas Auer, Daniel Schwierzeck, Tom Rini,
	Michal Simek, Ilias Apalodimas, Steffen Jaeckel, Simon Glass

Add a bootmeth driver which handles EFI boot, using EFI_LOADER.

In effect, this provides the same functionality as the 'bootefi' command
and shares the same code. But the interface into it is via a bootmeth,
so it does not require any special scripts, etc.

For now this requires the 'bootefi' command be enabled. Future work may
tidy this up so that it can be used without CONFIG_CMDLINE being enabled.

There was much discussion about whether this is needed, but it seems
that it is, at least for now.

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

(no changes since v4)

Changes in v4:
- Allow use without a block device
- Fix 'a' typo
- Improve debugging in efiload_read_file()

Changes in v3:
- Add a log category
- Align the EFI load address
- Use a short name when BOOTSTD_FULL is not enabled
- Use common bootmeth functions

 boot/Kconfig        |  21 +++++
 boot/Makefile       |   1 +
 boot/bootmeth_efi.c | 188 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 210 insertions(+)
 create mode 100644 boot/bootmeth_efi.c

diff --git a/boot/Kconfig b/boot/Kconfig
index 9e02a7549af..0bfea2791da 100644
--- a/boot/Kconfig
+++ b/boot/Kconfig
@@ -349,6 +349,27 @@ config BOOTMETH_DISTRO_PXE
 
 	  This provides a way to try out standard boot on an existing boot flow.
 
+config BOOTMETH_EFILOADER
+	bool "Bootdev support for EFI boot"
+	depends on CMD_BOOTEFI
+	default y
+	help
+	  Enables support for EFI boot using bootdevs. This makes the
+	  bootdevs look for a 'boot<arch>.efi' on each filesystem
+	  they scan. The resulting file is booted after enabling U-Boot's
+	  EFI loader support.
+
+	  The <arch> depends on the architecture of the board:
+
+	     aa64      - aarch64 (ARM 64-bit)
+	     arm       - ARM 32-bit
+	     ia32      - x86 32-bit
+	     x64       - x86 64-bit
+	     riscv32   - RISC-V 32-bit
+	     riscv64   - RISC-V 64-bit
+
+	  This provides a way to try out standard boot on an existing boot flow.
+
 endif
 
 config LEGACY_IMAGE_FORMAT
diff --git a/boot/Makefile b/boot/Makefile
index 9c9613d22a1..4f9267687fa 100644
--- a/boot/Makefile
+++ b/boot/Makefile
@@ -26,6 +26,7 @@ obj-$(CONFIG_$(SPL_TPL_)BOOTSTD) += bootstd-uclass.o
 
 obj-$(CONFIG_$(SPL_TPL_)BOOTMETH_DISTRO) += bootmeth_distro.o
 obj-$(CONFIG_$(SPL_TPL_)BOOTMETH_DISTRO_PXE) += bootmeth_pxe.o
+obj-$(CONFIG_$(SPL_TPL_)BOOTMETH_EFILOADER) += bootmeth_efi.o
 
 obj-$(CONFIG_$(SPL_TPL_)OF_LIBFDT) += image-fdt.o
 obj-$(CONFIG_$(SPL_TPL_)FIT_SIGNATURE) += fdt_region.o
diff --git a/boot/bootmeth_efi.c b/boot/bootmeth_efi.c
new file mode 100644
index 00000000000..d5438eb67b9
--- /dev/null
+++ b/boot/bootmeth_efi.c
@@ -0,0 +1,188 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Bootmethod for distro boot via EFI
+ *
+ * Copyright 2021 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#define LOG_CATEGORY UCLASS_BOOTSTD
+
+#include <common.h>
+#include <bootdev.h>
+#include <bootflow.h>
+#include <bootmeth.h>
+#include <command.h>
+#include <dm.h>
+#include <efi_loader.h>
+#include <fs.h>
+#include <malloc.h>
+#include <mapmem.h>
+#include <mmc.h>
+#include <pxe_utils.h>
+
+#define EFI_DIRNAME	"efi/boot/"
+
+/**
+ * get_efi_leafname() - Get the leaf name for the EFI file we expect
+ *
+ * @str: Place to put leaf name for this architecture, e.g. "bootaa64.efi".
+ *	Must have at least 16 bytes of space
+ * @max_len: Length of @str, must be >=16
+ */
+static int get_efi_leafname(char *str, int max_len)
+{
+	const char *base;
+
+	if (max_len < 16)
+		return log_msg_ret("spc", -ENOSPC);
+	if (IS_ENABLED(CONFIG_ARM64))
+		base = "bootaa64";
+	else if (IS_ENABLED(CONFIG_ARM))
+		base = "bootarm";
+	else if (IS_ENABLED(CONFIG_X86_RUN_32BIT))
+		base = "bootia32";
+	else if (IS_ENABLED(CONFIG_X86_RUN_64BIT))
+		base = "bootx64";
+	else if (IS_ENABLED(CONFIG_ARCH_RV32I))
+		base = "bootriscv32";
+	else if (IS_ENABLED(CONFIG_ARCH_RV64I))
+		base = "bootriscv64";
+	else if (IS_ENABLED(CONFIG_SANDBOX))
+		base = "bootsbox";
+	else
+		return -EINVAL;
+
+	strcpy(str, base);
+	strcat(str, ".efi");
+
+	return 0;
+}
+
+static int efiload_read_file(struct blk_desc *desc, struct bootflow *bflow)
+{
+	const struct udevice *media_dev;
+	int size = bflow->size;
+	const char *dev_name;
+	char devnum_str[9];
+	char dirname[200];
+	char *last_slash;
+	int ret;
+
+	ret = bootmeth_alloc_file(bflow, 0x2000000, 0x10000);
+	if (ret)
+		return log_msg_ret("read", ret);
+
+	/*
+	 * This is a horrible hack to tell EFI about this boot device. Once we
+	 * unify EFI with the rest of U-Boot we can clean this up. The same hack
+	 * exists in multiple places, e.g. in the fs, tftp and load commands.
+	 *
+	 * Once we can clean up the EFI code to make proper use of driver model,
+	 * this can go away.
+	 */
+	media_dev = dev_get_parent(bflow->dev);
+	snprintf(devnum_str, sizeof(devnum_str), "%x", dev_seq(media_dev));
+
+	strlcpy(dirname, bflow->fname, sizeof(dirname));
+	last_slash = strrchr(dirname, '/');
+	if (last_slash)
+		*last_slash = '\0';
+
+	log_debug("setting bootdev %s, %s, %s, %p, %x\n",
+		  dev_get_uclass_name(media_dev), devnum_str, bflow->fname,
+		  bflow->buf, size);
+	dev_name = device_get_uclass_id(media_dev) == UCLASS_MASS_STORAGE ?
+		 "usb" : dev_get_uclass_name(media_dev);
+	efi_set_bootdev(dev_name, devnum_str, bflow->fname, bflow->buf, size);
+
+	return 0;
+}
+
+static int distro_efi_check(struct udevice *dev, struct bootflow_iter *iter)
+{
+	int ret;
+
+	/* This only works on block devices */
+	ret = bootflow_iter_uses_blk_dev(iter);
+	if (ret)
+		return log_msg_ret("blk", ret);
+
+	return 0;
+}
+
+static int distro_efi_read_bootflow(struct udevice *dev, struct bootflow *bflow)
+{
+	struct blk_desc *desc = NULL;
+	char fname[sizeof(EFI_DIRNAME) + 16];
+	int ret;
+
+	/* We require a partition table */
+	if (!bflow->part)
+		return -ENOENT;
+
+	strcpy(fname, EFI_DIRNAME);
+	ret = get_efi_leafname(fname + strlen(fname),
+			       sizeof(fname) - strlen(fname));
+	if (ret)
+		return log_msg_ret("leaf", ret);
+
+	if (bflow->blk)
+		 desc = dev_get_uclass_plat(bflow->blk);
+	ret = bootmeth_try_file(bflow, desc, NULL, fname);
+	if (ret)
+		return log_msg_ret("try", ret);
+
+	ret = efiload_read_file(desc, bflow);
+	if (ret)
+		return log_msg_ret("read", -EINVAL);
+
+	return 0;
+}
+
+int distro_efi_boot(struct udevice *dev, struct bootflow *bflow)
+{
+	char cmd[50];
+
+	/*
+	 * At some point we can add a real interface to bootefi so we can call
+	 * this directly. For now, go through the CLI like distro boot.
+	 */
+	snprintf(cmd, sizeof(cmd), "bootefi %lx %lx",
+		 (ulong)map_to_sysmem(bflow->buf),
+		 (ulong)map_to_sysmem(gd->fdt_blob));
+	if (run_command(cmd, 0))
+		return log_msg_ret("run", -EINVAL);
+
+	return 0;
+}
+
+static int distro_bootmeth_efi_bind(struct udevice *dev)
+{
+	struct bootmeth_uc_plat *plat = dev_get_uclass_plat(dev);
+
+	plat->desc = IS_ENABLED(CONFIG_BOOTSTD_FULL) ?
+		"EFI boot from an .efi file" : "EFI";
+
+	return 0;
+}
+
+static struct bootmeth_ops distro_efi_bootmeth_ops = {
+	.check		= distro_efi_check,
+	.read_bootflow	= distro_efi_read_bootflow,
+	.read_file	= bootmeth_common_read_file,
+	.boot		= distro_efi_boot,
+};
+
+static const struct udevice_id distro_efi_bootmeth_ids[] = {
+	{ .compatible = "u-boot,distro-efi" },
+	{ }
+};
+
+U_BOOT_DRIVER(bootmeth_efi) = {
+	.name		= "bootmeth_efi",
+	.id		= UCLASS_BOOTMETH,
+	.of_match	= distro_efi_bootmeth_ids,
+	.ops		= &distro_efi_bootmeth_ops,
+	.bind		= distro_bootmeth_efi_bind,
+};
-- 
2.36.0.rc2.479.g8af0fa9b8e-goog


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH v5 26/34] bootstd: Add an implementation of EFI bootmgr
  2022-04-25  5:30 [PATCH v5 00/34] Initial implementation of standard boot Simon Glass
                   ` (22 preceding siblings ...)
  2022-04-25  5:31 ` [PATCH v5 24/34] bootstd: Add an implementation of EFI boot Simon Glass
@ 2022-04-25  5:31 ` Simon Glass
  2022-04-25  5:31 ` [PATCH v5 27/34] bootstd: Add a sandbox bootmeth driver Simon Glass
                   ` (7 subsequent siblings)
  31 siblings, 0 replies; 36+ messages in thread
From: Simon Glass @ 2022-04-25  5:31 UTC (permalink / raw)
  To: U-Boot Mailing List
  Cc: Dennis Gilmore, Lukas Auer, Daniel Schwierzeck, Tom Rini,
	Michal Simek, Ilias Apalodimas, Steffen Jaeckel, Simon Glass

Add a bootmeth driver which handles EFI boot manager, using EFI_LOADER.

In effect, this provides the same functionality as the 'bootefi bootmgr'
command and shares the same code. But the interface into it is via a
bootmeth, so it does not require any special scripts, etc.

For now this requires the 'bootefi' command be enabled. Future work may
tidy this up so that it can be used without CONFIG_CMDLINE being enabled.

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

(no changes since v3)

Changes in v3:
- Add a log category

 boot/Makefile           |  3 ++
 boot/bootmeth_efi_mgr.c | 86 +++++++++++++++++++++++++++++++++++++++++
 2 files changed, 89 insertions(+)
 create mode 100644 boot/bootmeth_efi_mgr.c

diff --git a/boot/Makefile b/boot/Makefile
index 7325dbc1dd6..fe221d7f441 100644
--- a/boot/Makefile
+++ b/boot/Makefile
@@ -27,6 +27,9 @@ obj-$(CONFIG_$(SPL_TPL_)BOOTSTD) += bootstd-uclass.o
 obj-$(CONFIG_$(SPL_TPL_)BOOTMETH_DISTRO) += bootmeth_distro.o
 obj-$(CONFIG_$(SPL_TPL_)BOOTMETH_DISTRO_PXE) += bootmeth_pxe.o
 obj-$(CONFIG_$(SPL_TPL_)BOOTMETH_EFILOADER) += bootmeth_efi.o
+ifdef CONFIG_$(SPL_TPL_)BOOTSTD_FULL
+obj-$(CONFIG_$(SPL_TPL_)CMD_BOOTEFI_BOOTMGR) += bootmeth_efi_mgr.o
+endif
 
 obj-$(CONFIG_$(SPL_TPL_)OF_LIBFDT) += image-fdt.o
 obj-$(CONFIG_$(SPL_TPL_)FIT_SIGNATURE) += fdt_region.o
diff --git a/boot/bootmeth_efi_mgr.c b/boot/bootmeth_efi_mgr.c
new file mode 100644
index 00000000000..a6914466db7
--- /dev/null
+++ b/boot/bootmeth_efi_mgr.c
@@ -0,0 +1,86 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Bootmethod for EFI boot manager
+ *
+ * Copyright 2021 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#define LOG_CATEGORY UCLASS_BOOTSTD
+
+#include <common.h>
+#include <bootdev.h>
+#include <bootflow.h>
+#include <bootmeth.h>
+#include <command.h>
+#include <dm.h>
+
+static int efi_mgr_check(struct udevice *dev, struct bootflow_iter *iter)
+{
+	int ret;
+
+	/* Must be an bootstd device */
+	ret = bootflow_iter_uses_system(iter);
+	if (ret)
+		return log_msg_ret("net", ret);
+
+	return 0;
+}
+
+static int efi_mgr_read_bootflow(struct udevice *dev, struct bootflow *bflow)
+{
+	/*
+	 * Just assume there is something to boot since we don't have any way
+	 * of knowing in advance
+	 */
+	bflow->state = BOOTFLOWST_READY;
+
+	return 0;
+}
+
+static int efi_mgr_read_file(struct udevice *dev, struct bootflow *bflow,
+				const char *file_path, ulong addr, ulong *sizep)
+{
+	/* Files are loaded by the 'bootefi bootmgr' command */
+
+	return -ENOSYS;
+}
+
+static int efi_mgr_boot(struct udevice *dev, struct bootflow *bflow)
+{
+	int ret;
+
+	/* Booting is handled by the 'bootefi bootmgr' command */
+	ret = run_command("bootefi bootmgr", 0);
+
+	return 0;
+}
+
+static int bootmeth_efi_mgr_bind(struct udevice *dev)
+{
+	struct bootmeth_uc_plat *plat = dev_get_uclass_plat(dev);
+
+	plat->desc = "EFI bootmgr flow";
+
+	return 0;
+}
+
+static struct bootmeth_ops efi_mgr_bootmeth_ops = {
+	.check		= efi_mgr_check,
+	.read_bootflow	= efi_mgr_read_bootflow,
+	.read_file	= efi_mgr_read_file,
+	.boot		= efi_mgr_boot,
+};
+
+static const struct udevice_id efi_mgr_bootmeth_ids[] = {
+	{ .compatible = "u-boot,efi-bootmgr" },
+	{ }
+};
+
+U_BOOT_DRIVER(bootmeth_zefi_mgr) = {
+	.name		= "bootmeth_efi_mgr",
+	.id		= UCLASS_BOOTMETH,
+	.of_match	= efi_mgr_bootmeth_ids,
+	.ops		= &efi_mgr_bootmeth_ops,
+	.bind		= bootmeth_efi_mgr_bind,
+};
-- 
2.36.0.rc2.479.g8af0fa9b8e-goog


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH v5 27/34] bootstd: Add a sandbox bootmeth driver
  2022-04-25  5:30 [PATCH v5 00/34] Initial implementation of standard boot Simon Glass
                   ` (23 preceding siblings ...)
  2022-04-25  5:31 ` [PATCH v5 26/34] bootstd: Add an implementation of EFI bootmgr Simon Glass
@ 2022-04-25  5:31 ` Simon Glass
  2022-04-25  5:31 ` [PATCH v5 29/34] bootstd: Add an implementation of script boot Simon Glass
                   ` (6 subsequent siblings)
  31 siblings, 0 replies; 36+ messages in thread
From: Simon Glass @ 2022-04-25  5:31 UTC (permalink / raw)
  To: U-Boot Mailing List
  Cc: Dennis Gilmore, Lukas Auer, Daniel Schwierzeck, Tom Rini,
	Michal Simek, Ilias Apalodimas, Steffen Jaeckel, Simon Glass

Add a bootmeth driver for sandbox, used for testing.

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

(no changes since v3)

Changes in v3:
- Add a log category

 boot/Kconfig            |  7 +++++
 boot/Makefile           |  1 +
 boot/bootmeth_sandbox.c | 69 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 77 insertions(+)
 create mode 100644 boot/bootmeth_sandbox.c

diff --git a/boot/Kconfig b/boot/Kconfig
index 0bfea2791da..373871c49a6 100644
--- a/boot/Kconfig
+++ b/boot/Kconfig
@@ -370,6 +370,13 @@ config BOOTMETH_EFILOADER
 
 	  This provides a way to try out standard boot on an existing boot flow.
 
+config BOOTMETH_SANDBOX
+	def_bool y
+	depends on SANDBOX
+	help
+	  This is a sandbox bootmeth driver used for testing. It always returns
+	  -ENOTSUPP when attempting to boot.
+
 endif
 
 config LEGACY_IMAGE_FORMAT
diff --git a/boot/Makefile b/boot/Makefile
index fe221d7f441..444e6975f10 100644
--- a/boot/Makefile
+++ b/boot/Makefile
@@ -27,6 +27,7 @@ obj-$(CONFIG_$(SPL_TPL_)BOOTSTD) += bootstd-uclass.o
 obj-$(CONFIG_$(SPL_TPL_)BOOTMETH_DISTRO) += bootmeth_distro.o
 obj-$(CONFIG_$(SPL_TPL_)BOOTMETH_DISTRO_PXE) += bootmeth_pxe.o
 obj-$(CONFIG_$(SPL_TPL_)BOOTMETH_EFILOADER) += bootmeth_efi.o
+obj-$(CONFIG_$(SPL_TPL_)BOOTMETH_SANDBOX) += bootmeth_sandbox.o
 ifdef CONFIG_$(SPL_TPL_)BOOTSTD_FULL
 obj-$(CONFIG_$(SPL_TPL_)CMD_BOOTEFI_BOOTMGR) += bootmeth_efi_mgr.o
 endif
diff --git a/boot/bootmeth_sandbox.c b/boot/bootmeth_sandbox.c
new file mode 100644
index 00000000000..13ec5e95e64
--- /dev/null
+++ b/boot/bootmeth_sandbox.c
@@ -0,0 +1,69 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Bootmethod for sandbox testing
+ *
+ * Copyright 2021 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#define LOG_CATEGORY UCLASS_BOOTSTD
+
+#include <common.h>
+#include <bootdev.h>
+#include <bootflow.h>
+#include <bootmeth.h>
+#include <dm.h>
+
+static int sandbox_check(struct udevice *dev, struct bootflow_iter *iter)
+{
+	return 0;
+}
+
+static int sandbox_read_bootflow(struct udevice *dev, struct bootflow *bflow)
+{
+	/* pretend we are ready */
+	bflow->state = BOOTFLOWST_READY;
+
+	return 0;
+}
+
+static int sandbox_read_file(struct udevice *dev, struct bootflow *bflow,
+			     const char *file_path, ulong addr, ulong *sizep)
+{
+	return -ENOSYS;
+}
+
+static int sandbox_boot(struct udevice *dev, struct bootflow *bflow)
+{
+	/* always fail: see bootflow_iter_disable() */
+	return -ENOTSUPP;
+}
+
+static int sandbox_bootmeth_bind(struct udevice *dev)
+{
+	struct bootmeth_uc_plat *plat = dev_get_uclass_plat(dev);
+
+	plat->desc = "Sandbox boot for testing";
+
+	return 0;
+}
+
+static struct bootmeth_ops sandbox_bootmeth_ops = {
+	.check		= sandbox_check,
+	.read_bootflow	= sandbox_read_bootflow,
+	.read_file	= sandbox_read_file,
+	.boot		= sandbox_boot,
+};
+
+static const struct udevice_id sandbox_bootmeth_ids[] = {
+	{ .compatible = "u-boot,sandbox-syslinux" },
+	{ }
+};
+
+U_BOOT_DRIVER(bootmeth_sandbox) = {
+	.name		= "bootmeth_sandbox",
+	.id		= UCLASS_BOOTMETH,
+	.of_match	= sandbox_bootmeth_ids,
+	.ops		= &sandbox_bootmeth_ops,
+	.bind		= sandbox_bootmeth_bind,
+};
-- 
2.36.0.rc2.479.g8af0fa9b8e-goog


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH v5 29/34] bootstd: Add an implementation of script boot
  2022-04-25  5:30 [PATCH v5 00/34] Initial implementation of standard boot Simon Glass
                   ` (24 preceding siblings ...)
  2022-04-25  5:31 ` [PATCH v5 27/34] bootstd: Add a sandbox bootmeth driver Simon Glass
@ 2022-04-25  5:31 ` Simon Glass
  2022-04-25  5:31 ` [PATCH v5 30/34] bootstd: usb: Add a bootdev driver Simon Glass
                   ` (5 subsequent siblings)
  31 siblings, 0 replies; 36+ messages in thread
From: Simon Glass @ 2022-04-25  5:31 UTC (permalink / raw)
  To: U-Boot Mailing List
  Cc: Dennis Gilmore, Lukas Auer, Daniel Schwierzeck, Tom Rini,
	Michal Simek, Ilias Apalodimas, Steffen Jaeckel, Simon Glass

Add a bootmeth driver which handles distro boot from a disk via a U-Boot
script, so we can boot a bootflow using this commonly used mechanism. This
is required by Armbian, for example.

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

(no changes since v4)

Changes in v4:
- Allow use without a block device

Changes in v3:
- Add support for script boot

 boot/Kconfig           |  11 ++++
 boot/Makefile          |   1 +
 boot/bootmeth_script.c | 139 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 151 insertions(+)
 create mode 100644 boot/bootmeth_script.c

diff --git a/boot/Kconfig b/boot/Kconfig
index 373871c49a6..64e16f3beba 100644
--- a/boot/Kconfig
+++ b/boot/Kconfig
@@ -377,6 +377,17 @@ config BOOTMETH_SANDBOX
 	  This is a sandbox bootmeth driver used for testing. It always returns
 	  -ENOTSUPP when attempting to boot.
 
+config BOOTMETH_SCRIPT
+	bool "Bootdev support for U-Boot scripts"
+	default y if BOOTSTD_FULL
+	help
+	  Enables support for booting a distro via a U-Boot script. This makes
+	  the bootdevs look for a 'boot/boot.scr' file which can be used to
+	  boot the distro.
+
+	  This provides a way to try out standard boot on an existing boot flow.
+	  It is not enabled by default to save space.
+
 endif
 
 config LEGACY_IMAGE_FORMAT
diff --git a/boot/Makefile b/boot/Makefile
index 444e6975f10..a70674259c1 100644
--- a/boot/Makefile
+++ b/boot/Makefile
@@ -28,6 +28,7 @@ obj-$(CONFIG_$(SPL_TPL_)BOOTMETH_DISTRO) += bootmeth_distro.o
 obj-$(CONFIG_$(SPL_TPL_)BOOTMETH_DISTRO_PXE) += bootmeth_pxe.o
 obj-$(CONFIG_$(SPL_TPL_)BOOTMETH_EFILOADER) += bootmeth_efi.o
 obj-$(CONFIG_$(SPL_TPL_)BOOTMETH_SANDBOX) += bootmeth_sandbox.o
+obj-$(CONFIG_$(SPL_TPL_)BOOTMETH_SCRIPT) += bootmeth_script.o
 ifdef CONFIG_$(SPL_TPL_)BOOTSTD_FULL
 obj-$(CONFIG_$(SPL_TPL_)CMD_BOOTEFI_BOOTMGR) += bootmeth_efi_mgr.o
 endif
diff --git a/boot/bootmeth_script.c b/boot/bootmeth_script.c
new file mode 100644
index 00000000000..d1c3f940037
--- /dev/null
+++ b/boot/bootmeth_script.c
@@ -0,0 +1,139 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Bootmethod for booting via a U-Boot script
+ *
+ * Copyright 2021 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#define LOG_CATEGORY UCLASS_BOOTSTD
+
+#include <common.h>
+#include <blk.h>
+#include <bootflow.h>
+#include <bootmeth.h>
+#include <bootstd.h>
+#include <dm.h>
+#include <env.h>
+#include <fs.h>
+#include <image.h>
+#include <malloc.h>
+#include <mapmem.h>
+
+#define SCRIPT_FNAME1	"boot.scr.uimg"
+#define SCRIPT_FNAME2	"boot.scr"
+
+static int script_check(struct udevice *dev, struct bootflow_iter *iter)
+{
+	int ret;
+
+	/* This only works on block devices */
+	ret = bootflow_iter_uses_blk_dev(iter);
+	if (ret)
+		return log_msg_ret("blk", ret);
+
+	return 0;
+}
+
+static int script_read_bootflow(struct udevice *dev, struct bootflow *bflow)
+{
+	struct blk_desc *desc = NULL;
+	const char *const *prefixes;
+	struct udevice *bootstd;
+	const char *prefix;
+	int ret, i;
+
+	ret = uclass_first_device_err(UCLASS_BOOTSTD, &bootstd);
+	if (ret)
+		return log_msg_ret("std", ret);
+
+	/* We require a partition table */
+	if (!bflow->part)
+		return -ENOENT;
+
+	if (bflow->blk)
+		 desc = dev_get_uclass_plat(bflow->blk);
+
+	prefixes = bootstd_get_prefixes(bootstd);
+	i = 0;
+	do {
+		prefix = prefixes ? prefixes[i] : NULL;
+
+		ret = bootmeth_try_file(bflow, desc, prefix, SCRIPT_FNAME1);
+		if (ret)
+			ret = bootmeth_try_file(bflow, desc, prefix,
+						SCRIPT_FNAME2);
+	} while (ret && prefixes && prefixes[++i]);
+	if (ret)
+		return log_msg_ret("try", ret);
+
+	bflow->subdir = strdup(prefix ? prefix : "");
+	if (!bflow->subdir)
+		return log_msg_ret("prefix", -ENOMEM);
+
+	ret = bootmeth_alloc_file(bflow, 0x10000, 1);
+	if (ret)
+		return log_msg_ret("read", ret);
+
+	return 0;
+}
+
+static int script_boot(struct udevice *dev, struct bootflow *bflow)
+{
+	struct blk_desc *desc = dev_get_uclass_plat(bflow->blk);
+	ulong addr;
+	int ret;
+
+	ret = env_set("devtype", blk_get_devtype(bflow->blk));
+	if (!ret)
+		ret = env_set_hex("devnum", desc->devnum);
+	if (!ret)
+		ret = env_set("prefix", bflow->subdir);
+	if (!ret && IS_ENABLED(CONFIG_ARCH_SUNXI) &&
+	    !strcmp("mmc", blk_get_devtype(bflow->blk)))
+		ret = env_set_hex("mmc_bootdev", desc->devnum);
+	if (ret)
+		return log_msg_ret("env", ret);
+
+	log_debug("devtype: %s\n", env_get("devtype"));
+	log_debug("devnum: %s\n", env_get("devnum"));
+	log_debug("prefix: %s\n", env_get("prefix"));
+	log_debug("mmc_bootdev: %s\n", env_get("mmc_bootdev"));
+
+	addr = map_to_sysmem(bflow->buf);
+	ret = image_source_script(addr, NULL);
+	if (ret)
+		return log_msg_ret("boot", ret);
+
+	return 0;
+}
+
+static int script_bootmeth_bind(struct udevice *dev)
+{
+	struct bootmeth_uc_plat *plat = dev_get_uclass_plat(dev);
+
+	plat->desc = IS_ENABLED(CONFIG_BOOTSTD_FULL) ?
+		"Script boot from a block device" : "script";
+
+	return 0;
+}
+
+static struct bootmeth_ops script_bootmeth_ops = {
+	.check		= script_check,
+	.read_bootflow	= script_read_bootflow,
+	.read_file	= bootmeth_common_read_file,
+	.boot		= script_boot,
+};
+
+static const struct udevice_id script_bootmeth_ids[] = {
+	{ .compatible = "u-boot,script" },
+	{ }
+};
+
+U_BOOT_DRIVER(bootmeth_script) = {
+	.name		= "bootmeth_script",
+	.id		= UCLASS_BOOTMETH,
+	.of_match	= script_bootmeth_ids,
+	.ops		= &script_bootmeth_ops,
+	.bind		= script_bootmeth_bind,
+};
-- 
2.36.0.rc2.479.g8af0fa9b8e-goog


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH v5 30/34] bootstd: usb: Add a bootdev driver
  2022-04-25  5:30 [PATCH v5 00/34] Initial implementation of standard boot Simon Glass
                   ` (25 preceding siblings ...)
  2022-04-25  5:31 ` [PATCH v5 29/34] bootstd: Add an implementation of script boot Simon Glass
@ 2022-04-25  5:31 ` Simon Glass
  2022-04-25  5:31 ` [PATCH v5 31/34] bootstd: Add tests for bootstd including all uclasses Simon Glass
                   ` (4 subsequent siblings)
  31 siblings, 0 replies; 36+ messages in thread
From: Simon Glass @ 2022-04-25  5:31 UTC (permalink / raw)
  To: U-Boot Mailing List
  Cc: Dennis Gilmore, Lukas Auer, Daniel Schwierzeck, Tom Rini,
	Michal Simek, Ilias Apalodimas, Steffen Jaeckel, Simon Glass,
	Marek Vasut

Add a bootdev driver for USB host. It can use the distro boot mechanism to
locate a file, or any other available bootmeth.

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

(no changes since v1)

 common/usb_storage.c           | 11 ++++++
 drivers/usb/host/Makefile      |  4 +++
 drivers/usb/host/usb_bootdev.c | 61 ++++++++++++++++++++++++++++++++++
 3 files changed, 76 insertions(+)
 create mode 100644 drivers/usb/host/usb_bootdev.c

diff --git a/common/usb_storage.c b/common/usb_storage.c
index 291728f37e0..eaa31374ef7 100644
--- a/common/usb_storage.c
+++ b/common/usb_storage.c
@@ -34,6 +34,7 @@
 
 #include <common.h>
 #include <blk.h>
+#include <bootdev.h>
 #include <command.h>
 #include <dm.h>
 #include <errno.h>
@@ -243,6 +244,16 @@ static int usb_stor_probe_device(struct usb_device *udev)
 		ret = blk_probe_or_unbind(dev);
 		if (ret)
 			return ret;
+
+		ret = bootdev_setup_sibling_blk(dev, "usb_bootdev");
+		if (ret) {
+			int ret2;
+
+			ret2 = device_unbind(dev);
+			if (ret2)
+				return log_msg_ret("bootdev", ret2);
+			return log_msg_ret("bootdev", ret);
+		}
 	}
 #else
 	/* We don't have space to even probe if we hit the maximum */
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
index a4472da9f18..7785b3744ef 100644
--- a/drivers/usb/host/Makefile
+++ b/drivers/usb/host/Makefile
@@ -8,6 +8,10 @@ obj-y += usb-uclass.o
 obj-$(CONFIG_SANDBOX) += usb-sandbox.o
 endif
 
+ifdef CONFIG_$(SPL_TPL_)USB_STORAGE
+obj-$(CONFIG_$(SPL_TPL_)BOOTSTD) += usb_bootdev.o
+endif
+
 # ohci
 obj-$(CONFIG_USB_OHCI_NEW) += ohci-hcd.o
 obj-$(CONFIG_USB_ATMEL) += ohci-at91.o
diff --git a/drivers/usb/host/usb_bootdev.c b/drivers/usb/host/usb_bootdev.c
new file mode 100644
index 00000000000..b85f699933d
--- /dev/null
+++ b/drivers/usb/host/usb_bootdev.c
@@ -0,0 +1,61 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Bootdevice for USB
+ *
+ * Copyright 2021 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <common.h>
+#include <bootdev.h>
+#include <dm.h>
+#include <usb.h>
+
+static int usb_get_bootflow(struct udevice *dev, struct bootflow_iter *iter,
+			    struct bootflow *bflow)
+{
+	struct udevice *blk;
+	int ret;
+
+	ret = bootdev_get_sibling_blk(dev, &blk);
+	/*
+	 * If there is no media, indicate that no more partitions should be
+	 * checked
+	 */
+	if (ret == -EOPNOTSUPP)
+		ret = -ESHUTDOWN;
+	if (ret)
+		return log_msg_ret("blk", ret);
+	assert(blk);
+	ret = bootdev_find_in_blk(dev, blk, iter, bflow);
+	if (ret)
+		return log_msg_ret("find", ret);
+
+	return 0;
+}
+
+static int usb_bootdev_bind(struct udevice *dev)
+{
+	struct bootdev_uc_plat *ucp = dev_get_uclass_plat(dev);
+
+	ucp->prio = BOOTDEVP_3_SCAN_SLOW;
+
+	return 0;
+}
+
+struct bootdev_ops usb_bootdev_ops = {
+	.get_bootflow	= usb_get_bootflow,
+};
+
+static const struct udevice_id usb_bootdev_ids[] = {
+	{ .compatible = "u-boot,bootdev-usb" },
+	{ }
+};
+
+U_BOOT_DRIVER(usb_bootdev) = {
+	.name		= "usb_bootdev",
+	.id		= UCLASS_BOOTDEV,
+	.ops		= &usb_bootdev_ops,
+	.bind		= usb_bootdev_bind,
+	.of_match	= usb_bootdev_ids,
+};
-- 
2.36.0.rc2.479.g8af0fa9b8e-goog


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH v5 31/34] bootstd: Add tests for bootstd including all uclasses
  2022-04-25  5:30 [PATCH v5 00/34] Initial implementation of standard boot Simon Glass
                   ` (26 preceding siblings ...)
  2022-04-25  5:31 ` [PATCH v5 30/34] bootstd: usb: Add a bootdev driver Simon Glass
@ 2022-04-25  5:31 ` Simon Glass
  2022-04-25  5:31 ` [PATCH v5 32/34] bootstd: Add setup for the bootflow tests Simon Glass
                   ` (3 subsequent siblings)
  31 siblings, 0 replies; 36+ messages in thread
From: Simon Glass @ 2022-04-25  5:31 UTC (permalink / raw)
  To: U-Boot Mailing List
  Cc: Dennis Gilmore, Lukas Auer, Daniel Schwierzeck, Tom Rini,
	Michal Simek, Ilias Apalodimas, Steffen Jaeckel, Simon Glass

Add a set of combined tests for the bootdev, bootflow and bootmeth
commands, along with associated functionality.

Expand the sandbox console-recording limit so that these can work.

These tests rely on a filesystem script which is not yet added to the
Python tests. It is included here as a shell script.

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

(no changes since v4)

Changes in v4:
- Update tests for code changes

Changes in v3:
- Add tests for the "bootmeths" env var
- Update bootdev_test_order() for boot_targets env var
- Update tests for the new 'bootmeth order' syntax

 MAINTAINERS                        |   1 +
 arch/sandbox/dts/test.dts          |  18 ++
 configs/sandbox_defconfig          |   3 +-
 configs/sandbox_flattree_defconfig |   3 +-
 include/test/suites.h              |   2 +
 test/Makefile                      |   1 +
 test/boot/Makefile                 |   5 +
 test/boot/bootdev.c                | 223 ++++++++++++++++
 test/boot/bootflow.c               | 400 +++++++++++++++++++++++++++++
 test/boot/bootmeth.c               | 122 +++++++++
 test/boot/bootstd_common.c         |  35 +++
 test/boot/bootstd_common.h         |  27 ++
 test/cmd_ut.c                      |   7 +
 13 files changed, 845 insertions(+), 2 deletions(-)
 create mode 100644 test/boot/Makefile
 create mode 100644 test/boot/bootdev.c
 create mode 100644 test/boot/bootflow.c
 create mode 100644 test/boot/bootmeth.c
 create mode 100644 test/boot/bootstd_common.c
 create mode 100644 test/boot/bootstd_common.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 77a34082249..963ec91c891 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -708,6 +708,7 @@ F:	include/bootflow.h
 F:	include/bootmeth.h
 F:	include/bootstd.h
 F:	net/eth_bootdevice.c
+F:	test/boot/
 
 BTRFS
 M:	Marek Behun <marek.behun@nic.cz>
diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts
index 5b38ee4a5f9..a8a86bc7155 100644
--- a/arch/sandbox/dts/test.dts
+++ b/arch/sandbox/dts/test.dts
@@ -74,6 +74,21 @@
 		};
 	};
 
+	bootstd {
+		compatible = "u-boot,boot-std";
+
+		filename-prefixes = "/", "/boot/";
+		bootdev-order = "mmc2", "mmc1";
+
+		syslinux {
+			compatible = "u-boot,distro-syslinux";
+		};
+
+		efi {
+			compatible = "u-boot,distro-efi";
+		};
+	};
+
 	reboot-mode0 {
 		compatible = "reboot-mode-gpio";
 		gpios = <&gpio_c 0 GPIO_ACTIVE_HIGH>, <&gpio_c 1 GPIO_ACTIVE_HIGH>;
@@ -891,10 +906,13 @@
 		non-removable;
 	};
 
+	/* This is used for the bootdev tests */
 	mmc1 {
 		compatible = "sandbox,mmc";
+		filename = "mmc1.img";
 	};
 
+	/* This is used for the fastboot tests */
 	mmc0 {
 		compatible = "sandbox,mmc";
 	};
diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig
index 14d7af4db28..fe8ea4626d9 100644
--- a/configs/sandbox_defconfig
+++ b/configs/sandbox_defconfig
@@ -28,7 +28,7 @@ CONFIG_AUTOBOOT_STOP_STR_CRYPT="$5$rounds=640000$HrpE65IkB8CM5nCL$BKT3QdF98Bo8fJ
 CONFIG_IMAGE_PRE_LOAD=y
 CONFIG_IMAGE_PRE_LOAD_SIG=y
 CONFIG_CONSOLE_RECORD=y
-CONFIG_CONSOLE_RECORD_OUT_SIZE=0x1000
+CONFIG_CONSOLE_RECORD_OUT_SIZE=0x6000
 CONFIG_PRE_CONSOLE_BUFFER=y
 CONFIG_LOG=y
 CONFIG_DISPLAY_BOARDINFO_LATE=y
@@ -120,6 +120,7 @@ CONFIG_ENV_IS_IN_EXT4=y
 CONFIG_ENV_EXT4_INTERFACE="host"
 CONFIG_ENV_EXT4_DEVICE_AND_PART="0:0"
 CONFIG_ENV_IMPORT_FDT=y
+# CONFIG_BOOTDEV_ETH is not set
 CONFIG_BOOTP_SEND_HOSTNAME=y
 CONFIG_NETCONSOLE=y
 CONFIG_IP_DEFRAG=y
diff --git a/configs/sandbox_flattree_defconfig b/configs/sandbox_flattree_defconfig
index ded16af9f51..80a4be47eba 100644
--- a/configs/sandbox_flattree_defconfig
+++ b/configs/sandbox_flattree_defconfig
@@ -17,7 +17,7 @@ CONFIG_BOOTSTAGE_FDT=y
 CONFIG_BOOTSTAGE_STASH=y
 CONFIG_BOOTSTAGE_STASH_SIZE=0x4096
 CONFIG_CONSOLE_RECORD=y
-CONFIG_CONSOLE_RECORD_OUT_SIZE=0x1000
+CONFIG_CONSOLE_RECORD_OUT_SIZE=0x6000
 CONFIG_DISPLAY_BOARDINFO_LATE=y
 CONFIG_CMD_CPU=y
 CONFIG_CMD_LICENSE=y
@@ -72,6 +72,7 @@ CONFIG_ENV_IS_NOWHERE=y
 CONFIG_ENV_IS_IN_EXT4=y
 CONFIG_ENV_EXT4_INTERFACE="host"
 CONFIG_ENV_EXT4_DEVICE_AND_PART="0:0"
+# CONFIG_BOOTDEV_ETH is not set
 CONFIG_BOOTP_SEND_HOSTNAME=y
 CONFIG_NETCONSOLE=y
 CONFIG_IP_DEFRAG=y
diff --git a/include/test/suites.h b/include/test/suites.h
index 6553a765c6a..ee6858a802a 100644
--- a/include/test/suites.h
+++ b/include/test/suites.h
@@ -29,6 +29,8 @@ int cmd_ut_category(const char *name, const char *prefix,
 int do_ut_addrmap(struct cmd_tbl *cmdtp, int flag, int argc,
 		  char *const argv[]);
 int do_ut_bootm(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]);
+int do_ut_bootstd(struct cmd_tbl *cmdtp, int flag, int argc,
+		  char *const argv[]);
 int do_ut_bloblist(struct cmd_tbl *cmdtp, int flag, int argc,
 		   char *const argv[]);
 int do_ut_common(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]);
diff --git a/test/Makefile b/test/Makefile
index b3b2902e2e7..abd605a4351 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -22,6 +22,7 @@ obj-$(CONFIG_UT_TIME) += time_ut.o
 obj-y += ut.o
 
 ifeq ($(CONFIG_SPL_BUILD),)
+obj-$(CONFIG_UNIT_TEST) += boot/
 obj-$(CONFIG_UNIT_TEST) += common/
 obj-$(CONFIG_UNIT_TEST) += lib/
 obj-y += log/
diff --git a/test/boot/Makefile b/test/boot/Makefile
new file mode 100644
index 00000000000..1730792b5fa
--- /dev/null
+++ b/test/boot/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# Copyright 2021 Google LLC
+
+obj-$(CONFIG_BOOTSTD) += bootdev.o bootstd_common.o bootflow.o bootmeth.o
diff --git a/test/boot/bootdev.c b/test/boot/bootdev.c
new file mode 100644
index 00000000000..1c2a79fb108
--- /dev/null
+++ b/test/boot/bootdev.c
@@ -0,0 +1,223 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Test for bootdev functions. All start with 'bootdev'
+ *
+ * Copyright 2021 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <common.h>
+#include <bootstd.h>
+#include <dm.h>
+#include <bootdev.h>
+#include <bootflow.h>
+#include <mapmem.h>
+#include <os.h>
+#include <test/suites.h>
+#include <test/ut.h>
+#include "bootstd_common.h"
+
+/* Allow reseting the USB-started flag */
+extern char usb_started;
+
+/* Check 'bootdev list' command */
+static int bootdev_test_cmd_list(struct unit_test_state *uts)
+{
+	int probed;
+
+	console_record_reset_enable();
+	for (probed = 0; probed < 2; probed++) {
+		int probe_ch = probed ? '+' : ' ';
+
+		ut_assertok(run_command(probed ? "bootdev list -p" :
+			"bootdev list", 0));
+		ut_assert_nextline("Seq  Probed  Status  Uclass    Name");
+		ut_assert_nextlinen("---");
+		ut_assert_nextline("%3x   [ %c ]  %6s  %-8s  %s", 0, probe_ch, "OK",
+				   "mmc", "mmc2.bootdev");
+		ut_assert_nextline("%3x   [ %c ]  %6s  %-8s  %s", 1, probe_ch, "OK",
+				   "mmc", "mmc1.bootdev");
+		ut_assert_nextline("%3x   [ %c ]  %6s  %-8s  %s", 2, probe_ch, "OK",
+				   "mmc", "mmc0.bootdev");
+		ut_assert_nextlinen("---");
+		ut_assert_nextline("(3 bootdevs)");
+		ut_assert_console_end();
+	}
+
+	return 0;
+}
+BOOTSTD_TEST(bootdev_test_cmd_list, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
+
+/* Check 'bootdev select' and 'info' commands */
+static int bootdev_test_cmd_select(struct unit_test_state *uts)
+{
+	struct bootstd_priv *std;
+
+	/* get access to the CLI's cur_bootdev */
+	ut_assertok(bootstd_get_priv(&std));
+
+	console_record_reset_enable();
+	ut_asserteq(1, run_command("bootdev info", 0));
+	ut_assert_nextlinen("Please use");
+	ut_assert_console_end();
+
+	/* select by sequence */
+	ut_assertok(run_command("bootdev select 0", 0));
+	ut_assert_console_end();
+
+	ut_assertok(run_command("bootdev info", 0));
+	ut_assert_nextline("Name:      mmc2.bootdev");
+	ut_assert_nextline("Sequence:  0");
+	ut_assert_nextline("Status:    Probed");
+	ut_assert_nextline("Uclass:    mmc");
+	ut_assert_nextline("Bootflows: 0 (0 valid)");
+	ut_assert_console_end();
+
+	/* select by bootdev name */
+	ut_assertok(run_command("bootdev select mmc1.bootdev", 0));
+	ut_assert_console_end();
+	ut_assertnonnull(std->cur_bootdev);
+	ut_asserteq_str("mmc1.bootdev", std->cur_bootdev->name);
+
+	/* select by bootdev label*/
+	ut_assertok(run_command("bootdev select mmc1", 0));
+	ut_assert_console_end();
+	ut_assertnonnull(std->cur_bootdev);
+	ut_asserteq_str("mmc1.bootdev", std->cur_bootdev->name);
+
+	/* deselect */
+	ut_assertok(run_command("bootdev select", 0));
+	ut_assert_console_end();
+	ut_assertnull(std->cur_bootdev);
+
+	ut_asserteq(1, run_command("bootdev info", 0));
+	ut_assert_nextlinen("Please use");
+	ut_assert_console_end();
+
+	return 0;
+}
+BOOTSTD_TEST(bootdev_test_cmd_select, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
+
+/* Check bootdev labels */
+static int bootdev_test_labels(struct unit_test_state *uts)
+{
+	struct udevice *dev, *media;
+
+	ut_assertok(bootdev_find_by_label("mmc2", &dev));
+	ut_asserteq(UCLASS_BOOTDEV, device_get_uclass_id(dev));
+	media = dev_get_parent(dev);
+	ut_asserteq(UCLASS_MMC, device_get_uclass_id(media));
+	ut_asserteq_str("mmc2", media->name);
+
+	/* Check invalid uclass */
+	ut_asserteq(-EINVAL, bootdev_find_by_label("fred0", &dev));
+
+	/* Check unknown sequence number */
+	ut_asserteq(-ENOENT, bootdev_find_by_label("mmc6", &dev));
+
+	return 0;
+}
+BOOTSTD_TEST(bootdev_test_labels, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
+
+/* Check bootdev ordering with the bootdev-order property */
+static int bootdev_test_order(struct unit_test_state *uts)
+{
+	struct bootflow_iter iter;
+	struct bootflow bflow;
+
+	/*
+	 * First try the order set by the bootdev-order property
+	 * Like all sandbox unit tests this relies on the devicetree setting up
+	 * the required devices:
+	 *
+	 * mmc0 - nothing connected
+	 * mmc1 - connected to mmc1.img file
+	 * mmc2 - nothing connected
+	 */
+	ut_assertok(env_set("boot_targets", NULL));
+	ut_assertok(bootflow_scan_first(&iter, 0, &bflow));
+	ut_asserteq(2, iter.num_devs);
+	ut_asserteq_str("mmc2.bootdev", iter.dev_order[0]->name);
+	ut_asserteq_str("mmc1.bootdev", iter.dev_order[1]->name);
+	bootflow_iter_uninit(&iter);
+
+	/* Use the environment variable to override it */
+	ut_assertok(env_set("boot_targets", "mmc1 mmc2"));
+	ut_assertok(bootflow_scan_first(&iter, 0, &bflow));
+	ut_asserteq(2, iter.num_devs);
+	ut_asserteq_str("mmc1.bootdev", iter.dev_order[0]->name);
+	ut_asserteq_str("mmc2.bootdev", iter.dev_order[1]->name);
+	bootflow_iter_uninit(&iter);
+
+	/*
+	 * Now drop both orderings, to check the default (prioriy/sequence)
+	 * ordering
+	 */
+	ut_assertok(env_set("boot_targets", NULL));
+	ut_assertok(bootstd_test_drop_bootdev_order(uts));
+
+	ut_assertok(bootflow_scan_first(&iter, 0, &bflow));
+	ut_asserteq(3, iter.num_devs);
+	ut_asserteq_str("mmc2.bootdev", iter.dev_order[0]->name);
+	ut_asserteq_str("mmc1.bootdev", iter.dev_order[1]->name);
+	ut_asserteq_str("mmc0.bootdev", iter.dev_order[2]->name);
+
+	/*
+	 * Check that adding aliases for the bootdevs works. We just fake it by
+	 * setting the sequence numbers directly.
+	 */
+	iter.dev_order[0]->seq_ = 0;
+	iter.dev_order[1]->seq_ = 3;
+	iter.dev_order[2]->seq_ = 2;
+	bootflow_iter_uninit(&iter);
+
+	ut_assertok(bootflow_scan_first(&iter, 0, &bflow));
+	ut_asserteq(3, iter.num_devs);
+	ut_asserteq_str("mmc2.bootdev", iter.dev_order[0]->name);
+	ut_asserteq_str("mmc0.bootdev", iter.dev_order[1]->name);
+	ut_asserteq_str("mmc1.bootdev", iter.dev_order[2]->name);
+	bootflow_iter_uninit(&iter);
+
+	return 0;
+}
+BOOTSTD_TEST(bootdev_test_order, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
+
+/* Check bootdev ordering with the uclass priority */
+static int bootdev_test_prio(struct unit_test_state *uts)
+{
+	struct bootdev_uc_plat *ucp;
+	struct bootflow_iter iter;
+	struct bootflow bflow;
+	struct udevice *blk;
+
+	/* Start up USB which gives us three additional bootdevs */
+	usb_started = false;
+	ut_assertok(run_command("usb start", 0));
+
+	ut_assertok(bootstd_test_drop_bootdev_order(uts));
+
+	/* 3 MMC and 3 USB bootdevs: MMC should come before USB */
+	console_record_reset_enable();
+	ut_assertok(bootflow_scan_first(&iter, 0, &bflow));
+	ut_asserteq(6, iter.num_devs);
+	ut_asserteq_str("mmc2.bootdev", iter.dev_order[0]->name);
+	ut_asserteq_str("usb_mass_storage.lun0.bootdev",
+			iter.dev_order[3]->name);
+
+	ut_assertok(bootdev_get_sibling_blk(iter.dev_order[3], &blk));
+	ut_asserteq_str("usb_mass_storage.lun0", blk->name);
+
+	/* adjust the priority of the first USB bootdev to the highest */
+	ucp = dev_get_uclass_plat(iter.dev_order[3]);
+	ucp->prio = 1;
+
+	bootflow_iter_uninit(&iter);
+	ut_assertok(bootflow_scan_first(&iter, 0, &bflow));
+	ut_asserteq(6, iter.num_devs);
+	ut_asserteq_str("usb_mass_storage.lun0.bootdev",
+			iter.dev_order[0]->name);
+	ut_asserteq_str("mmc2.bootdev", iter.dev_order[1]->name);
+
+	return 0;
+}
+BOOTSTD_TEST(bootdev_test_prio, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
diff --git a/test/boot/bootflow.c b/test/boot/bootflow.c
new file mode 100644
index 00000000000..1ebb789e97b
--- /dev/null
+++ b/test/boot/bootflow.c
@@ -0,0 +1,400 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Test for bootdev functions. All start with 'bootdev'
+ *
+ * Copyright 2021 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <common.h>
+#include <bootdev.h>
+#include <bootflow.h>
+#include <bootstd.h>
+#include <dm.h>
+#include <dm/lists.h>
+#include <test/suites.h>
+#include <test/ut.h>
+#include "bootstd_common.h"
+
+/* Check 'bootflow scan/list' commands */
+static int bootflow_cmd(struct unit_test_state *uts)
+{
+	console_record_reset_enable();
+	ut_assertok(run_command("bootdev select 1", 0));
+	ut_assert_console_end();
+	ut_assertok(run_command("bootflow scan -l", 0));
+	ut_assert_nextline("Scanning for bootflows in bootdev 'mmc1.bootdev'");
+	ut_assert_nextline("Seq  Method       State   Uclass    Part  Name                      Filename");
+	ut_assert_nextlinen("---");
+	ut_assert_nextline("  0  syslinux     ready   mmc          1  mmc1.bootdev.part_1       /extlinux/extlinux.conf");
+	ut_assert_nextlinen("---");
+	ut_assert_nextline("(1 bootflow, 1 valid)");
+	ut_assert_console_end();
+
+	ut_assertok(run_command("bootflow list", 0));
+	ut_assert_nextline("Showing bootflows for bootdev 'mmc1.bootdev'");
+	ut_assert_nextline("Seq  Method       State   Uclass    Part  Name                      Filename");
+	ut_assert_nextlinen("---");
+	ut_assert_nextline("  0  syslinux     ready   mmc          1  mmc1.bootdev.part_1       /extlinux/extlinux.conf");
+	ut_assert_nextlinen("---");
+	ut_assert_nextline("(1 bootflow, 1 valid)");
+	ut_assert_console_end();
+
+	return 0;
+}
+BOOTSTD_TEST(bootflow_cmd, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
+
+/* Check 'bootflow scan' with a name / label / seq */
+static int bootflow_cmd_label(struct unit_test_state *uts)
+{
+	console_record_reset_enable();
+	ut_assertok(run_command("bootflow scan -l mmc1", 0));
+	ut_assert_nextline("Scanning for bootflows in bootdev 'mmc1.bootdev'");
+	ut_assert_skip_to_line("(1 bootflow, 1 valid)");
+	ut_assert_console_end();
+
+	ut_assertok(run_command("bootflow scan -l mmc0.bootdev", 0));
+	ut_assert_nextline("Scanning for bootflows in bootdev 'mmc0.bootdev'");
+	ut_assert_skip_to_line("(0 bootflows, 0 valid)");
+	ut_assert_console_end();
+
+	ut_assertok(run_command("bootflow scan -l 0", 0));
+	ut_assert_nextline("Scanning for bootflows in bootdev 'mmc2.bootdev'");
+	ut_assert_skip_to_line("(0 bootflows, 0 valid)");
+	ut_assert_console_end();
+
+	return 0;
+}
+BOOTSTD_TEST(bootflow_cmd_label, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
+
+/* Check 'bootflow scan/list' commands using all bootdevs */
+static int bootflow_cmd_glob(struct unit_test_state *uts)
+{
+	ut_assertok(bootstd_test_drop_bootdev_order(uts));
+
+	console_record_reset_enable();
+	ut_assertok(run_command("bootflow scan -l", 0));
+	ut_assert_nextline("Scanning for bootflows in all bootdevs");
+	ut_assert_nextline("Seq  Method       State   Uclass    Part  Name                      Filename");
+	ut_assert_nextlinen("---");
+	ut_assert_nextline("Scanning bootdev 'mmc2.bootdev':");
+	ut_assert_nextline("Scanning bootdev 'mmc1.bootdev':");
+	ut_assert_nextline("  0  syslinux     ready   mmc          1  mmc1.bootdev.part_1       /extlinux/extlinux.conf");
+	ut_assert_nextline("Scanning bootdev 'mmc0.bootdev':");
+	ut_assert_nextline("No more bootdevs");
+	ut_assert_nextlinen("---");
+	ut_assert_nextline("(1 bootflow, 1 valid)");
+	ut_assert_console_end();
+
+	ut_assertok(run_command("bootflow list", 0));
+	ut_assert_nextline("Showing all bootflows");
+	ut_assert_nextline("Seq  Method       State   Uclass    Part  Name                      Filename");
+	ut_assert_nextlinen("---");
+	ut_assert_nextline("  0  syslinux     ready   mmc          1  mmc1.bootdev.part_1       /extlinux/extlinux.conf");
+	ut_assert_nextlinen("---");
+	ut_assert_nextline("(1 bootflow, 1 valid)");
+	ut_assert_console_end();
+
+	return 0;
+}
+BOOTSTD_TEST(bootflow_cmd_glob, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
+
+/* Check 'bootflow scan -e' */
+static int bootflow_cmd_scan_e(struct unit_test_state *uts)
+{
+	ut_assertok(bootstd_test_drop_bootdev_order(uts));
+
+	console_record_reset_enable();
+	ut_assertok(run_command("bootflow scan -ale", 0));
+	ut_assert_nextline("Scanning for bootflows in all bootdevs");
+	ut_assert_nextline("Seq  Method       State   Uclass    Part  Name                      Filename");
+	ut_assert_nextlinen("---");
+	ut_assert_nextline("Scanning bootdev 'mmc2.bootdev':");
+	ut_assert_nextline("  0  syslinux     media   mmc          0  mmc2.bootdev.whole        <NULL>");
+	ut_assert_nextline("     ** No partition found, err=-93");
+	ut_assert_nextline("  1  efi          media   mmc          0  mmc2.bootdev.whole        <NULL>");
+	ut_assert_nextline("     ** No partition found, err=-93");
+
+	ut_assert_nextline("Scanning bootdev 'mmc1.bootdev':");
+	ut_assert_nextline("  2  syslinux     media   mmc          0  mmc1.bootdev.whole        <NULL>");
+	ut_assert_nextline("     ** No partition found, err=-2");
+	ut_assert_nextline("  3  efi          media   mmc          0  mmc1.bootdev.whole        <NULL>");
+	ut_assert_nextline("     ** No partition found, err=-2");
+	ut_assert_nextline("  4  syslinux     ready   mmc          1  mmc1.bootdev.part_1       /extlinux/extlinux.conf");
+	ut_assert_nextline("  5  efi          fs      mmc          1  mmc1.bootdev.part_1       efi/boot/bootsbox.efi");
+
+	ut_assert_skip_to_line("Scanning bootdev 'mmc0.bootdev':");
+	ut_assert_skip_to_line(" 3f  efi          media   mmc          0  mmc0.bootdev.whole        <NULL>");
+	ut_assert_nextline("     ** No partition found, err=-93");
+	ut_assert_nextline("No more bootdevs");
+	ut_assert_nextlinen("---");
+	ut_assert_nextline("(64 bootflows, 1 valid)");
+	ut_assert_console_end();
+
+	ut_assertok(run_command("bootflow list", 0));
+	ut_assert_nextline("Showing all bootflows");
+	ut_assert_nextline("Seq  Method       State   Uclass    Part  Name                      Filename");
+	ut_assert_nextlinen("---");
+	ut_assert_nextline("  0  syslinux     media   mmc          0  mmc2.bootdev.whole        <NULL>");
+	ut_assert_nextline("  1  efi          media   mmc          0  mmc2.bootdev.whole        <NULL>");
+	ut_assert_skip_to_line("  4  syslinux     ready   mmc          1  mmc1.bootdev.part_1       /extlinux/extlinux.conf");
+	ut_assert_skip_to_line(" 3f  efi          media   mmc          0  mmc0.bootdev.whole        <NULL>");
+	ut_assert_nextlinen("---");
+	ut_assert_nextline("(64 bootflows, 1 valid)");
+	ut_assert_console_end();
+
+	return 0;
+}
+BOOTSTD_TEST(bootflow_cmd_scan_e, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
+
+/* Check 'bootflow info' */
+static int bootflow_cmd_info(struct unit_test_state *uts)
+{
+	console_record_reset_enable();
+	ut_assertok(run_command("bootdev select 1", 0));
+	ut_assert_console_end();
+	ut_assertok(run_command("bootflow scan", 0));
+	ut_assert_console_end();
+	ut_assertok(run_command("bootflow select 0", 0));
+	ut_assert_console_end();
+	ut_assertok(run_command("bootflow info", 0));
+	ut_assert_nextline("Name:      mmc1.bootdev.part_1");
+	ut_assert_nextline("Device:    mmc1.bootdev");
+	ut_assert_nextline("Block dev: mmc1.blk");
+	ut_assert_nextline("Method:    syslinux");
+	ut_assert_nextline("State:     ready");
+	ut_assert_nextline("Partition: 1");
+	ut_assert_nextline("Subdir:    (none)");
+	ut_assert_nextline("Filename:  /extlinux/extlinux.conf");
+	ut_assert_nextlinen("Buffer:    ");
+	ut_assert_nextline("Size:      253 (595 bytes)");
+	ut_assert_nextline("Error:     0");
+	ut_assert_console_end();
+
+	ut_assertok(run_command("bootflow info -d", 0));
+	ut_assert_nextline("Name:      mmc1.bootdev.part_1");
+	ut_assert_skip_to_line("Error:     0");
+	ut_assert_nextline("Contents:");
+	ut_assert_nextline("%s", "");
+	ut_assert_nextline("# extlinux.conf generated by appliance-creator");
+	ut_assert_skip_to_line("        initrd /initramfs-5.3.7-301.fc31.armv7hl.img");
+	ut_assert_console_end();
+
+	return 0;
+}
+BOOTSTD_TEST(bootflow_cmd_info, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
+
+/* Check 'bootflow scan -b' to boot the first available bootdev */
+static int bootflow_scan_boot(struct unit_test_state *uts)
+{
+	console_record_reset_enable();
+	ut_assertok(run_command("bootflow scan -b", 0));
+	ut_assert_nextline(
+		"** Booting bootflow 'mmc1.bootdev.part_1' with syslinux");
+	ut_assert_nextline("Ignoring unknown command: ui");
+
+	/*
+	 * We expect it to get through to boot although sandbox always returns
+	 * -EFAULT as it cannot actually boot the kernel
+	 */
+	ut_assert_skip_to_line("sandbox: continuing, as we cannot run Linux");
+	ut_assert_nextline("Boot failed (err=-14)");
+	ut_assert_console_end();
+
+	return 0;
+}
+BOOTSTD_TEST(bootflow_scan_boot, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
+
+/* Check iterating through available bootflows */
+static int bootflow_iter(struct unit_test_state *uts)
+{
+	struct bootflow_iter iter;
+	struct bootflow bflow;
+
+	bootstd_clear_glob();
+
+	/* The first device is mmc2.bootdev which has no media */
+	ut_asserteq(-EPROTONOSUPPORT,
+		    bootflow_scan_first(&iter, BOOTFLOWF_ALL, &bflow));
+	ut_asserteq(2, iter.num_methods);
+	ut_asserteq(0, iter.cur_method);
+	ut_asserteq(0, iter.part);
+	ut_asserteq(0, iter.max_part);
+	ut_asserteq_str("syslinux", iter.method->name);
+	ut_asserteq(0, bflow.err);
+
+	/*
+	 * This shows MEDIA even though there is none, since int
+	 * bootdev_find_in_blk() we call part_get_info() which returns
+	 * -EPROTONOSUPPORT. Ideally it would return -EEOPNOTSUPP and we would
+	 * know.
+	 */
+	ut_asserteq(BOOTFLOWST_MEDIA, bflow.state);
+
+	ut_asserteq(-EPROTONOSUPPORT, bootflow_scan_next(&iter, &bflow));
+	ut_asserteq(2, iter.num_methods);
+	ut_asserteq(1, iter.cur_method);
+	ut_asserteq(0, iter.part);
+	ut_asserteq(0, iter.max_part);
+	ut_asserteq_str("efi", iter.method->name);
+	ut_asserteq(0, bflow.err);
+	ut_asserteq(BOOTFLOWST_MEDIA, bflow.state);
+	bootflow_free(&bflow);
+
+	/* The next device is mmc1.bootdev - at first we use the whole device */
+	ut_asserteq(-ENOENT, bootflow_scan_next(&iter, &bflow));
+	ut_asserteq(2, iter.num_methods);
+	ut_asserteq(0, iter.cur_method);
+	ut_asserteq(0, iter.part);
+	ut_asserteq(0x1e, iter.max_part);
+	ut_asserteq_str("syslinux", iter.method->name);
+	ut_asserteq(0, bflow.err);
+	ut_asserteq(BOOTFLOWST_MEDIA, bflow.state);
+	bootflow_free(&bflow);
+
+	ut_asserteq(-ENOENT, bootflow_scan_next(&iter, &bflow));
+	ut_asserteq(2, iter.num_methods);
+	ut_asserteq(1, iter.cur_method);
+	ut_asserteq(0, iter.part);
+	ut_asserteq(0x1e, iter.max_part);
+	ut_asserteq_str("efi", iter.method->name);
+	ut_asserteq(0, bflow.err);
+	ut_asserteq(BOOTFLOWST_MEDIA, bflow.state);
+	bootflow_free(&bflow);
+
+	/* Then more to partition 1 where we find something */
+	ut_assertok(bootflow_scan_next(&iter, &bflow));
+	ut_asserteq(2, iter.num_methods);
+	ut_asserteq(0, iter.cur_method);
+	ut_asserteq(1, iter.part);
+	ut_asserteq(0x1e, iter.max_part);
+	ut_asserteq_str("syslinux", iter.method->name);
+	ut_asserteq(0, bflow.err);
+	ut_asserteq(BOOTFLOWST_READY, bflow.state);
+	bootflow_free(&bflow);
+
+	ut_asserteq(-ENOENT, bootflow_scan_next(&iter, &bflow));
+	ut_asserteq(2, iter.num_methods);
+	ut_asserteq(1, iter.cur_method);
+	ut_asserteq(1, iter.part);
+	ut_asserteq(0x1e, iter.max_part);
+	ut_asserteq_str("efi", iter.method->name);
+	ut_asserteq(0, bflow.err);
+	ut_asserteq(BOOTFLOWST_FS, bflow.state);
+	bootflow_free(&bflow);
+
+	/* Then more to partition 2 which doesn't exist */
+	ut_asserteq(-ENOENT, bootflow_scan_next(&iter, &bflow));
+	ut_asserteq(2, iter.num_methods);
+	ut_asserteq(0, iter.cur_method);
+	ut_asserteq(2, iter.part);
+	ut_asserteq(0x1e, iter.max_part);
+	ut_asserteq_str("syslinux", iter.method->name);
+	ut_asserteq(0, bflow.err);
+	ut_asserteq(BOOTFLOWST_MEDIA, bflow.state);
+	bootflow_free(&bflow);
+
+	bootflow_iter_uninit(&iter);
+
+	ut_assert_console_end();
+
+	return 0;
+}
+BOOTSTD_TEST(bootflow_iter, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
+
+/* Check using the system bootdev */
+static int bootflow_system(struct unit_test_state *uts)
+{
+	struct udevice *bootstd, *dev;
+
+	/* Add the EFI bootmgr driver */
+	ut_assertok(uclass_first_device_err(UCLASS_BOOTSTD, &bootstd));
+	ut_assertok(device_bind_driver(bootstd, "bootmeth_efi_mgr", "bootmgr",
+				       &dev));
+
+	/* Add the system bootdev that it uses */
+	ut_assertok(device_bind_driver(bootstd, "system_bootdev",
+				       "system-bootdev", &dev));
+
+	ut_assertok(bootstd_test_drop_bootdev_order(uts));
+
+	/* We should get a single 'bootmgr' method right at the end */
+	bootstd_clear_glob();
+	console_record_reset_enable();
+	ut_assertok(run_command("bootflow scan -l", 0));
+	ut_assert_skip_to_line("  1  bootmgr      ready   bootstd      0  <NULL>                    <NULL>");
+	ut_assert_nextline("No more bootdevs");
+	ut_assert_skip_to_line("(2 bootflows, 2 valid)");
+	ut_assert_console_end();
+
+	return 0;
+}
+BOOTSTD_TEST(bootflow_system, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
+
+/* Check disabling a bootmethod if it requests it */
+static int bootflow_iter_disable(struct unit_test_state *uts)
+{
+	struct udevice *bootstd, *dev;
+	struct bootflow_iter iter;
+	struct bootflow bflow;
+	int i;
+
+	/* Add the EFI bootmgr driver */
+	ut_assertok(uclass_first_device_err(UCLASS_BOOTSTD, &bootstd));
+	ut_assertok(device_bind_driver(bootstd, "bootmeth_sandbox", "sandbox",
+				       &dev));
+
+	/* Add the system bootdev that it uses */
+	ut_assertok(device_bind_driver(bootstd, "system_bootdev",
+				       "system-bootdev", &dev));
+
+	ut_assertok(bootstd_test_drop_bootdev_order(uts));
+
+	bootstd_clear_glob();
+	ut_assertok(run_command("bootflow scan -lb", 0));
+
+	/* Try to boot the bootmgr flow, which will fail */
+	console_record_reset_enable();
+	ut_assertok(bootflow_scan_first(&iter, 0, &bflow));
+	ut_asserteq(3, iter.num_methods);
+	ut_asserteq_str("sandbox", iter.method->name);
+	ut_asserteq(-ENOTSUPP, bootflow_run_boot(&iter, &bflow));
+
+	ut_assert_skip_to_line("Boot method 'sandbox' failed and will not be retried");
+	ut_assert_console_end();
+
+	/* Check that the sandbox bootmeth has been removed */
+	ut_asserteq(2, iter.num_methods);
+	for (i = 0; i < iter.num_methods; i++)
+		ut_assert(strcmp("sandbox", iter.method_order[i]->name));
+
+	return 0;
+}
+BOOTSTD_TEST(bootflow_iter_disable, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
+
+/* Check 'bootflow boot' to boot a selected bootflow */
+static int bootflow_cmd_boot(struct unit_test_state *uts)
+{
+	console_record_reset_enable();
+	ut_assertok(run_command("bootdev select 1", 0));
+	ut_assert_console_end();
+	ut_assertok(run_command("bootflow scan", 0));
+	ut_assert_console_end();
+	ut_assertok(run_command("bootflow select 0", 0));
+	ut_assert_console_end();
+	ut_asserteq(1, run_command("bootflow boot", 0));
+	ut_assert_nextline(
+		"** Booting bootflow 'mmc1.bootdev.part_1' with syslinux");
+	ut_assert_nextline("Ignoring unknown command: ui");
+
+	/*
+	 * We expect it to get through to boot although sandbox always returns
+	 * -EFAULT as it cannot actually boot the kernel
+	 */
+	ut_assert_skip_to_line("sandbox: continuing, as we cannot run Linux");
+	ut_assert_nextline("Boot failed (err=-14)");
+	ut_assert_console_end();
+
+	return 0;
+}
+BOOTSTD_TEST(bootflow_cmd_boot, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
diff --git a/test/boot/bootmeth.c b/test/boot/bootmeth.c
new file mode 100644
index 00000000000..07776c5368d
--- /dev/null
+++ b/test/boot/bootmeth.c
@@ -0,0 +1,122 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Test for bootdev functions. All start with 'bootdev'
+ *
+ * Copyright 2021 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <common.h>
+#include <bootstd.h>
+#include <test/suites.h>
+#include <test/ut.h>
+#include "bootstd_common.h"
+
+/* Check 'bootmeth list' command */
+static int bootmeth_cmd_list(struct unit_test_state *uts)
+{
+	console_record_reset_enable();
+	ut_assertok(run_command("bootmeth list", 0));
+	ut_assert_nextline("Order  Seq  Name                Description");
+	ut_assert_nextlinen("---");
+	ut_assert_nextline("    0    0  syslinux            Syslinux boot from a block device");
+	ut_assert_nextline("    1    1  efi                 EFI boot from an .efi file");
+	ut_assert_nextlinen("---");
+	ut_assert_nextline("(2 bootmeths)");
+	ut_assert_console_end();
+
+	return 0;
+}
+BOOTSTD_TEST(bootmeth_cmd_list, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
+
+/* Check 'bootmeth order' command */
+static int bootmeth_cmd_order(struct unit_test_state *uts)
+{
+	/* Select just one bootmethod */
+	console_record_reset_enable();
+	ut_assertok(run_command("bootmeth order syslinux", 0));
+	ut_assert_console_end();
+	ut_assertnonnull(env_get("bootmeths"));
+	ut_asserteq_str("syslinux", env_get("bootmeths"));
+
+	/* Only that one should be listed */
+	ut_assertok(run_command("bootmeth list", 0));
+	ut_assert_nextline("Order  Seq  Name                Description");
+	ut_assert_nextlinen("---");
+	ut_assert_nextline("    0    0  syslinux            Syslinux boot from a block device");
+	ut_assert_nextlinen("---");
+	ut_assert_nextline("(1 bootmeth)");
+	ut_assert_console_end();
+
+	/* Check the -a flag, efi should show as not in the order ("-") */
+	ut_assertok(run_command("bootmeth list -a", 0));
+	ut_assert_nextline("Order  Seq  Name                Description");
+	ut_assert_nextlinen("---");
+	ut_assert_nextline("    0    0  syslinux            Syslinux boot from a block device");
+	ut_assert_nextline("    -    1  efi                 EFI boot from an .efi file");
+	ut_assert_nextlinen("---");
+	ut_assert_nextline("(2 bootmeths)");
+	ut_assert_console_end();
+
+	/* Check the -a flag with the reverse order */
+	ut_assertok(run_command("bootmeth order \"efi syslinux\"", 0));
+	ut_assert_console_end();
+	ut_assertok(run_command("bootmeth list -a", 0));
+	ut_assert_nextline("Order  Seq  Name                Description");
+	ut_assert_nextlinen("---");
+	ut_assert_nextline("    1    0  syslinux            Syslinux boot from a block device");
+	ut_assert_nextline("    0    1  efi                 EFI boot from an .efi file");
+	ut_assert_nextlinen("---");
+	ut_assert_nextline("(2 bootmeths)");
+	ut_assert_console_end();
+
+	/* Now reset the order to empty, which should show all of them again */
+	ut_assertok(run_command("bootmeth order", 0));
+	ut_assert_console_end();
+	ut_assertnull(env_get("bootmeths"));
+	ut_assertok(run_command("bootmeth list", 0));
+	ut_assert_skip_to_line("(2 bootmeths)");
+
+	/* Try reverse order */
+	ut_assertok(run_command("bootmeth order \"efi syslinux\"", 0));
+	ut_assert_console_end();
+	ut_assertok(run_command("bootmeth list", 0));
+	ut_assert_nextline("Order  Seq  Name                Description");
+	ut_assert_nextlinen("---");
+	ut_assert_nextline("    0    1  efi                 EFI boot from an .efi file");
+	ut_assert_nextline("    1    0  syslinux            Syslinux boot from a block device");
+	ut_assert_nextlinen("---");
+	ut_assert_nextline("(2 bootmeths)");
+	ut_assertnonnull(env_get("bootmeths"));
+	ut_asserteq_str("efi syslinux", env_get("bootmeths"));
+	ut_assert_console_end();
+
+	return 0;
+}
+BOOTSTD_TEST(bootmeth_cmd_order, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
+
+/* Check 'bootmeths' env var */
+static int bootmeth_env(struct unit_test_state *uts)
+{
+	struct bootstd_priv *std;
+
+	ut_assertok(bootstd_get_priv(&std));
+
+	/* Select just one bootmethod */
+	console_record_reset_enable();
+	ut_assertok(env_set("bootmeths", "syslinux"));
+	ut_asserteq(1, std->bootmeth_count);
+
+	/* Select an invalid bootmethod */
+	ut_asserteq(1, run_command("setenv bootmeths fred", 0));
+	ut_assert_nextline("Unknown bootmeth 'fred'");
+	ut_assert_nextlinen("## Error inserting");
+	ut_assert_console_end();
+
+	ut_assertok(env_set("bootmeths", "efi syslinux"));
+	ut_asserteq(2, std->bootmeth_count);
+	ut_assert_console_end();
+
+	return 0;
+}
+BOOTSTD_TEST(bootmeth_env, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
diff --git a/test/boot/bootstd_common.c b/test/boot/bootstd_common.c
new file mode 100644
index 00000000000..05347d87106
--- /dev/null
+++ b/test/boot/bootstd_common.c
@@ -0,0 +1,35 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Test for bootdev functions. All start with 'bootdev'
+ *
+ * Copyright 2021 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <common.h>
+#include <bootstd.h>
+#include <dm.h>
+#include <test/suites.h>
+#include <test/ut.h>
+#include "bootstd_common.h"
+
+int bootstd_test_drop_bootdev_order(struct unit_test_state *uts)
+{
+	struct bootstd_priv *priv;
+	struct udevice *bootstd;
+
+	ut_assertok(uclass_first_device_err(UCLASS_BOOTSTD, &bootstd));
+	priv = dev_get_priv(bootstd);
+	priv->bootdev_order = NULL;
+
+	return 0;
+}
+
+int do_ut_bootstd(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+	struct unit_test *tests = UNIT_TEST_SUITE_START(bootstd_test);
+	const int n_ents = UNIT_TEST_SUITE_COUNT(bootstd_test);
+
+	return cmd_ut_category("bootstd", "bootstd_test_",
+			       tests, n_ents, argc, argv);
+}
diff --git a/test/boot/bootstd_common.h b/test/boot/bootstd_common.h
new file mode 100644
index 00000000000..676ef0a57f9
--- /dev/null
+++ b/test/boot/bootstd_common.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Common header file for bootdev, bootflow, bootmeth tests
+ *
+ * Copyright 2021 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#ifndef __bootstd_common_h
+#define __bootstd_common_h
+
+/* Declare a new bootdev test */
+#define BOOTSTD_TEST(_name, _flags) \
+		UNIT_TEST(_name, _flags, bootstd_test)
+
+struct unit_test_state;
+
+/**
+ * bootstd_test_drop_bootdev_order() - Remove the existing boot order
+ *
+ * Drop the boot order so that all bootdevs are used in their alias order
+ *
+ * @uts: Unit test state to use for ut_assert...() functions
+ */
+int bootstd_test_drop_bootdev_order(struct unit_test_state *uts);
+
+#endif
diff --git a/test/cmd_ut.c b/test/cmd_ut.c
index 90b260f72d6..67a13ee32b8 100644
--- a/test/cmd_ut.c
+++ b/test/cmd_ut.c
@@ -28,6 +28,10 @@ int cmd_ut_category(const char *name, const char *prefix,
 
 static struct cmd_tbl cmd_ut_sub[] = {
 	U_BOOT_CMD_MKENT(all, CONFIG_SYS_MAXARGS, 1, do_ut_all, "", ""),
+#ifdef CONFIG_BOOTSTD
+	U_BOOT_CMD_MKENT(bootstd, CONFIG_SYS_MAXARGS, 1, do_ut_bootstd,
+			 "", ""),
+#endif
 	U_BOOT_CMD_MKENT(common, CONFIG_SYS_MAXARGS, 1, do_ut_common, "", ""),
 #if defined(CONFIG_UT_DM)
 	U_BOOT_CMD_MKENT(dm, CONFIG_SYS_MAXARGS, 1, do_ut_dm, "", ""),
@@ -115,6 +119,9 @@ static char ut_help_text[] =
 	"ut bloblist - Test bloblist implementation\n"
 	"ut compression - Test compressors and bootm decompression\n"
 #endif
+#ifdef CONFIG_BOOTSTD
+	"ut bootstd - Test standard boot implementation\n"
+#endif
 #ifdef CONFIG_UT_DM
 	"ut dm [test-name]\n"
 #endif
-- 
2.36.0.rc2.479.g8af0fa9b8e-goog


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH v5 32/34] bootstd: Add setup for the bootflow tests
  2022-04-25  5:30 [PATCH v5 00/34] Initial implementation of standard boot Simon Glass
                   ` (27 preceding siblings ...)
  2022-04-25  5:31 ` [PATCH v5 31/34] bootstd: Add tests for bootstd including all uclasses Simon Glass
@ 2022-04-25  5:31 ` Simon Glass
  2022-05-09 18:06   ` Heinrich Schuchardt
  2022-04-25  5:31 ` [PATCH v5 33/34] bootstd: doc: Add documentation Simon Glass
                   ` (2 subsequent siblings)
  31 siblings, 1 reply; 36+ messages in thread
From: Simon Glass @ 2022-04-25  5:31 UTC (permalink / raw)
  To: U-Boot Mailing List
  Cc: Dennis Gilmore, Lukas Auer, Daniel Schwierzeck, Tom Rini,
	Michal Simek, Ilias Apalodimas, Steffen Jaeckel, Simon Glass

We need to create a disk image with a partition table and a DOS-format
filesystem containing a few files. Provide a fallback binary for CI since
it does not seem able to detect the loopback partitions.

Add this to a dm_init test so that it happens when needed.

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

(no changes since v3)

Changes in v3:
- Update test condition to use CMD_BOOTFLOW

 test/py/tests/bootstd/mmc1.img.xz | Bin 0 -> 4448 bytes
 test/py/tests/test_ut.py          | 103 ++++++++++++++++++++++++++++++
 2 files changed, 103 insertions(+)
 create mode 100644 test/py/tests/bootstd/mmc1.img.xz

diff --git a/test/py/tests/bootstd/mmc1.img.xz b/test/py/tests/bootstd/mmc1.img.xz
new file mode 100644
index 0000000000000000000000000000000000000000..4e7f39b830eb4bddcb9ff6df39e89f130b5706b9
GIT binary patch
literal 4448
zcmeI0c{JOJ7RP@`Y(-R5?X`}rGNKxz9<j8IUF{-@c9c=1cC|-CYOSqGnW?SD*rkjm
zwrEML9ZRXDs3;}Y(T7+w@167BKku)3b7tQA$KUsld(Y?I^ZkC$J<$wzbOHd-@<hW;
zRzR9X2mpYfua`=g%tdzeZ2)+1a%`NFk@nORl#0;_jb!Af2?Uq<OQN^F=hL1^yra5*
z)ULN=qSItcRC)YMV_~JMi8PdmuvKSER?oUli7w*j@UDfq@L=ZOpyC)?J-1xS$XpK2
zYZF!p9&ow&?GR4z*q+#}I`^&8k@vQAr-#^4?EB>Y1f(ncsMm4@s?)THBgy{6U{9Xf
zjhWyTLq>&G;svF#Bje#;bOmkhhE$<HEn&c%2h{26<0EU8pdm?&l)=HQjSfS}ol6@x
zpzJgMv-Wm(JIu8LqHF45kPhWz@@FypAEFuOhM3ukHzDQ6k#k3RYA}0pTKq@YfHbYw
zdUq%F7k&Fy&GRZR1s1fW??laBe{OaWLwSpAO-JdpaXPcd3)>q9$<~I^9H$OcZc$TV
zMm~rDFT{WWjO6gcMYYHc+6;^JG8pkme*14Ce5le4a3Xkbt70^O7K>VGXX_ybx)*eD
zw6^43jCQ-EnZSx&neK#B$O{UBSSV)XEs1b#mSt8v(^ROEPcQ~<C-O-O`gl%=c`dC@
zZlJMG>?h~&+LRdcB!|Pg*6V;d|HfzwOc`HZ0F{bOiS(PfoQ$+96!SnBncAog%~~q+
z4pv?MwLy<7e_muzu9NC3mOJG=EjfgHZ^&EDT#iavdv!R~fYhpQHts|&4o{9py`Y_%
z31`~6QY-sMx{bA(spJ|Hg0FssAbqKREdQ!nnHCFF(SgD|u*oO@)b?YHOfi`nb2iqE
zo#dvbuHH>ts%D|qG|pzT<NA5=U}Cbr#w>pV-7MtB>3Vq!+w8Zx#o9YoLQmNagFld#
zP(41!ib&tauzdKXL3Wdvj^TFM^wfBX8wSVK3`s(RwOWClx9g+fhLgZM^F}%6wv}i0
z_2T0?&*=|tVg38)KCU;((P$nEiz00YJ12kgQ7#!ImFl3}h1PaL*2_u7xNC-Xp8x}W
zrZ#;_6cWGawviOU!FpMa4xBc8BKBpF@HY;3jrzikAK)rrz7cK3SIdLk);?Swpj+u!
zauVI(Z3zMX;2A<am3^4{a9nP@6&l!P$d!7NOA$g52)R<#;v=pvjUNMFv}5~Gpn<XO
zf&ZFXQ@4{ZzAqDJ4&Hl+Q7Ry)Vx3J)9j{n$D2$b|O9az}FRWcPYku-t()ezlD$;Tr
z{<MNtCh(+P)vKt-598=eQQ>oFtX#dnXQY97ZpySTjNrJ{%!f$Y`gK%8nEg4geO3mc
z-OP*|Ch_5AWY9NP%p&~M>ZAJ??-^R7P2+1Hb2rgsO2X$eMJ<7l))#5bjryO*Dqa)`
z%;XHzXL<#CLI9X5|4{QLx2n8#cjne^EA6#n{fUKE*apICl#Rr5`b4w-6|DH9x(%>u
zAblDl+7gOW;#{pAugK1-4d((c<m--*W%*!KZ`#Yrg*yS}QFfbytt^U?hi1<(z;~<9
z>KffC%}1y~xAgK-kIY#om$h-hU6uL%6{?p4=faq|cA$OJ7|(inY7C)NO<foyYD}kK
zpk%Vbqa~iOxE%Y9biE>BNMaX+?yr2|aOhe9tKnj)<)sFT!^mcwJjoRb=G@*C$wRry
zPTo*uOsd<i&-G@k9BIb#%Lp+wS5XrE;Ug_Ar5_L-w35%d&}Y=@X=U~b`RqY1eoyIW
z1?~tZ8PYQ9Aj}u!U;X968@+To%~0N@*zT)7XK@X?#EJH;wuw{nu8u$=o@j5zGKrX&
zJDO5(48p&Q>FL<_`GKQyEJE$O5|axr@ik&j;SomgcM_v6f@QoLawwxx>yHJePg!~N
z!c}v~e55d};P9~<!QsvJsRGm4PdupPSil^DD*B8!R^Q3Fk3Ef^!?6)FEql?IuLvyX
zEkBm|Dr@V$SFvVNmVfc^w|dGcjIoa7;`PGVTo8qCog$n4y0@}P;v)~}Zatb2m>2ji
z?smHa;#j<|fU46!w;JJ1IC}O?Fiq)m)lfbK=CSvquxAa|fn+FXteGTw`$1R2xqL53
z+Rq|Rc3KPV#k%(QqK*DO9AuCG9)ulipIS+(&QWE;*777sZ;1-R&S=7_hqYtJ>Yt>x
zRotR}1x$Vx6<|HkrtGAnCK4`M(3A?t`n@QVbzN5+h#$$$O^Q~|*ii`H7a_Cfn&+NO
z){dt`y5(&cyBP>LxU_EbL6@I7if1I_d_}d}Uo+;vP2meZmVEF3+S&?&e02uw_ZaxU
zc6A2q_a>e*VE<)AJOg$H?0@1nXBu{<VSjS)`j13&c42?u!f>3A05H(`mV)`mrvw0O
h4`jqrLheQZEG;rP9FCigsLUjGdmgd=vj{>v{RVxmraJ%t

literal 0
HcmV?d00001

diff --git a/test/py/tests/test_ut.py b/test/py/tests/test_ut.py
index 01c2b3ffa12..35fb393c1ff 100644
--- a/test/py/tests/test_ut.py
+++ b/test/py/tests/test_ut.py
@@ -1,9 +1,102 @@
 # SPDX-License-Identifier: GPL-2.0
 # Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
 
+import gzip
+import os
 import os.path
 import pytest
 
+import u_boot_utils
+
+def mkdir_cond(dirname):
+    """Create a directory if it doesn't already exist
+
+    Args:
+        dirname: Name of directory to create
+    """
+    if not os.path.exists(dirname):
+        os.mkdir(dirname)
+
+def setup_bootflow_image(u_boot_console):
+    """Create a 20MB disk image with a single FAT partition"""
+    cons = u_boot_console
+    fname = os.path.join(cons.config.source_dir, 'mmc1.img')
+    mnt = os.path.join(cons.config.persistent_data_dir, 'mnt')
+    mkdir_cond(mnt)
+
+    u_boot_utils.run_and_log(cons, 'qemu-img create %s 20M' % fname)
+    u_boot_utils.run_and_log(cons, 'sudo sfdisk %s' % fname,
+                             stdin=b'type=c')
+
+    loop = None
+    mounted = False
+    complete = False
+    try:
+        out = u_boot_utils.run_and_log(cons,
+                                       'sudo losetup --show -f -P %s' % fname)
+        loop = out.strip()
+        fatpart = '%sp1' % loop
+        u_boot_utils.run_and_log(cons, 'sudo mkfs.vfat %s' % fatpart)
+        u_boot_utils.run_and_log(
+            cons, 'sudo mount -o loop %s %s -o uid=%d,gid=%d' %
+            (fatpart, mnt, os.getuid(), os.getgid()))
+        mounted = True
+
+        vmlinux = 'vmlinuz-5.3.7-301.fc31.armv7hl'
+        initrd = 'initramfs-5.3.7-301.fc31.armv7hl.img'
+        dtbdir = 'dtb-5.3.7-301.fc31.armv7hl'
+        script = '''# extlinux.conf generated by appliance-creator
+ui menu.c32
+menu autoboot Welcome to Fedora-Workstation-armhfp-31-1.9. Automatic boot in # second{,s}. Press a key for options.
+menu title Fedora-Workstation-armhfp-31-1.9 Boot Options.
+menu hidden
+timeout 20
+totaltimeout 600
+
+label Fedora-Workstation-armhfp-31-1.9 (5.3.7-301.fc31.armv7hl)
+        kernel /%s
+        append ro root=UUID=9732b35b-4cd5-458b-9b91-80f7047e0b8a rhgb quiet LANG=en_US.UTF-8 cma=192MB cma=256MB
+        fdtdir /%s/
+        initrd /%s''' % (vmlinux, dtbdir, initrd)
+        ext = os.path.join(mnt, 'extlinux')
+        mkdir_cond(ext)
+
+        with open(os.path.join(ext, 'extlinux.conf'), 'w') as fd:
+            print(script, file=fd)
+
+        inf = os.path.join(cons.config.persistent_data_dir, 'inf')
+        with open(inf, 'wb') as fd:
+            fd.write(gzip.compress(b'vmlinux'))
+        u_boot_utils.run_and_log(cons, 'mkimage -f auto -d %s %s' %
+                                 (inf, os.path.join(mnt, vmlinux)))
+
+        with open(os.path.join(mnt, initrd), 'w') as fd:
+            print('initrd', file=fd)
+
+        mkdir_cond(os.path.join(mnt, dtbdir))
+
+        dtb_file = os.path.join(mnt, '%s/sandbox.dtb' % dtbdir)
+        u_boot_utils.run_and_log(
+            cons, 'dtc -o %s' % dtb_file, stdin=b'/dts-v1/; / {};')
+        complete = True
+    except ValueError as exc:
+        print('Falled to create image, failing back to prepared copy: %s',
+              str(exc))
+    finally:
+        if mounted:
+            u_boot_utils.run_and_log(cons, 'sudo umount %s' % mnt)
+        if loop:
+            u_boot_utils.run_and_log(cons, 'sudo losetup -d %s' % loop)
+
+    if not complete:
+        # Use a prepared image since we cannot create one
+        infname = os.path.join(cons.config.source_dir,
+                               'test/py/tests/bootstd/mmc1.img.xz')
+        u_boot_utils.run_and_log(
+            cons,
+            ['sh', '-c', 'xz -dc %s >%s' % (infname, fname)])
+
+
 @pytest.mark.buildconfigspec('ut_dm')
 def test_ut_dm_init(u_boot_console):
     """Initialize data for ut dm tests."""
@@ -21,6 +114,16 @@ def test_ut_dm_init(u_boot_console):
         with open(fn, 'wb') as fh:
             fh.write(data)
 
+@pytest.mark.buildconfigspec('cmd_bootflow')
+def test_ut_dm_init_bootstd(u_boot_console):
+    """Initialise data for bootflow tests"""
+
+    setup_bootflow_image(u_boot_console)
+
+    # Restart so that the new mmc1.img is picked up
+    u_boot_console.restart_uboot()
+
+
 def test_ut(u_boot_console, ut_subtest):
     """Execute a "ut" subtest.
 
-- 
2.36.0.rc2.479.g8af0fa9b8e-goog


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH v5 33/34] bootstd: doc: Add documentation
  2022-04-25  5:30 [PATCH v5 00/34] Initial implementation of standard boot Simon Glass
                   ` (28 preceding siblings ...)
  2022-04-25  5:31 ` [PATCH v5 32/34] bootstd: Add setup for the bootflow tests Simon Glass
@ 2022-04-25  5:31 ` Simon Glass
  2022-04-25  5:31 ` [PATCH v5 34/34] bootstd: Provide a default command Simon Glass
  2022-04-25 22:50 ` [PATCH v5 00/34] Initial implementation of standard boot Tom Rini
  31 siblings, 0 replies; 36+ messages in thread
From: Simon Glass @ 2022-04-25  5:31 UTC (permalink / raw)
  To: U-Boot Mailing List
  Cc: Dennis Gilmore, Lukas Auer, Daniel Schwierzeck, Tom Rini,
	Michal Simek, Ilias Apalodimas, Steffen Jaeckel, Simon Glass

Add documentation for this feature, including the commands and full
devicetree bindings.

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

Changes in v5:
- Add a Kconfig to select bootstd by default
- Rebase to master

Changes in v4:
- Update docs for typos that have been fixed

Changes in v3:
- Update docs for "bootmeths" and "boot_targets" env vars

 MAINTAINERS                           |   4 +
 doc/develop/bootstd.rst               | 638 ++++++++++++++++++++++++++
 doc/develop/distro.rst                |   3 +
 doc/develop/index.rst                 |   1 +
 doc/device-tree-bindings/bootdev.txt  |  18 +
 doc/device-tree-bindings/bootmeth.txt |  31 ++
 doc/device-tree-bindings/bootstd.txt  |   8 +
 doc/usage/cmd/bootdev.rst             | 135 ++++++
 doc/usage/cmd/bootflow.rst            | 427 +++++++++++++++++
 doc/usage/cmd/bootmeth.rst            | 108 +++++
 doc/usage/index.rst                   |   3 +
 11 files changed, 1376 insertions(+)
 create mode 100644 doc/develop/bootstd.rst
 create mode 100644 doc/device-tree-bindings/bootmeth.txt
 create mode 100644 doc/usage/cmd/bootdev.rst
 create mode 100644 doc/usage/cmd/bootflow.rst
 create mode 100644 doc/usage/cmd/bootmeth.rst

diff --git a/MAINTAINERS b/MAINTAINERS
index 963ec91c891..bda7b81efc5 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -702,6 +702,10 @@ F:	boot/bootmeth*.c
 F:	boot/bootstd.c
 F:	cmd/bootdev.c
 F:	cmd/bootflow.c
+F:	doc/develop/bootstd.rst
+F:	doc/usage/bootdev.rst
+F:	doc/usage/bootflow.rst
+F:	doc/usage/bootmeth.rst
 F:	drivers/mmc/mmc_bootdev.c
 F:	include/bootdev.h
 F:	include/bootflow.h
diff --git a/doc/develop/bootstd.rst b/doc/develop/bootstd.rst
new file mode 100644
index 00000000000..5e9c0d282bb
--- /dev/null
+++ b/doc/develop/bootstd.rst
@@ -0,0 +1,638 @@
+.. SPDX-License-Identifier: GPL-2.0+:
+
+U-Boot Standard Boot
+====================
+
+Introduction
+------------
+
+Standard boot provides a built-in way for U-Boot to automatically boot
+an Operating System without custom scripting and other customisation. It
+introduces the following concepts:
+
+   - bootdev  - a device which can hold or access a distro (e.g. MMC, Ethernet)
+   - bootmeth - a method to scan a bootdev to find bootflows (e.g. distro boot)
+   - bootflow - a description of how to boot (provided by the distro)
+
+For Linux, the distro (Linux distribution, e.g. Debian, Fedora) is responsible
+for creating a bootflow for each kernel combination that it wants to offer.
+These bootflows are stored on media so they can be discovered by U-Boot. This
+feature is typically called `distro boot` (see :doc:`distro`) because it is
+a way for distributions to boot on any hardware.
+
+Traditionally U-Boot has relied on scripts to implement this feature. See
+disto_boodcmd_ for details. This is done because U-Boot has no native support
+for scanning devices. While the scripts work remarkably well, they can be hard
+to understand and extend, and the feature does not include tests. They are also
+making it difficult to move away from ad-hoc CONFIGs, since they are implemented
+using the environment and a lot of #defines.
+
+Standard boot is a generalisation of distro boot. It provides a more built-in
+way to boot with U-Boot. The feature is extensible to different Operating
+Systems (such as Chromium OS) and devices (beyond just block and network
+devices). It supports EFI boot and EFI bootmgr too.
+
+
+Bootflow
+--------
+
+A bootflow is a file that describes how to boot a distro. Conceptually there can
+be different formats for that file but at present U-Boot only supports the
+BootLoaderSpec_ format. which looks something like this::
+
+   menu autoboot Welcome to Fedora-Workstation-armhfp-31-1.9. Automatic boot in # second{,s}. Press a key for options.
+   menu title Fedora-Workstation-armhfp-31-1.9 Boot Options.
+   menu hidden
+
+   label Fedora-Workstation-armhfp-31-1.9 (5.3.7-301.fc31.armv7hl)
+       kernel /vmlinuz-5.3.7-301.fc31.armv7hl
+       append ro root=UUID=9732b35b-4cd5-458b-9b91-80f7047e0b8a rhgb quiet LANG=en_US.UTF-8 cma=192MB cma=256MB
+       fdtdir /dtb-5.3.7-301.fc31.armv7hl/
+       initrd /initramfs-5.3.7-301.fc31.armv7hl.img
+
+As you can see it specifies a kernel, a ramdisk (initrd) and a directory from
+which to load devicetree files. The details are described in disto_boodcmd_.
+
+The bootflow is provided by the distro. It is not part of U-Boot. U-Boot's job
+is simply to interpret the file and carry out the instructions. This allows
+distros to boot on essentially any device supported by U-Boot.
+
+Typically the first available bootflow is selected and booted. If that fails,
+then the next one is tried.
+
+
+Bootdev
+-------
+
+Where does U-Boot find the media that holds the operating systems? That is the
+job of bootdev. A bootdev is simply a layer on top of a media device (such as
+MMC, NVMe). The bootdev accesses the device, including partitions and
+filesystems that might contain things related to an operating system.
+
+For example, an MMC bootdev provides access to the individual partitions on the
+MMC device. It scans through these to find filesystems, then provides a list of
+these for consideration.
+
+
+Bootmeth
+--------
+
+Once the list of filesystems is provided, how does U-Boot find the bootflow
+files in these filesystems. That is the job of bootmeth. Each boot method has
+its own way of doing this.
+
+For example, the distro bootmeth simply looks through the provided filesystem
+for a file called `extlinux/extlinux.conf`. This files constitutes a bootflow.
+If the distro bootmeth is used on multiple partitions it may produce multiple
+bootflows.
+
+Note: it is possible to have a bootmeth that uses a partition or a whole device
+directly, but it is more common to use a filesystem.
+
+
+Boot process
+------------
+
+U-Boot tries to use the 'lazy init' approach whereever possible and distro boot
+is no exception. The algorithm is::
+
+   while (get next bootdev)
+      while (get next bootmeth)
+          while (get next bootflow)
+              try to boot it
+
+So U-Boot works its way through the bootdevs, trying each bootmeth in turn to
+obtain bootflows, until it either boots or exhausts the available options.
+
+Instead of 500 lines of #defines and a 4KB boot script, all that is needed is
+the following command::
+
+   bootflow scan -lb
+
+which scans for available bootflows, optionally listing each find it finds (-l)
+and trying to boot it (-b).
+
+
+Controlling ordering
+--------------------
+
+Several options are available to control the ordering of boot scanning:
+
+
+boot_targets
+~~~~~~~~~~~~
+
+This environment variable can be used to control the list of bootdevs searched
+and their ordering, for example::
+
+   setenv boot_targets "mmc0 mmc1 usb pxe"
+
+Entries may be removed or re-ordered in this list to affect the boot order. If
+the variable is empty, the default ordering is used, based on the priority of
+bootdevs and their sequence numbers.
+
+
+bootmeths
+~~~~~~~~~
+
+This environment variable can be used to control the list of bootmeths used and
+their ordering for example::
+
+   setenv bootmeths "syslinux efi"
+
+Entries may be removed or re-ordered in this list to affect the order the
+bootmeths are tried on each bootdev. If the variable is empty, the default
+ordering is used, based on the bootmeth sequence numbers, which can be
+controlled by aliases.
+
+The :ref:`usage/cmd/bootmeth:bootmeth command` (`bootmeth order`) operates in
+the same way as setting this variable.
+
+
+Bootdev uclass
+--------------
+
+The bootdev uclass provides an simple API call to obtain a bootflows from a
+device::
+
+   int bootdev_get_bootflow(struct udevice *dev, struct bootflow_iter *iter,
+                            struct bootflow *bflow);
+
+This takes a iterator which indicates the bootdev, partition and bootmeth to
+use. It returns a bootflow. This is the core of the bootdev implementation. The
+bootdev drivers that implement this differ depending on the media they are
+reading from, but each is responsible for returning a valid bootflow if
+available.
+
+A helper called `bootdev_find_in_blk()` makes it fairly easy to implement this
+function for each media device uclass, in a few lines of code.
+
+
+Bootdev drivers
+---------------
+
+A bootdev driver is typically fairly simple. Here is one for mmc::
+
+    static int mmc_get_bootflow(struct udevice *dev, struct bootflow_iter *iter,
+                    struct bootflow *bflow)
+    {
+        struct udevice *mmc_dev = dev_get_parent(dev);
+        struct udevice *blk;
+        int ret;
+
+        ret = mmc_get_blk(mmc_dev, &blk);
+        /*
+         * If there is no media, indicate that no more partitions should be
+         * checked
+         */
+        if (ret == -EOPNOTSUPP)
+            ret = -ESHUTDOWN;
+        if (ret)
+            return log_msg_ret("blk", ret);
+        assert(blk);
+        ret = bootdev_find_in_blk(dev, blk, iter, bflow);
+        if (ret)
+            return log_msg_ret("find", ret);
+
+        return 0;
+    }
+
+    static int mmc_bootdev_bind(struct udevice *dev)
+    {
+        struct bootdev_uc_plat *ucp = dev_get_uclass_plat(dev);
+
+        ucp->prio = BOOTDEVP_0_INTERNAL_FAST;
+
+        return 0;
+    }
+
+    struct bootdev_ops mmc_bootdev_ops = {
+        .get_bootflow    = mmc_get_bootflow,
+    };
+
+    static const struct udevice_id mmc_bootdev_ids[] = {
+        { .compatible = "u-boot,bootdev-mmc" },
+        { }
+    };
+
+    U_BOOT_DRIVER(mmc_bootdev) = {
+        .name        = "mmc_bootdev",
+        .id        = UCLASS_BOOTDEV,
+        .ops        = &mmc_bootdev_ops,
+        .bind        = mmc_bootdev_bind,
+        .of_match    = mmc_bootdev_ids,
+    };
+
+The implementation of the `get_bootflow()` method is simply to obtain the
+block device and call a bootdev helper function to do the rest. The
+implementation of `bootdev_find_in_blk()` checks the partition table, and
+attempts to read a file from a filesystem on the partition number given by the
+`@iter->part` parameter.
+
+Each bootdev has a priority, which indicates the order in which it is used.
+Faster bootdevs are used first, since they are more likely to be able to boot
+the device quickly.
+
+
+Device hierarchy
+----------------
+
+A bootdev device is a child of the media device. In this example, you can see
+that the bootdev is a sibling of the block device and both are children of
+media device::
+
+    mmc           0  [ + ]   bcm2835-sdhost        |   |-- mmc@7e202000
+    blk           0  [ + ]   mmc_blk               |   |   |-- mmc@7e202000.blk
+    bootdev       0  [   ]   mmc_bootdev           |   |   `-- mmc@7e202000.bootdev
+    mmc           1  [ + ]   sdhci-bcm2835         |   |-- sdhci@7e300000
+    blk           1  [   ]   mmc_blk               |   |   |-- sdhci@7e300000.blk
+    bootdev       1  [   ]   mmc_bootdev           |   |   `-- sdhci@7e300000.bootdev
+
+The bootdev device is typically created automatically in the media uclass'
+`post_bind()` method by calling `bootdev_setup_for_dev()`. The code typically
+something like this::
+
+    ret = bootdev_setup_for_dev(dev, "eth_bootdev");
+    if (ret)
+        return log_msg_ret("bootdev", ret);
+
+Here, `eth_bootdev` is the name of the Ethernet bootdev driver and `dev`
+is the ethernet device. This function is safe to call even if standard boot is
+not enabled, since it does nothing in that case. It can be added to all uclasses
+which implement suitable media.
+
+
+The bootstd device
+------------------
+
+Standard boot requires a single instance of the bootstd device to make things
+work. This includes global information about the state of standard boot. See
+`struct bootstd_priv` for this structure, accessed with `bootstd_get_priv()`.
+
+Within the devicetree, if you add bootmeth devices or a system bootdev, they
+should be children of the bootstd device. See `arch/sandbox/dts/test.dts` for
+an example of this.
+
+
+The system bootdev
+------------------
+
+Some bootmeths don't operate on individual bootdevs, but on the whole system.
+For example, the EFI boot manager does its own device scanning and does not
+make use of the bootdev devices. Such bootmeths can make use of the system
+bootdev, typically considered last, after everything else has been tried.
+
+
+.. _`Automatic Devices`:
+
+Automatic devices
+-----------------
+
+It is possible to define all the required devices in the devicetree manually,
+but it is not necessary. The bootstd uclass includes a `dm_scan_other()`
+function which creates the bootstd device if not found. If no bootmeth devices
+are found at all, it creates one for each available bootmeth driver as well as a
+system bootdev.
+
+If your devicetree has any bootmeth device it must have all of them that you
+want to use, as well as the system bootdev if needed, since no bootmeth devices
+will be created automatically in that case.
+
+
+Using devicetree
+----------------
+
+If a bootdev is complicated or needs configuration information, it can be
+added to the devicetree as a child of the media device. For example, imagine a
+bootdev which reads a bootflow from SPI flash. The devicetree fragment might
+look like this::
+
+    spi@0 {
+        flash@0 {
+            reg = <0>;
+            compatible = "spansion,m25p16", "jedec,spi-nor";
+            spi-max-frequency = <40000000>;
+
+            bootdev {
+                compatible = "u-boot,sf-bootdev";
+                offset = <0x2000>;
+                size = <0x1000>;
+            };
+        };
+    };
+
+The `sf-bootdev` driver can implement a way to read from the SPI flash, using
+the offset and size provided, and return that bootflow file back to the caller.
+When distro boot wants to read the kernel it calls disto_getfile() which must
+provide a way to read from the SPI flash. See `distro_boot()` at distro_boot_
+for more details.
+
+Of course this is all internal to U-Boot. All the distro sees is another way
+to boot.
+
+
+Configuration
+-------------
+
+Standard boot is enabled with `CONFIG_BOOTSTD`. Each bootmeth has its own CONFIG
+option also. For example, `CONFIG_BOOTMETH_DISTRO` enables support for distro
+boot from a disk.
+
+
+Available bootmeth drivers
+--------------------------
+
+Bootmeth drivers are provided for:
+
+   - distro boot from a disk (syslinux)
+   - distro boot from a network (PXE)
+   - EFI boot using bootefi
+   - EFI boot using boot manager
+
+
+Command interface
+-----------------
+
+Three commands are available:
+
+`bootdev`
+    Allows listing of available bootdevs, selecting a particular one and
+    getting information about it. See :doc:`../usage/cmd/bootdev`
+
+`bootflow`
+    Allows scanning one or more bootdevs for bootflows, listing available
+    bootflows, selecting one, obtaining information about it and booting it.
+    See :doc:`../usage/cmd/bootflow`
+
+`bootmeth`
+    Allow listing of available bootmethds and setting the order in which they
+    are tried. See :doc:`../usage/cmd/bootmeth`
+
+.. _BootflowStates:
+
+Bootflow states
+---------------
+
+Here is a list of states that a bootflow can be in:
+
+=======  =======================================================================
+State    Meaning
+=======  =======================================================================
+base     Starting-out state, indicates that no media/partition was found. For an
+         SD card socket it may indicate that the card is not inserted.
+media    Media was found (e.g. SD card is inserted) but no partition information
+         was found. It might lack a partition table or have a read error.
+part     Partition was found but a filesystem could not be read. This could be
+         because the partition does not hold a filesystem or the filesystem is
+         very corrupted.
+fs       Filesystem was found but the file could not be read. It could be
+         missing or in the wrong subdirectory.
+file     File was found and its size detected, but it could not be read. This
+         could indicate filesystem corruption.
+ready    File was loaded and is ready for use. In this state the bootflow is
+         ready to be booted.
+=======  =======================================================================
+
+
+Theory of operation
+-------------------
+
+This describes how standard boot progresses through to booting an operating
+system.
+
+To start. all the necessary devices must be bound, including bootstd, which
+provides the top-level `struct bootstd_priv` containing optional configuration
+information. The bootstd device is also holds the various lists used while
+scanning. This step is normally handled automatically by driver model, as
+described in `Automatic Devices`_.
+
+Bootdevs are also required, to provide access to the media to use. These are not
+useful by themselves: bootmeths are needed to provide the means of scanning
+those bootdevs. So, all up, we need a single bootstd device, one or more bootdev
+devices and one or more bootmeth devices.
+
+Once these are ready, typically a `bootflow scan` command is issued. This kicks
+of the iteration process, which involves looking through the bootdevs and their
+partitions one by one to find bootflows.
+
+Iteration is kicked off using `bootflow_scan_first()`, which calls
+`bootflow_scan_bootdev()`.
+
+The iterator is set up with `bootflow_iter_init()`. This simply creates an
+empty one with the given flags. Flags are used to control whether each
+iteration is displayed, whether to return iterations even if they did not result
+in a valid bootflow, whether to iterate through just a single bootdev, etc.
+
+Then the ordering of bootdevs is determined, by `bootdev_setup_iter_order()`. By
+default, the bootdevs are used in the order specified by the `boot_targets`
+environment variable (e.g. "mmc2 mmc0 usb"). If that is missing then their
+sequence order is used, as determined by the `/aliases` node, or failing that
+their order in the devicetree. For BOOTSTD_FULL, if there is a `bootdev-order`
+property in the bootstd node, then this is used as a final fallback. In any
+case, the iterator ends up with a `dev_order` array containing the bootdevs that
+are going to be used, with `num_devs` set to the number of bootdevs and
+`cur_dev` starting at 0.
+
+Next, the ordering of bootdevs is determined, by `bootmeth_setup_iter_order()`.
+By default the ordering is again by sequence number, i.e. the `/aliases` node,
+or failing that the order in the devicetree. But the `bootmeth order` command
+or `bootmeths` environment variable can be used to set up an ordering. If that
+has been done, the ordering is in `struct bootstd_priv`, so that ordering is
+simply copied into the iterator. Either way, the `method_order` array it set up,
+along with `num_methods`. Then `cur_method` is set to 0.
+
+At this point the iterator is ready to use, with the first bootdev and bootmeth
+selected. All the other fields are 0. This means that the current partition is
+0, which is taken to mean the whole device, since partition numbers start at 1.
+It also means that `max_part` is 0, i.e. the maximum partition number we know
+about is 0, meaning that, as far as we know, there is no partition table on this
+bootdev.
+
+With the iterator ready, `bootflow_scan_bootdev()` checks whether the current
+settings produce a valid bootflow. This is handled by `bootflow_check()`, which
+either returns 0 (if it got something) or an error if not (more on that later).
+If the `BOOTFLOWF_ALL` iterator flag is set, even errors are returned as
+incomplete bootflows, but normally an error results in moving onto the next
+iteration.
+
+The `bootflow_scan_next()` function handles moving onto the next iteration and
+checking it. In fact it sits in a loop doing that repeatedly until it finds
+something it wants to return.
+
+The actual 'moving on' part is implemented in `iter_incr()`. This is a very
+simple function. It increments the first counter. If that hits its maximum, it
+sets it to zero and increments the second counter. You can think of all the
+counters together as a number with three digits which increment in order, with
+the least-sigificant digit on the right, counting like this:
+
+   ========    =======    =======
+   bootdev     part       method
+   ========    =======    =======
+   0           0          0
+   0           0          1
+   0           0          2
+   0           1          0
+   0           1          1
+   0           1          1
+   1           0          0
+   1           0          1
+   ========    =======    =======
+
+The maximum value for `method` is `num_methods - 1` so when it exceeds that, it
+goes back to 0 and the next `part` is considered. The maximum value for that is
+`max_part`, which is initially zero for all bootdevs. If we find a partition
+table on that bootdev, `max_part` can be updated during the iteration to a
+higher value - see `bootdev_find_in_blk()` for that, described later. If that
+exceeds its maximum, then the next bootdev is used. In this way, iter_incr()
+works its way through all possibilities, moving forward one each time it is
+called.
+
+There is no expectation that iteration will actually finish. Quite often a
+valid bootflow is found early on. With `bootflow scan -b`, that causes the
+bootflow to be immediately booted. Assuming it is successful, the iteration never
+completes.
+
+Also note that the iterator hold the **current** combination being considered.
+So when `iter_incr()` is called, it increments to the next one and returns it,
+the new **current** combination.
+
+Note also the `err` field in `struct bootflow_iter`. This is normally 0 and has
+thus has no effect on `iter_inc()`. But if it is non-zero, signalling an error,
+it indicates to the iterator what it should do when called. It can force moving
+to the next partition, or bootdev, for example. The special values
+`BF_NO_MORE_PARTS` and `BF_NO_MORE_DEVICES` handle this. When `iter_incr` sees
+`BF_NO_MORE_PARTS` it knows that it should immediately move to the next bootdev.
+When it sees `BF_NO_MORE_DEVICES` it knows that there is nothing more it can do
+so it should immediately return. The caller of `iter_incr()` is responsible for
+updating the `err` field, based on the return value it sees.
+
+The above describes the iteration process at a high level. It is basically a
+very simple increment function with a checker called `bootflow_check()` that
+checks the result of each iteration generated, to determine whether it can
+produce a bootflow.
+
+So what happens inside of `bootflow_check()`? It simply calls the uclass
+method `bootdev_get_bootflow()` to ask the bootdev to return a bootflow. It
+passes the iterator to the bootdev method, so that function knows what we are
+talking about. At first, the bootflow is set up in the state `BOOTFLOWST_BASE`,
+with just the `method` and `dev` intiialised. But the bootdev may fill in more,
+e.g. updating the state, depending on what it finds.
+
+Based on what the bootdev responds with, `bootflow_check()` either
+returns a valid bootflow, or a partial one with an error. A partial bootflow
+is one that has some fields set up, but did not reach the `BOOTFLOWST_READY`
+state. As noted before, if the `BOOTFLOWF_ALL` iterator flag is set, then all
+bootflows are returned, even partial ones. This can help with debugging.
+
+So at this point you can see that total control over whether a bootflow can
+be generated from a particular iteration, or not, rests with the bootdev.
+Each one can adopt its own approach.
+
+Going down a level, what does the bootdev do in its `get_bootflow()` method?
+Let us consider the MMC bootdev. In that case the call to
+`bootdev_get_bootflow()` ends up in `mmc_get_bootflow()`. It locates the parent
+device of the bootdev, i.e. the `UCLASS_MMC` device itself, then finds the block
+device associated with it. It then calls the helper function
+`bootdev_find_in_blk()` to do all the work. This is common with just about any
+bootdev that is based on a media device.
+
+The `bootdev_find_in_blk()` helper is implemented in the bootdev uclass. It
+names the bootflow and copies the partition number in from the iterator. Then it
+calls the bootmeth device to check if it can support this device. This is
+important since some bootmeths only work with network devices, for example. If
+that check fails, it stops.
+
+Assuming the bootmeth is happy, or at least indicates that it is willing to try
+(by returning 0 from its `check()` method), the next step is to try the
+partition. If that works it tries to detect a file system. If that works then it
+calls the bootmeth device once more, this time to read the bootflow.
+
+Note: At present a filesystem is needed for the bootmeth to be called on block
+devices, simply because we don't have any examples where this is not the case.
+This feature can be added as needed.
+
+If we take the example of the `bootmeth_distro` driver, this call ends up at
+`distro_read_bootflow()`. It has the filesystem ready, so tries various
+filenames to try to find the `extlinux.conf` file, reading it if possible. If
+all goes well the bootflow ends up in the `BOOTFLOWST_READY` state.
+
+At this point, we fall back from the bootmeth driver, to
+`bootdev_find_in_blk()`, then back to `mmc_get_bootflow()`, then to
+`bootdev_get_bootflow()`, then to `bootflow_check()` and finally to its caller,
+either `bootflow_scan_bootdev()` or `bootflow_scan_next()`. In either case,
+the bootflow is returned as the result of this iteration, assuming it made it to
+the  `BOOTFLOWST_READY` state.
+
+That is the basic operation of scanning for bootflows. The process of booting a
+bootflow is handled by the bootmeth driver for that bootflow. In the case of
+distro boot, this parses and processes the `extlinux.conf` file that was read.
+See `distro_boot()` for how that works. The processing may involve reading
+additional files, which is handled by the `read_file()` method, which is
+`distro_read_file()` in this case. All bootmethds should support reading files,
+since the bootflow is typically only the basic instructions and does not include
+the operating system itself, ramdisk, device tree, etc.
+
+The vast majority of the bootstd code is concerned with iterating through
+partitions on bootdevs and using bootmethds to find bootflows.
+
+How about bootdevs which are not block devices? They are handled by the same
+methods as above, but with a different implementation. For example, the bootmeth
+for PXE boot (over a network) uses `tftp` to read files rather than `fs_read()`.
+But other than that it is very similar.
+
+
+Tests
+-----
+
+Tests are located in `test/boot` and cover the core functionality as well as
+the commands. All tests use sandbox so can be run on a standard Linux computer
+and in U-Boot's CI.
+
+For testing, a DOS-formatted disk image is used with a single FAT partition on
+it. This is created in `setup_bootflow_image()`, with a canned one from the
+source tree used if it cannot be created (e.g. in CI).
+
+
+Bootflow internals
+------------------
+
+The bootstd device holds a linked list of scanned bootflows as well as the
+currently selected bootdev and bootflow (for use by commands). This is in
+`struct bootstd_priv`.
+
+Each bootdev device has its own `struct bootdev_uc_plat` which holds a
+list of scanned bootflows just for that device.
+
+The bootflow itself is documented in bootflow_h_. It includes various bits of
+information about the bootflow and a buffer to hold the file.
+
+
+Future
+------
+
+Apart from the to-do items below, different types of bootflow files may be
+implemented in future, e.g. Chromium OS support which is currently only
+available as a script in chromebook_coral.
+
+
+To do
+-----
+
+Some things that need to be done to completely replace the distro-boot scripts:
+
+- add bootdev drivers for dhcp, sata, scsi, ide, virtio
+- PXE boot for EFI
+- support for loading U-Boot scripts
+
+Other ideas:
+
+- `bootflow prep` to load everything preparing for boot, so that `bootflow boot`
+  can just do the boot.
+- automatically load kernel, FDT, etc. to suitable addresses so the board does
+  not need to specify things like `pxefile_addr_r`
+
+
+.. _disto_boodcmd: https://github.com/u-boot/u-boot/blob/master/include/config_distro_bootcmd.h
+.. _BootLoaderSpec: http://www.freedesktop.org/wiki/Specifications/BootLoaderSpec/
+.. _distro_boot: https://github.com/u-boot/u-boot/blob/master/boot/distro.c
+.. _bootflow_h: https://github.com/u-boot/u-boot/blob/master/include/bootflow.h
diff --git a/doc/develop/distro.rst b/doc/develop/distro.rst
index 0113a3d4ec4..3ee3dac6a27 100644
--- a/doc/develop/distro.rst
+++ b/doc/develop/distro.rst
@@ -157,6 +157,9 @@ a line with "CONFIG_DISTRO_DEFAULTS=y". If you want to enable this
 from Kconfig itself, for e.g. all boards using a specific SoC then
 add a "imply DISTRO_DEFAULTS" to your SoC CONFIG option.
 
+
+TO BE UPDATED:
+
 In your board configuration file, include the following::
 
     #ifndef CONFIG_SPL_BUILD
diff --git a/doc/develop/index.rst b/doc/develop/index.rst
index 2e6d6c302a5..d32ec490221 100644
--- a/doc/develop/index.rst
+++ b/doc/develop/index.rst
@@ -10,6 +10,7 @@ Implementation
    :maxdepth: 1
 
    bloblist
+   bootstd
    ci_testing
    commands
    config_binding
diff --git a/doc/device-tree-bindings/bootdev.txt b/doc/device-tree-bindings/bootdev.txt
index 95b7fec8212..4bb2345a0b9 100644
--- a/doc/device-tree-bindings/bootdev.txt
+++ b/doc/device-tree-bindings/bootdev.txt
@@ -6,3 +6,21 @@ child of the media device (UCLASS_MMC, UCLASS_SPI_FLASH, etc.)
 
 The bootdev driver is provided by the media devices. The bindings for each
 are described in this file (to come).
+
+Required properties:
+
+compatible:
+   "u-boot,bootdev-eth" - Ethernet bootdev
+   "u-boot,bootdev-mmc" - MMC bootdev
+   "u-boot,bootdev-usb" - USB bootdev
+
+
+Example:
+
+	mmc1 {
+		compatible = "sandbox,mmc";
+
+		mmc-bootdev {
+			compatible = "u-boot,bootdev-eth";
+		};
+	};
diff --git a/doc/device-tree-bindings/bootmeth.txt b/doc/device-tree-bindings/bootmeth.txt
new file mode 100644
index 00000000000..de6396a7b35
--- /dev/null
+++ b/doc/device-tree-bindings/bootmeth.txt
@@ -0,0 +1,31 @@
+U-Boot standard boot methods (bootmeth)
+======================================
+
+This provides methods (called bootmeths) for locating bootflows on a boot
+device (bootdev). These are normally created as children of the bootstd device.
+
+Required properties:
+
+compatible:
+   "u-boot,distro-syslinux" - distro boot from a block device
+   "u-boot,distro-pxe" - distro boot from a network device
+   "u-boot,distro-efi" - EFI boot from an .efi file
+   "u-boot,efi-bootmgr" - EFI boot using boot manager (bootmgr)
+
+
+Example:
+
+	bootstd {
+		compatible = "u-boot,boot-std";
+
+		filename-prefixes = "/", "/boot/";
+		bootdev-order = "mmc2", "mmc1";
+
+		syslinux {
+			compatible = "u-boot,distro-syslinux";
+		};
+
+		efi {
+			compatible = "u-boot,distro-efi";
+		};
+	};
diff --git a/doc/device-tree-bindings/bootstd.txt b/doc/device-tree-bindings/bootstd.txt
index f048b9dd327..8706c5f4993 100644
--- a/doc/device-tree-bindings/bootstd.txt
+++ b/doc/device-tree-bindings/bootstd.txt
@@ -25,4 +25,12 @@ Example:
 
 		filename-prefixes = "/", "/boot/";
 		bootdev-order = "mmc2", "mmc1";
+
+		syslinux {
+			compatible = "u-boot,distro-syslinux";
+		};
+
+		efi {
+			compatible = "u-boot,distro-efi";
+		};
 	};
diff --git a/doc/usage/cmd/bootdev.rst b/doc/usage/cmd/bootdev.rst
new file mode 100644
index 00000000000..5e02e32c514
--- /dev/null
+++ b/doc/usage/cmd/bootdev.rst
@@ -0,0 +1,135 @@
+.. SPDX-License-Identifier: GPL-2.0+:
+
+bootdev command
+===============
+
+Synopis
+-------
+
+::
+
+    bootdev list [-p]      - list all available bootdevs (-p to probe)\n"
+    bootdev select <bm>    - select a bootdev by name\n"
+    bootdev info [-p]      - show information about a bootdev";
+
+Description
+-----------
+
+The `bootdev` command is used to manage bootdevs. It can list available
+bootdevs, select one and obtain information about it.
+
+See :doc:`../../develop/bootstd` for more information about bootdevs in general.
+
+
+bootdev list
+~~~~~~~~~~~~
+
+This lists available bootdevs
+
+Scanning with `-p` causes the bootdevs to be probed. This happens automatically
+when they are used.
+
+The list looks something like this:
+
+===  ======  ======  ========  =========================
+Seq  Probed  Status  Uclass    Name
+===  ======  ======  ========  =========================
+  0   [ + ]      OK  mmc       mmc@7e202000.bootdev
+  1   [   ]      OK  mmc       sdhci@7e300000.bootdev
+  2   [   ]      OK  ethernet  smsc95xx_eth.bootdev
+===  ======  ======  ========  =========================
+
+
+The fields are as follows:
+
+Seq:
+    Sequence number in the scan, used to reference the bootflow later
+
+Probed:
+    Shows a plus (+) if the device is probed, empty if not.
+
+Status:
+    Shows the status of the device. Typically this is `OK` meaning that there is
+    no error. If you use -p and an error occurs when probing, then this shows
+    the error number. You can look up Linux error codes to find the meaning of
+    the number.
+
+Uclass:
+    Name of the media device's Uclass. This indicates the type of the parent
+    device (e.g. MMC, Ethernet).
+
+Name:
+    Name of the bootdev. This is generated from the media device appended
+    with `.bootdev`
+
+
+bootdev select
+~~~~~~~~~~~~~~~~~
+
+Use this to select a particular bootdev. You can select it by the sequence
+number or name, as shown in `bootdev list`.
+
+Once a bootdev is selected, you can use `bootdev info` to look at it or
+`bootflow scan` to scan it.
+
+If no bootdev name or number is provided, then any existing bootdev is
+unselected.
+
+
+bootdev info
+~~~~~~~~~~~~~~~
+
+This shows information on the current bootdev, with the format looking like
+this:
+
+=========  =======================
+Name       mmc@7e202000.bootdev
+Sequence   0
+Status     Probed
+Uclass     mmc
+Bootflows  1 (1 valid)
+=========  =======================
+
+Most of the information is the same as `bootdev list` above. The new fields
+are:
+
+Device
+    Name of the bootdev
+
+Status
+    Shows `Probed` if the device is probed, `OK` if not. If `-p` is used and the
+    device fails to probe, an error code is shown.
+
+Bootflows
+    Indicates the number of bootflows attached to the bootdev. This is 0
+    unless you have used 'bootflow scan' on the bootflow, or on all bootflows.
+
+
+Example
+-------
+
+This example shows listing available bootdev and getting information about
+one of them::
+
+   U-Boot> bootdev list
+   Seq  Probed  Status  Uclass    Name
+   ---  ------  ------  --------  ------------------
+     0   [ + ]      OK  mmc       mmc@7e202000.bootdev
+     1   [   ]      OK  mmc       sdhci@7e300000.bootdev
+     2   [   ]      OK  ethernet  smsc95xx_eth.bootdev
+   ---  ------  ------  --------  ------------------
+   (3 devices)
+   U-Boot> bootdev sel 0
+   U-Boot> bootflow scan
+   U-Boot> bootdev info
+   Name:      mmc@7e202000.bootdev
+   Sequence:  0
+   Status:    Probed
+   Uclass:    mmc
+   Bootflows: 1 (1 valid)
+
+
+Return value
+------------
+
+The return value $? is always 0 (true).
diff --git a/doc/usage/cmd/bootflow.rst b/doc/usage/cmd/bootflow.rst
new file mode 100644
index 00000000000..aa12dc2e3ab
--- /dev/null
+++ b/doc/usage/cmd/bootflow.rst
@@ -0,0 +1,427 @@
+.. SPDX-License-Identifier: GPL-2.0+:
+
+bootflow command
+================
+
+Synopis
+-------
+
+::
+
+    bootflow scan [-abel] [bootdev]
+    bootflow list [-e]
+    bootflow select [<num|name>]
+    bootflow info [-d]
+    bootflow boot
+
+
+Description
+-----------
+
+The `bootflow` command is used to manage bootflows. It can scan bootdevs to
+locate bootflows, list them and boot them.
+
+See :doc:`../../develop/bootstd` for more information.
+
+
+bootflow scan
+~~~~~~~~~~~~~
+
+Scans for available bootflows, optionally booting the first valid one it finds.
+This operates in two modes:
+
+- If no bootdev is selected (see `bootdev select`) it scans bootflows one
+  by one, extracting all the bootdevs from each
+- If a bootdev is selected, it just scans that one bootflow
+
+Flags are:
+
+-a
+    Collect all bootflows, even those that cannot be loaded. Normally if a file
+    is not where it is expected, then the bootflow fails and so is dropped
+    during the scan. With this option you can see why each bootflow would be
+    dropped.
+
+-b
+    Boot each valid bootflow as it is scanned. Typically only the first bootflow
+    matters, since by then the system boots in the OS and U-Boot is no-longer
+    running. `bootflow scan -b` is a quick way to boot the first available OS.
+    A valid bootflow is one that made it all the way to the `loaded` state.
+
+-e
+    Used with -l to also show errors for each bootflow. The shows detailed error
+    information for each bootflow that failed to make it to the `loaded` state.
+
+-l
+    List bootflows while scanning. This is helpful when you want to see what
+    is happening during scanning. Use it with the `-b` flag to see which
+    bootdev and bootflows are being tried.
+
+The optional argument specifies a particular bootdev to scan. This can either be
+the name of a bootdev or its sequence number (both shown with `bootdev list`).
+Alternatively a convenience label can be used, like `mmc0`, which is the type of
+device and an optional sequence number. Specifically, the label is the uclass of
+the bootdev's parent followed by the sequence number of that parent. Sequence
+numbers are typically set by aliases, so if you have 'mmc0' in your devicetree
+alias section, then `mmc0` refers to the bootdev attached to that device.
+
+
+bootflow list
+~~~~~~~~~~~~~
+
+Lists the previously scanned bootflows. You must use `bootflow scan` before this
+to see anything.
+
+If you scanned with -a and have bootflows with errors, -e can be used to show
+those errors.
+
+The list looks something like this:
+
+===  ======  ======  ========  ====  ===============================   ================
+Seq  Method  State   Uclass    Part  Name                              Filename
+===  ======  ======  ========  ====  ===============================   ================
+  0  distro  ready   mmc          2  mmc\@7e202000.bootdev.part_2      /boot/extlinux/extlinux.conf
+  1  pxe     ready   ethernet     0  smsc95xx_eth.bootdev.0            rpi.pxe/extlinux/extlinux.conf
+===  ======  ======  ========  ====  ===============================   ================
+
+The fields are as follows:
+
+Seq:
+    Sequence number in the scan, used to reference the bootflow later
+
+Method:
+    The boot method (bootmeth) used to find the bootflow. Several methods are
+    included in U-Boot.
+
+State:
+    Current state of the bootflow, indicating how far the bootdev got in
+    obtaining a valid one. See :ref:`BootflowStates` for a list of states.
+
+Uclass:
+    Name of the media device's Uclass. This indicates the type of the parent
+    device (e.g. MMC, Ethernet).
+
+Part:
+    Partition number being accesseed, numbered from 1. Normally a device will
+    have a partition table with a small number of partitions. For devices
+    without partition tables (e.g. network) this field is 0.
+
+Name:
+    Name of the bootflow. This is generated from the bootdev appended with
+    the partition information
+
+Filename:
+    Name of the bootflow file. This indicates where the file is on the
+    filesystem or network device.
+
+
+bootflow select
+~~~~~~~~~~~~~~~
+
+Use this to select a particular bootflow. You can select it by the sequence
+number or name, as shown in `bootflow list`.
+
+Once a bootflow is selected, you can use `bootflow info` and `bootflow boot`.
+
+If no bootflow name or number is provided, then any existing bootflow is
+unselected.
+
+
+bootflow info
+~~~~~~~~~~~~~
+
+This shows information on the current bootflow, with the format looking like
+this:
+
+=========  ===============================
+Name       mmc\@7e202000.bootdev.part_2
+Device     mmc\@7e202000.bootdev
+Block dev  mmc\@7e202000.blk
+Type       distro
+Method:    syslinux
+State      ready
+Partition  2
+Subdir     (none)
+Filename   /extlinux/extlinux.conf
+Buffer     3db7ad48
+Size       232 (562 bytes)
+Error      0
+=========  ===============================
+
+Most of the information is the same as `bootflow list` above. The new fields
+are:
+
+Device
+    Name of the bootdev
+
+Block dev
+    Name of the block device, if any. Network devices don't have a block device.
+
+Subdir
+    Subdirectory used for retrieving files. For network bootdevs this is the
+    directory of the 'bootfile' parameter passed from DHCP. All file retrievals
+    when booting are relative to this.
+
+Buffer
+    Buffer containing the bootflow file. You can use the :doc:`md` to look at
+    it, or dump it with `bootflow info -d`.
+
+Size
+    Size of the bootflow file
+
+Error
+    Error number returned from scanning for the bootflow. This is 0 if the
+    bootflow is in the 'loaded' state, or a negative error value on error. You
+    can look up Linux error codes to find the meaning of the number.
+
+Use the `-d` flag to dump out the contents of the bootfile file.
+
+
+bootflow boot
+~~~~~~~~~~~~~
+
+This boots the current bootflow.
+
+
+Example
+-------
+
+Here is an example of scanning for bootflows, then listing them::
+
+    U-Boot> bootflow scan -l
+    Scanning for bootflows in all bootdevs
+    Seq  Type         State   Uclass    Part  Name                      Filename
+    ---  -----------  ------  --------  ----  ------------------------  ----------------
+    Scanning bootdev 'mmc@7e202000.bootdev':
+      0  distro       ready   mmc          2  mmc@7e202000.bootdev.p    /extlinux/extlinux.conf
+    Scanning bootdev 'sdhci@7e300000.bootdev':
+    Card did not respond to voltage select! : -110
+    Scanning bootdev 'smsc95xx_eth.bootdev':
+    Waiting for Ethernet connection... done.
+    BOOTP broadcast 1
+    DHCP client bound to address 192.168.4.30 (4 ms)
+    Using smsc95xx_eth device
+    TFTP from server 192.168.4.1; our IP address is 192.168.4.30
+    Filename 'rpi.pxe/'.
+    Load address: 0x200000
+    Loading: *
+    TFTP error: 'Is a directory' (0)
+    Starting again
+
+    missing environment variable: pxeuuid
+    Retrieving file: rpi.pxe/pxelinux.cfg/01-b8-27-eb-a6-61-e1
+    Waiting for Ethernet connection... done.
+    Using smsc95xx_eth device
+    TFTP from server 192.168.4.1; our IP address is 192.168.4.30
+    Filename 'rpi.pxe/pxelinux.cfg/01-b8-27-eb-a6-61-e1'.
+    Load address: 0x2500000
+    Loading: ##################################################  566 Bytes
+    	 45.9 KiB/s
+    done
+    Bytes transferred = 566 (236 hex)
+      1  distro       ready   ethernet     0  smsc95xx_eth.bootdev.0 rpi.pxe/extlinux/extlinux.conf
+    No more bootdevs
+    ---  -----------  ------  --------  ----  ------------------------  ----------------
+    (2 bootflows, 2 valid)
+    U-Boot> bootflow l
+    Showing all bootflows
+    Seq  Type         State   Uclass    Part  Name                      Filename
+    ---  -----------  ------  --------  ----  ------------------------  ----------------
+      0  distro       ready   mmc          2  mmc@7e202000.bootdev.p    /extlinux/extlinux.conf
+      1  pxe          ready   ethernet     0  smsc95xx_eth.bootdev.0     rpi.pxe/extlinux/extlinux.conf
+    ---  -----------  ------  --------  ----  ------------------------  ----------------
+    (2 bootflows, 2 valid)
+
+
+The second one is then selected by name (we could instead use `bootflow sel 0`),
+displayed and booted::
+
+    U-Boot> bootflow info
+    No bootflow selected
+    U-Boot> bootflow sel mmc@7e202000.bootdev.part_2
+    U-Boot> bootflow info
+    Name:      mmc@7e202000.bootdev.part_2
+    Device:    mmc@7e202000.bootdev
+    Block dev: mmc@7e202000.blk
+    Sequence:  1
+    Method:    distro
+    State:     ready
+    Partition: 2
+    Subdir:    (none)
+    Filename:  extlinux/extlinux.conf
+    Buffer:    3db7ae88
+    Size:      232 (562 bytes)
+    Error:     0
+    U-Boot> bootflow boot
+    ** Booting bootflow 'smsc95xx_eth.bootdev.0'
+    Ignoring unknown command: ui
+    Ignoring malformed menu command:  autoboot
+    Ignoring malformed menu command:  hidden
+    Ignoring unknown command: totaltimeout
+    1:	Fedora-Workstation-armhfp-31-1.9 (5.3.7-301.fc31.armv7hl)
+    Retrieving file: rpi.pxe/initramfs-5.3.7-301.fc31.armv7hl.img
+    get 2700000 rpi.pxe/initramfs-5.3.7-301.fc31.armv7hl.img
+    Waiting for Ethernet connection... done.
+    Using smsc95xx_eth device
+    TFTP from server 192.168.4.1; our IP address is 192.168.4.30
+    Filename 'rpi.pxe/initramfs-5.3.7-301.fc31.armv7hl.img'.
+    Load address: 0x2700000
+    Loading: ###################################T ###############  57.7 MiB
+    	 1.9 MiB/s
+    done
+    Bytes transferred = 60498594 (39b22a2 hex)
+    Retrieving file: rpi.pxe//vmlinuz-5.3.7-301.fc31.armv7hl
+    get 80000 rpi.pxe//vmlinuz-5.3.7-301.fc31.armv7hl
+    Waiting for Ethernet connection... done.
+    Using smsc95xx_eth device
+    TFTP from server 192.168.4.1; our IP address is 192.168.4.30
+    Filename 'rpi.pxe//vmlinuz-5.3.7-301.fc31.armv7hl'.
+    Load address: 0x80000
+    Loading: ##################################################  7.2 MiB
+    	 2.3 MiB/s
+    done
+    Bytes transferred = 7508480 (729200 hex)
+    append: ro root=UUID=9732b35b-4cd5-458b-9b91-80f7047e0b8a rhgb quiet LANG=en_US.UTF-8 cma=192MB cma=256MB
+    Retrieving file: rpi.pxe//dtb-5.3.7-301.fc31.armv7hl/bcm2837-rpi-3-b.dtb
+    get 2600000 rpi.pxe//dtb-5.3.7-301.fc31.armv7hl/bcm2837-rpi-3-b.dtb
+    Waiting for Ethernet connection... done.
+    Using smsc95xx_eth device
+    TFTP from server 192.168.4.1; our IP address is 192.168.4.30
+    Filename 'rpi.pxe//dtb-5.3.7-301.fc31.armv7hl/bcm2837-rpi-3-b.dtb'.
+    Load address: 0x2600000
+    Loading: ##################################################  13.8 KiB
+    	 764.6 KiB/s
+    done
+    Bytes transferred = 14102 (3716 hex)
+    Kernel image @ 0x080000 [ 0x000000 - 0x729200 ]
+    ## Flattened Device Tree blob at 02600000
+       Booting using the fdt blob at 0x2600000
+       Using Device Tree in place at 02600000, end 02606715
+
+    Starting kernel ...
+
+    [  OK  ] Started Show Plymouth Boot Screen.
+    [  OK  ] Started Forward Password R…s to Plymouth Directory Watch.
+    [  OK  ] Reached target Local Encrypted Volumes.
+    [  OK  ] Reached target Paths.
+    ....
+
+
+Here we scan for bootflows and boot the first one found::
+
+    U-Boot> bootflow scan -bl
+    Scanning for bootflows in all bootdevs
+    Seq  Method       State   Uclass    Part  Name                    Filename
+    ---  -----------  ------  --------  ----  ----------------------  ----------------
+    Scanning bootdev 'mmc@7e202000.bootdev':
+      0  distro       ready   mmc          2  mmc@7e202000.bootdev.p  /extlinux/extlinux.conf
+    ** Booting bootflow 'mmc@7e202000.bootdev.part_2'
+    Ignoring unknown command: ui
+    Ignoring malformed menu command:  autoboot
+    Ignoring malformed menu command:  hidden
+    Ignoring unknown command: totaltimeout
+    1:	Fedora-KDE-armhfp-31-1.9 (5.3.7-301.fc31.armv7hl)
+    Retrieving file: /initramfs-5.3.7-301.fc31.armv7hl.img
+    getfile 2700000 /initramfs-5.3.7-301.fc31.armv7hl.img
+    Retrieving file: /vmlinuz-5.3.7-301.fc31.armv7hl
+    getfile 80000 /vmlinuz-5.3.7-301.fc31.armv7hl
+    append: ro root=UUID=b8781f09-e2dd-4cb8-979b-7df5eeaaabea rhgb LANG=en_US.UTF-8 cma=192MB console=tty0 console=ttyS1,115200
+    Retrieving file: /dtb-5.3.7-301.fc31.armv7hl/bcm2837-rpi-3-b.dtb
+    getfile 2600000 /dtb-5.3.7-301.fc31.armv7hl/bcm2837-rpi-3-b.dtb
+    Kernel image @ 0x080000 [ 0x000000 - 0x729200 ]
+    ## Flattened Device Tree blob at 02600000
+       Booting using the fdt blob at 0x2600000
+       Using Device Tree in place at 02600000, end 02606715
+
+    Starting kernel ...
+
+    [    0.000000] Booting Linux on physical CPU 0x0
+
+
+Here is am example using the -e flag to see all errors::
+
+    U-Boot> bootflow scan -a
+    Card did not respond to voltage select! : -110
+    Waiting for Ethernet connection... done.
+    BOOTP broadcast 1
+    DHCP client bound to address 192.168.4.30 (4 ms)
+    Using smsc95xx_eth device
+    TFTP from server 192.168.4.1; our IP address is 192.168.4.30
+    Filename 'rpi.pxe/'.
+    Load address: 0x200000
+    Loading: *
+    TFTP error: 'Is a directory' (0)
+    Starting again
+
+    missing environment variable: pxeuuid
+    Retrieving file: rpi.pxe/pxelinux.cfg/01-b8-27-eb-a6-61-e1
+    Waiting for Ethernet connection... done.
+    Using smsc95xx_eth device
+    TFTP from server 192.168.4.1; our IP address is 192.168.4.30
+    Filename 'rpi.pxe/pxelinux.cfg/01-b8-27-eb-a6-61-e1'.
+    Load address: 0x2500000
+    Loading: ##################################################  566 Bytes
+    	 49.8 KiB/s
+    done
+    Bytes transferred = 566 (236 hex)
+    U-Boot> bootflow l -e
+    Showing all bootflows
+    Seq  Type         State   Uclass    Part  Name                   Filename
+    ---  -----------  ------  --------  ----  ---------------------  ----------------
+      0  distro       fs      mmc          1  mmc@7e202000.bootdev.p /extlinux/extlinux.conf
+         ** File not found, err=-2
+      1  distro       ready   mmc          2  mmc@7e202000.bootdev.p /extlinux/extlinux.conf
+      2  distro       fs      mmc          3  mmc@7e202000.bootdev.p /extlinux/extlinux.conf
+         ** File not found, err=-1
+      3  distro       media   mmc          0  mmc@7e202000.bootdev.p <NULL>
+         ** No partition found, err=-2
+      4  distro       media   mmc          0  mmc@7e202000.bootdev.p <NULL>
+         ** No partition found, err=-2
+      5  distro       media   mmc          0  mmc@7e202000.bootdev.p <NULL>
+         ** No partition found, err=-2
+      6  distro       media   mmc          0  mmc@7e202000.bootdev.p <NULL>
+         ** No partition found, err=-2
+      7  distro       media   mmc          0  mmc@7e202000.bootdev.p <NULL>
+         ** No partition found, err=-2
+      8  distro       media   mmc          0  mmc@7e202000.bootdev.p <NULL>
+         ** No partition found, err=-2
+      9  distro       media   mmc          0  mmc@7e202000.bootdev.p <NULL>
+         ** No partition found, err=-2
+      a  distro       media   mmc          0  mmc@7e202000.bootdev.p <NULL>
+         ** No partition found, err=-2
+      b  distro       media   mmc          0  mmc@7e202000.bootdev.p <NULL>
+         ** No partition found, err=-2
+      c  distro       media   mmc          0  mmc@7e202000.bootdev.p <NULL>
+         ** No partition found, err=-2
+      d  distro       media   mmc          0  mmc@7e202000.bootdev.p <NULL>
+         ** No partition found, err=-2
+      e  distro       media   mmc          0  mmc@7e202000.bootdev.p <NULL>
+         ** No partition found, err=-2
+      f  distro       media   mmc          0  mmc@7e202000.bootdev.p <NULL>
+         ** No partition found, err=-2
+     10  distro       media   mmc          0  mmc@7e202000.bootdev.p <NULL>
+         ** No partition found, err=-2
+     11  distro       media   mmc          0  mmc@7e202000.bootdev.p <NULL>
+         ** No partition found, err=-2
+     12  distro       media   mmc          0  mmc@7e202000.bootdev.p <NULL>
+         ** No partition found, err=-2
+     13  distro       media   mmc          0  mmc@7e202000.bootdev.p <NULL>
+         ** No partition found, err=-2
+     14  distro       ready   ethernet     0  smsc95xx_eth.bootdev.0 rpi.pxe/extlinux/extlinux.conf
+    ---  -----------  ------  --------  ----  ---------------------  ----------------
+    (21 bootflows, 2 valid)
+    U-Boot>
+
+
+Return value
+------------
+
+On success `bootflow boot` normally boots into the Operating System and does not
+return to U-Boot. If something about the U-Boot processing fails, then the
+return value $? is 1. If the boot succeeds but for some reason the Operating
+System returns, then $? is 0, indicating success.
+
+For other subcommands, the return value $? is always 0 (true).
+
+
+.. BootflowStates_:
diff --git a/doc/usage/cmd/bootmeth.rst b/doc/usage/cmd/bootmeth.rst
new file mode 100644
index 00000000000..9fc7ebf0abf
--- /dev/null
+++ b/doc/usage/cmd/bootmeth.rst
@@ -0,0 +1,108 @@
+.. SPDX-License-Identifier: GPL-2.0+:
+
+bootmeth command
+================
+
+Synopis
+-------
+
+::
+
+    bootmeth list [-a]          - list selected bootmeths (-a for all)
+    bootmeth order "[<bm> ...]" - select the order of bootmeths\n"
+
+
+Description
+-----------
+
+The `bootmeth` command is used to manage bootmeths. It can list them and change
+the order in which they are used.
+
+See :doc:`../../develop/bootstd` for more information.
+
+
+.. _bootmeth_order:
+
+bootmeth order
+~~~~~~~~~~~~~~
+
+Selects which bootmeths to use and the order in which they are invoked. When
+scanning bootdevs, each bootmeth is tried in turn to see if it can find a valid
+bootflow. You can use this command to adjust the order or even to omit some
+boomeths.
+
+The argument is a quoted list of bootmeths to use, by name.
+
+
+bootmeth list
+~~~~~~~~~~~~~
+
+This lists the selected bootmeths, or all of them, if the `-a` flag is used.
+The format looks like this:
+
+=====  ===  ==================  =================================
+Order  Seq  Name                Description
+=====  ===  ==================  =================================
+    0    0  distro              Syslinux boot from a block device
+    1    1  efi                 EFI boot from an .efi file
+    2    2  pxe                 PXE boot from a network device
+    3    3  sandbox             Sandbox boot for testing
+    4    4  efi_mgr             EFI bootmgr flow
+=====  ===  ==================  =================================
+
+The fields are as follows:
+
+Order:
+    The order in which these bootmeths are invoked for each bootdev. If this
+    shows as a hyphen, then the bootmeth is not in the current ordering.
+
+Seq:
+    The sequence number of the bootmeth, i.e. the normal ordering if none is set
+
+Name:
+    Name of the bootmeth
+
+Description:
+    A friendly description for the bootmeth
+
+
+Example
+-------
+
+This shows listing bootmeths. All are present and in the normal order::
+
+    => bootmeth list
+    Order  Seq  Name                Description
+    -----  ---  ------------------  ------------------
+        0    0  distro              Syslinux boot from a block device
+        1    1  efi                 EFI boot from an .efi file
+        2    2  pxe                 PXE boot from a network device
+        3    3  sandbox             Sandbox boot for testing
+        4    4  efi_mgr             EFI bootmgr flow
+    -----  ---  ------------------  ------------------
+    (5 bootmeths)
+
+Now the order is changed, to include only two of them::
+
+    => bootmeth order "sandbox distro"
+    => bootmeth list
+    Order  Seq  Name                Description
+    -----  ---  ------------------  ------------------
+        0    3  sandbox             Sandbox boot for testing
+        1    0  distro              Syslinux boot from a block device
+    -----  ---  ------------------  ------------------
+    (2 bootmeths)
+
+The -a flag shows all bootmeths so you can clearly see which ones are used and
+which are not::
+
+    => bootmeth list -a
+    Order  Seq  Name                Description
+    -----  ---  ------------------  ------------------
+        1    0  distro              Syslinux boot from a block device
+        -    1  efi                 EFI boot from an .efi file
+        -    2  pxe                 PXE boot from a network device
+        0    3  sandbox             Sandbox boot for testing
+        -    4  efi_mgr             EFI bootmgr flow
+    -----  ---  ------------------  ------------------
+    (5 bootmeths)
diff --git a/doc/usage/index.rst b/doc/usage/index.rst
index f343e4e05f7..c03f4aef9eb 100644
--- a/doc/usage/index.rst
+++ b/doc/usage/index.rst
@@ -23,9 +23,12 @@ Shell commands
    cmd/addrmap
    cmd/askenv
    cmd/base
+   cmd/bootdev
    cmd/bootefi
+   cmd/bootflow
    cmd/booti
    cmd/bootmenu
+   cmd/bootmeth
    cmd/button
    cmd/cbsysinfo
    cmd/conitrace
-- 
2.36.0.rc2.479.g8af0fa9b8e-goog


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH v5 34/34] bootstd: Provide a default command
  2022-04-25  5:30 [PATCH v5 00/34] Initial implementation of standard boot Simon Glass
                   ` (29 preceding siblings ...)
  2022-04-25  5:31 ` [PATCH v5 33/34] bootstd: doc: Add documentation Simon Glass
@ 2022-04-25  5:31 ` Simon Glass
  2022-04-25 22:50 ` [PATCH v5 00/34] Initial implementation of standard boot Tom Rini
  31 siblings, 0 replies; 36+ messages in thread
From: Simon Glass @ 2022-04-25  5:31 UTC (permalink / raw)
  To: U-Boot Mailing List
  Cc: Dennis Gilmore, Lukas Auer, Daniel Schwierzeck, Tom Rini,
	Michal Simek, Ilias Apalodimas, Steffen Jaeckel, Simon Glass

We would like to use bootstd by default when EFI boot manager is not
enabled. But so far bootstd does not support all the of distro-boot
fetures. So for now, add an option to select this.

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

(no changes since v1)

 boot/Kconfig | 17 ++++++++++++++++-
 1 file changed, 16 insertions(+), 1 deletion(-)

diff --git a/boot/Kconfig b/boot/Kconfig
index 64e16f3beba..4b0802b8c6e 100644
--- a/boot/Kconfig
+++ b/boot/Kconfig
@@ -328,6 +328,19 @@ config BOOTSTD_FULL
 
 if BOOTSTD
 
+config BOOTSTD_BOOTCOMMAND
+	bool "Use bootstd to boot"
+	default y if !DISTRO_DEFAULTS
+	help
+	  Enable this to select a default boot-command suitable for booting
+	  with standard boot. This can be overridden by the board if needed,
+	  but the default command should be enough for most boards which use
+	  standard boot.
+
+	  For now this is only selected if distro boot is NOT used, since
+	  standard boot does not support all of the features of distro boot
+	  yet.
+
 config BOOTMETH_DISTRO
 	bool "Bootdev support for distro boot"
 	depends on CMD_PXE
@@ -1260,7 +1273,9 @@ config USE_BOOTCOMMAND
 config BOOTCOMMAND
 	string "bootcmd value"
 	depends on USE_BOOTCOMMAND && !USE_DEFAULT_ENV_FILE
-	default "run distro_bootcmd" if DISTRO_DEFAULTS
+	default "bootflow scan -lb" if BOOTSTD_BOOTCOMMAND && CMD_BOOTFLOW_FULL
+	default "bootflow scan" if BOOTSTD_BOOTCOMMAND && !CMD_BOOTFLOW_FULL
+	default "run distro_bootcmd" if !BOOTSTD_BOOTCOMMAND && DISTRO_DEFAULTS
 	help
 	  This is the string of commands that will be used as bootcmd and if
 	  AUTOBOOT is set, automatically run.
-- 
2.36.0.rc2.479.g8af0fa9b8e-goog


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* Re: [PATCH v5 00/34] Initial implementation of standard boot
  2022-04-25  5:30 [PATCH v5 00/34] Initial implementation of standard boot Simon Glass
                   ` (30 preceding siblings ...)
  2022-04-25  5:31 ` [PATCH v5 34/34] bootstd: Provide a default command Simon Glass
@ 2022-04-25 22:50 ` Tom Rini
  2022-04-26  4:55   ` Peter Robinson
  31 siblings, 1 reply; 36+ messages in thread
From: Tom Rini @ 2022-04-25 22:50 UTC (permalink / raw)
  To: Simon Glass
  Cc: U-Boot Mailing List, Dennis Gilmore, Lukas Auer,
	Daniel Schwierzeck, Michal Simek, Ilias Apalodimas,
	Steffen Jaeckel, Jaehoon Chung, Marek Vasut, Pavel Herrmann,
	Peng Fan

[-- Attachment #1: Type: text/plain, Size: 1267 bytes --]

On Sun, Apr 24, 2022 at 11:30:53PM -0600, Simon Glass wrote:

> The bootflow feature provide a built-in way for U-Boot to automatically
> boot an Operating System without custom scripting and other customisation.
> This is called 'standard boot' since it provides a standard way for
> U-Boot to boot a distro, without scripting.
> 
> It introduces the following concepts:
> 
>    - bootdev - a device which can hold a distro
>    - 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)
> 
> This series provides an implementation of these, enabled to scan for
> bootflows from MMC, USB and Ethernet. It supports the existing distro
> boot as well as the EFI loader flow (bootefi/bootmgr). It works
> similiarly to the existing script-based approach, but is native to
> U-Boot.

I've put most of this cover letter in the merge commit, and applied this
to u-boot/master.  This is an incremental starting point at providing a
alternative way of constructing and controlling the load and execute OS
stage of booting.  There is some growth on most platforms for this, but
it is a reasonable alternative and will be iterated on.

-- 
Tom

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 659 bytes --]

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH v5 00/34] Initial implementation of standard boot
  2022-04-25 22:50 ` [PATCH v5 00/34] Initial implementation of standard boot Tom Rini
@ 2022-04-26  4:55   ` Peter Robinson
  2022-04-26 12:17     ` Tom Rini
  0 siblings, 1 reply; 36+ messages in thread
From: Peter Robinson @ 2022-04-26  4:55 UTC (permalink / raw)
  To: Tom Rini
  Cc: Simon Glass, U-Boot Mailing List, Dennis Gilmore, Lukas Auer,
	Daniel Schwierzeck, Michal Simek, Ilias Apalodimas,
	Steffen Jaeckel, Jaehoon Chung, Marek Vasut, Pavel Herrmann,
	Peng Fan

> > The bootflow feature provide a built-in way for U-Boot to automatically
> > boot an Operating System without custom scripting and other customisation.
> > This is called 'standard boot' since it provides a standard way for
> > U-Boot to boot a distro, without scripting.
> >
> > It introduces the following concepts:
> >
> >    - bootdev - a device which can hold a distro
> >    - 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)
> >
> > This series provides an implementation of these, enabled to scan for
> > bootflows from MMC, USB and Ethernet. It supports the existing distro
> > boot as well as the EFI loader flow (bootefi/bootmgr). It works
> > similiarly to the existing script-based approach, but is native to
> > U-Boot.
>
> I've put most of this cover letter in the merge commit, and applied this
> to u-boot/master.  This is an incremental starting point at providing a
> alternative way of constructing and controlling the load and execute OS
> stage of booting.  There is some growth on most platforms for this, but
> it is a reasonable alternative and will be iterated on.

I'm guessing this would allow us to optionally disable hush and
associated pieces for a lot of the boards which may equal it out?

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH v5 00/34] Initial implementation of standard boot
  2022-04-26  4:55   ` Peter Robinson
@ 2022-04-26 12:17     ` Tom Rini
  0 siblings, 0 replies; 36+ messages in thread
From: Tom Rini @ 2022-04-26 12:17 UTC (permalink / raw)
  To: Peter Robinson
  Cc: Simon Glass, U-Boot Mailing List, Dennis Gilmore, Lukas Auer,
	Daniel Schwierzeck, Michal Simek, Ilias Apalodimas,
	Steffen Jaeckel, Jaehoon Chung, Marek Vasut, Pavel Herrmann,
	Peng Fan

[-- Attachment #1: Type: text/plain, Size: 1693 bytes --]

On Tue, Apr 26, 2022 at 05:55:36AM +0100, Peter Robinson wrote:
> > > The bootflow feature provide a built-in way for U-Boot to automatically
> > > boot an Operating System without custom scripting and other customisation.
> > > This is called 'standard boot' since it provides a standard way for
> > > U-Boot to boot a distro, without scripting.
> > >
> > > It introduces the following concepts:
> > >
> > >    - bootdev - a device which can hold a distro
> > >    - 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)
> > >
> > > This series provides an implementation of these, enabled to scan for
> > > bootflows from MMC, USB and Ethernet. It supports the existing distro
> > > boot as well as the EFI loader flow (bootefi/bootmgr). It works
> > > similiarly to the existing script-based approach, but is native to
> > > U-Boot.
> >
> > I've put most of this cover letter in the merge commit, and applied this
> > to u-boot/master.  This is an incremental starting point at providing a
> > alternative way of constructing and controlling the load and execute OS
> > stage of booting.  There is some growth on most platforms for this, but
> > it is a reasonable alternative and will be iterated on.
> 
> I'm guessing this would allow us to optionally disable hush and
> associated pieces for a lot of the boards which may equal it out?

Long term, we'll see what makes the most sense for everyone as a
reasonable set of defaults.  I also think that high level, there'll be
places to optimize this for overall size down the line.

-- 
Tom

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 659 bytes --]

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH v5 32/34] bootstd: Add setup for the bootflow tests
  2022-04-25  5:31 ` [PATCH v5 32/34] bootstd: Add setup for the bootflow tests Simon Glass
@ 2022-05-09 18:06   ` Heinrich Schuchardt
  0 siblings, 0 replies; 36+ messages in thread
From: Heinrich Schuchardt @ 2022-05-09 18:06 UTC (permalink / raw)
  To: Simon Glass
  Cc: Dennis Gilmore, Lukas Auer, Daniel Schwierzeck, Tom Rini,
	Michal Simek, Ilias Apalodimas, Steffen Jaeckel,
	U-Boot Mailing List

On 4/25/22 07:31, Simon Glass wrote:
> We need to create a disk image with a partition table and a DOS-format
> filesystem containing a few files. Provide a fallback binary for CI since
> it does not seem able to detect the loopback partitions.
>
> Add this to a dm_init test so that it happens when needed.
>
> Signed-off-by: Simon Glass <sjg@chromium.org>
> ---
>
> (no changes since v3)
>
> Changes in v3:
> - Update test condition to use CMD_BOOTFLOW
>
>   test/py/tests/bootstd/mmc1.img.xz | Bin 0 -> 4448 bytes
>   test/py/tests/test_ut.py          | 103 ++++++++++++++++++++++++++++++
>   2 files changed, 103 insertions(+)
>   create mode 100644 test/py/tests/bootstd/mmc1.img.xz
>
> diff --git a/test/py/tests/bootstd/mmc1.img.xz b/test/py/tests/bootstd/mmc1.img.xz
> new file mode 100644
> index 0000000000000000000000000000000000000000..4e7f39b830eb4bddcb9ff6df39e89f130b5706b9
> GIT binary patch
> literal 4448
> zcmeI0c{JOJ7RP@`Y(-R5?X`}rGNKxz9<j8IUF{-@c9c=1cC|-CYOSqGnW?SD*rkjm
> zwrEML9ZRXDs3;}Y(T7+w@167BKku)3b7tQA$KUsld(Y?I^ZkC$J<$wzbOHd-@<hW;
> zRzR9X2mpYfua`=g%tdzeZ2)+1a%`NFk@nORl#0;_jb!Af2?Uq<OQN^F=hL1^yra5*
> z)ULN=qSItcRC)YMV_~JMi8PdmuvKSER?oUli7w*j@UDfq@L=ZOpyC)?J-1xS$XpK2
> zYZF!p9&ow&?GR4z*q+#}I`^&8k@vQAr-#^4?EB>Y1f(ncsMm4@s?)THBgy{6U{9Xf
> zjhWyTLq>&G;svF#Bje#;bOmkhhE$<HEn&c%2h{26<0EU8pdm?&l)=HQjSfS}ol6@x
> zpzJgMv-Wm(JIu8LqHF45kPhWz@@FypAEFuOhM3ukHzDQ6k#k3RYA}0pTKq@YfHbYw
> zdUq%F7k&Fy&GRZR1s1fW??laBe{OaWLwSpAO-JdpaXPcd3)>q9$<~I^9H$OcZc$TV
> zMm~rDFT{WWjO6gcMYYHc+6;^JG8pkme*14Ce5le4a3Xkbt70^O7K>VGXX_ybx)*eD
> zw6^43jCQ-EnZSx&neK#B$O{UBSSV)XEs1b#mSt8v(^ROEPcQ~<C-O-O`gl%=c`dC@
> zZlJMG>?h~&+LRdcB!|Pg*6V;d|HfzwOc`HZ0F{bOiS(PfoQ$+96!SnBncAog%~~q+
> z4pv?MwLy<7e_muzu9NC3mOJG=EjfgHZ^&EDT#iavdv!R~fYhpQHts|&4o{9py`Y_%
> z31`~6QY-sMx{bA(spJ|Hg0FssAbqKREdQ!nnHCFF(SgD|u*oO@)b?YHOfi`nb2iqE
> zo#dvbuHH>ts%D|qG|pzT<NA5=U}Cbr#w>pV-7MtB>3Vq!+w8Zx#o9YoLQmNagFld#
> zP(41!ib&tauzdKXL3Wdvj^TFM^wfBX8wSVK3`s(RwOWClx9g+fhLgZM^F}%6wv}i0
> z_2T0?&*=|tVg38)KCU;((P$nEiz00YJ12kgQ7#!ImFl3}h1PaL*2_u7xNC-Xp8x}W
> zrZ#;_6cWGawviOU!FpMa4xBc8BKBpF@HY;3jrzikAK)rrz7cK3SIdLk);?Swpj+u!
> zauVI(Z3zMX;2A<am3^4{a9nP@6&l!P$d!7NOA$g52)R<#;v=pvjUNMFv}5~Gpn<XO
> zf&ZFXQ@4{ZzAqDJ4&Hl+Q7Ry)Vx3J)9j{n$D2$b|O9az}FRWcPYku-t()ezlD$;Tr
> z{<MNtCh(+P)vKt-598=eQQ>oFtX#dnXQY97ZpySTjNrJ{%!f$Y`gK%8nEg4geO3mc
> z-OP*|Ch_5AWY9NP%p&~M>ZAJ??-^R7P2+1Hb2rgsO2X$eMJ<7l))#5bjryO*Dqa)`
> z%;XHzXL<#CLI9X5|4{QLx2n8#cjne^EA6#n{fUKE*apICl#Rr5`b4w-6|DH9x(%>u
> zAblDl+7gOW;#{pAugK1-4d((c<m--*W%*!KZ`#Yrg*yS}QFfbytt^U?hi1<(z;~<9
> z>KffC%}1y~xAgK-kIY#om$h-hU6uL%6{?p4=faq|cA$OJ7|(inY7C)NO<foyYD}kK
> zpk%Vbqa~iOxE%Y9biE>BNMaX+?yr2|aOhe9tKnj)<)sFT!^mcwJjoRb=G@*C$wRry
> zPTo*uOsd<i&-G@k9BIb#%Lp+wS5XrE;Ug_Ar5_L-w35%d&}Y=@X=U~b`RqY1eoyIW
> z1?~tZ8PYQ9Aj}u!U;X968@+To%~0N@*zT)7XK@X?#EJH;wuw{nu8u$=o@j5zGKrX&
> zJDO5(48p&Q>FL<_`GKQyEJE$O5|axr@ik&j;SomgcM_v6f@QoLawwxx>yHJePg!~N
> z!c}v~e55d};P9~<!QsvJsRGm4PdupPSil^DD*B8!R^Q3Fk3Ef^!?6)FEql?IuLvyX
> zEkBm|Dr@V$SFvVNmVfc^w|dGcjIoa7;`PGVTo8qCog$n4y0@}P;v)~}Zatb2m>2ji
> z?smHa;#j<|fU46!w;JJ1IC}O?Fiq)m)lfbK=CSvquxAa|fn+FXteGTw`$1R2xqL53
> z+Rq|Rc3KPV#k%(QqK*DO9AuCG9)ulipIS+(&QWE;*777sZ;1-R&S=7_hqYtJ>Yt>x
> zRotR}1x$Vx6<|HkrtGAnCK4`M(3A?t`n@QVbzN5+h#$$$O^Q~|*ii`H7a_Cfn&+NO
> z){dt`y5(&cyBP>LxU_EbL6@I7if1I_d_}d}Uo+;vP2meZmVEF3+S&?&e02uw_ZaxU
> zc6A2q_a>e*VE<)AJOg$H?0@1nXBu{<VSjS)`j13&c42?u!f>3A05H(`mV)`mrvw0O
> h4`jqrLheQZEG;rP9FCigsLUjGdmgd=vj{>v{RVxmraJ%t
>
> literal 0
> HcmV?d00001
>
> diff --git a/test/py/tests/test_ut.py b/test/py/tests/test_ut.py
> index 01c2b3ffa12..35fb393c1ff 100644
> --- a/test/py/tests/test_ut.py
> +++ b/test/py/tests/test_ut.py
> @@ -1,9 +1,102 @@
>   # SPDX-License-Identifier: GPL-2.0
>   # Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
>
> +import gzip
> +import os
>   import os.path
>   import pytest
>
> +import u_boot_utils
> +
> +def mkdir_cond(dirname):
> +    """Create a directory if it doesn't already exist
> +
> +    Args:
> +        dirname: Name of directory to create
> +    """
> +    if not os.path.exists(dirname):
> +        os.mkdir(dirname)
> +
> +def setup_bootflow_image(u_boot_console):
> +    """Create a 20MB disk image with a single FAT partition"""
> +    cons = u_boot_console
> +    fname = os.path.join(cons.config.source_dir, 'mmc1.img')
> +    mnt = os.path.join(cons.config.persistent_data_dir, 'mnt')
> +    mkdir_cond(mnt)
> +
> +    u_boot_utils.run_and_log(cons, 'qemu-img create %s 20M' % fname)
> +    u_boot_utils.run_and_log(cons, 'sudo sfdisk %s' % fname,
> +                             stdin=b'type=c')

We should never request to run a test as sudo.

Please, change this test to use virt-make-fs like we do in all other
tests that create an image.

Best regards

Heinrich

> +
> +    loop = None
> +    mounted = False
> +    complete = False
> +    try:
> +        out = u_boot_utils.run_and_log(cons,
> +                                       'sudo losetup --show -f -P %s' % fname)
> +        loop = out.strip()
> +        fatpart = '%sp1' % loop
> +        u_boot_utils.run_and_log(cons, 'sudo mkfs.vfat %s' % fatpart)
> +        u_boot_utils.run_and_log(
> +            cons, 'sudo mount -o loop %s %s -o uid=%d,gid=%d' %
> +            (fatpart, mnt, os.getuid(), os.getgid()))
> +        mounted = True
> +
> +        vmlinux = 'vmlinuz-5.3.7-301.fc31.armv7hl'
> +        initrd = 'initramfs-5.3.7-301.fc31.armv7hl.img'
> +        dtbdir = 'dtb-5.3.7-301.fc31.armv7hl'
> +        script = '''# extlinux.conf generated by appliance-creator
> +ui menu.c32
> +menu autoboot Welcome to Fedora-Workstation-armhfp-31-1.9. Automatic boot in # second{,s}. Press a key for options.
> +menu title Fedora-Workstation-armhfp-31-1.9 Boot Options.
> +menu hidden
> +timeout 20
> +totaltimeout 600
> +
> +label Fedora-Workstation-armhfp-31-1.9 (5.3.7-301.fc31.armv7hl)
> +        kernel /%s
> +        append ro root=UUID=9732b35b-4cd5-458b-9b91-80f7047e0b8a rhgb quiet LANG=en_US.UTF-8 cma=192MB cma=256MB
> +        fdtdir /%s/
> +        initrd /%s''' % (vmlinux, dtbdir, initrd)
> +        ext = os.path.join(mnt, 'extlinux')
> +        mkdir_cond(ext)
> +
> +        with open(os.path.join(ext, 'extlinux.conf'), 'w') as fd:
> +            print(script, file=fd)
> +
> +        inf = os.path.join(cons.config.persistent_data_dir, 'inf')
> +        with open(inf, 'wb') as fd:
> +            fd.write(gzip.compress(b'vmlinux'))
> +        u_boot_utils.run_and_log(cons, 'mkimage -f auto -d %s %s' %
> +                                 (inf, os.path.join(mnt, vmlinux)))
> +
> +        with open(os.path.join(mnt, initrd), 'w') as fd:
> +            print('initrd', file=fd)
> +
> +        mkdir_cond(os.path.join(mnt, dtbdir))
> +
> +        dtb_file = os.path.join(mnt, '%s/sandbox.dtb' % dtbdir)
> +        u_boot_utils.run_and_log(
> +            cons, 'dtc -o %s' % dtb_file, stdin=b'/dts-v1/; / {};')
> +        complete = True
> +    except ValueError as exc:
> +        print('Falled to create image, failing back to prepared copy: %s',
> +              str(exc))
> +    finally:
> +        if mounted:
> +            u_boot_utils.run_and_log(cons, 'sudo umount %s' % mnt)
> +        if loop:
> +            u_boot_utils.run_and_log(cons, 'sudo losetup -d %s' % loop)
> +
> +    if not complete:
> +        # Use a prepared image since we cannot create one
> +        infname = os.path.join(cons.config.source_dir,
> +                               'test/py/tests/bootstd/mmc1.img.xz')
> +        u_boot_utils.run_and_log(
> +            cons,
> +            ['sh', '-c', 'xz -dc %s >%s' % (infname, fname)])
> +
> +
>   @pytest.mark.buildconfigspec('ut_dm')
>   def test_ut_dm_init(u_boot_console):
>       """Initialize data for ut dm tests."""
> @@ -21,6 +114,16 @@ def test_ut_dm_init(u_boot_console):
>           with open(fn, 'wb') as fh:
>               fh.write(data)
>
> +@pytest.mark.buildconfigspec('cmd_bootflow')
> +def test_ut_dm_init_bootstd(u_boot_console):
> +    """Initialise data for bootflow tests"""
> +
> +    setup_bootflow_image(u_boot_console)
> +
> +    # Restart so that the new mmc1.img is picked up
> +    u_boot_console.restart_uboot()
> +
> +
>   def test_ut(u_boot_console, ut_subtest):
>       """Execute a "ut" subtest.
>


^ permalink raw reply	[flat|nested] 36+ messages in thread

end of thread, other threads:[~2022-05-09 18:06 UTC | newest]

Thread overview: 36+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-04-25  5:30 [PATCH v5 00/34] Initial implementation of standard boot Simon Glass
2022-04-25  5:30 ` [PATCH v5 01/34] lib: Move string tests to the string module Simon Glass
2022-04-25  5:30 ` [PATCH v5 02/34] test: Add tests for trailing_strtol() Simon Glass
2022-04-25  5:30 ` [PATCH v5 03/34] lib: Correct comment formatting to avoid sphinx problems Simon Glass
2022-04-25  5:30 ` [PATCH v5 04/34] lib: Fix a few bugs in trailing_strtoln() Simon Glass
2022-04-25  5:30 ` [PATCH v5 05/34] lib: Add a way to find the postiion of a trailing number Simon Glass
2022-04-25  5:30 ` [PATCH v5 06/34] dm: core: Rename and fix uclass_get_by_name_len() Simon Glass
2022-04-25  5:31 ` [PATCH v5 07/34] dm: core: Allow finding a uclass device by partial name Simon Glass
2022-04-25  5:31 ` [PATCH v5 08/34] test: fastboot: Avoid using mmc1 Simon Glass
2022-04-25  5:31 ` [PATCH v5 09/34] test: dm: Restart USB before assuming it is stopped Simon Glass
2022-04-25  5:31 ` [PATCH v5 10/34] dm: blk: Add a function to return the device type Simon Glass
2022-04-25  5:31 ` [PATCH v5 11/34] fs: Add a function to set the filesystem type Simon Glass
2022-04-25  5:31 ` [PATCH v5 12/34] bootstd: Add the concept of a bootflow Simon Glass
2022-04-25  5:31 ` [PATCH v5 13/34] bootstd: Add the bootstd uclass and core implementation Simon Glass
2022-04-25  5:31 ` [PATCH v5 14/34] bootstd: Add the bootdev uclass Simon Glass
2022-04-25  5:31 ` [PATCH v5 15/34] bootstd: Add the bootmeth uclass and helpers Simon Glass
2022-04-25  5:31 ` [PATCH v5 16/34] bootstd: Add support for bootflows Simon Glass
2022-04-25  5:31 ` [PATCH v5 17/34] bootstd: Add a bootdev command Simon Glass
2022-04-25  5:31 ` [PATCH v5 18/34] bootstd: Add a bootflow command Simon Glass
2022-04-25  5:31 ` [PATCH v5 19/34] bootstd: Add a bootmeth command Simon Glass
2022-04-25  5:31 ` [PATCH v5 21/34] bootstd: mmc: Add a bootdev driver Simon Glass
2022-04-25  5:31 ` [PATCH v5 22/34] bootstd: ethernet: " Simon Glass
2022-04-25  5:31 ` [PATCH v5 23/34] bootstd: Add an implementation of distro PXE boot Simon Glass
2022-04-25  5:31 ` [PATCH v5 24/34] bootstd: Add an implementation of EFI boot Simon Glass
2022-04-25  5:31 ` [PATCH v5 26/34] bootstd: Add an implementation of EFI bootmgr Simon Glass
2022-04-25  5:31 ` [PATCH v5 27/34] bootstd: Add a sandbox bootmeth driver Simon Glass
2022-04-25  5:31 ` [PATCH v5 29/34] bootstd: Add an implementation of script boot Simon Glass
2022-04-25  5:31 ` [PATCH v5 30/34] bootstd: usb: Add a bootdev driver Simon Glass
2022-04-25  5:31 ` [PATCH v5 31/34] bootstd: Add tests for bootstd including all uclasses Simon Glass
2022-04-25  5:31 ` [PATCH v5 32/34] bootstd: Add setup for the bootflow tests Simon Glass
2022-05-09 18:06   ` Heinrich Schuchardt
2022-04-25  5:31 ` [PATCH v5 33/34] bootstd: doc: Add documentation Simon Glass
2022-04-25  5:31 ` [PATCH v5 34/34] bootstd: Provide a default command Simon Glass
2022-04-25 22:50 ` [PATCH v5 00/34] Initial implementation of standard boot Tom Rini
2022-04-26  4:55   ` Peter Robinson
2022-04-26 12:17     ` Tom Rini

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.