All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v5 00/17] enable menu-driven boot device selection
@ 2022-04-28  8:09 Masahisa Kojima
  2022-04-28  8:09 ` [PATCH v5 01/17] lib/charset: add u16_strlcat() function Masahisa Kojima
                   ` (18 more replies)
  0 siblings, 19 replies; 58+ messages in thread
From: Masahisa Kojima @ 2022-04-28  8:09 UTC (permalink / raw)
  To: u-boot
  Cc: Heinrich Schuchardt, Ilias Apalodimas, Simon Glass,
	Takahiro Akashi, Francois Ozog, Mark Kettenis, Masahisa Kojima

This patch series adds the menu-driven boot device selection,
by extending the existing "bootmenu" to include UEFI and distro_boot
related entries, and supports menu-driven UEFI boot variable
maintenance.

This patch series also includes the removable media support
that UEFI specification requires to support.

The menu example is as follows.

  *** U-Boot Boot Menu ***

     bootmenu_00   : Boot 1. kernel
     bootmenu_01   : Boot 2. kernel
     bootmenu_02   : Reset board
     UEFI BOOT0000 : debian
     UEFI BOOT0001 : ubuntu
     UEFI BOOT0002 : mmc0:1
     UEFI BOOT0003 : mmc0:2
     UEFI BOOT0004 : nvme0:1
     UEFI BOOT0005 : nvme0:2
     UEFI BOOT0006 : usb0:2
     UEFI BOOT0007 : usb1:1
     UEFI BOOT0008 : usb1:2
     distro_boot   : usb0
     distro_boot   : scsi0
     distro_boot   : virtio0
     distro_boot   : dhcp

  Press UP/DOWN to move, ENTER to select, ESC/CTRL+C to quit

[How to run on QEMU(arm64)]
1) clone source code
 $ git clone https://git.linaro.org/people/masahisa.kojima/u-boot.git \
-b kojima/bootmenu_v5_upstream_0428 --depth 1

2) prepare U-Boot .config
 $ make qemu_arm64_menuconfig
  then, enable CONFIG_CMD_BOOTMENU and CONFIG_AUTOBOOT_MENU_SHOW

3) run on QEMU(arm64) example
 $ qemu-system-aarch64 -machine virt,gic-version=3 -cpu cortex-a57 -m 4G -nographic \
   -no-acpi -bios ./u-boot.bin -hda xxx.img

[Major Changes]
- rebased to v2022.07-rc1
- there is detailed changelog in each commit

AKASHI Takahiro (2):
  efi_loader: export efi_locate_device_handle()
  efi_loader: bootmgr: add booting from removable media

Masahisa Kojima (15):
  lib/charset: add u16_strlcat() function
  test: unit test for u16_strlcat()
  menu: always show the menu regardless of the number of entry
  menu: menu_get_choice() return -ENOENT if menu item is empty
  bootmenu: flush input buffer before waiting user key input
  bootmenu: update bootmenu_entry structure
  bootmenu: add UEFI boot entry into bootmenu
  bootmenu: add distro boot entry
  bootmenu: add Kconfig option not to enter U-Boot console
  bootmenu: factor out the user input handling
  efi_loader: menu-driven addition of UEFI boot option
  efi_loader: menu-driven deletion of UEFI boot variable
  efi_loader: menu-driven update of UEFI bootorder variable
  bootmenu: add removable media entries
  doc:bootmenu: add UEFI boot and distro boot support description

 cmd/Kconfig                               |   10 +
 cmd/bootmenu.c                            |  807 +++++++++----
 common/menu.c                             |  142 ++-
 doc/usage/cmd/bootmenu.rst                |   78 +-
 include/charset.h                         |   17 +
 include/config_distro_bootcmd.h           |   14 +-
 include/efi_default_filename.h            |   33 +
 include/efi_loader.h                      |   61 +
 include/menu.h                            |   20 +
 lib/charset.c                             |   22 +
 lib/efi_loader/Makefile                   |    1 +
 lib/efi_loader/efi_bootmenu_maintenance.c | 1245 +++++++++++++++++++++
 lib/efi_loader/efi_bootmgr.c              |   50 +-
 lib/efi_loader/efi_boottime.c             |   59 +-
 lib/efi_loader/efi_console.c              |   81 ++
 lib/efi_loader/efi_disk.c                 |   11 +
 lib/efi_loader/efi_file.c                 |   75 +-
 test/unicode_ut.c                         |   50 +
 18 files changed, 2518 insertions(+), 258 deletions(-)
 create mode 100644 include/efi_default_filename.h
 create mode 100644 lib/efi_loader/efi_bootmenu_maintenance.c

-- 
2.17.1


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

* [PATCH v5 01/17] lib/charset: add u16_strlcat() function
  2022-04-28  8:09 [PATCH v5 00/17] enable menu-driven boot device selection Masahisa Kojima
@ 2022-04-28  8:09 ` Masahisa Kojima
  2022-04-29 19:36   ` Heinrich Schuchardt
  2022-04-28  8:09 ` [PATCH v5 02/17] test: unit test for u16_strlcat() Masahisa Kojima
                   ` (17 subsequent siblings)
  18 siblings, 1 reply; 58+ messages in thread
From: Masahisa Kojima @ 2022-04-28  8:09 UTC (permalink / raw)
  To: u-boot
  Cc: Heinrich Schuchardt, Ilias Apalodimas, Simon Glass,
	Takahiro Akashi, Francois Ozog, Mark Kettenis, Masahisa Kojima

Provide u16 string version of strlcat().

Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
---
Changes in v5:
- change 3rd argument from size to count, it indicates
  the maximum u16 string count that dest buffer can have.
  Other u16_strXXX functions in U-Boot use string count, not the
  buffer size. u16_strlcat() should follow.
- update function comment to clealy describe the behavior
- use strlen() instead of strnlen(), to correctly handle
  the case if the count is smaller than or equal to initial dest count

Changes in v4:
- add blank line above the return statement

Changes in v2:
- implement u16_strlcat(with the destination buffer size in argument)
  instead of u16_strcat
 include/charset.h | 17 +++++++++++++++++
 lib/charset.c     | 22 ++++++++++++++++++++++
 2 files changed, 39 insertions(+)

diff --git a/include/charset.h b/include/charset.h
index 38908e08f0..ae356ad9a5 100644
--- a/include/charset.h
+++ b/include/charset.h
@@ -261,6 +261,23 @@ u16 *u16_strcpy(u16 *dest, const u16 *src);
  */
 u16 *u16_strdup(const void *src);
 
+/**
+ * u16_strlcat() - Append a length-limited, %NUL-terminated string to another
+ *
+ * Append the src string to the dest string, overwriting the terminating
+ * null word at the end of dest, and then adds a terminating null word.
+ *
+ * @dest:		destination buffer (must include the trailing 0x0000)
+ * @src:		source buffer (must include the trailing 0x0000)
+ * @count:		maximum number of code points that dest buffer can have,
+ *			including the trailing 0x0000
+ * Return:		total count of the created u16 string
+ *			u16_strlen(src) + min(count, u16_strlen(initial dest)),
+ *			does not include trailing 0x0000.
+ *			If return value >= count, truncation occurred.
+ */
+size_t u16_strlcat(u16 *dest, const u16 *src, size_t size);
+
 /**
  * utf16_to_utf8() - Convert an utf16 string to utf8
  *
diff --git a/lib/charset.c b/lib/charset.c
index de201cf3b9..f295dfc11a 100644
--- a/lib/charset.c
+++ b/lib/charset.c
@@ -416,6 +416,28 @@ u16 *u16_strdup(const void *src)
 	return new;
 }
 
+size_t u16_strlcat(u16 *dest, const u16 *src, size_t count)
+{
+	size_t destlen, srclen, ret;
+
+	destlen = u16_strlen(dest);
+	srclen = u16_strlen(src);
+	ret = min(count, destlen) + srclen; /* does not include trailing 0x0000 */
+
+	if (destlen >= count)
+		return ret;
+
+	dest += destlen;
+	count -= destlen;
+	if (srclen >= count)
+		srclen = count - 1 /* for trailing 0x0000 */;
+
+	memcpy(dest, src, srclen * sizeof(u16));
+	dest[srclen] = u'\0';
+
+	return ret;
+}
+
 /* Convert UTF-16 to UTF-8.  */
 uint8_t *utf16_to_utf8(uint8_t *dest, const uint16_t *src, size_t size)
 {
-- 
2.17.1


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

* [PATCH v5 02/17] test: unit test for u16_strlcat()
  2022-04-28  8:09 [PATCH v5 00/17] enable menu-driven boot device selection Masahisa Kojima
  2022-04-28  8:09 ` [PATCH v5 01/17] lib/charset: add u16_strlcat() function Masahisa Kojima
@ 2022-04-28  8:09 ` Masahisa Kojima
  2022-04-28  8:09 ` [PATCH v5 03/17] menu: always show the menu regardless of the number of entry Masahisa Kojima
                   ` (16 subsequent siblings)
  18 siblings, 0 replies; 58+ messages in thread
From: Masahisa Kojima @ 2022-04-28  8:09 UTC (permalink / raw)
  To: u-boot
  Cc: Heinrich Schuchardt, Ilias Apalodimas, Simon Glass,
	Takahiro Akashi, Francois Ozog, Mark Kettenis, Masahisa Kojima

Provide a unit test for function u16_strlcat().

Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
---
Changes in v5:
- u16_strlcat() 3rd agument update from buffer size
  to maximum string count
- increase buffer size for test
- change u16 string for the test, the letters in the test are different each other
- buffer to be used for the test is filled with non-zero value except for the
  last two bytes
- add test cases

Newly created in v2

 test/unicode_ut.c | 50 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 50 insertions(+)

diff --git a/test/unicode_ut.c b/test/unicode_ut.c
index f2f63d5367..81c9549ba5 100644
--- a/test/unicode_ut.c
+++ b/test/unicode_ut.c
@@ -758,6 +758,56 @@ static int unicode_test_efi_create_indexed_name(struct unit_test_state *uts)
 UNICODE_TEST(unicode_test_efi_create_indexed_name);
 #endif
 
+static int unicode_test_u16_strlcat(struct unit_test_state *uts)
+{
+	u16 buf[40];
+	u16 dest[] = {0x3053, 0x3093, 0x306b, 0x3061, 0x306f, 0}; /* u"こんにちは" */
+	u16 src[] = {0x03B1, 0x2172, 0x6F5C, 0x8247, 0}; /* u"αⅲ潜艇" */
+	u16 concat_str[] = {0x3053, 0x3093, 0x306b, 0x3061, 0x306f,
+			    0x03B1, 0x2172, 0x6F5C, 0x8247, 0};
+	u16 null_src = u'\0';
+	size_t ret, expected;
+	int i;
+
+	/* dest and src are empty string */
+	memset(buf, 0, sizeof(buf));
+	ret = u16_strlcat(buf, &null_src, sizeof(buf));
+	ut_asserteq(0, ret);
+
+	/* dest is empty string */
+	memset(buf, 0, sizeof(buf));
+	ret = u16_strlcat(buf, src, sizeof(buf));
+	ut_asserteq(4, ret);
+	ut_assert(!unicode_test_u16_strcmp(buf, src, 40));
+
+	/* src is empty string */
+	memset(buf, 0xCD, (sizeof(buf) - sizeof(u16)));
+	buf[39] = 0;
+	memcpy(buf, dest, sizeof(dest));
+	ret = u16_strlcat(buf, &null_src, sizeof(buf));
+	ut_asserteq(5, ret);
+	ut_assert(!unicode_test_u16_strcmp(buf, dest, 40));
+
+	for (i = 0; i <= 40; i++) {
+		memset(buf, 0xCD, (sizeof(buf) - sizeof(u16)));
+		buf[39] = 0;
+		memcpy(buf, dest, sizeof(dest));
+		expected = min(i, 5 /* u16_strlen(dest) */) + 4 /* u16_strlen(src) */;
+		ret = u16_strlcat(buf, src, i);
+		ut_asserteq(expected, ret);
+		if (i <= 6) {
+			ut_assert(!unicode_test_u16_strcmp(buf, dest, 40));
+		} else if (i < 10) {
+			ut_assert(!unicode_test_u16_strcmp(buf, concat_str, i - 1));
+		} else {
+			ut_assert(!unicode_test_u16_strcmp(buf, concat_str, 40));
+		}
+	}
+
+	return 0;
+}
+UNICODE_TEST(unicode_test_u16_strlcat);
+
 int do_ut_unicode(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
 {
 	struct unit_test *tests = UNIT_TEST_SUITE_START(unicode_test);
-- 
2.17.1


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

* [PATCH v5 03/17] menu: always show the menu regardless of the number of entry
  2022-04-28  8:09 [PATCH v5 00/17] enable menu-driven boot device selection Masahisa Kojima
  2022-04-28  8:09 ` [PATCH v5 01/17] lib/charset: add u16_strlcat() function Masahisa Kojima
  2022-04-28  8:09 ` [PATCH v5 02/17] test: unit test for u16_strlcat() Masahisa Kojima
@ 2022-04-28  8:09 ` Masahisa Kojima
  2022-05-01 15:37   ` Heinrich Schuchardt
  2022-04-28  8:09 ` [PATCH v5 04/17] menu: menu_get_choice() return -ENOENT if menu item is empty Masahisa Kojima
                   ` (15 subsequent siblings)
  18 siblings, 1 reply; 58+ messages in thread
From: Masahisa Kojima @ 2022-04-28  8:09 UTC (permalink / raw)
  To: u-boot
  Cc: Heinrich Schuchardt, Ilias Apalodimas, Simon Glass,
	Takahiro Akashi, Francois Ozog, Mark Kettenis, Masahisa Kojima

To make user aware of the menu entry selection, menu always
appears regardless of the number of entry.

Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
---
No changes since v4

Newly created in v4

 common/menu.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/common/menu.c b/common/menu.c
index 5fb2ffbd06..b577d80b4f 100644
--- a/common/menu.c
+++ b/common/menu.c
@@ -271,7 +271,7 @@ int menu_get_choice(struct menu *m, void **choice)
 	if (!m || !choice)
 		return -EINVAL;
 
-	if (!m->prompt || m->item_cnt == 1)
+	if (!m->prompt)
 		return menu_default_choice(m, choice);
 
 	return menu_interactive_choice(m, choice);
-- 
2.17.1


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

* [PATCH v5 04/17] menu: menu_get_choice() return -ENOENT if menu item is empty
  2022-04-28  8:09 [PATCH v5 00/17] enable menu-driven boot device selection Masahisa Kojima
                   ` (2 preceding siblings ...)
  2022-04-28  8:09 ` [PATCH v5 03/17] menu: always show the menu regardless of the number of entry Masahisa Kojima
@ 2022-04-28  8:09 ` Masahisa Kojima
  2022-04-29 19:38   ` Heinrich Schuchardt
  2022-04-28  8:09 ` [PATCH v5 05/17] efi_loader: export efi_locate_device_handle() Masahisa Kojima
                   ` (14 subsequent siblings)
  18 siblings, 1 reply; 58+ messages in thread
From: Masahisa Kojima @ 2022-04-28  8:09 UTC (permalink / raw)
  To: u-boot
  Cc: Heinrich Schuchardt, Ilias Apalodimas, Simon Glass,
	Takahiro Akashi, Francois Ozog, Mark Kettenis, Masahisa Kojima

menu_get_choice() needs to handle the case that menu item
is empty. In this case, menu_get_choice() returns -ENOENT.

Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
---
Newly created in v5

 common/menu.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/common/menu.c b/common/menu.c
index b577d80b4f..4118c6dc3c 100644
--- a/common/menu.c
+++ b/common/menu.c
@@ -271,6 +271,9 @@ int menu_get_choice(struct menu *m, void **choice)
 	if (!m || !choice)
 		return -EINVAL;
 
+	if (m->item_cnt == 0)
+		return -ENOENT;
+
 	if (!m->prompt)
 		return menu_default_choice(m, choice);
 
-- 
2.17.1


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

* [PATCH v5 05/17] efi_loader: export efi_locate_device_handle()
  2022-04-28  8:09 [PATCH v5 00/17] enable menu-driven boot device selection Masahisa Kojima
                   ` (3 preceding siblings ...)
  2022-04-28  8:09 ` [PATCH v5 04/17] menu: menu_get_choice() return -ENOENT if menu item is empty Masahisa Kojima
@ 2022-04-28  8:09 ` Masahisa Kojima
  2022-05-01 18:53   ` Heinrich Schuchardt
  2022-04-28  8:09 ` [PATCH v5 06/17] efi_loader: bootmgr: add booting from removable media Masahisa Kojima
                   ` (13 subsequent siblings)
  18 siblings, 1 reply; 58+ messages in thread
From: Masahisa Kojima @ 2022-04-28  8:09 UTC (permalink / raw)
  To: u-boot
  Cc: Heinrich Schuchardt, Ilias Apalodimas, Simon Glass,
	Takahiro Akashi, Francois Ozog, Mark Kettenis

From: AKASHI Takahiro <takahiro.akashi@linaro.org>

This function will be used in the next commit where some behavior
of EFI boot manager will be expanded.

Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
Reviewed-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
---
No changes from original version

 include/efi_loader.h          | 4 ++++
 lib/efi_loader/efi_boottime.c | 7 +++----
 2 files changed, 7 insertions(+), 4 deletions(-)

diff --git a/include/efi_loader.h b/include/efi_loader.h
index ba79a9afb4..effb43369d 100644
--- a/include/efi_loader.h
+++ b/include/efi_loader.h
@@ -595,6 +595,10 @@ efi_status_t efi_create_handle(efi_handle_t *handle);
 void efi_delete_handle(efi_handle_t obj);
 /* Call this to validate a handle and find the EFI object for it */
 struct efi_object *efi_search_obj(const efi_handle_t handle);
+/* Locate device_path handle */
+efi_status_t EFIAPI efi_locate_device_path(const efi_guid_t *protocol,
+					   struct efi_device_path **device_path,
+					   efi_handle_t *device);
 /* Load image */
 efi_status_t EFIAPI efi_load_image(bool boot_policy,
 				   efi_handle_t parent_image,
diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c
index 5bcb8253ed..4da64b5d29 100644
--- a/lib/efi_loader/efi_boottime.c
+++ b/lib/efi_loader/efi_boottime.c
@@ -1799,10 +1799,9 @@ failure:
  *
  * Return: status code
  */
-static efi_status_t EFIAPI efi_locate_device_path(
-			const efi_guid_t *protocol,
-			struct efi_device_path **device_path,
-			efi_handle_t *device)
+efi_status_t EFIAPI efi_locate_device_path(const efi_guid_t *protocol,
+					   struct efi_device_path **device_path,
+					   efi_handle_t *device)
 {
 	struct efi_device_path *dp;
 	size_t i;
-- 
2.17.1


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

* [PATCH v5 06/17] efi_loader: bootmgr: add booting from removable media
  2022-04-28  8:09 [PATCH v5 00/17] enable menu-driven boot device selection Masahisa Kojima
                   ` (4 preceding siblings ...)
  2022-04-28  8:09 ` [PATCH v5 05/17] efi_loader: export efi_locate_device_handle() Masahisa Kojima
@ 2022-04-28  8:09 ` Masahisa Kojima
  2022-04-29 17:03   ` Heinrich Schuchardt
  2022-04-28  8:09 ` [PATCH v5 07/17] bootmenu: flush input buffer before waiting user key input Masahisa Kojima
                   ` (12 subsequent siblings)
  18 siblings, 1 reply; 58+ messages in thread
From: Masahisa Kojima @ 2022-04-28  8:09 UTC (permalink / raw)
  To: u-boot
  Cc: Heinrich Schuchardt, Ilias Apalodimas, Simon Glass,
	Takahiro Akashi, Francois Ozog, Mark Kettenis, Masahisa Kojima,
	Mark Kettenis, Peter Hoyes, Neil Armstrong, Andre Przywara

From: AKASHI Takahiro <takahiro.akashi@linaro.org>

Under the current implementation, booting from removable media using
a architecture-specific default image name, say BOOTAA64.EFI, is
supported only in distro_bootcmd script. See the commit 74522c898b35
("efi_loader: Add distro boot script for removable media").

This is, however, half-baked implementation because
1) UEFI specification requires this feature to be implemented as part
   of Boot Manager's responsibility:

  3 - Boot Manager
  3.5.1 Boot via the Simple File Protocol
  When booting via the EFI_SIMPLE_FILE_SYSTEM_PROTOCOL, the FilePath will
  start with a device path that points to the device that implements the
  EFI_SIMPLE_FILE_SYSTEM_PROTOCOL or the EFI_BLOCK_IO_PROTOCOL. The next
  part of the FilePath may point to the file name, including
  subdirectories, which contain the bootable image. If the file name is
  a null device path, the file name must be generated from the rules
  defined below.
  ...
  3.5.1.1 Removable Media Boot Behavior
  To generate a file name when none is present in the FilePath, the
  firmware must append a default file name in the form
  \EFI\BOOT\BOOT{machine type short-name}.EFI ...

2) So (1) entails the hehavior that the user's preference of boot media
   order should be determined by Boot#### and BootOrder variables.

With this patch, the semantics mentioned above is fully implemented.
For example, if you want to boot the system from USB and SCSI in this
order,
* define Boot0001 which contains only a device path to the USB device
  (without any file path/name)
* define Boot0002 which contains only a device path to the SCSI device,
and
* set BootOrder to Boot0001:Boot0002

To avoid build error for sandbox, default file name "BOOTSANDBOX.efi"
is defined even if it is out of scope of UEFI specification.

Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
---
Changes in v5:
- add default file name definition for SANDBOX to avoid build error

Changes from original version:
- create new include file "efi_default_filename.h" to
  avoid conflict with config_distro_bootcmd.h
- modify the target pointer of efi_free_pool(), expand_media_path() should
  only free the pointer allocated by efi_dp_from_file() function.
 include/config_distro_bootcmd.h | 14 +--------
 include/efi_default_filename.h  | 33 ++++++++++++++++++++++
 lib/efi_loader/efi_bootmgr.c    | 50 ++++++++++++++++++++++++++++++++-
 3 files changed, 83 insertions(+), 14 deletions(-)
 create mode 100644 include/efi_default_filename.h

diff --git a/include/config_distro_bootcmd.h b/include/config_distro_bootcmd.h
index c55023889c..6a3110f27b 100644
--- a/include/config_distro_bootcmd.h
+++ b/include/config_distro_bootcmd.h
@@ -91,19 +91,7 @@
 #endif
 
 #ifdef CONFIG_EFI_LOADER
-#if defined(CONFIG_ARM64)
-#define BOOTEFI_NAME "bootaa64.efi"
-#elif defined(CONFIG_ARM)
-#define BOOTEFI_NAME "bootarm.efi"
-#elif defined(CONFIG_X86_RUN_32BIT)
-#define BOOTEFI_NAME "bootia32.efi"
-#elif defined(CONFIG_X86_RUN_64BIT)
-#define BOOTEFI_NAME "bootx64.efi"
-#elif defined(CONFIG_ARCH_RV32I)
-#define BOOTEFI_NAME "bootriscv32.efi"
-#elif defined(CONFIG_ARCH_RV64I)
-#define BOOTEFI_NAME "bootriscv64.efi"
-#endif
+#include <efi_default_filename.h>
 #endif
 
 #ifdef BOOTEFI_NAME
diff --git a/include/efi_default_filename.h b/include/efi_default_filename.h
new file mode 100644
index 0000000000..cb2ef9e131
--- /dev/null
+++ b/include/efi_default_filename.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Default boot file name when none is present in the FilePath.
+ * This is defined in the UEFI specification.
+ *
+ * Copyright (c) 2022, Linaro Limited
+ */
+#ifndef _EFI_DEFAULT_FILENAME_H
+#define _EFI_DEFAULT_FILENAME_H
+
+#if defined(CONFIG_ARM64)
+#define BOOTEFI_NAME "BOOTAA64.EFI"
+#elif defined(CONFIG_ARM)
+#define BOOTEFI_NAME "BOOTARM.EFI"
+#elif defined(CONFIG_X86_64)
+#define BOOTEFI_NAME "BOOTX64.EFI"
+#elif defined(CONFIG_X86)
+#define BOOTEFI_NAME "BOOTIA32.EFI"
+#elif defined(CONFIG_ARCH_RV32I)
+#define BOOTEFI_NAME "BOOTRISCV32.EFI"
+#elif defined(CONFIG_ARCH_RV64I)
+#define BOOTEFI_NAME "BOOTRISCV64.EFI"
+#elif defined(CONFIG_SANDBOX)
+/*
+ * SANDBOX is not defined in UEFI specification, but
+ * this definition avoids build failure for SANDBOX.
+ */
+#define BOOTEFI_NAME "BOOTSANDBOX.EFI"
+#else
+#error Unsupported UEFI architecture
+#endif
+
+#endif
diff --git a/lib/efi_loader/efi_bootmgr.c b/lib/efi_loader/efi_bootmgr.c
index 8c04ecbdc8..22a4302aac 100644
--- a/lib/efi_loader/efi_bootmgr.c
+++ b/lib/efi_loader/efi_bootmgr.c
@@ -11,6 +11,7 @@
 #include <charset.h>
 #include <log.h>
 #include <malloc.h>
+#include <efi_default_filename.h>
 #include <efi_loader.h>
 #include <efi_variable.h>
 #include <asm/unaligned.h>
@@ -30,6 +31,50 @@ static const struct efi_runtime_services *rs;
  * should do normal or recovery boot.
  */
 
+/**
+ * expand_media_path() - expand a device path for default file name
+ * @device_path:	device path to check against
+ *
+ * If @device_path is a media or disk partition which houses a file
+ * system, this function returns a full device path which contains
+ * an architecture-specific default file name for removable media.
+ *
+ * Return:	a newly allocated device path
+ */
+static
+struct efi_device_path *expand_media_path(struct efi_device_path *device_path)
+{
+	struct efi_device_path *dp, *full_path;
+	efi_handle_t handle;
+	efi_status_t ret;
+
+	if (!device_path)
+		return NULL;
+
+	/*
+	 * If device_path is a (removable) media or partition which provides
+	 * simple file system protocol, append a default file name to support
+	 * booting from removable media.
+	 */
+	dp = device_path;
+	ret = efi_locate_device_path(&efi_simple_file_system_protocol_guid,
+				     &dp, &handle);
+	if (ret == EFI_SUCCESS) {
+		if (dp->type == DEVICE_PATH_TYPE_END) {
+			dp = efi_dp_from_file(NULL, 0,
+					      "/EFI/BOOT/" BOOTEFI_NAME);
+			full_path = efi_dp_append(device_path, dp);
+			efi_free_pool(dp);
+		} else {
+			full_path = efi_dp_dup(device_path);
+		}
+	} else {
+		full_path = efi_dp_dup(device_path);
+	}
+
+	return full_path;
+}
+
 /**
  * try_load_entry() - try to load image for boot option
  *
@@ -68,13 +113,16 @@ static efi_status_t try_load_entry(u16 n, efi_handle_t *handle,
 	}
 
 	if (lo.attributes & LOAD_OPTION_ACTIVE) {
+		struct efi_device_path *file_path;
 		u32 attributes;
 
 		log_debug("%s: trying to load \"%ls\" from %pD\n",
 			  __func__, lo.label, lo.file_path);
 
-		ret = EFI_CALL(efi_load_image(true, efi_root, lo.file_path,
+		file_path = expand_media_path(lo.file_path);
+		ret = EFI_CALL(efi_load_image(true, efi_root, file_path,
 					      NULL, 0, handle));
+		efi_free_pool(file_path);
 		if (ret != EFI_SUCCESS) {
 			log_warning("Loading %ls '%ls' failed\n",
 				    varname, lo.label);
-- 
2.17.1


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

* [PATCH v5 07/17] bootmenu: flush input buffer before waiting user key input
  2022-04-28  8:09 [PATCH v5 00/17] enable menu-driven boot device selection Masahisa Kojima
                   ` (5 preceding siblings ...)
  2022-04-28  8:09 ` [PATCH v5 06/17] efi_loader: bootmgr: add booting from removable media Masahisa Kojima
@ 2022-04-28  8:09 ` Masahisa Kojima
  2022-04-29 19:46   ` Heinrich Schuchardt
  2022-04-28  8:09 ` [PATCH v5 08/17] bootmenu: update bootmenu_entry structure Masahisa Kojima
                   ` (11 subsequent siblings)
  18 siblings, 1 reply; 58+ messages in thread
From: Masahisa Kojima @ 2022-04-28  8:09 UTC (permalink / raw)
  To: u-boot
  Cc: Heinrich Schuchardt, Ilias Apalodimas, Simon Glass,
	Takahiro Akashi, Francois Ozog, Mark Kettenis, Masahisa Kojima

Before waiting user key input to stop autoboot, the input buffer
must be flushed.

Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
---
Change in v5:
- newly created, split into separate patch

 cmd/bootmenu.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/cmd/bootmenu.c b/cmd/bootmenu.c
index d573487272..9a32a18b19 100644
--- a/cmd/bootmenu.c
+++ b/cmd/bootmenu.c
@@ -87,6 +87,10 @@ static void bootmenu_autoboot_loop(struct bootmenu_data *menu,
 	int i, c;
 
 	if (menu->delay > 0) {
+		/* flush input */
+		while (tstc())
+			getchar();
+
 		printf(ANSI_CURSOR_POSITION, menu->count + 5, 1);
 		printf("  Hit any key to stop autoboot: %2d ", menu->delay);
 	}
-- 
2.17.1


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

* [PATCH v5 08/17] bootmenu: update bootmenu_entry structure
  2022-04-28  8:09 [PATCH v5 00/17] enable menu-driven boot device selection Masahisa Kojima
                   ` (6 preceding siblings ...)
  2022-04-28  8:09 ` [PATCH v5 07/17] bootmenu: flush input buffer before waiting user key input Masahisa Kojima
@ 2022-04-28  8:09 ` Masahisa Kojima
  2022-04-29 19:51   ` Heinrich Schuchardt
  2022-04-28  8:09 ` [PATCH v5 09/17] bootmenu: add UEFI boot entry into bootmenu Masahisa Kojima
                   ` (10 subsequent siblings)
  18 siblings, 1 reply; 58+ messages in thread
From: Masahisa Kojima @ 2022-04-28  8:09 UTC (permalink / raw)
  To: u-boot
  Cc: Heinrich Schuchardt, Ilias Apalodimas, Simon Glass,
	Takahiro Akashi, Francois Ozog, Mark Kettenis, Masahisa Kojima

This is a preparation for succeeding addition of uefi boot
and distro boot menu entries into bootmenu.
The bootmenu_entry title is updated to u16 string because
uefi use u16 string. This commit also factors out the function
to prepare the entries generated by "bootmenu_x" U-Boot environment
variable.

This commit also updates the bootmenu entry title.
The entry derived from "bootmenu_x" U-Boot environment variable
has the "bootmenu_xx" prefix as below.

  *** U-Boot Boot Menu ***

     bootmenu_00   : Boot 1. kernel
     bootmenu_01   : Boot 2. kernel
     bootmenu_02   : Reset board

Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
---
Changes in v5:
- split into the separate patch
- add function description comment

 cmd/bootmenu.c | 110 +++++++++++++++++++++++++++++++++++--------------
 1 file changed, 78 insertions(+), 32 deletions(-)

diff --git a/cmd/bootmenu.c b/cmd/bootmenu.c
index 9a32a18b19..15ad621c9f 100644
--- a/cmd/bootmenu.c
+++ b/cmd/bootmenu.c
@@ -3,6 +3,7 @@
  * (C) Copyright 2011-2013 Pali Rohár <pali@kernel.org>
  */
 
+#include <charset.h>
 #include <common.h>
 #include <command.h>
 #include <ansi.h>
@@ -24,11 +25,18 @@
  */
 #define MAX_ENV_SIZE	(9 + 2 + 1)
 
+enum boot_type {
+	BOOTMENU_TYPE_NONE = 0,
+	BOOTMENU_TYPE_BOOTMENU,
+};
+
 struct bootmenu_entry {
 	unsigned short int num;		/* unique number 0 .. MAX_COUNT */
 	char key[3];			/* key identifier of number */
-	char *title;			/* title of entry */
+	u16 *title;			/* title of entry */
 	char *command;			/* hush command of entry */
+	enum boot_type type;		/* boot type of entry */
+	u16 bootorder;			/* order for each boot type */
 	struct bootmenu_data *menu;	/* this bootmenu */
 	struct bootmenu_entry *next;	/* next menu entry (num+1) */
 };
@@ -75,7 +83,10 @@ static void bootmenu_print_entry(void *data)
 	if (reverse)
 		puts(ANSI_COLOR_REVERSE);
 
-	puts(entry->title);
+	if (entry->type == BOOTMENU_TYPE_BOOTMENU)
+		printf("bootmenu_%02d   : %ls", entry->bootorder, entry->title);
+	else
+		printf("%ls", entry->title);
 
 	if (reverse)
 		puts(ANSI_COLOR_RESET);
@@ -279,31 +290,32 @@ static void bootmenu_destroy(struct bootmenu_data *menu)
 	free(menu);
 }
 
-static struct bootmenu_data *bootmenu_create(int delay)
+/**
+ * prepare_bootmenu_entry() - generate the bootmenu_xx entries
+ *
+ * This function read the "bootmenu_x" U-Boot environment variable
+ * and generate the bootmenu entries.
+ *
+ * @menu:	pointer to the bootmenu structure
+ * @current:	pointer to the last bootmenu entry list
+ * @index:	pointer to the index of the last bootmenu entry,
+ *		the number of bootmenu entry is added by this function
+ * Return:	1 on success, negative value on error
+ */
+static int prepare_bootmenu_entry(struct bootmenu_data *menu,
+				  struct bootmenu_entry **current,
+				  unsigned short int *index)
 {
-	unsigned short int i = 0;
-	const char *option;
-	struct bootmenu_data *menu;
-	struct bootmenu_entry *iter = NULL;
-
 	int len;
 	char *sep;
-	char *default_str;
-	struct bootmenu_entry *entry;
-
-	menu = malloc(sizeof(struct bootmenu_data));
-	if (!menu)
-		return NULL;
-
-	menu->delay = delay;
-	menu->active = 0;
-	menu->first = NULL;
-
-	default_str = env_get("bootmenu_default");
-	if (default_str)
-		menu->active = (int)simple_strtol(default_str, NULL, 10);
+	const char *option;
+	unsigned short int i = *index;
+	struct bootmenu_entry *entry = NULL;
+	struct bootmenu_entry *iter = *current;
 
 	while ((option = bootmenu_getoption(i))) {
+		u16 *buf;
+
 		sep = strchr(option, '=');
 		if (!sep) {
 			printf("Invalid bootmenu entry: %s\n", option);
@@ -312,23 +324,23 @@ static struct bootmenu_data *bootmenu_create(int delay)
 
 		entry = malloc(sizeof(struct bootmenu_entry));
 		if (!entry)
-			goto cleanup;
+			return -ENOMEM;
 
 		len = sep-option;
-		entry->title = malloc(len + 1);
+		buf = calloc(1, (len + 1) * sizeof(u16));
+		entry->title = buf;
 		if (!entry->title) {
 			free(entry);
-			goto cleanup;
+			return -ENOMEM;
 		}
-		memcpy(entry->title, option, len);
-		entry->title[len] = 0;
+		utf8_utf16_strncpy(&buf, option, len);
 
 		len = strlen(sep + 1);
 		entry->command = malloc(len + 1);
 		if (!entry->command) {
 			free(entry->title);
 			free(entry);
-			goto cleanup;
+			return -ENOMEM;
 		}
 		memcpy(entry->command, sep + 1, len);
 		entry->command[len] = 0;
@@ -337,6 +349,8 @@ static struct bootmenu_data *bootmenu_create(int delay)
 
 		entry->num = i;
 		entry->menu = menu;
+		entry->type = BOOTMENU_TYPE_BOOTMENU;
+		entry->bootorder = i;
 		entry->next = NULL;
 
 		if (!iter)
@@ -351,13 +365,44 @@ static struct bootmenu_data *bootmenu_create(int delay)
 			break;
 	}
 
+	*index = i;
+	*current = iter;
+
+	return 1;
+}
+
+static struct bootmenu_data *bootmenu_create(int delay)
+{
+	int ret;
+	unsigned short int i = 0;
+	struct bootmenu_data *menu;
+	struct bootmenu_entry *iter = NULL;
+	struct bootmenu_entry *entry;
+	char *default_str;
+
+	menu = malloc(sizeof(struct bootmenu_data));
+	if (!menu)
+		return NULL;
+
+	menu->delay = delay;
+	menu->active = 0;
+	menu->first = NULL;
+
+	default_str = env_get("bootmenu_default");
+	if (default_str)
+		menu->active = (int)simple_strtol(default_str, NULL, 10);
+
+	ret = prepare_bootmenu_entry(menu, &iter, &i);
+	if (ret < 0)
+		goto cleanup;
+
 	/* Add U-Boot console entry at the end */
 	if (i <= MAX_COUNT - 1) {
 		entry = malloc(sizeof(struct bootmenu_entry));
 		if (!entry)
 			goto cleanup;
 
-		entry->title = strdup("U-Boot console");
+		entry->title = u16_strdup(u"U-Boot console");
 		if (!entry->title) {
 			free(entry);
 			goto cleanup;
@@ -374,6 +419,7 @@ static struct bootmenu_data *bootmenu_create(int delay)
 
 		entry->num = i;
 		entry->menu = menu;
+		entry->type = BOOTMENU_TYPE_NONE;
 		entry->next = NULL;
 
 		if (!iter)
@@ -431,7 +477,7 @@ static void bootmenu_show(int delay)
 {
 	int init = 0;
 	void *choice = NULL;
-	char *title = NULL;
+	u16 *title = NULL;
 	char *command = NULL;
 	struct menu *menu;
 	struct bootmenu_data *bootmenu;
@@ -482,7 +528,7 @@ static void bootmenu_show(int delay)
 
 	if (menu_get_choice(menu, &choice) == 1) {
 		iter = choice;
-		title = strdup(iter->title);
+		title = u16_strdup(iter->title);
 		command = strdup(iter->command);
 	}
 
@@ -497,7 +543,7 @@ cleanup:
 	}
 
 	if (title && command) {
-		debug("Starting entry '%s'\n", title);
+		debug("Starting entry '%ls'\n", title);
 		free(title);
 		run_command(command, 0);
 		free(command);
-- 
2.17.1


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

* [PATCH v5 09/17] bootmenu: add UEFI boot entry into bootmenu
  2022-04-28  8:09 [PATCH v5 00/17] enable menu-driven boot device selection Masahisa Kojima
                   ` (7 preceding siblings ...)
  2022-04-28  8:09 ` [PATCH v5 08/17] bootmenu: update bootmenu_entry structure Masahisa Kojima
@ 2022-04-28  8:09 ` Masahisa Kojima
  2022-05-01 21:44   ` Heinrich Schuchardt
  2022-04-28  8:09 ` [PATCH v5 10/17] bootmenu: add distro boot entry Masahisa Kojima
                   ` (9 subsequent siblings)
  18 siblings, 1 reply; 58+ messages in thread
From: Masahisa Kojima @ 2022-04-28  8:09 UTC (permalink / raw)
  To: u-boot
  Cc: Heinrich Schuchardt, Ilias Apalodimas, Simon Glass,
	Takahiro Akashi, Francois Ozog, Mark Kettenis, Masahisa Kojima

This commit adds the UEFI related menu entries
into the bootmenu.

User can select which UEFI "Boot####" option to execute
from bootmenu, then bootmenu sets the "BootNext" UEFI
variable and invoke efi bootmgr. The efi bootmgr
will handle the "BootNext" UEFI variable.

If the "BootNext" UEFI variable is preset and efi bootmgr is enabled,
bootmenu invokes efi bootmgr to handle "BootNext" as first priority.

The UEFI boot entry has the "UEFI BOOTXXXX" prefix as below.

  *** U-Boot Boot Menu ***

     UEFI BOOT0000 : debian
     UEFI BOOT0001 : ubuntu

Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
---
Changes in v5:
- split into the separate patch
- add function description comment
- remove non-volatile attribute for BootNext variable to minimize
  the access to the non-volatile storage

 cmd/bootmenu.c | 155 ++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 154 insertions(+), 1 deletion(-)

diff --git a/cmd/bootmenu.c b/cmd/bootmenu.c
index 15ad621c9f..da688e6213 100644
--- a/cmd/bootmenu.c
+++ b/cmd/bootmenu.c
@@ -7,6 +7,8 @@
 #include <common.h>
 #include <command.h>
 #include <ansi.h>
+#include <efi_loader.h>
+#include <efi_variable.h>
 #include <env.h>
 #include <log.h>
 #include <menu.h>
@@ -28,6 +30,7 @@
 enum boot_type {
 	BOOTMENU_TYPE_NONE = 0,
 	BOOTMENU_TYPE_BOOTMENU,
+	BOOTMENU_TYPE_UEFI_BOOT_OPTION,
 };
 
 struct bootmenu_entry {
@@ -85,6 +88,8 @@ static void bootmenu_print_entry(void *data)
 
 	if (entry->type == BOOTMENU_TYPE_BOOTMENU)
 		printf("bootmenu_%02d   : %ls", entry->bootorder, entry->title);
+	else if (entry->type == BOOTMENU_TYPE_UEFI_BOOT_OPTION)
+		printf("UEFI BOOT%04X : %ls", entry->bootorder, entry->title);
 	else
 		printf("%ls", entry->title);
 
@@ -371,6 +376,95 @@ static int prepare_bootmenu_entry(struct bootmenu_data *menu,
 	return 1;
 }
 
+/**
+ * prepare_uefi_bootorder_entry() - generate the uefi bootmenu entries
+ *
+ * This function read the "BootOrder" UEFI variable
+ * and generate the bootmenu entries in the order of "BootOrder".
+ *
+ * @menu:	pointer to the bootmenu structure
+ * @current:	pointer to the last bootmenu entry list
+ * @index:	pointer to the index of the last bootmenu entry,
+ *		the number of uefi entry is added by this function
+ * Return:	1 on success, negative value on error
+ */
+static int prepare_uefi_bootorder_entry(struct bootmenu_data *menu,
+					struct bootmenu_entry **current,
+					unsigned short int *index)
+{
+	u16 *bootorder;
+	efi_status_t ret;
+	unsigned short j;
+	efi_uintn_t num, size;
+	void *load_option;
+	struct efi_load_option lo;
+	u16 varname[] = u"Boot####";
+	unsigned short int i = *index;
+	struct bootmenu_entry *entry = NULL;
+	struct bootmenu_entry *iter = *current;
+
+	bootorder = efi_get_var(u"BootOrder", &efi_global_variable_guid, &size);
+	if (!bootorder)
+		return -ENOENT;
+
+	num = size / sizeof(u16);
+	for (j = 0; j < num; j++) {
+		entry = malloc(sizeof(struct bootmenu_entry));
+		if (!entry)
+			return -ENOMEM;
+
+		efi_create_indexed_name(varname, sizeof(varname),
+					"Boot", bootorder[j]);
+		load_option = efi_get_var(varname, &efi_global_variable_guid, &size);
+		if (!load_option)
+			continue;
+
+		ret = efi_deserialize_load_option(&lo, load_option, &size);
+		if (ret != EFI_SUCCESS) {
+			log_warning("Invalid load option for %ls\n", varname);
+			free(load_option);
+			free(entry);
+			continue;
+		}
+
+		if (lo.attributes & LOAD_OPTION_ACTIVE) {
+			entry->title = u16_strdup(lo.label);
+			if (!entry->title) {
+				free(load_option);
+				free(entry);
+				free(bootorder);
+				return -ENOMEM;
+			}
+			entry->command = strdup("bootefi bootmgr");
+			sprintf(entry->key, "%d", i);
+			entry->num = i;
+			entry->menu = menu;
+			entry->type = BOOTMENU_TYPE_UEFI_BOOT_OPTION;
+			entry->bootorder = bootorder[j];
+			entry->next = NULL;
+
+			if (!iter)
+				menu->first = entry;
+			else
+				iter->next = entry;
+
+			iter = entry;
+			i++;
+		}
+
+		free(load_option);
+
+		if (i == MAX_COUNT - 1)
+			break;
+	}
+
+	free(bootorder);
+	*index = i;
+	*current = iter;
+
+	return 1;
+}
+
 static struct bootmenu_data *bootmenu_create(int delay)
 {
 	int ret;
@@ -396,6 +490,14 @@ static struct bootmenu_data *bootmenu_create(int delay)
 	if (ret < 0)
 		goto cleanup;
 
+	if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR)) {
+		if (i < MAX_COUNT - 1) {
+			ret = prepare_uefi_bootorder_entry(menu, &iter, &i);
+			if (ret < 0 && ret != -ENOENT)
+				goto cleanup;
+		}
+	}
+
 	/* Add U-Boot console entry at the end */
 	if (i <= MAX_COUNT - 1) {
 		entry = malloc(sizeof(struct bootmenu_entry));
@@ -473,6 +575,31 @@ static void menu_display_statusline(struct menu *m)
 	puts(ANSI_CLEAR_LINE);
 }
 
+static void handle_uefi_bootnext(void)
+{
+	u16 bootnext;
+	efi_status_t ret;
+	efi_uintn_t size;
+
+	/* Initialize EFI drivers */
+	ret = efi_init_obj_list();
+	if (ret != EFI_SUCCESS) {
+		log_err("Error: Cannot initialize UEFI sub-system, r = %lu\n",
+			ret & ~EFI_ERROR_MASK);
+
+		return;
+	}
+
+	/* If UEFI BootNext variable is set, boot the BootNext load option */
+	size = sizeof(u16);
+	ret = efi_get_variable_int(u"BootNext",
+				   &efi_global_variable_guid,
+				   NULL, &size, &bootnext, NULL);
+	if (ret == EFI_SUCCESS)
+		/* BootNext does exist here, try to boot */
+		run_command("bootefi bootmgr", 0);
+}
+
 static void bootmenu_show(int delay)
 {
 	int init = 0;
@@ -482,8 +609,12 @@ static void bootmenu_show(int delay)
 	struct menu *menu;
 	struct bootmenu_data *bootmenu;
 	struct bootmenu_entry *iter;
+	efi_status_t efi_ret = EFI_SUCCESS;
 	char *option, *sep;
 
+	if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR))
+		handle_uefi_bootnext();
+
 	/* If delay is 0 do not create menu, just run first entry */
 	if (delay == 0) {
 		option = bootmenu_getoption(0);
@@ -532,6 +663,27 @@ static void bootmenu_show(int delay)
 		command = strdup(iter->command);
 	}
 
+	/*
+	 * If the selected entry is UEFI BOOT####, set the BootNext variable.
+	 * Then uefi bootmgr is invoked by the preset command in iter->command.
+	 */
+	if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR)) {
+		if (iter->type == BOOTMENU_TYPE_UEFI_BOOT_OPTION) {
+			/*
+			 * UEFI specification requires BootNext variable needs non-volatile
+			 * attribute, but this BootNext is only used inside of U-Boot and
+			 * removed by efi bootmgr once BootNext is processed.
+			 * So this BootNext can be volatile.
+			 */
+			efi_ret = efi_set_variable_int(u"BootNext", &efi_global_variable_guid,
+						       EFI_VARIABLE_BOOTSERVICE_ACCESS |
+						       EFI_VARIABLE_RUNTIME_ACCESS,
+						       sizeof(u16), &iter->bootorder, false);
+			if (efi_ret != EFI_SUCCESS)
+				goto cleanup;
+		}
+	}
+
 cleanup:
 	menu_destroy(menu);
 	bootmenu_destroy(bootmenu);
@@ -545,7 +697,8 @@ cleanup:
 	if (title && command) {
 		debug("Starting entry '%ls'\n", title);
 		free(title);
-		run_command(command, 0);
+		if (efi_ret == EFI_SUCCESS)
+			run_command(command, 0);
 		free(command);
 	}
 
-- 
2.17.1


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

* [PATCH v5 10/17] bootmenu: add distro boot entry
  2022-04-28  8:09 [PATCH v5 00/17] enable menu-driven boot device selection Masahisa Kojima
                   ` (8 preceding siblings ...)
  2022-04-28  8:09 ` [PATCH v5 09/17] bootmenu: add UEFI boot entry into bootmenu Masahisa Kojima
@ 2022-04-28  8:09 ` Masahisa Kojima
  2022-05-01 21:48   ` Heinrich Schuchardt
  2022-04-28  8:09 ` [PATCH v5 11/17] bootmenu: add Kconfig option not to enter U-Boot console Masahisa Kojima
                   ` (8 subsequent siblings)
  18 siblings, 1 reply; 58+ messages in thread
From: Masahisa Kojima @ 2022-04-28  8:09 UTC (permalink / raw)
  To: u-boot
  Cc: Heinrich Schuchardt, Ilias Apalodimas, Simon Glass,
	Takahiro Akashi, Francois Ozog, Mark Kettenis, Masahisa Kojima

This commit adds the distro_boot entries into the bootmenu.
The bootmenu read the "boot_targets" U-Boot environment variable
and enumerate it.
User can select the distro boot entry, then bootmenu executes
"run bootcmd_xxx" command.

The bootmenu also checks the existing block devices and network
option("dhcp" and "pxe") availability, then filter out
the "boot_targets" appeared in bootmenu.

The bootmenu example is as follows, distro boot entry has the
"distro_boot" prefix.

  *** U-Boot Boot Menu ***

     distro_boot   : usb0
     distro_boot   : scsi0
     distro_boot   : virtio0
     distro_boot   : dhcp

Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
---
Changes in v5:
- split into the separate patch
- add function description comment
- handle the case boot_targets variable is empty
- filter out the non-exist device entry

 cmd/bootmenu.c | 177 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 177 insertions(+)

diff --git a/cmd/bootmenu.c b/cmd/bootmenu.c
index da688e6213..afe42b8041 100644
--- a/cmd/bootmenu.c
+++ b/cmd/bootmenu.c
@@ -7,6 +7,7 @@
 #include <common.h>
 #include <command.h>
 #include <ansi.h>
+#include <dm.h>
 #include <efi_loader.h>
 #include <efi_variable.h>
 #include <env.h>
@@ -14,6 +15,7 @@
 #include <menu.h>
 #include <watchdog.h>
 #include <malloc.h>
+#include <linux/ctype.h>
 #include <linux/delay.h>
 #include <linux/string.h>
 
@@ -31,6 +33,7 @@ enum boot_type {
 	BOOTMENU_TYPE_NONE = 0,
 	BOOTMENU_TYPE_BOOTMENU,
 	BOOTMENU_TYPE_UEFI_BOOT_OPTION,
+	BOOTMENU_TYPE_DISTRO_BOOT,
 };
 
 struct bootmenu_entry {
@@ -90,6 +93,8 @@ static void bootmenu_print_entry(void *data)
 		printf("bootmenu_%02d   : %ls", entry->bootorder, entry->title);
 	else if (entry->type == BOOTMENU_TYPE_UEFI_BOOT_OPTION)
 		printf("UEFI BOOT%04X : %ls", entry->bootorder, entry->title);
+	else if (entry->type == BOOTMENU_TYPE_DISTRO_BOOT)
+		printf("distro_boot   : %ls", entry->title);
 	else
 		printf("%ls", entry->title);
 
@@ -465,6 +470,172 @@ static int prepare_uefi_bootorder_entry(struct bootmenu_data *menu,
 	return 1;
 }
 
+static int is_blk_device_available(char *token)
+{
+	struct driver *d = ll_entry_start(struct driver, driver);
+	const int n_ents = ll_entry_count(struct driver, driver);
+	struct driver *entry;
+	struct udevice *udev;
+	struct uclass *uc;
+	struct blk_desc *desc;
+	int ret, i;
+	const char *if_type_name;
+
+	ret = uclass_get(UCLASS_BLK, &uc);
+	if (ret)
+		return -ENODEV;
+
+	for (entry = d; entry < d + n_ents; entry++) {
+		if (entry->id != UCLASS_BLK)
+			continue;
+		i = 0;
+		uclass_foreach_dev(udev, uc) {
+			if (udev->driver != entry)
+				continue;
+			desc = dev_get_uclass_plat(udev);
+			if_type_name = blk_get_if_type_name(desc->if_type);
+			if (strncmp(token, if_type_name, strlen(if_type_name)) == 0) {
+				char *p;
+				int j, len;
+				int devnum = 0;
+
+				p = token + strlen(if_type_name);
+				len = strlen(p);
+				if (!len)
+					continue; /* no device number */
+
+				for (j = 0; j < len; j++) {
+					if (!isdigit(*p)) {
+						/* invalid device number */
+						devnum = INT_MAX;
+						break;
+					}
+					devnum = (devnum * 10) + (*p++ - '0');
+				}
+
+				if (devnum == INT_MAX)
+					continue;
+
+				if (devnum == desc->devnum)
+					return 1;
+			}
+		}
+	}
+
+	if (strncmp(token, "dhcp", strlen("dhcp")) == 0) {
+		if (IS_ENABLED(CONFIG_CMD_DHCP))
+			return 1;
+	}
+
+	if (strncmp(token, "pxe", strlen("pxe")) == 0) {
+		if (IS_ENABLED(CONFIG_CMD_PXE))
+			return 1;
+	}
+
+	return -ENODEV;
+}
+
+/**
+ * prepare_distro_boot_entry() - generate the distro boot entries
+ *
+ * This function read the "boot_targets" U-Boot environment variable
+ * and generate the bootmenu entries.
+ *
+ * @menu:	pointer to the bootmenu structure
+ * @current:	pointer to the last bootmenu entry list
+ * @index:	pointer to the index of the last bootmenu entry,
+ *		the number of uefi entry is added by this function
+ * Return:	1 on success, negative value on error
+ */
+static int prepare_distro_boot_entry(struct bootmenu_data *menu,
+				     struct bootmenu_entry **current,
+				     unsigned short int *index)
+{
+	char *p;
+	int len;
+	char *token;
+	char *boot_targets;
+	unsigned short int i = *index;
+	struct bootmenu_entry *entry = NULL;
+	struct bootmenu_entry *iter = *current;
+
+	/* list the distro boot "boot_targets" */
+	boot_targets = env_get("boot_targets");
+	if (!boot_targets)
+		return -ENOENT;
+
+	len = strlen(boot_targets);
+	if (!len)
+		return -ENOENT;
+
+	p = calloc(1, len + 1);
+	strlcpy(p, boot_targets, len);
+
+	token = strtok(p, " ");
+
+	do {
+		u16 *buf;
+		char *command;
+		int command_size;
+
+		if (is_blk_device_available(token) != 1) {
+			token = strtok(NULL, " ");
+			continue;
+		}
+
+		entry = malloc(sizeof(struct bootmenu_entry));
+		if (!entry) {
+			free(p);
+			return -ENOMEM;
+		}
+
+		len = strlen(token);
+		buf = calloc(1, (len + 1) * sizeof(u16));
+		entry->title = buf;
+		if (!entry->title) {
+			free(entry);
+			free(p);
+			return -ENOMEM;
+		}
+		utf8_utf16_strncpy(&buf, token, len);
+		sprintf(entry->key, "%d", i);
+		entry->num = i;
+		entry->menu = menu;
+
+		command_size = sizeof("run bootcmd_") + len;
+		command = calloc(1, command_size);
+		if (!command) {
+			free(entry->title);
+			free(entry);
+			free(p);
+			return -ENOMEM;
+		}
+		snprintf(command, command_size, "run bootcmd_%s", token);
+		entry->command = command;
+		entry->type = BOOTMENU_TYPE_DISTRO_BOOT;
+		entry->next = NULL;
+
+		if (!iter)
+			menu->first = entry;
+		else
+			iter->next = entry;
+
+		iter = entry;
+		i++;
+
+		if (i == MAX_COUNT - 1)
+			break;
+
+		token = strtok(NULL, " ");
+	} while (token);
+
+	free(p);
+	*index = i;
+	*current = iter;
+
+	return 1;
+}
+
 static struct bootmenu_data *bootmenu_create(int delay)
 {
 	int ret;
@@ -498,6 +669,12 @@ static struct bootmenu_data *bootmenu_create(int delay)
 		}
 	}
 
+	if (i < MAX_COUNT - 1) {
+		ret = prepare_distro_boot_entry(menu, &iter, &i);
+		if (ret < 0 && ret != -ENOENT)
+			goto cleanup;
+	}
+
 	/* Add U-Boot console entry at the end */
 	if (i <= MAX_COUNT - 1) {
 		entry = malloc(sizeof(struct bootmenu_entry));
-- 
2.17.1


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

* [PATCH v5 11/17] bootmenu: add Kconfig option not to enter U-Boot console
  2022-04-28  8:09 [PATCH v5 00/17] enable menu-driven boot device selection Masahisa Kojima
                   ` (9 preceding siblings ...)
  2022-04-28  8:09 ` [PATCH v5 10/17] bootmenu: add distro boot entry Masahisa Kojima
@ 2022-04-28  8:09 ` Masahisa Kojima
  2022-04-29  8:50   ` Mark Kettenis
  2022-04-28  8:09 ` [PATCH v5 12/17] bootmenu: factor out the user input handling Masahisa Kojima
                   ` (7 subsequent siblings)
  18 siblings, 1 reply; 58+ messages in thread
From: Masahisa Kojima @ 2022-04-28  8:09 UTC (permalink / raw)
  To: u-boot
  Cc: Heinrich Schuchardt, Ilias Apalodimas, Simon Glass,
	Takahiro Akashi, Francois Ozog, Mark Kettenis, Masahisa Kojima,
	Michal Simek, Kory Maincent, Ovidiu Panait, Ashok Reddy Soma

This commit adds the Kconfig option to disable to enter
the U-Boot console from bootmenu.

If CMD_BOOTMENU_ENTER_UBOOT_CONSOLE is enabled, "U-Boot console"
entry is appeared as the last entry in the bootmenu, then user can
enter U-Boot console.

If CMD_BOOTMENU_ENTER_UBOOT_CONSOLE is disabled, "Quit" entry
is appeared as the last entry instead of "U-Boot console".
When user chooses "Quit" from bootmenu, the following default
commands are invoked.

 - "bootefi bootmgr" (if efi bootmgr is enabled)
 - "run bootcmd"

If the both commands are executed and returns to the bootmenu,
the bootmenu will appears again.

Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
---
Changes in v5:
- split into the separate patch
- clear the console when user select "U-Boot console"
- if the console is disabled, the last entry title is "Quit"

 cmd/Kconfig    | 10 ++++++++
 cmd/bootmenu.c | 69 ++++++++++++++++++++++++++++++++++++++++++--------
 2 files changed, 68 insertions(+), 11 deletions(-)

diff --git a/cmd/Kconfig b/cmd/Kconfig
index 2b575a2b42..99a1435467 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -356,6 +356,16 @@ config CMD_BOOTMENU
 	help
 	  Add an ANSI terminal boot menu command.
 
+config CMD_BOOTMENU_ENTER_UBOOT_CONSOLE
+	bool "Allow Bootmenu to enter the U-Boot console"
+	depends on CMD_BOOTMENU
+	default n
+	help
+	  Add an entry to enter U-Boot console in bootmenu.
+	  If this option is disabled, user can not enter
+	  the U-Boot console from bootmenu. It increases
+	  the system security.
+
 config CMD_ADTIMG
 	bool "adtimg"
 	help
diff --git a/cmd/bootmenu.c b/cmd/bootmenu.c
index afe42b8041..bfbb1b5248 100644
--- a/cmd/bootmenu.c
+++ b/cmd/bootmenu.c
@@ -29,6 +29,13 @@
  */
 #define MAX_ENV_SIZE	(9 + 2 + 1)
 
+enum bootmenu_ret {
+	BOOTMENU_RET_SUCCESS = 0,
+	BOOTMENU_RET_FAIL,
+	BOOTMENU_RET_QUIT,
+	BOOTMENU_RET_UPDATED,
+};
+
 enum boot_type {
 	BOOTMENU_TYPE_NONE = 0,
 	BOOTMENU_TYPE_BOOTMENU,
@@ -681,7 +688,12 @@ static struct bootmenu_data *bootmenu_create(int delay)
 		if (!entry)
 			goto cleanup;
 
-		entry->title = u16_strdup(u"U-Boot console");
+		/* Add Quit entry if entering U-Boot console is disabled */
+		if (IS_ENABLED(CONFIG_CMD_BOOTMENU_ENTER_UBOOT_CONSOLE))
+			entry->title = u16_strdup(u"U-Boot console");
+		else
+			entry->title = u16_strdup(u"Quit");
+
 		if (!entry->title) {
 			free(entry);
 			goto cleanup;
@@ -777,15 +789,17 @@ static void handle_uefi_bootnext(void)
 		run_command("bootefi bootmgr", 0);
 }
 
-static void bootmenu_show(int delay)
+static enum bootmenu_ret bootmenu_show(int delay)
 {
+	int cmd_ret;
 	int init = 0;
 	void *choice = NULL;
 	u16 *title = NULL;
 	char *command = NULL;
 	struct menu *menu;
-	struct bootmenu_data *bootmenu;
 	struct bootmenu_entry *iter;
+	int ret = BOOTMENU_RET_SUCCESS;
+	struct bootmenu_data *bootmenu;
 	efi_status_t efi_ret = EFI_SUCCESS;
 	char *option, *sep;
 
@@ -797,27 +811,27 @@ static void bootmenu_show(int delay)
 		option = bootmenu_getoption(0);
 		if (!option) {
 			puts("bootmenu option 0 was not found\n");
-			return;
+			return BOOTMENU_RET_FAIL;
 		}
 		sep = strchr(option, '=');
 		if (!sep) {
 			puts("bootmenu option 0 is invalid\n");
-			return;
+			return BOOTMENU_RET_FAIL;
 		}
-		run_command(sep+1, 0);
-		return;
+		cmd_ret = run_command(sep + 1, 0);
+		return (cmd_ret == CMD_RET_SUCCESS ? BOOTMENU_RET_SUCCESS : BOOTMENU_RET_FAIL);
 	}
 
 	bootmenu = bootmenu_create(delay);
 	if (!bootmenu)
-		return;
+		return BOOTMENU_RET_FAIL;
 
 	menu = menu_create(NULL, bootmenu->delay, 1, menu_display_statusline,
 			   bootmenu_print_entry, bootmenu_choice_entry,
 			   bootmenu);
 	if (!menu) {
 		bootmenu_destroy(bootmenu);
-		return;
+		return BOOTMENU_RET_FAIL;
 	}
 
 	for (iter = bootmenu->first; iter; iter = iter->next) {
@@ -838,6 +852,14 @@ static void bootmenu_show(int delay)
 		iter = choice;
 		title = u16_strdup(iter->title);
 		command = strdup(iter->command);
+
+		/* last entry is U-Boot console or Quit */
+		if (iter->num == iter->menu->count - 1) {
+			ret = BOOTMENU_RET_QUIT;
+			goto cleanup;
+		}
+	} else {
+		goto cleanup;
 	}
 
 	/*
@@ -875,19 +897,44 @@ cleanup:
 		debug("Starting entry '%ls'\n", title);
 		free(title);
 		if (efi_ret == EFI_SUCCESS)
-			run_command(command, 0);
+			cmd_ret = run_command(command, 0);
 		free(command);
 	}
 
 #ifdef CONFIG_POSTBOOTMENU
 	run_command(CONFIG_POSTBOOTMENU, 0);
 #endif
+
+	if (efi_ret != EFI_SUCCESS || cmd_ret != CMD_RET_SUCCESS)
+		ret = BOOTMENU_RET_FAIL;
+
+	return ret;
 }
 
 #ifdef CONFIG_AUTOBOOT_MENU_SHOW
 int menu_show(int bootdelay)
 {
-	bootmenu_show(bootdelay);
+	int ret;
+
+	while (1) {
+		ret = bootmenu_show(bootdelay);
+		bootdelay = -1;
+		if (ret == BOOTMENU_RET_UPDATED)
+			continue;
+
+		if (!IS_ENABLED(CONFIG_CMD_BOOTMENU_ENTER_UBOOT_CONSOLE)) {
+			if (ret == BOOTMENU_RET_QUIT) {
+				/* default boot process */
+				if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR))
+					run_command("bootefi bootmgr", 0);
+
+				run_command("run bootcmd", 0);
+			}
+		} else {
+			break;
+		}
+	}
+
 	return -1; /* -1 - abort boot and run monitor code */
 }
 #endif
-- 
2.17.1


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

* [PATCH v5 12/17] bootmenu: factor out the user input handling
  2022-04-28  8:09 [PATCH v5 00/17] enable menu-driven boot device selection Masahisa Kojima
                   ` (10 preceding siblings ...)
  2022-04-28  8:09 ` [PATCH v5 11/17] bootmenu: add Kconfig option not to enter U-Boot console Masahisa Kojima
@ 2022-04-28  8:09 ` Masahisa Kojima
  2022-04-28  8:09 ` [PATCH v5 13/17] efi_loader: menu-driven addition of UEFI boot option Masahisa Kojima
                   ` (6 subsequent siblings)
  18 siblings, 0 replies; 58+ messages in thread
From: Masahisa Kojima @ 2022-04-28  8:09 UTC (permalink / raw)
  To: u-boot
  Cc: Heinrich Schuchardt, Ilias Apalodimas, Simon Glass,
	Takahiro Akashi, Francois Ozog, Mark Kettenis, Masahisa Kojima

This commit moves the user input handling from cmd/bootmenu.c
to common/menu.c to reuse it from other modules.

Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
---
No change since v4

Newly created in v4.

 cmd/bootmenu.c | 149 -------------------------------------------------
 common/menu.c  | 137 +++++++++++++++++++++++++++++++++++++++++++++
 include/menu.h |  20 +++++++
 3 files changed, 157 insertions(+), 149 deletions(-)

diff --git a/cmd/bootmenu.c b/cmd/bootmenu.c
index bfbb1b5248..eb23afdd41 100644
--- a/cmd/bootmenu.c
+++ b/cmd/bootmenu.c
@@ -54,21 +54,6 @@ struct bootmenu_entry {
 	struct bootmenu_entry *next;	/* next menu entry (num+1) */
 };
 
-struct bootmenu_data {
-	int delay;			/* delay for autoboot */
-	int active;			/* active menu entry */
-	int count;			/* total count of menu entries */
-	struct bootmenu_entry *first;	/* first menu entry */
-};
-
-enum bootmenu_key {
-	KEY_NONE = 0,
-	KEY_UP,
-	KEY_DOWN,
-	KEY_SELECT,
-	KEY_QUIT,
-};
-
 static char *bootmenu_getoption(unsigned short int n)
 {
 	char name[MAX_ENV_SIZE];
@@ -109,140 +94,6 @@ static void bootmenu_print_entry(void *data)
 		puts(ANSI_COLOR_RESET);
 }
 
-static void bootmenu_autoboot_loop(struct bootmenu_data *menu,
-				enum bootmenu_key *key, int *esc)
-{
-	int i, c;
-
-	if (menu->delay > 0) {
-		/* flush input */
-		while (tstc())
-			getchar();
-
-		printf(ANSI_CURSOR_POSITION, menu->count + 5, 1);
-		printf("  Hit any key to stop autoboot: %2d ", menu->delay);
-	}
-
-	while (menu->delay > 0) {
-		for (i = 0; i < 100; ++i) {
-			if (!tstc()) {
-				WATCHDOG_RESET();
-				mdelay(10);
-				continue;
-			}
-
-			menu->delay = -1;
-			c = getchar();
-
-			switch (c) {
-			case '\e':
-				*esc = 1;
-				*key = KEY_NONE;
-				break;
-			case '\r':
-				*key = KEY_SELECT;
-				break;
-			case 0x3: /* ^C */
-				*key = KEY_QUIT;
-				break;
-			default:
-				*key = KEY_NONE;
-				break;
-			}
-
-			break;
-		}
-
-		if (menu->delay < 0)
-			break;
-
-		--menu->delay;
-		printf("\b\b\b%2d ", menu->delay);
-	}
-
-	printf(ANSI_CURSOR_POSITION, menu->count + 5, 1);
-	puts(ANSI_CLEAR_LINE);
-
-	if (menu->delay == 0)
-		*key = KEY_SELECT;
-}
-
-static void bootmenu_loop(struct bootmenu_data *menu,
-		enum bootmenu_key *key, int *esc)
-{
-	int c;
-
-	if (*esc == 1) {
-		if (tstc()) {
-			c = getchar();
-		} else {
-			WATCHDOG_RESET();
-			mdelay(10);
-			if (tstc())
-				c = getchar();
-			else
-				c = '\e';
-		}
-	} else {
-		while (!tstc()) {
-			WATCHDOG_RESET();
-			mdelay(10);
-		}
-		c = getchar();
-	}
-
-	switch (*esc) {
-	case 0:
-		/* First char of ANSI escape sequence '\e' */
-		if (c == '\e') {
-			*esc = 1;
-			*key = KEY_NONE;
-		}
-		break;
-	case 1:
-		/* Second char of ANSI '[' */
-		if (c == '[') {
-			*esc = 2;
-			*key = KEY_NONE;
-		} else {
-		/* Alone ESC key was pressed */
-			*key = KEY_QUIT;
-			*esc = (c == '\e') ? 1 : 0;
-		}
-		break;
-	case 2:
-	case 3:
-		/* Third char of ANSI (number '1') - optional */
-		if (*esc == 2 && c == '1') {
-			*esc = 3;
-			*key = KEY_NONE;
-			break;
-		}
-
-		*esc = 0;
-
-		/* ANSI 'A' - key up was pressed */
-		if (c == 'A')
-			*key = KEY_UP;
-		/* ANSI 'B' - key down was pressed */
-		else if (c == 'B')
-			*key = KEY_DOWN;
-		/* other key was pressed */
-		else
-			*key = KEY_NONE;
-
-		break;
-	}
-
-	/* enter key was pressed */
-	if (c == '\r')
-		*key = KEY_SELECT;
-
-	/* ^C was pressed */
-	if (c == 0x3)
-		*key = KEY_QUIT;
-}
-
 static char *bootmenu_choice_entry(void *data)
 {
 	struct bootmenu_data *menu = data;
diff --git a/common/menu.c b/common/menu.c
index 4118c6dc3c..53b026b52e 100644
--- a/common/menu.c
+++ b/common/menu.c
@@ -4,11 +4,14 @@
  * Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved.
  */
 
+#include <ansi.h>
 #include <common.h>
 #include <cli.h>
 #include <malloc.h>
 #include <errno.h>
+#include <linux/delay.h>
 #include <linux/list.h>
+#include <watchdog.h>
 
 #include "menu.h"
 
@@ -421,3 +424,137 @@ int menu_destroy(struct menu *m)
 
 	return 1;
 }
+
+void bootmenu_autoboot_loop(struct bootmenu_data *menu,
+			    enum bootmenu_key *key, int *esc)
+{
+	int i, c;
+
+	if (menu->delay > 0) {
+		/* flush input */
+		while (tstc())
+			getchar();
+
+		printf(ANSI_CURSOR_POSITION, menu->count + 5, 1);
+		printf("  Hit any key to stop autoboot: %2d ", menu->delay);
+	}
+
+	while (menu->delay > 0) {
+		for (i = 0; i < 100; ++i) {
+			if (!tstc()) {
+				WATCHDOG_RESET();
+				mdelay(10);
+				continue;
+			}
+
+			menu->delay = -1;
+			c = getchar();
+
+			switch (c) {
+			case '\e':
+				*esc = 1;
+				*key = KEY_NONE;
+				break;
+			case '\r':
+				*key = KEY_SELECT;
+				break;
+			case 0x3: /* ^C */
+				*key = KEY_QUIT;
+				break;
+			default:
+				*key = KEY_NONE;
+				break;
+			}
+
+			break;
+		}
+
+		if (menu->delay < 0)
+			break;
+
+		--menu->delay;
+		printf("\b\b\b%2d ", menu->delay);
+	}
+
+	printf(ANSI_CURSOR_POSITION, menu->count + 5, 1);
+	puts(ANSI_CLEAR_LINE);
+
+	if (menu->delay == 0)
+		*key = KEY_SELECT;
+}
+
+void bootmenu_loop(struct bootmenu_data *menu,
+		   enum bootmenu_key *key, int *esc)
+{
+	int c;
+
+	if (*esc == 1) {
+		if (tstc()) {
+			c = getchar();
+		} else {
+			WATCHDOG_RESET();
+			mdelay(10);
+			if (tstc())
+				c = getchar();
+			else
+				c = '\e';
+		}
+	} else {
+		while (!tstc()) {
+			WATCHDOG_RESET();
+			mdelay(10);
+		}
+		c = getchar();
+	}
+
+	switch (*esc) {
+	case 0:
+		/* First char of ANSI escape sequence '\e' */
+		if (c == '\e') {
+			*esc = 1;
+			*key = KEY_NONE;
+		}
+		break;
+	case 1:
+		/* Second char of ANSI '[' */
+		if (c == '[') {
+			*esc = 2;
+			*key = KEY_NONE;
+		} else {
+		/* Alone ESC key was pressed */
+			*key = KEY_QUIT;
+			*esc = (c == '\e') ? 1 : 0;
+		}
+		break;
+	case 2:
+	case 3:
+		/* Third char of ANSI (number '1') - optional */
+		if (*esc == 2 && c == '1') {
+			*esc = 3;
+			*key = KEY_NONE;
+			break;
+		}
+
+		*esc = 0;
+
+		/* ANSI 'A' - key up was pressed */
+		if (c == 'A')
+			*key = KEY_UP;
+		/* ANSI 'B' - key down was pressed */
+		else if (c == 'B')
+			*key = KEY_DOWN;
+		/* other key was pressed */
+		else
+			*key = KEY_NONE;
+
+		break;
+	}
+
+	/* enter key was pressed */
+	if (c == '\r')
+		*key = KEY_SELECT;
+
+	/* ^C was pressed */
+	if (c == 0x3)
+		*key = KEY_QUIT;
+}
diff --git a/include/menu.h b/include/menu.h
index ad5859437e..e74616cae8 100644
--- a/include/menu.h
+++ b/include/menu.h
@@ -35,4 +35,24 @@ int menu_default_choice(struct menu *m, void **choice);
  */
 int menu_show(int bootdelay);
 
+struct bootmenu_data {
+	int delay;			/* delay for autoboot */
+	int active;			/* active menu entry */
+	int count;			/* total count of menu entries */
+	struct bootmenu_entry *first;	/* first menu entry */
+};
+
+enum bootmenu_key {
+	KEY_NONE = 0,
+	KEY_UP,
+	KEY_DOWN,
+	KEY_SELECT,
+	KEY_QUIT,
+};
+
+void bootmenu_autoboot_loop(struct bootmenu_data *menu,
+			    enum bootmenu_key *key, int *esc);
+void bootmenu_loop(struct bootmenu_data *menu,
+		   enum bootmenu_key *key, int *esc);
+
 #endif /* __MENU_H__ */
-- 
2.17.1


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

* [PATCH v5 13/17] efi_loader: menu-driven addition of UEFI boot option
  2022-04-28  8:09 [PATCH v5 00/17] enable menu-driven boot device selection Masahisa Kojima
                   ` (11 preceding siblings ...)
  2022-04-28  8:09 ` [PATCH v5 12/17] bootmenu: factor out the user input handling Masahisa Kojima
@ 2022-04-28  8:09 ` Masahisa Kojima
  2022-04-28 16:33   ` Heinrich Schuchardt
  2022-04-28  8:09 ` [PATCH v5 14/17] efi_loader: menu-driven deletion of UEFI boot variable Masahisa Kojima
                   ` (5 subsequent siblings)
  18 siblings, 1 reply; 58+ messages in thread
From: Masahisa Kojima @ 2022-04-28  8:09 UTC (permalink / raw)
  To: u-boot
  Cc: Heinrich Schuchardt, Ilias Apalodimas, Simon Glass,
	Takahiro Akashi, Francois Ozog, Mark Kettenis, Masahisa Kojima

This commit supports the menu-driven UEFI boot option addition.
User can select the block device volume having
efi_simple_file_system_protocol and select the file corresponding
to the Boot#### variable. Then user enter the label of the BOOT####
variable in utf8.

Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
---
Changes in v5:
- remove forward declarations
- add const qualifier for menu items
- fix the possible unaligned access for directory info access
- split into three commit 1)add boot option 2) delete boot option 3)change boot order
  This commit is 1)add boot option.
- fix file name buffer allocation size, it should be EFI_BOOTMENU_FILE_PATH_MAX * sizeof(u16)
- fix wrong size checking for file selection

Chanes in v4:
- UEFI boot option maintenance menu is integrated into bootmenu
- display the simplified volume name(e.g. usb0:1, nvme1:2) for the
  volume selection
- instead of extending lib/efi_loader/efi_bootmgr.c, newly create
  lib/efi_loader/efi_bootmenu_maintenance.c and implement boot
  variable maintenance into it.

Changes in RFC v3:
 not included in v3 series

Changes in RFC v2:
- enable utf8 user input for boot option name
- create lib/efi_loader/efi_console.c::efi_console_get_u16_string() for
  utf8 user input handling
- use u16_strlcat instead of u16_strcat
- remove the EFI_CALLs, and newly create or expose the following
  xxx_int() functions.
    efi_locate_handle_buffer_int(), efi_open_volume_int(),
    efi_file_open_int(), efi_file_close_int(), efi_file_read_int() and
    efi_file_setpos_int().
  Note that EFI_CALLs still exist for EFI_DEVICE_PATH_TO_TEXT_PROTOCOL
  and EFI_SIMPLE_TEXT_INPUT/OUTPUT_PROTOCOL
- use efi_search_protocol() instead of calling locate_protocol() to get
  the device_path_to_text_protocol interface.
- remove unnecessary puts(ANSI_CLEAR_LINE), this patch is still depends on
  puts(ANSI_CLEAR_CONSOLE)
- skip SetVariable() if the bootorder is not changed

 cmd/bootmenu.c                            |  69 +-
 include/efi_loader.h                      |  37 +
 lib/efi_loader/Makefile                   |   1 +
 lib/efi_loader/efi_bootmenu_maintenance.c | 862 ++++++++++++++++++++++
 lib/efi_loader/efi_boottime.c             |  52 +-
 lib/efi_loader/efi_console.c              |  81 ++
 lib/efi_loader/efi_disk.c                 |  11 +
 lib/efi_loader/efi_file.c                 |  75 +-
 8 files changed, 1133 insertions(+), 55 deletions(-)
 create mode 100644 lib/efi_loader/efi_bootmenu_maintenance.c

diff --git a/cmd/bootmenu.c b/cmd/bootmenu.c
index eb23afdd41..860cb83182 100644
--- a/cmd/bootmenu.c
+++ b/cmd/bootmenu.c
@@ -21,6 +21,8 @@
 
 /* maximum bootmenu entries */
 #define MAX_COUNT	99
+#define STATIC_ENTRY 2
+#define MAX_DYNAMIC_ENTRY (MAX_COUNT - STATIC_ENTRY)
 
 /* maximal size of bootmenu env
  *  9 = strlen("bootmenu_")
@@ -41,10 +43,11 @@ enum boot_type {
 	BOOTMENU_TYPE_BOOTMENU,
 	BOOTMENU_TYPE_UEFI_BOOT_OPTION,
 	BOOTMENU_TYPE_DISTRO_BOOT,
+	BOOTMENU_TYPE_UEFI_MAINTENANCE,
 };
 
 struct bootmenu_entry {
-	unsigned short int num;		/* unique number 0 .. MAX_COUNT */
+	unsigned short int num;		/* unique number 0 .. MAX_DYNAMIC_ENTRY */
 	char key[3];			/* key identifier of number */
 	u16 *title;			/* title of entry */
 	char *command;			/* hush command of entry */
@@ -58,7 +61,7 @@ static char *bootmenu_getoption(unsigned short int n)
 {
 	char name[MAX_ENV_SIZE];
 
-	if (n > MAX_COUNT)
+	if (n > MAX_DYNAMIC_ENTRY)
 		return NULL;
 
 	sprintf(name, "bootmenu_%d", n);
@@ -229,7 +232,7 @@ static int prepare_bootmenu_entry(struct bootmenu_data *menu,
 		iter = entry;
 		++i;
 
-		if (i == MAX_COUNT - 1)
+		if (i == MAX_DYNAMIC_ENTRY)
 			break;
 	}
 
@@ -317,7 +320,7 @@ static int prepare_uefi_bootorder_entry(struct bootmenu_data *menu,
 
 		free(load_option);
 
-		if (i == MAX_COUNT - 1)
+		if (i == MAX_DYNAMIC_ENTRY)
 			break;
 	}
 
@@ -481,7 +484,7 @@ static int prepare_distro_boot_entry(struct bootmenu_data *menu,
 		iter = entry;
 		i++;
 
-		if (i == MAX_COUNT - 1)
+		if (i == MAX_DYNAMIC_ENTRY)
 			break;
 
 		token = strtok(NULL, " ");
@@ -520,19 +523,56 @@ static struct bootmenu_data *bootmenu_create(int delay)
 		goto cleanup;
 
 	if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR)) {
-		if (i < MAX_COUNT - 1) {
+		if (i < MAX_DYNAMIC_ENTRY) {
 			ret = prepare_uefi_bootorder_entry(menu, &iter, &i);
 			if (ret < 0 && ret != -ENOENT)
 				goto cleanup;
 		}
 	}
 
-	if (i < MAX_COUNT - 1) {
+	if (i < MAX_DYNAMIC_ENTRY) {
 		ret = prepare_distro_boot_entry(menu, &iter, &i);
 		if (ret < 0 && ret != -ENOENT)
 			goto cleanup;
 	}
 
+	if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR)) {
+		/* Add UEFI Boot Manager Maintenance entry */
+		if (i <= MAX_DYNAMIC_ENTRY) {
+			entry = malloc(sizeof(struct bootmenu_entry));
+			if (!entry)
+				goto cleanup;
+
+			entry->title = u16_strdup(u"UEFI Boot Manager Maintenance");
+			if (!entry->title) {
+				free(entry);
+				goto cleanup;
+			}
+
+			entry->command = strdup("");
+			if (!entry->command) {
+				free(entry->title);
+				free(entry);
+				goto cleanup;
+			}
+
+			sprintf(entry->key, "%d", i);
+
+			entry->num = i;
+			entry->menu = menu;
+			entry->type = BOOTMENU_TYPE_UEFI_MAINTENANCE;
+			entry->next = NULL;
+
+			if (!iter)
+				menu->first = entry;
+			else
+				iter->next = entry;
+
+			iter = entry;
+			i++;
+		}
+	}
+
 	/* Add U-Boot console entry at the end */
 	if (i <= MAX_COUNT - 1) {
 		entry = malloc(sizeof(struct bootmenu_entry));
@@ -704,6 +744,12 @@ static enum bootmenu_ret bootmenu_show(int delay)
 		title = u16_strdup(iter->title);
 		command = strdup(iter->command);
 
+		if (iter->type == BOOTMENU_TYPE_UEFI_MAINTENANCE) {
+			efi_bootmenu_show_maintenance_menu();
+			ret = BOOTMENU_RET_UPDATED;
+			goto cleanup;
+		}
+
 		/* last entry is U-Boot console or Quit */
 		if (iter->num == iter->menu->count - 1) {
 			ret = BOOTMENU_RET_QUIT;
@@ -794,6 +840,7 @@ int do_bootmenu(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
 {
 	char *delay_str = NULL;
 	int delay = 10;
+	int ret;
 
 #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
 	delay = CONFIG_BOOTDELAY;
@@ -808,7 +855,13 @@ int do_bootmenu(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
 	if (delay_str)
 		delay = (int)simple_strtol(delay_str, NULL, 10);
 
-	bootmenu_show(delay);
+	while (1) {
+		ret =  bootmenu_show(delay);
+		delay = -1;
+		if (ret != BOOTMENU_RET_UPDATED)
+			break;
+	}
+
 	return 0;
 }
 
diff --git a/include/efi_loader.h b/include/efi_loader.h
index effb43369d..533618341b 100644
--- a/include/efi_loader.h
+++ b/include/efi_loader.h
@@ -226,6 +226,9 @@ const char *__efi_nesting_dec(void);
 #define EFI_CACHELINE_SIZE 128
 #endif
 
+/* max bootmenu title size for volume selection */
+#define BOOTMENU_DEVICE_NAME_MAX 16
+
 /* Key identifying current memory map */
 extern efi_uintn_t efi_memory_map_key;
 
@@ -312,6 +315,9 @@ extern const efi_guid_t efi_guid_firmware_management_protocol;
 extern const efi_guid_t efi_esrt_guid;
 /* GUID of the SMBIOS table */
 extern const efi_guid_t smbios_guid;
+/*GUID of console */
+extern const efi_guid_t efi_guid_text_input_protocol;
+extern const efi_guid_t efi_guid_text_output_protocol;
 
 extern char __efi_runtime_start[], __efi_runtime_stop[];
 extern char __efi_runtime_rel_start[], __efi_runtime_rel_stop[];
@@ -871,6 +877,8 @@ efi_status_t efi_set_load_options(efi_handle_t handle,
 				  void *load_options);
 efi_status_t efi_bootmgr_load(efi_handle_t *handle, void **load_options);
 
+efi_status_t efi_bootmenu_show_maintenance_menu(void);
+
 /**
  * struct efi_image_regions - A list of memory regions
  *
@@ -1042,4 +1050,33 @@ efi_status_t efi_esrt_populate(void);
 efi_status_t efi_load_capsule_drivers(void);
 
 efi_status_t platform_get_eventlog(struct udevice *dev, u64 *addr, u32 *sz);
+
+efi_status_t efi_locate_handle_buffer_int(enum efi_locate_search_type search_type,
+					  const efi_guid_t *protocol, void *search_key,
+					  efi_uintn_t *no_handles, efi_handle_t **buffer);
+
+efi_status_t efi_open_volume_int(struct efi_simple_file_system_protocol *this,
+				 struct efi_file_handle **root);
+efi_status_t efi_file_open_int(struct efi_file_handle *this,
+			       struct efi_file_handle **new_handle,
+			       u16 *file_name, u64 open_mode,
+			       u64 attributes);
+efi_status_t efi_file_close_int(struct efi_file_handle *file);
+efi_status_t efi_file_read_int(struct efi_file_handle *this,
+			       efi_uintn_t *buffer_size, void *buffer);
+efi_status_t efi_file_setpos_int(struct efi_file_handle *file, u64 pos);
+
+typedef efi_status_t (*efi_console_filter_func)(struct efi_input_key *key);
+efi_status_t efi_console_get_u16_string
+		(struct efi_simple_text_input_protocol *cin,
+		 struct efi_simple_text_output_protocol *cout,
+		 u16 *buf, efi_uintn_t count, efi_console_filter_func filer_func,
+		 int row, int col);
+
+efi_status_t efi_bootmenu_get_unused_bootoption(u16 *buf,
+						efi_uintn_t buf_size, u32 *index);
+efi_status_t efi_bootmenu_append_bootorder(u16 index);
+
+efi_status_t efi_disk_get_device_name(struct efi_block_io *this, char *buf, int size);
+
 #endif /* _EFI_LOADER_H */
diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile
index aaaa25cefe..792eabe18a 100644
--- a/lib/efi_loader/Makefile
+++ b/lib/efi_loader/Makefile
@@ -77,6 +77,7 @@ obj-$(CONFIG_EFI_TCG2_PROTOCOL) += efi_tcg2.o
 obj-$(CONFIG_EFI_RISCV_BOOT_PROTOCOL) += efi_riscv.o
 obj-$(CONFIG_EFI_LOAD_FILE2_INITRD) += efi_load_initrd.o
 obj-$(CONFIG_EFI_SIGNATURE_SUPPORT) += efi_signature.o
+obj-$(CONFIG_CMD_BOOTMENU) += efi_bootmenu_maintenance.o
 
 EFI_VAR_SEED_FILE := $(subst $\",,$(CONFIG_EFI_VAR_SEED_FILE))
 $(obj)/efi_var_seed.o: $(srctree)/$(EFI_VAR_SEED_FILE)
diff --git a/lib/efi_loader/efi_bootmenu_maintenance.c b/lib/efi_loader/efi_bootmenu_maintenance.c
new file mode 100644
index 0000000000..77401a7829
--- /dev/null
+++ b/lib/efi_loader/efi_bootmenu_maintenance.c
@@ -0,0 +1,862 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ *  Menu-driven UEFI Boot Variable maintenance
+ *
+ *  Copyright (c) 2022 Masahisa Kojima, Linaro Limited
+ */
+
+#define LOG_CATEGORY LOGC_EFI
+
+#include <ansi.h>
+#include <common.h>
+#include <charset.h>
+#include <log.h>
+#include <malloc.h>
+#include <menu.h>
+#include <efi_loader.h>
+#include <efi_variable.h>
+#include <asm/unaligned.h>
+
+static struct efi_simple_text_input_protocol *cin;
+static struct efi_simple_text_output_protocol *cout;
+
+#define EFI_BOOTMENU_ENTRY_NUM_MAX 99
+#define EFI_BOOTMENU_FILE_PATH_MAX 512
+#define EFI_BOOTMENU_FILE_PATH_BUF_SIZE (EFI_BOOTMENU_FILE_PATH_MAX * sizeof(u16))
+#define EFI_BOOTMENU_BOOT_NAME_MAX 32
+#define EFI_BOOT_ORDER_MAX_SIZE_IN_DECIMAL 6
+
+typedef efi_status_t (*efi_bootmenu_entry_func)(void *data, bool *exit);
+
+/**
+ * struct efi_bootmenu_entry - menu entry structure
+ *
+ * @num:		menu entry index
+ * @title:		title of entry
+ * @key:		unique key
+ * @bootmgr_menu:	pointer to the menu structure
+ * @next:		pointer to the next entry
+ * @func:		callback function to be called when this entry is selected
+ * @data:		data to be passed to the callback function
+ */
+struct efi_bootmenu_entry {
+	u32 num;
+	u16 *title;
+	char key[6];
+	struct efi_bootmenu *bootmgr_menu;
+	struct efi_bootmenu_entry *next;
+	efi_bootmenu_entry_func func;
+	void *data;
+};
+
+/**
+ * struct efi_bootmenu - bootmgr menu structure
+ *
+ * @delay:	delay for autoboot
+ * @active:	active menu entry index
+ * @count:	total count of menu entry
+ * @first:	pointer to the first menu entry
+ */
+struct efi_bootmenu {
+	int delay;
+	int active;
+	int count;
+	struct efi_bootmenu_entry *first;
+};
+
+struct efi_bootmenu_item {
+	u16 *title;
+	efi_bootmenu_entry_func func;
+	void *data;
+};
+
+struct efi_bootmenu_boot_option {
+	struct efi_simple_file_system_protocol *current_volume;
+	struct efi_device_path *dp_volume;
+	u16 *current_path;
+	u16 *boot_name;
+	bool file_selected;
+};
+
+static const struct efi_device_path END = {
+	.type     = DEVICE_PATH_TYPE_END,
+	.sub_type = DEVICE_PATH_SUB_TYPE_END,
+	.length   = sizeof(END),
+};
+
+struct efi_bootmenu_volume_entry_data {
+	struct efi_bootmenu_boot_option *bo;
+	struct efi_simple_file_system_protocol *v;
+	struct efi_device_path *dp;
+};
+
+struct efi_bootmenu_file_entry_data {
+	struct efi_bootmenu_boot_option *bo;
+	bool is_directory;
+	u16 *file_name;
+};
+
+static void efi_bootmenu_print_entry(void *data)
+{
+	struct efi_bootmenu_entry *entry = data;
+	int reverse = (entry->bootmgr_menu->active == entry->num);
+
+	/* TODO: support scroll or page for many entries */
+
+	/*
+	 * Move cursor to line where the entry will be drown (entry->count)
+	 * First 3 lines contain bootmgr menu header + one empty line
+	 * For the last "Quit" entry, add one empty line
+	 */
+	if (entry->num == (entry->bootmgr_menu->count - 1))
+		printf(ANSI_CURSOR_POSITION, entry->num + 5, 1);
+	else
+		printf(ANSI_CURSOR_POSITION, entry->num + 4, 1);
+
+	puts("     ");
+
+	if (reverse)
+		puts(ANSI_COLOR_REVERSE);
+
+	printf("%ls", entry->title);
+
+	if (reverse)
+		puts(ANSI_COLOR_RESET);
+}
+
+static void efi_bootmenu_display_statusline(struct menu *m)
+{
+	struct efi_bootmenu_entry *entry;
+	struct efi_bootmenu *bootmgr_menu;
+
+	if (menu_default_choice(m, (void *)&entry) < 0)
+		return;
+
+	bootmgr_menu = entry->bootmgr_menu;
+
+	printf(ANSI_CURSOR_POSITION, 1, 1);
+	puts(ANSI_CLEAR_LINE);
+	printf(ANSI_CURSOR_POSITION, 2, 1);
+	puts("  *** U-Boot EFI Boot Manager ***");
+	puts(ANSI_CLEAR_LINE_TO_END);
+	printf(ANSI_CURSOR_POSITION, 3, 1);
+	puts(ANSI_CLEAR_LINE);
+
+	/* First 3 lines are bootmgr_menu header + 2 empty lines between entries */
+	printf(ANSI_CURSOR_POSITION, bootmgr_menu->count + 5, 1);
+	puts(ANSI_CLEAR_LINE);
+	printf(ANSI_CURSOR_POSITION, bootmgr_menu->count + 6, 1);
+	puts("  Press UP/DOWN to move, ENTER to select, ESC/CTRL+C to quit");
+	puts(ANSI_CLEAR_LINE_TO_END);
+	printf(ANSI_CURSOR_POSITION, bootmgr_menu->count + 7, 1);
+	puts(ANSI_CLEAR_LINE);
+}
+
+static char *efi_bootmenu_choice_entry(void *data)
+{
+	int i;
+	int esc = 0;
+	struct efi_bootmenu_entry *iter;
+	enum bootmenu_key key = KEY_NONE;
+	struct efi_bootmenu *bootmgr_menu = data;
+
+	while (1) {
+		if (bootmgr_menu->delay >= 0) {
+			/* Autoboot was not stopped */
+			bootmenu_autoboot_loop((struct bootmenu_data *)bootmgr_menu, &key, &esc);
+		} else {
+			/* Some key was pressed, so autoboot was stopped */
+			bootmenu_loop((struct bootmenu_data *)bootmgr_menu, &key, &esc);
+		}
+
+		if (bootmgr_menu->delay == 0)
+			key = KEY_QUIT;
+
+		switch (key) {
+		case KEY_UP:
+			if (bootmgr_menu->active > 0)
+				--bootmgr_menu->active;
+			/* no menu key selected, regenerate menu */
+			return NULL;
+		case KEY_DOWN:
+			if (bootmgr_menu->active < bootmgr_menu->count - 1)
+				++bootmgr_menu->active;
+			/* no menu key selected, regenerate menu */
+			return NULL;
+		case KEY_SELECT:
+			iter = bootmgr_menu->first;
+			for (i = 0; i < bootmgr_menu->active; ++i)
+				iter = iter->next;
+			return iter->key;
+		case KEY_QUIT:
+			/* Quit by choosing the last entry */
+			iter = bootmgr_menu->first;
+			while (iter->next)
+				iter = iter->next;
+			return iter->key;
+		default:
+			break;
+		}
+	}
+
+	/* never happens */
+	debug("bootmgr menu: this should not happen");
+	return NULL;
+}
+
+static void efi_bootmenu_destroy(struct efi_bootmenu *bootmgr_menu)
+{
+	struct efi_bootmenu_entry *next;
+	struct efi_bootmenu_entry *iter = bootmgr_menu->first;
+
+	while (iter) {
+		next = iter->next;
+		free(iter);
+		iter = next;
+	}
+	free(bootmgr_menu);
+}
+
+/**
+ * efi_bootmenu_process_common() - main handler for uefi bootmgr menu
+ *
+ * Construct the structures required to show the menu, then handle
+ * the user input intracting with u-boot menu functions.
+ *
+ * @items:	pointer to the structure of each menu entry
+ * @count:	the number of menu entry
+ * @delay:	delay for autoboot/autoselect
+ * Return:	status code
+ */
+static efi_status_t efi_bootmenu_process_common(const struct efi_bootmenu_item *items,
+						int count, int delay)
+{
+	u32 i;
+	bool exit = false;
+	efi_status_t ret;
+	struct menu *menu;
+	void *choice = NULL;
+	struct efi_bootmenu_entry *entry;
+	struct efi_bootmenu *bootmgr_menu;
+	struct efi_bootmenu_entry *iter = NULL;
+
+	if (count > EFI_BOOTMENU_ENTRY_NUM_MAX)
+		return EFI_OUT_OF_RESOURCES;
+
+	bootmgr_menu = calloc(1, sizeof(struct efi_bootmenu));
+	if (!bootmgr_menu)
+		return EFI_OUT_OF_RESOURCES;
+
+	bootmgr_menu->delay = delay;
+	bootmgr_menu->active = 0;
+	bootmgr_menu->first = NULL;
+
+	for (i = 0; i < count; i++) {
+		entry = calloc(1, sizeof(struct efi_bootmenu_entry));
+		if (!entry) {
+			ret = EFI_LOAD_ERROR;
+			goto out;
+		}
+
+		entry->num = i;
+		entry->title = items->title;
+		snprintf(entry->key, sizeof(entry->key), "%04X", i);
+		entry->bootmgr_menu = bootmgr_menu;
+		entry->func = items->func;
+		entry->data = items->data;
+		entry->next = NULL;
+
+		if (!iter)
+			bootmgr_menu->first = entry;
+		else
+			iter->next = entry;
+
+		iter = entry;
+		items++;
+	}
+	bootmgr_menu->count = count;
+
+	menu = menu_create(NULL, 0, 1, efi_bootmenu_display_statusline,
+			   efi_bootmenu_print_entry, efi_bootmenu_choice_entry,
+			   bootmgr_menu);
+	if (!menu) {
+		ret = EFI_INVALID_PARAMETER;
+		goto out;
+	}
+
+	for (entry = bootmgr_menu->first; entry; entry = entry->next) {
+		if (!menu_item_add(menu, entry->key, entry)) {
+			ret = EFI_INVALID_PARAMETER;
+			goto out;
+		}
+	}
+
+	menu_default_set(menu, bootmgr_menu->first->key);
+
+	while (!exit) {
+		puts(ANSI_CURSOR_HIDE);
+		puts(ANSI_CLEAR_CONSOLE);
+		printf(ANSI_CURSOR_POSITION, 1, 1);
+
+		if (menu_get_choice(menu, &choice)) {
+			entry = choice;
+			if (entry->func)
+				ret = entry->func(entry->data, &exit);
+
+			/* last entry "Quit" is selected, exit this menu */
+			if (entry->num == (entry->bootmgr_menu->count - 1)) {
+				ret = EFI_ABORTED;
+				break;
+			}
+		}
+	}
+
+out:
+	menu_destroy(menu);
+	efi_bootmenu_destroy(bootmgr_menu);
+
+	puts(ANSI_CLEAR_CONSOLE);
+	printf(ANSI_CURSOR_POSITION, 1, 1);
+	puts(ANSI_CURSOR_SHOW);
+
+	return ret;
+}
+
+static efi_status_t efi_bootmenu_volume_selected(void *data, bool *exit)
+{
+	struct efi_bootmenu_volume_entry_data *info = data;
+
+	*exit = true;
+
+	if (info) {
+		info->bo->current_volume = info->v;
+		info->bo->dp_volume = info->dp;
+	}
+
+	return EFI_SUCCESS;
+}
+
+static efi_status_t efi_bootmenu_file_selected(void *data, bool *exit)
+{
+	struct efi_bootmenu_file_entry_data *info = data;
+
+	*exit = true;
+
+	if (!info)
+		return EFI_INVALID_PARAMETER;
+
+	if (u16_strncmp(info->file_name, u".", 1) == 0 &&
+	    u16_strlen(info->file_name) == 1) {
+		/* stay current path */
+	} else if (u16_strncmp(info->file_name, u"..", 2) == 0 &&
+		   u16_strlen(info->file_name) == 2) {
+		u32 i;
+		int len = u16_strlen(info->bo->current_path);
+
+		for (i = len - 2; i > 0; i--) {
+			if (info->bo->current_path[i] == u'\\')
+				break;
+		}
+
+		if (i == 0)
+			info->bo->current_path[0] = u'\0';
+		else
+			info->bo->current_path[i + 1] = u'\0';
+	} else {
+		size_t new_len;
+
+		new_len = u16_strlen(info->bo->current_path) +
+				     u16_strlen(info->file_name) + 1;
+		if (new_len > EFI_BOOTMENU_FILE_PATH_MAX) {
+			/* TODO: show error notification to user */
+			log_err("file path is too long\n");
+			return EFI_INVALID_PARAMETER;
+		}
+		u16_strlcat(info->bo->current_path, info->file_name,
+			    EFI_BOOTMENU_FILE_PATH_MAX);
+		if (info->is_directory) {
+			/*
+			 * Remainig buffer should have enough space to contain u"\\" and
+			 * at least one character for file name
+			 */
+			if (new_len + 2 > EFI_BOOTMENU_FILE_PATH_MAX) {
+				log_err("directory path is too long\n");
+				return EFI_INVALID_PARAMETER;
+			}
+			u16_strlcat(info->bo->current_path, u"\\",
+				    EFI_BOOTMENU_FILE_PATH_MAX);
+		} else {
+			info->bo->file_selected = true;
+		}
+	}
+	return EFI_SUCCESS;
+}
+
+static efi_status_t efi_bootmenu_select_volume(struct efi_bootmenu_boot_option *bo)
+{
+	u32 i;
+	efi_status_t ret;
+	efi_uintn_t count;
+	struct efi_handler *handler;
+	struct efi_device_path *device_path;
+	efi_handle_t *volume_handles = NULL;
+	struct efi_simple_file_system_protocol *v;
+	struct efi_bootmenu_item *menu_item, *iter;
+
+	ret = efi_locate_handle_buffer_int(BY_PROTOCOL, &efi_simple_file_system_protocol_guid,
+					   NULL, &count, (efi_handle_t **)&volume_handles);
+	if (ret != EFI_SUCCESS)
+		return ret;
+
+	menu_item = calloc(count + 1, sizeof(struct efi_bootmenu_item));
+	if (!menu_item) {
+		ret = EFI_OUT_OF_RESOURCES;
+		goto out1;
+	}
+
+	iter = menu_item;
+	for (i = 0; i < count; i++) {
+		u16 *dev_name, *p;
+		struct efi_block_io *block_io;
+		char buf[BOOTMENU_DEVICE_NAME_MAX];
+		struct efi_bootmenu_volume_entry_data *info;
+
+		ret = efi_search_protocol(volume_handles[i],
+					  &efi_simple_file_system_protocol_guid, &handler);
+		if (ret != EFI_SUCCESS)
+			continue;
+		ret = efi_protocol_open(handler, (void **)&v, efi_root, NULL,
+					EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+		if (ret != EFI_SUCCESS)
+			continue;
+
+		ret = efi_search_protocol(volume_handles[i], &efi_guid_device_path, &handler);
+		if (ret != EFI_SUCCESS)
+			continue;
+		ret = efi_protocol_open(handler, (void **)&device_path,
+					efi_root, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+		if (ret != EFI_SUCCESS)
+			continue;
+
+		ret = efi_search_protocol(volume_handles[i], &efi_block_io_guid, &handler);
+		if (ret != EFI_SUCCESS)
+			continue;
+		ret = efi_protocol_open(handler, (void **)&block_io,
+					efi_root, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+		if (ret != EFI_SUCCESS)
+			continue;
+
+		info = calloc(1, sizeof(struct efi_bootmenu_volume_entry_data));
+		if (!info) {
+			ret = EFI_OUT_OF_RESOURCES;
+			goto out2;
+		}
+
+		efi_disk_get_device_name(block_io, buf, BOOTMENU_DEVICE_NAME_MAX);
+		dev_name = calloc(1, (strlen(buf) + 1) * sizeof(u16));
+		if (!dev_name) {
+			free(info);
+			ret = EFI_OUT_OF_RESOURCES;
+			goto out2;
+		}
+		p = dev_name;
+		utf8_utf16_strncpy(&p, buf, strlen(buf));
+
+		info->v = v;
+		info->dp = device_path;
+		info->bo = bo;
+		iter->title = dev_name;
+		iter->func = efi_bootmenu_volume_selected;
+		iter->data = info;
+		iter++;
+	}
+
+	iter->title = u16_strdup(u"Quit");
+	iter->func = NULL;
+	iter->data = NULL;
+	count += 1;
+
+	ret = efi_bootmenu_process_common(menu_item, count, -1);
+
+out2:
+	iter = menu_item;
+	for (i = 0; i < count; i++) {
+		struct efi_bootmenu_volume_entry_data *p;
+
+		p = (struct efi_bootmenu_volume_entry_data *)(iter->data);
+		free(iter->title);
+		free(p);
+		iter++;
+	}
+
+	free(menu_item);
+
+out1:
+	efi_free_pool(volume_handles);
+
+	return ret;
+}
+
+static efi_status_t efi_bootmenu_select_file(struct efi_bootmenu_boot_option *bo,
+					     struct efi_file_handle *root)
+{
+	u32 i;
+	struct efi_file_info *buf;
+	u32 count = 0;
+	efi_uintn_t len;
+	efi_status_t ret;
+	struct efi_file_handle *f;
+	struct efi_bootmenu_item *menu_item, *iter;
+
+	buf = calloc(1, sizeof(struct efi_file_info) + EFI_BOOTMENU_FILE_PATH_BUF_SIZE);
+	if (!buf)
+		return EFI_OUT_OF_RESOURCES;
+
+	while (!bo->file_selected) {
+		count = 0;
+
+		ret = efi_file_open_int(root, &f, bo->current_path, EFI_FILE_MODE_READ, 0);
+		if (ret != EFI_SUCCESS)
+			return ret;
+
+		/* calculate directory information total count */
+		for (;;) {
+			len = sizeof(struct efi_file_info) + EFI_BOOTMENU_FILE_PATH_BUF_SIZE;
+			ret = efi_file_read_int(f, &len, buf);
+			if (ret != EFI_SUCCESS || len == 0)
+				break;
+
+			count++;
+		}
+
+		menu_item = calloc(count + 1, sizeof(struct efi_bootmenu_item));
+		if (!menu_item) {
+			efi_file_close_int(f);
+			ret = EFI_OUT_OF_RESOURCES;
+			goto out;
+		}
+
+		/* read directory and construct menu structure */
+		efi_file_setpos_int(f, 0);
+		iter = menu_item;
+		for (i = 0; i < count; i++) {
+			u16 *name;
+			int name_len;
+			struct efi_bootmenu_file_entry_data *info;
+
+			len = sizeof(struct efi_file_info) + EFI_BOOTMENU_FILE_PATH_BUF_SIZE;
+			ret = efi_file_read_int(f, &len, buf);
+			if (ret != EFI_SUCCESS || len == 0)
+				goto err;
+
+			info = calloc(1, sizeof(struct efi_bootmenu_file_entry_data));
+			if (!info) {
+				ret = EFI_OUT_OF_RESOURCES;
+				goto err;
+			}
+
+			if (buf->attribute & EFI_FILE_DIRECTORY) {
+				/* append u'/' at the end of directory name */
+				name_len = u16_strsize(buf->file_name) + sizeof(u16);
+				name = calloc(1, name_len);
+				if (!name) {
+					ret = EFI_OUT_OF_RESOURCES;
+					goto err;
+				}
+				u16_strcpy(name, buf->file_name);
+				name[u16_strlen(buf->file_name)] = u'/';
+
+				info->is_directory = true;
+			} else {
+				name_len = u16_strsize(buf->file_name);
+				name = calloc(1, name_len);
+				if (!name) {
+					ret = EFI_OUT_OF_RESOURCES;
+					goto err;
+				}
+				u16_strcpy(name, buf->file_name);
+			}
+
+			info->file_name = u16_strdup(buf->file_name);
+			info->bo = bo;
+			iter->title = name;
+			iter->func = efi_bootmenu_file_selected;
+			iter->data = info;
+			iter++;
+		}
+
+		/* add "Quit" entry */
+		iter->title = u"Quit";
+		iter->func = NULL;
+		iter->data = NULL;
+		count += 1;
+
+		ret = efi_bootmenu_process_common(menu_item, count, -1);
+err:
+		efi_file_close_int(f);
+		iter = menu_item;
+		for (i = 0; i < count - 1; i++, iter++) {
+			free(((struct efi_bootmenu_file_entry_data *)(iter->data))->file_name);
+			free(iter->title);
+			free(iter->data);
+		}
+
+		free(menu_item);
+
+		if (ret != EFI_SUCCESS)
+			break;
+	}
+
+out:
+	free(buf);
+	return ret;
+}
+
+static efi_status_t efi_bootmenu_boot_add_enter_name(struct efi_bootmenu_boot_option *bo)
+{
+	efi_status_t ret;
+
+	printf(ANSI_CURSOR_POSITION, 2, 1);
+	puts("  *** U-Boot EFI Boot Manager Menu ***");
+	printf(ANSI_CURSOR_POSITION, 4, 1);
+	puts("  enter name:");
+
+	printf(ANSI_CURSOR_POSITION, 8, 1);
+	puts("  ENTER to complete, ESC/CTRL+C to quit");
+
+	ret = efi_console_get_u16_string(cin, cout, bo->boot_name,
+					 EFI_BOOTMENU_BOOT_NAME_MAX, NULL, 4, 15);
+
+	return ret;
+}
+
+static efi_status_t efi_bootmenu_select_file_handler(struct efi_bootmenu_boot_option *bo)
+{
+	efi_status_t ret;
+	struct efi_file_handle *root;
+
+	bo->file_selected = false;
+
+	while (!bo->file_selected) {
+		bo->current_volume = NULL;
+		memset(bo->current_path, 0, sizeof(bo->current_path));
+
+		ret = efi_bootmenu_select_volume(bo);
+		if (ret != EFI_SUCCESS)
+			return ret;
+
+		if (!bo->current_volume)
+			return EFI_INVALID_PARAMETER;
+
+		ret = efi_open_volume_int(bo->current_volume, &root);
+		if (ret != EFI_SUCCESS)
+			return ret;
+
+		ret = efi_bootmenu_select_file(bo, root);
+		if (ret != EFI_SUCCESS)
+			return ret;
+	}
+
+	ret = efi_bootmenu_boot_add_enter_name(bo);
+
+	return ret;
+}
+
+efi_status_t efi_bootmenu_get_unused_bootoption(u16 *buf,
+						efi_uintn_t buf_size, u32 *index)
+{
+	u32 i;
+	efi_status_t ret;
+	efi_uintn_t size;
+
+	if (buf_size < u16_strsize(u"Boot####"))
+		return EFI_BUFFER_TOO_SMALL;
+
+	for (i = 0; i <= 0xFFFF; i++) {
+		size = 0;
+		efi_create_indexed_name(buf, buf_size, "Boot", i);
+		ret = efi_get_variable_int(buf, &efi_global_variable_guid,
+					   NULL, &size, NULL, NULL);
+		if (ret == EFI_BUFFER_TOO_SMALL)
+			continue;
+		else
+			break;
+	}
+
+	if (i > 0xFFFF)
+		return EFI_OUT_OF_RESOURCES;
+
+	*index = i;
+
+	return EFI_SUCCESS;
+}
+
+static efi_status_t efi_bootmenu_set_boot_option(u16 *var_name, struct efi_device_path *dp,
+						 u16 *label, char *optional_data)
+{
+	void *p = NULL;
+	efi_status_t ret;
+	efi_uintn_t size;
+	struct efi_load_option lo;
+
+	lo.file_path = dp;
+	lo.file_path_length = efi_dp_size(dp) + sizeof(END);
+	lo.attributes = LOAD_OPTION_ACTIVE;
+	lo.optional_data = optional_data;
+	lo.label = label;
+
+	size = efi_serialize_load_option(&lo, (u8 **)&p);
+	if (!size)
+		return EFI_INVALID_PARAMETER;
+
+	ret = efi_set_variable_int(var_name, &efi_global_variable_guid,
+				   EFI_VARIABLE_NON_VOLATILE |
+				   EFI_VARIABLE_BOOTSERVICE_ACCESS |
+				   EFI_VARIABLE_RUNTIME_ACCESS,
+				   size, p, false);
+	free(p);
+
+	return ret;
+}
+
+efi_status_t efi_bootmenu_append_bootorder(u16 index)
+{
+	u16 *bootorder;
+	efi_status_t ret;
+	u16 *new_bootorder = NULL;
+	efi_uintn_t last, size, new_size;
+
+	/* append new boot option */
+	bootorder = efi_get_var(u"BootOrder", &efi_global_variable_guid, &size);
+	last = size / sizeof(u16);
+	new_size = size + sizeof(u16);
+	new_bootorder = calloc(1, new_size);
+	if (!new_bootorder) {
+		ret = EFI_OUT_OF_RESOURCES;
+		goto out;
+	}
+	memcpy(new_bootorder, bootorder, size);
+	new_bootorder[last] = index;
+
+	ret = efi_set_variable_int(u"BootOrder", &efi_global_variable_guid,
+				   EFI_VARIABLE_NON_VOLATILE |
+				   EFI_VARIABLE_BOOTSERVICE_ACCESS |
+				   EFI_VARIABLE_RUNTIME_ACCESS,
+				   new_size, new_bootorder, false);
+	if (ret != EFI_SUCCESS)
+		goto out;
+
+out:
+	free(bootorder);
+	free(new_bootorder);
+
+	return ret;
+}
+
+static efi_status_t efi_bootmenu_process_add_boot_option(void *data, bool *exit)
+{
+	u32 index;
+	u16 var_name[9];
+	char *buf = NULL;
+	efi_status_t ret;
+	char *iter = NULL;
+	efi_uintn_t dp_size, fp_size;
+	struct efi_bootmenu_boot_option bo;
+	struct efi_device_path_file_path *fp;
+
+	ret = efi_bootmenu_get_unused_bootoption(var_name, sizeof(var_name),
+						 &index);
+	if (ret != EFI_SUCCESS)
+		return ret;
+
+	bo.current_path = calloc(1, EFI_BOOTMENU_FILE_PATH_BUF_SIZE);
+	if (!bo.current_path)
+		goto out;
+
+	bo.boot_name = calloc(1, EFI_BOOTMENU_BOOT_NAME_MAX * sizeof(u16));
+	if (!bo.boot_name)
+		goto out;
+
+	ret = efi_bootmenu_select_file_handler(&bo);
+	if (ret != EFI_SUCCESS)
+		goto out;
+
+	dp_size = efi_dp_size(bo.dp_volume);
+	fp_size = sizeof(struct efi_device_path) +
+		  ((u16_strlen(bo.current_path) + 1) * sizeof(u16));
+	buf = calloc(1, dp_size + fp_size + sizeof(END));
+	if (!buf)
+		goto out;
+
+	iter = buf;
+	memcpy(iter, bo.dp_volume, dp_size);
+	iter += dp_size;
+
+	fp = (struct efi_device_path_file_path *)iter;
+	fp->dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE;
+	fp->dp.sub_type = DEVICE_PATH_SUB_TYPE_FILE_PATH;
+	fp->dp.length = (u16)fp_size;
+	u16_strcpy(fp->str, bo.current_path);
+	iter += fp_size;
+	*((struct efi_device_path *)iter) = END;
+
+	ret = efi_bootmenu_set_boot_option(var_name, (struct efi_device_path *)buf,
+					   bo.boot_name, NULL);
+	if (ret != EFI_SUCCESS)
+		goto out;
+
+	efi_bootmenu_append_bootorder((u16)index);
+	if (ret != EFI_SUCCESS)
+		goto out;
+
+out:
+	free(buf);
+	free(bo.boot_name);
+	free(bo.current_path);
+
+	return ret;
+}
+
+static efi_status_t efi_bootmenu_init(void)
+{
+	efi_status_t ret;
+	struct efi_handler *handler;
+
+	ret = efi_search_protocol(efi_root, &efi_guid_text_input_protocol, &handler);
+	if (ret != EFI_SUCCESS)
+		return ret;
+
+	ret = efi_protocol_open(handler, (void **)&cin, efi_root, NULL,
+				EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+	if (ret != EFI_SUCCESS)
+		return ret;
+
+	ret = efi_search_protocol(efi_root, &efi_guid_text_output_protocol, &handler);
+	if (ret != EFI_SUCCESS)
+		return ret;
+
+	ret = efi_protocol_open(handler, (void **)&cout, efi_root, NULL,
+				EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+	if (ret != EFI_SUCCESS)
+		return ret;
+
+	return ret;
+}
+
+static const struct efi_bootmenu_item maintenance_menu_items[] = {
+	{u"Add Boot Option", efi_bootmenu_process_add_boot_option},
+	{u"Quit", NULL},
+};
+
+efi_status_t efi_bootmenu_show_maintenance_menu(void)
+{
+	efi_status_t ret;
+
+	ret = efi_bootmenu_init();
+	if (ret != EFI_SUCCESS)
+		return ret;
+
+	return efi_bootmenu_process_common(maintenance_menu_items,
+					  ARRAY_SIZE(maintenance_menu_items),
+					  -1);
+}
diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c
index 4da64b5d29..1233418e77 100644
--- a/lib/efi_loader/efi_boottime.c
+++ b/lib/efi_loader/efi_boottime.c
@@ -2453,6 +2453,35 @@ static efi_status_t EFIAPI efi_protocols_per_handle(
 	return EFI_EXIT(EFI_SUCCESS);
 }
 
+efi_status_t efi_locate_handle_buffer_int(enum efi_locate_search_type search_type,
+					  const efi_guid_t *protocol, void *search_key,
+					  efi_uintn_t *no_handles, efi_handle_t **buffer)
+{
+	efi_status_t r;
+	efi_uintn_t buffer_size = 0;
+
+	if (!no_handles || !buffer) {
+		r = EFI_INVALID_PARAMETER;
+		goto out;
+	}
+	*no_handles = 0;
+	*buffer = NULL;
+	r = efi_locate_handle(search_type, protocol, search_key, &buffer_size,
+			      *buffer);
+	if (r != EFI_BUFFER_TOO_SMALL)
+		goto out;
+	r = efi_allocate_pool(EFI_BOOT_SERVICES_DATA, buffer_size,
+			      (void **)buffer);
+	if (r != EFI_SUCCESS)
+		goto out;
+	r = efi_locate_handle(search_type, protocol, search_key, &buffer_size,
+			      *buffer);
+	if (r == EFI_SUCCESS)
+		*no_handles = buffer_size / sizeof(efi_handle_t);
+out:
+	return r;
+}
+
 /**
  * efi_locate_handle_buffer() - locate handles implementing a protocol
  * @search_type: selection criterion
@@ -2474,30 +2503,13 @@ efi_status_t EFIAPI efi_locate_handle_buffer(
 			efi_uintn_t *no_handles, efi_handle_t **buffer)
 {
 	efi_status_t r;
-	efi_uintn_t buffer_size = 0;
 
 	EFI_ENTRY("%d, %pUs, %p, %p, %p", search_type, protocol, search_key,
 		  no_handles, buffer);
 
-	if (!no_handles || !buffer) {
-		r = EFI_INVALID_PARAMETER;
-		goto out;
-	}
-	*no_handles = 0;
-	*buffer = NULL;
-	r = efi_locate_handle(search_type, protocol, search_key, &buffer_size,
-			      *buffer);
-	if (r != EFI_BUFFER_TOO_SMALL)
-		goto out;
-	r = efi_allocate_pool(EFI_BOOT_SERVICES_DATA, buffer_size,
-			      (void **)buffer);
-	if (r != EFI_SUCCESS)
-		goto out;
-	r = efi_locate_handle(search_type, protocol, search_key, &buffer_size,
-			      *buffer);
-	if (r == EFI_SUCCESS)
-		*no_handles = buffer_size / sizeof(efi_handle_t);
-out:
+	r = efi_locate_handle_buffer_int(search_type, protocol, search_key,
+					 no_handles, buffer);
+
 	return EFI_EXIT(r);
 }
 
diff --git a/lib/efi_loader/efi_console.c b/lib/efi_loader/efi_console.c
index ba68a15017..f5002e1c99 100644
--- a/lib/efi_loader/efi_console.c
+++ b/lib/efi_loader/efi_console.c
@@ -5,6 +5,7 @@
  *  Copyright (c) 2016 Alexander Graf
  */
 
+#include <ansi.h>
 #include <common.h>
 #include <charset.h>
 #include <malloc.h>
@@ -1312,3 +1313,83 @@ out_of_memory:
 	printf("ERROR: Out of memory\n");
 	return r;
 }
+
+/**
+ * efi_console_get_u16_string() - get user input string
+ *
+ * @cin:		protocol interface to EFI_SIMPLE_TEXT_INPUT_PROTOCOL
+ * @cout:		protocol interface to EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
+ * @buf:		buffer to store user input string in UTF16
+ * @size:		buffer size including NULL terminator
+ * @filter_func:	callback to filter user input
+ * @row:		row number to locate user input form
+ * @col:		column number to locate user input form
+ * Return:		status code
+ */
+efi_status_t efi_console_get_u16_string(struct efi_simple_text_input_protocol *cin,
+					struct efi_simple_text_output_protocol *cout,
+					u16 *buf, efi_uintn_t size,
+					efi_console_filter_func filter_func,
+					int row, int col)
+{
+	efi_status_t ret;
+	efi_uintn_t len = 0;
+	struct efi_input_key key;
+
+	printf(ANSI_CURSOR_POSITION, row, col);
+	puts(ANSI_CLEAR_LINE_TO_END);
+	puts(ANSI_CURSOR_SHOW);
+
+	ret = EFI_CALL(cin->reset(cin, false));
+	if (ret != EFI_SUCCESS)
+		return ret;
+
+	for (;;) {
+		do {
+			ret = EFI_CALL(cin->read_key_stroke(cin, &key));
+			mdelay(10);
+		} while (ret == EFI_NOT_READY);
+
+		if (key.unicode_char == u'\b') {
+			if (len > 0)
+				buf[--len] = u'\0';
+
+			printf(ANSI_CURSOR_POSITION, row, col);
+			ret = EFI_CALL(cout->output_string(cout, buf));
+			if (ret != EFI_SUCCESS)
+				return ret;
+
+			puts(ANSI_CLEAR_LINE_TO_END);
+			continue;
+		} else if (key.unicode_char == u'\r') {
+			if (len == 0) /* no user input */
+				continue;
+
+			buf[len] = u'\0';
+			return EFI_SUCCESS;
+		} else if (key.unicode_char == 0x3 || key.scan_code == 23) {
+			return EFI_ABORTED;
+		} else if (key.unicode_char < 0x20) {
+			/* ignore control codes other than Ctrl+C, '\r' and '\b' */
+			continue;
+		} else if (key.scan_code != 0) {
+			/* only accept single ESC press for cancel */
+			continue;
+		}
+
+		if (filter_func) {
+			if (filter_func(&key) != EFI_SUCCESS)
+				continue;
+		}
+
+		if (len >= (size - 1))
+			continue;
+
+		buf[len] = key.unicode_char;
+		len++;
+		printf(ANSI_CURSOR_POSITION, row, col);
+		ret = EFI_CALL(cout->output_string(cout, buf));
+		if (ret != EFI_SUCCESS)
+			return ret;
+	}
+}
diff --git a/lib/efi_loader/efi_disk.c b/lib/efi_loader/efi_disk.c
index 8fb5b2363c..58736a8a5b 100644
--- a/lib/efi_loader/efi_disk.c
+++ b/lib/efi_loader/efi_disk.c
@@ -750,3 +750,14 @@ efi_status_t efi_disk_init(void)
 
 	return EFI_SUCCESS;
 }
+
+efi_status_t efi_disk_get_device_name(struct efi_block_io *this, char *buf, int size)
+{
+	struct efi_disk_obj *diskobj;
+
+	diskobj = container_of(this, struct efi_disk_obj, ops);
+
+	snprintf(buf, size, "%s%d:%d", diskobj->ifname, diskobj->dev_index, diskobj->part);
+
+	return EFI_SUCCESS;
+}
diff --git a/lib/efi_loader/efi_file.c b/lib/efi_loader/efi_file.c
index 7a7077e6d0..c96a7f7ca3 100644
--- a/lib/efi_loader/efi_file.c
+++ b/lib/efi_loader/efi_file.c
@@ -246,10 +246,10 @@ error:
 	return NULL;
 }
 
-static efi_status_t efi_file_open_int(struct efi_file_handle *this,
-				      struct efi_file_handle **new_handle,
-				      u16 *file_name, u64 open_mode,
-				      u64 attributes)
+efi_status_t efi_file_open_int(struct efi_file_handle *this,
+			       struct efi_file_handle **new_handle,
+			       u16 *file_name, u64 open_mode,
+			       u64 attributes)
 {
 	struct file_handle *fh = to_fh(this);
 	efi_status_t ret;
@@ -369,11 +369,17 @@ static efi_status_t file_close(struct file_handle *fh)
 	return EFI_SUCCESS;
 }
 
-static efi_status_t EFIAPI efi_file_close(struct efi_file_handle *file)
+efi_status_t efi_file_close_int(struct efi_file_handle *file)
 {
 	struct file_handle *fh = to_fh(file);
+
+	return file_close(fh);
+}
+
+static efi_status_t EFIAPI efi_file_close(struct efi_file_handle *file)
+{
 	EFI_ENTRY("%p", file);
-	return EFI_EXIT(file_close(fh));
+	return EFI_EXIT(efi_file_close_int(file));
 }
 
 static efi_status_t EFIAPI efi_file_delete(struct efi_file_handle *file)
@@ -562,8 +568,8 @@ static efi_status_t dir_read(struct file_handle *fh, u64 *buffer_size,
 	return EFI_SUCCESS;
 }
 
-static efi_status_t efi_file_read_int(struct efi_file_handle *this,
-				      efi_uintn_t *buffer_size, void *buffer)
+efi_status_t efi_file_read_int(struct efi_file_handle *this,
+			       efi_uintn_t *buffer_size, void *buffer)
 {
 	struct file_handle *fh = to_fh(this);
 	efi_status_t ret = EFI_SUCCESS;
@@ -773,24 +779,11 @@ out:
 	return EFI_EXIT(ret);
 }
 
-/**
- * efi_file_setpos() - set current position in file
- *
- * This function implements the SetPosition service of the EFI file protocol.
- * See the UEFI spec for details.
- *
- * @file:	file handle
- * @pos:	new file position
- * Return:	status code
- */
-static efi_status_t EFIAPI efi_file_setpos(struct efi_file_handle *file,
-					   u64 pos)
+efi_status_t efi_file_setpos_int(struct efi_file_handle *file, u64 pos)
 {
 	struct file_handle *fh = to_fh(file);
 	efi_status_t ret = EFI_SUCCESS;
 
-	EFI_ENTRY("%p, %llu", file, pos);
-
 	if (fh->isdir) {
 		if (pos != 0) {
 			ret = EFI_UNSUPPORTED;
@@ -812,6 +805,28 @@ static efi_status_t EFIAPI efi_file_setpos(struct efi_file_handle *file,
 	fh->offset = pos;
 
 error:
+	return ret;
+}
+
+/**
+ * efi_file_setpos() - set current position in file
+ *
+ * This function implements the SetPosition service of the EFI file protocol.
+ * See the UEFI spec for details.
+ *
+ * @file:	file handle
+ * @pos:	new file position
+ * Return:	status code
+ */
+static efi_status_t EFIAPI efi_file_setpos(struct efi_file_handle *file,
+					   u64 pos)
+{
+	efi_status_t ret = EFI_SUCCESS;
+
+	EFI_ENTRY("%p, %llu", file, pos);
+
+	ret = efi_file_setpos_int(file, pos);
+
 	return EFI_EXIT(ret);
 }
 
@@ -1138,17 +1153,23 @@ struct efi_file_handle *efi_file_from_path(struct efi_device_path *fp)
 	return f;
 }
 
+efi_status_t efi_open_volume_int(struct efi_simple_file_system_protocol *this,
+				 struct efi_file_handle **root)
+{
+	struct file_system *fs = to_fs(this);
+
+	*root = file_open(fs, NULL, NULL, 0, 0);
+
+	return EFI_SUCCESS;
+}
+
 static efi_status_t EFIAPI
 efi_open_volume(struct efi_simple_file_system_protocol *this,
 		struct efi_file_handle **root)
 {
-	struct file_system *fs = to_fs(this);
-
 	EFI_ENTRY("%p, %p", this, root);
 
-	*root = file_open(fs, NULL, NULL, 0, 0);
-
-	return EFI_EXIT(EFI_SUCCESS);
+	return EFI_EXIT(efi_open_volume_int(this, root));
 }
 
 struct efi_simple_file_system_protocol *
-- 
2.17.1


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

* [PATCH v5 14/17] efi_loader: menu-driven deletion of UEFI boot variable
  2022-04-28  8:09 [PATCH v5 00/17] enable menu-driven boot device selection Masahisa Kojima
                   ` (12 preceding siblings ...)
  2022-04-28  8:09 ` [PATCH v5 13/17] efi_loader: menu-driven addition of UEFI boot option Masahisa Kojima
@ 2022-04-28  8:09 ` Masahisa Kojima
  2022-04-28  8:09 ` [PATCH v5 15/17] efi_loader: menu-driven update of UEFI bootorder variable Masahisa Kojima
                   ` (4 subsequent siblings)
  18 siblings, 0 replies; 58+ messages in thread
From: Masahisa Kojima @ 2022-04-28  8:09 UTC (permalink / raw)
  To: u-boot
  Cc: Heinrich Schuchardt, Ilias Apalodimas, Simon Glass,
	Takahiro Akashi, Francois Ozog, Mark Kettenis, Masahisa Kojima

This commit adds the menu-driven deletion of UEFI boot variable.

Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
---
Changes in v5:
- split into the separate patch

 lib/efi_loader/efi_bootmenu_maintenance.c | 142 ++++++++++++++++++++++
 1 file changed, 142 insertions(+)

diff --git a/lib/efi_loader/efi_bootmenu_maintenance.c b/lib/efi_loader/efi_bootmenu_maintenance.c
index 77401a7829..26320a8b73 100644
--- a/lib/efi_loader/efi_bootmenu_maintenance.c
+++ b/lib/efi_loader/efi_bootmenu_maintenance.c
@@ -70,6 +70,12 @@ struct efi_bootmenu_item {
 	void *data;
 };
 
+struct efi_bootmenu_boot_selection_data {
+	u16 bootorder_index;
+	void *load_option;
+	int *selected;
+};
+
 struct efi_bootmenu_boot_option {
 	struct efi_simple_file_system_protocol *current_volume;
 	struct efi_device_path *dp_volume;
@@ -322,6 +328,89 @@ out:
 	return ret;
 }
 
+static efi_status_t efi_bootmenu_process_boot_selected(void *data, bool *exit)
+{
+	struct efi_bootmenu_boot_selection_data *info = data;
+
+	*exit = true;
+
+	if (info)
+		*info->selected = info->bootorder_index;
+
+	return EFI_SUCCESS;
+}
+
+static efi_status_t efi_bootmenu_show_boot_selection(u16 *bootorder, efi_uintn_t count,
+						     int *selected)
+{
+	u32 i;
+	efi_status_t ret;
+	efi_uintn_t size;
+	void *load_option;
+	struct efi_load_option lo;
+	u16 varname[] = u"Boot####";
+	struct efi_bootmenu_item *menu_item, *iter;
+
+	menu_item = calloc(count + 1, sizeof(struct efi_bootmenu_item));
+	if (!menu_item) {
+		ret = EFI_OUT_OF_RESOURCES;
+		goto out;
+	}
+
+	iter = menu_item;
+	for (i = 0; i < count; i++) {
+		efi_create_indexed_name(varname, sizeof(varname),
+					"Boot", bootorder[i]);
+		load_option = efi_get_var(varname, &efi_global_variable_guid, &size);
+		if (!load_option)
+			continue;
+
+		ret = efi_deserialize_load_option(&lo, load_option, &size);
+		if (ret != EFI_SUCCESS) {
+			log_warning("Invalid load option for %ls\n", varname);
+			free(load_option);
+			continue;
+		}
+
+		if (lo.attributes & LOAD_OPTION_ACTIVE) {
+			struct efi_bootmenu_boot_selection_data *info;
+
+			info = calloc(1, sizeof(struct efi_bootmenu_boot_selection_data));
+			if (!info) {
+				ret = EFI_OUT_OF_RESOURCES;
+				goto out;
+			}
+
+			info->bootorder_index = i;
+			info->load_option = load_option;
+			info->selected = selected;
+			iter->title = lo.label;
+			iter->func = efi_bootmenu_process_boot_selected;
+			iter->data = info;
+			iter++;
+		}
+	}
+
+	/* add "Quit" entry */
+	iter->title = u"Quit";
+	iter->func = NULL;
+	iter->data = NULL;
+	count += 1;
+
+	ret = efi_bootmenu_process_common(menu_item, count, -1);
+
+out:
+	iter = menu_item;
+	for (i = 0; i < count - 1; i++, iter++) {
+		free(((struct efi_bootmenu_boot_selection_data *)iter->data)->load_option);
+		free(iter->data);
+	}
+
+	free(menu_item);
+
+	return ret;
+}
+
 static efi_status_t efi_bootmenu_volume_selected(void *data, bool *exit)
 {
 	struct efi_bootmenu_volume_entry_data *info = data;
@@ -817,6 +906,58 @@ out:
 	return ret;
 }
 
+static efi_status_t delete_boot_option(u16 *bootorder, u16 index, efi_uintn_t size)
+{
+	u16 var_name[9];
+	efi_status_t ret;
+	efi_uintn_t num;
+
+	efi_create_indexed_name(var_name, sizeof(var_name),
+				"Boot", bootorder[index]);
+	ret = efi_set_variable_int(var_name, &efi_global_variable_guid,
+				   0, 0, NULL, false);
+	if (ret != EFI_SUCCESS) {
+		log_err("delete boot option(%ls) failed\n", var_name);
+		return ret;
+	}
+
+	/* update BootOrder */
+	num = size / sizeof(u16);
+	memmove(&bootorder[index], &bootorder[index + 1],
+		(num - index - 1) * sizeof(u16));
+	size -= sizeof(u16);
+	ret = efi_set_variable_int(u"BootOrder", &efi_global_variable_guid,
+				   EFI_VARIABLE_NON_VOLATILE |
+				   EFI_VARIABLE_BOOTSERVICE_ACCESS |
+				   EFI_VARIABLE_RUNTIME_ACCESS,
+				   size, bootorder, false);
+
+	return ret;
+}
+
+static efi_status_t efi_bootmenu_process_delete_boot_option(void *data, bool *exit)
+{
+	int selected;
+	u16 *bootorder;
+	efi_status_t ret;
+	efi_uintn_t num, size;
+
+	bootorder = efi_get_var(u"BootOrder", &efi_global_variable_guid, &size);
+	if (!bootorder) {
+		ret = EFI_NOT_FOUND;
+		return ret;
+	}
+
+	num = size / sizeof(u16);
+	ret = efi_bootmenu_show_boot_selection(bootorder, num, &selected);
+	if (ret == EFI_SUCCESS)
+		ret = delete_boot_option(bootorder, selected, size);
+
+	free(bootorder);
+
+	return ret;
+}
+
 static efi_status_t efi_bootmenu_init(void)
 {
 	efi_status_t ret;
@@ -845,6 +986,7 @@ static efi_status_t efi_bootmenu_init(void)
 
 static const struct efi_bootmenu_item maintenance_menu_items[] = {
 	{u"Add Boot Option", efi_bootmenu_process_add_boot_option},
+	{u"Delete Boot Option", efi_bootmenu_process_delete_boot_option},
 	{u"Quit", NULL},
 };
 
-- 
2.17.1


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

* [PATCH v5 15/17] efi_loader: menu-driven update of UEFI bootorder variable
  2022-04-28  8:09 [PATCH v5 00/17] enable menu-driven boot device selection Masahisa Kojima
                   ` (13 preceding siblings ...)
  2022-04-28  8:09 ` [PATCH v5 14/17] efi_loader: menu-driven deletion of UEFI boot variable Masahisa Kojima
@ 2022-04-28  8:09 ` Masahisa Kojima
  2022-04-28  8:09 ` [PATCH v5 16/17] bootmenu: add removable media entries Masahisa Kojima
                   ` (3 subsequent siblings)
  18 siblings, 0 replies; 58+ messages in thread
From: Masahisa Kojima @ 2022-04-28  8:09 UTC (permalink / raw)
  To: u-boot
  Cc: Heinrich Schuchardt, Ilias Apalodimas, Simon Glass,
	Takahiro Akashi, Francois Ozog, Mark Kettenis, Masahisa Kojima

This commit adds the menu-driven update of UEFI bootorder
variable.

Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
---
Changes in v5:
- split into the separate patch

 lib/efi_loader/efi_bootmenu_maintenance.c | 102 ++++++++++++++++++++++
 1 file changed, 102 insertions(+)

diff --git a/lib/efi_loader/efi_bootmenu_maintenance.c b/lib/efi_loader/efi_bootmenu_maintenance.c
index 26320a8b73..8c3f94c695 100644
--- a/lib/efi_loader/efi_bootmenu_maintenance.c
+++ b/lib/efi_loader/efi_bootmenu_maintenance.c
@@ -719,6 +719,56 @@ static efi_status_t efi_bootmenu_boot_add_enter_name(struct efi_bootmenu_boot_op
 	return ret;
 }
 
+static efi_status_t allow_decimal(struct efi_input_key *key)
+{
+	if (u'0' <= key->unicode_char && key->unicode_char <= u'9')
+		return EFI_SUCCESS;
+
+	return EFI_INVALID_PARAMETER;
+}
+
+static efi_status_t efi_bootmenu_change_boot_order(int selected, int max, int *new)
+{
+	efi_status_t ret;
+	u16 new_order[EFI_BOOT_ORDER_MAX_SIZE_IN_DECIMAL] = {0};
+
+	printf(ANSI_CURSOR_POSITION, 2, 1);
+	puts("  *** U-Boot EFI Boot Manager Menu ***");
+	printf(ANSI_CURSOR_POSITION, 4, 1);
+	printf("  current boot order      : %d", selected);
+
+	printf(ANSI_CURSOR_POSITION, 6, 1);
+	printf("  new boot order(0 - %4d): ", max);
+
+	printf(ANSI_CURSOR_POSITION, 8, 1);
+	puts("  ENTER to complete, ESC/CTRL+C to quit");
+
+	printf(ANSI_CURSOR_POSITION, 6, 29);
+	puts(ANSI_CURSOR_SHOW);
+
+	for (;;) {
+		memset(new_order, 0, sizeof(new_order));
+		ret = efi_console_get_u16_string(cin, cout, new_order, 6, allow_decimal, 6, 29);
+		if (ret == EFI_SUCCESS) {
+			int i;
+			int val = 0;
+
+			for (i = 0;
+			     i < u16_strnlen(new_order, EFI_BOOT_ORDER_MAX_SIZE_IN_DECIMAL - 1);
+			     i++)
+				val = (val * 10) + (new_order[i] - u'0');
+
+			if (val > max)
+				continue;
+
+			*new = val;
+			return EFI_SUCCESS;
+		} else {
+			return ret;
+		}
+	}
+}
+
 static efi_status_t efi_bootmenu_select_file_handler(struct efi_bootmenu_boot_option *bo)
 {
 	efi_status_t ret;
@@ -958,6 +1008,57 @@ static efi_status_t efi_bootmenu_process_delete_boot_option(void *data, bool *ex
 	return ret;
 }
 
+static efi_status_t efi_bootmenu_process_change_boot_order(void *data, bool *exit)
+{
+	int selected;
+	int new_order;
+	efi_status_t ret;
+	efi_uintn_t num, size;
+	u16 *bootorder = NULL;
+	u16 *new_bootorder = NULL;
+
+	bootorder = efi_get_var(u"BootOrder", &efi_global_variable_guid, &size);
+	if (!bootorder)
+		return EFI_NOT_FOUND;
+
+	num = size / sizeof(u16);
+	ret = efi_bootmenu_show_boot_selection(bootorder, num, &selected);
+	if (ret != EFI_SUCCESS)
+		goto out;
+
+	ret = efi_bootmenu_change_boot_order(selected, num - 1, &new_order);
+	if (ret != EFI_SUCCESS)
+		goto out;
+
+	new_bootorder = calloc(1, size);
+	if (!new_bootorder)
+		goto out;
+
+	memcpy(new_bootorder, bootorder, size);
+	if (selected > new_order) {
+		new_bootorder[new_order] = bootorder[selected];
+		memcpy(&new_bootorder[new_order + 1], &bootorder[new_order],
+		       (selected - new_order) * sizeof(u16));
+	} else if (selected < new_order) {
+		new_bootorder[new_order] = bootorder[selected];
+		memcpy(&new_bootorder[selected], &bootorder[selected + 1],
+		       (new_order - selected) * sizeof(u16));
+	} else {
+		/* nothing to change */
+		goto out;
+	}
+	ret = efi_set_variable_int(u"BootOrder", &efi_global_variable_guid,
+				   EFI_VARIABLE_NON_VOLATILE |
+				   EFI_VARIABLE_BOOTSERVICE_ACCESS |
+				   EFI_VARIABLE_RUNTIME_ACCESS,
+				   size, new_bootorder, false);
+out:
+	free(new_bootorder);
+	free(bootorder);
+
+	return ret;
+}
+
 static efi_status_t efi_bootmenu_init(void)
 {
 	efi_status_t ret;
@@ -987,6 +1088,7 @@ static efi_status_t efi_bootmenu_init(void)
 static const struct efi_bootmenu_item maintenance_menu_items[] = {
 	{u"Add Boot Option", efi_bootmenu_process_add_boot_option},
 	{u"Delete Boot Option", efi_bootmenu_process_delete_boot_option},
+	{u"Change Boot Order", efi_bootmenu_process_change_boot_order},
 	{u"Quit", NULL},
 };
 
-- 
2.17.1


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

* [PATCH v5 16/17] bootmenu: add removable media entries
  2022-04-28  8:09 [PATCH v5 00/17] enable menu-driven boot device selection Masahisa Kojima
                   ` (14 preceding siblings ...)
  2022-04-28  8:09 ` [PATCH v5 15/17] efi_loader: menu-driven update of UEFI bootorder variable Masahisa Kojima
@ 2022-04-28  8:09 ` Masahisa Kojima
  2022-04-28 16:53   ` Heinrich Schuchardt
  2022-04-28  8:09 ` [PATCH v5 17/17] doc:bootmenu: add UEFI boot and distro boot support description Masahisa Kojima
                   ` (2 subsequent siblings)
  18 siblings, 1 reply; 58+ messages in thread
From: Masahisa Kojima @ 2022-04-28  8:09 UTC (permalink / raw)
  To: u-boot
  Cc: Heinrich Schuchardt, Ilias Apalodimas, Simon Glass,
	Takahiro Akashi, Francois Ozog, Mark Kettenis, Masahisa Kojima

UEFI specification requires booting from removal media using
a architecture-specific default image name such as BOOTAA64.EFI.
This commit adds the removable media entries into bootmenu,
so that user can select the removable media and boot with
default image.

The bootmenu automatically enumerates the possible bootable
media devices supporting EFI_SIMPLE_FILE_SYSTEM_PROTOCOL,
add it as new UEFI boot option(BOOT####) and update BootOrder
variable. This automatically generated UEFI boot option
has the dedicated guid in the optional_data to distinguish it from
the UEFI boot option user adds manually.

This commit also provides the BOOT#### variable maintenance feature.
Depending on the system hardware setup, some devices
may not exist at a later system boot, so bootmenu checks the
available device in each bootmenu invocation and automatically
removes the BOOT#### variable corrensponding to the non-existent
media device.

Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
---
Changes in v5:
- Return EFI_SUCCESS if there is no BootOrder defined
- correctly handle the case if no removable device found
- use guid to identify the automatically generated entry by bootmenu

Newly created in v4

 cmd/bootmenu.c                            |  94 +++++++++++++++
 include/efi_loader.h                      |  20 ++++
 lib/efi_loader/efi_bootmenu_maintenance.c | 139 ++++++++++++++++++++++
 3 files changed, 253 insertions(+)

diff --git a/cmd/bootmenu.c b/cmd/bootmenu.c
index 860cb83182..970db3ee01 100644
--- a/cmd/bootmenu.c
+++ b/cmd/bootmenu.c
@@ -396,6 +396,89 @@ static int is_blk_device_available(char *token)
 	return -ENODEV;
 }
 
+/**
+ * prepare_media_device_entry() - generate the media device entries
+ *
+ * This function enumerates all devices supporting EFI_SIMPLE_FILE_SYSTEM_PROTOCOL
+ * and generate the bootmenu entries.
+ * This function also provide the BOOT#### variable maintenance for
+ * the media device entries.
+ *   - Automatically create the BOOT#### variable for the newly detected device,
+ *     this BOOT#### variable is distinguished by the special GUID
+ *     stored in the EFI_LOAD_OPTION.optional_data
+ *   - If the device is not attached to the system, the associated BOOT#### variable
+ *     is automatically deleted.
+ *
+ * Return:	status code
+ */
+static efi_status_t prepare_media_device_entry(void)
+{
+	u32 i;
+	efi_status_t ret;
+	efi_uintn_t count;
+	efi_handle_t *volume_handles = NULL;
+	struct efi_bootmenu_media_boot_option *opt = NULL;
+
+	ret = efi_locate_handle_buffer_int(BY_PROTOCOL, &efi_simple_file_system_protocol_guid,
+					   NULL, &count, (efi_handle_t **)&volume_handles);
+	if (ret != EFI_SUCCESS)
+		return ret;
+
+	opt = calloc(count, sizeof(struct efi_bootmenu_media_boot_option));
+	if (!opt)
+		goto out;
+
+	/* enumerate all devices supporting EFI_SIMPLE_FILE_SYSTEM_PROTOCOL */
+	ret = efi_bootmenu_enumerate_boot_option(opt, volume_handles, count);
+	if (ret != EFI_SUCCESS)
+		goto out;
+
+	/*
+	 * System hardware configuration may vary depending on the user setup.
+	 * The boot option is automatically added by the bootmenu.
+	 * If the device is not attached to the system, the boot option needs
+	 * to be deleted.
+	 */
+	ret = efi_bootmenu_delete_invalid_boot_option(opt, count);
+	if (ret != EFI_SUCCESS)
+		goto out;
+
+	/* add non-existent boot option */
+	for (i = 0; i < count; i++) {
+		u32 boot_index;
+		u16 var_name[9];
+
+		if (!opt[i].exist) {
+			ret = efi_bootmenu_get_unused_bootoption(var_name, sizeof(var_name),
+								 &boot_index);
+			if (ret != EFI_SUCCESS)
+				goto out;
+
+			ret = efi_set_variable_int(var_name, &efi_global_variable_guid,
+						   EFI_VARIABLE_NON_VOLATILE |
+						   EFI_VARIABLE_BOOTSERVICE_ACCESS |
+						   EFI_VARIABLE_RUNTIME_ACCESS,
+						   opt[i].size, opt[i].lo, false);
+			if (ret != EFI_SUCCESS)
+				goto out;
+
+			ret = efi_bootmenu_append_bootorder(boot_index);
+			if (ret != EFI_SUCCESS)
+				goto out;
+		}
+	}
+
+out:
+	if (opt) {
+		for (i = 0; i < count; i++)
+			free(opt[i].lo);
+	}
+	free(opt);
+	efi_free_pool(volume_handles);
+
+	return ret;
+}
+
 /**
  * prepare_distro_boot_entry() - generate the distro boot entries
  *
@@ -500,6 +583,7 @@ static int prepare_distro_boot_entry(struct bootmenu_data *menu,
 static struct bootmenu_data *bootmenu_create(int delay)
 {
 	int ret;
+	efi_status_t efi_ret;
 	unsigned short int i = 0;
 	struct bootmenu_data *menu;
 	struct bootmenu_entry *iter = NULL;
@@ -523,6 +607,16 @@ static struct bootmenu_data *bootmenu_create(int delay)
 		goto cleanup;
 
 	if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR)) {
+		if (i < MAX_DYNAMIC_ENTRY) {
+			/*
+			 * UEFI specification requires booting from removal media using
+			 * a architecture-specific default image name such as BOOTAA64.EFI.
+			 */
+			efi_ret = prepare_media_device_entry();
+			if (efi_ret != EFI_SUCCESS && efi_ret != EFI_NOT_FOUND)
+				goto cleanup;
+		}
+
 		if (i < MAX_DYNAMIC_ENTRY) {
 			ret = prepare_uefi_bootorder_entry(menu, &iter, &i);
 			if (ret < 0 && ret != -ENOENT)
diff --git a/include/efi_loader.h b/include/efi_loader.h
index 533618341b..bef492ccb9 100644
--- a/include/efi_loader.h
+++ b/include/efi_loader.h
@@ -928,6 +928,22 @@ struct efi_signature_store {
 struct x509_certificate;
 struct pkcs7_message;
 
+/**
+ * struct efi_bootmenu_media_boot_option - boot option for (removable) media device
+ *
+ * This structure is used to enumerate possible boot option
+ *
+ * @lo:		Serialized load option data
+ * @size:	Size of serialized load option data
+ * @exist:	Flag to indicate the load option already exists
+ *		in Non-volatile load option
+ */
+struct efi_bootmenu_media_boot_option {
+	void *lo;
+	efi_uintn_t size;
+	bool exist;
+};
+
 bool efi_signature_lookup_digest(struct efi_image_regions *regs,
 				 struct efi_signature_store *db,
 				 bool dbx);
@@ -1076,6 +1092,10 @@ efi_status_t efi_console_get_u16_string
 efi_status_t efi_bootmenu_get_unused_bootoption(u16 *buf,
 						efi_uintn_t buf_size, u32 *index);
 efi_status_t efi_bootmenu_append_bootorder(u16 index);
+efi_status_t efi_bootmenu_enumerate_boot_option(struct efi_bootmenu_media_boot_option *opt,
+						efi_handle_t *volume_handles, efi_status_t count);
+efi_status_t efi_bootmenu_delete_invalid_boot_option(struct efi_bootmenu_media_boot_option *opt,
+						     efi_status_t count);
 
 efi_status_t efi_disk_get_device_name(struct efi_block_io *this, char *buf, int size);
 
diff --git a/lib/efi_loader/efi_bootmenu_maintenance.c b/lib/efi_loader/efi_bootmenu_maintenance.c
index 8c3f94c695..33b37fd11a 100644
--- a/lib/efi_loader/efi_bootmenu_maintenance.c
+++ b/lib/efi_loader/efi_bootmenu_maintenance.c
@@ -26,6 +26,13 @@ static struct efi_simple_text_output_protocol *cout;
 #define EFI_BOOTMENU_BOOT_NAME_MAX 32
 #define EFI_BOOT_ORDER_MAX_SIZE_IN_DECIMAL 6
 
+#define EFI_BOOTMENU_AUTO_GENERATED_ENTRY_GUID \
+	EFI_GUID(0x38c1acc1, 0x9fc0, 0x41f0, \
+		 0xb9, 0x01, 0xfa, 0x74, 0xd6, 0xd6, 0xe4, 0xde)
+
+static const efi_guid_t efi_guid_bootmenu_auto_generated =
+		EFI_BOOTMENU_AUTO_GENERATED_ENTRY_GUID;
+
 typedef efi_status_t (*efi_bootmenu_entry_func)(void *data, bool *exit);
 
 /**
@@ -1104,3 +1111,135 @@ efi_status_t efi_bootmenu_show_maintenance_menu(void)
 					  ARRAY_SIZE(maintenance_menu_items),
 					  -1);
 }
+
+efi_status_t efi_bootmenu_enumerate_boot_option(struct efi_bootmenu_media_boot_option *opt,
+						efi_handle_t *volume_handles, efi_status_t count)
+{
+	u32 i;
+	struct efi_handler *handler;
+	efi_status_t ret = EFI_SUCCESS;
+
+	for (i = 0; i < count; i++) {
+		char *optional_data;
+		u16 *dev_name, *p;
+		struct efi_load_option lo;
+		struct efi_block_io *block_io;
+		char buf[BOOTMENU_DEVICE_NAME_MAX];
+		struct efi_device_path *device_path;
+
+		ret = efi_search_protocol(volume_handles[i], &efi_guid_device_path, &handler);
+		if (ret != EFI_SUCCESS)
+			continue;
+		ret = efi_protocol_open(handler, (void **)&device_path,
+					efi_root, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+		if (ret != EFI_SUCCESS)
+			continue;
+
+		ret = efi_search_protocol(volume_handles[i], &efi_block_io_guid, &handler);
+		if (ret != EFI_SUCCESS)
+			continue;
+		ret = efi_protocol_open(handler, (void **)&block_io,
+					efi_root, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+		if (ret != EFI_SUCCESS)
+			continue;
+
+		efi_disk_get_device_name(block_io, buf, BOOTMENU_DEVICE_NAME_MAX);
+		dev_name = calloc(1, (strlen(buf) + 1) * sizeof(u16));
+		if (!dev_name) {
+			ret = EFI_OUT_OF_RESOURCES;
+			goto out;
+		}
+		p = dev_name;
+		utf8_utf16_strncpy(&p, buf, strlen(buf));
+
+		lo.label = dev_name;
+		lo.attributes = LOAD_OPTION_ACTIVE;
+		lo.file_path = device_path;
+		lo.file_path_length = efi_dp_size(device_path) + sizeof(END);
+		/*
+		 * Set the dedicated guid to optional_data, it is used to identify
+		 * the boot option that automatically generated by the bootmenu.
+		 * efi_serialize_load_option() expects optional_data is null-terminated
+		 * utf8 string, so set the "dummystr" string to allocate enough space
+		 * to store guid, instead of realloc the load_option.
+		 *
+		 * This will allocate 16 bytes for guid plus trailing 0x0000.
+		 * The guid does not require trailing 0x0000, but it is for safety
+		 * in case some program handle the optional_data as u16 string.
+		 */
+		lo.optional_data = "dummystr";
+		opt[i].size = efi_serialize_load_option(&lo, (u8 **)&opt[i].lo);
+		if (!opt[i].size) {
+			ret = EFI_OUT_OF_RESOURCES;
+			free(dev_name);
+			goto out;
+		}
+		/* set the guid */
+		optional_data = (char *)opt[i].lo + (opt[i].size - u16_strsize(u"dummystr"));
+		memcpy(optional_data, &efi_guid_bootmenu_auto_generated, sizeof(efi_guid_t));
+		free(dev_name);
+	}
+
+out:
+	return ret;
+}
+
+efi_status_t efi_bootmenu_delete_invalid_boot_option(struct efi_bootmenu_media_boot_option *opt,
+						     efi_status_t count)
+{
+	u16 *bootorder;
+	u32 i, j;
+	efi_status_t ret;
+	efi_uintn_t num, size, bootorder_size;
+	void *load_option;
+	struct efi_load_option lo;
+	u16 varname[] = u"Boot####";
+
+	bootorder = efi_get_var(u"BootOrder", &efi_global_variable_guid, &bootorder_size);
+	if (!bootorder)
+		return EFI_SUCCESS; /* BootOrder is not defined, nothing to do */
+
+	num = bootorder_size / sizeof(u16);
+	for (i = 0; i < num;) {
+		efi_uintn_t tmp;
+
+		efi_create_indexed_name(varname, sizeof(varname),
+					"Boot", bootorder[i]);
+		load_option = efi_get_var(varname, &efi_global_variable_guid, &size);
+		if (!load_option)
+			goto next;
+
+		tmp = size;
+		ret = efi_deserialize_load_option(&lo, load_option, &tmp);
+		if (ret != EFI_SUCCESS)
+			goto next;
+
+		if (guidcmp(lo.optional_data, &efi_guid_bootmenu_auto_generated) == 0) {
+			for (j = 0; j < count; j++) {
+				if (memcmp(opt[j].lo, load_option, size) == 0) {
+					opt[j].exist = true;
+					break;
+				}
+			}
+
+			if (j == count) {
+				ret = delete_boot_option(bootorder, i, bootorder_size);
+				if (ret != EFI_SUCCESS) {
+					free(load_option);
+					goto out;
+				}
+
+				num--;
+				bootorder_size -= sizeof(u16);
+				free(load_option);
+				continue;
+			}
+		}
+next:
+		free(load_option);
+		i++;
+	}
+
+out:
+	return ret;
+}
-- 
2.17.1


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

* [PATCH v5 17/17] doc:bootmenu: add UEFI boot and distro boot support description
  2022-04-28  8:09 [PATCH v5 00/17] enable menu-driven boot device selection Masahisa Kojima
                   ` (15 preceding siblings ...)
  2022-04-28  8:09 ` [PATCH v5 16/17] bootmenu: add removable media entries Masahisa Kojima
@ 2022-04-28  8:09 ` Masahisa Kojima
  2022-04-28 16:31 ` [PATCH v5 00/17] enable menu-driven boot device selection Heinrich Schuchardt
  2022-04-29  8:45 ` Mark Kettenis
  18 siblings, 0 replies; 58+ messages in thread
From: Masahisa Kojima @ 2022-04-28  8:09 UTC (permalink / raw)
  To: u-boot
  Cc: Heinrich Schuchardt, Ilias Apalodimas, Simon Glass,
	Takahiro Akashi, Francois Ozog, Mark Kettenis, Masahisa Kojima,
	Bin Meng

The bootmenu enumerates the UEFI boot options and distro boot
(boot_targets) for boot device selection.

This commit adds the description how the UEFI boot and distro boot
work in bootmenu. This commit also adds "Synopsis", "Description"
and "Configuration" sections to follow the U-Boot command
documentation format.

Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
---
Changes in v5:
- follow the cmd documentation format same as other command, add "Synopsis",
  "Description" add "Configuration" sections

Newly created in v4

 doc/usage/cmd/bootmenu.rst | 78 +++++++++++++++++++++++++++++++++++++-
 1 file changed, 77 insertions(+), 1 deletion(-)

diff --git a/doc/usage/cmd/bootmenu.rst b/doc/usage/cmd/bootmenu.rst
index 1016ac8ceb..48f76a82a3 100644
--- a/doc/usage/cmd/bootmenu.rst
+++ b/doc/usage/cmd/bootmenu.rst
@@ -4,7 +4,17 @@
 bootmenu command
 ================
 
-The "bootmenu" command uses U-Boot menu interfaces and provides
+Synopsis
+--------
+
+::
+
+    bootmenu
+
+Description
+-----------
+
+The *bootmenu* command uses U-Boot menu interfaces and provides
 a simple mechanism for creating menus with different boot items.
 The cursor keys "Up" and "Down" are used for navigation through
 the items. Current active menu item is highlighted and can be
@@ -79,6 +89,67 @@ The above example will be rendered as below::
 The selected menu entry will be highlighted - it will have inverted
 background and text colors.
 
+UEFI boot variable enumeration
+''''''''''''''''''''''''''''''
+
+The bootmenu automatically generates the UEFI boot variable("BOOT####")
+in order of "BootOrder". When the user selects the UEFI boot
+variable entry, bootmenu sets the selected boot variable index
+to "BootNext", then call the uefi boot manager with the command
+"bootefi bootmgr".
+
+The bootmenu automatically enumerates the possible bootable
+media devices supporting EFI_SIMPLE_FILE_SYSTEM_PROTOCOL.
+
+The bootmenu prints the EFI_LOAD_OPTION.Description[] as title
+of the entry together with "UEFI" prefix and BOOT#### variable name.
+
+Example bootmenu is as below::
+
+    *** U-Boot Boot Menu ***
+
+       UEFI BOOT0000 : mmc0:1
+       UEFI BOOT0001 : mmc0:2
+       UEFI BOOT0002 : debian
+       UEFI BOOT0003 : nvme0:1
+       UEFI BOOT0004 : ubuntu
+       UEFI BOOT0005 : nvme0:2
+       UEFI BOOT0006 : usb0:2
+
+To scan the discoverable devices connected to the buses such as
+USB and PCIe prior to bootmenu showing up, CONFIG_PREBOOT can be
+used to run the command before showing the bootmenu, i.e.::
+
+    CONFIG_USE_PREBOOT=y
+    CONFIG_PREBOOT="pci enum; usb start; scsi scan; nvme scan; virtio scan"
+
+distro boot command enumeration
+'''''''''''''''''''''''''''''''
+
+The bootmenu also automatically generates the entries for
+distro boot command. The bootmenu shows the devices in boot_targets
+environment variable.
+When the user selects the distro boot command entry, the bootmenu
+runs the command defined in "bootcmd_xxx" environment variable.
+As an example, if user selects "usb0" entry, bootmenu runs the
+command defined in "bootcmd_usb0".
+
+Example boot_targets::
+
+    usb0, scsi0, nvme0, dhcp
+
+Example bootmenu is as below::
+
+    *** U-Boot Boot Menu ***
+
+       distro_boot   : usb0
+       distro_boot   : scsi0
+       distro_boot   : nvme0
+       distro_boot   : dhcp
+
+Configuration
+-------------
+
 The "bootmenu" cammand is enabled by::
 
     CONFIG_CMD_BOOTMENU=y
@@ -88,3 +159,8 @@ To run the bootmenu at startup add these additional settings::
     CONFIG_AUTOBOOT_KEYED=y
     CONFIG_BOOTDELAY=30
     CONFIG_AUTOBOOT_MENU_SHOW=y
+
+To improve the product security, entering U-Boot console from bootmenu
+can be disabled by::
+
+    CONFIG_CMD_BOOTMENU_ENTER_UBOOT_CONSOLE=n
-- 
2.17.1


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

* Re: [PATCH v5 00/17] enable menu-driven boot device selection
  2022-04-28  8:09 [PATCH v5 00/17] enable menu-driven boot device selection Masahisa Kojima
                   ` (16 preceding siblings ...)
  2022-04-28  8:09 ` [PATCH v5 17/17] doc:bootmenu: add UEFI boot and distro boot support description Masahisa Kojima
@ 2022-04-28 16:31 ` Heinrich Schuchardt
  2022-04-28 16:58   ` Heinrich Schuchardt
  2022-04-29  8:45 ` Mark Kettenis
  18 siblings, 1 reply; 58+ messages in thread
From: Heinrich Schuchardt @ 2022-04-28 16:31 UTC (permalink / raw)
  To: Masahisa Kojima
  Cc: Ilias Apalodimas, Simon Glass, Takahiro Akashi, Francois Ozog,
	Mark Kettenis, u-boot, AKASHI Takahiro

On 4/28/22 10:09, Masahisa Kojima wrote:
> This patch series adds the menu-driven boot device selection,
> by extending the existing "bootmenu" to include UEFI and distro_boot
> related entries, and supports menu-driven UEFI boot variable
> maintenance.
>
> This patch series also includes the removable media support
> that UEFI specification requires to support.
>
> The menu example is as follows.
>
>    *** U-Boot Boot Menu ***
>
>       bootmenu_00   : Boot 1. kernel
>       bootmenu_01   : Boot 2. kernel
>       bootmenu_02   : Reset board
>       UEFI BOOT0000 : debian
>       UEFI BOOT0001 : ubuntu
>       UEFI BOOT0002 : mmc0:1
>       UEFI BOOT0003 : mmc0:2
>       UEFI BOOT0004 : nvme0:1
>       UEFI BOOT0005 : nvme0:2
>       UEFI BOOT0006 : usb0:2
>       UEFI BOOT0007 : usb1:1
>       UEFI BOOT0008 : usb1:2
>       distro_boot   : usb0
>       distro_boot   : scsi0
>       distro_boot   : virtio0
>       distro_boot   : dhcp
>
>    Press UP/DOWN to move, ENTER to select, ESC/CTRL+C to quit
>
> [How to run on QEMU(arm64)]
> 1) clone source code
>   $ git clone https://git.linaro.org/people/masahisa.kojima/u-boot.git \
> -b kojima/bootmenu_v5_upstream_0428 --depth 1
>
> 2) prepare U-Boot .config
>   $ make qemu_arm64_menuconfig
>    then, enable CONFIG_CMD_BOOTMENU and CONFIG_AUTOBOOT_MENU_SHOW
>
> 3) run on QEMU(arm64) example
>   $ qemu-system-aarch64 -machine virt,gic-version=3 -cpu cortex-a57 -m 4G -nographic \
>     -no-acpi -bios ./u-boot.bin -hda xxx.img
>
> [Major Changes]
> - rebased to v2022.07-rc1
> - there is detailed changelog in each commit
>
> AKASHI Takahiro (2):
>    efi_loader: export efi_locate_device_handle()
>    efi_loader: bootmgr: add booting from removable media
>
> Masahisa Kojima (15):
>    lib/charset: add u16_strlcat() function
>    test: unit test for u16_strlcat()
>    menu: always show the menu regardless of the number of entry
>    menu: menu_get_choice() return -ENOENT if menu item is empty
>    bootmenu: flush input buffer before waiting user key input
>    bootmenu: update bootmenu_entry structure
>    bootmenu: add UEFI boot entry into bootmenu
>    bootmenu: add distro boot entry
>    bootmenu: add Kconfig option not to enter U-Boot console
>    bootmenu: factor out the user input handling
>    efi_loader: menu-driven addition of UEFI boot option
>    efi_loader: menu-driven deletion of UEFI boot variable
>    efi_loader: menu-driven update of UEFI bootorder variable
>    bootmenu: add removable media entries
>    doc:bootmenu: add UEFI boot and distro boot support description
>
>   cmd/Kconfig                               |   10 +
>   cmd/bootmenu.c                            |  807 +++++++++----
>   common/menu.c                             |  142 ++-
>   doc/usage/cmd/bootmenu.rst                |   78 +-
>   include/charset.h                         |   17 +
>   include/config_distro_bootcmd.h           |   14 +-
>   include/efi_default_filename.h            |   33 +
>   include/efi_loader.h                      |   61 +
>   include/menu.h                            |   20 +
>   lib/charset.c                             |   22 +
>   lib/efi_loader/Makefile                   |    1 +
>   lib/efi_loader/efi_bootmenu_maintenance.c | 1245 +++++++++++++++++++++
>   lib/efi_loader/efi_bootmgr.c              |   50 +-
>   lib/efi_loader/efi_boottime.c             |   59 +-
>   lib/efi_loader/efi_console.c              |   81 ++
>   lib/efi_loader/efi_disk.c                 |   11 +
>   lib/efi_loader/efi_file.c                 |   75 +-
>   test/unicode_ut.c                         |   50 +
>   18 files changed, 2518 insertions(+), 258 deletions(-)
>   create mode 100644 include/efi_default_filename.h
>   create mode 100644 lib/efi_loader/efi_bootmenu_maintenance.c
>

I prepared my test as follows:

make sandbox_defconfig
CONFIG_CMD_BOOTMENU=y
CONFIG_AUTOBOOT_MENU_SHOW=y
PREBOOT=host bind 0 ../sandbox.img

I created ../sandbox.img as GTP partioned image with a single EF00
partition. Onto the partition I copied lib/efi_loader/helloworld.efi
into path EFI/BOOT/BOOTX64.EFI.

I set up the following boot option:

=> efidebug boot dump
Boot0001:
attributes: A-- (0x00000001)
   label: hello host 0:1
   file_path:
/HD(1,GPT,7e5c17c5-3f5f-49d0-ae96-511b21d7f273,0x800,0x3f7df)/EFI\BOOT\BOOTX64.EFI
   data:
     00000000: 6d 00 79 00 20 00 6d 00 65 00 73 00 73 00 61 00  m.y.
.m.e.s.s.a.
     00000010: 67 00 65 00 00 00                                g.e...
=> efidebug boot order
  1: Boot0001: hello host 0:1
=> reset

Now the boot menu looks like this:

   *** U-Boot Boot Menu ***

      UEFI BOOT0001 : hello host 0:1
      UEFI BOOT0000 : host0:1
      UEFI Boot Manager Maintenance
      U-Boot console

When I select nothing:

try_load_entry: trying to load "hello host 0:1" from
/HD(1,GPT,7e5c17c5-3f5f-49d0-ae96-511b21d7f273,0x800,0x3f7df)/EFI\BOOT\BOOTX64.EFI
Booting: hello host 0:1
Hello, world!
Running on UEFI 2.9
Have ACPI 2.0 table
Have SMBIOS table
Load options: my message
Boot device:
/VenHw(e61d73b9-a384-4acc-aeab-82e828f3628b)/VenHw(bbe4e671-5773-4ea1-9aab-3a7dbf40c482,00)/HD(1,GPT,7e5c17c5-3f5f-49d0-ae96-511b21d7f273,0x800,0x3f7df)
File path: /EFI\BOOT\BOOTX64.EFI

This is ok.

   *** U-Boot Boot Menu ***

      UEFI BOOT0001 : hello host 0:1
      UEFI BOOT0000 : host0:1
      UEFI Boot Manager Maintenance
      U-Boot console


   Press UP/DOWN to move, ENTER to select, ESC/CTRL+C to quit

When I select the second item 'UEFI BOOT0000 : host0:1':

try_load_entry: trying to load "host0:1" from
/VenHw(e61d73b9-a384-4acc-aeab-82e828f3628b)/VenHw(bbe4e671-5773-4ea1-9aab-3a7dbf40c482,00)/HD(1,GPT,7e5c17c5-3f5f-49d0-ae96-511b21d7f273,0x800,0x3f7df)
Loading Boot0000 'host0:1' failed

After I fixed this in
[PATCH 1/1] efi_loader: BOOTSANDBOX.EFI is not a default file name
https://lists.denx.de/pipermail/u-boot/2022-April/482763.html

Booting: host 0:1
Hello, world!
Running on UEFI 2.9
Have ACPI 2.0 table
Have SMBIOS table
Load options: 곁㣁鿀䇰ƹ瓺훖?
Boot device:
/VenHw(e61d73b9-a384-4acc-aeab-82e828f3628b)/VenHw(bbe4e671-5773-4ea1-9aab-3a7dbf40c482,00)/HD(1,GPT,7e5c17c5-3f5f-49d0-ae96-511b21d7f273,0x800,0x3f7df)
File path: /\EFI\BOOT\BOOTX64.EFI

See those Chinese letters. Something is going really wrong here.

Best regards

Heinrich








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

* Re: [PATCH v5 13/17] efi_loader: menu-driven addition of UEFI boot option
  2022-04-28  8:09 ` [PATCH v5 13/17] efi_loader: menu-driven addition of UEFI boot option Masahisa Kojima
@ 2022-04-28 16:33   ` Heinrich Schuchardt
  2022-04-29 10:56     ` Heinrich Schuchardt
  0 siblings, 1 reply; 58+ messages in thread
From: Heinrich Schuchardt @ 2022-04-28 16:33 UTC (permalink / raw)
  To: Masahisa Kojima, u-boot
  Cc: Ilias Apalodimas, Simon Glass, Takahiro Akashi, Francois Ozog,
	Mark Kettenis

On 4/28/22 10:09, Masahisa Kojima wrote:
> This commit supports the menu-driven UEFI boot option addition.
> User can select the block device volume having
> efi_simple_file_system_protocol and select the file corresponding
> to the Boot#### variable. Then user enter the label of the BOOT####
> variable in utf8.
>
> Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
> ---
> Changes in v5:
> - remove forward declarations
> - add const qualifier for menu items
> - fix the possible unaligned access for directory info access
> - split into three commit 1)add boot option 2) delete boot option 3)change boot order
>    This commit is 1)add boot option.
> - fix file name buffer allocation size, it should be EFI_BOOTMENU_FILE_PATH_MAX * sizeof(u16)
> - fix wrong size checking for file selection
>
> Chanes in v4:
> - UEFI boot option maintenance menu is integrated into bootmenu
> - display the simplified volume name(e.g. usb0:1, nvme1:2) for the
>    volume selection
> - instead of extending lib/efi_loader/efi_bootmgr.c, newly create
>    lib/efi_loader/efi_bootmenu_maintenance.c and implement boot
>    variable maintenance into it.
>
> Changes in RFC v3:
>   not included in v3 series
>
> Changes in RFC v2:
> - enable utf8 user input for boot option name
> - create lib/efi_loader/efi_console.c::efi_console_get_u16_string() for
>    utf8 user input handling
> - use u16_strlcat instead of u16_strcat
> - remove the EFI_CALLs, and newly create or expose the following
>    xxx_int() functions.
>      efi_locate_handle_buffer_int(), efi_open_volume_int(),
>      efi_file_open_int(), efi_file_close_int(), efi_file_read_int() and
>      efi_file_setpos_int().
>    Note that EFI_CALLs still exist for EFI_DEVICE_PATH_TO_TEXT_PROTOCOL
>    and EFI_SIMPLE_TEXT_INPUT/OUTPUT_PROTOCOL
> - use efi_search_protocol() instead of calling locate_protocol() to get
>    the device_path_to_text_protocol interface.
> - remove unnecessary puts(ANSI_CLEAR_LINE), this patch is still depends on
>    puts(ANSI_CLEAR_CONSOLE)
> - skip SetVariable() if the bootorder is not changed
>
>   cmd/bootmenu.c                            |  69 +-
>   include/efi_loader.h                      |  37 +
>   lib/efi_loader/Makefile                   |   1 +
>   lib/efi_loader/efi_bootmenu_maintenance.c | 862 ++++++++++++++++++++++
>   lib/efi_loader/efi_boottime.c             |  52 +-
>   lib/efi_loader/efi_console.c              |  81 ++
>   lib/efi_loader/efi_disk.c                 |  11 +
>   lib/efi_loader/efi_file.c                 |  75 +-
>   8 files changed, 1133 insertions(+), 55 deletions(-)
>   create mode 100644 lib/efi_loader/efi_bootmenu_maintenance.c
>
> diff --git a/cmd/bootmenu.c b/cmd/bootmenu.c
> index eb23afdd41..860cb83182 100644
> --- a/cmd/bootmenu.c
> +++ b/cmd/bootmenu.c
> @@ -21,6 +21,8 @@
>
>   /* maximum bootmenu entries */
>   #define MAX_COUNT	99
> +#define STATIC_ENTRY 2
> +#define MAX_DYNAMIC_ENTRY (MAX_COUNT - STATIC_ENTRY)
>
>   /* maximal size of bootmenu env
>    *  9 = strlen("bootmenu_")
> @@ -41,10 +43,11 @@ enum boot_type {
>   	BOOTMENU_TYPE_BOOTMENU,
>   	BOOTMENU_TYPE_UEFI_BOOT_OPTION,
>   	BOOTMENU_TYPE_DISTRO_BOOT,
> +	BOOTMENU_TYPE_UEFI_MAINTENANCE,
>   };
>
>   struct bootmenu_entry {
> -	unsigned short int num;		/* unique number 0 .. MAX_COUNT */
> +	unsigned short int num;		/* unique number 0 .. MAX_DYNAMIC_ENTRY */
>   	char key[3];			/* key identifier of number */
>   	u16 *title;			/* title of entry */
>   	char *command;			/* hush command of entry */
> @@ -58,7 +61,7 @@ static char *bootmenu_getoption(unsigned short int n)
>   {
>   	char name[MAX_ENV_SIZE];
>
> -	if (n > MAX_COUNT)
> +	if (n > MAX_DYNAMIC_ENTRY)
>   		return NULL;
>
>   	sprintf(name, "bootmenu_%d", n);
> @@ -229,7 +232,7 @@ static int prepare_bootmenu_entry(struct bootmenu_data *menu,
>   		iter = entry;
>   		++i;
>
> -		if (i == MAX_COUNT - 1)
> +		if (i == MAX_DYNAMIC_ENTRY)
>   			break;
>   	}
>
> @@ -317,7 +320,7 @@ static int prepare_uefi_bootorder_entry(struct bootmenu_data *menu,
>
>   		free(load_option);
>
> -		if (i == MAX_COUNT - 1)
> +		if (i == MAX_DYNAMIC_ENTRY)
>   			break;
>   	}
>
> @@ -481,7 +484,7 @@ static int prepare_distro_boot_entry(struct bootmenu_data *menu,
>   		iter = entry;
>   		i++;
>
> -		if (i == MAX_COUNT - 1)
> +		if (i == MAX_DYNAMIC_ENTRY)
>   			break;
>
>   		token = strtok(NULL, " ");
> @@ -520,19 +523,56 @@ static struct bootmenu_data *bootmenu_create(int delay)
>   		goto cleanup;
>
>   	if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR)) {
> -		if (i < MAX_COUNT - 1) {
> +		if (i < MAX_DYNAMIC_ENTRY) {
>   			ret = prepare_uefi_bootorder_entry(menu, &iter, &i);
>   			if (ret < 0 && ret != -ENOENT)
>   				goto cleanup;
>   		}
>   	}
>
> -	if (i < MAX_COUNT - 1) {
> +	if (i < MAX_DYNAMIC_ENTRY) {
>   		ret = prepare_distro_boot_entry(menu, &iter, &i);
>   		if (ret < 0 && ret != -ENOENT)
>   			goto cleanup;
>   	}
>
> +	if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR)) {
> +		/* Add UEFI Boot Manager Maintenance entry */
> +		if (i <= MAX_DYNAMIC_ENTRY) {
> +			entry = malloc(sizeof(struct bootmenu_entry));
> +			if (!entry)
> +				goto cleanup;
> +
> +			entry->title = u16_strdup(u"UEFI Boot Manager Maintenance");
> +			if (!entry->title) {
> +				free(entry);
> +				goto cleanup;
> +			}
> +
> +			entry->command = strdup("");
> +			if (!entry->command) {
> +				free(entry->title);
> +				free(entry);
> +				goto cleanup;
> +			}
> +
> +			sprintf(entry->key, "%d", i);
> +
> +			entry->num = i;
> +			entry->menu = menu;
> +			entry->type = BOOTMENU_TYPE_UEFI_MAINTENANCE;
> +			entry->next = NULL;
> +
> +			if (!iter)
> +				menu->first = entry;
> +			else
> +				iter->next = entry;
> +
> +			iter = entry;
> +			i++;
> +		}
> +	}
> +
>   	/* Add U-Boot console entry at the end */
>   	if (i <= MAX_COUNT - 1) {
>   		entry = malloc(sizeof(struct bootmenu_entry));
> @@ -704,6 +744,12 @@ static enum bootmenu_ret bootmenu_show(int delay)
>   		title = u16_strdup(iter->title);
>   		command = strdup(iter->command);
>
> +		if (iter->type == BOOTMENU_TYPE_UEFI_MAINTENANCE) {
> +			efi_bootmenu_show_maintenance_menu();
> +			ret = BOOTMENU_RET_UPDATED;
> +			goto cleanup;
> +		}
> +
>   		/* last entry is U-Boot console or Quit */
>   		if (iter->num == iter->menu->count - 1) {
>   			ret = BOOTMENU_RET_QUIT;
> @@ -794,6 +840,7 @@ int do_bootmenu(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
>   {
>   	char *delay_str = NULL;
>   	int delay = 10;
> +	int ret;
>
>   #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
>   	delay = CONFIG_BOOTDELAY;
> @@ -808,7 +855,13 @@ int do_bootmenu(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
>   	if (delay_str)
>   		delay = (int)simple_strtol(delay_str, NULL, 10);
>
> -	bootmenu_show(delay);
> +	while (1) {
> +		ret =  bootmenu_show(delay);
> +		delay = -1;
> +		if (ret != BOOTMENU_RET_UPDATED)
> +			break;
> +	}
> +
>   	return 0;
>   }
>
> diff --git a/include/efi_loader.h b/include/efi_loader.h
> index effb43369d..533618341b 100644
> --- a/include/efi_loader.h
> +++ b/include/efi_loader.h
> @@ -226,6 +226,9 @@ const char *__efi_nesting_dec(void);
>   #define EFI_CACHELINE_SIZE 128
>   #endif
>
> +/* max bootmenu title size for volume selection */
> +#define BOOTMENU_DEVICE_NAME_MAX 16
> +
>   /* Key identifying current memory map */
>   extern efi_uintn_t efi_memory_map_key;
>
> @@ -312,6 +315,9 @@ extern const efi_guid_t efi_guid_firmware_management_protocol;
>   extern const efi_guid_t efi_esrt_guid;
>   /* GUID of the SMBIOS table */
>   extern const efi_guid_t smbios_guid;
> +/*GUID of console */
> +extern const efi_guid_t efi_guid_text_input_protocol;
> +extern const efi_guid_t efi_guid_text_output_protocol;
>
>   extern char __efi_runtime_start[], __efi_runtime_stop[];
>   extern char __efi_runtime_rel_start[], __efi_runtime_rel_stop[];
> @@ -871,6 +877,8 @@ efi_status_t efi_set_load_options(efi_handle_t handle,
>   				  void *load_options);
>   efi_status_t efi_bootmgr_load(efi_handle_t *handle, void **load_options);
>
> +efi_status_t efi_bootmenu_show_maintenance_menu(void);
> +
>   /**
>    * struct efi_image_regions - A list of memory regions
>    *
> @@ -1042,4 +1050,33 @@ efi_status_t efi_esrt_populate(void);
>   efi_status_t efi_load_capsule_drivers(void);
>
>   efi_status_t platform_get_eventlog(struct udevice *dev, u64 *addr, u32 *sz);
> +
> +efi_status_t efi_locate_handle_buffer_int(enum efi_locate_search_type search_type,
> +					  const efi_guid_t *protocol, void *search_key,
> +					  efi_uintn_t *no_handles, efi_handle_t **buffer);
> +
> +efi_status_t efi_open_volume_int(struct efi_simple_file_system_protocol *this,
> +				 struct efi_file_handle **root);
> +efi_status_t efi_file_open_int(struct efi_file_handle *this,
> +			       struct efi_file_handle **new_handle,
> +			       u16 *file_name, u64 open_mode,
> +			       u64 attributes);
> +efi_status_t efi_file_close_int(struct efi_file_handle *file);
> +efi_status_t efi_file_read_int(struct efi_file_handle *this,
> +			       efi_uintn_t *buffer_size, void *buffer);
> +efi_status_t efi_file_setpos_int(struct efi_file_handle *file, u64 pos);
> +
> +typedef efi_status_t (*efi_console_filter_func)(struct efi_input_key *key);
> +efi_status_t efi_console_get_u16_string
> +		(struct efi_simple_text_input_protocol *cin,
> +		 struct efi_simple_text_output_protocol *cout,
> +		 u16 *buf, efi_uintn_t count, efi_console_filter_func filer_func,
> +		 int row, int col);
> +
> +efi_status_t efi_bootmenu_get_unused_bootoption(u16 *buf,
> +						efi_uintn_t buf_size, u32 *index);
> +efi_status_t efi_bootmenu_append_bootorder(u16 index);
> +
> +efi_status_t efi_disk_get_device_name(struct efi_block_io *this, char *buf, int size);
> +
>   #endif /* _EFI_LOADER_H */
> diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile
> index aaaa25cefe..792eabe18a 100644
> --- a/lib/efi_loader/Makefile
> +++ b/lib/efi_loader/Makefile
> @@ -77,6 +77,7 @@ obj-$(CONFIG_EFI_TCG2_PROTOCOL) += efi_tcg2.o
>   obj-$(CONFIG_EFI_RISCV_BOOT_PROTOCOL) += efi_riscv.o
>   obj-$(CONFIG_EFI_LOAD_FILE2_INITRD) += efi_load_initrd.o
>   obj-$(CONFIG_EFI_SIGNATURE_SUPPORT) += efi_signature.o
> +obj-$(CONFIG_CMD_BOOTMENU) += efi_bootmenu_maintenance.o
>
>   EFI_VAR_SEED_FILE := $(subst $\",,$(CONFIG_EFI_VAR_SEED_FILE))
>   $(obj)/efi_var_seed.o: $(srctree)/$(EFI_VAR_SEED_FILE)
> diff --git a/lib/efi_loader/efi_bootmenu_maintenance.c b/lib/efi_loader/efi_bootmenu_maintenance.c
> new file mode 100644
> index 0000000000..77401a7829
> --- /dev/null
> +++ b/lib/efi_loader/efi_bootmenu_maintenance.c
> @@ -0,0 +1,862 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + *  Menu-driven UEFI Boot Variable maintenance
> + *
> + *  Copyright (c) 2022 Masahisa Kojima, Linaro Limited
> + */
> +
> +#define LOG_CATEGORY LOGC_EFI
> +
> +#include <ansi.h>
> +#include <common.h>
> +#include <charset.h>
> +#include <log.h>
> +#include <malloc.h>
> +#include <menu.h>
> +#include <efi_loader.h>
> +#include <efi_variable.h>
> +#include <asm/unaligned.h>
> +
> +static struct efi_simple_text_input_protocol *cin;
> +static struct efi_simple_text_output_protocol *cout;
> +
> +#define EFI_BOOTMENU_ENTRY_NUM_MAX 99
> +#define EFI_BOOTMENU_FILE_PATH_MAX 512
> +#define EFI_BOOTMENU_FILE_PATH_BUF_SIZE (EFI_BOOTMENU_FILE_PATH_MAX * sizeof(u16))
> +#define EFI_BOOTMENU_BOOT_NAME_MAX 32
> +#define EFI_BOOT_ORDER_MAX_SIZE_IN_DECIMAL 6
> +
> +typedef efi_status_t (*efi_bootmenu_entry_func)(void *data, bool *exit);
> +
> +/**
> + * struct efi_bootmenu_entry - menu entry structure
> + *
> + * @num:		menu entry index
> + * @title:		title of entry
> + * @key:		unique key
> + * @bootmgr_menu:	pointer to the menu structure
> + * @next:		pointer to the next entry
> + * @func:		callback function to be called when this entry is selected
> + * @data:		data to be passed to the callback function
> + */
> +struct efi_bootmenu_entry {
> +	u32 num;
> +	u16 *title;
> +	char key[6];
> +	struct efi_bootmenu *bootmgr_menu;
> +	struct efi_bootmenu_entry *next;
> +	efi_bootmenu_entry_func func;
> +	void *data;
> +};
> +
> +/**
> + * struct efi_bootmenu - bootmgr menu structure
> + *
> + * @delay:	delay for autoboot
> + * @active:	active menu entry index
> + * @count:	total count of menu entry
> + * @first:	pointer to the first menu entry
> + */
> +struct efi_bootmenu {
> +	int delay;
> +	int active;
> +	int count;
> +	struct efi_bootmenu_entry *first;
> +};
> +
> +struct efi_bootmenu_item {
> +	u16 *title;
> +	efi_bootmenu_entry_func func;
> +	void *data;
> +};
> +
> +struct efi_bootmenu_boot_option {
> +	struct efi_simple_file_system_protocol *current_volume;
> +	struct efi_device_path *dp_volume;
> +	u16 *current_path;
> +	u16 *boot_name;
> +	bool file_selected;
> +};
> +
> +static const struct efi_device_path END = {
> +	.type     = DEVICE_PATH_TYPE_END,
> +	.sub_type = DEVICE_PATH_SUB_TYPE_END,
> +	.length   = sizeof(END),
> +};
> +
> +struct efi_bootmenu_volume_entry_data {
> +	struct efi_bootmenu_boot_option *bo;
> +	struct efi_simple_file_system_protocol *v;
> +	struct efi_device_path *dp;
> +};
> +
> +struct efi_bootmenu_file_entry_data {
> +	struct efi_bootmenu_boot_option *bo;
> +	bool is_directory;
> +	u16 *file_name;
> +};
> +
> +static void efi_bootmenu_print_entry(void *data)
> +{
> +	struct efi_bootmenu_entry *entry = data;
> +	int reverse = (entry->bootmgr_menu->active == entry->num);
> +
> +	/* TODO: support scroll or page for many entries */
> +
> +	/*
> +	 * Move cursor to line where the entry will be drown (entry->count)

%s/drown/drawn/

> +	 * First 3 lines contain bootmgr menu header + one empty line
> +	 * For the last "Quit" entry, add one empty line
> +	 */
> +	if (entry->num == (entry->bootmgr_menu->count - 1))
> +		printf(ANSI_CURSOR_POSITION, entry->num + 5, 1);
> +	else
> +		printf(ANSI_CURSOR_POSITION, entry->num + 4, 1);
> +
> +	puts("     ");
> +
> +	if (reverse)
> +		puts(ANSI_COLOR_REVERSE);
> +
> +	printf("%ls", entry->title);
> +
> +	if (reverse)
> +		puts(ANSI_COLOR_RESET);
> +}
> +
> +static void efi_bootmenu_display_statusline(struct menu *m)
> +{
> +	struct efi_bootmenu_entry *entry;
> +	struct efi_bootmenu *bootmgr_menu;
> +
> +	if (menu_default_choice(m, (void *)&entry) < 0)
> +		return;
> +
> +	bootmgr_menu = entry->bootmgr_menu;
> +
> +	printf(ANSI_CURSOR_POSITION, 1, 1);
> +	puts(ANSI_CLEAR_LINE);
> +	printf(ANSI_CURSOR_POSITION, 2, 1);
> +	puts("  *** U-Boot EFI Boot Manager ***");
> +	puts(ANSI_CLEAR_LINE_TO_END);
> +	printf(ANSI_CURSOR_POSITION, 3, 1);
> +	puts(ANSI_CLEAR_LINE);
> +
> +	/* First 3 lines are bootmgr_menu header + 2 empty lines between entries */
> +	printf(ANSI_CURSOR_POSITION, bootmgr_menu->count + 5, 1);
> +	puts(ANSI_CLEAR_LINE);
> +	printf(ANSI_CURSOR_POSITION, bootmgr_menu->count + 6, 1);
> +	puts("  Press UP/DOWN to move, ENTER to select, ESC/CTRL+C to quit");
> +	puts(ANSI_CLEAR_LINE_TO_END);
> +	printf(ANSI_CURSOR_POSITION, bootmgr_menu->count + 7, 1);
> +	puts(ANSI_CLEAR_LINE);
> +}
> +
> +static char *efi_bootmenu_choice_entry(void *data)
> +{
> +	int i;
> +	int esc = 0;
> +	struct efi_bootmenu_entry *iter;
> +	enum bootmenu_key key = KEY_NONE;
> +	struct efi_bootmenu *bootmgr_menu = data;
> +
> +	while (1) {
> +		if (bootmgr_menu->delay >= 0) {
> +			/* Autoboot was not stopped */
> +			bootmenu_autoboot_loop((struct bootmenu_data *)bootmgr_menu, &key, &esc);
> +		} else {
> +			/* Some key was pressed, so autoboot was stopped */
> +			bootmenu_loop((struct bootmenu_data *)bootmgr_menu, &key, &esc);
> +		}
> +
> +		if (bootmgr_menu->delay == 0)
> +			key = KEY_QUIT;
> +
> +		switch (key) {
> +		case KEY_UP:
> +			if (bootmgr_menu->active > 0)
> +				--bootmgr_menu->active;
> +			/* no menu key selected, regenerate menu */
> +			return NULL;
> +		case KEY_DOWN:
> +			if (bootmgr_menu->active < bootmgr_menu->count - 1)
> +				++bootmgr_menu->active;
> +			/* no menu key selected, regenerate menu */
> +			return NULL;
> +		case KEY_SELECT:
> +			iter = bootmgr_menu->first;
> +			for (i = 0; i < bootmgr_menu->active; ++i)
> +				iter = iter->next;
> +			return iter->key;
> +		case KEY_QUIT:
> +			/* Quit by choosing the last entry */
> +			iter = bootmgr_menu->first;
> +			while (iter->next)
> +				iter = iter->next;
> +			return iter->key;
> +		default:
> +			break;
> +		}
> +	}
> +
> +	/* never happens */
> +	debug("bootmgr menu: this should not happen");
> +	return NULL;
> +}
> +
> +static void efi_bootmenu_destroy(struct efi_bootmenu *bootmgr_menu)
> +{
> +	struct efi_bootmenu_entry *next;
> +	struct efi_bootmenu_entry *iter = bootmgr_menu->first;
> +
> +	while (iter) {
> +		next = iter->next;
> +		free(iter);
> +		iter = next;
> +	}
> +	free(bootmgr_menu);
> +}
> +
> +/**
> + * efi_bootmenu_process_common() - main handler for uefi bootmgr menu
> + *
> + * Construct the structures required to show the menu, then handle
> + * the user input intracting with u-boot menu functions.
> + *
> + * @items:	pointer to the structure of each menu entry
> + * @count:	the number of menu entry
> + * @delay:	delay for autoboot/autoselect
> + * Return:	status code
> + */
> +static efi_status_t efi_bootmenu_process_common(const struct efi_bootmenu_item *items,
> +						int count, int delay)
> +{
> +	u32 i;
> +	bool exit = false;
> +	efi_status_t ret;
> +	struct menu *menu;
> +	void *choice = NULL;
> +	struct efi_bootmenu_entry *entry;
> +	struct efi_bootmenu *bootmgr_menu;
> +	struct efi_bootmenu_entry *iter = NULL;
> +
> +	if (count > EFI_BOOTMENU_ENTRY_NUM_MAX)
> +		return EFI_OUT_OF_RESOURCES;
> +
> +	bootmgr_menu = calloc(1, sizeof(struct efi_bootmenu));
> +	if (!bootmgr_menu)
> +		return EFI_OUT_OF_RESOURCES;
> +
> +	bootmgr_menu->delay = delay;
> +	bootmgr_menu->active = 0;
> +	bootmgr_menu->first = NULL;
> +
> +	for (i = 0; i < count; i++) {
> +		entry = calloc(1, sizeof(struct efi_bootmenu_entry));
> +		if (!entry) {
> +			ret = EFI_LOAD_ERROR;
> +			goto out;
> +		}
> +
> +		entry->num = i;
> +		entry->title = items->title;
> +		snprintf(entry->key, sizeof(entry->key), "%04X", i);
> +		entry->bootmgr_menu = bootmgr_menu;
> +		entry->func = items->func;
> +		entry->data = items->data;
> +		entry->next = NULL;
> +
> +		if (!iter)
> +			bootmgr_menu->first = entry;
> +		else
> +			iter->next = entry;
> +
> +		iter = entry;
> +		items++;
> +	}
> +	bootmgr_menu->count = count;
> +
> +	menu = menu_create(NULL, 0, 1, efi_bootmenu_display_statusline,
> +			   efi_bootmenu_print_entry, efi_bootmenu_choice_entry,
> +			   bootmgr_menu);
> +	if (!menu) {
> +		ret = EFI_INVALID_PARAMETER;
> +		goto out;
> +	}
> +
> +	for (entry = bootmgr_menu->first; entry; entry = entry->next) {
> +		if (!menu_item_add(menu, entry->key, entry)) {
> +			ret = EFI_INVALID_PARAMETER;
> +			goto out;
> +		}
> +	}
> +
> +	menu_default_set(menu, bootmgr_menu->first->key);
> +
> +	while (!exit) {
> +		puts(ANSI_CURSOR_HIDE);
> +		puts(ANSI_CLEAR_CONSOLE);
> +		printf(ANSI_CURSOR_POSITION, 1, 1);
> +
> +		if (menu_get_choice(menu, &choice)) {
> +			entry = choice;
> +			if (entry->func)
> +				ret = entry->func(entry->data, &exit);
> +
> +			/* last entry "Quit" is selected, exit this menu */
> +			if (entry->num == (entry->bootmgr_menu->count - 1)) {
> +				ret = EFI_ABORTED;
> +				break;
> +			}
> +		}
> +	}
> +
> +out:
> +	menu_destroy(menu);
> +	efi_bootmenu_destroy(bootmgr_menu);
> +
> +	puts(ANSI_CLEAR_CONSOLE);
> +	printf(ANSI_CURSOR_POSITION, 1, 1);
> +	puts(ANSI_CURSOR_SHOW);
> +
> +	return ret;
> +}
> +
> +static efi_status_t efi_bootmenu_volume_selected(void *data, bool *exit)
> +{
> +	struct efi_bootmenu_volume_entry_data *info = data;
> +
> +	*exit = true;
> +
> +	if (info) {
> +		info->bo->current_volume = info->v;
> +		info->bo->dp_volume = info->dp;
> +	}
> +
> +	return EFI_SUCCESS;
> +}
> +
> +static efi_status_t efi_bootmenu_file_selected(void *data, bool *exit)
> +{
> +	struct efi_bootmenu_file_entry_data *info = data;
> +
> +	*exit = true;
> +
> +	if (!info)
> +		return EFI_INVALID_PARAMETER;
> +
> +	if (u16_strncmp(info->file_name, u".", 1) == 0 &&
> +	    u16_strlen(info->file_name) == 1) {
> +		/* stay current path */
> +	} else if (u16_strncmp(info->file_name, u"..", 2) == 0 &&
> +		   u16_strlen(info->file_name) == 2) {
> +		u32 i;
> +		int len = u16_strlen(info->bo->current_path);
> +
> +		for (i = len - 2; i > 0; i--) {
> +			if (info->bo->current_path[i] == u'\\')
> +				break;
> +		}
> +
> +		if (i == 0)
> +			info->bo->current_path[0] = u'\0';
> +		else
> +			info->bo->current_path[i + 1] = u'\0';
> +	} else {
> +		size_t new_len;
> +
> +		new_len = u16_strlen(info->bo->current_path) +
> +				     u16_strlen(info->file_name) + 1;
> +		if (new_len > EFI_BOOTMENU_FILE_PATH_MAX) {
> +			/* TODO: show error notification to user */
> +			log_err("file path is too long\n");
> +			return EFI_INVALID_PARAMETER;
> +		}
> +		u16_strlcat(info->bo->current_path, info->file_name,
> +			    EFI_BOOTMENU_FILE_PATH_MAX);
> +		if (info->is_directory) {
> +			/*
> +			 * Remainig buffer should have enough space to contain u"\\" and
> +			 * at least one character for file name
> +			 */
> +			if (new_len + 2 > EFI_BOOTMENU_FILE_PATH_MAX) {
> +				log_err("directory path is too long\n");
> +				return EFI_INVALID_PARAMETER;
> +			}
> +			u16_strlcat(info->bo->current_path, u"\\",
> +				    EFI_BOOTMENU_FILE_PATH_MAX);
> +		} else {
> +			info->bo->file_selected = true;
> +		}
> +	}
> +	return EFI_SUCCESS;
> +}
> +
> +static efi_status_t efi_bootmenu_select_volume(struct efi_bootmenu_boot_option *bo)
> +{
> +	u32 i;
> +	efi_status_t ret;
> +	efi_uintn_t count;
> +	struct efi_handler *handler;
> +	struct efi_device_path *device_path;
> +	efi_handle_t *volume_handles = NULL;
> +	struct efi_simple_file_system_protocol *v;
> +	struct efi_bootmenu_item *menu_item, *iter;
> +
> +	ret = efi_locate_handle_buffer_int(BY_PROTOCOL, &efi_simple_file_system_protocol_guid,
> +					   NULL, &count, (efi_handle_t **)&volume_handles);
> +	if (ret != EFI_SUCCESS)
> +		return ret;
> +
> +	menu_item = calloc(count + 1, sizeof(struct efi_bootmenu_item));
> +	if (!menu_item) {
> +		ret = EFI_OUT_OF_RESOURCES;
> +		goto out1;
> +	}
> +
> +	iter = menu_item;
> +	for (i = 0; i < count; i++) {
> +		u16 *dev_name, *p;
> +		struct efi_block_io *block_io;
> +		char buf[BOOTMENU_DEVICE_NAME_MAX];
> +		struct efi_bootmenu_volume_entry_data *info;
> +
> +		ret = efi_search_protocol(volume_handles[i],
> +					  &efi_simple_file_system_protocol_guid, &handler);
> +		if (ret != EFI_SUCCESS)
> +			continue;
> +		ret = efi_protocol_open(handler, (void **)&v, efi_root, NULL,
> +					EFI_OPEN_PROTOCOL_GET_PROTOCOL);
> +		if (ret != EFI_SUCCESS)
> +			continue;
> +
> +		ret = efi_search_protocol(volume_handles[i], &efi_guid_device_path, &handler);
> +		if (ret != EFI_SUCCESS)
> +			continue;
> +		ret = efi_protocol_open(handler, (void **)&device_path,
> +					efi_root, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
> +		if (ret != EFI_SUCCESS)
> +			continue;
> +
> +		ret = efi_search_protocol(volume_handles[i], &efi_block_io_guid, &handler);
> +		if (ret != EFI_SUCCESS)
> +			continue;
> +		ret = efi_protocol_open(handler, (void **)&block_io,
> +					efi_root, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
> +		if (ret != EFI_SUCCESS)
> +			continue;
> +
> +		info = calloc(1, sizeof(struct efi_bootmenu_volume_entry_data));
> +		if (!info) {
> +			ret = EFI_OUT_OF_RESOURCES;
> +			goto out2;
> +		}
> +
> +		efi_disk_get_device_name(block_io, buf, BOOTMENU_DEVICE_NAME_MAX);
> +		dev_name = calloc(1, (strlen(buf) + 1) * sizeof(u16));
> +		if (!dev_name) {
> +			free(info);
> +			ret = EFI_OUT_OF_RESOURCES;
> +			goto out2;
> +		}
> +		p = dev_name;
> +		utf8_utf16_strncpy(&p, buf, strlen(buf));
> +
> +		info->v = v;
> +		info->dp = device_path;
> +		info->bo = bo;
> +		iter->title = dev_name;
> +		iter->func = efi_bootmenu_volume_selected;
> +		iter->data = info;
> +		iter++;
> +	}
> +
> +	iter->title = u16_strdup(u"Quit");
> +	iter->func = NULL;
> +	iter->data = NULL;
> +	count += 1;
> +
> +	ret = efi_bootmenu_process_common(menu_item, count, -1);
> +
> +out2:
> +	iter = menu_item;
> +	for (i = 0; i < count; i++) {
> +		struct efi_bootmenu_volume_entry_data *p;
> +
> +		p = (struct efi_bootmenu_volume_entry_data *)(iter->data);
> +		free(iter->title);
> +		free(p);
> +		iter++;
> +	}
> +
> +	free(menu_item);
> +
> +out1:
> +	efi_free_pool(volume_handles);
> +
> +	return ret;
> +}
> +
> +static efi_status_t efi_bootmenu_select_file(struct efi_bootmenu_boot_option *bo,
> +					     struct efi_file_handle *root)
> +{
> +	u32 i;
> +	struct efi_file_info *buf;
> +	u32 count = 0;
> +	efi_uintn_t len;
> +	efi_status_t ret;
> +	struct efi_file_handle *f;
> +	struct efi_bootmenu_item *menu_item, *iter;
> +
> +	buf = calloc(1, sizeof(struct efi_file_info) + EFI_BOOTMENU_FILE_PATH_BUF_SIZE);
> +	if (!buf)
> +		return EFI_OUT_OF_RESOURCES;
> +
> +	while (!bo->file_selected) {
> +		count = 0;
> +
> +		ret = efi_file_open_int(root, &f, bo->current_path, EFI_FILE_MODE_READ, 0);
> +		if (ret != EFI_SUCCESS)
> +			return ret;
> +
> +		/* calculate directory information total count */
> +		for (;;) {
> +			len = sizeof(struct efi_file_info) + EFI_BOOTMENU_FILE_PATH_BUF_SIZE;
> +			ret = efi_file_read_int(f, &len, buf);
> +			if (ret != EFI_SUCCESS || len == 0)
> +				break;
> +
> +			count++;
> +		}
> +
> +		menu_item = calloc(count + 1, sizeof(struct efi_bootmenu_item));
> +		if (!menu_item) {
> +			efi_file_close_int(f);
> +			ret = EFI_OUT_OF_RESOURCES;
> +			goto out;
> +		}
> +
> +		/* read directory and construct menu structure */
> +		efi_file_setpos_int(f, 0);
> +		iter = menu_item;
> +		for (i = 0; i < count; i++) {
> +			u16 *name;
> +			int name_len;
> +			struct efi_bootmenu_file_entry_data *info;
> +
> +			len = sizeof(struct efi_file_info) + EFI_BOOTMENU_FILE_PATH_BUF_SIZE;
> +			ret = efi_file_read_int(f, &len, buf);
> +			if (ret != EFI_SUCCESS || len == 0)
> +				goto err;
> +
> +			info = calloc(1, sizeof(struct efi_bootmenu_file_entry_data));
> +			if (!info) {
> +				ret = EFI_OUT_OF_RESOURCES;
> +				goto err;
> +			}
> +
> +			if (buf->attribute & EFI_FILE_DIRECTORY) {
> +				/* append u'/' at the end of directory name */
> +				name_len = u16_strsize(buf->file_name) + sizeof(u16);
> +				name = calloc(1, name_len);
> +				if (!name) {
> +					ret = EFI_OUT_OF_RESOURCES;
> +					goto err;
> +				}
> +				u16_strcpy(name, buf->file_name);
> +				name[u16_strlen(buf->file_name)] = u'/';
> +
> +				info->is_directory = true;
> +			} else {
> +				name_len = u16_strsize(buf->file_name);
> +				name = calloc(1, name_len);
> +				if (!name) {
> +					ret = EFI_OUT_OF_RESOURCES;
> +					goto err;
> +				}
> +				u16_strcpy(name, buf->file_name);
> +			}
> +
> +			info->file_name = u16_strdup(buf->file_name);
> +			info->bo = bo;
> +			iter->title = name;
> +			iter->func = efi_bootmenu_file_selected;
> +			iter->data = info;
> +			iter++;
> +		}
> +
> +		/* add "Quit" entry */
> +		iter->title = u"Quit";
> +		iter->func = NULL;
> +		iter->data = NULL;
> +		count += 1;
> +
> +		ret = efi_bootmenu_process_common(menu_item, count, -1);
> +err:
> +		efi_file_close_int(f);
> +		iter = menu_item;
> +		for (i = 0; i < count - 1; i++, iter++) {
> +			free(((struct efi_bootmenu_file_entry_data *)(iter->data))->file_name);
> +			free(iter->title);
> +			free(iter->data);
> +		}
> +
> +		free(menu_item);
> +
> +		if (ret != EFI_SUCCESS)
> +			break;
> +	}
> +
> +out:
> +	free(buf);
> +	return ret;
> +}
> +
> +static efi_status_t efi_bootmenu_boot_add_enter_name(struct efi_bootmenu_boot_option *bo)
> +{
> +	efi_status_t ret;
> +
> +	printf(ANSI_CURSOR_POSITION, 2, 1);
> +	puts("  *** U-Boot EFI Boot Manager Menu ***");
> +	printf(ANSI_CURSOR_POSITION, 4, 1);
> +	puts("  enter name:");
> +
> +	printf(ANSI_CURSOR_POSITION, 8, 1);
> +	puts("  ENTER to complete, ESC/CTRL+C to quit");
> +
> +	ret = efi_console_get_u16_string(cin, cout, bo->boot_name,
> +					 EFI_BOOTMENU_BOOT_NAME_MAX, NULL, 4, 15);
> +
> +	return ret;
> +}
> +
> +static efi_status_t efi_bootmenu_select_file_handler(struct efi_bootmenu_boot_option *bo)
> +{
> +	efi_status_t ret;
> +	struct efi_file_handle *root;
> +
> +	bo->file_selected = false;
> +
> +	while (!bo->file_selected) {
> +		bo->current_volume = NULL;
> +		memset(bo->current_path, 0, sizeof(bo->current_path));
> +
> +		ret = efi_bootmenu_select_volume(bo);
> +		if (ret != EFI_SUCCESS)
> +			return ret;
> +
> +		if (!bo->current_volume)
> +			return EFI_INVALID_PARAMETER;
> +
> +		ret = efi_open_volume_int(bo->current_volume, &root);
> +		if (ret != EFI_SUCCESS)
> +			return ret;
> +
> +		ret = efi_bootmenu_select_file(bo, root);
> +		if (ret != EFI_SUCCESS)
> +			return ret;
> +	}
> +
> +	ret = efi_bootmenu_boot_add_enter_name(bo);
> +
> +	return ret;
> +}
> +
> +efi_status_t efi_bootmenu_get_unused_bootoption(u16 *buf,
> +						efi_uintn_t buf_size, u32 *index)
> +{
> +	u32 i;
> +	efi_status_t ret;
> +	efi_uintn_t size;
> +
> +	if (buf_size < u16_strsize(u"Boot####"))
> +		return EFI_BUFFER_TOO_SMALL;
> +
> +	for (i = 0; i <= 0xFFFF; i++) {
> +		size = 0;
> +		efi_create_indexed_name(buf, buf_size, "Boot", i);
> +		ret = efi_get_variable_int(buf, &efi_global_variable_guid,
> +					   NULL, &size, NULL, NULL);
> +		if (ret == EFI_BUFFER_TOO_SMALL)
> +			continue;
> +		else
> +			break;
> +	}
> +
> +	if (i > 0xFFFF)
> +		return EFI_OUT_OF_RESOURCES;
> +
> +	*index = i;
> +
> +	return EFI_SUCCESS;
> +}
> +
> +static efi_status_t efi_bootmenu_set_boot_option(u16 *var_name, struct efi_device_path *dp,
> +						 u16 *label, char *optional_data)
> +{
> +	void *p = NULL;
> +	efi_status_t ret;
> +	efi_uintn_t size;
> +	struct efi_load_option lo;
> +
> +	lo.file_path = dp;
> +	lo.file_path_length = efi_dp_size(dp) + sizeof(END);
> +	lo.attributes = LOAD_OPTION_ACTIVE;
> +	lo.optional_data = optional_data;
> +	lo.label = label;
> +
> +	size = efi_serialize_load_option(&lo, (u8 **)&p);
> +	if (!size)
> +		return EFI_INVALID_PARAMETER;
> +
> +	ret = efi_set_variable_int(var_name, &efi_global_variable_guid,
> +				   EFI_VARIABLE_NON_VOLATILE |
> +				   EFI_VARIABLE_BOOTSERVICE_ACCESS |
> +				   EFI_VARIABLE_RUNTIME_ACCESS,
> +				   size, p, false);
> +	free(p);
> +
> +	return ret;
> +}
> +
> +efi_status_t efi_bootmenu_append_bootorder(u16 index)
> +{
> +	u16 *bootorder;
> +	efi_status_t ret;
> +	u16 *new_bootorder = NULL;
> +	efi_uintn_t last, size, new_size;
> +
> +	/* append new boot option */
> +	bootorder = efi_get_var(u"BootOrder", &efi_global_variable_guid, &size);
> +	last = size / sizeof(u16);
> +	new_size = size + sizeof(u16);
> +	new_bootorder = calloc(1, new_size);
> +	if (!new_bootorder) {
> +		ret = EFI_OUT_OF_RESOURCES;
> +		goto out;
> +	}
> +	memcpy(new_bootorder, bootorder, size);
> +	new_bootorder[last] = index;
> +
> +	ret = efi_set_variable_int(u"BootOrder", &efi_global_variable_guid,
> +				   EFI_VARIABLE_NON_VOLATILE |
> +				   EFI_VARIABLE_BOOTSERVICE_ACCESS |
> +				   EFI_VARIABLE_RUNTIME_ACCESS,
> +				   new_size, new_bootorder, false);
> +	if (ret != EFI_SUCCESS)
> +		goto out;
> +
> +out:
> +	free(bootorder);
> +	free(new_bootorder);
> +
> +	return ret;
> +}
> +
> +static efi_status_t efi_bootmenu_process_add_boot_option(void *data, bool *exit)
> +{
> +	u32 index;
> +	u16 var_name[9];
> +	char *buf = NULL;
> +	efi_status_t ret;
> +	char *iter = NULL;
> +	efi_uintn_t dp_size, fp_size;
> +	struct efi_bootmenu_boot_option bo;
> +	struct efi_device_path_file_path *fp;
> +
> +	ret = efi_bootmenu_get_unused_bootoption(var_name, sizeof(var_name),
> +						 &index);
> +	if (ret != EFI_SUCCESS)
> +		return ret;
> +
> +	bo.current_path = calloc(1, EFI_BOOTMENU_FILE_PATH_BUF_SIZE);
> +	if (!bo.current_path)
> +		goto out;
> +
> +	bo.boot_name = calloc(1, EFI_BOOTMENU_BOOT_NAME_MAX * sizeof(u16));
> +	if (!bo.boot_name)
> +		goto out;
> +
> +	ret = efi_bootmenu_select_file_handler(&bo);
> +	if (ret != EFI_SUCCESS)
> +		goto out;
> +
> +	dp_size = efi_dp_size(bo.dp_volume);
> +	fp_size = sizeof(struct efi_device_path) +
> +		  ((u16_strlen(bo.current_path) + 1) * sizeof(u16));
> +	buf = calloc(1, dp_size + fp_size + sizeof(END));
> +	if (!buf)
> +		goto out;
> +
> +	iter = buf;
> +	memcpy(iter, bo.dp_volume, dp_size);
> +	iter += dp_size;
> +
> +	fp = (struct efi_device_path_file_path *)iter;
> +	fp->dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE;
> +	fp->dp.sub_type = DEVICE_PATH_SUB_TYPE_FILE_PATH;
> +	fp->dp.length = (u16)fp_size;
> +	u16_strcpy(fp->str, bo.current_path);
> +	iter += fp_size;
> +	*((struct efi_device_path *)iter) = END;
> +
> +	ret = efi_bootmenu_set_boot_option(var_name, (struct efi_device_path *)buf,
> +					   bo.boot_name, NULL);
> +	if (ret != EFI_SUCCESS)
> +		goto out;
> +
> +	efi_bootmenu_append_bootorder((u16)index);
> +	if (ret != EFI_SUCCESS)
> +		goto out;
> +
> +out:
> +	free(buf);
> +	free(bo.boot_name);
> +	free(bo.current_path);
> +
> +	return ret;
> +}
> +
> +static efi_status_t efi_bootmenu_init(void)
> +{
> +	efi_status_t ret;
> +	struct efi_handler *handler;
> +
> +	ret = efi_search_protocol(efi_root, &efi_guid_text_input_protocol, &handler);
> +	if (ret != EFI_SUCCESS)
> +		return ret;
> +
> +	ret = efi_protocol_open(handler, (void **)&cin, efi_root, NULL,
> +				EFI_OPEN_PROTOCOL_GET_PROTOCOL);
> +	if (ret != EFI_SUCCESS)
> +		return ret;
> +
> +	ret = efi_search_protocol(efi_root, &efi_guid_text_output_protocol, &handler);
> +	if (ret != EFI_SUCCESS)
> +		return ret;
> +
> +	ret = efi_protocol_open(handler, (void **)&cout, efi_root, NULL,
> +				EFI_OPEN_PROTOCOL_GET_PROTOCOL);
> +	if (ret != EFI_SUCCESS)
> +		return ret;
> +
> +	return ret;
> +}
> +
> +static const struct efi_bootmenu_item maintenance_menu_items[] = {
> +	{u"Add Boot Option", efi_bootmenu_process_add_boot_option},
> +	{u"Quit", NULL},
> +};
> +
> +efi_status_t efi_bootmenu_show_maintenance_menu(void)
> +{
> +	efi_status_t ret;
> +
> +	ret = efi_bootmenu_init();
> +	if (ret != EFI_SUCCESS)
> +		return ret;
> +
> +	return efi_bootmenu_process_common(maintenance_menu_items,
> +					  ARRAY_SIZE(maintenance_menu_items),
> +					  -1);
> +}
> diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c
> index 4da64b5d29..1233418e77 100644
> --- a/lib/efi_loader/efi_boottime.c
> +++ b/lib/efi_loader/efi_boottime.c
> @@ -2453,6 +2453,35 @@ static efi_status_t EFIAPI efi_protocols_per_handle(
>   	return EFI_EXIT(EFI_SUCCESS);
>   }
>
> +efi_status_t efi_locate_handle_buffer_int(enum efi_locate_search_type search_type,
> +					  const efi_guid_t *protocol, void *search_key,
> +					  efi_uintn_t *no_handles, efi_handle_t **buffer)
> +{
> +	efi_status_t r;
> +	efi_uintn_t buffer_size = 0;
> +
> +	if (!no_handles || !buffer) {
> +		r = EFI_INVALID_PARAMETER;
> +		goto out;
> +	}
> +	*no_handles = 0;
> +	*buffer = NULL;
> +	r = efi_locate_handle(search_type, protocol, search_key, &buffer_size,
> +			      *buffer);
> +	if (r != EFI_BUFFER_TOO_SMALL)
> +		goto out;
> +	r = efi_allocate_pool(EFI_BOOT_SERVICES_DATA, buffer_size,
> +			      (void **)buffer);
> +	if (r != EFI_SUCCESS)
> +		goto out;
> +	r = efi_locate_handle(search_type, protocol, search_key, &buffer_size,
> +			      *buffer);
> +	if (r == EFI_SUCCESS)
> +		*no_handles = buffer_size / sizeof(efi_handle_t);
> +out:
> +	return r;
> +}
> +
>   /**
>    * efi_locate_handle_buffer() - locate handles implementing a protocol
>    * @search_type: selection criterion
> @@ -2474,30 +2503,13 @@ efi_status_t EFIAPI efi_locate_handle_buffer(
>   			efi_uintn_t *no_handles, efi_handle_t **buffer)
>   {
>   	efi_status_t r;
> -	efi_uintn_t buffer_size = 0;
>
>   	EFI_ENTRY("%d, %pUs, %p, %p, %p", search_type, protocol, search_key,
>   		  no_handles, buffer);
>
> -	if (!no_handles || !buffer) {
> -		r = EFI_INVALID_PARAMETER;
> -		goto out;
> -	}
> -	*no_handles = 0;
> -	*buffer = NULL;
> -	r = efi_locate_handle(search_type, protocol, search_key, &buffer_size,
> -			      *buffer);
> -	if (r != EFI_BUFFER_TOO_SMALL)
> -		goto out;
> -	r = efi_allocate_pool(EFI_BOOT_SERVICES_DATA, buffer_size,
> -			      (void **)buffer);
> -	if (r != EFI_SUCCESS)
> -		goto out;
> -	r = efi_locate_handle(search_type, protocol, search_key, &buffer_size,
> -			      *buffer);
> -	if (r == EFI_SUCCESS)
> -		*no_handles = buffer_size / sizeof(efi_handle_t);
> -out:
> +	r = efi_locate_handle_buffer_int(search_type, protocol, search_key,
> +					 no_handles, buffer);
> +
>   	return EFI_EXIT(r);
>   }
>
> diff --git a/lib/efi_loader/efi_console.c b/lib/efi_loader/efi_console.c
> index ba68a15017..f5002e1c99 100644
> --- a/lib/efi_loader/efi_console.c
> +++ b/lib/efi_loader/efi_console.c
> @@ -5,6 +5,7 @@
>    *  Copyright (c) 2016 Alexander Graf
>    */
>
> +#include <ansi.h>
>   #include <common.h>
>   #include <charset.h>
>   #include <malloc.h>
> @@ -1312,3 +1313,83 @@ out_of_memory:
>   	printf("ERROR: Out of memory\n");
>   	return r;
>   }
> +
> +/**
> + * efi_console_get_u16_string() - get user input string
> + *
> + * @cin:		protocol interface to EFI_SIMPLE_TEXT_INPUT_PROTOCOL
> + * @cout:		protocol interface to EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
> + * @buf:		buffer to store user input string in UTF16
> + * @size:		buffer size including NULL terminator
> + * @filter_func:	callback to filter user input
> + * @row:		row number to locate user input form
> + * @col:		column number to locate user input form
> + * Return:		status code
> + */
> +efi_status_t efi_console_get_u16_string(struct efi_simple_text_input_protocol *cin,
> +					struct efi_simple_text_output_protocol *cout,
> +					u16 *buf, efi_uintn_t size,
> +					efi_console_filter_func filter_func,
> +					int row, int col)
> +{
> +	efi_status_t ret;
> +	efi_uintn_t len = 0;
> +	struct efi_input_key key;
> +
> +	printf(ANSI_CURSOR_POSITION, row, col);
> +	puts(ANSI_CLEAR_LINE_TO_END);
> +	puts(ANSI_CURSOR_SHOW);
> +
> +	ret = EFI_CALL(cin->reset(cin, false));
> +	if (ret != EFI_SUCCESS)
> +		return ret;
> +
> +	for (;;) {
> +		do {
> +			ret = EFI_CALL(cin->read_key_stroke(cin, &key));
> +			mdelay(10);
> +		} while (ret == EFI_NOT_READY);
> +
> +		if (key.unicode_char == u'\b') {
> +			if (len > 0)
> +				buf[--len] = u'\0';
> +
> +			printf(ANSI_CURSOR_POSITION, row, col);
> +			ret = EFI_CALL(cout->output_string(cout, buf));
> +			if (ret != EFI_SUCCESS)
> +				return ret;
> +
> +			puts(ANSI_CLEAR_LINE_TO_END);
> +			continue;
> +		} else if (key.unicode_char == u'\r') {
> +			if (len == 0) /* no user input */
> +				continue;
> +
> +			buf[len] = u'\0';
> +			return EFI_SUCCESS;
> +		} else if (key.unicode_char == 0x3 || key.scan_code == 23) {
> +			return EFI_ABORTED;
> +		} else if (key.unicode_char < 0x20) {
> +			/* ignore control codes other than Ctrl+C, '\r' and '\b' */
> +			continue;
> +		} else if (key.scan_code != 0) {
> +			/* only accept single ESC press for cancel */
> +			continue;
> +		}
> +
> +		if (filter_func) {
> +			if (filter_func(&key) != EFI_SUCCESS)
> +				continue;
> +		}
> +
> +		if (len >= (size - 1))
> +			continue;
> +
> +		buf[len] = key.unicode_char;
> +		len++;
> +		printf(ANSI_CURSOR_POSITION, row, col);
> +		ret = EFI_CALL(cout->output_string(cout, buf));
> +		if (ret != EFI_SUCCESS)
> +			return ret;
> +	}
> +}
> diff --git a/lib/efi_loader/efi_disk.c b/lib/efi_loader/efi_disk.c
> index 8fb5b2363c..58736a8a5b 100644
> --- a/lib/efi_loader/efi_disk.c
> +++ b/lib/efi_loader/efi_disk.c
> @@ -750,3 +750,14 @@ efi_status_t efi_disk_init(void)
>
>   	return EFI_SUCCESS;
>   }
> +
> +efi_status_t efi_disk_get_device_name(struct efi_block_io *this, char *buf, int size)
> +{
> +	struct efi_disk_obj *diskobj;
> +
> +	diskobj = container_of(this, struct efi_disk_obj, ops);
> +
> +	snprintf(buf, size, "%s%d:%d", diskobj->ifname, diskobj->dev_index, diskobj->part);

A space would improve readability and better match U-Boot syntax.
%s/%s%d:%d/%s %d:%d/

I guess for MMC we are only supporting booting form the user partition.
Otherwise the information would be incomplete.

Best regards

Heinrich

> +
> +	return EFI_SUCCESS;
> +}
> diff --git a/lib/efi_loader/efi_file.c b/lib/efi_loader/efi_file.c
> index 7a7077e6d0..c96a7f7ca3 100644
> --- a/lib/efi_loader/efi_file.c
> +++ b/lib/efi_loader/efi_file.c
> @@ -246,10 +246,10 @@ error:
>   	return NULL;
>   }
>
> -static efi_status_t efi_file_open_int(struct efi_file_handle *this,
> -				      struct efi_file_handle **new_handle,
> -				      u16 *file_name, u64 open_mode,
> -				      u64 attributes)
> +efi_status_t efi_file_open_int(struct efi_file_handle *this,
> +			       struct efi_file_handle **new_handle,
> +			       u16 *file_name, u64 open_mode,
> +			       u64 attributes)
>   {
>   	struct file_handle *fh = to_fh(this);
>   	efi_status_t ret;
> @@ -369,11 +369,17 @@ static efi_status_t file_close(struct file_handle *fh)
>   	return EFI_SUCCESS;
>   }
>
> -static efi_status_t EFIAPI efi_file_close(struct efi_file_handle *file)
> +efi_status_t efi_file_close_int(struct efi_file_handle *file)
>   {
>   	struct file_handle *fh = to_fh(file);
> +
> +	return file_close(fh);
> +}
> +
> +static efi_status_t EFIAPI efi_file_close(struct efi_file_handle *file)
> +{
>   	EFI_ENTRY("%p", file);
> -	return EFI_EXIT(file_close(fh));
> +	return EFI_EXIT(efi_file_close_int(file));
>   }
>
>   static efi_status_t EFIAPI efi_file_delete(struct efi_file_handle *file)
> @@ -562,8 +568,8 @@ static efi_status_t dir_read(struct file_handle *fh, u64 *buffer_size,
>   	return EFI_SUCCESS;
>   }
>
> -static efi_status_t efi_file_read_int(struct efi_file_handle *this,
> -				      efi_uintn_t *buffer_size, void *buffer)
> +efi_status_t efi_file_read_int(struct efi_file_handle *this,
> +			       efi_uintn_t *buffer_size, void *buffer)
>   {
>   	struct file_handle *fh = to_fh(this);
>   	efi_status_t ret = EFI_SUCCESS;
> @@ -773,24 +779,11 @@ out:
>   	return EFI_EXIT(ret);
>   }
>
> -/**
> - * efi_file_setpos() - set current position in file
> - *
> - * This function implements the SetPosition service of the EFI file protocol.
> - * See the UEFI spec for details.
> - *
> - * @file:	file handle
> - * @pos:	new file position
> - * Return:	status code
> - */
> -static efi_status_t EFIAPI efi_file_setpos(struct efi_file_handle *file,
> -					   u64 pos)
> +efi_status_t efi_file_setpos_int(struct efi_file_handle *file, u64 pos)
>   {
>   	struct file_handle *fh = to_fh(file);
>   	efi_status_t ret = EFI_SUCCESS;
>
> -	EFI_ENTRY("%p, %llu", file, pos);
> -
>   	if (fh->isdir) {
>   		if (pos != 0) {
>   			ret = EFI_UNSUPPORTED;
> @@ -812,6 +805,28 @@ static efi_status_t EFIAPI efi_file_setpos(struct efi_file_handle *file,
>   	fh->offset = pos;
>
>   error:
> +	return ret;
> +}
> +
> +/**
> + * efi_file_setpos() - set current position in file
> + *
> + * This function implements the SetPosition service of the EFI file protocol.
> + * See the UEFI spec for details.
> + *
> + * @file:	file handle
> + * @pos:	new file position
> + * Return:	status code
> + */
> +static efi_status_t EFIAPI efi_file_setpos(struct efi_file_handle *file,
> +					   u64 pos)
> +{
> +	efi_status_t ret = EFI_SUCCESS;
> +
> +	EFI_ENTRY("%p, %llu", file, pos);
> +
> +	ret = efi_file_setpos_int(file, pos);
> +
>   	return EFI_EXIT(ret);
>   }
>
> @@ -1138,17 +1153,23 @@ struct efi_file_handle *efi_file_from_path(struct efi_device_path *fp)
>   	return f;
>   }
>
> +efi_status_t efi_open_volume_int(struct efi_simple_file_system_protocol *this,
> +				 struct efi_file_handle **root)
> +{
> +	struct file_system *fs = to_fs(this);
> +
> +	*root = file_open(fs, NULL, NULL, 0, 0);
> +
> +	return EFI_SUCCESS;
> +}
> +
>   static efi_status_t EFIAPI
>   efi_open_volume(struct efi_simple_file_system_protocol *this,
>   		struct efi_file_handle **root)
>   {
> -	struct file_system *fs = to_fs(this);
> -
>   	EFI_ENTRY("%p, %p", this, root);
>
> -	*root = file_open(fs, NULL, NULL, 0, 0);
> -
> -	return EFI_EXIT(EFI_SUCCESS);
> +	return EFI_EXIT(efi_open_volume_int(this, root));
>   }
>
>   struct efi_simple_file_system_protocol *


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

* Re: [PATCH v5 16/17] bootmenu: add removable media entries
  2022-04-28  8:09 ` [PATCH v5 16/17] bootmenu: add removable media entries Masahisa Kojima
@ 2022-04-28 16:53   ` Heinrich Schuchardt
  2022-05-09  8:23     ` Masahisa Kojima
  0 siblings, 1 reply; 58+ messages in thread
From: Heinrich Schuchardt @ 2022-04-28 16:53 UTC (permalink / raw)
  To: Masahisa Kojima
  Cc: Ilias Apalodimas, Simon Glass, Takahiro Akashi, Francois Ozog,
	Mark Kettenis, u-boot

On 4/28/22 10:09, Masahisa Kojima wrote:
> UEFI specification requires booting from removal media using
> a architecture-specific default image name such as BOOTAA64.EFI.
> This commit adds the removable media entries into bootmenu,
> so that user can select the removable media and boot with
> default image.
>
> The bootmenu automatically enumerates the possible bootable
> media devices supporting EFI_SIMPLE_FILE_SYSTEM_PROTOCOL,
> add it as new UEFI boot option(BOOT####) and update BootOrder
> variable. This automatically generated UEFI boot option
> has the dedicated guid in the optional_data to distinguish it from
> the UEFI boot option user adds manually.
>
> This commit also provides the BOOT#### variable maintenance feature.
> Depending on the system hardware setup, some devices
> may not exist at a later system boot, so bootmenu checks the
> available device in each bootmenu invocation and automatically
> removes the BOOT#### variable corrensponding to the non-existent
> media device.
>
> Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
> ---
> Changes in v5:
> - Return EFI_SUCCESS if there is no BootOrder defined
> - correctly handle the case if no removable device found
> - use guid to identify the automatically generated entry by bootmenu
>
> Newly created in v4
>
>   cmd/bootmenu.c                            |  94 +++++++++++++++
>   include/efi_loader.h                      |  20 ++++
>   lib/efi_loader/efi_bootmenu_maintenance.c | 139 ++++++++++++++++++++++
>   3 files changed, 253 insertions(+)
>
> diff --git a/cmd/bootmenu.c b/cmd/bootmenu.c
> index 860cb83182..970db3ee01 100644
> --- a/cmd/bootmenu.c
> +++ b/cmd/bootmenu.c
> @@ -396,6 +396,89 @@ static int is_blk_device_available(char *token)
>   	return -ENODEV;
>   }
>
> +/**
> + * prepare_media_device_entry() - generate the media device entries
> + *
> + * This function enumerates all devices supporting EFI_SIMPLE_FILE_SYSTEM_PROTOCOL
> + * and generate the bootmenu entries.
> + * This function also provide the BOOT#### variable maintenance for
> + * the media device entries.
> + *   - Automatically create the BOOT#### variable for the newly detected device,
> + *     this BOOT#### variable is distinguished by the special GUID
> + *     stored in the EFI_LOAD_OPTION.optional_data
> + *   - If the device is not attached to the system, the associated BOOT#### variable
> + *     is automatically deleted.
> + *
> + * Return:	status code
> + */
> +static efi_status_t prepare_media_device_entry(void)
> +{
> +	u32 i;
> +	efi_status_t ret;
> +	efi_uintn_t count;
> +	efi_handle_t *volume_handles = NULL;
> +	struct efi_bootmenu_media_boot_option *opt = NULL;
> +
> +	ret = efi_locate_handle_buffer_int(BY_PROTOCOL, &efi_simple_file_system_protocol_guid,
> +					   NULL, &count, (efi_handle_t **)&volume_handles);
> +	if (ret != EFI_SUCCESS)
> +		return ret;
> +
> +	opt = calloc(count, sizeof(struct efi_bootmenu_media_boot_option));
> +	if (!opt)
> +		goto out;
> +
> +	/* enumerate all devices supporting EFI_SIMPLE_FILE_SYSTEM_PROTOCOL */
> +	ret = efi_bootmenu_enumerate_boot_option(opt, volume_handles, count);
> +	if (ret != EFI_SUCCESS)
> +		goto out;
> +
> +	/*
> +	 * System hardware configuration may vary depending on the user setup.
> +	 * The boot option is automatically added by the bootmenu.
> +	 * If the device is not attached to the system, the boot option needs
> +	 * to be deleted.
> +	 */
> +	ret = efi_bootmenu_delete_invalid_boot_option(opt, count);
> +	if (ret != EFI_SUCCESS)
> +		goto out;
> +
> +	/* add non-existent boot option */
> +	for (i = 0; i < count; i++) {
> +		u32 boot_index;
> +		u16 var_name[9];
> +
> +		if (!opt[i].exist) {
> +			ret = efi_bootmenu_get_unused_bootoption(var_name, sizeof(var_name),
> +								 &boot_index);
> +			if (ret != EFI_SUCCESS)
> +				goto out;
> +
> +			ret = efi_set_variable_int(var_name, &efi_global_variable_guid,
> +						   EFI_VARIABLE_NON_VOLATILE |
> +						   EFI_VARIABLE_BOOTSERVICE_ACCESS |
> +						   EFI_VARIABLE_RUNTIME_ACCESS,
> +						   opt[i].size, opt[i].lo, false);
> +			if (ret != EFI_SUCCESS)
> +				goto out;
> +
> +			ret = efi_bootmenu_append_bootorder(boot_index);
> +			if (ret != EFI_SUCCESS)
> +				goto out;
> +		}
> +	}
> +
> +out:
> +	if (opt) {
> +		for (i = 0; i < count; i++)
> +			free(opt[i].lo);
> +	}
> +	free(opt);
> +	efi_free_pool(volume_handles);
> +
> +	return ret;
> +}
> +
>   /**
>    * prepare_distro_boot_entry() - generate the distro boot entries
>    *
> @@ -500,6 +583,7 @@ static int prepare_distro_boot_entry(struct bootmenu_data *menu,
>   static struct bootmenu_data *bootmenu_create(int delay)
>   {
>   	int ret;
> +	efi_status_t efi_ret;
>   	unsigned short int i = 0;
>   	struct bootmenu_data *menu;
>   	struct bootmenu_entry *iter = NULL;
> @@ -523,6 +607,16 @@ static struct bootmenu_data *bootmenu_create(int delay)
>   		goto cleanup;
>
>   	if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR)) {
> +		if (i < MAX_DYNAMIC_ENTRY) {
> +			/*
> +			 * UEFI specification requires booting from removal media using
> +			 * a architecture-specific default image name such as BOOTAA64.EFI.
> +			 */
> +			efi_ret = prepare_media_device_entry();
> +			if (efi_ret != EFI_SUCCESS && efi_ret != EFI_NOT_FOUND)
> +				goto cleanup;
> +		}
> +
>   		if (i < MAX_DYNAMIC_ENTRY) {
>   			ret = prepare_uefi_bootorder_entry(menu, &iter, &i);
>   			if (ret < 0 && ret != -ENOENT)
> diff --git a/include/efi_loader.h b/include/efi_loader.h
> index 533618341b..bef492ccb9 100644
> --- a/include/efi_loader.h
> +++ b/include/efi_loader.h
> @@ -928,6 +928,22 @@ struct efi_signature_store {
>   struct x509_certificate;
>   struct pkcs7_message;
>
> +/**
> + * struct efi_bootmenu_media_boot_option - boot option for (removable) media device
> + *
> + * This structure is used to enumerate possible boot option
> + *
> + * @lo:		Serialized load option data
> + * @size:	Size of serialized load option data
> + * @exist:	Flag to indicate the load option already exists
> + *		in Non-volatile load option
> + */
> +struct efi_bootmenu_media_boot_option {
> +	void *lo;
> +	efi_uintn_t size;
> +	bool exist;
> +};
> +
>   bool efi_signature_lookup_digest(struct efi_image_regions *regs,
>   				 struct efi_signature_store *db,
>   				 bool dbx);
> @@ -1076,6 +1092,10 @@ efi_status_t efi_console_get_u16_string
>   efi_status_t efi_bootmenu_get_unused_bootoption(u16 *buf,
>   						efi_uintn_t buf_size, u32 *index);
>   efi_status_t efi_bootmenu_append_bootorder(u16 index);
> +efi_status_t efi_bootmenu_enumerate_boot_option(struct efi_bootmenu_media_boot_option *opt,
> +						efi_handle_t *volume_handles, efi_status_t count);
> +efi_status_t efi_bootmenu_delete_invalid_boot_option(struct efi_bootmenu_media_boot_option *opt,
> +						     efi_status_t count);
>
>   efi_status_t efi_disk_get_device_name(struct efi_block_io *this, char *buf, int size);
>
> diff --git a/lib/efi_loader/efi_bootmenu_maintenance.c b/lib/efi_loader/efi_bootmenu_maintenance.c
> index 8c3f94c695..33b37fd11a 100644
> --- a/lib/efi_loader/efi_bootmenu_maintenance.c
> +++ b/lib/efi_loader/efi_bootmenu_maintenance.c
> @@ -26,6 +26,13 @@ static struct efi_simple_text_output_protocol *cout;
>   #define EFI_BOOTMENU_BOOT_NAME_MAX 32
>   #define EFI_BOOT_ORDER_MAX_SIZE_IN_DECIMAL 6
>
> +#define EFI_BOOTMENU_AUTO_GENERATED_ENTRY_GUID \
> +	EFI_GUID(0x38c1acc1, 0x9fc0, 0x41f0, \
> +		 0xb9, 0x01, 0xfa, 0x74, 0xd6, 0xd6, 0xe4, 0xde)
> +
> +static const efi_guid_t efi_guid_bootmenu_auto_generated =
> +		EFI_BOOTMENU_AUTO_GENERATED_ENTRY_GUID;
> +
>   typedef efi_status_t (*efi_bootmenu_entry_func)(void *data, bool *exit);
>
>   /**
> @@ -1104,3 +1111,135 @@ efi_status_t efi_bootmenu_show_maintenance_menu(void)
>   					  ARRAY_SIZE(maintenance_menu_items),
>   					  -1);
>   }
> +
> +efi_status_t efi_bootmenu_enumerate_boot_option(struct efi_bootmenu_media_boot_option *opt,
> +						efi_handle_t *volume_handles, efi_status_t count)
> +{
> +	u32 i;
> +	struct efi_handler *handler;
> +	efi_status_t ret = EFI_SUCCESS;
> +
> +	for (i = 0; i < count; i++) {
> +		char *optional_data;
> +		u16 *dev_name, *p;
> +		struct efi_load_option lo;
> +		struct efi_block_io *block_io;
> +		char buf[BOOTMENU_DEVICE_NAME_MAX];
> +		struct efi_device_path *device_path;
> +
> +		ret = efi_search_protocol(volume_handles[i], &efi_guid_device_path, &handler);
> +		if (ret != EFI_SUCCESS)
> +			continue;
> +		ret = efi_protocol_open(handler, (void **)&device_path,
> +					efi_root, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
> +		if (ret != EFI_SUCCESS)
> +			continue;
> +
> +		ret = efi_search_protocol(volume_handles[i], &efi_block_io_guid, &handler);
> +		if (ret != EFI_SUCCESS)
> +			continue;
> +		ret = efi_protocol_open(handler, (void **)&block_io,
> +					efi_root, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
> +		if (ret != EFI_SUCCESS)
> +			continue;
> +
> +		efi_disk_get_device_name(block_io, buf, BOOTMENU_DEVICE_NAME_MAX);
> +		dev_name = calloc(1, (strlen(buf) + 1) * sizeof(u16));
> +		if (!dev_name) {
> +			ret = EFI_OUT_OF_RESOURCES;
> +			goto out;
> +		}
> +		p = dev_name;
> +		utf8_utf16_strncpy(&p, buf, strlen(buf));
> +
> +		lo.label = dev_name;
> +		lo.attributes = LOAD_OPTION_ACTIVE;
> +		lo.file_path = device_path;
> +		lo.file_path_length = efi_dp_size(device_path) + sizeof(END);
> +		/*
> +		 * Set the dedicated guid to optional_data, it is used to identify
> +		 * the boot option that automatically generated by the bootmenu.
> +		 * efi_serialize_load_option() expects optional_data is null-terminated
> +		 * utf8 string, so set the "dummystr" string to allocate enough space
> +		 * to store guid, instead of realloc the load_option.
> +		 *
> +		 * This will allocate 16 bytes for guid plus trailing 0x0000.
> +		 * The guid does not require trailing 0x0000, but it is for safety
> +		 * in case some program handle the optional_data as u16 string.
> +		 */
> +		lo.optional_data = "dummystr";

Why do you want to reserve 18 bytes when 16 are enough for the GUID?

> +		opt[i].size = efi_serialize_load_option(&lo, (u8 **)&opt[i].lo);
> +		if (!opt[i].size) {
> +			ret = EFI_OUT_OF_RESOURCES;
> +			free(dev_name);
> +			goto out;
> +		}
> +		/* set the guid */
> +		optional_data = (char *)opt[i].lo + (opt[i].size - u16_strsize(u"dummystr"));
> +		memcpy(optional_data, &efi_guid_bootmenu_auto_generated, sizeof(efi_guid_t));
> +		free(dev_name);
> +	}
> +
> +out:
> +	return ret;
> +}
> +
> +efi_status_t efi_bootmenu_delete_invalid_boot_option(struct efi_bootmenu_media_boot_option *opt,
> +						     efi_status_t count)
> +{
> +	u16 *bootorder;
> +	u32 i, j;
> +	efi_status_t ret;
> +	efi_uintn_t num, size, bootorder_size;
> +	void *load_option;
> +	struct efi_load_option lo;
> +	u16 varname[] = u"Boot####";
> +
> +	bootorder = efi_get_var(u"BootOrder", &efi_global_variable_guid, &bootorder_size);
> +	if (!bootorder)
> +		return EFI_SUCCESS; /* BootOrder is not defined, nothing to do */
> +
> +	num = bootorder_size / sizeof(u16);
> +	for (i = 0; i < num;) {
> +		efi_uintn_t tmp;
> +
> +		efi_create_indexed_name(varname, sizeof(varname),
> +					"Boot", bootorder[i]);
> +		load_option = efi_get_var(varname, &efi_global_variable_guid, &size);
> +		if (!load_option)
> +			goto next;
> +
> +		tmp = size;

This copying of variables is superfluous. Just keep size.

> +		ret = efi_deserialize_load_option(&lo, load_option, &tmp);
> +		if (ret != EFI_SUCCESS)
> +			goto next;
> +
> +		if (guidcmp(lo.optional_data, &efi_guid_bootmenu_auto_generated) == 0) {

You must avoid a buffer overrun. Check size >= 16.

Best regards

Heinrich

> +			for (j = 0; j < count; j++) {
> +				if (memcmp(opt[j].lo, load_option, size) == 0) {
> +					opt[j].exist = true;
> +					break;
> +				}
> +			}
> +
> +			if (j == count) {
> +				ret = delete_boot_option(bootorder, i, bootorder_size);
> +				if (ret != EFI_SUCCESS) {
> +					free(load_option);
> +					goto out;
> +				}
> +
> +				num--;
> +				bootorder_size -= sizeof(u16);
> +				free(load_option);
> +				continue;
> +			}
> +		}
> +next:
> +		free(load_option);
> +		i++;
> +	}
> +
> +out:
> +	return ret;
> +}


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

* Re: [PATCH v5 00/17] enable menu-driven boot device selection
  2022-04-28 16:31 ` [PATCH v5 00/17] enable menu-driven boot device selection Heinrich Schuchardt
@ 2022-04-28 16:58   ` Heinrich Schuchardt
  0 siblings, 0 replies; 58+ messages in thread
From: Heinrich Schuchardt @ 2022-04-28 16:58 UTC (permalink / raw)
  To: Masahisa Kojima
  Cc: Ilias Apalodimas, Simon Glass, Takahiro Akashi, Francois Ozog,
	Mark Kettenis, u-boot

On 4/28/22 18:31, Heinrich Schuchardt wrote:
> On 4/28/22 10:09, Masahisa Kojima wrote:
>> This patch series adds the menu-driven boot device selection,
>> by extending the existing "bootmenu" to include UEFI and distro_boot
>> related entries, and supports menu-driven UEFI boot variable
>> maintenance.
>>
>> This patch series also includes the removable media support
>> that UEFI specification requires to support.
>>
>> The menu example is as follows.
>>
>>    *** U-Boot Boot Menu ***
>>
>>       bootmenu_00   : Boot 1. kernel
>>       bootmenu_01   : Boot 2. kernel
>>       bootmenu_02   : Reset board
>>       UEFI BOOT0000 : debian
>>       UEFI BOOT0001 : ubuntu
>>       UEFI BOOT0002 : mmc0:1
>>       UEFI BOOT0003 : mmc0:2
>>       UEFI BOOT0004 : nvme0:1
>>       UEFI BOOT0005 : nvme0:2
>>       UEFI BOOT0006 : usb0:2
>>       UEFI BOOT0007 : usb1:1
>>       UEFI BOOT0008 : usb1:2
>>       distro_boot   : usb0
>>       distro_boot   : scsi0
>>       distro_boot   : virtio0
>>       distro_boot   : dhcp
>>
>>    Press UP/DOWN to move, ENTER to select, ESC/CTRL+C to quit
>>
>> [How to run on QEMU(arm64)]
>> 1) clone source code
>>   $ git clone https://git.linaro.org/people/masahisa.kojima/u-boot.git \
>> -b kojima/bootmenu_v5_upstream_0428 --depth 1
>>
>> 2) prepare U-Boot .config
>>   $ make qemu_arm64_menuconfig
>>    then, enable CONFIG_CMD_BOOTMENU and CONFIG_AUTOBOOT_MENU_SHOW
>>
>> 3) run on QEMU(arm64) example
>>   $ qemu-system-aarch64 -machine virt,gic-version=3 -cpu cortex-a57 -m
>> 4G -nographic \
>>     -no-acpi -bios ./u-boot.bin -hda xxx.img
>>
>> [Major Changes]
>> - rebased to v2022.07-rc1
>> - there is detailed changelog in each commit
>>
>> AKASHI Takahiro (2):
>>    efi_loader: export efi_locate_device_handle()
>>    efi_loader: bootmgr: add booting from removable media
>>
>> Masahisa Kojima (15):
>>    lib/charset: add u16_strlcat() function
>>    test: unit test for u16_strlcat()
>>    menu: always show the menu regardless of the number of entry
>>    menu: menu_get_choice() return -ENOENT if menu item is empty
>>    bootmenu: flush input buffer before waiting user key input
>>    bootmenu: update bootmenu_entry structure
>>    bootmenu: add UEFI boot entry into bootmenu
>>    bootmenu: add distro boot entry
>>    bootmenu: add Kconfig option not to enter U-Boot console
>>    bootmenu: factor out the user input handling
>>    efi_loader: menu-driven addition of UEFI boot option
>>    efi_loader: menu-driven deletion of UEFI boot variable
>>    efi_loader: menu-driven update of UEFI bootorder variable
>>    bootmenu: add removable media entries
>>    doc:bootmenu: add UEFI boot and distro boot support description
>>
>>   cmd/Kconfig                               |   10 +
>>   cmd/bootmenu.c                            |  807 +++++++++----
>>   common/menu.c                             |  142 ++-
>>   doc/usage/cmd/bootmenu.rst                |   78 +-
>>   include/charset.h                         |   17 +
>>   include/config_distro_bootcmd.h           |   14 +-
>>   include/efi_default_filename.h            |   33 +
>>   include/efi_loader.h                      |   61 +
>>   include/menu.h                            |   20 +
>>   lib/charset.c                             |   22 +
>>   lib/efi_loader/Makefile                   |    1 +
>>   lib/efi_loader/efi_bootmenu_maintenance.c | 1245 +++++++++++++++++++++
>>   lib/efi_loader/efi_bootmgr.c              |   50 +-
>>   lib/efi_loader/efi_boottime.c             |   59 +-
>>   lib/efi_loader/efi_console.c              |   81 ++
>>   lib/efi_loader/efi_disk.c                 |   11 +
>>   lib/efi_loader/efi_file.c                 |   75 +-
>>   test/unicode_ut.c                         |   50 +
>>   18 files changed, 2518 insertions(+), 258 deletions(-)
>>   create mode 100644 include/efi_default_filename.h
>>   create mode 100644 lib/efi_loader/efi_bootmenu_maintenance.c
>>
>
> I prepared my test as follows:
>
> make sandbox_defconfig
> CONFIG_CMD_BOOTMENU=y
> CONFIG_AUTOBOOT_MENU_SHOW=y
> PREBOOT=host bind 0 ../sandbox.img
>
> I created ../sandbox.img as GTP partioned image with a single EF00
> partition. Onto the partition I copied lib/efi_loader/helloworld.efi
> into path EFI/BOOT/BOOTX64.EFI.
>
> I set up the following boot option:
>
> => efidebug boot dump
> Boot0001:
> attributes: A-- (0x00000001)
>    label: hello host 0:1
>    file_path:
> /HD(1,GPT,7e5c17c5-3f5f-49d0-ae96-511b21d7f273,0x800,0x3f7df)/EFI\BOOT\BOOTX64.EFI
>
>    data:
>      00000000: 6d 00 79 00 20 00 6d 00 65 00 73 00 73 00 61 00  m.y.
> .m.e.s.s.a.
>      00000010: 67 00 65 00 00 00                                g.e...
> => efidebug boot order
>   1: Boot0001: hello host 0:1
> => reset
>
> Now the boot menu looks like this:
>
>    *** U-Boot Boot Menu ***
>
>       UEFI BOOT0001 : hello host 0:1
>       UEFI BOOT0000 : host0:1
>       UEFI Boot Manager Maintenance
>       U-Boot console
>
> When I select nothing:
>
> try_load_entry: trying to load "hello host 0:1" from
> /HD(1,GPT,7e5c17c5-3f5f-49d0-ae96-511b21d7f273,0x800,0x3f7df)/EFI\BOOT\BOOTX64.EFI
>
> Booting: hello host 0:1
> Hello, world!
> Running on UEFI 2.9
> Have ACPI 2.0 table
> Have SMBIOS table
> Load options: my message
> Boot device:
> /VenHw(e61d73b9-a384-4acc-aeab-82e828f3628b)/VenHw(bbe4e671-5773-4ea1-9aab-3a7dbf40c482,00)/HD(1,GPT,7e5c17c5-3f5f-49d0-ae96-511b21d7f273,0x800,0x3f7df)
>
> File path: /EFI\BOOT\BOOTX64.EFI
>
> This is ok.
>
>    *** U-Boot Boot Menu ***
>
>       UEFI BOOT0001 : hello host 0:1
>       UEFI BOOT0000 : host0:1
>       UEFI Boot Manager Maintenance
>       U-Boot console
>
>
>    Press UP/DOWN to move, ENTER to select, ESC/CTRL+C to quit
>
> When I select the second item 'UEFI BOOT0000 : host0:1':
>
> try_load_entry: trying to load "host0:1" from
> /VenHw(e61d73b9-a384-4acc-aeab-82e828f3628b)/VenHw(bbe4e671-5773-4ea1-9aab-3a7dbf40c482,00)/HD(1,GPT,7e5c17c5-3f5f-49d0-ae96-511b21d7f273,0x800,0x3f7df)
>
> Loading Boot0000 'host0:1' failed
>
> After I fixed this in
> [PATCH 1/1] efi_loader: BOOTSANDBOX.EFI is not a default file name
> https://lists.denx.de/pipermail/u-boot/2022-April/482763.html
>
> Booting: host 0:1
> Hello, world!
> Running on UEFI 2.9
> Have ACPI 2.0 table
> Have SMBIOS table
> Load options: 곁㣁鿀䇰ƹ瓺훖?

After converting to hex:
acc1 38c1 9fc0 41f0 01b9 74fa d6d6 dee4

This is just your GUID:

#define EFI_BOOTMENU_AUTO_GENERATED_ENTRY_GUID \
EFI_GUID(0x38c1acc1, 0x9fc0, 0x41f0, \
	0xb9, 0x01, 0xfa, 0x74, 0xd6, 0xd6, 0xe4, 0xde)

You need to remove it from the load option.

Best regards

Heinrich

> Boot device:
> /VenHw(e61d73b9-a384-4acc-aeab-82e828f3628b)/VenHw(bbe4e671-5773-4ea1-9aab-3a7dbf40c482,00)/HD(1,GPT,7e5c17c5-3f5f-49d0-ae96-511b21d7f273,0x800,0x3f7df)
>
> File path: /\EFI\BOOT\BOOTX64.EFI
>
> See those Chinese letters. Something is going really wrong here.
>
> Best regards
>
> Heinrich
>
>
>
>
>
>
>


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

* Re: [PATCH v5 00/17] enable menu-driven boot device selection
  2022-04-28  8:09 [PATCH v5 00/17] enable menu-driven boot device selection Masahisa Kojima
                   ` (17 preceding siblings ...)
  2022-04-28 16:31 ` [PATCH v5 00/17] enable menu-driven boot device selection Heinrich Schuchardt
@ 2022-04-29  8:45 ` Mark Kettenis
  18 siblings, 0 replies; 58+ messages in thread
From: Mark Kettenis @ 2022-04-29  8:45 UTC (permalink / raw)
  To: Masahisa Kojima
  Cc: u-boot, xypron.glpk, ilias.apalodimas, sjg, takahiro.akashi,
	francois.ozog, masahisa.kojima

> From: Masahisa Kojima <masahisa.kojima@linaro.org>
> Date: Thu, 28 Apr 2022 17:09:33 +0900
> 
> This patch series adds the menu-driven boot device selection,
> by extending the existing "bootmenu" to include UEFI and distro_boot
> related entries, and supports menu-driven UEFI boot variable
> maintenance.
> 
> This patch series also includes the removable media support
> that UEFI specification requires to support.
> 
> The menu example is as follows.
> 
>   *** U-Boot Boot Menu ***
> 
>      bootmenu_00   : Boot 1. kernel
>      bootmenu_01   : Boot 2. kernel
>      bootmenu_02   : Reset board
>      UEFI BOOT0000 : debian
>      UEFI BOOT0001 : ubuntu
>      UEFI BOOT0002 : mmc0:1
>      UEFI BOOT0003 : mmc0:2
>      UEFI BOOT0004 : nvme0:1
>      UEFI BOOT0005 : nvme0:2
>      UEFI BOOT0006 : usb0:2
>      UEFI BOOT0007 : usb1:1
>      UEFI BOOT0008 : usb1:2
>      distro_boot   : usb0
>      distro_boot   : scsi0
>      distro_boot   : virtio0
>      distro_boot   : dhcp
> 
>   Press UP/DOWN to move, ENTER to select, ESC/CTRL+C to quit
> 
> [How to run on QEMU(arm64)]
> 1) clone source code
>  $ git clone https://git.linaro.org/people/masahisa.kojima/u-boot.git \
> -b kojima/bootmenu_v5_upstream_0428 --depth 1
> 
> 2) prepare U-Boot .config
>  $ make qemu_arm64_menuconfig
>   then, enable CONFIG_CMD_BOOTMENU and CONFIG_AUTOBOOT_MENU_SHOW

So when I do this on a Apple M1 system it ends up doing something
weird.  With CONFIG_AUTOBOOT_MENU_SHOW=y, it shows the boot menu and
then automaticaly selects the "UEFI Boot Manager Maintenance" option
after the boot delay expires.  That is a bit weird.  Initially I
expected it to fall through into the normal distro_bootcmd script by
executing bootcmd.

Now this happens because NVMe isn't probed automatically.  So no
viable boot options are found.  But distro_boot explicitly probes NVMe
if CONFIG_NVME is enabled (see include/config_distro_bootcmd.h).  So
falling through into the distro boot would actually have worked.

Another surprise is that the default boot order seems to depend on the
order in which devices are probed.  With:

  CONFIG_PREBOOT="nvme scan; usb start"

it boots from NVMe by default, and with:

  CONFIG_PREBOOT="usb start; nvme scan"

it boots from USB by default, despite the fact that in
include/configs/apple.h I have:

  #define BOOT_TARGET_DEVICES(func) \
          BOOT_TARGET_NVME(func) \
	  BOOT_TARGET_USB(func)

If I change the boot order through the "UEFI Boot Manager Maintenance"
option, it seems that the new boot order only persists if I actually
boot into the OS.  When I reset the board before doing that it keeps
the old boot order.

Last but not least, it seems that
CONFIG_CMD_BOOTMENU_ENTER_UBOOT_CONSOLE is disabled by default despite
the fact that doc/usage/cmd/bootmenu.rst suggests the opposite.  I
think the default for that option should be "y" otherwise I fear a lot
of boards will end up with shipping with a U-Boot with no way for
users to get at the u-boot prompt.

All in all, I don't think any of this is a show-stopper.  It just
needs some further polishing, which probably is best done after this
lands (except for the CMD_BOOTMENU_ENTER_UBOOT_CONSOLE thing).  It
does make it possible for me to use the EFI bootmgr on Apple M1
systems and it definitely improves the user interface for naive users.

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

* Re: [PATCH v5 11/17] bootmenu: add Kconfig option not to enter U-Boot console
  2022-04-28  8:09 ` [PATCH v5 11/17] bootmenu: add Kconfig option not to enter U-Boot console Masahisa Kojima
@ 2022-04-29  8:50   ` Mark Kettenis
  0 siblings, 0 replies; 58+ messages in thread
From: Mark Kettenis @ 2022-04-29  8:50 UTC (permalink / raw)
  To: Masahisa Kojima
  Cc: u-boot, xypron.glpk, ilias.apalodimas, sjg, takahiro.akashi,
	francois.ozog, masahisa.kojima, michal.simek, kory.maincent,
	ovidiu.panait, ashok.reddy.soma

> From: Masahisa Kojima <masahisa.kojima@linaro.org>
> Date: Thu, 28 Apr 2022 17:09:44 +0900
> 
> This commit adds the Kconfig option to disable to enter
> the U-Boot console from bootmenu.
> 
> If CMD_BOOTMENU_ENTER_UBOOT_CONSOLE is enabled, "U-Boot console"
> entry is appeared as the last entry in the bootmenu, then user can
> enter U-Boot console.
> 
> If CMD_BOOTMENU_ENTER_UBOOT_CONSOLE is disabled, "Quit" entry
> is appeared as the last entry instead of "U-Boot console".
> When user chooses "Quit" from bootmenu, the following default
> commands are invoked.
> 
>  - "bootefi bootmgr" (if efi bootmgr is enabled)
>  - "run bootcmd"
> 
> If the both commands are executed and returns to the bootmenu,
> the bootmenu will appears again.

I think the default for this option should be "y", otherwise I fear
too many boards will ship with a "locked down" U-Boot where the user
has no way to get at the U-Boot prompt.

> Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
> ---
> Changes in v5:
> - split into the separate patch
> - clear the console when user select "U-Boot console"
> - if the console is disabled, the last entry title is "Quit"
> 
>  cmd/Kconfig    | 10 ++++++++
>  cmd/bootmenu.c | 69 ++++++++++++++++++++++++++++++++++++++++++--------
>  2 files changed, 68 insertions(+), 11 deletions(-)
> 
> diff --git a/cmd/Kconfig b/cmd/Kconfig
> index 2b575a2b42..99a1435467 100644
> --- a/cmd/Kconfig
> +++ b/cmd/Kconfig
> @@ -356,6 +356,16 @@ config CMD_BOOTMENU
>  	help
>  	  Add an ANSI terminal boot menu command.
>  
> +config CMD_BOOTMENU_ENTER_UBOOT_CONSOLE
> +	bool "Allow Bootmenu to enter the U-Boot console"
> +	depends on CMD_BOOTMENU
> +	default n
> +	help
> +	  Add an entry to enter U-Boot console in bootmenu.
> +	  If this option is disabled, user can not enter
> +	  the U-Boot console from bootmenu. It increases
> +	  the system security.
> +
>  config CMD_ADTIMG
>  	bool "adtimg"
>  	help
> diff --git a/cmd/bootmenu.c b/cmd/bootmenu.c
> index afe42b8041..bfbb1b5248 100644
> --- a/cmd/bootmenu.c
> +++ b/cmd/bootmenu.c
> @@ -29,6 +29,13 @@
>   */
>  #define MAX_ENV_SIZE	(9 + 2 + 1)
>  
> +enum bootmenu_ret {
> +	BOOTMENU_RET_SUCCESS = 0,
> +	BOOTMENU_RET_FAIL,
> +	BOOTMENU_RET_QUIT,
> +	BOOTMENU_RET_UPDATED,
> +};
> +
>  enum boot_type {
>  	BOOTMENU_TYPE_NONE = 0,
>  	BOOTMENU_TYPE_BOOTMENU,
> @@ -681,7 +688,12 @@ static struct bootmenu_data *bootmenu_create(int delay)
>  		if (!entry)
>  			goto cleanup;
>  
> -		entry->title = u16_strdup(u"U-Boot console");
> +		/* Add Quit entry if entering U-Boot console is disabled */
> +		if (IS_ENABLED(CONFIG_CMD_BOOTMENU_ENTER_UBOOT_CONSOLE))
> +			entry->title = u16_strdup(u"U-Boot console");
> +		else
> +			entry->title = u16_strdup(u"Quit");
> +
>  		if (!entry->title) {
>  			free(entry);
>  			goto cleanup;
> @@ -777,15 +789,17 @@ static void handle_uefi_bootnext(void)
>  		run_command("bootefi bootmgr", 0);
>  }
>  
> -static void bootmenu_show(int delay)
> +static enum bootmenu_ret bootmenu_show(int delay)
>  {
> +	int cmd_ret;
>  	int init = 0;
>  	void *choice = NULL;
>  	u16 *title = NULL;
>  	char *command = NULL;
>  	struct menu *menu;
> -	struct bootmenu_data *bootmenu;
>  	struct bootmenu_entry *iter;
> +	int ret = BOOTMENU_RET_SUCCESS;
> +	struct bootmenu_data *bootmenu;
>  	efi_status_t efi_ret = EFI_SUCCESS;
>  	char *option, *sep;
>  
> @@ -797,27 +811,27 @@ static void bootmenu_show(int delay)
>  		option = bootmenu_getoption(0);
>  		if (!option) {
>  			puts("bootmenu option 0 was not found\n");
> -			return;
> +			return BOOTMENU_RET_FAIL;
>  		}
>  		sep = strchr(option, '=');
>  		if (!sep) {
>  			puts("bootmenu option 0 is invalid\n");
> -			return;
> +			return BOOTMENU_RET_FAIL;
>  		}
> -		run_command(sep+1, 0);
> -		return;
> +		cmd_ret = run_command(sep + 1, 0);
> +		return (cmd_ret == CMD_RET_SUCCESS ? BOOTMENU_RET_SUCCESS : BOOTMENU_RET_FAIL);
>  	}
>  
>  	bootmenu = bootmenu_create(delay);
>  	if (!bootmenu)
> -		return;
> +		return BOOTMENU_RET_FAIL;
>  
>  	menu = menu_create(NULL, bootmenu->delay, 1, menu_display_statusline,
>  			   bootmenu_print_entry, bootmenu_choice_entry,
>  			   bootmenu);
>  	if (!menu) {
>  		bootmenu_destroy(bootmenu);
> -		return;
> +		return BOOTMENU_RET_FAIL;
>  	}
>  
>  	for (iter = bootmenu->first; iter; iter = iter->next) {
> @@ -838,6 +852,14 @@ static void bootmenu_show(int delay)
>  		iter = choice;
>  		title = u16_strdup(iter->title);
>  		command = strdup(iter->command);
> +
> +		/* last entry is U-Boot console or Quit */
> +		if (iter->num == iter->menu->count - 1) {
> +			ret = BOOTMENU_RET_QUIT;
> +			goto cleanup;
> +		}
> +	} else {
> +		goto cleanup;
>  	}
>  
>  	/*
> @@ -875,19 +897,44 @@ cleanup:
>  		debug("Starting entry '%ls'\n", title);
>  		free(title);
>  		if (efi_ret == EFI_SUCCESS)
> -			run_command(command, 0);
> +			cmd_ret = run_command(command, 0);
>  		free(command);
>  	}
>  
>  #ifdef CONFIG_POSTBOOTMENU
>  	run_command(CONFIG_POSTBOOTMENU, 0);
>  #endif
> +
> +	if (efi_ret != EFI_SUCCESS || cmd_ret != CMD_RET_SUCCESS)
> +		ret = BOOTMENU_RET_FAIL;
> +
> +	return ret;
>  }
>  
>  #ifdef CONFIG_AUTOBOOT_MENU_SHOW
>  int menu_show(int bootdelay)
>  {
> -	bootmenu_show(bootdelay);
> +	int ret;
> +
> +	while (1) {
> +		ret = bootmenu_show(bootdelay);
> +		bootdelay = -1;
> +		if (ret == BOOTMENU_RET_UPDATED)
> +			continue;
> +
> +		if (!IS_ENABLED(CONFIG_CMD_BOOTMENU_ENTER_UBOOT_CONSOLE)) {
> +			if (ret == BOOTMENU_RET_QUIT) {
> +				/* default boot process */
> +				if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR))
> +					run_command("bootefi bootmgr", 0);
> +
> +				run_command("run bootcmd", 0);
> +			}
> +		} else {
> +			break;
> +		}
> +	}
> +
>  	return -1; /* -1 - abort boot and run monitor code */
>  }
>  #endif
> -- 
> 2.17.1
> 
> 

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

* Re: [PATCH v5 13/17] efi_loader: menu-driven addition of UEFI boot option
  2022-04-28 16:33   ` Heinrich Schuchardt
@ 2022-04-29 10:56     ` Heinrich Schuchardt
  2022-04-30 12:49       ` Heinrich Schuchardt
  0 siblings, 1 reply; 58+ messages in thread
From: Heinrich Schuchardt @ 2022-04-29 10:56 UTC (permalink / raw)
  To: Masahisa Kojima
  Cc: Ilias Apalodimas, Simon Glass, Takahiro Akashi, Francois Ozog,
	Mark Kettenis, u-boot

On 4/28/22 18:33, Heinrich Schuchardt wrote:
> On 4/28/22 10:09, Masahisa Kojima wrote:
>> This commit supports the menu-driven UEFI boot option addition.
>> User can select the block device volume having
>> efi_simple_file_system_protocol and select the file corresponding
>> to the Boot#### variable. Then user enter the label of the BOOT####
>> variable in utf8.
>>
>> Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
>> ---
>> Changes in v5:
>> - remove forward declarations
>> - add const qualifier for menu items
>> - fix the possible unaligned access for directory info access
>> - split into three commit 1)add boot option 2) delete boot option 
>> 3)change boot order
>>    This commit is 1)add boot option.
>> - fix file name buffer allocation size, it should be 
>> EFI_BOOTMENU_FILE_PATH_MAX * sizeof(u16)
>> - fix wrong size checking for file selection
>>
>> Chanes in v4:
>> - UEFI boot option maintenance menu is integrated into bootmenu
>> - display the simplified volume name(e.g. usb0:1, nvme1:2) for the
>>    volume selection
>> - instead of extending lib/efi_loader/efi_bootmgr.c, newly create
>>    lib/efi_loader/efi_bootmenu_maintenance.c and implement boot
>>    variable maintenance into it.
>>
>> Changes in RFC v3:
>>   not included in v3 series
>>
>> Changes in RFC v2:
>> - enable utf8 user input for boot option name
>> - create lib/efi_loader/efi_console.c::efi_console_get_u16_string() for
>>    utf8 user input handling
>> - use u16_strlcat instead of u16_strcat
>> - remove the EFI_CALLs, and newly create or expose the following
>>    xxx_int() functions.
>>      efi_locate_handle_buffer_int(), efi_open_volume_int(),
>>      efi_file_open_int(), efi_file_close_int(), efi_file_read_int() and
>>      efi_file_setpos_int().
>>    Note that EFI_CALLs still exist for EFI_DEVICE_PATH_TO_TEXT_PROTOCOL
>>    and EFI_SIMPLE_TEXT_INPUT/OUTPUT_PROTOCOL
>> - use efi_search_protocol() instead of calling locate_protocol() to get
>>    the device_path_to_text_protocol interface.
>> - remove unnecessary puts(ANSI_CLEAR_LINE), this patch is still 
>> depends on
>>    puts(ANSI_CLEAR_CONSOLE)
>> - skip SetVariable() if the bootorder is not changed
>>
>>   cmd/bootmenu.c                            |  69 +-
>>   include/efi_loader.h                      |  37 +
>>   lib/efi_loader/Makefile                   |   1 +
>>   lib/efi_loader/efi_bootmenu_maintenance.c | 862 ++++++++++++++++++++++
>>   lib/efi_loader/efi_boottime.c             |  52 +-
>>   lib/efi_loader/efi_console.c              |  81 ++
>>   lib/efi_loader/efi_disk.c                 |  11 +
>>   lib/efi_loader/efi_file.c                 |  75 +-
>>   8 files changed, 1133 insertions(+), 55 deletions(-)
>>   create mode 100644 lib/efi_loader/efi_bootmenu_maintenance.c
>>
>> diff --git a/cmd/bootmenu.c b/cmd/bootmenu.c
>> index eb23afdd41..860cb83182 100644
>> --- a/cmd/bootmenu.c
>> +++ b/cmd/bootmenu.c
>> @@ -21,6 +21,8 @@
>>
>>   /* maximum bootmenu entries */
>>   #define MAX_COUNT    99
>> +#define STATIC_ENTRY 2
>> +#define MAX_DYNAMIC_ENTRY (MAX_COUNT - STATIC_ENTRY)
>>
>>   /* maximal size of bootmenu env
>>    *  9 = strlen("bootmenu_")
>> @@ -41,10 +43,11 @@ enum boot_type {
>>       BOOTMENU_TYPE_BOOTMENU,
>>       BOOTMENU_TYPE_UEFI_BOOT_OPTION,
>>       BOOTMENU_TYPE_DISTRO_BOOT,
>> +    BOOTMENU_TYPE_UEFI_MAINTENANCE,
>>   };
>>
>>   struct bootmenu_entry {
>> -    unsigned short int num;        /* unique number 0 .. MAX_COUNT */
>> +    unsigned short int num;        /* unique number 0 .. 
>> MAX_DYNAMIC_ENTRY */
>>       char key[3];            /* key identifier of number */
>>       u16 *title;            /* title of entry */
>>       char *command;            /* hush command of entry */
>> @@ -58,7 +61,7 @@ static char *bootmenu_getoption(unsigned short int n)
>>   {
>>       char name[MAX_ENV_SIZE];
>>
>> -    if (n > MAX_COUNT)
>> +    if (n > MAX_DYNAMIC_ENTRY)
>>           return NULL;
>>
>>       sprintf(name, "bootmenu_%d", n);
>> @@ -229,7 +232,7 @@ static int prepare_bootmenu_entry(struct 
>> bootmenu_data *menu,
>>           iter = entry;
>>           ++i;
>>
>> -        if (i == MAX_COUNT - 1)
>> +        if (i == MAX_DYNAMIC_ENTRY)
>>               break;
>>       }
>>
>> @@ -317,7 +320,7 @@ static int prepare_uefi_bootorder_entry(struct 
>> bootmenu_data *menu,
>>
>>           free(load_option);
>>
>> -        if (i == MAX_COUNT - 1)
>> +        if (i == MAX_DYNAMIC_ENTRY)
>>               break;
>>       }
>>
>> @@ -481,7 +484,7 @@ static int prepare_distro_boot_entry(struct 
>> bootmenu_data *menu,
>>           iter = entry;
>>           i++;
>>
>> -        if (i == MAX_COUNT - 1)
>> +        if (i == MAX_DYNAMIC_ENTRY)
>>               break;
>>
>>           token = strtok(NULL, " ");
>> @@ -520,19 +523,56 @@ static struct bootmenu_data *bootmenu_create(int 
>> delay)
>>           goto cleanup;
>>
>>       if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR)) {
>> -        if (i < MAX_COUNT - 1) {
>> +        if (i < MAX_DYNAMIC_ENTRY) {
>>               ret = prepare_uefi_bootorder_entry(menu, &iter, &i);
>>               if (ret < 0 && ret != -ENOENT)
>>                   goto cleanup;
>>           }
>>       }
>>
>> -    if (i < MAX_COUNT - 1) {
>> +    if (i < MAX_DYNAMIC_ENTRY) {
>>           ret = prepare_distro_boot_entry(menu, &iter, &i);
>>           if (ret < 0 && ret != -ENOENT)
>>               goto cleanup;
>>       }
>>
>> +    if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR)) {
>> +        /* Add UEFI Boot Manager Maintenance entry */
>> +        if (i <= MAX_DYNAMIC_ENTRY) {
>> +            entry = malloc(sizeof(struct bootmenu_entry));
>> +            if (!entry)
>> +                goto cleanup;
>> +
>> +            entry->title = u16_strdup(u"UEFI Boot Manager Maintenance");
>> +            if (!entry->title) {
>> +                free(entry);
>> +                goto cleanup;
>> +            }
>> +
>> +            entry->command = strdup("");
>> +            if (!entry->command) {
>> +                free(entry->title);
>> +                free(entry);
>> +                goto cleanup;
>> +            }
>> +
>> +            sprintf(entry->key, "%d", i);
>> +
>> +            entry->num = i;
>> +            entry->menu = menu;
>> +            entry->type = BOOTMENU_TYPE_UEFI_MAINTENANCE;
>> +            entry->next = NULL;
>> +
>> +            if (!iter)
>> +                menu->first = entry;
>> +            else
>> +                iter->next = entry;
>> +
>> +            iter = entry;
>> +            i++;
>> +        }
>> +    }
>> +
>>       /* Add U-Boot console entry at the end */
>>       if (i <= MAX_COUNT - 1) {
>>           entry = malloc(sizeof(struct bootmenu_entry));
>> @@ -704,6 +744,12 @@ static enum bootmenu_ret bootmenu_show(int delay)
>>           title = u16_strdup(iter->title);
>>           command = strdup(iter->command);
>>
>> +        if (iter->type == BOOTMENU_TYPE_UEFI_MAINTENANCE) {
>> +            efi_bootmenu_show_maintenance_menu();

This does not compile for CONFIG_EFI_LOADER=n.
And it is not needed for CONFIG_CMD_BOOTEFI_BOOTMGR=n.

>> +            ret = BOOTMENU_RET_UPDATED;
>> +            goto cleanup;
>> +        }
>> +
>>           /* last entry is U-Boot console or Quit */
>>           if (iter->num == iter->menu->count - 1) {
>>               ret = BOOTMENU_RET_QUIT;
>> @@ -794,6 +840,7 @@ int do_bootmenu(struct cmd_tbl *cmdtp, int flag, 
>> int argc, char *const argv[])
>>   {
>>       char *delay_str = NULL;
>>       int delay = 10;
>> +    int ret;
>>
>>   #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
>>       delay = CONFIG_BOOTDELAY;
>> @@ -808,7 +855,13 @@ int do_bootmenu(struct cmd_tbl *cmdtp, int flag, 
>> int argc, char *const argv[])
>>       if (delay_str)
>>           delay = (int)simple_strtol(delay_str, NULL, 10);
>>
>> -    bootmenu_show(delay);
>> +    while (1) {
>> +        ret =  bootmenu_show(delay);
>> +        delay = -1;
>> +        if (ret != BOOTMENU_RET_UPDATED)
>> +            break;
>> +    }
>> +
>>       return 0;
>>   }
>>
>> diff --git a/include/efi_loader.h b/include/efi_loader.h
>> index effb43369d..533618341b 100644
>> --- a/include/efi_loader.h
>> +++ b/include/efi_loader.h
>> @@ -226,6 +226,9 @@ const char *__efi_nesting_dec(void);
>>   #define EFI_CACHELINE_SIZE 128
>>   #endif
>>
>> +/* max bootmenu title size for volume selection */
>> +#define BOOTMENU_DEVICE_NAME_MAX 16
>> +
>>   /* Key identifying current memory map */
>>   extern efi_uintn_t efi_memory_map_key;
>>
>> @@ -312,6 +315,9 @@ extern const efi_guid_t 
>> efi_guid_firmware_management_protocol;
>>   extern const efi_guid_t efi_esrt_guid;
>>   /* GUID of the SMBIOS table */
>>   extern const efi_guid_t smbios_guid;
>> +/*GUID of console */
>> +extern const efi_guid_t efi_guid_text_input_protocol;
>> +extern const efi_guid_t efi_guid_text_output_protocol;
>>
>>   extern char __efi_runtime_start[], __efi_runtime_stop[];
>>   extern char __efi_runtime_rel_start[], __efi_runtime_rel_stop[];
>> @@ -871,6 +877,8 @@ efi_status_t efi_set_load_options(efi_handle_t 
>> handle,
>>                     void *load_options);
>>   efi_status_t efi_bootmgr_load(efi_handle_t *handle, void 
>> **load_options);
>>
>> +efi_status_t efi_bootmenu_show_maintenance_menu(void);
>> +
>>   /**
>>    * struct efi_image_regions - A list of memory regions
>>    *
>> @@ -1042,4 +1050,33 @@ efi_status_t efi_esrt_populate(void);
>>   efi_status_t efi_load_capsule_drivers(void);
>>
>>   efi_status_t platform_get_eventlog(struct udevice *dev, u64 *addr, 
>> u32 *sz);
>> +
>> +efi_status_t efi_locate_handle_buffer_int(enum efi_locate_search_type 
>> search_type,
>> +                      const efi_guid_t *protocol, void *search_key,
>> +                      efi_uintn_t *no_handles, efi_handle_t **buffer);
>> +
>> +efi_status_t efi_open_volume_int(struct 
>> efi_simple_file_system_protocol *this,
>> +                 struct efi_file_handle **root);
>> +efi_status_t efi_file_open_int(struct efi_file_handle *this,
>> +                   struct efi_file_handle **new_handle,
>> +                   u16 *file_name, u64 open_mode,
>> +                   u64 attributes);
>> +efi_status_t efi_file_close_int(struct efi_file_handle *file);
>> +efi_status_t efi_file_read_int(struct efi_file_handle *this,
>> +                   efi_uintn_t *buffer_size, void *buffer);
>> +efi_status_t efi_file_setpos_int(struct efi_file_handle *file, u64 pos);
>> +
>> +typedef efi_status_t (*efi_console_filter_func)(struct efi_input_key 
>> *key);
>> +efi_status_t efi_console_get_u16_string
>> +        (struct efi_simple_text_input_protocol *cin,
>> +         struct efi_simple_text_output_protocol *cout,
>> +         u16 *buf, efi_uintn_t count, efi_console_filter_func 
>> filer_func,
>> +         int row, int col);
>> +
>> +efi_status_t efi_bootmenu_get_unused_bootoption(u16 *buf,
>> +                        efi_uintn_t buf_size, u32 *index);
>> +efi_status_t efi_bootmenu_append_bootorder(u16 index);
>> +
>> +efi_status_t efi_disk_get_device_name(struct efi_block_io *this, char 
>> *buf, int size);
>> +
>>   #endif /* _EFI_LOADER_H */
>> diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile
>> index aaaa25cefe..792eabe18a 100644
>> --- a/lib/efi_loader/Makefile
>> +++ b/lib/efi_loader/Makefile
>> @@ -77,6 +77,7 @@ obj-$(CONFIG_EFI_TCG2_PROTOCOL) += efi_tcg2.o
>>   obj-$(CONFIG_EFI_RISCV_BOOT_PROTOCOL) += efi_riscv.o
>>   obj-$(CONFIG_EFI_LOAD_FILE2_INITRD) += efi_load_initrd.o
>>   obj-$(CONFIG_EFI_SIGNATURE_SUPPORT) += efi_signature.o
>> +obj-$(CONFIG_CMD_BOOTMENU) += efi_bootmenu_maintenance.o

Why should we compile this for CONFIG_CMD_BOOTEFI_BOOTMGR=n?

Best regards

Heinrich

>>
>>   EFI_VAR_SEED_FILE := $(subst $\",,$(CONFIG_EFI_VAR_SEED_FILE))
>>   $(obj)/efi_var_seed.o: $(srctree)/$(EFI_VAR_SEED_FILE)
>> diff --git a/lib/efi_loader/efi_bootmenu_maintenance.c 
>> b/lib/efi_loader/efi_bootmenu_maintenance.c
>> new file mode 100644
>> index 0000000000..77401a7829
>> --- /dev/null
>> +++ b/lib/efi_loader/efi_bootmenu_maintenance.c
>> @@ -0,0 +1,862 @@
>> +// SPDX-License-Identifier: GPL-2.0+
>> +/*
>> + *  Menu-driven UEFI Boot Variable maintenance
>> + *
>> + *  Copyright (c) 2022 Masahisa Kojima, Linaro Limited
>> + */
>> +
>> +#define LOG_CATEGORY LOGC_EFI
>> +
>> +#include <ansi.h>
>> +#include <common.h>
>> +#include <charset.h>
>> +#include <log.h>
>> +#include <malloc.h>
>> +#include <menu.h>
>> +#include <efi_loader.h>
>> +#include <efi_variable.h>
>> +#include <asm/unaligned.h>
>> +
>> +static struct efi_simple_text_input_protocol *cin;
>> +static struct efi_simple_text_output_protocol *cout;
>> +
>> +#define EFI_BOOTMENU_ENTRY_NUM_MAX 99
>> +#define EFI_BOOTMENU_FILE_PATH_MAX 512
>> +#define EFI_BOOTMENU_FILE_PATH_BUF_SIZE (EFI_BOOTMENU_FILE_PATH_MAX * 
>> sizeof(u16))
>> +#define EFI_BOOTMENU_BOOT_NAME_MAX 32
>> +#define EFI_BOOT_ORDER_MAX_SIZE_IN_DECIMAL 6
>> +
>> +typedef efi_status_t (*efi_bootmenu_entry_func)(void *data, bool *exit);
>> +
>> +/**
>> + * struct efi_bootmenu_entry - menu entry structure
>> + *
>> + * @num:        menu entry index
>> + * @title:        title of entry
>> + * @key:        unique key
>> + * @bootmgr_menu:    pointer to the menu structure
>> + * @next:        pointer to the next entry
>> + * @func:        callback function to be called when this entry is 
>> selected
>> + * @data:        data to be passed to the callback function
>> + */
>> +struct efi_bootmenu_entry {
>> +    u32 num;
>> +    u16 *title;
>> +    char key[6];
>> +    struct efi_bootmenu *bootmgr_menu;
>> +    struct efi_bootmenu_entry *next;
>> +    efi_bootmenu_entry_func func;
>> +    void *data;
>> +};
>> +
>> +/**
>> + * struct efi_bootmenu - bootmgr menu structure
>> + *
>> + * @delay:    delay for autoboot
>> + * @active:    active menu entry index
>> + * @count:    total count of menu entry
>> + * @first:    pointer to the first menu entry
>> + */
>> +struct efi_bootmenu {
>> +    int delay;
>> +    int active;
>> +    int count;
>> +    struct efi_bootmenu_entry *first;
>> +};
>> +
>> +struct efi_bootmenu_item {
>> +    u16 *title;
>> +    efi_bootmenu_entry_func func;
>> +    void *data;
>> +};
>> +
>> +struct efi_bootmenu_boot_option {
>> +    struct efi_simple_file_system_protocol *current_volume;
>> +    struct efi_device_path *dp_volume;
>> +    u16 *current_path;
>> +    u16 *boot_name;
>> +    bool file_selected;
>> +};
>> +
>> +static const struct efi_device_path END = {
>> +    .type     = DEVICE_PATH_TYPE_END,
>> +    .sub_type = DEVICE_PATH_SUB_TYPE_END,
>> +    .length   = sizeof(END),
>> +};
>> +
>> +struct efi_bootmenu_volume_entry_data {
>> +    struct efi_bootmenu_boot_option *bo;
>> +    struct efi_simple_file_system_protocol *v;
>> +    struct efi_device_path *dp;
>> +};
>> +
>> +struct efi_bootmenu_file_entry_data {
>> +    struct efi_bootmenu_boot_option *bo;
>> +    bool is_directory;
>> +    u16 *file_name;
>> +};
>> +
>> +static void efi_bootmenu_print_entry(void *data)
>> +{
>> +    struct efi_bootmenu_entry *entry = data;
>> +    int reverse = (entry->bootmgr_menu->active == entry->num);
>> +
>> +    /* TODO: support scroll or page for many entries */
>> +
>> +    /*
>> +     * Move cursor to line where the entry will be drown (entry->count)
> 
> %s/drown/drawn/
> 
>> +     * First 3 lines contain bootmgr menu header + one empty line
>> +     * For the last "Quit" entry, add one empty line
>> +     */
>> +    if (entry->num == (entry->bootmgr_menu->count - 1))
>> +        printf(ANSI_CURSOR_POSITION, entry->num + 5, 1);
>> +    else
>> +        printf(ANSI_CURSOR_POSITION, entry->num + 4, 1);
>> +
>> +    puts("     ");
>> +
>> +    if (reverse)
>> +        puts(ANSI_COLOR_REVERSE);
>> +
>> +    printf("%ls", entry->title);
>> +
>> +    if (reverse)
>> +        puts(ANSI_COLOR_RESET);
>> +}
>> +
>> +static void efi_bootmenu_display_statusline(struct menu *m)
>> +{
>> +    struct efi_bootmenu_entry *entry;
>> +    struct efi_bootmenu *bootmgr_menu;
>> +
>> +    if (menu_default_choice(m, (void *)&entry) < 0)
>> +        return;
>> +
>> +    bootmgr_menu = entry->bootmgr_menu;
>> +
>> +    printf(ANSI_CURSOR_POSITION, 1, 1);
>> +    puts(ANSI_CLEAR_LINE);
>> +    printf(ANSI_CURSOR_POSITION, 2, 1);
>> +    puts("  *** U-Boot EFI Boot Manager ***");
>> +    puts(ANSI_CLEAR_LINE_TO_END);
>> +    printf(ANSI_CURSOR_POSITION, 3, 1);
>> +    puts(ANSI_CLEAR_LINE);
>> +
>> +    /* First 3 lines are bootmgr_menu header + 2 empty lines between 
>> entries */
>> +    printf(ANSI_CURSOR_POSITION, bootmgr_menu->count + 5, 1);
>> +    puts(ANSI_CLEAR_LINE);
>> +    printf(ANSI_CURSOR_POSITION, bootmgr_menu->count + 6, 1);
>> +    puts("  Press UP/DOWN to move, ENTER to select, ESC/CTRL+C to 
>> quit");
>> +    puts(ANSI_CLEAR_LINE_TO_END);
>> +    printf(ANSI_CURSOR_POSITION, bootmgr_menu->count + 7, 1);
>> +    puts(ANSI_CLEAR_LINE);
>> +}
>> +
>> +static char *efi_bootmenu_choice_entry(void *data)
>> +{
>> +    int i;
>> +    int esc = 0;
>> +    struct efi_bootmenu_entry *iter;
>> +    enum bootmenu_key key = KEY_NONE;
>> +    struct efi_bootmenu *bootmgr_menu = data;
>> +
>> +    while (1) {
>> +        if (bootmgr_menu->delay >= 0) {
>> +            /* Autoboot was not stopped */
>> +            bootmenu_autoboot_loop((struct bootmenu_data 
>> *)bootmgr_menu, &key, &esc);
>> +        } else {
>> +            /* Some key was pressed, so autoboot was stopped */
>> +            bootmenu_loop((struct bootmenu_data *)bootmgr_menu, &key, 
>> &esc);
>> +        }
>> +
>> +        if (bootmgr_menu->delay == 0)
>> +            key = KEY_QUIT;
>> +
>> +        switch (key) {
>> +        case KEY_UP:
>> +            if (bootmgr_menu->active > 0)
>> +                --bootmgr_menu->active;
>> +            /* no menu key selected, regenerate menu */
>> +            return NULL;
>> +        case KEY_DOWN:
>> +            if (bootmgr_menu->active < bootmgr_menu->count - 1)
>> +                ++bootmgr_menu->active;
>> +            /* no menu key selected, regenerate menu */
>> +            return NULL;
>> +        case KEY_SELECT:
>> +            iter = bootmgr_menu->first;
>> +            for (i = 0; i < bootmgr_menu->active; ++i)
>> +                iter = iter->next;
>> +            return iter->key;
>> +        case KEY_QUIT:
>> +            /* Quit by choosing the last entry */
>> +            iter = bootmgr_menu->first;
>> +            while (iter->next)
>> +                iter = iter->next;
>> +            return iter->key;
>> +        default:
>> +            break;
>> +        }
>> +    }
>> +
>> +    /* never happens */
>> +    debug("bootmgr menu: this should not happen");
>> +    return NULL;
>> +}
>> +
>> +static void efi_bootmenu_destroy(struct efi_bootmenu *bootmgr_menu)
>> +{
>> +    struct efi_bootmenu_entry *next;
>> +    struct efi_bootmenu_entry *iter = bootmgr_menu->first;
>> +
>> +    while (iter) {
>> +        next = iter->next;
>> +        free(iter);
>> +        iter = next;
>> +    }
>> +    free(bootmgr_menu);
>> +}
>> +
>> +/**
>> + * efi_bootmenu_process_common() - main handler for uefi bootmgr menu
>> + *
>> + * Construct the structures required to show the menu, then handle
>> + * the user input intracting with u-boot menu functions.
>> + *
>> + * @items:    pointer to the structure of each menu entry
>> + * @count:    the number of menu entry
>> + * @delay:    delay for autoboot/autoselect
>> + * Return:    status code
>> + */
>> +static efi_status_t efi_bootmenu_process_common(const struct 
>> efi_bootmenu_item *items,
>> +                        int count, int delay)
>> +{
>> +    u32 i;
>> +    bool exit = false;
>> +    efi_status_t ret;
>> +    struct menu *menu;
>> +    void *choice = NULL;
>> +    struct efi_bootmenu_entry *entry;
>> +    struct efi_bootmenu *bootmgr_menu;
>> +    struct efi_bootmenu_entry *iter = NULL;
>> +
>> +    if (count > EFI_BOOTMENU_ENTRY_NUM_MAX)
>> +        return EFI_OUT_OF_RESOURCES;
>> +
>> +    bootmgr_menu = calloc(1, sizeof(struct efi_bootmenu));
>> +    if (!bootmgr_menu)
>> +        return EFI_OUT_OF_RESOURCES;
>> +
>> +    bootmgr_menu->delay = delay;
>> +    bootmgr_menu->active = 0;
>> +    bootmgr_menu->first = NULL;
>> +
>> +    for (i = 0; i < count; i++) {
>> +        entry = calloc(1, sizeof(struct efi_bootmenu_entry));
>> +        if (!entry) {
>> +            ret = EFI_LOAD_ERROR;
>> +            goto out;
>> +        }
>> +
>> +        entry->num = i;
>> +        entry->title = items->title;
>> +        snprintf(entry->key, sizeof(entry->key), "%04X", i);
>> +        entry->bootmgr_menu = bootmgr_menu;
>> +        entry->func = items->func;
>> +        entry->data = items->data;
>> +        entry->next = NULL;
>> +
>> +        if (!iter)
>> +            bootmgr_menu->first = entry;
>> +        else
>> +            iter->next = entry;
>> +
>> +        iter = entry;
>> +        items++;
>> +    }
>> +    bootmgr_menu->count = count;
>> +
>> +    menu = menu_create(NULL, 0, 1, efi_bootmenu_display_statusline,
>> +               efi_bootmenu_print_entry, efi_bootmenu_choice_entry,
>> +               bootmgr_menu);
>> +    if (!menu) {
>> +        ret = EFI_INVALID_PARAMETER;
>> +        goto out;
>> +    }
>> +
>> +    for (entry = bootmgr_menu->first; entry; entry = entry->next) {
>> +        if (!menu_item_add(menu, entry->key, entry)) {
>> +            ret = EFI_INVALID_PARAMETER;
>> +            goto out;
>> +        }
>> +    }
>> +
>> +    menu_default_set(menu, bootmgr_menu->first->key);
>> +
>> +    while (!exit) {
>> +        puts(ANSI_CURSOR_HIDE);
>> +        puts(ANSI_CLEAR_CONSOLE);
>> +        printf(ANSI_CURSOR_POSITION, 1, 1);
>> +
>> +        if (menu_get_choice(menu, &choice)) {
>> +            entry = choice;
>> +            if (entry->func)
>> +                ret = entry->func(entry->data, &exit);
>> +
>> +            /* last entry "Quit" is selected, exit this menu */
>> +            if (entry->num == (entry->bootmgr_menu->count - 1)) {
>> +                ret = EFI_ABORTED;
>> +                break;
>> +            }
>> +        }
>> +    }
>> +
>> +out:
>> +    menu_destroy(menu);
>> +    efi_bootmenu_destroy(bootmgr_menu);
>> +
>> +    puts(ANSI_CLEAR_CONSOLE);
>> +    printf(ANSI_CURSOR_POSITION, 1, 1);
>> +    puts(ANSI_CURSOR_SHOW);
>> +
>> +    return ret;
>> +}
>> +
>> +static efi_status_t efi_bootmenu_volume_selected(void *data, bool *exit)
>> +{
>> +    struct efi_bootmenu_volume_entry_data *info = data;
>> +
>> +    *exit = true;
>> +
>> +    if (info) {
>> +        info->bo->current_volume = info->v;
>> +        info->bo->dp_volume = info->dp;
>> +    }
>> +
>> +    return EFI_SUCCESS;
>> +}
>> +
>> +static efi_status_t efi_bootmenu_file_selected(void *data, bool *exit)
>> +{
>> +    struct efi_bootmenu_file_entry_data *info = data;
>> +
>> +    *exit = true;
>> +
>> +    if (!info)
>> +        return EFI_INVALID_PARAMETER;
>> +
>> +    if (u16_strncmp(info->file_name, u".", 1) == 0 &&
>> +        u16_strlen(info->file_name) == 1) {
>> +        /* stay current path */
>> +    } else if (u16_strncmp(info->file_name, u"..", 2) == 0 &&
>> +           u16_strlen(info->file_name) == 2) {
>> +        u32 i;
>> +        int len = u16_strlen(info->bo->current_path);
>> +
>> +        for (i = len - 2; i > 0; i--) {
>> +            if (info->bo->current_path[i] == u'\\')
>> +                break;
>> +        }
>> +
>> +        if (i == 0)
>> +            info->bo->current_path[0] = u'\0';
>> +        else
>> +            info->bo->current_path[i + 1] = u'\0';
>> +    } else {
>> +        size_t new_len;
>> +
>> +        new_len = u16_strlen(info->bo->current_path) +
>> +                     u16_strlen(info->file_name) + 1;
>> +        if (new_len > EFI_BOOTMENU_FILE_PATH_MAX) {
>> +            /* TODO: show error notification to user */
>> +            log_err("file path is too long\n");
>> +            return EFI_INVALID_PARAMETER;
>> +        }
>> +        u16_strlcat(info->bo->current_path, info->file_name,
>> +                EFI_BOOTMENU_FILE_PATH_MAX);
>> +        if (info->is_directory) {
>> +            /*
>> +             * Remainig buffer should have enough space to contain 
>> u"\\" and
>> +             * at least one character for file name
>> +             */
>> +            if (new_len + 2 > EFI_BOOTMENU_FILE_PATH_MAX) {
>> +                log_err("directory path is too long\n");
>> +                return EFI_INVALID_PARAMETER;
>> +            }
>> +            u16_strlcat(info->bo->current_path, u"\\",
>> +                    EFI_BOOTMENU_FILE_PATH_MAX);
>> +        } else {
>> +            info->bo->file_selected = true;
>> +        }
>> +    }
>> +    return EFI_SUCCESS;
>> +}
>> +
>> +static efi_status_t efi_bootmenu_select_volume(struct 
>> efi_bootmenu_boot_option *bo)
>> +{
>> +    u32 i;
>> +    efi_status_t ret;
>> +    efi_uintn_t count;
>> +    struct efi_handler *handler;
>> +    struct efi_device_path *device_path;
>> +    efi_handle_t *volume_handles = NULL;
>> +    struct efi_simple_file_system_protocol *v;
>> +    struct efi_bootmenu_item *menu_item, *iter;
>> +
>> +    ret = efi_locate_handle_buffer_int(BY_PROTOCOL, 
>> &efi_simple_file_system_protocol_guid,
>> +                       NULL, &count, (efi_handle_t **)&volume_handles);
>> +    if (ret != EFI_SUCCESS)
>> +        return ret;
>> +
>> +    menu_item = calloc(count + 1, sizeof(struct efi_bootmenu_item));
>> +    if (!menu_item) {
>> +        ret = EFI_OUT_OF_RESOURCES;
>> +        goto out1;
>> +    }
>> +
>> +    iter = menu_item;
>> +    for (i = 0; i < count; i++) {
>> +        u16 *dev_name, *p;
>> +        struct efi_block_io *block_io;
>> +        char buf[BOOTMENU_DEVICE_NAME_MAX];
>> +        struct efi_bootmenu_volume_entry_data *info;
>> +
>> +        ret = efi_search_protocol(volume_handles[i],
>> +                      &efi_simple_file_system_protocol_guid, &handler);
>> +        if (ret != EFI_SUCCESS)
>> +            continue;
>> +        ret = efi_protocol_open(handler, (void **)&v, efi_root, NULL,
>> +                    EFI_OPEN_PROTOCOL_GET_PROTOCOL);
>> +        if (ret != EFI_SUCCESS)
>> +            continue;
>> +
>> +        ret = efi_search_protocol(volume_handles[i], 
>> &efi_guid_device_path, &handler);
>> +        if (ret != EFI_SUCCESS)
>> +            continue;
>> +        ret = efi_protocol_open(handler, (void **)&device_path,
>> +                    efi_root, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
>> +        if (ret != EFI_SUCCESS)
>> +            continue;
>> +
>> +        ret = efi_search_protocol(volume_handles[i], 
>> &efi_block_io_guid, &handler);
>> +        if (ret != EFI_SUCCESS)
>> +            continue;
>> +        ret = efi_protocol_open(handler, (void **)&block_io,
>> +                    efi_root, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
>> +        if (ret != EFI_SUCCESS)
>> +            continue;
>> +
>> +        info = calloc(1, sizeof(struct efi_bootmenu_volume_entry_data));
>> +        if (!info) {
>> +            ret = EFI_OUT_OF_RESOURCES;
>> +            goto out2;
>> +        }
>> +
>> +        efi_disk_get_device_name(block_io, buf, 
>> BOOTMENU_DEVICE_NAME_MAX);
>> +        dev_name = calloc(1, (strlen(buf) + 1) * sizeof(u16));
>> +        if (!dev_name) {
>> +            free(info);
>> +            ret = EFI_OUT_OF_RESOURCES;
>> +            goto out2;
>> +        }
>> +        p = dev_name;
>> +        utf8_utf16_strncpy(&p, buf, strlen(buf));
>> +
>> +        info->v = v;
>> +        info->dp = device_path;
>> +        info->bo = bo;
>> +        iter->title = dev_name;
>> +        iter->func = efi_bootmenu_volume_selected;
>> +        iter->data = info;
>> +        iter++;
>> +    }
>> +
>> +    iter->title = u16_strdup(u"Quit");
>> +    iter->func = NULL;
>> +    iter->data = NULL;
>> +    count += 1;
>> +
>> +    ret = efi_bootmenu_process_common(menu_item, count, -1);
>> +
>> +out2:
>> +    iter = menu_item;
>> +    for (i = 0; i < count; i++) {
>> +        struct efi_bootmenu_volume_entry_data *p;
>> +
>> +        p = (struct efi_bootmenu_volume_entry_data *)(iter->data);
>> +        free(iter->title);
>> +        free(p);
>> +        iter++;
>> +    }
>> +
>> +    free(menu_item);
>> +
>> +out1:
>> +    efi_free_pool(volume_handles);
>> +
>> +    return ret;
>> +}
>> +
>> +static efi_status_t efi_bootmenu_select_file(struct 
>> efi_bootmenu_boot_option *bo,
>> +                         struct efi_file_handle *root)
>> +{
>> +    u32 i;
>> +    struct efi_file_info *buf;
>> +    u32 count = 0;
>> +    efi_uintn_t len;
>> +    efi_status_t ret;
>> +    struct efi_file_handle *f;
>> +    struct efi_bootmenu_item *menu_item, *iter;
>> +
>> +    buf = calloc(1, sizeof(struct efi_file_info) + 
>> EFI_BOOTMENU_FILE_PATH_BUF_SIZE);
>> +    if (!buf)
>> +        return EFI_OUT_OF_RESOURCES;
>> +
>> +    while (!bo->file_selected) {
>> +        count = 0;
>> +
>> +        ret = efi_file_open_int(root, &f, bo->current_path, 
>> EFI_FILE_MODE_READ, 0);
>> +        if (ret != EFI_SUCCESS)
>> +            return ret;
>> +
>> +        /* calculate directory information total count */
>> +        for (;;) {
>> +            len = sizeof(struct efi_file_info) + 
>> EFI_BOOTMENU_FILE_PATH_BUF_SIZE;
>> +            ret = efi_file_read_int(f, &len, buf);
>> +            if (ret != EFI_SUCCESS || len == 0)
>> +                break;
>> +
>> +            count++;
>> +        }
>> +
>> +        menu_item = calloc(count + 1, sizeof(struct efi_bootmenu_item));
>> +        if (!menu_item) {
>> +            efi_file_close_int(f);
>> +            ret = EFI_OUT_OF_RESOURCES;
>> +            goto out;
>> +        }
>> +
>> +        /* read directory and construct menu structure */
>> +        efi_file_setpos_int(f, 0);
>> +        iter = menu_item;
>> +        for (i = 0; i < count; i++) {
>> +            u16 *name;
>> +            int name_len;
>> +            struct efi_bootmenu_file_entry_data *info;
>> +
>> +            len = sizeof(struct efi_file_info) + 
>> EFI_BOOTMENU_FILE_PATH_BUF_SIZE;
>> +            ret = efi_file_read_int(f, &len, buf);
>> +            if (ret != EFI_SUCCESS || len == 0)
>> +                goto err;
>> +
>> +            info = calloc(1, sizeof(struct 
>> efi_bootmenu_file_entry_data));
>> +            if (!info) {
>> +                ret = EFI_OUT_OF_RESOURCES;
>> +                goto err;
>> +            }
>> +
>> +            if (buf->attribute & EFI_FILE_DIRECTORY) {
>> +                /* append u'/' at the end of directory name */
>> +                name_len = u16_strsize(buf->file_name) + sizeof(u16);
>> +                name = calloc(1, name_len);
>> +                if (!name) {
>> +                    ret = EFI_OUT_OF_RESOURCES;
>> +                    goto err;
>> +                }
>> +                u16_strcpy(name, buf->file_name);
>> +                name[u16_strlen(buf->file_name)] = u'/';
>> +
>> +                info->is_directory = true;
>> +            } else {
>> +                name_len = u16_strsize(buf->file_name);
>> +                name = calloc(1, name_len);
>> +                if (!name) {
>> +                    ret = EFI_OUT_OF_RESOURCES;
>> +                    goto err;
>> +                }
>> +                u16_strcpy(name, buf->file_name);
>> +            }
>> +
>> +            info->file_name = u16_strdup(buf->file_name);
>> +            info->bo = bo;
>> +            iter->title = name;
>> +            iter->func = efi_bootmenu_file_selected;
>> +            iter->data = info;
>> +            iter++;
>> +        }
>> +
>> +        /* add "Quit" entry */
>> +        iter->title = u"Quit";
>> +        iter->func = NULL;
>> +        iter->data = NULL;
>> +        count += 1;
>> +
>> +        ret = efi_bootmenu_process_common(menu_item, count, -1);
>> +err:
>> +        efi_file_close_int(f);
>> +        iter = menu_item;
>> +        for (i = 0; i < count - 1; i++, iter++) {
>> +            free(((struct efi_bootmenu_file_entry_data 
>> *)(iter->data))->file_name);
>> +            free(iter->title);
>> +            free(iter->data);
>> +        }
>> +
>> +        free(menu_item);
>> +
>> +        if (ret != EFI_SUCCESS)
>> +            break;
>> +    }
>> +
>> +out:
>> +    free(buf);
>> +    return ret;
>> +}
>> +
>> +static efi_status_t efi_bootmenu_boot_add_enter_name(struct 
>> efi_bootmenu_boot_option *bo)
>> +{
>> +    efi_status_t ret;
>> +
>> +    printf(ANSI_CURSOR_POSITION, 2, 1);
>> +    puts("  *** U-Boot EFI Boot Manager Menu ***");
>> +    printf(ANSI_CURSOR_POSITION, 4, 1);
>> +    puts("  enter name:");
>> +
>> +    printf(ANSI_CURSOR_POSITION, 8, 1);
>> +    puts("  ENTER to complete, ESC/CTRL+C to quit");
>> +
>> +    ret = efi_console_get_u16_string(cin, cout, bo->boot_name,
>> +                     EFI_BOOTMENU_BOOT_NAME_MAX, NULL, 4, 15);
>> +
>> +    return ret;
>> +}
>> +
>> +static efi_status_t efi_bootmenu_select_file_handler(struct 
>> efi_bootmenu_boot_option *bo)
>> +{
>> +    efi_status_t ret;
>> +    struct efi_file_handle *root;
>> +
>> +    bo->file_selected = false;
>> +
>> +    while (!bo->file_selected) {
>> +        bo->current_volume = NULL;
>> +        memset(bo->current_path, 0, sizeof(bo->current_path));
>> +
>> +        ret = efi_bootmenu_select_volume(bo);
>> +        if (ret != EFI_SUCCESS)
>> +            return ret;
>> +
>> +        if (!bo->current_volume)
>> +            return EFI_INVALID_PARAMETER;
>> +
>> +        ret = efi_open_volume_int(bo->current_volume, &root);
>> +        if (ret != EFI_SUCCESS)
>> +            return ret;
>> +
>> +        ret = efi_bootmenu_select_file(bo, root);
>> +        if (ret != EFI_SUCCESS)
>> +            return ret;
>> +    }
>> +
>> +    ret = efi_bootmenu_boot_add_enter_name(bo);
>> +
>> +    return ret;
>> +}
>> +
>> +efi_status_t efi_bootmenu_get_unused_bootoption(u16 *buf,
>> +                        efi_uintn_t buf_size, u32 *index)
>> +{
>> +    u32 i;
>> +    efi_status_t ret;
>> +    efi_uintn_t size;
>> +
>> +    if (buf_size < u16_strsize(u"Boot####"))
>> +        return EFI_BUFFER_TOO_SMALL;
>> +
>> +    for (i = 0; i <= 0xFFFF; i++) {
>> +        size = 0;
>> +        efi_create_indexed_name(buf, buf_size, "Boot", i);
>> +        ret = efi_get_variable_int(buf, &efi_global_variable_guid,
>> +                       NULL, &size, NULL, NULL);
>> +        if (ret == EFI_BUFFER_TOO_SMALL)
>> +            continue;
>> +        else
>> +            break;
>> +    }
>> +
>> +    if (i > 0xFFFF)
>> +        return EFI_OUT_OF_RESOURCES;
>> +
>> +    *index = i;
>> +
>> +    return EFI_SUCCESS;
>> +}
>> +
>> +static efi_status_t efi_bootmenu_set_boot_option(u16 *var_name, 
>> struct efi_device_path *dp,
>> +                         u16 *label, char *optional_data)
>> +{
>> +    void *p = NULL;
>> +    efi_status_t ret;
>> +    efi_uintn_t size;
>> +    struct efi_load_option lo;
>> +
>> +    lo.file_path = dp;
>> +    lo.file_path_length = efi_dp_size(dp) + sizeof(END);
>> +    lo.attributes = LOAD_OPTION_ACTIVE;
>> +    lo.optional_data = optional_data;
>> +    lo.label = label;
>> +
>> +    size = efi_serialize_load_option(&lo, (u8 **)&p);
>> +    if (!size)
>> +        return EFI_INVALID_PARAMETER;
>> +
>> +    ret = efi_set_variable_int(var_name, &efi_global_variable_guid,
>> +                   EFI_VARIABLE_NON_VOLATILE |
>> +                   EFI_VARIABLE_BOOTSERVICE_ACCESS |
>> +                   EFI_VARIABLE_RUNTIME_ACCESS,
>> +                   size, p, false);
>> +    free(p);
>> +
>> +    return ret;
>> +}
>> +
>> +efi_status_t efi_bootmenu_append_bootorder(u16 index)
>> +{
>> +    u16 *bootorder;
>> +    efi_status_t ret;
>> +    u16 *new_bootorder = NULL;
>> +    efi_uintn_t last, size, new_size;
>> +
>> +    /* append new boot option */
>> +    bootorder = efi_get_var(u"BootOrder", &efi_global_variable_guid, 
>> &size);
>> +    last = size / sizeof(u16);
>> +    new_size = size + sizeof(u16);
>> +    new_bootorder = calloc(1, new_size);
>> +    if (!new_bootorder) {
>> +        ret = EFI_OUT_OF_RESOURCES;
>> +        goto out;
>> +    }
>> +    memcpy(new_bootorder, bootorder, size);
>> +    new_bootorder[last] = index;
>> +
>> +    ret = efi_set_variable_int(u"BootOrder", &efi_global_variable_guid,
>> +                   EFI_VARIABLE_NON_VOLATILE |
>> +                   EFI_VARIABLE_BOOTSERVICE_ACCESS |
>> +                   EFI_VARIABLE_RUNTIME_ACCESS,
>> +                   new_size, new_bootorder, false);
>> +    if (ret != EFI_SUCCESS)
>> +        goto out;
>> +
>> +out:
>> +    free(bootorder);
>> +    free(new_bootorder);
>> +
>> +    return ret;
>> +}
>> +
>> +static efi_status_t efi_bootmenu_process_add_boot_option(void *data, 
>> bool *exit)
>> +{
>> +    u32 index;
>> +    u16 var_name[9];
>> +    char *buf = NULL;
>> +    efi_status_t ret;
>> +    char *iter = NULL;
>> +    efi_uintn_t dp_size, fp_size;
>> +    struct efi_bootmenu_boot_option bo;
>> +    struct efi_device_path_file_path *fp;
>> +
>> +    ret = efi_bootmenu_get_unused_bootoption(var_name, sizeof(var_name),
>> +                         &index);
>> +    if (ret != EFI_SUCCESS)
>> +        return ret;
>> +
>> +    bo.current_path = calloc(1, EFI_BOOTMENU_FILE_PATH_BUF_SIZE);
>> +    if (!bo.current_path)
>> +        goto out;
>> +
>> +    bo.boot_name = calloc(1, EFI_BOOTMENU_BOOT_NAME_MAX * sizeof(u16));
>> +    if (!bo.boot_name)
>> +        goto out;
>> +
>> +    ret = efi_bootmenu_select_file_handler(&bo);
>> +    if (ret != EFI_SUCCESS)
>> +        goto out;
>> +
>> +    dp_size = efi_dp_size(bo.dp_volume);
>> +    fp_size = sizeof(struct efi_device_path) +
>> +          ((u16_strlen(bo.current_path) + 1) * sizeof(u16));
>> +    buf = calloc(1, dp_size + fp_size + sizeof(END));
>> +    if (!buf)
>> +        goto out;
>> +
>> +    iter = buf;
>> +    memcpy(iter, bo.dp_volume, dp_size);
>> +    iter += dp_size;
>> +
>> +    fp = (struct efi_device_path_file_path *)iter;
>> +    fp->dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE;
>> +    fp->dp.sub_type = DEVICE_PATH_SUB_TYPE_FILE_PATH;
>> +    fp->dp.length = (u16)fp_size;
>> +    u16_strcpy(fp->str, bo.current_path);
>> +    iter += fp_size;
>> +    *((struct efi_device_path *)iter) = END;
>> +
>> +    ret = efi_bootmenu_set_boot_option(var_name, (struct 
>> efi_device_path *)buf,
>> +                       bo.boot_name, NULL);
>> +    if (ret != EFI_SUCCESS)
>> +        goto out;
>> +
>> +    efi_bootmenu_append_bootorder((u16)index);
>> +    if (ret != EFI_SUCCESS)
>> +        goto out;
>> +
>> +out:
>> +    free(buf);
>> +    free(bo.boot_name);
>> +    free(bo.current_path);
>> +
>> +    return ret;
>> +}
>> +
>> +static efi_status_t efi_bootmenu_init(void)
>> +{
>> +    efi_status_t ret;
>> +    struct efi_handler *handler;
>> +
>> +    ret = efi_search_protocol(efi_root, 
>> &efi_guid_text_input_protocol, &handler);
>> +    if (ret != EFI_SUCCESS)
>> +        return ret;
>> +
>> +    ret = efi_protocol_open(handler, (void **)&cin, efi_root, NULL,
>> +                EFI_OPEN_PROTOCOL_GET_PROTOCOL);
>> +    if (ret != EFI_SUCCESS)
>> +        return ret;
>> +
>> +    ret = efi_search_protocol(efi_root, 
>> &efi_guid_text_output_protocol, &handler);
>> +    if (ret != EFI_SUCCESS)
>> +        return ret;
>> +
>> +    ret = efi_protocol_open(handler, (void **)&cout, efi_root, NULL,
>> +                EFI_OPEN_PROTOCOL_GET_PROTOCOL);
>> +    if (ret != EFI_SUCCESS)
>> +        return ret;
>> +
>> +    return ret;
>> +}
>> +
>> +static const struct efi_bootmenu_item maintenance_menu_items[] = {
>> +    {u"Add Boot Option", efi_bootmenu_process_add_boot_option},
>> +    {u"Quit", NULL},
>> +};
>> +
>> +efi_status_t efi_bootmenu_show_maintenance_menu(void)
>> +{
>> +    efi_status_t ret;
>> +
>> +    ret = efi_bootmenu_init();
>> +    if (ret != EFI_SUCCESS)
>> +        return ret;
>> +
>> +    return efi_bootmenu_process_common(maintenance_menu_items,
>> +                      ARRAY_SIZE(maintenance_menu_items),
>> +                      -1);
>> +}
>> diff --git a/lib/efi_loader/efi_boottime.c 
>> b/lib/efi_loader/efi_boottime.c
>> index 4da64b5d29..1233418e77 100644
>> --- a/lib/efi_loader/efi_boottime.c
>> +++ b/lib/efi_loader/efi_boottime.c
>> @@ -2453,6 +2453,35 @@ static efi_status_t EFIAPI 
>> efi_protocols_per_handle(
>>       return EFI_EXIT(EFI_SUCCESS);
>>   }
>>
>> +efi_status_t efi_locate_handle_buffer_int(enum efi_locate_search_type 
>> search_type,
>> +                      const efi_guid_t *protocol, void *search_key,
>> +                      efi_uintn_t *no_handles, efi_handle_t **buffer)
>> +{
>> +    efi_status_t r;
>> +    efi_uintn_t buffer_size = 0;
>> +
>> +    if (!no_handles || !buffer) {
>> +        r = EFI_INVALID_PARAMETER;
>> +        goto out;
>> +    }
>> +    *no_handles = 0;
>> +    *buffer = NULL;
>> +    r = efi_locate_handle(search_type, protocol, search_key, 
>> &buffer_size,
>> +                  *buffer);
>> +    if (r != EFI_BUFFER_TOO_SMALL)
>> +        goto out;
>> +    r = efi_allocate_pool(EFI_BOOT_SERVICES_DATA, buffer_size,
>> +                  (void **)buffer);
>> +    if (r != EFI_SUCCESS)
>> +        goto out;
>> +    r = efi_locate_handle(search_type, protocol, search_key, 
>> &buffer_size,
>> +                  *buffer);
>> +    if (r == EFI_SUCCESS)
>> +        *no_handles = buffer_size / sizeof(efi_handle_t);
>> +out:
>> +    return r;
>> +}
>> +
>>   /**
>>    * efi_locate_handle_buffer() - locate handles implementing a protocol
>>    * @search_type: selection criterion
>> @@ -2474,30 +2503,13 @@ efi_status_t EFIAPI efi_locate_handle_buffer(
>>               efi_uintn_t *no_handles, efi_handle_t **buffer)
>>   {
>>       efi_status_t r;
>> -    efi_uintn_t buffer_size = 0;
>>
>>       EFI_ENTRY("%d, %pUs, %p, %p, %p", search_type, protocol, 
>> search_key,
>>             no_handles, buffer);
>>
>> -    if (!no_handles || !buffer) {
>> -        r = EFI_INVALID_PARAMETER;
>> -        goto out;
>> -    }
>> -    *no_handles = 0;
>> -    *buffer = NULL;
>> -    r = efi_locate_handle(search_type, protocol, search_key, 
>> &buffer_size,
>> -                  *buffer);
>> -    if (r != EFI_BUFFER_TOO_SMALL)
>> -        goto out;
>> -    r = efi_allocate_pool(EFI_BOOT_SERVICES_DATA, buffer_size,
>> -                  (void **)buffer);
>> -    if (r != EFI_SUCCESS)
>> -        goto out;
>> -    r = efi_locate_handle(search_type, protocol, search_key, 
>> &buffer_size,
>> -                  *buffer);
>> -    if (r == EFI_SUCCESS)
>> -        *no_handles = buffer_size / sizeof(efi_handle_t);
>> -out:
>> +    r = efi_locate_handle_buffer_int(search_type, protocol, search_key,
>> +                     no_handles, buffer);
>> +
>>       return EFI_EXIT(r);
>>   }
>>
>> diff --git a/lib/efi_loader/efi_console.c b/lib/efi_loader/efi_console.c
>> index ba68a15017..f5002e1c99 100644
>> --- a/lib/efi_loader/efi_console.c
>> +++ b/lib/efi_loader/efi_console.c
>> @@ -5,6 +5,7 @@
>>    *  Copyright (c) 2016 Alexander Graf
>>    */
>>
>> +#include <ansi.h>
>>   #include <common.h>
>>   #include <charset.h>
>>   #include <malloc.h>
>> @@ -1312,3 +1313,83 @@ out_of_memory:
>>       printf("ERROR: Out of memory\n");
>>       return r;
>>   }
>> +
>> +/**
>> + * efi_console_get_u16_string() - get user input string
>> + *
>> + * @cin:        protocol interface to EFI_SIMPLE_TEXT_INPUT_PROTOCOL
>> + * @cout:        protocol interface to EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
>> + * @buf:        buffer to store user input string in UTF16
>> + * @size:        buffer size including NULL terminator
>> + * @filter_func:    callback to filter user input
>> + * @row:        row number to locate user input form
>> + * @col:        column number to locate user input form
>> + * Return:        status code
>> + */
>> +efi_status_t efi_console_get_u16_string(struct 
>> efi_simple_text_input_protocol *cin,
>> +                    struct efi_simple_text_output_protocol *cout,
>> +                    u16 *buf, efi_uintn_t size,
>> +                    efi_console_filter_func filter_func,
>> +                    int row, int col)
>> +{
>> +    efi_status_t ret;
>> +    efi_uintn_t len = 0;
>> +    struct efi_input_key key;
>> +
>> +    printf(ANSI_CURSOR_POSITION, row, col);
>> +    puts(ANSI_CLEAR_LINE_TO_END);
>> +    puts(ANSI_CURSOR_SHOW);
>> +
>> +    ret = EFI_CALL(cin->reset(cin, false));
>> +    if (ret != EFI_SUCCESS)
>> +        return ret;
>> +
>> +    for (;;) {
>> +        do {
>> +            ret = EFI_CALL(cin->read_key_stroke(cin, &key));
>> +            mdelay(10);
>> +        } while (ret == EFI_NOT_READY);
>> +
>> +        if (key.unicode_char == u'\b') {
>> +            if (len > 0)
>> +                buf[--len] = u'\0';
>> +
>> +            printf(ANSI_CURSOR_POSITION, row, col);
>> +            ret = EFI_CALL(cout->output_string(cout, buf));
>> +            if (ret != EFI_SUCCESS)
>> +                return ret;
>> +
>> +            puts(ANSI_CLEAR_LINE_TO_END);
>> +            continue;
>> +        } else if (key.unicode_char == u'\r') {
>> +            if (len == 0) /* no user input */
>> +                continue;
>> +
>> +            buf[len] = u'\0';
>> +            return EFI_SUCCESS;
>> +        } else if (key.unicode_char == 0x3 || key.scan_code == 23) {
>> +            return EFI_ABORTED;
>> +        } else if (key.unicode_char < 0x20) {
>> +            /* ignore control codes other than Ctrl+C, '\r' and '\b' */
>> +            continue;
>> +        } else if (key.scan_code != 0) {
>> +            /* only accept single ESC press for cancel */
>> +            continue;
>> +        }
>> +
>> +        if (filter_func) {
>> +            if (filter_func(&key) != EFI_SUCCESS)
>> +                continue;
>> +        }
>> +
>> +        if (len >= (size - 1))
>> +            continue;
>> +
>> +        buf[len] = key.unicode_char;
>> +        len++;
>> +        printf(ANSI_CURSOR_POSITION, row, col);
>> +        ret = EFI_CALL(cout->output_string(cout, buf));
>> +        if (ret != EFI_SUCCESS)
>> +            return ret;
>> +    }
>> +}
>> diff --git a/lib/efi_loader/efi_disk.c b/lib/efi_loader/efi_disk.c
>> index 8fb5b2363c..58736a8a5b 100644
>> --- a/lib/efi_loader/efi_disk.c
>> +++ b/lib/efi_loader/efi_disk.c
>> @@ -750,3 +750,14 @@ efi_status_t efi_disk_init(void)
>>
>>       return EFI_SUCCESS;
>>   }
>> +
>> +efi_status_t efi_disk_get_device_name(struct efi_block_io *this, char 
>> *buf, int size)
>> +{
>> +    struct efi_disk_obj *diskobj;
>> +
>> +    diskobj = container_of(this, struct efi_disk_obj, ops);
>> +
>> +    snprintf(buf, size, "%s%d:%d", diskobj->ifname, 
>> diskobj->dev_index, diskobj->part);
> 
> A space would improve readability and better match U-Boot syntax.
> %s/%s%d:%d/%s %d:%d/
> 
> I guess for MMC we are only supporting booting form the user partition.
> Otherwise the information would be incomplete.
> 
> Best regards
> 
> Heinrich
> 
>> +
>> +    return EFI_SUCCESS;
>> +}
>> diff --git a/lib/efi_loader/efi_file.c b/lib/efi_loader/efi_file.c
>> index 7a7077e6d0..c96a7f7ca3 100644
>> --- a/lib/efi_loader/efi_file.c
>> +++ b/lib/efi_loader/efi_file.c
>> @@ -246,10 +246,10 @@ error:
>>       return NULL;
>>   }
>>
>> -static efi_status_t efi_file_open_int(struct efi_file_handle *this,
>> -                      struct efi_file_handle **new_handle,
>> -                      u16 *file_name, u64 open_mode,
>> -                      u64 attributes)
>> +efi_status_t efi_file_open_int(struct efi_file_handle *this,
>> +                   struct efi_file_handle **new_handle,
>> +                   u16 *file_name, u64 open_mode,
>> +                   u64 attributes)
>>   {
>>       struct file_handle *fh = to_fh(this);
>>       efi_status_t ret;
>> @@ -369,11 +369,17 @@ static efi_status_t file_close(struct 
>> file_handle *fh)
>>       return EFI_SUCCESS;
>>   }
>>
>> -static efi_status_t EFIAPI efi_file_close(struct efi_file_handle *file)
>> +efi_status_t efi_file_close_int(struct efi_file_handle *file)
>>   {
>>       struct file_handle *fh = to_fh(file);
>> +
>> +    return file_close(fh);
>> +}
>> +
>> +static efi_status_t EFIAPI efi_file_close(struct efi_file_handle *file)
>> +{
>>       EFI_ENTRY("%p", file);
>> -    return EFI_EXIT(file_close(fh));
>> +    return EFI_EXIT(efi_file_close_int(file));
>>   }
>>
>>   static efi_status_t EFIAPI efi_file_delete(struct efi_file_handle 
>> *file)
>> @@ -562,8 +568,8 @@ static efi_status_t dir_read(struct file_handle 
>> *fh, u64 *buffer_size,
>>       return EFI_SUCCESS;
>>   }
>>
>> -static efi_status_t efi_file_read_int(struct efi_file_handle *this,
>> -                      efi_uintn_t *buffer_size, void *buffer)
>> +efi_status_t efi_file_read_int(struct efi_file_handle *this,
>> +                   efi_uintn_t *buffer_size, void *buffer)
>>   {
>>       struct file_handle *fh = to_fh(this);
>>       efi_status_t ret = EFI_SUCCESS;
>> @@ -773,24 +779,11 @@ out:
>>       return EFI_EXIT(ret);
>>   }
>>
>> -/**
>> - * efi_file_setpos() - set current position in file
>> - *
>> - * This function implements the SetPosition service of the EFI file 
>> protocol.
>> - * See the UEFI spec for details.
>> - *
>> - * @file:    file handle
>> - * @pos:    new file position
>> - * Return:    status code
>> - */
>> -static efi_status_t EFIAPI efi_file_setpos(struct efi_file_handle *file,
>> -                       u64 pos)
>> +efi_status_t efi_file_setpos_int(struct efi_file_handle *file, u64 pos)
>>   {
>>       struct file_handle *fh = to_fh(file);
>>       efi_status_t ret = EFI_SUCCESS;
>>
>> -    EFI_ENTRY("%p, %llu", file, pos);
>> -
>>       if (fh->isdir) {
>>           if (pos != 0) {
>>               ret = EFI_UNSUPPORTED;
>> @@ -812,6 +805,28 @@ static efi_status_t EFIAPI efi_file_setpos(struct 
>> efi_file_handle *file,
>>       fh->offset = pos;
>>
>>   error:
>> +    return ret;
>> +}
>> +
>> +/**
>> + * efi_file_setpos() - set current position in file
>> + *
>> + * This function implements the SetPosition service of the EFI file 
>> protocol.
>> + * See the UEFI spec for details.
>> + *
>> + * @file:    file handle
>> + * @pos:    new file position
>> + * Return:    status code
>> + */
>> +static efi_status_t EFIAPI efi_file_setpos(struct efi_file_handle *file,
>> +                       u64 pos)
>> +{
>> +    efi_status_t ret = EFI_SUCCESS;
>> +
>> +    EFI_ENTRY("%p, %llu", file, pos);
>> +
>> +    ret = efi_file_setpos_int(file, pos);
>> +
>>       return EFI_EXIT(ret);
>>   }
>>
>> @@ -1138,17 +1153,23 @@ struct efi_file_handle 
>> *efi_file_from_path(struct efi_device_path *fp)
>>       return f;
>>   }
>>
>> +efi_status_t efi_open_volume_int(struct 
>> efi_simple_file_system_protocol *this,
>> +                 struct efi_file_handle **root)
>> +{
>> +    struct file_system *fs = to_fs(this);
>> +
>> +    *root = file_open(fs, NULL, NULL, 0, 0);
>> +
>> +    return EFI_SUCCESS;
>> +}
>> +
>>   static efi_status_t EFIAPI
>>   efi_open_volume(struct efi_simple_file_system_protocol *this,
>>           struct efi_file_handle **root)
>>   {
>> -    struct file_system *fs = to_fs(this);
>> -
>>       EFI_ENTRY("%p, %p", this, root);
>>
>> -    *root = file_open(fs, NULL, NULL, 0, 0);
>> -
>> -    return EFI_EXIT(EFI_SUCCESS);
>> +    return EFI_EXIT(efi_open_volume_int(this, root));
>>   }
>>
>>   struct efi_simple_file_system_protocol *
> 


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

* Re: [PATCH v5 06/17] efi_loader: bootmgr: add booting from removable media
  2022-04-28  8:09 ` [PATCH v5 06/17] efi_loader: bootmgr: add booting from removable media Masahisa Kojima
@ 2022-04-29 17:03   ` Heinrich Schuchardt
  2022-05-05 12:05     ` Mark Kettenis
  0 siblings, 1 reply; 58+ messages in thread
From: Heinrich Schuchardt @ 2022-04-29 17:03 UTC (permalink / raw)
  To: Masahisa Kojima
  Cc: Ilias Apalodimas, Simon Glass, Takahiro Akashi, Francois Ozog,
	Mark Kettenis, Mark Kettenis, Peter Hoyes, Neil Armstrong,
	Andre Przywara, u-boot

On 4/28/22 10:09, Masahisa Kojima wrote:
> From: AKASHI Takahiro <takahiro.akashi@linaro.org>
>
> Under the current implementation, booting from removable media using
> a architecture-specific default image name, say BOOTAA64.EFI, is
> supported only in distro_bootcmd script. See the commit 74522c898b35
> ("efi_loader: Add distro boot script for removable media").
>
> This is, however, half-baked implementation because
> 1) UEFI specification requires this feature to be implemented as part
>     of Boot Manager's responsibility:
>
>    3 - Boot Manager
>    3.5.1 Boot via the Simple File Protocol
>    When booting via the EFI_SIMPLE_FILE_SYSTEM_PROTOCOL, the FilePath will
>    start with a device path that points to the device that implements the
>    EFI_SIMPLE_FILE_SYSTEM_PROTOCOL or the EFI_BLOCK_IO_PROTOCOL. The next
>    part of the FilePath may point to the file name, including
>    subdirectories, which contain the bootable image. If the file name is
>    a null device path, the file name must be generated from the rules
>    defined below.
>    ...
>    3.5.1.1 Removable Media Boot Behavior
>    To generate a file name when none is present in the FilePath, the
>    firmware must append a default file name in the form
>    \EFI\BOOT\BOOT{machine type short-name}.EFI ...
>
> 2) So (1) entails the hehavior that the user's preference of boot media
>     order should be determined by Boot#### and BootOrder variables.
>
> With this patch, the semantics mentioned above is fully implemented.
> For example, if you want to boot the system from USB and SCSI in this
> order,
> * define Boot0001 which contains only a device path to the USB device
>    (without any file path/name)
> * define Boot0002 which contains only a device path to the SCSI device,
> and
> * set BootOrder to Boot0001:Boot0002
>
> To avoid build error for sandbox, default file name "BOOTSANDBOX.efi"
> is defined even if it is out of scope of UEFI specification.
>
> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
> ---
> Changes in v5:
> - add default file name definition for SANDBOX to avoid build error
>
> Changes from original version:
> - create new include file "efi_default_filename.h" to
>    avoid conflict with config_distro_bootcmd.h
> - modify the target pointer of efi_free_pool(), expand_media_path() should
>    only free the pointer allocated by efi_dp_from_file() function.
>   include/config_distro_bootcmd.h | 14 +--------
>   include/efi_default_filename.h  | 33 ++++++++++++++++++++++
>   lib/efi_loader/efi_bootmgr.c    | 50 ++++++++++++++++++++++++++++++++-
>   3 files changed, 83 insertions(+), 14 deletions(-)
>   create mode 100644 include/efi_default_filename.h
>
> diff --git a/include/config_distro_bootcmd.h b/include/config_distro_bootcmd.h
> index c55023889c..6a3110f27b 100644
> --- a/include/config_distro_bootcmd.h
> +++ b/include/config_distro_bootcmd.h
> @@ -91,19 +91,7 @@
>   #endif
>
>   #ifdef CONFIG_EFI_LOADER
> -#if defined(CONFIG_ARM64)
> -#define BOOTEFI_NAME "bootaa64.efi"
> -#elif defined(CONFIG_ARM)
> -#define BOOTEFI_NAME "bootarm.efi"
> -#elif defined(CONFIG_X86_RUN_32BIT)
> -#define BOOTEFI_NAME "bootia32.efi"
> -#elif defined(CONFIG_X86_RUN_64BIT)
> -#define BOOTEFI_NAME "bootx64.efi"
> -#elif defined(CONFIG_ARCH_RV32I)
> -#define BOOTEFI_NAME "bootriscv32.efi"
> -#elif defined(CONFIG_ARCH_RV64I)
> -#define BOOTEFI_NAME "bootriscv64.efi"
> -#endif
> +#include <efi_default_filename.h>
>   #endif
>
>   #ifdef BOOTEFI_NAME
> diff --git a/include/efi_default_filename.h b/include/efi_default_filename.h
> new file mode 100644
> index 0000000000..cb2ef9e131
> --- /dev/null
> +++ b/include/efi_default_filename.h
> @@ -0,0 +1,33 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +/*
> + * Default boot file name when none is present in the FilePath.
> + * This is defined in the UEFI specification.
> + *
> + * Copyright (c) 2022, Linaro Limited
> + */
> +#ifndef _EFI_DEFAULT_FILENAME_H
> +#define _EFI_DEFAULT_FILENAME_H
> +
> +#if defined(CONFIG_ARM64)
> +#define BOOTEFI_NAME "BOOTAA64.EFI"
> +#elif defined(CONFIG_ARM)
> +#define BOOTEFI_NAME "BOOTARM.EFI"
> +#elif defined(CONFIG_X86_64)
> +#define BOOTEFI_NAME "BOOTX64.EFI"
> +#elif defined(CONFIG_X86)
> +#define BOOTEFI_NAME "BOOTIA32.EFI"
> +#elif defined(CONFIG_ARCH_RV32I)
> +#define BOOTEFI_NAME "BOOTRISCV32.EFI"
> +#elif defined(CONFIG_ARCH_RV64I)
> +#define BOOTEFI_NAME "BOOTRISCV64.EFI"
> +#elif defined(CONFIG_SANDBOX)
> +/*
> + * SANDBOX is not defined in UEFI specification, but
> + * this definition avoids build failure for SANDBOX.
> + */
> +#define BOOTEFI_NAME "BOOTSANDBOX.EFI"

The sandbox should boot the default binary for the host architecture:

#ifndef _EFI_DEFAULT_FILENAME_H
#define _EFI_DEFAULT_FILENAME_H

#include <host_arch.h>

#undef BOOTEFI_NAME

#if HOST_ARCH == HOST_ARCH_X86_64
#define BOOTEFI_NAME "BOOTX64.EFI"
#endif

#if HOST_ARCH == HOST_ARCH_X86
#define BOOTEFI_NAME "BOOTIA32.EFI"
#endif

#if HOST_ARCH == HOST_ARCH_AARCH64
#define BOOTEFI_NAME "BOOTAA64.EFI"
#endif

#if HOST_ARCH == HOST_ARCH_ARM
#define BOOTEFI_NAME "BOOTARM.EFI"
#endif

#if HOST_ARCH == HOST_ARCH_RISCV32
#define BOOTEFI_NAME "BOOTRISCV32.EFI"
#endif

#if HOST_ARCH == HOST_ARCH_RISCV64
#define BOOTEFI_NAME "BOOTRISCV64.EFI"
#endif

#ifndef BOOTEFI_NAME
#error Unsupported UEFI architecture
#endif

#endif

> +#else
> +#error Unsupported UEFI architecture
> +#endif
> +
> +#endif
> diff --git a/lib/efi_loader/efi_bootmgr.c b/lib/efi_loader/efi_bootmgr.c
> index 8c04ecbdc8..22a4302aac 100644
> --- a/lib/efi_loader/efi_bootmgr.c
> +++ b/lib/efi_loader/efi_bootmgr.c
> @@ -11,6 +11,7 @@
>   #include <charset.h>
>   #include <log.h>
>   #include <malloc.h>
> +#include <efi_default_filename.h>
>   #include <efi_loader.h>
>   #include <efi_variable.h>
>   #include <asm/unaligned.h>
> @@ -30,6 +31,50 @@ static const struct efi_runtime_services *rs;
>    * should do normal or recovery boot.
>    */
>
> +/**
> + * expand_media_path() - expand a device path for default file name
> + * @device_path:	device path to check against
> + *
> + * If @device_path is a media or disk partition which houses a file
> + * system, this function returns a full device path which contains
> + * an architecture-specific default file name for removable media.
> + *
> + * Return:	a newly allocated device path
> + */
> +static
> +struct efi_device_path *expand_media_path(struct efi_device_path *device_path)
> +{
> +	struct efi_device_path *dp, *full_path;
> +	efi_handle_t handle;
> +	efi_status_t ret;
> +
> +	if (!device_path)
> +		return NULL;
> +
> +	/*
> +	 * If device_path is a (removable) media or partition which provides
> +	 * simple file system protocol, append a default file name to support
> +	 * booting from removable media.
> +	 */
> +	dp = device_path;
> +	ret = efi_locate_device_path(&efi_simple_file_system_protocol_guid,
> +				     &dp, &handle);

Functions defined as EFIAPI must be called with EFI_CALL() due to the
usage of a register for the global data pointer on ARM.

Best regards

Heinrich

> +	if (ret == EFI_SUCCESS) {
> +		if (dp->type == DEVICE_PATH_TYPE_END) {
> +			dp = efi_dp_from_file(NULL, 0,
> +					      "/EFI/BOOT/" BOOTEFI_NAME);
> +			full_path = efi_dp_append(device_path, dp);
> +			efi_free_pool(dp);
> +		} else {
> +			full_path = efi_dp_dup(device_path);
> +		}
> +	} else {
> +		full_path = efi_dp_dup(device_path);
> +	}
> +
> +	return full_path;
> +}
> +
>   /**
>    * try_load_entry() - try to load image for boot option
>    *
> @@ -68,13 +113,16 @@ static efi_status_t try_load_entry(u16 n, efi_handle_t *handle,
>   	}
>
>   	if (lo.attributes & LOAD_OPTION_ACTIVE) {
> +		struct efi_device_path *file_path;
>   		u32 attributes;
>
>   		log_debug("%s: trying to load \"%ls\" from %pD\n",
>   			  __func__, lo.label, lo.file_path);
>
> -		ret = EFI_CALL(efi_load_image(true, efi_root, lo.file_path,
> +		file_path = expand_media_path(lo.file_path);
> +		ret = EFI_CALL(efi_load_image(true, efi_root, file_path,
>   					      NULL, 0, handle));
> +		efi_free_pool(file_path);
>   		if (ret != EFI_SUCCESS) {
>   			log_warning("Loading %ls '%ls' failed\n",
>   				    varname, lo.label);


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

* Re: [PATCH v5 01/17] lib/charset: add u16_strlcat() function
  2022-04-28  8:09 ` [PATCH v5 01/17] lib/charset: add u16_strlcat() function Masahisa Kojima
@ 2022-04-29 19:36   ` Heinrich Schuchardt
  0 siblings, 0 replies; 58+ messages in thread
From: Heinrich Schuchardt @ 2022-04-29 19:36 UTC (permalink / raw)
  To: Masahisa Kojima
  Cc: Ilias Apalodimas, Simon Glass, Takahiro Akashi, Francois Ozog,
	Mark Kettenis, u-boot

On 4/28/22 10:09, Masahisa Kojima wrote:
> Provide u16 string version of strlcat().
>
> Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
> ---
> Changes in v5:
> - change 3rd argument from size to count, it indicates
>    the maximum u16 string count that dest buffer can have.
>    Other u16_strXXX functions in U-Boot use string count, not the
>    buffer size. u16_strlcat() should follow.
> - update function comment to clealy describe the behavior
> - use strlen() instead of strnlen(), to correctly handle
>    the case if the count is smaller than or equal to initial dest count
>
> Changes in v4:
> - add blank line above the return statement
>
> Changes in v2:
> - implement u16_strlcat(with the destination buffer size in argument)
>    instead of u16_strcat
>   include/charset.h | 17 +++++++++++++++++
>   lib/charset.c     | 22 ++++++++++++++++++++++
>   2 files changed, 39 insertions(+)
>
> diff --git a/include/charset.h b/include/charset.h
> index 38908e08f0..ae356ad9a5 100644
> --- a/include/charset.h
> +++ b/include/charset.h
> @@ -261,6 +261,23 @@ u16 *u16_strcpy(u16 *dest, const u16 *src);
>    */
>   u16 *u16_strdup(const void *src);
>
> +/**
> + * u16_strlcat() - Append a length-limited, %NUL-terminated string to another
> + *
> + * Append the src string to the dest string, overwriting the terminating
> + * null word at the end of dest, and then adds a terminating null word.
> + *
> + * @dest:		destination buffer (must include the trailing 0x0000)
> + * @src:		source buffer (must include the trailing 0x0000)
> + * @count:		maximum number of code points that dest buffer can have,
> + *			including the trailing 0x0000
> + * Return:		total count of the created u16 string
> + *			u16_strlen(src) + min(count, u16_strlen(initial dest)),
> + *			does not include trailing 0x0000.
> + *			If return value >= count, truncation occurred.
> + */
> +size_t u16_strlcat(u16 *dest, const u16 *src, size_t size);
> +
>   /**
>    * utf16_to_utf8() - Convert an utf16 string to utf8
>    *
> diff --git a/lib/charset.c b/lib/charset.c
> index de201cf3b9..f295dfc11a 100644
> --- a/lib/charset.c
> +++ b/lib/charset.c
> @@ -416,6 +416,28 @@ u16 *u16_strdup(const void *src)
>   	return new;
>   }
>
> +size_t u16_strlcat(u16 *dest, const u16 *src, size_t count)
> +{
> +	size_t destlen, srclen, ret;
> +
> +	destlen = u16_strlen(dest);
> +	srclen = u16_strlen(src);
> +	ret = min(count, destlen) + srclen; /* does not include trailing 0x0000 */

Why should count influence the return value?

Using a first call with count = 0 and src = NULL to retrieve the buffer
size to realloc() and a second call after re-allocation would be a
typical usage scenario.

Best regards

Heinrich

> +
> +	if (destlen >= count)
> +		return ret;
> +
> +	dest += destlen;
> +	count -= destlen;
> +	if (srclen >= count)
> +		srclen = count - 1 /* for trailing 0x0000 */;
> +
> +	memcpy(dest, src, srclen * sizeof(u16));
> +	dest[srclen] = u'\0';
> +
> +	return ret;
> +}
> +
>   /* Convert UTF-16 to UTF-8.  */
>   uint8_t *utf16_to_utf8(uint8_t *dest, const uint16_t *src, size_t size)
>   {


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

* Re: [PATCH v5 04/17] menu: menu_get_choice() return -ENOENT if menu item is empty
  2022-04-28  8:09 ` [PATCH v5 04/17] menu: menu_get_choice() return -ENOENT if menu item is empty Masahisa Kojima
@ 2022-04-29 19:38   ` Heinrich Schuchardt
  0 siblings, 0 replies; 58+ messages in thread
From: Heinrich Schuchardt @ 2022-04-29 19:38 UTC (permalink / raw)
  To: Masahisa Kojima, u-boot
  Cc: Ilias Apalodimas, Simon Glass, Takahiro Akashi, Francois Ozog,
	Mark Kettenis

On 4/28/22 10:09, Masahisa Kojima wrote:
> menu_get_choice() needs to handle the case that menu item
> is empty. In this case, menu_get_choice() returns -ENOENT.
>
> Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
> ---
> Newly created in v5
>
>   common/menu.c | 3 +++
>   1 file changed, 3 insertions(+)
>
> diff --git a/common/menu.c b/common/menu.c
> index b577d80b4f..4118c6dc3c 100644
> --- a/common/menu.c
> +++ b/common/menu.c
> @@ -271,6 +271,9 @@ int menu_get_choice(struct menu *m, void **choice)
>   	if (!m || !choice)
>   		return -EINVAL;
>
> +	if (m->item_cnt == 0)

nits:

We tend to use (!m->item_cnt).

Best regards

Heinrich

> +		return -ENOENT;
> +
>   	if (!m->prompt)
>   		return menu_default_choice(m, choice);
>


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

* Re: [PATCH v5 07/17] bootmenu: flush input buffer before waiting user key input
  2022-04-28  8:09 ` [PATCH v5 07/17] bootmenu: flush input buffer before waiting user key input Masahisa Kojima
@ 2022-04-29 19:46   ` Heinrich Schuchardt
  2022-05-09  8:33     ` Masahisa Kojima
  0 siblings, 1 reply; 58+ messages in thread
From: Heinrich Schuchardt @ 2022-04-29 19:46 UTC (permalink / raw)
  To: Masahisa Kojima
  Cc: Ilias Apalodimas, Simon Glass, Takahiro Akashi, Francois Ozog,
	Mark Kettenis, u-boot

On 4/28/22 10:09, Masahisa Kojima wrote:
> Before waiting user key input to stop autoboot, the input buffer
> must be flushed.

I am not sure if this is desirable.

The default time for autoboot stopping is just 2 seconds. So it is nice
to be able to type ahead.

On the other hand I have seen boards not booting because after the
serial driver was initialized it found some random byte in the input buffer.

I would prefer flushing of input in serial_init().

Best regards

Heinrich

>
> Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
> ---
> Change in v5:
> - newly created, split into separate patch
>
>   cmd/bootmenu.c | 4 ++++
>   1 file changed, 4 insertions(+)
>
> diff --git a/cmd/bootmenu.c b/cmd/bootmenu.c
> index d573487272..9a32a18b19 100644
> --- a/cmd/bootmenu.c
> +++ b/cmd/bootmenu.c
> @@ -87,6 +87,10 @@ static void bootmenu_autoboot_loop(struct bootmenu_data *menu,
>   	int i, c;
>
>   	if (menu->delay > 0) {
> +		/* flush input */
> +		while (tstc())
> +			getchar();
> +
>   		printf(ANSI_CURSOR_POSITION, menu->count + 5, 1);
>   		printf("  Hit any key to stop autoboot: %2d ", menu->delay);
>   	}


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

* Re: [PATCH v5 08/17] bootmenu: update bootmenu_entry structure
  2022-04-28  8:09 ` [PATCH v5 08/17] bootmenu: update bootmenu_entry structure Masahisa Kojima
@ 2022-04-29 19:51   ` Heinrich Schuchardt
  2022-05-01 20:54     ` Heinrich Schuchardt
  0 siblings, 1 reply; 58+ messages in thread
From: Heinrich Schuchardt @ 2022-04-29 19:51 UTC (permalink / raw)
  To: Masahisa Kojima, u-boot
  Cc: Ilias Apalodimas, Simon Glass, Takahiro Akashi, Francois Ozog,
	Mark Kettenis

On 4/28/22 10:09, Masahisa Kojima wrote:
> This is a preparation for succeeding addition of uefi boot
> and distro boot menu entries into bootmenu.
> The bootmenu_entry title is updated to u16 string because
> uefi use u16 string. This commit also factors out the function
> to prepare the entries generated by "bootmenu_x" U-Boot environment
> variable.
>
> This commit also updates the bootmenu entry title.
> The entry derived from "bootmenu_x" U-Boot environment variable
> has the "bootmenu_xx" prefix as below.
>
>    *** U-Boot Boot Menu ***
>
>       bootmenu_00   : Boot 1. kernel
>       bootmenu_01   : Boot 2. kernel
>       bootmenu_02   : Reset board
>
> Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
> ---
> Changes in v5:
> - split into the separate patch
> - add function description comment
>
>   cmd/bootmenu.c | 110 +++++++++++++++++++++++++++++++++++--------------
>   1 file changed, 78 insertions(+), 32 deletions(-)
>
> diff --git a/cmd/bootmenu.c b/cmd/bootmenu.c
> index 9a32a18b19..15ad621c9f 100644
> --- a/cmd/bootmenu.c
> +++ b/cmd/bootmenu.c
> @@ -3,6 +3,7 @@
>    * (C) Copyright 2011-2013 Pali Rohár <pali@kernel.org>
>    */
>
> +#include <charset.h>
>   #include <common.h>
>   #include <command.h>
>   #include <ansi.h>
> @@ -24,11 +25,18 @@
>    */
>   #define MAX_ENV_SIZE	(9 + 2 + 1)
>
> +enum boot_type {
> +	BOOTMENU_TYPE_NONE = 0,
> +	BOOTMENU_TYPE_BOOTMENU,
> +};
> +
>   struct bootmenu_entry {
>   	unsigned short int num;		/* unique number 0 .. MAX_COUNT */
>   	char key[3];			/* key identifier of number */
> -	char *title;			/* title of entry */
> +	u16 *title;			/* title of entry */
>   	char *command;			/* hush command of entry */
> +	enum boot_type type;		/* boot type of entry */
> +	u16 bootorder;			/* order for each boot type */
>   	struct bootmenu_data *menu;	/* this bootmenu */
>   	struct bootmenu_entry *next;	/* next menu entry (num+1) */
>   };
> @@ -75,7 +83,10 @@ static void bootmenu_print_entry(void *data)
>   	if (reverse)
>   		puts(ANSI_COLOR_REVERSE);
>
> -	puts(entry->title);
> +	if (entry->type == BOOTMENU_TYPE_BOOTMENU)
> +		printf("bootmenu_%02d   : %ls", entry->bootorder, entry->title);
> +	else
> +		printf("%ls", entry->title);
>
>   	if (reverse)
>   		puts(ANSI_COLOR_RESET);
> @@ -279,31 +290,32 @@ static void bootmenu_destroy(struct bootmenu_data *menu)
>   	free(menu);
>   }
>
> -static struct bootmenu_data *bootmenu_create(int delay)
> +/**
> + * prepare_bootmenu_entry() - generate the bootmenu_xx entries
> + *
> + * This function read the "bootmenu_x" U-Boot environment variable
> + * and generate the bootmenu entries.
> + *
> + * @menu:	pointer to the bootmenu structure
> + * @current:	pointer to the last bootmenu entry list
> + * @index:	pointer to the index of the last bootmenu entry,
> + *		the number of bootmenu entry is added by this function
> + * Return:	1 on success, negative value on error
> + */
> +static int prepare_bootmenu_entry(struct bootmenu_data *menu,
> +				  struct bootmenu_entry **current,
> +				  unsigned short int *index)
>   {
> -	unsigned short int i = 0;
> -	const char *option;
> -	struct bootmenu_data *menu;
> -	struct bootmenu_entry *iter = NULL;
> -
>   	int len;
>   	char *sep;
> -	char *default_str;
> -	struct bootmenu_entry *entry;
> -
> -	menu = malloc(sizeof(struct bootmenu_data));
> -	if (!menu)
> -		return NULL;
> -
> -	menu->delay = delay;
> -	menu->active = 0;
> -	menu->first = NULL;
> -
> -	default_str = env_get("bootmenu_default");
> -	if (default_str)
> -		menu->active = (int)simple_strtol(default_str, NULL, 10);
> +	const char *option;
> +	unsigned short int i = *index;
> +	struct bootmenu_entry *entry = NULL;
> +	struct bootmenu_entry *iter = *current;
>
>   	while ((option = bootmenu_getoption(i))) {
> +		u16 *buf;
> +
>   		sep = strchr(option, '=');
>   		if (!sep) {
>   			printf("Invalid bootmenu entry: %s\n", option);
> @@ -312,23 +324,23 @@ static struct bootmenu_data *bootmenu_create(int delay)
>
>   		entry = malloc(sizeof(struct bootmenu_entry));
>   		if (!entry)
> -			goto cleanup;
> +			return -ENOMEM;
>
>   		len = sep-option;
> -		entry->title = malloc(len + 1);
> +		buf = calloc(1, (len + 1) * sizeof(u16));
> +		entry->title = buf;
>   		if (!entry->title) {
>   			free(entry);
> -			goto cleanup;
> +			return -ENOMEM;
>   		}
> -		memcpy(entry->title, option, len);
> -		entry->title[len] = 0;
> +		utf8_utf16_strncpy(&buf, option, len);
>
>   		len = strlen(sep + 1);
>   		entry->command = malloc(len + 1);
>   		if (!entry->command) {
>   			free(entry->title);
>   			free(entry);
> -			goto cleanup;
> +			return -ENOMEM;
>   		}
>   		memcpy(entry->command, sep + 1, len);
>   		entry->command[len] = 0;
> @@ -337,6 +349,8 @@ static struct bootmenu_data *bootmenu_create(int delay)
>
>   		entry->num = i;
>   		entry->menu = menu;
> +		entry->type = BOOTMENU_TYPE_BOOTMENU;
> +		entry->bootorder = i;
>   		entry->next = NULL;
>
>   		if (!iter)
> @@ -351,13 +365,44 @@ static struct bootmenu_data *bootmenu_create(int delay)
>   			break;
>   	}
>
> +	*index = i;
> +	*current = iter;
> +
> +	return 1;
> +}
> +
> +static struct bootmenu_data *bootmenu_create(int delay)
> +{
> +	int ret;
> +	unsigned short int i = 0;
> +	struct bootmenu_data *menu;
> +	struct bootmenu_entry *iter = NULL;
> +	struct bootmenu_entry *entry;
> +	char *default_str;
> +
> +	menu = malloc(sizeof(struct bootmenu_data));
> +	if (!menu)
> +		return NULL;
> +
> +	menu->delay = delay;
> +	menu->active = 0;
> +	menu->first = NULL;
> +
> +	default_str = env_get("bootmenu_default");
> +	if (default_str)
> +		menu->active = (int)simple_strtol(default_str, NULL, 10);
> +
> +	ret = prepare_bootmenu_entry(menu, &iter, &i);
> +	if (ret < 0)
> +		goto cleanup;
> +
>   	/* Add U-Boot console entry at the end */
>   	if (i <= MAX_COUNT - 1) {
>   		entry = malloc(sizeof(struct bootmenu_entry));
>   		if (!entry)
>   			goto cleanup;
>
> -		entry->title = strdup("U-Boot console");
> +		entry->title = u16_strdup(u"U-Boot console");
>   		if (!entry->title) {
>   			free(entry);
>   			goto cleanup;
> @@ -374,6 +419,7 @@ static struct bootmenu_data *bootmenu_create(int delay)
>
>   		entry->num = i;
>   		entry->menu = menu;
> +		entry->type = BOOTMENU_TYPE_NONE;
>   		entry->next = NULL;
>
>   		if (!iter)
> @@ -431,7 +477,7 @@ static void bootmenu_show(int delay)
>   {
>   	int init = 0;
>   	void *choice = NULL;
> -	char *title = NULL;
> +	u16 *title = NULL;
>   	char *command = NULL;
>   	struct menu *menu;
>   	struct bootmenu_data *bootmenu;
> @@ -482,7 +528,7 @@ static void bootmenu_show(int delay)
>
>   	if (menu_get_choice(menu, &choice) == 1) {
>   		iter = choice;
> -		title = strdup(iter->title);
> +		title = u16_strdup(iter->title);

We must still be able to use the bootmenu command outside of the UEFI
sub-system. There wide strings will not be used.

We need a testcase for this.

Best regards

Heinrich

>   		command = strdup(iter->command);
>   	}
>
> @@ -497,7 +543,7 @@ cleanup:
>   	}
>
>   	if (title && command) {
> -		debug("Starting entry '%s'\n", title);
> +		debug("Starting entry '%ls'\n", title);
>   		free(title);
>   		run_command(command, 0);
>   		free(command);


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

* Re: [PATCH v5 13/17] efi_loader: menu-driven addition of UEFI boot option
  2022-04-29 10:56     ` Heinrich Schuchardt
@ 2022-04-30 12:49       ` Heinrich Schuchardt
  2022-05-06 17:30         ` Heinrich Schuchardt
  0 siblings, 1 reply; 58+ messages in thread
From: Heinrich Schuchardt @ 2022-04-30 12:49 UTC (permalink / raw)
  To: Masahisa Kojima
  Cc: Ilias Apalodimas, Simon Glass, Takahiro Akashi, Francois Ozog,
	Mark Kettenis, u-boot

On 4/29/22 12:56, Heinrich Schuchardt wrote:
> On 4/28/22 18:33, Heinrich Schuchardt wrote:
>> On 4/28/22 10:09, Masahisa Kojima wrote:
>>> This commit supports the menu-driven UEFI boot option addition.
>>> User can select the block device volume having
>>> efi_simple_file_system_protocol and select the file corresponding
>>> to the Boot#### variable. Then user enter the label of the BOOT####
>>> variable in utf8.
>>>
>>> Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
>>> ---
>>> Changes in v5:
>>> - remove forward declarations
>>> - add const qualifier for menu items
>>> - fix the possible unaligned access for directory info access
>>> - split into three commit 1)add boot option 2) delete boot option 
>>> 3)change boot order
>>>    This commit is 1)add boot option.
>>> - fix file name buffer allocation size, it should be 
>>> EFI_BOOTMENU_FILE_PATH_MAX * sizeof(u16)
>>> - fix wrong size checking for file selection
>>>
>>> Chanes in v4:
>>> - UEFI boot option maintenance menu is integrated into bootmenu
>>> - display the simplified volume name(e.g. usb0:1, nvme1:2) for the
>>>    volume selection
>>> - instead of extending lib/efi_loader/efi_bootmgr.c, newly create
>>>    lib/efi_loader/efi_bootmenu_maintenance.c and implement boot
>>>    variable maintenance into it.
>>>
>>> Changes in RFC v3:
>>>   not included in v3 series
>>>
>>> Changes in RFC v2:
>>> - enable utf8 user input for boot option name
>>> - create lib/efi_loader/efi_console.c::efi_console_get_u16_string() for
>>>    utf8 user input handling
>>> - use u16_strlcat instead of u16_strcat
>>> - remove the EFI_CALLs, and newly create or expose the following
>>>    xxx_int() functions.
>>>      efi_locate_handle_buffer_int(), efi_open_volume_int(),
>>>      efi_file_open_int(), efi_file_close_int(), efi_file_read_int() and
>>>      efi_file_setpos_int().
>>>    Note that EFI_CALLs still exist for EFI_DEVICE_PATH_TO_TEXT_PROTOCOL
>>>    and EFI_SIMPLE_TEXT_INPUT/OUTPUT_PROTOCOL
>>> - use efi_search_protocol() instead of calling locate_protocol() to get
>>>    the device_path_to_text_protocol interface.
>>> - remove unnecessary puts(ANSI_CLEAR_LINE), this patch is still 
>>> depends on
>>>    puts(ANSI_CLEAR_CONSOLE)
>>> - skip SetVariable() if the bootorder is not changed
>>>
>>>   cmd/bootmenu.c                            |  69 +-
>>>   include/efi_loader.h                      |  37 +
>>>   lib/efi_loader/Makefile                   |   1 +
>>>   lib/efi_loader/efi_bootmenu_maintenance.c | 862 ++++++++++++++++++++++
>>>   lib/efi_loader/efi_boottime.c             |  52 +-
>>>   lib/efi_loader/efi_console.c              |  81 ++
>>>   lib/efi_loader/efi_disk.c                 |  11 +
>>>   lib/efi_loader/efi_file.c                 |  75 +-
>>>   8 files changed, 1133 insertions(+), 55 deletions(-)
>>>   create mode 100644 lib/efi_loader/efi_bootmenu_maintenance.c
>>>
>>> diff --git a/cmd/bootmenu.c b/cmd/bootmenu.c
>>> index eb23afdd41..860cb83182 100644
>>> --- a/cmd/bootmenu.c
>>> +++ b/cmd/bootmenu.c
>>> @@ -21,6 +21,8 @@
>>>
>>>   /* maximum bootmenu entries */
>>>   #define MAX_COUNT    99
>>> +#define STATIC_ENTRY 2
>>> +#define MAX_DYNAMIC_ENTRY (MAX_COUNT - STATIC_ENTRY)
>>>
>>>   /* maximal size of bootmenu env
>>>    *  9 = strlen("bootmenu_")
>>> @@ -41,10 +43,11 @@ enum boot_type {
>>>       BOOTMENU_TYPE_BOOTMENU,
>>>       BOOTMENU_TYPE_UEFI_BOOT_OPTION,
>>>       BOOTMENU_TYPE_DISTRO_BOOT,
>>> +    BOOTMENU_TYPE_UEFI_MAINTENANCE,
>>>   };
>>>
>>>   struct bootmenu_entry {
>>> -    unsigned short int num;        /* unique number 0 .. MAX_COUNT */
>>> +    unsigned short int num;        /* unique number 0 .. 
>>> MAX_DYNAMIC_ENTRY */
>>>       char key[3];            /* key identifier of number */
>>>       u16 *title;            /* title of entry */
>>>       char *command;            /* hush command of entry */
>>> @@ -58,7 +61,7 @@ static char *bootmenu_getoption(unsigned short int n)
>>>   {
>>>       char name[MAX_ENV_SIZE];
>>>
>>> -    if (n > MAX_COUNT)
>>> +    if (n > MAX_DYNAMIC_ENTRY)
>>>           return NULL;
>>>
>>>       sprintf(name, "bootmenu_%d", n);
>>> @@ -229,7 +232,7 @@ static int prepare_bootmenu_entry(struct 
>>> bootmenu_data *menu,
>>>           iter = entry;
>>>           ++i;
>>>
>>> -        if (i == MAX_COUNT - 1)
>>> +        if (i == MAX_DYNAMIC_ENTRY)
>>>               break;
>>>       }
>>>
>>> @@ -317,7 +320,7 @@ static int prepare_uefi_bootorder_entry(struct 
>>> bootmenu_data *menu,
>>>
>>>           free(load_option);
>>>
>>> -        if (i == MAX_COUNT - 1)
>>> +        if (i == MAX_DYNAMIC_ENTRY)
>>>               break;
>>>       }
>>>
>>> @@ -481,7 +484,7 @@ static int prepare_distro_boot_entry(struct 
>>> bootmenu_data *menu,
>>>           iter = entry;
>>>           i++;
>>>
>>> -        if (i == MAX_COUNT - 1)
>>> +        if (i == MAX_DYNAMIC_ENTRY)
>>>               break;
>>>
>>>           token = strtok(NULL, " ");
>>> @@ -520,19 +523,56 @@ static struct bootmenu_data 
>>> *bootmenu_create(int delay)
>>>           goto cleanup;
>>>
>>>       if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR)) {
>>> -        if (i < MAX_COUNT - 1) {
>>> +        if (i < MAX_DYNAMIC_ENTRY) {
>>>               ret = prepare_uefi_bootorder_entry(menu, &iter, &i);
>>>               if (ret < 0 && ret != -ENOENT)
>>>                   goto cleanup;
>>>           }
>>>       }
>>>
>>> -    if (i < MAX_COUNT - 1) {
>>> +    if (i < MAX_DYNAMIC_ENTRY) {
>>>           ret = prepare_distro_boot_entry(menu, &iter, &i);
>>>           if (ret < 0 && ret != -ENOENT)
>>>               goto cleanup;
>>>       }
>>>
>>> +    if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR)) {
>>> +        /* Add UEFI Boot Manager Maintenance entry */
>>> +        if (i <= MAX_DYNAMIC_ENTRY) {
>>> +            entry = malloc(sizeof(struct bootmenu_entry));
>>> +            if (!entry)
>>> +                goto cleanup;
>>> +
>>> +            entry->title = u16_strdup(u"UEFI Boot Manager 
>>> Maintenance");
>>> +            if (!entry->title) {
>>> +                free(entry);
>>> +                goto cleanup;
>>> +            }
>>> +
>>> +            entry->command = strdup("");
>>> +            if (!entry->command) {
>>> +                free(entry->title);
>>> +                free(entry);
>>> +                goto cleanup;
>>> +            }
>>> +
>>> +            sprintf(entry->key, "%d", i);
>>> +
>>> +            entry->num = i;
>>> +            entry->menu = menu;
>>> +            entry->type = BOOTMENU_TYPE_UEFI_MAINTENANCE;
>>> +            entry->next = NULL;
>>> +
>>> +            if (!iter)
>>> +                menu->first = entry;
>>> +            else
>>> +                iter->next = entry;
>>> +
>>> +            iter = entry;
>>> +            i++;
>>> +        }
>>> +    }
>>> +
>>>       /* Add U-Boot console entry at the end */
>>>       if (i <= MAX_COUNT - 1) {
>>>           entry = malloc(sizeof(struct bootmenu_entry));
>>> @@ -704,6 +744,12 @@ static enum bootmenu_ret bootmenu_show(int delay)
>>>           title = u16_strdup(iter->title);
>>>           command = strdup(iter->command);
>>>
>>> +        if (iter->type == BOOTMENU_TYPE_UEFI_MAINTENANCE) {
>>> +            efi_bootmenu_show_maintenance_menu();
> 
> This does not compile for CONFIG_EFI_LOADER=n.
> And it is not needed for CONFIG_CMD_BOOTEFI_BOOTMGR=n.
> 
>>> +            ret = BOOTMENU_RET_UPDATED;
>>> +            goto cleanup;
>>> +        }
>>> +
>>>           /* last entry is U-Boot console or Quit */
>>>           if (iter->num == iter->menu->count - 1) {
>>>               ret = BOOTMENU_RET_QUIT;
>>> @@ -794,6 +840,7 @@ int do_bootmenu(struct cmd_tbl *cmdtp, int flag, 
>>> int argc, char *const argv[])
>>>   {
>>>       char *delay_str = NULL;
>>>       int delay = 10;
>>> +    int ret;
>>>
>>>   #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
>>>       delay = CONFIG_BOOTDELAY;
>>> @@ -808,7 +855,13 @@ int do_bootmenu(struct cmd_tbl *cmdtp, int flag, 
>>> int argc, char *const argv[])
>>>       if (delay_str)
>>>           delay = (int)simple_strtol(delay_str, NULL, 10);
>>>
>>> -    bootmenu_show(delay);
>>> +    while (1) {
>>> +        ret =  bootmenu_show(delay);
>>> +        delay = -1;
>>> +        if (ret != BOOTMENU_RET_UPDATED)
>>> +            break;
>>> +    }
>>> +
>>>       return 0;
>>>   }
>>>
>>> diff --git a/include/efi_loader.h b/include/efi_loader.h
>>> index effb43369d..533618341b 100644
>>> --- a/include/efi_loader.h
>>> +++ b/include/efi_loader.h
>>> @@ -226,6 +226,9 @@ const char *__efi_nesting_dec(void);
>>>   #define EFI_CACHELINE_SIZE 128
>>>   #endif
>>>
>>> +/* max bootmenu title size for volume selection */
>>> +#define BOOTMENU_DEVICE_NAME_MAX 16
>>> +
>>>   /* Key identifying current memory map */
>>>   extern efi_uintn_t efi_memory_map_key;
>>>
>>> @@ -312,6 +315,9 @@ extern const efi_guid_t 
>>> efi_guid_firmware_management_protocol;
>>>   extern const efi_guid_t efi_esrt_guid;
>>>   /* GUID of the SMBIOS table */
>>>   extern const efi_guid_t smbios_guid;
>>> +/*GUID of console */
>>> +extern const efi_guid_t efi_guid_text_input_protocol;
>>> +extern const efi_guid_t efi_guid_text_output_protocol;
>>>
>>>   extern char __efi_runtime_start[], __efi_runtime_stop[];
>>>   extern char __efi_runtime_rel_start[], __efi_runtime_rel_stop[];
>>> @@ -871,6 +877,8 @@ efi_status_t efi_set_load_options(efi_handle_t 
>>> handle,
>>>                     void *load_options);
>>>   efi_status_t efi_bootmgr_load(efi_handle_t *handle, void 
>>> **load_options);
>>>
>>> +efi_status_t efi_bootmenu_show_maintenance_menu(void);
>>> +
>>>   /**
>>>    * struct efi_image_regions - A list of memory regions
>>>    *
>>> @@ -1042,4 +1050,33 @@ efi_status_t efi_esrt_populate(void);
>>>   efi_status_t efi_load_capsule_drivers(void);
>>>
>>>   efi_status_t platform_get_eventlog(struct udevice *dev, u64 *addr, 
>>> u32 *sz);
>>> +
>>> +efi_status_t efi_locate_handle_buffer_int(enum 
>>> efi_locate_search_type search_type,
>>> +                      const efi_guid_t *protocol, void *search_key,
>>> +                      efi_uintn_t *no_handles, efi_handle_t **buffer);
>>> +
>>> +efi_status_t efi_open_volume_int(struct 
>>> efi_simple_file_system_protocol *this,
>>> +                 struct efi_file_handle **root);
>>> +efi_status_t efi_file_open_int(struct efi_file_handle *this,
>>> +                   struct efi_file_handle **new_handle,
>>> +                   u16 *file_name, u64 open_mode,
>>> +                   u64 attributes);
>>> +efi_status_t efi_file_close_int(struct efi_file_handle *file);
>>> +efi_status_t efi_file_read_int(struct efi_file_handle *this,
>>> +                   efi_uintn_t *buffer_size, void *buffer);
>>> +efi_status_t efi_file_setpos_int(struct efi_file_handle *file, u64 
>>> pos);
>>> +
>>> +typedef efi_status_t (*efi_console_filter_func)(struct efi_input_key 
>>> *key);
>>> +efi_status_t efi_console_get_u16_string
>>> +        (struct efi_simple_text_input_protocol *cin,
>>> +         struct efi_simple_text_output_protocol *cout,
>>> +         u16 *buf, efi_uintn_t count, efi_console_filter_func 
>>> filer_func,
>>> +         int row, int col);
>>> +
>>> +efi_status_t efi_bootmenu_get_unused_bootoption(u16 *buf,
>>> +                        efi_uintn_t buf_size, u32 *index);
>>> +efi_status_t efi_bootmenu_append_bootorder(u16 index);
>>> +
>>> +efi_status_t efi_disk_get_device_name(struct efi_block_io *this, 
>>> char *buf, int size);
>>> +
>>>   #endif /* _EFI_LOADER_H */
>>> diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile
>>> index aaaa25cefe..792eabe18a 100644
>>> --- a/lib/efi_loader/Makefile
>>> +++ b/lib/efi_loader/Makefile
>>> @@ -77,6 +77,7 @@ obj-$(CONFIG_EFI_TCG2_PROTOCOL) += efi_tcg2.o
>>>   obj-$(CONFIG_EFI_RISCV_BOOT_PROTOCOL) += efi_riscv.o
>>>   obj-$(CONFIG_EFI_LOAD_FILE2_INITRD) += efi_load_initrd.o
>>>   obj-$(CONFIG_EFI_SIGNATURE_SUPPORT) += efi_signature.o
>>> +obj-$(CONFIG_CMD_BOOTMENU) += efi_bootmenu_maintenance.o
> 
> Why should we compile this for CONFIG_CMD_BOOTEFI_BOOTMGR=n?
> 
> Best regards
> 
> Heinrich
> 
>>>
>>>   EFI_VAR_SEED_FILE := $(subst $\",,$(CONFIG_EFI_VAR_SEED_FILE))
>>>   $(obj)/efi_var_seed.o: $(srctree)/$(EFI_VAR_SEED_FILE)
>>> diff --git a/lib/efi_loader/efi_bootmenu_maintenance.c 
>>> b/lib/efi_loader/efi_bootmenu_maintenance.c
>>> new file mode 100644
>>> index 0000000000..77401a7829
>>> --- /dev/null
>>> +++ b/lib/efi_loader/efi_bootmenu_maintenance.c
>>> @@ -0,0 +1,862 @@
>>> +// SPDX-License-Identifier: GPL-2.0+
>>> +/*
>>> + *  Menu-driven UEFI Boot Variable maintenance
>>> + *
>>> + *  Copyright (c) 2022 Masahisa Kojima, Linaro Limited
>>> + */
>>> +
>>> +#define LOG_CATEGORY LOGC_EFI
>>> +
>>> +#include <ansi.h>
>>> +#include <common.h>
>>> +#include <charset.h>
>>> +#include <log.h>
>>> +#include <malloc.h>
>>> +#include <menu.h>
>>> +#include <efi_loader.h>
>>> +#include <efi_variable.h>
>>> +#include <asm/unaligned.h>
>>> +
>>> +static struct efi_simple_text_input_protocol *cin;
>>> +static struct efi_simple_text_output_protocol *cout;
>>> +
>>> +#define EFI_BOOTMENU_ENTRY_NUM_MAX 99
>>> +#define EFI_BOOTMENU_FILE_PATH_MAX 512
>>> +#define EFI_BOOTMENU_FILE_PATH_BUF_SIZE (EFI_BOOTMENU_FILE_PATH_MAX 
>>> * sizeof(u16))
>>> +#define EFI_BOOTMENU_BOOT_NAME_MAX 32
>>> +#define EFI_BOOT_ORDER_MAX_SIZE_IN_DECIMAL 6
>>> +
>>> +typedef efi_status_t (*efi_bootmenu_entry_func)(void *data, bool 
>>> *exit);
>>> +
>>> +/**
>>> + * struct efi_bootmenu_entry - menu entry structure
>>> + *
>>> + * @num:        menu entry index
>>> + * @title:        title of entry
>>> + * @key:        unique key
>>> + * @bootmgr_menu:    pointer to the menu structure
>>> + * @next:        pointer to the next entry
>>> + * @func:        callback function to be called when this entry is 
>>> selected
>>> + * @data:        data to be passed to the callback function
>>> + */
>>> +struct efi_bootmenu_entry {
>>> +    u32 num;
>>> +    u16 *title;
>>> +    char key[6];
>>> +    struct efi_bootmenu *bootmgr_menu;
>>> +    struct efi_bootmenu_entry *next;
>>> +    efi_bootmenu_entry_func func;
>>> +    void *data;
>>> +};
>>> +
>>> +/**
>>> + * struct efi_bootmenu - bootmgr menu structure
>>> + *
>>> + * @delay:    delay for autoboot
>>> + * @active:    active menu entry index
>>> + * @count:    total count of menu entry
>>> + * @first:    pointer to the first menu entry
>>> + */
>>> +struct efi_bootmenu {
>>> +    int delay;
>>> +    int active;
>>> +    int count;
>>> +    struct efi_bootmenu_entry *first;
>>> +};
>>> +
>>> +struct efi_bootmenu_item {
>>> +    u16 *title;
>>> +    efi_bootmenu_entry_func func;
>>> +    void *data;
>>> +};
>>> +
>>> +struct efi_bootmenu_boot_option {
>>> +    struct efi_simple_file_system_protocol *current_volume;
>>> +    struct efi_device_path *dp_volume;
>>> +    u16 *current_path;
>>> +    u16 *boot_name;
>>> +    bool file_selected;
>>> +};
>>> +
>>> +static const struct efi_device_path END = {
>>> +    .type     = DEVICE_PATH_TYPE_END,
>>> +    .sub_type = DEVICE_PATH_SUB_TYPE_END,
>>> +    .length   = sizeof(END),
>>> +};
>>> +
>>> +struct efi_bootmenu_volume_entry_data {
>>> +    struct efi_bootmenu_boot_option *bo;
>>> +    struct efi_simple_file_system_protocol *v;
>>> +    struct efi_device_path *dp;
>>> +};
>>> +
>>> +struct efi_bootmenu_file_entry_data {
>>> +    struct efi_bootmenu_boot_option *bo;
>>> +    bool is_directory;
>>> +    u16 *file_name;
>>> +};
>>> +
>>> +static void efi_bootmenu_print_entry(void *data)
>>> +{
>>> +    struct efi_bootmenu_entry *entry = data;
>>> +    int reverse = (entry->bootmgr_menu->active == entry->num);
>>> +
>>> +    /* TODO: support scroll or page for many entries */
>>> +
>>> +    /*
>>> +     * Move cursor to line where the entry will be drown (entry->count)
>>
>> %s/drown/drawn/
>>
>>> +     * First 3 lines contain bootmgr menu header + one empty line
>>> +     * For the last "Quit" entry, add one empty line
>>> +     */
>>> +    if (entry->num == (entry->bootmgr_menu->count - 1))
>>> +        printf(ANSI_CURSOR_POSITION, entry->num + 5, 1);
>>> +    else
>>> +        printf(ANSI_CURSOR_POSITION, entry->num + 4, 1);
>>> +
>>> +    puts("     ");
>>> +
>>> +    if (reverse)
>>> +        puts(ANSI_COLOR_REVERSE);
>>> +
>>> +    printf("%ls", entry->title);
>>> +
>>> +    if (reverse)
>>> +        puts(ANSI_COLOR_RESET);
>>> +}
>>> +
>>> +static void efi_bootmenu_display_statusline(struct menu *m)
>>> +{
>>> +    struct efi_bootmenu_entry *entry;
>>> +    struct efi_bootmenu *bootmgr_menu;
>>> +
>>> +    if (menu_default_choice(m, (void *)&entry) < 0)
>>> +        return;
>>> +
>>> +    bootmgr_menu = entry->bootmgr_menu;
>>> +
>>> +    printf(ANSI_CURSOR_POSITION, 1, 1);
>>> +    puts(ANSI_CLEAR_LINE);
>>> +    printf(ANSI_CURSOR_POSITION, 2, 1);
>>> +    puts("  *** U-Boot EFI Boot Manager ***");
>>> +    puts(ANSI_CLEAR_LINE_TO_END);
>>> +    printf(ANSI_CURSOR_POSITION, 3, 1);
>>> +    puts(ANSI_CLEAR_LINE);
>>> +
>>> +    /* First 3 lines are bootmgr_menu header + 2 empty lines between 
>>> entries */
>>> +    printf(ANSI_CURSOR_POSITION, bootmgr_menu->count + 5, 1);
>>> +    puts(ANSI_CLEAR_LINE);
>>> +    printf(ANSI_CURSOR_POSITION, bootmgr_menu->count + 6, 1);
>>> +    puts("  Press UP/DOWN to move, ENTER to select, ESC/CTRL+C to 
>>> quit");
>>> +    puts(ANSI_CLEAR_LINE_TO_END);
>>> +    printf(ANSI_CURSOR_POSITION, bootmgr_menu->count + 7, 1);
>>> +    puts(ANSI_CLEAR_LINE);
>>> +}
>>> +
>>> +static char *efi_bootmenu_choice_entry(void *data)
>>> +{
>>> +    int i;
>>> +    int esc = 0;
>>> +    struct efi_bootmenu_entry *iter;
>>> +    enum bootmenu_key key = KEY_NONE;
>>> +    struct efi_bootmenu *bootmgr_menu = data;
>>> +
>>> +    while (1) {
>>> +        if (bootmgr_menu->delay >= 0) {
>>> +            /* Autoboot was not stopped */
>>> +            bootmenu_autoboot_loop((struct bootmenu_data 
>>> *)bootmgr_menu, &key, &esc);
>>> +        } else {
>>> +            /* Some key was pressed, so autoboot was stopped */
>>> +            bootmenu_loop((struct bootmenu_data *)bootmgr_menu, 
>>> &key, &esc);
>>> +        }
>>> +
>>> +        if (bootmgr_menu->delay == 0)
>>> +            key = KEY_QUIT;
>>> +
>>> +        switch (key) {
>>> +        case KEY_UP:
>>> +            if (bootmgr_menu->active > 0)
>>> +                --bootmgr_menu->active;
>>> +            /* no menu key selected, regenerate menu */
>>> +            return NULL;
>>> +        case KEY_DOWN:
>>> +            if (bootmgr_menu->active < bootmgr_menu->count - 1)
>>> +                ++bootmgr_menu->active;
>>> +            /* no menu key selected, regenerate menu */
>>> +            return NULL;
>>> +        case KEY_SELECT:
>>> +            iter = bootmgr_menu->first;
>>> +            for (i = 0; i < bootmgr_menu->active; ++i)
>>> +                iter = iter->next;
>>> +            return iter->key;
>>> +        case KEY_QUIT:
>>> +            /* Quit by choosing the last entry */
>>> +            iter = bootmgr_menu->first;
>>> +            while (iter->next)
>>> +                iter = iter->next;
>>> +            return iter->key;
>>> +        default:
>>> +            break;
>>> +        }
>>> +    }
>>> +
>>> +    /* never happens */
>>> +    debug("bootmgr menu: this should not happen");
>>> +    return NULL;
>>> +}
>>> +
>>> +static void efi_bootmenu_destroy(struct efi_bootmenu *bootmgr_menu)
>>> +{
>>> +    struct efi_bootmenu_entry *next;
>>> +    struct efi_bootmenu_entry *iter = bootmgr_menu->first;
>>> +
>>> +    while (iter) {
>>> +        next = iter->next;
>>> +        free(iter);
>>> +        iter = next;
>>> +    }
>>> +    free(bootmgr_menu);
>>> +}
>>> +
>>> +/**
>>> + * efi_bootmenu_process_common() - main handler for uefi bootmgr menu
>>> + *
>>> + * Construct the structures required to show the menu, then handle
>>> + * the user input intracting with u-boot menu functions.
>>> + *
>>> + * @items:    pointer to the structure of each menu entry
>>> + * @count:    the number of menu entry
>>> + * @delay:    delay for autoboot/autoselect
>>> + * Return:    status code
>>> + */
>>> +static efi_status_t efi_bootmenu_process_common(const struct 
>>> efi_bootmenu_item *items,
>>> +                        int count, int delay)
>>> +{
>>> +    u32 i;
>>> +    bool exit = false;
>>> +    efi_status_t ret;
>>> +    struct menu *menu;
>>> +    void *choice = NULL;
>>> +    struct efi_bootmenu_entry *entry;
>>> +    struct efi_bootmenu *bootmgr_menu;
>>> +    struct efi_bootmenu_entry *iter = NULL;
>>> +
>>> +    if (count > EFI_BOOTMENU_ENTRY_NUM_MAX)
>>> +        return EFI_OUT_OF_RESOURCES;
>>> +
>>> +    bootmgr_menu = calloc(1, sizeof(struct efi_bootmenu));
>>> +    if (!bootmgr_menu)
>>> +        return EFI_OUT_OF_RESOURCES;
>>> +
>>> +    bootmgr_menu->delay = delay;
>>> +    bootmgr_menu->active = 0;
>>> +    bootmgr_menu->first = NULL;
>>> +
>>> +    for (i = 0; i < count; i++) {
>>> +        entry = calloc(1, sizeof(struct efi_bootmenu_entry));
>>> +        if (!entry) {
>>> +            ret = EFI_LOAD_ERROR;
>>> +            goto out;
>>> +        }
>>> +
>>> +        entry->num = i;
>>> +        entry->title = items->title;
>>> +        snprintf(entry->key, sizeof(entry->key), "%04X", i);
>>> +        entry->bootmgr_menu = bootmgr_menu;
>>> +        entry->func = items->func;
>>> +        entry->data = items->data;
>>> +        entry->next = NULL;
>>> +
>>> +        if (!iter)
>>> +            bootmgr_menu->first = entry;
>>> +        else
>>> +            iter->next = entry;
>>> +
>>> +        iter = entry;
>>> +        items++;
>>> +    }
>>> +    bootmgr_menu->count = count;
>>> +
>>> +    menu = menu_create(NULL, 0, 1, efi_bootmenu_display_statusline,
>>> +               efi_bootmenu_print_entry, efi_bootmenu_choice_entry,
>>> +               bootmgr_menu);
>>> +    if (!menu) {
>>> +        ret = EFI_INVALID_PARAMETER;
>>> +        goto out;
>>> +    }
>>> +
>>> +    for (entry = bootmgr_menu->first; entry; entry = entry->next) {
>>> +        if (!menu_item_add(menu, entry->key, entry)) {
>>> +            ret = EFI_INVALID_PARAMETER;
>>> +            goto out;
>>> +        }
>>> +    }
>>> +
>>> +    menu_default_set(menu, bootmgr_menu->first->key);
>>> +
>>> +    while (!exit) {
>>> +        puts(ANSI_CURSOR_HIDE);
>>> +        puts(ANSI_CLEAR_CONSOLE);
>>> +        printf(ANSI_CURSOR_POSITION, 1, 1);
>>> +
>>> +        if (menu_get_choice(menu, &choice)) {
>>> +            entry = choice;
>>> +            if (entry->func)
>>> +                ret = entry->func(entry->data, &exit);
>>> +
>>> +            /* last entry "Quit" is selected, exit this menu */
>>> +            if (entry->num == (entry->bootmgr_menu->count - 1)) {
>>> +                ret = EFI_ABORTED;
>>> +                break;
>>> +            }
>>> +        }
>>> +    }
>>> +
>>> +out:
>>> +    menu_destroy(menu);
>>> +    efi_bootmenu_destroy(bootmgr_menu);
>>> +
>>> +    puts(ANSI_CLEAR_CONSOLE);
>>> +    printf(ANSI_CURSOR_POSITION, 1, 1);
>>> +    puts(ANSI_CURSOR_SHOW);
>>> +
>>> +    return ret;
>>> +}
>>> +
>>> +static efi_status_t efi_bootmenu_volume_selected(void *data, bool 
>>> *exit)
>>> +{
>>> +    struct efi_bootmenu_volume_entry_data *info = data;
>>> +
>>> +    *exit = true;
>>> +
>>> +    if (info) {
>>> +        info->bo->current_volume = info->v;
>>> +        info->bo->dp_volume = info->dp;
>>> +    }
>>> +
>>> +    return EFI_SUCCESS;
>>> +}
>>> +
>>> +static efi_status_t efi_bootmenu_file_selected(void *data, bool *exit)
>>> +{
>>> +    struct efi_bootmenu_file_entry_data *info = data;
>>> +
>>> +    *exit = true;
>>> +
>>> +    if (!info)
>>> +        return EFI_INVALID_PARAMETER;
>>> +
>>> +    if (u16_strncmp(info->file_name, u".", 1) == 0 &&
>>> +        u16_strlen(info->file_name) == 1) {
>>> +        /* stay current path */
>>> +    } else if (u16_strncmp(info->file_name, u"..", 2) == 0 &&
>>> +           u16_strlen(info->file_name) == 2) {

u16_strcmp(info->file_name, u"..") will yield the same result with less 
effort.

>>> +        u32 i;
>>> +        int len = u16_strlen(info->bo->current_path);
>>> +
>>> +        for (i = len - 2; i > 0; i--) {
>>> +            if (info->bo->current_path[i] == u'\\')

"a\b.efi" is a valid file name on ext4. How do we handle it?
Do we ensure that it is not selected?
Or would we just ignore this border case and accept unexpected behavior?

>>> +                break;
>>> +        }
>>> +
>>> +        if (i == 0)
>>> +            info->bo->current_path[0] = u'\0';
>>> +        else
>>> +            info->bo->current_path[i + 1] = u'\0';
>>> +    } else {
>>> +        size_t new_len;
>>> +
>>> +        new_len = u16_strlen(info->bo->current_path) +
>>> +                     u16_strlen(info->file_name) + 1;
>>> +        if (new_len > EFI_BOOTMENU_FILE_PATH_MAX) {

%s/>/>=/ and save the + 1 above.

>>> +            /* TODO: show error notification to user */
>>> +            log_err("file path is too long\n");
>>> +            return EFI_INVALID_PARAMETER;
>>> +        }
>>> +        u16_strlcat(info->bo->current_path, info->file_name,
>>> +                EFI_BOOTMENU_FILE_PATH_MAX);
>>> +        if (info->is_directory) {
>>> +            /*
>>> +             * Remainig buffer should have enough space to contain 
>>> u"\\" and
>>> +             * at least one character for file name
>>> +             */
>>> +            if (new_len + 2 > EFI_BOOTMENU_FILE_PATH_MAX) {

%s/>/>=/

Best regards

Heinrich

>>> +                log_err("directory path is too long\n");
>>> +                return EFI_INVALID_PARAMETER;
>>> +            }
>>> +            u16_strlcat(info->bo->current_path, u"\\",
>>> +                    EFI_BOOTMENU_FILE_PATH_MAX);
>>> +        } else {
>>> +            info->bo->file_selected = true;
>>> +        }
>>> +    }
>>> +    return EFI_SUCCESS;
>>> +}
>>> +
>>> +static efi_status_t efi_bootmenu_select_volume(struct 
>>> efi_bootmenu_boot_option *bo)
>>> +{
>>> +    u32 i;
>>> +    efi_status_t ret;
>>> +    efi_uintn_t count;
>>> +    struct efi_handler *handler;
>>> +    struct efi_device_path *device_path;
>>> +    efi_handle_t *volume_handles = NULL;
>>> +    struct efi_simple_file_system_protocol *v;
>>> +    struct efi_bootmenu_item *menu_item, *iter;
>>> +
>>> +    ret = efi_locate_handle_buffer_int(BY_PROTOCOL, 
>>> &efi_simple_file_system_protocol_guid,
>>> +                       NULL, &count, (efi_handle_t **)&volume_handles);
>>> +    if (ret != EFI_SUCCESS)
>>> +        return ret;
>>> +
>>> +    menu_item = calloc(count + 1, sizeof(struct efi_bootmenu_item));
>>> +    if (!menu_item) {
>>> +        ret = EFI_OUT_OF_RESOURCES;
>>> +        goto out1;
>>> +    }
>>> +
>>> +    iter = menu_item;
>>> +    for (i = 0; i < count; i++) {
>>> +        u16 *dev_name, *p;
>>> +        struct efi_block_io *block_io;
>>> +        char buf[BOOTMENU_DEVICE_NAME_MAX];
>>> +        struct efi_bootmenu_volume_entry_data *info;
>>> +
>>> +        ret = efi_search_protocol(volume_handles[i],
>>> +                      &efi_simple_file_system_protocol_guid, &handler);
>>> +        if (ret != EFI_SUCCESS)
>>> +            continue;
>>> +        ret = efi_protocol_open(handler, (void **)&v, efi_root, NULL,
>>> +                    EFI_OPEN_PROTOCOL_GET_PROTOCOL);
>>> +        if (ret != EFI_SUCCESS)
>>> +            continue;
>>> +
>>> +        ret = efi_search_protocol(volume_handles[i], 
>>> &efi_guid_device_path, &handler);
>>> +        if (ret != EFI_SUCCESS)
>>> +            continue;
>>> +        ret = efi_protocol_open(handler, (void **)&device_path,
>>> +                    efi_root, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
>>> +        if (ret != EFI_SUCCESS)
>>> +            continue;
>>> +
>>> +        ret = efi_search_protocol(volume_handles[i], 
>>> &efi_block_io_guid, &handler);
>>> +        if (ret != EFI_SUCCESS)
>>> +            continue;
>>> +        ret = efi_protocol_open(handler, (void **)&block_io,
>>> +                    efi_root, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
>>> +        if (ret != EFI_SUCCESS)
>>> +            continue;
>>> +
>>> +        info = calloc(1, sizeof(struct 
>>> efi_bootmenu_volume_entry_data));
>>> +        if (!info) {
>>> +            ret = EFI_OUT_OF_RESOURCES;
>>> +            goto out2;
>>> +        }
>>> +
>>> +        efi_disk_get_device_name(block_io, buf, 
>>> BOOTMENU_DEVICE_NAME_MAX);
>>> +        dev_name = calloc(1, (strlen(buf) + 1) * sizeof(u16));
>>> +        if (!dev_name) {
>>> +            free(info);
>>> +            ret = EFI_OUT_OF_RESOURCES;
>>> +            goto out2;
>>> +        }
>>> +        p = dev_name;
>>> +        utf8_utf16_strncpy(&p, buf, strlen(buf));
>>> +
>>> +        info->v = v;
>>> +        info->dp = device_path;
>>> +        info->bo = bo;
>>> +        iter->title = dev_name;
>>> +        iter->func = efi_bootmenu_volume_selected;
>>> +        iter->data = info;
>>> +        iter++;
>>> +    }
>>> +
>>> +    iter->title = u16_strdup(u"Quit");
>>> +    iter->func = NULL;
>>> +    iter->data = NULL;
>>> +    count += 1;
>>> +
>>> +    ret = efi_bootmenu_process_common(menu_item, count, -1);
>>> +
>>> +out2:
>>> +    iter = menu_item;
>>> +    for (i = 0; i < count; i++) {
>>> +        struct efi_bootmenu_volume_entry_data *p;
>>> +
>>> +        p = (struct efi_bootmenu_volume_entry_data *)(iter->data);
>>> +        free(iter->title);
>>> +        free(p);
>>> +        iter++;
>>> +    }
>>> +
>>> +    free(menu_item);
>>> +
>>> +out1:
>>> +    efi_free_pool(volume_handles);
>>> +
>>> +    return ret;
>>> +}
>>> +
>>> +static efi_status_t efi_bootmenu_select_file(struct 
>>> efi_bootmenu_boot_option *bo,
>>> +                         struct efi_file_handle *root)
>>> +{
>>> +    u32 i;
>>> +    struct efi_file_info *buf;
>>> +    u32 count = 0;
>>> +    efi_uintn_t len;
>>> +    efi_status_t ret;
>>> +    struct efi_file_handle *f;
>>> +    struct efi_bootmenu_item *menu_item, *iter;
>>> +
>>> +    buf = calloc(1, sizeof(struct efi_file_info) + 
>>> EFI_BOOTMENU_FILE_PATH_BUF_SIZE);
>>> +    if (!buf)
>>> +        return EFI_OUT_OF_RESOURCES;
>>> +
>>> +    while (!bo->file_selected) {
>>> +        count = 0;
>>> +
>>> +        ret = efi_file_open_int(root, &f, bo->current_path, 
>>> EFI_FILE_MODE_READ, 0);
>>> +        if (ret != EFI_SUCCESS)
>>> +            return ret;
>>> +
>>> +        /* calculate directory information total count */
>>> +        for (;;) {
>>> +            len = sizeof(struct efi_file_info) + 
>>> EFI_BOOTMENU_FILE_PATH_BUF_SIZE;
>>> +            ret = efi_file_read_int(f, &len, buf);
>>> +            if (ret != EFI_SUCCESS || len == 0)
>>> +                break;
>>> +
>>> +            count++;
>>> +        }
>>> +
>>> +        menu_item = calloc(count + 1, sizeof(struct 
>>> efi_bootmenu_item));
>>> +        if (!menu_item) {
>>> +            efi_file_close_int(f);
>>> +            ret = EFI_OUT_OF_RESOURCES;
>>> +            goto out;
>>> +        }
>>> +
>>> +        /* read directory and construct menu structure */
>>> +        efi_file_setpos_int(f, 0);
>>> +        iter = menu_item;
>>> +        for (i = 0; i < count; i++) {
>>> +            u16 *name;
>>> +            int name_len;
>>> +            struct efi_bootmenu_file_entry_data *info;
>>> +
>>> +            len = sizeof(struct efi_file_info) + 
>>> EFI_BOOTMENU_FILE_PATH_BUF_SIZE;
>>> +            ret = efi_file_read_int(f, &len, buf);
>>> +            if (ret != EFI_SUCCESS || len == 0)
>>> +                goto err;
>>> +
>>> +            info = calloc(1, sizeof(struct 
>>> efi_bootmenu_file_entry_data));
>>> +            if (!info) {
>>> +                ret = EFI_OUT_OF_RESOURCES;
>>> +                goto err;
>>> +            }
>>> +
>>> +            if (buf->attribute & EFI_FILE_DIRECTORY) {
>>> +                /* append u'/' at the end of directory name */
>>> +                name_len = u16_strsize(buf->file_name) + sizeof(u16);
>>> +                name = calloc(1, name_len);
>>> +                if (!name) {
>>> +                    ret = EFI_OUT_OF_RESOURCES;
>>> +                    goto err;
>>> +                }
>>> +                u16_strcpy(name, buf->file_name);
>>> +                name[u16_strlen(buf->file_name)] = u'/';
>>> +
>>> +                info->is_directory = true;
>>> +            } else {
>>> +                name_len = u16_strsize(buf->file_name);
>>> +                name = calloc(1, name_len);
>>> +                if (!name) {
>>> +                    ret = EFI_OUT_OF_RESOURCES;
>>> +                    goto err;
>>> +                }
>>> +                u16_strcpy(name, buf->file_name);
>>> +            }
>>> +
>>> +            info->file_name = u16_strdup(buf->file_name);
>>> +            info->bo = bo;
>>> +            iter->title = name;
>>> +            iter->func = efi_bootmenu_file_selected;
>>> +            iter->data = info;
>>> +            iter++;
>>> +        }
>>> +
>>> +        /* add "Quit" entry */
>>> +        iter->title = u"Quit";
>>> +        iter->func = NULL;
>>> +        iter->data = NULL;
>>> +        count += 1;
>>> +
>>> +        ret = efi_bootmenu_process_common(menu_item, count, -1);
>>> +err:
>>> +        efi_file_close_int(f);
>>> +        iter = menu_item;
>>> +        for (i = 0; i < count - 1; i++, iter++) {
>>> +            free(((struct efi_bootmenu_file_entry_data 
>>> *)(iter->data))->file_name);
>>> +            free(iter->title);
>>> +            free(iter->data);
>>> +        }
>>> +
>>> +        free(menu_item);
>>> +
>>> +        if (ret != EFI_SUCCESS)
>>> +            break;
>>> +    }
>>> +
>>> +out:
>>> +    free(buf);
>>> +    return ret;
>>> +}
>>> +
>>> +static efi_status_t efi_bootmenu_boot_add_enter_name(struct 
>>> efi_bootmenu_boot_option *bo)
>>> +{
>>> +    efi_status_t ret;
>>> +
>>> +    printf(ANSI_CURSOR_POSITION, 2, 1);
>>> +    puts("  *** U-Boot EFI Boot Manager Menu ***");
>>> +    printf(ANSI_CURSOR_POSITION, 4, 1);
>>> +    puts("  enter name:");
>>> +
>>> +    printf(ANSI_CURSOR_POSITION, 8, 1);
>>> +    puts("  ENTER to complete, ESC/CTRL+C to quit");
>>> +
>>> +    ret = efi_console_get_u16_string(cin, cout, bo->boot_name,
>>> +                     EFI_BOOTMENU_BOOT_NAME_MAX, NULL, 4, 15);
>>> +
>>> +    return ret;
>>> +}
>>> +
>>> +static efi_status_t efi_bootmenu_select_file_handler(struct 
>>> efi_bootmenu_boot_option *bo)
>>> +{
>>> +    efi_status_t ret;
>>> +    struct efi_file_handle *root;
>>> +
>>> +    bo->file_selected = false;
>>> +
>>> +    while (!bo->file_selected) {
>>> +        bo->current_volume = NULL;
>>> +        memset(bo->current_path, 0, sizeof(bo->current_path));
>>> +
>>> +        ret = efi_bootmenu_select_volume(bo);
>>> +        if (ret != EFI_SUCCESS)
>>> +            return ret;
>>> +
>>> +        if (!bo->current_volume)
>>> +            return EFI_INVALID_PARAMETER;
>>> +
>>> +        ret = efi_open_volume_int(bo->current_volume, &root);
>>> +        if (ret != EFI_SUCCESS)
>>> +            return ret;
>>> +
>>> +        ret = efi_bootmenu_select_file(bo, root);
>>> +        if (ret != EFI_SUCCESS)
>>> +            return ret;
>>> +    }
>>> +
>>> +    ret = efi_bootmenu_boot_add_enter_name(bo);
>>> +
>>> +    return ret;
>>> +}
>>> +
>>> +efi_status_t efi_bootmenu_get_unused_bootoption(u16 *buf,
>>> +                        efi_uintn_t buf_size, u32 *index)
>>> +{
>>> +    u32 i;
>>> +    efi_status_t ret;
>>> +    efi_uintn_t size;
>>> +
>>> +    if (buf_size < u16_strsize(u"Boot####"))
>>> +        return EFI_BUFFER_TOO_SMALL;
>>> +
>>> +    for (i = 0; i <= 0xFFFF; i++) {
>>> +        size = 0;
>>> +        efi_create_indexed_name(buf, buf_size, "Boot", i);
>>> +        ret = efi_get_variable_int(buf, &efi_global_variable_guid,
>>> +                       NULL, &size, NULL, NULL);
>>> +        if (ret == EFI_BUFFER_TOO_SMALL)
>>> +            continue;
>>> +        else
>>> +            break;
>>> +    }
>>> +
>>> +    if (i > 0xFFFF)
>>> +        return EFI_OUT_OF_RESOURCES;
>>> +
>>> +    *index = i;
>>> +
>>> +    return EFI_SUCCESS;
>>> +}
>>> +
>>> +static efi_status_t efi_bootmenu_set_boot_option(u16 *var_name, 
>>> struct efi_device_path *dp,
>>> +                         u16 *label, char *optional_data)
>>> +{
>>> +    void *p = NULL;
>>> +    efi_status_t ret;
>>> +    efi_uintn_t size;
>>> +    struct efi_load_option lo;
>>> +
>>> +    lo.file_path = dp;
>>> +    lo.file_path_length = efi_dp_size(dp) + sizeof(END);
>>> +    lo.attributes = LOAD_OPTION_ACTIVE;
>>> +    lo.optional_data = optional_data;
>>> +    lo.label = label;
>>> +
>>> +    size = efi_serialize_load_option(&lo, (u8 **)&p);
>>> +    if (!size)
>>> +        return EFI_INVALID_PARAMETER;
>>> +
>>> +    ret = efi_set_variable_int(var_name, &efi_global_variable_guid,
>>> +                   EFI_VARIABLE_NON_VOLATILE |
>>> +                   EFI_VARIABLE_BOOTSERVICE_ACCESS |
>>> +                   EFI_VARIABLE_RUNTIME_ACCESS,
>>> +                   size, p, false);
>>> +    free(p);
>>> +
>>> +    return ret;
>>> +}
>>> +
>>> +efi_status_t efi_bootmenu_append_bootorder(u16 index)
>>> +{
>>> +    u16 *bootorder;
>>> +    efi_status_t ret;
>>> +    u16 *new_bootorder = NULL;
>>> +    efi_uintn_t last, size, new_size;
>>> +
>>> +    /* append new boot option */
>>> +    bootorder = efi_get_var(u"BootOrder", &efi_global_variable_guid, 
>>> &size);
>>> +    last = size / sizeof(u16);
>>> +    new_size = size + sizeof(u16);
>>> +    new_bootorder = calloc(1, new_size);
>>> +    if (!new_bootorder) {
>>> +        ret = EFI_OUT_OF_RESOURCES;
>>> +        goto out;
>>> +    }
>>> +    memcpy(new_bootorder, bootorder, size);
>>> +    new_bootorder[last] = index;
>>> +
>>> +    ret = efi_set_variable_int(u"BootOrder", &efi_global_variable_guid,
>>> +                   EFI_VARIABLE_NON_VOLATILE |
>>> +                   EFI_VARIABLE_BOOTSERVICE_ACCESS |
>>> +                   EFI_VARIABLE_RUNTIME_ACCESS,
>>> +                   new_size, new_bootorder, false);
>>> +    if (ret != EFI_SUCCESS)
>>> +        goto out;
>>> +
>>> +out:
>>> +    free(bootorder);
>>> +    free(new_bootorder);
>>> +
>>> +    return ret;
>>> +}
>>> +
>>> +static efi_status_t efi_bootmenu_process_add_boot_option(void *data, 
>>> bool *exit)
>>> +{
>>> +    u32 index;
>>> +    u16 var_name[9];
>>> +    char *buf = NULL;
>>> +    efi_status_t ret;
>>> +    char *iter = NULL;
>>> +    efi_uintn_t dp_size, fp_size;
>>> +    struct efi_bootmenu_boot_option bo;
>>> +    struct efi_device_path_file_path *fp;
>>> +
>>> +    ret = efi_bootmenu_get_unused_bootoption(var_name, 
>>> sizeof(var_name),
>>> +                         &index);
>>> +    if (ret != EFI_SUCCESS)
>>> +        return ret;
>>> +
>>> +    bo.current_path = calloc(1, EFI_BOOTMENU_FILE_PATH_BUF_SIZE);
>>> +    if (!bo.current_path)
>>> +        goto out;
>>> +
>>> +    bo.boot_name = calloc(1, EFI_BOOTMENU_BOOT_NAME_MAX * sizeof(u16));
>>> +    if (!bo.boot_name)
>>> +        goto out;
>>> +
>>> +    ret = efi_bootmenu_select_file_handler(&bo);
>>> +    if (ret != EFI_SUCCESS)
>>> +        goto out;
>>> +
>>> +    dp_size = efi_dp_size(bo.dp_volume);
>>> +    fp_size = sizeof(struct efi_device_path) +
>>> +          ((u16_strlen(bo.current_path) + 1) * sizeof(u16));
>>> +    buf = calloc(1, dp_size + fp_size + sizeof(END));
>>> +    if (!buf)
>>> +        goto out;
>>> +
>>> +    iter = buf;
>>> +    memcpy(iter, bo.dp_volume, dp_size);
>>> +    iter += dp_size;
>>> +
>>> +    fp = (struct efi_device_path_file_path *)iter;
>>> +    fp->dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE;
>>> +    fp->dp.sub_type = DEVICE_PATH_SUB_TYPE_FILE_PATH;
>>> +    fp->dp.length = (u16)fp_size;
>>> +    u16_strcpy(fp->str, bo.current_path);
>>> +    iter += fp_size;
>>> +    *((struct efi_device_path *)iter) = END;
>>> +
>>> +    ret = efi_bootmenu_set_boot_option(var_name, (struct 
>>> efi_device_path *)buf,
>>> +                       bo.boot_name, NULL);
>>> +    if (ret != EFI_SUCCESS)
>>> +        goto out;
>>> +
>>> +    efi_bootmenu_append_bootorder((u16)index);
>>> +    if (ret != EFI_SUCCESS)
>>> +        goto out;
>>> +
>>> +out:
>>> +    free(buf);
>>> +    free(bo.boot_name);
>>> +    free(bo.current_path);
>>> +
>>> +    return ret;
>>> +}
>>> +
>>> +static efi_status_t efi_bootmenu_init(void)
>>> +{
>>> +    efi_status_t ret;
>>> +    struct efi_handler *handler;
>>> +
>>> +    ret = efi_search_protocol(efi_root, 
>>> &efi_guid_text_input_protocol, &handler);
>>> +    if (ret != EFI_SUCCESS)
>>> +        return ret;
>>> +
>>> +    ret = efi_protocol_open(handler, (void **)&cin, efi_root, NULL,
>>> +                EFI_OPEN_PROTOCOL_GET_PROTOCOL);
>>> +    if (ret != EFI_SUCCESS)
>>> +        return ret;
>>> +
>>> +    ret = efi_search_protocol(efi_root, 
>>> &efi_guid_text_output_protocol, &handler);
>>> +    if (ret != EFI_SUCCESS)
>>> +        return ret;
>>> +
>>> +    ret = efi_protocol_open(handler, (void **)&cout, efi_root, NULL,
>>> +                EFI_OPEN_PROTOCOL_GET_PROTOCOL);
>>> +    if (ret != EFI_SUCCESS)
>>> +        return ret;
>>> +
>>> +    return ret;
>>> +}
>>> +
>>> +static const struct efi_bootmenu_item maintenance_menu_items[] = {
>>> +    {u"Add Boot Option", efi_bootmenu_process_add_boot_option},
>>> +    {u"Quit", NULL},
>>> +};
>>> +
>>> +efi_status_t efi_bootmenu_show_maintenance_menu(void)
>>> +{
>>> +    efi_status_t ret;
>>> +
>>> +    ret = efi_bootmenu_init();
>>> +    if (ret != EFI_SUCCESS)
>>> +        return ret;
>>> +
>>> +    return efi_bootmenu_process_common(maintenance_menu_items,
>>> +                      ARRAY_SIZE(maintenance_menu_items),
>>> +                      -1);
>>> +}
>>> diff --git a/lib/efi_loader/efi_boottime.c 
>>> b/lib/efi_loader/efi_boottime.c
>>> index 4da64b5d29..1233418e77 100644
>>> --- a/lib/efi_loader/efi_boottime.c
>>> +++ b/lib/efi_loader/efi_boottime.c
>>> @@ -2453,6 +2453,35 @@ static efi_status_t EFIAPI 
>>> efi_protocols_per_handle(
>>>       return EFI_EXIT(EFI_SUCCESS);
>>>   }
>>>
>>> +efi_status_t efi_locate_handle_buffer_int(enum 
>>> efi_locate_search_type search_type,
>>> +                      const efi_guid_t *protocol, void *search_key,
>>> +                      efi_uintn_t *no_handles, efi_handle_t **buffer)
>>> +{
>>> +    efi_status_t r;
>>> +    efi_uintn_t buffer_size = 0;
>>> +
>>> +    if (!no_handles || !buffer) {
>>> +        r = EFI_INVALID_PARAMETER;
>>> +        goto out;
>>> +    }
>>> +    *no_handles = 0;
>>> +    *buffer = NULL;
>>> +    r = efi_locate_handle(search_type, protocol, search_key, 
>>> &buffer_size,
>>> +                  *buffer);
>>> +    if (r != EFI_BUFFER_TOO_SMALL)
>>> +        goto out;
>>> +    r = efi_allocate_pool(EFI_BOOT_SERVICES_DATA, buffer_size,
>>> +                  (void **)buffer);
>>> +    if (r != EFI_SUCCESS)
>>> +        goto out;
>>> +    r = efi_locate_handle(search_type, protocol, search_key, 
>>> &buffer_size,
>>> +                  *buffer);
>>> +    if (r == EFI_SUCCESS)
>>> +        *no_handles = buffer_size / sizeof(efi_handle_t);
>>> +out:
>>> +    return r;
>>> +}
>>> +
>>>   /**
>>>    * efi_locate_handle_buffer() - locate handles implementing a protocol
>>>    * @search_type: selection criterion
>>> @@ -2474,30 +2503,13 @@ efi_status_t EFIAPI efi_locate_handle_buffer(
>>>               efi_uintn_t *no_handles, efi_handle_t **buffer)
>>>   {
>>>       efi_status_t r;
>>> -    efi_uintn_t buffer_size = 0;
>>>
>>>       EFI_ENTRY("%d, %pUs, %p, %p, %p", search_type, protocol, 
>>> search_key,
>>>             no_handles, buffer);
>>>
>>> -    if (!no_handles || !buffer) {
>>> -        r = EFI_INVALID_PARAMETER;
>>> -        goto out;
>>> -    }
>>> -    *no_handles = 0;
>>> -    *buffer = NULL;
>>> -    r = efi_locate_handle(search_type, protocol, search_key, 
>>> &buffer_size,
>>> -                  *buffer);
>>> -    if (r != EFI_BUFFER_TOO_SMALL)
>>> -        goto out;
>>> -    r = efi_allocate_pool(EFI_BOOT_SERVICES_DATA, buffer_size,
>>> -                  (void **)buffer);
>>> -    if (r != EFI_SUCCESS)
>>> -        goto out;
>>> -    r = efi_locate_handle(search_type, protocol, search_key, 
>>> &buffer_size,
>>> -                  *buffer);
>>> -    if (r == EFI_SUCCESS)
>>> -        *no_handles = buffer_size / sizeof(efi_handle_t);
>>> -out:
>>> +    r = efi_locate_handle_buffer_int(search_type, protocol, search_key,
>>> +                     no_handles, buffer);
>>> +
>>>       return EFI_EXIT(r);
>>>   }
>>>
>>> diff --git a/lib/efi_loader/efi_console.c b/lib/efi_loader/efi_console.c
>>> index ba68a15017..f5002e1c99 100644
>>> --- a/lib/efi_loader/efi_console.c
>>> +++ b/lib/efi_loader/efi_console.c
>>> @@ -5,6 +5,7 @@
>>>    *  Copyright (c) 2016 Alexander Graf
>>>    */
>>>
>>> +#include <ansi.h>
>>>   #include <common.h>
>>>   #include <charset.h>
>>>   #include <malloc.h>
>>> @@ -1312,3 +1313,83 @@ out_of_memory:
>>>       printf("ERROR: Out of memory\n");
>>>       return r;
>>>   }
>>> +
>>> +/**
>>> + * efi_console_get_u16_string() - get user input string
>>> + *
>>> + * @cin:        protocol interface to EFI_SIMPLE_TEXT_INPUT_PROTOCOL
>>> + * @cout:        protocol interface to EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
>>> + * @buf:        buffer to store user input string in UTF16
>>> + * @size:        buffer size including NULL terminator
>>> + * @filter_func:    callback to filter user input
>>> + * @row:        row number to locate user input form
>>> + * @col:        column number to locate user input form
>>> + * Return:        status code
>>> + */
>>> +efi_status_t efi_console_get_u16_string(struct 
>>> efi_simple_text_input_protocol *cin,
>>> +                    struct efi_simple_text_output_protocol *cout,
>>> +                    u16 *buf, efi_uintn_t size,
>>> +                    efi_console_filter_func filter_func,
>>> +                    int row, int col)
>>> +{
>>> +    efi_status_t ret;
>>> +    efi_uintn_t len = 0;
>>> +    struct efi_input_key key;
>>> +
>>> +    printf(ANSI_CURSOR_POSITION, row, col);
>>> +    puts(ANSI_CLEAR_LINE_TO_END);
>>> +    puts(ANSI_CURSOR_SHOW);
>>> +
>>> +    ret = EFI_CALL(cin->reset(cin, false));
>>> +    if (ret != EFI_SUCCESS)
>>> +        return ret;
>>> +
>>> +    for (;;) {
>>> +        do {
>>> +            ret = EFI_CALL(cin->read_key_stroke(cin, &key));
>>> +            mdelay(10);
>>> +        } while (ret == EFI_NOT_READY);
>>> +
>>> +        if (key.unicode_char == u'\b') {
>>> +            if (len > 0)
>>> +                buf[--len] = u'\0';
>>> +
>>> +            printf(ANSI_CURSOR_POSITION, row, col);
>>> +            ret = EFI_CALL(cout->output_string(cout, buf));
>>> +            if (ret != EFI_SUCCESS)
>>> +                return ret;
>>> +
>>> +            puts(ANSI_CLEAR_LINE_TO_END);
>>> +            continue;
>>> +        } else if (key.unicode_char == u'\r') {
>>> +            if (len == 0) /* no user input */
>>> +                continue;
>>> +
>>> +            buf[len] = u'\0';
>>> +            return EFI_SUCCESS;
>>> +        } else if (key.unicode_char == 0x3 || key.scan_code == 23) {
>>> +            return EFI_ABORTED;
>>> +        } else if (key.unicode_char < 0x20) {
>>> +            /* ignore control codes other than Ctrl+C, '\r' and '\b' */
>>> +            continue;
>>> +        } else if (key.scan_code != 0) {
>>> +            /* only accept single ESC press for cancel */
>>> +            continue;
>>> +        }
>>> +
>>> +        if (filter_func) {
>>> +            if (filter_func(&key) != EFI_SUCCESS)
>>> +                continue;
>>> +        }
>>> +
>>> +        if (len >= (size - 1))
>>> +            continue;
>>> +
>>> +        buf[len] = key.unicode_char;
>>> +        len++;
>>> +        printf(ANSI_CURSOR_POSITION, row, col);
>>> +        ret = EFI_CALL(cout->output_string(cout, buf));
>>> +        if (ret != EFI_SUCCESS)
>>> +            return ret;
>>> +    }
>>> +}
>>> diff --git a/lib/efi_loader/efi_disk.c b/lib/efi_loader/efi_disk.c
>>> index 8fb5b2363c..58736a8a5b 100644
>>> --- a/lib/efi_loader/efi_disk.c
>>> +++ b/lib/efi_loader/efi_disk.c
>>> @@ -750,3 +750,14 @@ efi_status_t efi_disk_init(void)
>>>
>>>       return EFI_SUCCESS;
>>>   }
>>> +
>>> +efi_status_t efi_disk_get_device_name(struct efi_block_io *this, 
>>> char *buf, int size)
>>> +{
>>> +    struct efi_disk_obj *diskobj;
>>> +
>>> +    diskobj = container_of(this, struct efi_disk_obj, ops);
>>> +
>>> +    snprintf(buf, size, "%s%d:%d", diskobj->ifname, 
>>> diskobj->dev_index, diskobj->part);
>>
>> A space would improve readability and better match U-Boot syntax.
>> %s/%s%d:%d/%s %d:%d/
>>
>> I guess for MMC we are only supporting booting form the user partition.
>> Otherwise the information would be incomplete.
>>
>> Best regards
>>
>> Heinrich
>>
>>> +
>>> +    return EFI_SUCCESS;
>>> +}
>>> diff --git a/lib/efi_loader/efi_file.c b/lib/efi_loader/efi_file.c
>>> index 7a7077e6d0..c96a7f7ca3 100644
>>> --- a/lib/efi_loader/efi_file.c
>>> +++ b/lib/efi_loader/efi_file.c
>>> @@ -246,10 +246,10 @@ error:
>>>       return NULL;
>>>   }
>>>
>>> -static efi_status_t efi_file_open_int(struct efi_file_handle *this,
>>> -                      struct efi_file_handle **new_handle,
>>> -                      u16 *file_name, u64 open_mode,
>>> -                      u64 attributes)
>>> +efi_status_t efi_file_open_int(struct efi_file_handle *this,
>>> +                   struct efi_file_handle **new_handle,
>>> +                   u16 *file_name, u64 open_mode,
>>> +                   u64 attributes)
>>>   {
>>>       struct file_handle *fh = to_fh(this);
>>>       efi_status_t ret;
>>> @@ -369,11 +369,17 @@ static efi_status_t file_close(struct 
>>> file_handle *fh)
>>>       return EFI_SUCCESS;
>>>   }
>>>
>>> -static efi_status_t EFIAPI efi_file_close(struct efi_file_handle *file)
>>> +efi_status_t efi_file_close_int(struct efi_file_handle *file)
>>>   {
>>>       struct file_handle *fh = to_fh(file);
>>> +
>>> +    return file_close(fh);
>>> +}
>>> +
>>> +static efi_status_t EFIAPI efi_file_close(struct efi_file_handle *file)
>>> +{
>>>       EFI_ENTRY("%p", file);
>>> -    return EFI_EXIT(file_close(fh));
>>> +    return EFI_EXIT(efi_file_close_int(file));
>>>   }
>>>
>>>   static efi_status_t EFIAPI efi_file_delete(struct efi_file_handle 
>>> *file)
>>> @@ -562,8 +568,8 @@ static efi_status_t dir_read(struct file_handle 
>>> *fh, u64 *buffer_size,
>>>       return EFI_SUCCESS;
>>>   }
>>>
>>> -static efi_status_t efi_file_read_int(struct efi_file_handle *this,
>>> -                      efi_uintn_t *buffer_size, void *buffer)
>>> +efi_status_t efi_file_read_int(struct efi_file_handle *this,
>>> +                   efi_uintn_t *buffer_size, void *buffer)
>>>   {
>>>       struct file_handle *fh = to_fh(this);
>>>       efi_status_t ret = EFI_SUCCESS;
>>> @@ -773,24 +779,11 @@ out:
>>>       return EFI_EXIT(ret);
>>>   }
>>>
>>> -/**
>>> - * efi_file_setpos() - set current position in file
>>> - *
>>> - * This function implements the SetPosition service of the EFI file 
>>> protocol.
>>> - * See the UEFI spec for details.
>>> - *
>>> - * @file:    file handle
>>> - * @pos:    new file position
>>> - * Return:    status code
>>> - */
>>> -static efi_status_t EFIAPI efi_file_setpos(struct efi_file_handle 
>>> *file,
>>> -                       u64 pos)
>>> +efi_status_t efi_file_setpos_int(struct efi_file_handle *file, u64 pos)
>>>   {
>>>       struct file_handle *fh = to_fh(file);
>>>       efi_status_t ret = EFI_SUCCESS;
>>>
>>> -    EFI_ENTRY("%p, %llu", file, pos);
>>> -
>>>       if (fh->isdir) {
>>>           if (pos != 0) {
>>>               ret = EFI_UNSUPPORTED;
>>> @@ -812,6 +805,28 @@ static efi_status_t EFIAPI 
>>> efi_file_setpos(struct efi_file_handle *file,
>>>       fh->offset = pos;
>>>
>>>   error:
>>> +    return ret;
>>> +}
>>> +
>>> +/**
>>> + * efi_file_setpos() - set current position in file
>>> + *
>>> + * This function implements the SetPosition service of the EFI file 
>>> protocol.
>>> + * See the UEFI spec for details.
>>> + *
>>> + * @file:    file handle
>>> + * @pos:    new file position
>>> + * Return:    status code
>>> + */
>>> +static efi_status_t EFIAPI efi_file_setpos(struct efi_file_handle 
>>> *file,
>>> +                       u64 pos)
>>> +{
>>> +    efi_status_t ret = EFI_SUCCESS;
>>> +
>>> +    EFI_ENTRY("%p, %llu", file, pos);
>>> +
>>> +    ret = efi_file_setpos_int(file, pos);
>>> +
>>>       return EFI_EXIT(ret);
>>>   }
>>>
>>> @@ -1138,17 +1153,23 @@ struct efi_file_handle 
>>> *efi_file_from_path(struct efi_device_path *fp)
>>>       return f;
>>>   }
>>>
>>> +efi_status_t efi_open_volume_int(struct 
>>> efi_simple_file_system_protocol *this,
>>> +                 struct efi_file_handle **root)
>>> +{
>>> +    struct file_system *fs = to_fs(this);
>>> +
>>> +    *root = file_open(fs, NULL, NULL, 0, 0);
>>> +
>>> +    return EFI_SUCCESS;
>>> +}
>>> +
>>>   static efi_status_t EFIAPI
>>>   efi_open_volume(struct efi_simple_file_system_protocol *this,
>>>           struct efi_file_handle **root)
>>>   {
>>> -    struct file_system *fs = to_fs(this);
>>> -
>>>       EFI_ENTRY("%p, %p", this, root);
>>>
>>> -    *root = file_open(fs, NULL, NULL, 0, 0);
>>> -
>>> -    return EFI_EXIT(EFI_SUCCESS);
>>> +    return EFI_EXIT(efi_open_volume_int(this, root));
>>>   }
>>>
>>>   struct efi_simple_file_system_protocol *
>>
> 


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

* Re: [PATCH v5 03/17] menu: always show the menu regardless of the number of entry
  2022-04-28  8:09 ` [PATCH v5 03/17] menu: always show the menu regardless of the number of entry Masahisa Kojima
@ 2022-05-01 15:37   ` Heinrich Schuchardt
  0 siblings, 0 replies; 58+ messages in thread
From: Heinrich Schuchardt @ 2022-05-01 15:37 UTC (permalink / raw)
  To: Masahisa Kojima, u-boot
  Cc: Ilias Apalodimas, Simon Glass, Takahiro Akashi, Francois Ozog,
	Mark Kettenis

On 4/28/22 10:09, Masahisa Kojima wrote:
> To make user aware of the menu entry selection, menu always
> appears regardless of the number of entry.
>
> Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>

Reviewed-by: Heinrich Schuchardt <xypron.glpk@gmx.de>

> ---
> No changes since v4
>
> Newly created in v4
>
>   common/menu.c | 2 +-
>   1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/common/menu.c b/common/menu.c
> index 5fb2ffbd06..b577d80b4f 100644
> --- a/common/menu.c
> +++ b/common/menu.c
> @@ -271,7 +271,7 @@ int menu_get_choice(struct menu *m, void **choice)
>   	if (!m || !choice)
>   		return -EINVAL;
>
> -	if (!m->prompt || m->item_cnt == 1)
> +	if (!m->prompt)
>   		return menu_default_choice(m, choice);
>
>   	return menu_interactive_choice(m, choice);


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

* Re: [PATCH v5 05/17] efi_loader: export efi_locate_device_handle()
  2022-04-28  8:09 ` [PATCH v5 05/17] efi_loader: export efi_locate_device_handle() Masahisa Kojima
@ 2022-05-01 18:53   ` Heinrich Schuchardt
  2022-05-04  9:17     ` Ilias Apalodimas
  0 siblings, 1 reply; 58+ messages in thread
From: Heinrich Schuchardt @ 2022-05-01 18:53 UTC (permalink / raw)
  To: Masahisa Kojima, u-boot
  Cc: Ilias Apalodimas, Simon Glass, Takahiro Akashi, Francois Ozog,
	Mark Kettenis

On 4/28/22 10:09, Masahisa Kojima wrote:
> From: AKASHI Takahiro <takahiro.akashi@linaro.org>
>
> This function will be used in the next commit where some behavior
> of EFI boot manager will be expanded.
>
> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> Reviewed-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>

Reviewed-by: Heinrich Schuchardt <xypron.glpk@gmx.de>

> ---
> No changes from original version
>
>   include/efi_loader.h          | 4 ++++
>   lib/efi_loader/efi_boottime.c | 7 +++----
>   2 files changed, 7 insertions(+), 4 deletions(-)
>
> diff --git a/include/efi_loader.h b/include/efi_loader.h
> index ba79a9afb4..effb43369d 100644
> --- a/include/efi_loader.h
> +++ b/include/efi_loader.h
> @@ -595,6 +595,10 @@ efi_status_t efi_create_handle(efi_handle_t *handle);
>   void efi_delete_handle(efi_handle_t obj);
>   /* Call this to validate a handle and find the EFI object for it */
>   struct efi_object *efi_search_obj(const efi_handle_t handle);
> +/* Locate device_path handle */
> +efi_status_t EFIAPI efi_locate_device_path(const efi_guid_t *protocol,
> +					   struct efi_device_path **device_path,
> +					   efi_handle_t *device);
>   /* Load image */
>   efi_status_t EFIAPI efi_load_image(bool boot_policy,
>   				   efi_handle_t parent_image,
> diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c
> index 5bcb8253ed..4da64b5d29 100644
> --- a/lib/efi_loader/efi_boottime.c
> +++ b/lib/efi_loader/efi_boottime.c
> @@ -1799,10 +1799,9 @@ failure:
>    *
>    * Return: status code
>    */
> -static efi_status_t EFIAPI efi_locate_device_path(
> -			const efi_guid_t *protocol,
> -			struct efi_device_path **device_path,
> -			efi_handle_t *device)
> +efi_status_t EFIAPI efi_locate_device_path(const efi_guid_t *protocol,
> +					   struct efi_device_path **device_path,
> +					   efi_handle_t *device)
>   {
>   	struct efi_device_path *dp;
>   	size_t i;


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

* Re: [PATCH v5 08/17] bootmenu: update bootmenu_entry structure
  2022-04-29 19:51   ` Heinrich Schuchardt
@ 2022-05-01 20:54     ` Heinrich Schuchardt
  2022-05-09  8:54       ` Masahisa Kojima
  0 siblings, 1 reply; 58+ messages in thread
From: Heinrich Schuchardt @ 2022-05-01 20:54 UTC (permalink / raw)
  To: Masahisa Kojima, u-boot
  Cc: Ilias Apalodimas, Simon Glass, Takahiro Akashi, Francois Ozog,
	Mark Kettenis

On 4/29/22 21:51, Heinrich Schuchardt wrote:
> On 4/28/22 10:09, Masahisa Kojima wrote:
>> This is a preparation for succeeding addition of uefi boot
>> and distro boot menu entries into bootmenu.
>> The bootmenu_entry title is updated to u16 string because
>> uefi use u16 string. This commit also factors out the function
>> to prepare the entries generated by "bootmenu_x" U-Boot environment
>> variable.
>>
>> This commit also updates the bootmenu entry title.
>> The entry derived from "bootmenu_x" U-Boot environment variable
>> has the "bootmenu_xx" prefix as below.
>>
>>    *** U-Boot Boot Menu ***
>>
>>       bootmenu_00   : Boot 1. kernel
>>       bootmenu_01   : Boot 2. kernel
>>       bootmenu_02   : Reset board
>>
>> Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
>> ---
>> Changes in v5:
>> - split into the separate patch
>> - add function description comment
>>
>>   cmd/bootmenu.c | 110 +++++++++++++++++++++++++++++++++++--------------
>>   1 file changed, 78 insertions(+), 32 deletions(-)
>>
>> diff --git a/cmd/bootmenu.c b/cmd/bootmenu.c
>> index 9a32a18b19..15ad621c9f 100644
>> --- a/cmd/bootmenu.c
>> +++ b/cmd/bootmenu.c
>> @@ -3,6 +3,7 @@
>>    * (C) Copyright 2011-2013 Pali Rohár <pali@kernel.org>
>>    */
>>
>> +#include <charset.h>
>>   #include <common.h>
>>   #include <command.h>
>>   #include <ansi.h>
>> @@ -24,11 +25,18 @@
>>    */
>>   #define MAX_ENV_SIZE    (9 + 2 + 1)
>>
>> +enum boot_type {
>> +    BOOTMENU_TYPE_NONE = 0,
>> +    BOOTMENU_TYPE_BOOTMENU,
>> +};
>> +
>>   struct bootmenu_entry {
>>       unsigned short int num;        /* unique number 0 .. MAX_COUNT */
>>       char key[3];            /* key identifier of number */
>> -    char *title;            /* title of entry */
>> +    u16 *title;            /* title of entry */
>>       char *command;            /* hush command of entry */
>> +    enum boot_type type;        /* boot type of entry */
>> +    u16 bootorder;            /* order for each boot type */
>>       struct bootmenu_data *menu;    /* this bootmenu */
>>       struct bootmenu_entry *next;    /* next menu entry (num+1) */
>>   };
>> @@ -75,7 +83,10 @@ static void bootmenu_print_entry(void *data)
>>       if (reverse)
>>           puts(ANSI_COLOR_REVERSE);
>>
>> -    puts(entry->title);
>> +    if (entry->type == BOOTMENU_TYPE_BOOTMENU)
>> +        printf("bootmenu_%02d   : %ls", entry->bootorder, entry->title);

This "bootmen_%02d" strings conveys not information of interest. Please 
remove this hunk.

>> +    else
>> +        printf("%ls", entry->title);
>>
>>       if (reverse)
>>           puts(ANSI_COLOR_RESET);
>> @@ -279,31 +290,32 @@ static void bootmenu_destroy(struct 
>> bootmenu_data *menu)
>>       free(menu);
>>   }
>>
>> -static struct bootmenu_data *bootmenu_create(int delay)
>> +/**
>> + * prepare_bootmenu_entry() - generate the bootmenu_xx entries
>> + *
>> + * This function read the "bootmenu_x" U-Boot environment variable
>> + * and generate the bootmenu entries.
>> + *
>> + * @menu:    pointer to the bootmenu structure
>> + * @current:    pointer to the last bootmenu entry list
>> + * @index:    pointer to the index of the last bootmenu entry,
>> + *        the number of bootmenu entry is added by this function
>> + * Return:    1 on success, negative value on error
>> + */
>> +static int prepare_bootmenu_entry(struct bootmenu_data *menu,
>> +                  struct bootmenu_entry **current,
>> +                  unsigned short int *index)
>>   {
>> -    unsigned short int i = 0;
>> -    const char *option;
>> -    struct bootmenu_data *menu;
>> -    struct bootmenu_entry *iter = NULL;
>> -
>>       int len;
>>       char *sep;
>> -    char *default_str;
>> -    struct bootmenu_entry *entry;
>> -
>> -    menu = malloc(sizeof(struct bootmenu_data));
>> -    if (!menu)
>> -        return NULL;
>> -
>> -    menu->delay = delay;
>> -    menu->active = 0;
>> -    menu->first = NULL;
>> -
>> -    default_str = env_get("bootmenu_default");
>> -    if (default_str)
>> -        menu->active = (int)simple_strtol(default_str, NULL, 10);
>> +    const char *option;
>> +    unsigned short int i = *index;
>> +    struct bootmenu_entry *entry = NULL;
>> +    struct bootmenu_entry *iter = *current;
>>
>>       while ((option = bootmenu_getoption(i))) {
>> +        u16 *buf;
>> +
>>           sep = strchr(option, '=');
>>           if (!sep) {
>>               printf("Invalid bootmenu entry: %s\n", option);
>> @@ -312,23 +324,23 @@ static struct bootmenu_data *bootmenu_create(int 
>> delay)
>>
>>           entry = malloc(sizeof(struct bootmenu_entry));
>>           if (!entry)
>> -            goto cleanup;
>> +            return -ENOMEM;
>>
>>           len = sep-option;
>> -        entry->title = malloc(len + 1);
>> +        buf = calloc(1, (len + 1) * sizeof(u16));
>> +        entry->title = buf;
>>           if (!entry->title) {
>>               free(entry);
>> -            goto cleanup;
>> +            return -ENOMEM;
>>           }
>> -        memcpy(entry->title, option, len);
>> -        entry->title[len] = 0;
>> +        utf8_utf16_strncpy(&buf, option, len);
>>
>>           len = strlen(sep + 1);
>>           entry->command = malloc(len + 1);
>>           if (!entry->command) {
>>               free(entry->title);
>>               free(entry);
>> -            goto cleanup;
>> +            return -ENOMEM;
>>           }
>>           memcpy(entry->command, sep + 1, len);
>>           entry->command[len] = 0;
>> @@ -337,6 +349,8 @@ static struct bootmenu_data *bootmenu_create(int 
>> delay)
>>
>>           entry->num = i;
>>           entry->menu = menu;
>> +        entry->type = BOOTMENU_TYPE_BOOTMENU;
>> +        entry->bootorder = i;
>>           entry->next = NULL;
>>
>>           if (!iter)
>> @@ -351,13 +365,44 @@ static struct bootmenu_data *bootmenu_create(int 
>> delay)
>>               break;
>>       }
>>
>> +    *index = i;
>> +    *current = iter;
>> +
>> +    return 1;
>> +}
>> +
>> +static struct bootmenu_data *bootmenu_create(int delay)
>> +{
>> +    int ret;
>> +    unsigned short int i = 0;
>> +    struct bootmenu_data *menu;
>> +    struct bootmenu_entry *iter = NULL;
>> +    struct bootmenu_entry *entry;
>> +    char *default_str;
>> +
>> +    menu = malloc(sizeof(struct bootmenu_data));
>> +    if (!menu)
>> +        return NULL;
>> +
>> +    menu->delay = delay;
>> +    menu->active = 0;
>> +    menu->first = NULL;
>> +
>> +    default_str = env_get("bootmenu_default");
>> +    if (default_str)
>> +        menu->active = (int)simple_strtol(default_str, NULL, 10);
>> +
>> +    ret = prepare_bootmenu_entry(menu, &iter, &i);
>> +    if (ret < 0)
>> +        goto cleanup;
>> +
>>       /* Add U-Boot console entry at the end */
>>       if (i <= MAX_COUNT - 1) {
>>           entry = malloc(sizeof(struct bootmenu_entry));
>>           if (!entry)
>>               goto cleanup;
>>
>> -        entry->title = strdup("U-Boot console");
>> +        entry->title = u16_strdup(u"U-Boot console");
>>           if (!entry->title) {
>>               free(entry);
>>               goto cleanup;
>> @@ -374,6 +419,7 @@ static struct bootmenu_data *bootmenu_create(int 
>> delay)
>>
>>           entry->num = i;
>>           entry->menu = menu;
>> +        entry->type = BOOTMENU_TYPE_NONE;
>>           entry->next = NULL;
>>
>>           if (!iter)
>> @@ -431,7 +477,7 @@ static void bootmenu_show(int delay)
>>   {
>>       int init = 0;
>>       void *choice = NULL;
>> -    char *title = NULL;
>> +    u16 *title = NULL;
>>       char *command = NULL;
>>       struct menu *menu;
>>       struct bootmenu_data *bootmenu;
>> @@ -482,7 +528,7 @@ static void bootmenu_show(int delay)
>>
>>       if (menu_get_choice(menu, &choice) == 1) {
>>           iter = choice;
>> -        title = strdup(iter->title);
>> +        title = u16_strdup(iter->title);
> 
> We must still be able to use the bootmenu command outside of the UEFI
> sub-system. There wide strings will not be used.
> 
> We need a testcase for this.

I have already provided a patch with this.

Best regards

Heinrich

> 
> Best regards
> 
> Heinrich
> 
>>           command = strdup(iter->command);
>>       }
>>
>> @@ -497,7 +543,7 @@ cleanup:
>>       }
>>
>>       if (title && command) {
>> -        debug("Starting entry '%s'\n", title);
>> +        debug("Starting entry '%ls'\n", title);
>>           free(title);
>>           run_command(command, 0);
>>           free(command);
> 


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

* Re: [PATCH v5 09/17] bootmenu: add UEFI boot entry into bootmenu
  2022-04-28  8:09 ` [PATCH v5 09/17] bootmenu: add UEFI boot entry into bootmenu Masahisa Kojima
@ 2022-05-01 21:44   ` Heinrich Schuchardt
  2022-05-09  8:59     ` Masahisa Kojima
  0 siblings, 1 reply; 58+ messages in thread
From: Heinrich Schuchardt @ 2022-05-01 21:44 UTC (permalink / raw)
  To: Masahisa Kojima
  Cc: Ilias Apalodimas, Simon Glass, Takahiro Akashi, Francois Ozog,
	Mark Kettenis, u-boot

On 4/28/22 10:09, Masahisa Kojima wrote:
> This commit adds the UEFI related menu entries
> into the bootmenu.
>
> User can select which UEFI "Boot####" option to execute
> from bootmenu, then bootmenu sets the "BootNext" UEFI
> variable and invoke efi bootmgr. The efi bootmgr
> will handle the "BootNext" UEFI variable.
>
> If the "BootNext" UEFI variable is preset and efi bootmgr is enabled,
> bootmenu invokes efi bootmgr to handle "BootNext" as first priority.
>
> The UEFI boot entry has the "UEFI BOOTXXXX" prefix as below.

This prefix provides no value.

>
>    *** U-Boot Boot Menu ***
>
>       UEFI BOOT0000 : debian
>       UEFI BOOT0001 : ubuntu
>
> Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
> ---
> Changes in v5:
> - split into the separate patch
> - add function description comment
> - remove non-volatile attribute for BootNext variable to minimize
>    the access to the non-volatile storage
>
>   cmd/bootmenu.c | 155 ++++++++++++++++++++++++++++++++++++++++++++++++-
>   1 file changed, 154 insertions(+), 1 deletion(-)
>
> diff --git a/cmd/bootmenu.c b/cmd/bootmenu.c
> index 15ad621c9f..da688e6213 100644
> --- a/cmd/bootmenu.c
> +++ b/cmd/bootmenu.c
> @@ -7,6 +7,8 @@
>   #include <common.h>
>   #include <command.h>
>   #include <ansi.h>
> +#include <efi_loader.h>
> +#include <efi_variable.h>
>   #include <env.h>
>   #include <log.h>
>   #include <menu.h>
> @@ -28,6 +30,7 @@
>   enum boot_type {
>   	BOOTMENU_TYPE_NONE = 0,
>   	BOOTMENU_TYPE_BOOTMENU,
> +	BOOTMENU_TYPE_UEFI_BOOT_OPTION,
>   };
>
>   struct bootmenu_entry {
> @@ -85,6 +88,8 @@ static void bootmenu_print_entry(void *data)
>
>   	if (entry->type == BOOTMENU_TYPE_BOOTMENU)
>   		printf("bootmenu_%02d   : %ls", entry->bootorder, entry->title);
> +	else if (entry->type == BOOTMENU_TYPE_UEFI_BOOT_OPTION)
> +		printf("UEFI BOOT%04X : %ls", entry->bootorder, entry->title);

Please, remove this hunk.

Best regards

Heinrich

>   	else
>   		printf("%ls", entry->title);
>
> @@ -371,6 +376,95 @@ static int prepare_bootmenu_entry(struct bootmenu_data *menu,
>   	return 1;
>   }
>
> +/**
> + * prepare_uefi_bootorder_entry() - generate the uefi bootmenu entries
> + *
> + * This function read the "BootOrder" UEFI variable
> + * and generate the bootmenu entries in the order of "BootOrder".
> + *
> + * @menu:	pointer to the bootmenu structure
> + * @current:	pointer to the last bootmenu entry list
> + * @index:	pointer to the index of the last bootmenu entry,
> + *		the number of uefi entry is added by this function
> + * Return:	1 on success, negative value on error
> + */
> +static int prepare_uefi_bootorder_entry(struct bootmenu_data *menu,
> +					struct bootmenu_entry **current,
> +					unsigned short int *index)
> +{
> +	u16 *bootorder;
> +	efi_status_t ret;
> +	unsigned short j;
> +	efi_uintn_t num, size;
> +	void *load_option;
> +	struct efi_load_option lo;
> +	u16 varname[] = u"Boot####";
> +	unsigned short int i = *index;
> +	struct bootmenu_entry *entry = NULL;
> +	struct bootmenu_entry *iter = *current;
> +
> +	bootorder = efi_get_var(u"BootOrder", &efi_global_variable_guid, &size);
> +	if (!bootorder)
> +		return -ENOENT;
> +
> +	num = size / sizeof(u16);
> +	for (j = 0; j < num; j++) {
> +		entry = malloc(sizeof(struct bootmenu_entry));
> +		if (!entry)
> +			return -ENOMEM;
> +
> +		efi_create_indexed_name(varname, sizeof(varname),
> +					"Boot", bootorder[j]);
> +		load_option = efi_get_var(varname, &efi_global_variable_guid, &size);
> +		if (!load_option)
> +			continue;
> +
> +		ret = efi_deserialize_load_option(&lo, load_option, &size);
> +		if (ret != EFI_SUCCESS) {
> +			log_warning("Invalid load option for %ls\n", varname);
> +			free(load_option);
> +			free(entry);
> +			continue;
> +		}
> +
> +		if (lo.attributes & LOAD_OPTION_ACTIVE) {
> +			entry->title = u16_strdup(lo.label);
> +			if (!entry->title) {
> +				free(load_option);
> +				free(entry);
> +				free(bootorder);
> +				return -ENOMEM;
> +			}
> +			entry->command = strdup("bootefi bootmgr");
> +			sprintf(entry->key, "%d", i);
> +			entry->num = i;
> +			entry->menu = menu;
> +			entry->type = BOOTMENU_TYPE_UEFI_BOOT_OPTION;
> +			entry->bootorder = bootorder[j];
> +			entry->next = NULL;
> +
> +			if (!iter)
> +				menu->first = entry;
> +			else
> +				iter->next = entry;
> +
> +			iter = entry;
> +			i++;
> +		}
> +
> +		free(load_option);
> +
> +		if (i == MAX_COUNT - 1)
> +			break;
> +	}
> +
> +	free(bootorder);
> +	*index = i;
> +	*current = iter;
> +
> +	return 1;
> +}
> +
>   static struct bootmenu_data *bootmenu_create(int delay)
>   {
>   	int ret;
> @@ -396,6 +490,14 @@ static struct bootmenu_data *bootmenu_create(int delay)
>   	if (ret < 0)
>   		goto cleanup;
>
> +	if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR)) {
> +		if (i < MAX_COUNT - 1) {
> +			ret = prepare_uefi_bootorder_entry(menu, &iter, &i);
> +			if (ret < 0 && ret != -ENOENT)
> +				goto cleanup;
> +		}
> +	}
> +
>   	/* Add U-Boot console entry at the end */
>   	if (i <= MAX_COUNT - 1) {
>   		entry = malloc(sizeof(struct bootmenu_entry));
> @@ -473,6 +575,31 @@ static void menu_display_statusline(struct menu *m)
>   	puts(ANSI_CLEAR_LINE);
>   }
>
> +static void handle_uefi_bootnext(void)
> +{
> +	u16 bootnext;
> +	efi_status_t ret;
> +	efi_uintn_t size;
> +
> +	/* Initialize EFI drivers */
> +	ret = efi_init_obj_list();
> +	if (ret != EFI_SUCCESS) {
> +		log_err("Error: Cannot initialize UEFI sub-system, r = %lu\n",
> +			ret & ~EFI_ERROR_MASK);
> +
> +		return;
> +	}
> +
> +	/* If UEFI BootNext variable is set, boot the BootNext load option */
> +	size = sizeof(u16);
> +	ret = efi_get_variable_int(u"BootNext",
> +				   &efi_global_variable_guid,
> +				   NULL, &size, &bootnext, NULL);
> +	if (ret == EFI_SUCCESS)
> +		/* BootNext does exist here, try to boot */
> +		run_command("bootefi bootmgr", 0);
> +}
> +
>   static void bootmenu_show(int delay)
>   {
>   	int init = 0;
> @@ -482,8 +609,12 @@ static void bootmenu_show(int delay)
>   	struct menu *menu;
>   	struct bootmenu_data *bootmenu;
>   	struct bootmenu_entry *iter;
> +	efi_status_t efi_ret = EFI_SUCCESS;
>   	char *option, *sep;
>
> +	if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR))
> +		handle_uefi_bootnext();
> +
>   	/* If delay is 0 do not create menu, just run first entry */
>   	if (delay == 0) {
>   		option = bootmenu_getoption(0);
> @@ -532,6 +663,27 @@ static void bootmenu_show(int delay)
>   		command = strdup(iter->command);
>   	}
>
> +	/*
> +	 * If the selected entry is UEFI BOOT####, set the BootNext variable.
> +	 * Then uefi bootmgr is invoked by the preset command in iter->command.
> +	 */
> +	if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR)) {
> +		if (iter->type == BOOTMENU_TYPE_UEFI_BOOT_OPTION) {
> +			/*
> +			 * UEFI specification requires BootNext variable needs non-volatile
> +			 * attribute, but this BootNext is only used inside of U-Boot and
> +			 * removed by efi bootmgr once BootNext is processed.
> +			 * So this BootNext can be volatile.
> +			 */
> +			efi_ret = efi_set_variable_int(u"BootNext", &efi_global_variable_guid,
> +						       EFI_VARIABLE_BOOTSERVICE_ACCESS |
> +						       EFI_VARIABLE_RUNTIME_ACCESS,
> +						       sizeof(u16), &iter->bootorder, false);
> +			if (efi_ret != EFI_SUCCESS)
> +				goto cleanup;
> +		}
> +	}
> +
>   cleanup:
>   	menu_destroy(menu);
>   	bootmenu_destroy(bootmenu);
> @@ -545,7 +697,8 @@ cleanup:
>   	if (title && command) {
>   		debug("Starting entry '%ls'\n", title);
>   		free(title);
> -		run_command(command, 0);
> +		if (efi_ret == EFI_SUCCESS)
> +			run_command(command, 0);
>   		free(command);
>   	}
>


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

* Re: [PATCH v5 10/17] bootmenu: add distro boot entry
  2022-04-28  8:09 ` [PATCH v5 10/17] bootmenu: add distro boot entry Masahisa Kojima
@ 2022-05-01 21:48   ` Heinrich Schuchardt
  2022-05-12  8:44     ` Takahiro Akashi
  0 siblings, 1 reply; 58+ messages in thread
From: Heinrich Schuchardt @ 2022-05-01 21:48 UTC (permalink / raw)
  To: Masahisa Kojima, u-boot
  Cc: Ilias Apalodimas, Simon Glass, Takahiro Akashi, Francois Ozog,
	Mark Kettenis

On 4/28/22 10:09, Masahisa Kojima wrote:
> This commit adds the distro_boot entries into the bootmenu.
> The bootmenu read the "boot_targets" U-Boot environment variable
> and enumerate it.
> User can select the distro boot entry, then bootmenu executes
> "run bootcmd_xxx" command.
>
> The bootmenu also checks the existing block devices and network
> option("dhcp" and "pxe") availability, then filter out
> the "boot_targets" appeared in bootmenu.
>
> The bootmenu example is as follows, distro boot entry has the
> "distro_boot" prefix.
>
>    *** U-Boot Boot Menu ***
>
>       distro_boot   : usb0
>       distro_boot   : scsi0
>       distro_boot   : virtio0
>       distro_boot   : dhcp

You will be creating UEFI boot options for all block devices anyway.
We should avoid duplicate entries. Please, drop this patch.

Best regards

heinrich

>
> Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
> ---
> Changes in v5:
> - split into the separate patch
> - add function description comment
> - handle the case boot_targets variable is empty
> - filter out the non-exist device entry
>
>   cmd/bootmenu.c | 177 +++++++++++++++++++++++++++++++++++++++++++++++++
>   1 file changed, 177 insertions(+)
>
> diff --git a/cmd/bootmenu.c b/cmd/bootmenu.c
> index da688e6213..afe42b8041 100644
> --- a/cmd/bootmenu.c
> +++ b/cmd/bootmenu.c
> @@ -7,6 +7,7 @@
>   #include <common.h>
>   #include <command.h>
>   #include <ansi.h>
> +#include <dm.h>
>   #include <efi_loader.h>
>   #include <efi_variable.h>
>   #include <env.h>
> @@ -14,6 +15,7 @@
>   #include <menu.h>
>   #include <watchdog.h>
>   #include <malloc.h>
> +#include <linux/ctype.h>
>   #include <linux/delay.h>
>   #include <linux/string.h>
>
> @@ -31,6 +33,7 @@ enum boot_type {
>   	BOOTMENU_TYPE_NONE = 0,
>   	BOOTMENU_TYPE_BOOTMENU,
>   	BOOTMENU_TYPE_UEFI_BOOT_OPTION,
> +	BOOTMENU_TYPE_DISTRO_BOOT,
>   };
>
>   struct bootmenu_entry {
> @@ -90,6 +93,8 @@ static void bootmenu_print_entry(void *data)
>   		printf("bootmenu_%02d   : %ls", entry->bootorder, entry->title);
>   	else if (entry->type == BOOTMENU_TYPE_UEFI_BOOT_OPTION)
>   		printf("UEFI BOOT%04X : %ls", entry->bootorder, entry->title);
> +	else if (entry->type == BOOTMENU_TYPE_DISTRO_BOOT)
> +		printf("distro_boot   : %ls", entry->title);
>   	else
>   		printf("%ls", entry->title);
>
> @@ -465,6 +470,172 @@ static int prepare_uefi_bootorder_entry(struct bootmenu_data *menu,
>   	return 1;
>   }
>
> +static int is_blk_device_available(char *token)
> +{
> +	struct driver *d = ll_entry_start(struct driver, driver);
> +	const int n_ents = ll_entry_count(struct driver, driver);
> +	struct driver *entry;
> +	struct udevice *udev;
> +	struct uclass *uc;
> +	struct blk_desc *desc;
> +	int ret, i;
> +	const char *if_type_name;
> +
> +	ret = uclass_get(UCLASS_BLK, &uc);
> +	if (ret)
> +		return -ENODEV;
> +
> +	for (entry = d; entry < d + n_ents; entry++) {
> +		if (entry->id != UCLASS_BLK)
> +			continue;
> +		i = 0;
> +		uclass_foreach_dev(udev, uc) {
> +			if (udev->driver != entry)
> +				continue;
> +			desc = dev_get_uclass_plat(udev);
> +			if_type_name = blk_get_if_type_name(desc->if_type);
> +			if (strncmp(token, if_type_name, strlen(if_type_name)) == 0) {
> +				char *p;
> +				int j, len;
> +				int devnum = 0;
> +
> +				p = token + strlen(if_type_name);
> +				len = strlen(p);
> +				if (!len)
> +					continue; /* no device number */
> +
> +				for (j = 0; j < len; j++) {
> +					if (!isdigit(*p)) {
> +						/* invalid device number */
> +						devnum = INT_MAX;
> +						break;
> +					}
> +					devnum = (devnum * 10) + (*p++ - '0');
> +				}
> +
> +				if (devnum == INT_MAX)
> +					continue;
> +
> +				if (devnum == desc->devnum)
> +					return 1;
> +			}
> +		}
> +	}
> +
> +	if (strncmp(token, "dhcp", strlen("dhcp")) == 0) {
> +		if (IS_ENABLED(CONFIG_CMD_DHCP))
> +			return 1;
> +	}
> +
> +	if (strncmp(token, "pxe", strlen("pxe")) == 0) {
> +		if (IS_ENABLED(CONFIG_CMD_PXE))
> +			return 1;
> +	}
> +
> +	return -ENODEV;
> +}
> +
> +/**
> + * prepare_distro_boot_entry() - generate the distro boot entries
> + *
> + * This function read the "boot_targets" U-Boot environment variable
> + * and generate the bootmenu entries.
> + *
> + * @menu:	pointer to the bootmenu structure
> + * @current:	pointer to the last bootmenu entry list
> + * @index:	pointer to the index of the last bootmenu entry,
> + *		the number of uefi entry is added by this function
> + * Return:	1 on success, negative value on error
> + */
> +static int prepare_distro_boot_entry(struct bootmenu_data *menu,
> +				     struct bootmenu_entry **current,
> +				     unsigned short int *index)
> +{
> +	char *p;
> +	int len;
> +	char *token;
> +	char *boot_targets;
> +	unsigned short int i = *index;
> +	struct bootmenu_entry *entry = NULL;
> +	struct bootmenu_entry *iter = *current;
> +
> +	/* list the distro boot "boot_targets" */
> +	boot_targets = env_get("boot_targets");
> +	if (!boot_targets)
> +		return -ENOENT;
> +
> +	len = strlen(boot_targets);
> +	if (!len)
> +		return -ENOENT;
> +
> +	p = calloc(1, len + 1);
> +	strlcpy(p, boot_targets, len);
> +
> +	token = strtok(p, " ");
> +
> +	do {
> +		u16 *buf;
> +		char *command;
> +		int command_size;
> +
> +		if (is_blk_device_available(token) != 1) {
> +			token = strtok(NULL, " ");
> +			continue;
> +		}
> +
> +		entry = malloc(sizeof(struct bootmenu_entry));
> +		if (!entry) {
> +			free(p);
> +			return -ENOMEM;
> +		}
> +
> +		len = strlen(token);
> +		buf = calloc(1, (len + 1) * sizeof(u16));
> +		entry->title = buf;
> +		if (!entry->title) {
> +			free(entry);
> +			free(p);
> +			return -ENOMEM;
> +		}
> +		utf8_utf16_strncpy(&buf, token, len);
> +		sprintf(entry->key, "%d", i);
> +		entry->num = i;
> +		entry->menu = menu;
> +
> +		command_size = sizeof("run bootcmd_") + len;
> +		command = calloc(1, command_size);
> +		if (!command) {
> +			free(entry->title);
> +			free(entry);
> +			free(p);
> +			return -ENOMEM;
> +		}
> +		snprintf(command, command_size, "run bootcmd_%s", token);
> +		entry->command = command;
> +		entry->type = BOOTMENU_TYPE_DISTRO_BOOT;
> +		entry->next = NULL;
> +
> +		if (!iter)
> +			menu->first = entry;
> +		else
> +			iter->next = entry;
> +
> +		iter = entry;
> +		i++;
> +
> +		if (i == MAX_COUNT - 1)
> +			break;
> +
> +		token = strtok(NULL, " ");
> +	} while (token);
> +
> +	free(p);
> +	*index = i;
> +	*current = iter;
> +
> +	return 1;
> +}
> +
>   static struct bootmenu_data *bootmenu_create(int delay)
>   {
>   	int ret;
> @@ -498,6 +669,12 @@ static struct bootmenu_data *bootmenu_create(int delay)
>   		}
>   	}
>
> +	if (i < MAX_COUNT - 1) {
> +		ret = prepare_distro_boot_entry(menu, &iter, &i);
> +		if (ret < 0 && ret != -ENOENT)
> +			goto cleanup;
> +	}
> +
>   	/* Add U-Boot console entry at the end */
>   	if (i <= MAX_COUNT - 1) {
>   		entry = malloc(sizeof(struct bootmenu_entry));


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

* Re: [PATCH v5 05/17] efi_loader: export efi_locate_device_handle()
  2022-05-01 18:53   ` Heinrich Schuchardt
@ 2022-05-04  9:17     ` Ilias Apalodimas
  0 siblings, 0 replies; 58+ messages in thread
From: Ilias Apalodimas @ 2022-05-04  9:17 UTC (permalink / raw)
  To: Heinrich Schuchardt
  Cc: Masahisa Kojima, u-boot, Simon Glass, Takahiro Akashi,
	Francois Ozog, Mark Kettenis

On Sun, 1 May 2022 at 21:59, Heinrich Schuchardt <xypron.glpk@gmx.de> wrote:
>
> On 4/28/22 10:09, Masahisa Kojima wrote:
> > From: AKASHI Takahiro <takahiro.akashi@linaro.org>
> >
> > This function will be used in the next commit where some behavior
> > of EFI boot manager will be expanded.
> >
> > Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> > Reviewed-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
>
> Reviewed-by: Heinrich Schuchardt <xypron.glpk@gmx.de>
>
> > ---
> > No changes from original version
> >
> >   include/efi_loader.h          | 4 ++++
> >   lib/efi_loader/efi_boottime.c | 7 +++----
> >   2 files changed, 7 insertions(+), 4 deletions(-)
> >
> > diff --git a/include/efi_loader.h b/include/efi_loader.h
> > index ba79a9afb4..effb43369d 100644
> > --- a/include/efi_loader.h
> > +++ b/include/efi_loader.h
> > @@ -595,6 +595,10 @@ efi_status_t efi_create_handle(efi_handle_t *handle);
> >   void efi_delete_handle(efi_handle_t obj);
> >   /* Call this to validate a handle and find the EFI object for it */
> >   struct efi_object *efi_search_obj(const efi_handle_t handle);
> > +/* Locate device_path handle */
> > +efi_status_t EFIAPI efi_locate_device_path(const efi_guid_t *protocol,
> > +                                        struct efi_device_path **device_path,
> > +                                        efi_handle_t *device);
> >   /* Load image */
> >   efi_status_t EFIAPI efi_load_image(bool boot_policy,
> >                                  efi_handle_t parent_image,
> > diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c
> > index 5bcb8253ed..4da64b5d29 100644
> > --- a/lib/efi_loader/efi_boottime.c
> > +++ b/lib/efi_loader/efi_boottime.c
> > @@ -1799,10 +1799,9 @@ failure:
> >    *
> >    * Return: status code
> >    */
> > -static efi_status_t EFIAPI efi_locate_device_path(
> > -                     const efi_guid_t *protocol,
> > -                     struct efi_device_path **device_path,
> > -                     efi_handle_t *device)
> > +efi_status_t EFIAPI efi_locate_device_path(const efi_guid_t *protocol,
> > +                                        struct efi_device_path **device_path,
> > +                                        efi_handle_t *device)
> >   {
> >       struct efi_device_path *dp;
> >       size_t i;
>

Reviewed-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>

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

* Re: [PATCH v5 06/17] efi_loader: bootmgr: add booting from removable media
  2022-04-29 17:03   ` Heinrich Schuchardt
@ 2022-05-05 12:05     ` Mark Kettenis
  2022-05-05 12:20       ` Heinrich Schuchardt
  2022-05-05 12:47       ` Mark Kettenis
  0 siblings, 2 replies; 58+ messages in thread
From: Mark Kettenis @ 2022-05-05 12:05 UTC (permalink / raw)
  To: Heinrich Schuchardt
  Cc: masahisa.kojima, ilias.apalodimas, sjg, takahiro.akashi,
	francois.ozog, kettenis, Peter.Hoyes, narmstrong, andre.przywara,
	u-boot

> Date: Fri, 29 Apr 2022 19:03:22 +0200
> From: Heinrich Schuchardt <xypron.glpk@gmx.de>
> 
> On 4/28/22 10:09, Masahisa Kojima wrote:
> > From: AKASHI Takahiro <takahiro.akashi@linaro.org>
> >
> > Under the current implementation, booting from removable media using
> > a architecture-specific default image name, say BOOTAA64.EFI, is
> > supported only in distro_bootcmd script. See the commit 74522c898b35
> > ("efi_loader: Add distro boot script for removable media").
> >
> > This is, however, half-baked implementation because
> > 1) UEFI specification requires this feature to be implemented as part
> >     of Boot Manager's responsibility:
> >
> >    3 - Boot Manager
> >    3.5.1 Boot via the Simple File Protocol
> >    When booting via the EFI_SIMPLE_FILE_SYSTEM_PROTOCOL, the FilePath will
> >    start with a device path that points to the device that implements the
> >    EFI_SIMPLE_FILE_SYSTEM_PROTOCOL or the EFI_BLOCK_IO_PROTOCOL. The next
> >    part of the FilePath may point to the file name, including
> >    subdirectories, which contain the bootable image. If the file name is
> >    a null device path, the file name must be generated from the rules
> >    defined below.
> >    ...
> >    3.5.1.1 Removable Media Boot Behavior
> >    To generate a file name when none is present in the FilePath, the
> >    firmware must append a default file name in the form
> >    \EFI\BOOT\BOOT{machine type short-name}.EFI ...
> >
> > 2) So (1) entails the hehavior that the user's preference of boot media
> >     order should be determined by Boot#### and BootOrder variables.
> >
> > With this patch, the semantics mentioned above is fully implemented.
> > For example, if you want to boot the system from USB and SCSI in this
> > order,
> > * define Boot0001 which contains only a device path to the USB device
> >    (without any file path/name)
> > * define Boot0002 which contains only a device path to the SCSI device,
> > and
> > * set BootOrder to Boot0001:Boot0002
> >
> > To avoid build error for sandbox, default file name "BOOTSANDBOX.efi"
> > is defined even if it is out of scope of UEFI specification.
> >
> > Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> > Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
> > ---
> > Changes in v5:
> > - add default file name definition for SANDBOX to avoid build error
> >
> > Changes from original version:
> > - create new include file "efi_default_filename.h" to
> >    avoid conflict with config_distro_bootcmd.h
> > - modify the target pointer of efi_free_pool(), expand_media_path() should
> >    only free the pointer allocated by efi_dp_from_file() function.
> >   include/config_distro_bootcmd.h | 14 +--------
> >   include/efi_default_filename.h  | 33 ++++++++++++++++++++++
> >   lib/efi_loader/efi_bootmgr.c    | 50 ++++++++++++++++++++++++++++++++-
> >   3 files changed, 83 insertions(+), 14 deletions(-)
> >   create mode 100644 include/efi_default_filename.h
> >
> > diff --git a/include/config_distro_bootcmd.h b/include/config_distro_bootcmd.h
> > index c55023889c..6a3110f27b 100644
> > --- a/include/config_distro_bootcmd.h
> > +++ b/include/config_distro_bootcmd.h
> > @@ -91,19 +91,7 @@
> >   #endif
> >
> >   #ifdef CONFIG_EFI_LOADER
> > -#if defined(CONFIG_ARM64)
> > -#define BOOTEFI_NAME "bootaa64.efi"
> > -#elif defined(CONFIG_ARM)
> > -#define BOOTEFI_NAME "bootarm.efi"
> > -#elif defined(CONFIG_X86_RUN_32BIT)
> > -#define BOOTEFI_NAME "bootia32.efi"
> > -#elif defined(CONFIG_X86_RUN_64BIT)
> > -#define BOOTEFI_NAME "bootx64.efi"
> > -#elif defined(CONFIG_ARCH_RV32I)
> > -#define BOOTEFI_NAME "bootriscv32.efi"
> > -#elif defined(CONFIG_ARCH_RV64I)
> > -#define BOOTEFI_NAME "bootriscv64.efi"
> > -#endif
> > +#include <efi_default_filename.h>
> >   #endif
> >
> >   #ifdef BOOTEFI_NAME
> > diff --git a/include/efi_default_filename.h b/include/efi_default_filename.h
> > new file mode 100644
> > index 0000000000..cb2ef9e131
> > --- /dev/null
> > +++ b/include/efi_default_filename.h
> > @@ -0,0 +1,33 @@
> > +/* SPDX-License-Identifier: GPL-2.0+ */
> > +/*
> > + * Default boot file name when none is present in the FilePath.
> > + * This is defined in the UEFI specification.
> > + *
> > + * Copyright (c) 2022, Linaro Limited
> > + */
> > +#ifndef _EFI_DEFAULT_FILENAME_H
> > +#define _EFI_DEFAULT_FILENAME_H
> > +
> > +#if defined(CONFIG_ARM64)
> > +#define BOOTEFI_NAME "BOOTAA64.EFI"
> > +#elif defined(CONFIG_ARM)
> > +#define BOOTEFI_NAME "BOOTARM.EFI"
> > +#elif defined(CONFIG_X86_64)
> > +#define BOOTEFI_NAME "BOOTX64.EFI"
> > +#elif defined(CONFIG_X86)
> > +#define BOOTEFI_NAME "BOOTIA32.EFI"
> > +#elif defined(CONFIG_ARCH_RV32I)
> > +#define BOOTEFI_NAME "BOOTRISCV32.EFI"
> > +#elif defined(CONFIG_ARCH_RV64I)
> > +#define BOOTEFI_NAME "BOOTRISCV64.EFI"
> > +#elif defined(CONFIG_SANDBOX)
> > +/*
> > + * SANDBOX is not defined in UEFI specification, but
> > + * this definition avoids build failure for SANDBOX.
> > + */
> > +#define BOOTEFI_NAME "BOOTSANDBOX.EFI"
> 
> The sandbox should boot the default binary for the host architecture:
> 
> #ifndef _EFI_DEFAULT_FILENAME_H
> #define _EFI_DEFAULT_FILENAME_H
> 
> #include <host_arch.h>
> 
> #undef BOOTEFI_NAME
> 
> #if HOST_ARCH == HOST_ARCH_X86_64
> #define BOOTEFI_NAME "BOOTX64.EFI"
> #endif
> 
> #if HOST_ARCH == HOST_ARCH_X86
> #define BOOTEFI_NAME "BOOTIA32.EFI"
> #endif
> 
> #if HOST_ARCH == HOST_ARCH_AARCH64
> #define BOOTEFI_NAME "BOOTAA64.EFI"
> #endif
> 
> #if HOST_ARCH == HOST_ARCH_ARM
> #define BOOTEFI_NAME "BOOTARM.EFI"
> #endif
> 
> #if HOST_ARCH == HOST_ARCH_RISCV32
> #define BOOTEFI_NAME "BOOTRISCV32.EFI"
> #endif
> 
> #if HOST_ARCH == HOST_ARCH_RISCV64
> #define BOOTEFI_NAME "BOOTRISCV64.EFI"
> #endif
> 
> #ifndef BOOTEFI_NAME
> #error Unsupported UEFI architecture
> #endif
> 
> #endif

Maybe sanbox is special, but using the host architecture for actual
boards makes no sense.  I see this has made its way into master
already, but when I cross-build for apple_m1_defconfig on an amd64
machine I end up with:

    $ strings ./lib/efi_loader/efi_bootmgr.o | grep BOOT
    /EFI/BOOT/BOOTX64.EFI

The original diff that used CONFIG_ARM64, CONFIG_ARM, etc, did this
right.

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

* Re: [PATCH v5 06/17] efi_loader: bootmgr: add booting from removable media
  2022-05-05 12:05     ` Mark Kettenis
@ 2022-05-05 12:20       ` Heinrich Schuchardt
  2022-05-05 12:35         ` Heinrich Schuchardt
  2022-05-05 12:47       ` Mark Kettenis
  1 sibling, 1 reply; 58+ messages in thread
From: Heinrich Schuchardt @ 2022-05-05 12:20 UTC (permalink / raw)
  To: Mark Kettenis
  Cc: masahisa.kojima, ilias.apalodimas, sjg, takahiro.akashi,
	francois.ozog, kettenis, Peter.Hoyes, narmstrong, andre.przywara,
	u-boot

On 5/5/22 14:05, Mark Kettenis wrote:
>> Date: Fri, 29 Apr 2022 19:03:22 +0200
>> From: Heinrich Schuchardt <xypron.glpk@gmx.de>
>>
>> On 4/28/22 10:09, Masahisa Kojima wrote:
>>> From: AKASHI Takahiro <takahiro.akashi@linaro.org>
>>>
>>> Under the current implementation, booting from removable media using
>>> a architecture-specific default image name, say BOOTAA64.EFI, is
>>> supported only in distro_bootcmd script. See the commit 74522c898b35
>>> ("efi_loader: Add distro boot script for removable media").
>>>
>>> This is, however, half-baked implementation because
>>> 1) UEFI specification requires this feature to be implemented as part
>>>      of Boot Manager's responsibility:
>>>
>>>     3 - Boot Manager
>>>     3.5.1 Boot via the Simple File Protocol
>>>     When booting via the EFI_SIMPLE_FILE_SYSTEM_PROTOCOL, the FilePath will
>>>     start with a device path that points to the device that implements the
>>>     EFI_SIMPLE_FILE_SYSTEM_PROTOCOL or the EFI_BLOCK_IO_PROTOCOL. The next
>>>     part of the FilePath may point to the file name, including
>>>     subdirectories, which contain the bootable image. If the file name is
>>>     a null device path, the file name must be generated from the rules
>>>     defined below.
>>>     ...
>>>     3.5.1.1 Removable Media Boot Behavior
>>>     To generate a file name when none is present in the FilePath, the
>>>     firmware must append a default file name in the form
>>>     \EFI\BOOT\BOOT{machine type short-name}.EFI ...
>>>
>>> 2) So (1) entails the hehavior that the user's preference of boot media
>>>      order should be determined by Boot#### and BootOrder variables.
>>>
>>> With this patch, the semantics mentioned above is fully implemented.
>>> For example, if you want to boot the system from USB and SCSI in this
>>> order,
>>> * define Boot0001 which contains only a device path to the USB device
>>>     (without any file path/name)
>>> * define Boot0002 which contains only a device path to the SCSI device,
>>> and
>>> * set BootOrder to Boot0001:Boot0002
>>>
>>> To avoid build error for sandbox, default file name "BOOTSANDBOX.efi"
>>> is defined even if it is out of scope of UEFI specification.
>>>
>>> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
>>> Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
>>> ---
>>> Changes in v5:
>>> - add default file name definition for SANDBOX to avoid build error
>>>
>>> Changes from original version:
>>> - create new include file "efi_default_filename.h" to
>>>     avoid conflict with config_distro_bootcmd.h
>>> - modify the target pointer of efi_free_pool(), expand_media_path() should
>>>     only free the pointer allocated by efi_dp_from_file() function.
>>>    include/config_distro_bootcmd.h | 14 +--------
>>>    include/efi_default_filename.h  | 33 ++++++++++++++++++++++
>>>    lib/efi_loader/efi_bootmgr.c    | 50 ++++++++++++++++++++++++++++++++-
>>>    3 files changed, 83 insertions(+), 14 deletions(-)
>>>    create mode 100644 include/efi_default_filename.h
>>>
>>> diff --git a/include/config_distro_bootcmd.h b/include/config_distro_bootcmd.h
>>> index c55023889c..6a3110f27b 100644
>>> --- a/include/config_distro_bootcmd.h
>>> +++ b/include/config_distro_bootcmd.h
>>> @@ -91,19 +91,7 @@
>>>    #endif
>>>
>>>    #ifdef CONFIG_EFI_LOADER
>>> -#if defined(CONFIG_ARM64)
>>> -#define BOOTEFI_NAME "bootaa64.efi"
>>> -#elif defined(CONFIG_ARM)
>>> -#define BOOTEFI_NAME "bootarm.efi"
>>> -#elif defined(CONFIG_X86_RUN_32BIT)
>>> -#define BOOTEFI_NAME "bootia32.efi"
>>> -#elif defined(CONFIG_X86_RUN_64BIT)
>>> -#define BOOTEFI_NAME "bootx64.efi"
>>> -#elif defined(CONFIG_ARCH_RV32I)
>>> -#define BOOTEFI_NAME "bootriscv32.efi"
>>> -#elif defined(CONFIG_ARCH_RV64I)
>>> -#define BOOTEFI_NAME "bootriscv64.efi"
>>> -#endif
>>> +#include <efi_default_filename.h>
>>>    #endif
>>>
>>>    #ifdef BOOTEFI_NAME
>>> diff --git a/include/efi_default_filename.h b/include/efi_default_filename.h
>>> new file mode 100644
>>> index 0000000000..cb2ef9e131
>>> --- /dev/null
>>> +++ b/include/efi_default_filename.h
>>> @@ -0,0 +1,33 @@
>>> +/* SPDX-License-Identifier: GPL-2.0+ */
>>> +/*
>>> + * Default boot file name when none is present in the FilePath.
>>> + * This is defined in the UEFI specification.
>>> + *
>>> + * Copyright (c) 2022, Linaro Limited
>>> + */
>>> +#ifndef _EFI_DEFAULT_FILENAME_H
>>> +#define _EFI_DEFAULT_FILENAME_H
>>> +
>>> +#if defined(CONFIG_ARM64)
>>> +#define BOOTEFI_NAME "BOOTAA64.EFI"
>>> +#elif defined(CONFIG_ARM)
>>> +#define BOOTEFI_NAME "BOOTARM.EFI"
>>> +#elif defined(CONFIG_X86_64)
>>> +#define BOOTEFI_NAME "BOOTX64.EFI"
>>> +#elif defined(CONFIG_X86)
>>> +#define BOOTEFI_NAME "BOOTIA32.EFI"
>>> +#elif defined(CONFIG_ARCH_RV32I)
>>> +#define BOOTEFI_NAME "BOOTRISCV32.EFI"
>>> +#elif defined(CONFIG_ARCH_RV64I)
>>> +#define BOOTEFI_NAME "BOOTRISCV64.EFI"
>>> +#elif defined(CONFIG_SANDBOX)
>>> +/*
>>> + * SANDBOX is not defined in UEFI specification, but
>>> + * this definition avoids build failure for SANDBOX.
>>> + */
>>> +#define BOOTEFI_NAME "BOOTSANDBOX.EFI"
>>
>> The sandbox should boot the default binary for the host architecture:
>>
>> #ifndef _EFI_DEFAULT_FILENAME_H
>> #define _EFI_DEFAULT_FILENAME_H
>>
>> #include <host_arch.h>
>>
>> #undef BOOTEFI_NAME
>>
>> #if HOST_ARCH == HOST_ARCH_X86_64
>> #define BOOTEFI_NAME "BOOTX64.EFI"
>> #endif
>>
>> #if HOST_ARCH == HOST_ARCH_X86
>> #define BOOTEFI_NAME "BOOTIA32.EFI"
>> #endif
>>
>> #if HOST_ARCH == HOST_ARCH_AARCH64
>> #define BOOTEFI_NAME "BOOTAA64.EFI"
>> #endif
>>
>> #if HOST_ARCH == HOST_ARCH_ARM
>> #define BOOTEFI_NAME "BOOTARM.EFI"
>> #endif
>>
>> #if HOST_ARCH == HOST_ARCH_RISCV32
>> #define BOOTEFI_NAME "BOOTRISCV32.EFI"
>> #endif
>>
>> #if HOST_ARCH == HOST_ARCH_RISCV64
>> #define BOOTEFI_NAME "BOOTRISCV64.EFI"
>> #endif
>>
>> #ifndef BOOTEFI_NAME
>> #error Unsupported UEFI architecture
>> #endif
>>
>> #endif
>
> Maybe sanbox is special, but using the host architecture for actual
> boards makes no sense.  I see this has made its way into master
> already, but when I cross-build for apple_m1_defconfig on an amd64
> machine I end up with:
>
>      $ strings ./lib/efi_loader/efi_bootmgr.o | grep BOOT
>      /EFI/BOOT/BOOTX64.EFI

Thanks for reporting the issue.

On Sandbox it should be the host architecture. on other defconfigs the
target architecture.

Best regards

Heinrich

>
> The original diff that used CONFIG_ARM64, CONFIG_ARM, etc, did this
> right.


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

* Re: [PATCH v5 06/17] efi_loader: bootmgr: add booting from removable media
  2022-05-05 12:20       ` Heinrich Schuchardt
@ 2022-05-05 12:35         ` Heinrich Schuchardt
  2022-05-05 13:25           ` Mark Kettenis
  0 siblings, 1 reply; 58+ messages in thread
From: Heinrich Schuchardt @ 2022-05-05 12:35 UTC (permalink / raw)
  To: Mark Kettenis
  Cc: masahisa.kojima, ilias.apalodimas, sjg, takahiro.akashi,
	francois.ozog, kettenis, Peter.Hoyes, narmstrong, andre.przywara,
	u-boot

On 5/5/22 14:20, Heinrich Schuchardt wrote:
> On 5/5/22 14:05, Mark Kettenis wrote:
>>> Date: Fri, 29 Apr 2022 19:03:22 +0200
>>> From: Heinrich Schuchardt <xypron.glpk@gmx.de>
>>>
>>> On 4/28/22 10:09, Masahisa Kojima wrote:
>>>> From: AKASHI Takahiro <takahiro.akashi@linaro.org>
>>>>
>>>> Under the current implementation, booting from removable media using
>>>> a architecture-specific default image name, say BOOTAA64.EFI, is
>>>> supported only in distro_bootcmd script. See the commit 74522c898b35
>>>> ("efi_loader: Add distro boot script for removable media").
>>>>
>>>> This is, however, half-baked implementation because
>>>> 1) UEFI specification requires this feature to be implemented as part
>>>>      of Boot Manager's responsibility:
>>>>
>>>>     3 - Boot Manager
>>>>     3.5.1 Boot via the Simple File Protocol
>>>>     When booting via the EFI_SIMPLE_FILE_SYSTEM_PROTOCOL, the
>>>> FilePath will
>>>>     start with a device path that points to the device that
>>>> implements the
>>>>     EFI_SIMPLE_FILE_SYSTEM_PROTOCOL or the EFI_BLOCK_IO_PROTOCOL.
>>>> The next
>>>>     part of the FilePath may point to the file name, including
>>>>     subdirectories, which contain the bootable image. If the file
>>>> name is
>>>>     a null device path, the file name must be generated from the rules
>>>>     defined below.
>>>>     ...
>>>>     3.5.1.1 Removable Media Boot Behavior
>>>>     To generate a file name when none is present in the FilePath, the
>>>>     firmware must append a default file name in the form
>>>>     \EFI\BOOT\BOOT{machine type short-name}.EFI ...
>>>>
>>>> 2) So (1) entails the hehavior that the user's preference of boot media
>>>>      order should be determined by Boot#### and BootOrder variables.
>>>>
>>>> With this patch, the semantics mentioned above is fully implemented.
>>>> For example, if you want to boot the system from USB and SCSI in this
>>>> order,
>>>> * define Boot0001 which contains only a device path to the USB device
>>>>     (without any file path/name)
>>>> * define Boot0002 which contains only a device path to the SCSI device,
>>>> and
>>>> * set BootOrder to Boot0001:Boot0002
>>>>
>>>> To avoid build error for sandbox, default file name "BOOTSANDBOX.efi"
>>>> is defined even if it is out of scope of UEFI specification.
>>>>
>>>> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
>>>> Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
>>>> ---
>>>> Changes in v5:
>>>> - add default file name definition for SANDBOX to avoid build error
>>>>
>>>> Changes from original version:
>>>> - create new include file "efi_default_filename.h" to
>>>>     avoid conflict with config_distro_bootcmd.h
>>>> - modify the target pointer of efi_free_pool(), expand_media_path()
>>>> should
>>>>     only free the pointer allocated by efi_dp_from_file() function.
>>>>    include/config_distro_bootcmd.h | 14 +--------
>>>>    include/efi_default_filename.h  | 33 ++++++++++++++++++++++
>>>>    lib/efi_loader/efi_bootmgr.c    | 50
>>>> ++++++++++++++++++++++++++++++++-
>>>>    3 files changed, 83 insertions(+), 14 deletions(-)
>>>>    create mode 100644 include/efi_default_filename.h
>>>>
>>>> diff --git a/include/config_distro_bootcmd.h
>>>> b/include/config_distro_bootcmd.h
>>>> index c55023889c..6a3110f27b 100644
>>>> --- a/include/config_distro_bootcmd.h
>>>> +++ b/include/config_distro_bootcmd.h
>>>> @@ -91,19 +91,7 @@
>>>>    #endif
>>>>
>>>>    #ifdef CONFIG_EFI_LOADER
>>>> -#if defined(CONFIG_ARM64)
>>>> -#define BOOTEFI_NAME "bootaa64.efi"
>>>> -#elif defined(CONFIG_ARM)
>>>> -#define BOOTEFI_NAME "bootarm.efi"
>>>> -#elif defined(CONFIG_X86_RUN_32BIT)
>>>> -#define BOOTEFI_NAME "bootia32.efi"
>>>> -#elif defined(CONFIG_X86_RUN_64BIT)
>>>> -#define BOOTEFI_NAME "bootx64.efi"
>>>> -#elif defined(CONFIG_ARCH_RV32I)
>>>> -#define BOOTEFI_NAME "bootriscv32.efi"
>>>> -#elif defined(CONFIG_ARCH_RV64I)
>>>> -#define BOOTEFI_NAME "bootriscv64.efi"
>>>> -#endif
>>>> +#include <efi_default_filename.h>
>>>>    #endif
>>>>
>>>>    #ifdef BOOTEFI_NAME
>>>> diff --git a/include/efi_default_filename.h
>>>> b/include/efi_default_filename.h
>>>> new file mode 100644
>>>> index 0000000000..cb2ef9e131
>>>> --- /dev/null
>>>> +++ b/include/efi_default_filename.h
>>>> @@ -0,0 +1,33 @@
>>>> +/* SPDX-License-Identifier: GPL-2.0+ */
>>>> +/*
>>>> + * Default boot file name when none is present in the FilePath.
>>>> + * This is defined in the UEFI specification.
>>>> + *
>>>> + * Copyright (c) 2022, Linaro Limited
>>>> + */
>>>> +#ifndef _EFI_DEFAULT_FILENAME_H
>>>> +#define _EFI_DEFAULT_FILENAME_H
>>>> +
>>>> +#if defined(CONFIG_ARM64)
>>>> +#define BOOTEFI_NAME "BOOTAA64.EFI"
>>>> +#elif defined(CONFIG_ARM)
>>>> +#define BOOTEFI_NAME "BOOTARM.EFI"
>>>> +#elif defined(CONFIG_X86_64)
>>>> +#define BOOTEFI_NAME "BOOTX64.EFI"
>>>> +#elif defined(CONFIG_X86)
>>>> +#define BOOTEFI_NAME "BOOTIA32.EFI"
>>>> +#elif defined(CONFIG_ARCH_RV32I)
>>>> +#define BOOTEFI_NAME "BOOTRISCV32.EFI"
>>>> +#elif defined(CONFIG_ARCH_RV64I)
>>>> +#define BOOTEFI_NAME "BOOTRISCV64.EFI"
>>>> +#elif defined(CONFIG_SANDBOX)
>>>> +/*
>>>> + * SANDBOX is not defined in UEFI specification, but
>>>> + * this definition avoids build failure for SANDBOX.
>>>> + */
>>>> +#define BOOTEFI_NAME "BOOTSANDBOX.EFI"
>>>
>>> The sandbox should boot the default binary for the host architecture:
>>>
>>> #ifndef _EFI_DEFAULT_FILENAME_H
>>> #define _EFI_DEFAULT_FILENAME_H
>>>
>>> #include <host_arch.h>
>>>
>>> #undef BOOTEFI_NAME
>>>
>>> #if HOST_ARCH == HOST_ARCH_X86_64
>>> #define BOOTEFI_NAME "BOOTX64.EFI"
>>> #endif
>>>
>>> #if HOST_ARCH == HOST_ARCH_X86
>>> #define BOOTEFI_NAME "BOOTIA32.EFI"
>>> #endif
>>>
>>> #if HOST_ARCH == HOST_ARCH_AARCH64
>>> #define BOOTEFI_NAME "BOOTAA64.EFI"
>>> #endif
>>>
>>> #if HOST_ARCH == HOST_ARCH_ARM
>>> #define BOOTEFI_NAME "BOOTARM.EFI"
>>> #endif
>>>
>>> #if HOST_ARCH == HOST_ARCH_RISCV32
>>> #define BOOTEFI_NAME "BOOTRISCV32.EFI"
>>> #endif
>>>
>>> #if HOST_ARCH == HOST_ARCH_RISCV64
>>> #define BOOTEFI_NAME "BOOTRISCV64.EFI"
>>> #endif
>>>
>>> #ifndef BOOTEFI_NAME
>>> #error Unsupported UEFI architecture
>>> #endif
>>>
>>> #endif
>>
>> Maybe sanbox is special, but using the host architecture for actual
>> boards makes no sense.  I see this has made its way into master
>> already, but when I cross-build for apple_m1_defconfig on an amd64
>> machine I end up with:
>>
>>      $ strings ./lib/efi_loader/efi_bootmgr.o | grep BOOT
>>      /EFI/BOOT/BOOTX64.EFI
>
> Thanks for reporting the issue.
>
> On Sandbox it should be the host architecture. on other defconfigs the
> target architecture.

On Ubuntu 22.04:

git reset --hard 1739a6db5403d187902dcebca548de0644c8078f
make apple_m1_defconfig
CROSS_COMPILE=aarch64-linux-gnu- make -j16

$ strings ./lib/efi_loader/efi_bootmgr.o | grep BOOT
/EFI/BOOT/BOOTAA64.EFI
UCLASS_REBOOT_MODE
UCLASS_BOOTSTD
EFI_OBJECT_TYPE_U_BOOT_FIRMWARE
UCLASS_BOOTDEV
UCLASS_BOOTMETH
UCLASS_BOOTCOUN

$ strings u-boot | grep 'BOOT\S*\.EFI'
/EFI/BOOT/BOOTAA64.EFI

How can I reproduce your problem?
Is this BSD specific? Which distro are you running?

Best regards

Heinrich

>
> Best regards
>
> Heinrich
>
>>
>> The original diff that used CONFIG_ARM64, CONFIG_ARM, etc, did this
>> right.
>


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

* Re: [PATCH v5 06/17] efi_loader: bootmgr: add booting from removable media
  2022-05-05 12:05     ` Mark Kettenis
  2022-05-05 12:20       ` Heinrich Schuchardt
@ 2022-05-05 12:47       ` Mark Kettenis
  2022-05-12  9:12         ` AKASHI Takahiro
  1 sibling, 1 reply; 58+ messages in thread
From: Mark Kettenis @ 2022-05-05 12:47 UTC (permalink / raw)
  To: Mark Kettenis
  Cc: xypron.glpk, masahisa.kojima, ilias.apalodimas, sjg,
	takahiro.akashi, francois.ozog, kettenis, Peter.Hoyes,
	narmstrong, andre.przywara, u-boot

> Date: Thu, 5 May 2022 14:05:04 +0200 (CEST)
> From: Mark Kettenis <mark.kettenis@xs4all.nl>
> 
> > Date: Fri, 29 Apr 2022 19:03:22 +0200
> > From: Heinrich Schuchardt <xypron.glpk@gmx.de>
> > 
> > On 4/28/22 10:09, Masahisa Kojima wrote:
> > > From: AKASHI Takahiro <takahiro.akashi@linaro.org>
> > >
> > > Under the current implementation, booting from removable media using
> > > a architecture-specific default image name, say BOOTAA64.EFI, is
> > > supported only in distro_bootcmd script. See the commit 74522c898b35
> > > ("efi_loader: Add distro boot script for removable media").
> > >
> > > This is, however, half-baked implementation because
> > > 1) UEFI specification requires this feature to be implemented as part
> > >     of Boot Manager's responsibility:
> > >
> > >    3 - Boot Manager
> > >    3.5.1 Boot via the Simple File Protocol
> > >    When booting via the EFI_SIMPLE_FILE_SYSTEM_PROTOCOL, the FilePath will
> > >    start with a device path that points to the device that implements the
> > >    EFI_SIMPLE_FILE_SYSTEM_PROTOCOL or the EFI_BLOCK_IO_PROTOCOL. The next
> > >    part of the FilePath may point to the file name, including
> > >    subdirectories, which contain the bootable image. If the file name is
> > >    a null device path, the file name must be generated from the rules
> > >    defined below.
> > >    ...
> > >    3.5.1.1 Removable Media Boot Behavior
> > >    To generate a file name when none is present in the FilePath, the
> > >    firmware must append a default file name in the form
> > >    \EFI\BOOT\BOOT{machine type short-name}.EFI ...
> > >
> > > 2) So (1) entails the hehavior that the user's preference of boot media
> > >     order should be determined by Boot#### and BootOrder variables.
> > >
> > > With this patch, the semantics mentioned above is fully implemented.
> > > For example, if you want to boot the system from USB and SCSI in this
> > > order,
> > > * define Boot0001 which contains only a device path to the USB device
> > >    (without any file path/name)
> > > * define Boot0002 which contains only a device path to the SCSI device,
> > > and
> > > * set BootOrder to Boot0001:Boot0002
> > >
> > > To avoid build error for sandbox, default file name "BOOTSANDBOX.efi"
> > > is defined even if it is out of scope of UEFI specification.
> > >
> > > Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> > > Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
> > > ---
> > > Changes in v5:
> > > - add default file name definition for SANDBOX to avoid build error
> > >
> > > Changes from original version:
> > > - create new include file "efi_default_filename.h" to
> > >    avoid conflict with config_distro_bootcmd.h
> > > - modify the target pointer of efi_free_pool(), expand_media_path() should
> > >    only free the pointer allocated by efi_dp_from_file() function.
> > >   include/config_distro_bootcmd.h | 14 +--------
> > >   include/efi_default_filename.h  | 33 ++++++++++++++++++++++
> > >   lib/efi_loader/efi_bootmgr.c    | 50 ++++++++++++++++++++++++++++++++-
> > >   3 files changed, 83 insertions(+), 14 deletions(-)
> > >   create mode 100644 include/efi_default_filename.h
> > >
> > > diff --git a/include/config_distro_bootcmd.h b/include/config_distro_bootcmd.h
> > > index c55023889c..6a3110f27b 100644
> > > --- a/include/config_distro_bootcmd.h
> > > +++ b/include/config_distro_bootcmd.h
> > > @@ -91,19 +91,7 @@
> > >   #endif
> > >
> > >   #ifdef CONFIG_EFI_LOADER
> > > -#if defined(CONFIG_ARM64)
> > > -#define BOOTEFI_NAME "bootaa64.efi"
> > > -#elif defined(CONFIG_ARM)
> > > -#define BOOTEFI_NAME "bootarm.efi"
> > > -#elif defined(CONFIG_X86_RUN_32BIT)
> > > -#define BOOTEFI_NAME "bootia32.efi"
> > > -#elif defined(CONFIG_X86_RUN_64BIT)
> > > -#define BOOTEFI_NAME "bootx64.efi"
> > > -#elif defined(CONFIG_ARCH_RV32I)
> > > -#define BOOTEFI_NAME "bootriscv32.efi"
> > > -#elif defined(CONFIG_ARCH_RV64I)
> > > -#define BOOTEFI_NAME "bootriscv64.efi"
> > > -#endif
> > > +#include <efi_default_filename.h>
> > >   #endif
> > >
> > >   #ifdef BOOTEFI_NAME
> > > diff --git a/include/efi_default_filename.h b/include/efi_default_filename.h
> > > new file mode 100644
> > > index 0000000000..cb2ef9e131
> > > --- /dev/null
> > > +++ b/include/efi_default_filename.h
> > > @@ -0,0 +1,33 @@
> > > +/* SPDX-License-Identifier: GPL-2.0+ */
> > > +/*
> > > + * Default boot file name when none is present in the FilePath.
> > > + * This is defined in the UEFI specification.
> > > + *
> > > + * Copyright (c) 2022, Linaro Limited
> > > + */
> > > +#ifndef _EFI_DEFAULT_FILENAME_H
> > > +#define _EFI_DEFAULT_FILENAME_H
> > > +
> > > +#if defined(CONFIG_ARM64)
> > > +#define BOOTEFI_NAME "BOOTAA64.EFI"
> > > +#elif defined(CONFIG_ARM)
> > > +#define BOOTEFI_NAME "BOOTARM.EFI"
> > > +#elif defined(CONFIG_X86_64)
> > > +#define BOOTEFI_NAME "BOOTX64.EFI"
> > > +#elif defined(CONFIG_X86)
> > > +#define BOOTEFI_NAME "BOOTIA32.EFI"
> > > +#elif defined(CONFIG_ARCH_RV32I)
> > > +#define BOOTEFI_NAME "BOOTRISCV32.EFI"
> > > +#elif defined(CONFIG_ARCH_RV64I)
> > > +#define BOOTEFI_NAME "BOOTRISCV64.EFI"
> > > +#elif defined(CONFIG_SANDBOX)
> > > +/*
> > > + * SANDBOX is not defined in UEFI specification, but
> > > + * this definition avoids build failure for SANDBOX.
> > > + */
> > > +#define BOOTEFI_NAME "BOOTSANDBOX.EFI"
> > 
> > The sandbox should boot the default binary for the host architecture:
> > 
> > #ifndef _EFI_DEFAULT_FILENAME_H
> > #define _EFI_DEFAULT_FILENAME_H
> > 
> > #include <host_arch.h>
> > 
> > #undef BOOTEFI_NAME
> > 
> > #if HOST_ARCH == HOST_ARCH_X86_64
> > #define BOOTEFI_NAME "BOOTX64.EFI"
> > #endif
> > 
> > #if HOST_ARCH == HOST_ARCH_X86
> > #define BOOTEFI_NAME "BOOTIA32.EFI"
> > #endif
> > 
> > #if HOST_ARCH == HOST_ARCH_AARCH64
> > #define BOOTEFI_NAME "BOOTAA64.EFI"
> > #endif
> > 
> > #if HOST_ARCH == HOST_ARCH_ARM
> > #define BOOTEFI_NAME "BOOTARM.EFI"
> > #endif
> > 
> > #if HOST_ARCH == HOST_ARCH_RISCV32
> > #define BOOTEFI_NAME "BOOTRISCV32.EFI"
> > #endif
> > 
> > #if HOST_ARCH == HOST_ARCH_RISCV64
> > #define BOOTEFI_NAME "BOOTRISCV64.EFI"
> > #endif
> > 
> > #ifndef BOOTEFI_NAME
> > #error Unsupported UEFI architecture
> > #endif
> > 
> > #endif
> 
> Maybe sanbox is special, but using the host architecture for actual
> boards makes no sense.  I see this has made its way into master
> already, but when I cross-build for apple_m1_defconfig on an amd64
> machine I end up with:
> 
>     $ strings ./lib/efi_loader/efi_bootmgr.o | grep BOOT
>     /EFI/BOOT/BOOTX64.EFI
> 
> The original diff that used CONFIG_ARM64, CONFIG_ARM, etc, did this
> right.

Hmm, forget about that.  The problem is in the sed expression in the
toplevel Makefile that is used to MK_ARCH that ends up setting
HOST_ARCH incorrectly.  Using a variable named HOST_ARCH to hold the
target architecture doesn't help understanding what's going wrong here
of course...

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

* Re: [PATCH v5 06/17] efi_loader: bootmgr: add booting from removable media
  2022-05-05 12:35         ` Heinrich Schuchardt
@ 2022-05-05 13:25           ` Mark Kettenis
  0 siblings, 0 replies; 58+ messages in thread
From: Mark Kettenis @ 2022-05-05 13:25 UTC (permalink / raw)
  To: Heinrich Schuchardt
  Cc: masahisa.kojima, ilias.apalodimas, sjg, takahiro.akashi,
	francois.ozog, kettenis, Peter.Hoyes, narmstrong, andre.przywara,
	u-boot

> Date: Thu, 5 May 2022 14:35:36 +0200
> From: Heinrich Schuchardt <xypron.glpk@gmx.de>
> 
> On 5/5/22 14:20, Heinrich Schuchardt wrote:
> > On 5/5/22 14:05, Mark Kettenis wrote:
> >>> Date: Fri, 29 Apr 2022 19:03:22 +0200
> >>> From: Heinrich Schuchardt <xypron.glpk@gmx.de>
> >>>
> >>> On 4/28/22 10:09, Masahisa Kojima wrote:
> >>>> From: AKASHI Takahiro <takahiro.akashi@linaro.org>
> >>>>
> >>>> Under the current implementation, booting from removable media using
> >>>> a architecture-specific default image name, say BOOTAA64.EFI, is
> >>>> supported only in distro_bootcmd script. See the commit 74522c898b35
> >>>> ("efi_loader: Add distro boot script for removable media").
> >>>>
> >>>> This is, however, half-baked implementation because
> >>>> 1) UEFI specification requires this feature to be implemented as part
> >>>>      of Boot Manager's responsibility:
> >>>>
> >>>>     3 - Boot Manager
> >>>>     3.5.1 Boot via the Simple File Protocol
> >>>>     When booting via the EFI_SIMPLE_FILE_SYSTEM_PROTOCOL, the
> >>>> FilePath will
> >>>>     start with a device path that points to the device that
> >>>> implements the
> >>>>     EFI_SIMPLE_FILE_SYSTEM_PROTOCOL or the EFI_BLOCK_IO_PROTOCOL.
> >>>> The next
> >>>>     part of the FilePath may point to the file name, including
> >>>>     subdirectories, which contain the bootable image. If the file
> >>>> name is
> >>>>     a null device path, the file name must be generated from the rules
> >>>>     defined below.
> >>>>     ...
> >>>>     3.5.1.1 Removable Media Boot Behavior
> >>>>     To generate a file name when none is present in the FilePath, the
> >>>>     firmware must append a default file name in the form
> >>>>     \EFI\BOOT\BOOT{machine type short-name}.EFI ...
> >>>>
> >>>> 2) So (1) entails the hehavior that the user's preference of boot media
> >>>>      order should be determined by Boot#### and BootOrder variables.
> >>>>
> >>>> With this patch, the semantics mentioned above is fully implemented.
> >>>> For example, if you want to boot the system from USB and SCSI in this
> >>>> order,
> >>>> * define Boot0001 which contains only a device path to the USB device
> >>>>     (without any file path/name)
> >>>> * define Boot0002 which contains only a device path to the SCSI device,
> >>>> and
> >>>> * set BootOrder to Boot0001:Boot0002
> >>>>
> >>>> To avoid build error for sandbox, default file name "BOOTSANDBOX.efi"
> >>>> is defined even if it is out of scope of UEFI specification.
> >>>>
> >>>> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> >>>> Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
> >>>> ---
> >>>> Changes in v5:
> >>>> - add default file name definition for SANDBOX to avoid build error
> >>>>
> >>>> Changes from original version:
> >>>> - create new include file "efi_default_filename.h" to
> >>>>     avoid conflict with config_distro_bootcmd.h
> >>>> - modify the target pointer of efi_free_pool(), expand_media_path()
> >>>> should
> >>>>     only free the pointer allocated by efi_dp_from_file() function.
> >>>>    include/config_distro_bootcmd.h | 14 +--------
> >>>>    include/efi_default_filename.h  | 33 ++++++++++++++++++++++
> >>>>    lib/efi_loader/efi_bootmgr.c    | 50
> >>>> ++++++++++++++++++++++++++++++++-
> >>>>    3 files changed, 83 insertions(+), 14 deletions(-)
> >>>>    create mode 100644 include/efi_default_filename.h
> >>>>
> >>>> diff --git a/include/config_distro_bootcmd.h
> >>>> b/include/config_distro_bootcmd.h
> >>>> index c55023889c..6a3110f27b 100644
> >>>> --- a/include/config_distro_bootcmd.h
> >>>> +++ b/include/config_distro_bootcmd.h
> >>>> @@ -91,19 +91,7 @@
> >>>>    #endif
> >>>>
> >>>>    #ifdef CONFIG_EFI_LOADER
> >>>> -#if defined(CONFIG_ARM64)
> >>>> -#define BOOTEFI_NAME "bootaa64.efi"
> >>>> -#elif defined(CONFIG_ARM)
> >>>> -#define BOOTEFI_NAME "bootarm.efi"
> >>>> -#elif defined(CONFIG_X86_RUN_32BIT)
> >>>> -#define BOOTEFI_NAME "bootia32.efi"
> >>>> -#elif defined(CONFIG_X86_RUN_64BIT)
> >>>> -#define BOOTEFI_NAME "bootx64.efi"
> >>>> -#elif defined(CONFIG_ARCH_RV32I)
> >>>> -#define BOOTEFI_NAME "bootriscv32.efi"
> >>>> -#elif defined(CONFIG_ARCH_RV64I)
> >>>> -#define BOOTEFI_NAME "bootriscv64.efi"
> >>>> -#endif
> >>>> +#include <efi_default_filename.h>
> >>>>    #endif
> >>>>
> >>>>    #ifdef BOOTEFI_NAME
> >>>> diff --git a/include/efi_default_filename.h
> >>>> b/include/efi_default_filename.h
> >>>> new file mode 100644
> >>>> index 0000000000..cb2ef9e131
> >>>> --- /dev/null
> >>>> +++ b/include/efi_default_filename.h
> >>>> @@ -0,0 +1,33 @@
> >>>> +/* SPDX-License-Identifier: GPL-2.0+ */
> >>>> +/*
> >>>> + * Default boot file name when none is present in the FilePath.
> >>>> + * This is defined in the UEFI specification.
> >>>> + *
> >>>> + * Copyright (c) 2022, Linaro Limited
> >>>> + */
> >>>> +#ifndef _EFI_DEFAULT_FILENAME_H
> >>>> +#define _EFI_DEFAULT_FILENAME_H
> >>>> +
> >>>> +#if defined(CONFIG_ARM64)
> >>>> +#define BOOTEFI_NAME "BOOTAA64.EFI"
> >>>> +#elif defined(CONFIG_ARM)
> >>>> +#define BOOTEFI_NAME "BOOTARM.EFI"
> >>>> +#elif defined(CONFIG_X86_64)
> >>>> +#define BOOTEFI_NAME "BOOTX64.EFI"
> >>>> +#elif defined(CONFIG_X86)
> >>>> +#define BOOTEFI_NAME "BOOTIA32.EFI"
> >>>> +#elif defined(CONFIG_ARCH_RV32I)
> >>>> +#define BOOTEFI_NAME "BOOTRISCV32.EFI"
> >>>> +#elif defined(CONFIG_ARCH_RV64I)
> >>>> +#define BOOTEFI_NAME "BOOTRISCV64.EFI"
> >>>> +#elif defined(CONFIG_SANDBOX)
> >>>> +/*
> >>>> + * SANDBOX is not defined in UEFI specification, but
> >>>> + * this definition avoids build failure for SANDBOX.
> >>>> + */
> >>>> +#define BOOTEFI_NAME "BOOTSANDBOX.EFI"
> >>>
> >>> The sandbox should boot the default binary for the host architecture:
> >>>
> >>> #ifndef _EFI_DEFAULT_FILENAME_H
> >>> #define _EFI_DEFAULT_FILENAME_H
> >>>
> >>> #include <host_arch.h>
> >>>
> >>> #undef BOOTEFI_NAME
> >>>
> >>> #if HOST_ARCH == HOST_ARCH_X86_64
> >>> #define BOOTEFI_NAME "BOOTX64.EFI"
> >>> #endif
> >>>
> >>> #if HOST_ARCH == HOST_ARCH_X86
> >>> #define BOOTEFI_NAME "BOOTIA32.EFI"
> >>> #endif
> >>>
> >>> #if HOST_ARCH == HOST_ARCH_AARCH64
> >>> #define BOOTEFI_NAME "BOOTAA64.EFI"
> >>> #endif
> >>>
> >>> #if HOST_ARCH == HOST_ARCH_ARM
> >>> #define BOOTEFI_NAME "BOOTARM.EFI"
> >>> #endif
> >>>
> >>> #if HOST_ARCH == HOST_ARCH_RISCV32
> >>> #define BOOTEFI_NAME "BOOTRISCV32.EFI"
> >>> #endif
> >>>
> >>> #if HOST_ARCH == HOST_ARCH_RISCV64
> >>> #define BOOTEFI_NAME "BOOTRISCV64.EFI"
> >>> #endif
> >>>
> >>> #ifndef BOOTEFI_NAME
> >>> #error Unsupported UEFI architecture
> >>> #endif
> >>>
> >>> #endif
> >>
> >> Maybe sanbox is special, but using the host architecture for actual
> >> boards makes no sense.  I see this has made its way into master
> >> already, but when I cross-build for apple_m1_defconfig on an amd64
> >> machine I end up with:
> >>
> >>      $ strings ./lib/efi_loader/efi_bootmgr.o | grep BOOT
> >>      /EFI/BOOT/BOOTX64.EFI
> >
> > Thanks for reporting the issue.
> >
> > On Sandbox it should be the host architecture. on other defconfigs the
> > target architecture.
> 
> On Ubuntu 22.04:
> 
> git reset --hard 1739a6db5403d187902dcebca548de0644c8078f
> make apple_m1_defconfig
> CROSS_COMPILE=aarch64-linux-gnu- make -j16
> 
> $ strings ./lib/efi_loader/efi_bootmgr.o | grep BOOT
> /EFI/BOOT/BOOTAA64.EFI
> UCLASS_REBOOT_MODE
> UCLASS_BOOTSTD
> EFI_OBJECT_TYPE_U_BOOT_FIRMWARE
> UCLASS_BOOTDEV
> UCLASS_BOOTMETH
> UCLASS_BOOTCOUN
> 
> $ strings u-boot | grep 'BOOT\S*\.EFI'
> /EFI/BOOT/BOOTAA64.EFI
> 
> How can I reproduce your problem?
> Is this BSD specific? Which distro are you running?

Yes, I'm always building on OpenBSD.  And the probablem is the use of
non-portable GNU sed extensions \s and \S.  Replacing those with
[[:space:]] and [^[:space:]] as in the diff below seems to work.
Decoding chicken scratches is alway fun...

This has been broken for a while, but didn't cause any build issues
until now.  I'll send a proper patch to the mailing list.


diff --git a/Makefile b/Makefile
index ea80f00716..6eceeb35b4 100644
--- a/Makefile
+++ b/Makefile
@@ -21,7 +21,7 @@ include include/host_arch.h
 ifeq ("", "$(CROSS_COMPILE)")
   MK_ARCH="${shell uname -m}"
 else
-  MK_ARCH="${shell echo $(CROSS_COMPILE) | sed -n 's/^\s*\([^\/]*\/\)*\([^-]*\)-\S*/\2/p'}"
+  MK_ARCH="${shell echo $(CROSS_COMPILE) | sed -n 's/^[[:space:]]*\([^\/]*\/\)*\([^-]*\)-[^[:space:]]*/\2/p'}"
 endif
 unexport HOST_ARCH
 ifeq ("x86_64", $(MK_ARCH))

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

* Re: [PATCH v5 13/17] efi_loader: menu-driven addition of UEFI boot option
  2022-04-30 12:49       ` Heinrich Schuchardt
@ 2022-05-06 17:30         ` Heinrich Schuchardt
  2022-05-06 18:10           ` Mark Kettenis
  0 siblings, 1 reply; 58+ messages in thread
From: Heinrich Schuchardt @ 2022-05-06 17:30 UTC (permalink / raw)
  To: Masahisa Kojima
  Cc: Ilias Apalodimas, Simon Glass, Takahiro Akashi, Francois Ozog,
	Mark Kettenis, u-boot

On 4/30/22 14:49, Heinrich Schuchardt wrote:
> On 4/29/22 12:56, Heinrich Schuchardt wrote:
>> On 4/28/22 18:33, Heinrich Schuchardt wrote:
>>> On 4/28/22 10:09, Masahisa Kojima wrote:
>>>> This commit supports the menu-driven UEFI boot option addition.
>>>> User can select the block device volume having
>>>> efi_simple_file_system_protocol and select the file corresponding
>>>> to the Boot#### variable. Then user enter the label of the BOOT####
>>>> variable in utf8.
>>>>
>>>> Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>

This patch creates unexpected behavior:

In the console I entered:

setenv bootmenu_0 foo=echo foo
setenv bootmenu_1 bar=echo bar
bootmenu 20

Nothing here relates to UEFI but the menu shows:

       foo
       bar
       UEFI Boot Manager Maintenance
       Quit

Please, don't show 'UEFI Boot Manager Maintenance' if we are not in the 
boot manager.

I have already merged most of the prior patches in the series. Please, 
consider this when resubmitting.

Best regards

Heinrich

>>>> ---
>>>> Changes in v5:
>>>> - remove forward declarations
>>>> - add const qualifier for menu items
>>>> - fix the possible unaligned access for directory info access
>>>> - split into three commit 1)add boot option 2) delete boot option 
>>>> 3)change boot order
>>>>    This commit is 1)add boot option.
>>>> - fix file name buffer allocation size, it should be 
>>>> EFI_BOOTMENU_FILE_PATH_MAX * sizeof(u16)
>>>> - fix wrong size checking for file selection
>>>>
>>>> Chanes in v4:
>>>> - UEFI boot option maintenance menu is integrated into bootmenu
>>>> - display the simplified volume name(e.g. usb0:1, nvme1:2) for the
>>>>    volume selection
>>>> - instead of extending lib/efi_loader/efi_bootmgr.c, newly create
>>>>    lib/efi_loader/efi_bootmenu_maintenance.c and implement boot
>>>>    variable maintenance into it.
>>>>
>>>> Changes in RFC v3:
>>>>   not included in v3 series
>>>>
>>>> Changes in RFC v2:
>>>> - enable utf8 user input for boot option name
>>>> - create lib/efi_loader/efi_console.c::efi_console_get_u16_string() for
>>>>    utf8 user input handling
>>>> - use u16_strlcat instead of u16_strcat
>>>> - remove the EFI_CALLs, and newly create or expose the following
>>>>    xxx_int() functions.
>>>>      efi_locate_handle_buffer_int(), efi_open_volume_int(),
>>>>      efi_file_open_int(), efi_file_close_int(), efi_file_read_int() and
>>>>      efi_file_setpos_int().
>>>>    Note that EFI_CALLs still exist for EFI_DEVICE_PATH_TO_TEXT_PROTOCOL
>>>>    and EFI_SIMPLE_TEXT_INPUT/OUTPUT_PROTOCOL
>>>> - use efi_search_protocol() instead of calling locate_protocol() to get
>>>>    the device_path_to_text_protocol interface.
>>>> - remove unnecessary puts(ANSI_CLEAR_LINE), this patch is still 
>>>> depends on
>>>>    puts(ANSI_CLEAR_CONSOLE)
>>>> - skip SetVariable() if the bootorder is not changed
>>>>
>>>>   cmd/bootmenu.c                            |  69 +-
>>>>   include/efi_loader.h                      |  37 +
>>>>   lib/efi_loader/Makefile                   |   1 +
>>>>   lib/efi_loader/efi_bootmenu_maintenance.c | 862 
>>>> ++++++++++++++++++++++
>>>>   lib/efi_loader/efi_boottime.c             |  52 +-
>>>>   lib/efi_loader/efi_console.c              |  81 ++
>>>>   lib/efi_loader/efi_disk.c                 |  11 +
>>>>   lib/efi_loader/efi_file.c                 |  75 +-
>>>>   8 files changed, 1133 insertions(+), 55 deletions(-)
>>>>   create mode 100644 lib/efi_loader/efi_bootmenu_maintenance.c
>>>>
>>>> diff --git a/cmd/bootmenu.c b/cmd/bootmenu.c
>>>> index eb23afdd41..860cb83182 100644
>>>> --- a/cmd/bootmenu.c
>>>> +++ b/cmd/bootmenu.c
>>>> @@ -21,6 +21,8 @@
>>>>
>>>>   /* maximum bootmenu entries */
>>>>   #define MAX_COUNT    99
>>>> +#define STATIC_ENTRY 2
>>>> +#define MAX_DYNAMIC_ENTRY (MAX_COUNT - STATIC_ENTRY)
>>>>
>>>>   /* maximal size of bootmenu env
>>>>    *  9 = strlen("bootmenu_")
>>>> @@ -41,10 +43,11 @@ enum boot_type {
>>>>       BOOTMENU_TYPE_BOOTMENU,
>>>>       BOOTMENU_TYPE_UEFI_BOOT_OPTION,
>>>>       BOOTMENU_TYPE_DISTRO_BOOT,
>>>> +    BOOTMENU_TYPE_UEFI_MAINTENANCE,
>>>>   };
>>>>
>>>>   struct bootmenu_entry {
>>>> -    unsigned short int num;        /* unique number 0 .. MAX_COUNT */
>>>> +    unsigned short int num;        /* unique number 0 .. 
>>>> MAX_DYNAMIC_ENTRY */
>>>>       char key[3];            /* key identifier of number */
>>>>       u16 *title;            /* title of entry */
>>>>       char *command;            /* hush command of entry */
>>>> @@ -58,7 +61,7 @@ static char *bootmenu_getoption(unsigned short int n)
>>>>   {
>>>>       char name[MAX_ENV_SIZE];
>>>>
>>>> -    if (n > MAX_COUNT)
>>>> +    if (n > MAX_DYNAMIC_ENTRY)
>>>>           return NULL;
>>>>
>>>>       sprintf(name, "bootmenu_%d", n);
>>>> @@ -229,7 +232,7 @@ static int prepare_bootmenu_entry(struct 
>>>> bootmenu_data *menu,
>>>>           iter = entry;
>>>>           ++i;
>>>>
>>>> -        if (i == MAX_COUNT - 1)
>>>> +        if (i == MAX_DYNAMIC_ENTRY)
>>>>               break;
>>>>       }
>>>>
>>>> @@ -317,7 +320,7 @@ static int prepare_uefi_bootorder_entry(struct 
>>>> bootmenu_data *menu,
>>>>
>>>>           free(load_option);
>>>>
>>>> -        if (i == MAX_COUNT - 1)
>>>> +        if (i == MAX_DYNAMIC_ENTRY)
>>>>               break;
>>>>       }
>>>>
>>>> @@ -481,7 +484,7 @@ static int prepare_distro_boot_entry(struct 
>>>> bootmenu_data *menu,
>>>>           iter = entry;
>>>>           i++;
>>>>
>>>> -        if (i == MAX_COUNT - 1)
>>>> +        if (i == MAX_DYNAMIC_ENTRY)
>>>>               break;
>>>>
>>>>           token = strtok(NULL, " ");
>>>> @@ -520,19 +523,56 @@ static struct bootmenu_data 
>>>> *bootmenu_create(int delay)
>>>>           goto cleanup;
>>>>
>>>>       if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR)) {
>>>> -        if (i < MAX_COUNT - 1) {
>>>> +        if (i < MAX_DYNAMIC_ENTRY) {
>>>>               ret = prepare_uefi_bootorder_entry(menu, &iter, &i);
>>>>               if (ret < 0 && ret != -ENOENT)
>>>>                   goto cleanup;
>>>>           }
>>>>       }
>>>>
>>>> -    if (i < MAX_COUNT - 1) {
>>>> +    if (i < MAX_DYNAMIC_ENTRY) {
>>>>           ret = prepare_distro_boot_entry(menu, &iter, &i);
>>>>           if (ret < 0 && ret != -ENOENT)
>>>>               goto cleanup;
>>>>       }
>>>>
>>>> +    if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR)) {
>>>> +        /* Add UEFI Boot Manager Maintenance entry */
>>>> +        if (i <= MAX_DYNAMIC_ENTRY) {
>>>> +            entry = malloc(sizeof(struct bootmenu_entry));
>>>> +            if (!entry)
>>>> +                goto cleanup;
>>>> +
>>>> +            entry->title = u16_strdup(u"UEFI Boot Manager 
>>>> Maintenance");
>>>> +            if (!entry->title) {
>>>> +                free(entry);
>>>> +                goto cleanup;
>>>> +            }
>>>> +
>>>> +            entry->command = strdup("");
>>>> +            if (!entry->command) {
>>>> +                free(entry->title);
>>>> +                free(entry);
>>>> +                goto cleanup;
>>>> +            }
>>>> +
>>>> +            sprintf(entry->key, "%d", i);
>>>> +
>>>> +            entry->num = i;
>>>> +            entry->menu = menu;
>>>> +            entry->type = BOOTMENU_TYPE_UEFI_MAINTENANCE;
>>>> +            entry->next = NULL;
>>>> +
>>>> +            if (!iter)
>>>> +                menu->first = entry;
>>>> +            else
>>>> +                iter->next = entry;
>>>> +
>>>> +            iter = entry;
>>>> +            i++;
>>>> +        }
>>>> +    }
>>>> +
>>>>       /* Add U-Boot console entry at the end */
>>>>       if (i <= MAX_COUNT - 1) {
>>>>           entry = malloc(sizeof(struct bootmenu_entry));
>>>> @@ -704,6 +744,12 @@ static enum bootmenu_ret bootmenu_show(int delay)
>>>>           title = u16_strdup(iter->title);
>>>>           command = strdup(iter->command);
>>>>
>>>> +        if (iter->type == BOOTMENU_TYPE_UEFI_MAINTENANCE) {
>>>> +            efi_bootmenu_show_maintenance_menu();
>>
>> This does not compile for CONFIG_EFI_LOADER=n.
>> And it is not needed for CONFIG_CMD_BOOTEFI_BOOTMGR=n.
>>
>>>> +            ret = BOOTMENU_RET_UPDATED;
>>>> +            goto cleanup;
>>>> +        }
>>>> +
>>>>           /* last entry is U-Boot console or Quit */
>>>>           if (iter->num == iter->menu->count - 1) {
>>>>               ret = BOOTMENU_RET_QUIT;
>>>> @@ -794,6 +840,7 @@ int do_bootmenu(struct cmd_tbl *cmdtp, int flag, 
>>>> int argc, char *const argv[])
>>>>   {
>>>>       char *delay_str = NULL;
>>>>       int delay = 10;
>>>> +    int ret;
>>>>
>>>>   #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
>>>>       delay = CONFIG_BOOTDELAY;
>>>> @@ -808,7 +855,13 @@ int do_bootmenu(struct cmd_tbl *cmdtp, int 
>>>> flag, int argc, char *const argv[])
>>>>       if (delay_str)
>>>>           delay = (int)simple_strtol(delay_str, NULL, 10);
>>>>
>>>> -    bootmenu_show(delay);
>>>> +    while (1) {
>>>> +        ret =  bootmenu_show(delay);
>>>> +        delay = -1;
>>>> +        if (ret != BOOTMENU_RET_UPDATED)
>>>> +            break;
>>>> +    }
>>>> +
>>>>       return 0;
>>>>   }
>>>>
>>>> diff --git a/include/efi_loader.h b/include/efi_loader.h
>>>> index effb43369d..533618341b 100644
>>>> --- a/include/efi_loader.h
>>>> +++ b/include/efi_loader.h
>>>> @@ -226,6 +226,9 @@ const char *__efi_nesting_dec(void);
>>>>   #define EFI_CACHELINE_SIZE 128
>>>>   #endif
>>>>
>>>> +/* max bootmenu title size for volume selection */
>>>> +#define BOOTMENU_DEVICE_NAME_MAX 16
>>>> +
>>>>   /* Key identifying current memory map */
>>>>   extern efi_uintn_t efi_memory_map_key;
>>>>
>>>> @@ -312,6 +315,9 @@ extern const efi_guid_t 
>>>> efi_guid_firmware_management_protocol;
>>>>   extern const efi_guid_t efi_esrt_guid;
>>>>   /* GUID of the SMBIOS table */
>>>>   extern const efi_guid_t smbios_guid;
>>>> +/*GUID of console */
>>>> +extern const efi_guid_t efi_guid_text_input_protocol;
>>>> +extern const efi_guid_t efi_guid_text_output_protocol;
>>>>
>>>>   extern char __efi_runtime_start[], __efi_runtime_stop[];
>>>>   extern char __efi_runtime_rel_start[], __efi_runtime_rel_stop[];
>>>> @@ -871,6 +877,8 @@ efi_status_t efi_set_load_options(efi_handle_t 
>>>> handle,
>>>>                     void *load_options);
>>>>   efi_status_t efi_bootmgr_load(efi_handle_t *handle, void 
>>>> **load_options);
>>>>
>>>> +efi_status_t efi_bootmenu_show_maintenance_menu(void);
>>>> +
>>>>   /**
>>>>    * struct efi_image_regions - A list of memory regions
>>>>    *
>>>> @@ -1042,4 +1050,33 @@ efi_status_t efi_esrt_populate(void);
>>>>   efi_status_t efi_load_capsule_drivers(void);
>>>>
>>>>   efi_status_t platform_get_eventlog(struct udevice *dev, u64 *addr, 
>>>> u32 *sz);
>>>> +
>>>> +efi_status_t efi_locate_handle_buffer_int(enum 
>>>> efi_locate_search_type search_type,
>>>> +                      const efi_guid_t *protocol, void *search_key,
>>>> +                      efi_uintn_t *no_handles, efi_handle_t **buffer);
>>>> +
>>>> +efi_status_t efi_open_volume_int(struct 
>>>> efi_simple_file_system_protocol *this,
>>>> +                 struct efi_file_handle **root);
>>>> +efi_status_t efi_file_open_int(struct efi_file_handle *this,
>>>> +                   struct efi_file_handle **new_handle,
>>>> +                   u16 *file_name, u64 open_mode,
>>>> +                   u64 attributes);
>>>> +efi_status_t efi_file_close_int(struct efi_file_handle *file);
>>>> +efi_status_t efi_file_read_int(struct efi_file_handle *this,
>>>> +                   efi_uintn_t *buffer_size, void *buffer);
>>>> +efi_status_t efi_file_setpos_int(struct efi_file_handle *file, u64 
>>>> pos);
>>>> +
>>>> +typedef efi_status_t (*efi_console_filter_func)(struct 
>>>> efi_input_key *key);
>>>> +efi_status_t efi_console_get_u16_string
>>>> +        (struct efi_simple_text_input_protocol *cin,
>>>> +         struct efi_simple_text_output_protocol *cout,
>>>> +         u16 *buf, efi_uintn_t count, efi_console_filter_func 
>>>> filer_func,
>>>> +         int row, int col);
>>>> +
>>>> +efi_status_t efi_bootmenu_get_unused_bootoption(u16 *buf,
>>>> +                        efi_uintn_t buf_size, u32 *index);
>>>> +efi_status_t efi_bootmenu_append_bootorder(u16 index);
>>>> +
>>>> +efi_status_t efi_disk_get_device_name(struct efi_block_io *this, 
>>>> char *buf, int size);
>>>> +
>>>>   #endif /* _EFI_LOADER_H */
>>>> diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile
>>>> index aaaa25cefe..792eabe18a 100644
>>>> --- a/lib/efi_loader/Makefile
>>>> +++ b/lib/efi_loader/Makefile
>>>> @@ -77,6 +77,7 @@ obj-$(CONFIG_EFI_TCG2_PROTOCOL) += efi_tcg2.o
>>>>   obj-$(CONFIG_EFI_RISCV_BOOT_PROTOCOL) += efi_riscv.o
>>>>   obj-$(CONFIG_EFI_LOAD_FILE2_INITRD) += efi_load_initrd.o
>>>>   obj-$(CONFIG_EFI_SIGNATURE_SUPPORT) += efi_signature.o
>>>> +obj-$(CONFIG_CMD_BOOTMENU) += efi_bootmenu_maintenance.o
>>
>> Why should we compile this for CONFIG_CMD_BOOTEFI_BOOTMGR=n?
>>
>> Best regards
>>
>> Heinrich
>>
>>>>
>>>>   EFI_VAR_SEED_FILE := $(subst $\",,$(CONFIG_EFI_VAR_SEED_FILE))
>>>>   $(obj)/efi_var_seed.o: $(srctree)/$(EFI_VAR_SEED_FILE)
>>>> diff --git a/lib/efi_loader/efi_bootmenu_maintenance.c 
>>>> b/lib/efi_loader/efi_bootmenu_maintenance.c
>>>> new file mode 100644
>>>> index 0000000000..77401a7829
>>>> --- /dev/null
>>>> +++ b/lib/efi_loader/efi_bootmenu_maintenance.c
>>>> @@ -0,0 +1,862 @@
>>>> +// SPDX-License-Identifier: GPL-2.0+
>>>> +/*
>>>> + *  Menu-driven UEFI Boot Variable maintenance
>>>> + *
>>>> + *  Copyright (c) 2022 Masahisa Kojima, Linaro Limited
>>>> + */
>>>> +
>>>> +#define LOG_CATEGORY LOGC_EFI
>>>> +
>>>> +#include <ansi.h>
>>>> +#include <common.h>
>>>> +#include <charset.h>
>>>> +#include <log.h>
>>>> +#include <malloc.h>
>>>> +#include <menu.h>
>>>> +#include <efi_loader.h>
>>>> +#include <efi_variable.h>
>>>> +#include <asm/unaligned.h>
>>>> +
>>>> +static struct efi_simple_text_input_protocol *cin;
>>>> +static struct efi_simple_text_output_protocol *cout;
>>>> +
>>>> +#define EFI_BOOTMENU_ENTRY_NUM_MAX 99
>>>> +#define EFI_BOOTMENU_FILE_PATH_MAX 512
>>>> +#define EFI_BOOTMENU_FILE_PATH_BUF_SIZE (EFI_BOOTMENU_FILE_PATH_MAX 
>>>> * sizeof(u16))
>>>> +#define EFI_BOOTMENU_BOOT_NAME_MAX 32
>>>> +#define EFI_BOOT_ORDER_MAX_SIZE_IN_DECIMAL 6
>>>> +
>>>> +typedef efi_status_t (*efi_bootmenu_entry_func)(void *data, bool 
>>>> *exit);
>>>> +
>>>> +/**
>>>> + * struct efi_bootmenu_entry - menu entry structure
>>>> + *
>>>> + * @num:        menu entry index
>>>> + * @title:        title of entry
>>>> + * @key:        unique key
>>>> + * @bootmgr_menu:    pointer to the menu structure
>>>> + * @next:        pointer to the next entry
>>>> + * @func:        callback function to be called when this entry is 
>>>> selected
>>>> + * @data:        data to be passed to the callback function
>>>> + */
>>>> +struct efi_bootmenu_entry {
>>>> +    u32 num;
>>>> +    u16 *title;
>>>> +    char key[6];
>>>> +    struct efi_bootmenu *bootmgr_menu;
>>>> +    struct efi_bootmenu_entry *next;
>>>> +    efi_bootmenu_entry_func func;
>>>> +    void *data;
>>>> +};
>>>> +
>>>> +/**
>>>> + * struct efi_bootmenu - bootmgr menu structure
>>>> + *
>>>> + * @delay:    delay for autoboot
>>>> + * @active:    active menu entry index
>>>> + * @count:    total count of menu entry
>>>> + * @first:    pointer to the first menu entry
>>>> + */
>>>> +struct efi_bootmenu {
>>>> +    int delay;
>>>> +    int active;
>>>> +    int count;
>>>> +    struct efi_bootmenu_entry *first;
>>>> +};
>>>> +
>>>> +struct efi_bootmenu_item {
>>>> +    u16 *title;
>>>> +    efi_bootmenu_entry_func func;
>>>> +    void *data;
>>>> +};
>>>> +
>>>> +struct efi_bootmenu_boot_option {
>>>> +    struct efi_simple_file_system_protocol *current_volume;
>>>> +    struct efi_device_path *dp_volume;
>>>> +    u16 *current_path;
>>>> +    u16 *boot_name;
>>>> +    bool file_selected;
>>>> +};
>>>> +
>>>> +static const struct efi_device_path END = {
>>>> +    .type     = DEVICE_PATH_TYPE_END,
>>>> +    .sub_type = DEVICE_PATH_SUB_TYPE_END,
>>>> +    .length   = sizeof(END),
>>>> +};
>>>> +
>>>> +struct efi_bootmenu_volume_entry_data {
>>>> +    struct efi_bootmenu_boot_option *bo;
>>>> +    struct efi_simple_file_system_protocol *v;
>>>> +    struct efi_device_path *dp;
>>>> +};
>>>> +
>>>> +struct efi_bootmenu_file_entry_data {
>>>> +    struct efi_bootmenu_boot_option *bo;
>>>> +    bool is_directory;
>>>> +    u16 *file_name;
>>>> +};
>>>> +
>>>> +static void efi_bootmenu_print_entry(void *data)
>>>> +{
>>>> +    struct efi_bootmenu_entry *entry = data;
>>>> +    int reverse = (entry->bootmgr_menu->active == entry->num);
>>>> +
>>>> +    /* TODO: support scroll or page for many entries */
>>>> +
>>>> +    /*
>>>> +     * Move cursor to line where the entry will be drown 
>>>> (entry->count)
>>>
>>> %s/drown/drawn/
>>>
>>>> +     * First 3 lines contain bootmgr menu header + one empty line
>>>> +     * For the last "Quit" entry, add one empty line
>>>> +     */
>>>> +    if (entry->num == (entry->bootmgr_menu->count - 1))
>>>> +        printf(ANSI_CURSOR_POSITION, entry->num + 5, 1);
>>>> +    else
>>>> +        printf(ANSI_CURSOR_POSITION, entry->num + 4, 1);
>>>> +
>>>> +    puts("     ");
>>>> +
>>>> +    if (reverse)
>>>> +        puts(ANSI_COLOR_REVERSE);
>>>> +
>>>> +    printf("%ls", entry->title);
>>>> +
>>>> +    if (reverse)
>>>> +        puts(ANSI_COLOR_RESET);
>>>> +}
>>>> +
>>>> +static void efi_bootmenu_display_statusline(struct menu *m)
>>>> +{
>>>> +    struct efi_bootmenu_entry *entry;
>>>> +    struct efi_bootmenu *bootmgr_menu;
>>>> +
>>>> +    if (menu_default_choice(m, (void *)&entry) < 0)
>>>> +        return;
>>>> +
>>>> +    bootmgr_menu = entry->bootmgr_menu;
>>>> +
>>>> +    printf(ANSI_CURSOR_POSITION, 1, 1);
>>>> +    puts(ANSI_CLEAR_LINE);
>>>> +    printf(ANSI_CURSOR_POSITION, 2, 1);
>>>> +    puts("  *** U-Boot EFI Boot Manager ***");
>>>> +    puts(ANSI_CLEAR_LINE_TO_END);
>>>> +    printf(ANSI_CURSOR_POSITION, 3, 1);
>>>> +    puts(ANSI_CLEAR_LINE);
>>>> +
>>>> +    /* First 3 lines are bootmgr_menu header + 2 empty lines 
>>>> between entries */
>>>> +    printf(ANSI_CURSOR_POSITION, bootmgr_menu->count + 5, 1);
>>>> +    puts(ANSI_CLEAR_LINE);
>>>> +    printf(ANSI_CURSOR_POSITION, bootmgr_menu->count + 6, 1);
>>>> +    puts("  Press UP/DOWN to move, ENTER to select, ESC/CTRL+C to 
>>>> quit");
>>>> +    puts(ANSI_CLEAR_LINE_TO_END);
>>>> +    printf(ANSI_CURSOR_POSITION, bootmgr_menu->count + 7, 1);
>>>> +    puts(ANSI_CLEAR_LINE);
>>>> +}
>>>> +
>>>> +static char *efi_bootmenu_choice_entry(void *data)
>>>> +{
>>>> +    int i;
>>>> +    int esc = 0;
>>>> +    struct efi_bootmenu_entry *iter;
>>>> +    enum bootmenu_key key = KEY_NONE;
>>>> +    struct efi_bootmenu *bootmgr_menu = data;
>>>> +
>>>> +    while (1) {
>>>> +        if (bootmgr_menu->delay >= 0) {
>>>> +            /* Autoboot was not stopped */
>>>> +            bootmenu_autoboot_loop((struct bootmenu_data 
>>>> *)bootmgr_menu, &key, &esc);
>>>> +        } else {
>>>> +            /* Some key was pressed, so autoboot was stopped */
>>>> +            bootmenu_loop((struct bootmenu_data *)bootmgr_menu, 
>>>> &key, &esc);
>>>> +        }
>>>> +
>>>> +        if (bootmgr_menu->delay == 0)
>>>> +            key = KEY_QUIT;
>>>> +
>>>> +        switch (key) {
>>>> +        case KEY_UP:
>>>> +            if (bootmgr_menu->active > 0)
>>>> +                --bootmgr_menu->active;
>>>> +            /* no menu key selected, regenerate menu */
>>>> +            return NULL;
>>>> +        case KEY_DOWN:
>>>> +            if (bootmgr_menu->active < bootmgr_menu->count - 1)
>>>> +                ++bootmgr_menu->active;
>>>> +            /* no menu key selected, regenerate menu */
>>>> +            return NULL;
>>>> +        case KEY_SELECT:
>>>> +            iter = bootmgr_menu->first;
>>>> +            for (i = 0; i < bootmgr_menu->active; ++i)
>>>> +                iter = iter->next;
>>>> +            return iter->key;
>>>> +        case KEY_QUIT:
>>>> +            /* Quit by choosing the last entry */
>>>> +            iter = bootmgr_menu->first;
>>>> +            while (iter->next)
>>>> +                iter = iter->next;
>>>> +            return iter->key;
>>>> +        default:
>>>> +            break;
>>>> +        }
>>>> +    }
>>>> +
>>>> +    /* never happens */
>>>> +    debug("bootmgr menu: this should not happen");
>>>> +    return NULL;
>>>> +}
>>>> +
>>>> +static void efi_bootmenu_destroy(struct efi_bootmenu *bootmgr_menu)
>>>> +{
>>>> +    struct efi_bootmenu_entry *next;
>>>> +    struct efi_bootmenu_entry *iter = bootmgr_menu->first;
>>>> +
>>>> +    while (iter) {
>>>> +        next = iter->next;
>>>> +        free(iter);
>>>> +        iter = next;
>>>> +    }
>>>> +    free(bootmgr_menu);
>>>> +}
>>>> +
>>>> +/**
>>>> + * efi_bootmenu_process_common() - main handler for uefi bootmgr menu
>>>> + *
>>>> + * Construct the structures required to show the menu, then handle
>>>> + * the user input intracting with u-boot menu functions.
>>>> + *
>>>> + * @items:    pointer to the structure of each menu entry
>>>> + * @count:    the number of menu entry
>>>> + * @delay:    delay for autoboot/autoselect
>>>> + * Return:    status code
>>>> + */
>>>> +static efi_status_t efi_bootmenu_process_common(const struct 
>>>> efi_bootmenu_item *items,
>>>> +                        int count, int delay)
>>>> +{
>>>> +    u32 i;
>>>> +    bool exit = false;
>>>> +    efi_status_t ret;
>>>> +    struct menu *menu;
>>>> +    void *choice = NULL;
>>>> +    struct efi_bootmenu_entry *entry;
>>>> +    struct efi_bootmenu *bootmgr_menu;
>>>> +    struct efi_bootmenu_entry *iter = NULL;
>>>> +
>>>> +    if (count > EFI_BOOTMENU_ENTRY_NUM_MAX)
>>>> +        return EFI_OUT_OF_RESOURCES;
>>>> +
>>>> +    bootmgr_menu = calloc(1, sizeof(struct efi_bootmenu));
>>>> +    if (!bootmgr_menu)
>>>> +        return EFI_OUT_OF_RESOURCES;
>>>> +
>>>> +    bootmgr_menu->delay = delay;
>>>> +    bootmgr_menu->active = 0;
>>>> +    bootmgr_menu->first = NULL;
>>>> +
>>>> +    for (i = 0; i < count; i++) {
>>>> +        entry = calloc(1, sizeof(struct efi_bootmenu_entry));
>>>> +        if (!entry) {
>>>> +            ret = EFI_LOAD_ERROR;
>>>> +            goto out;
>>>> +        }
>>>> +
>>>> +        entry->num = i;
>>>> +        entry->title = items->title;
>>>> +        snprintf(entry->key, sizeof(entry->key), "%04X", i);
>>>> +        entry->bootmgr_menu = bootmgr_menu;
>>>> +        entry->func = items->func;
>>>> +        entry->data = items->data;
>>>> +        entry->next = NULL;
>>>> +
>>>> +        if (!iter)
>>>> +            bootmgr_menu->first = entry;
>>>> +        else
>>>> +            iter->next = entry;
>>>> +
>>>> +        iter = entry;
>>>> +        items++;
>>>> +    }
>>>> +    bootmgr_menu->count = count;
>>>> +
>>>> +    menu = menu_create(NULL, 0, 1, efi_bootmenu_display_statusline,
>>>> +               efi_bootmenu_print_entry, efi_bootmenu_choice_entry,
>>>> +               bootmgr_menu);
>>>> +    if (!menu) {
>>>> +        ret = EFI_INVALID_PARAMETER;
>>>> +        goto out;
>>>> +    }
>>>> +
>>>> +    for (entry = bootmgr_menu->first; entry; entry = entry->next) {
>>>> +        if (!menu_item_add(menu, entry->key, entry)) {
>>>> +            ret = EFI_INVALID_PARAMETER;
>>>> +            goto out;
>>>> +        }
>>>> +    }
>>>> +
>>>> +    menu_default_set(menu, bootmgr_menu->first->key);
>>>> +
>>>> +    while (!exit) {
>>>> +        puts(ANSI_CURSOR_HIDE);
>>>> +        puts(ANSI_CLEAR_CONSOLE);
>>>> +        printf(ANSI_CURSOR_POSITION, 1, 1);
>>>> +
>>>> +        if (menu_get_choice(menu, &choice)) {
>>>> +            entry = choice;
>>>> +            if (entry->func)
>>>> +                ret = entry->func(entry->data, &exit);
>>>> +
>>>> +            /* last entry "Quit" is selected, exit this menu */
>>>> +            if (entry->num == (entry->bootmgr_menu->count - 1)) {
>>>> +                ret = EFI_ABORTED;
>>>> +                break;
>>>> +            }
>>>> +        }
>>>> +    }
>>>> +
>>>> +out:
>>>> +    menu_destroy(menu);
>>>> +    efi_bootmenu_destroy(bootmgr_menu);
>>>> +
>>>> +    puts(ANSI_CLEAR_CONSOLE);
>>>> +    printf(ANSI_CURSOR_POSITION, 1, 1);
>>>> +    puts(ANSI_CURSOR_SHOW);
>>>> +
>>>> +    return ret;
>>>> +}
>>>> +
>>>> +static efi_status_t efi_bootmenu_volume_selected(void *data, bool 
>>>> *exit)
>>>> +{
>>>> +    struct efi_bootmenu_volume_entry_data *info = data;
>>>> +
>>>> +    *exit = true;
>>>> +
>>>> +    if (info) {
>>>> +        info->bo->current_volume = info->v;
>>>> +        info->bo->dp_volume = info->dp;
>>>> +    }
>>>> +
>>>> +    return EFI_SUCCESS;
>>>> +}
>>>> +
>>>> +static efi_status_t efi_bootmenu_file_selected(void *data, bool *exit)
>>>> +{
>>>> +    struct efi_bootmenu_file_entry_data *info = data;
>>>> +
>>>> +    *exit = true;
>>>> +
>>>> +    if (!info)
>>>> +        return EFI_INVALID_PARAMETER;
>>>> +
>>>> +    if (u16_strncmp(info->file_name, u".", 1) == 0 &&
>>>> +        u16_strlen(info->file_name) == 1) {
>>>> +        /* stay current path */
>>>> +    } else if (u16_strncmp(info->file_name, u"..", 2) == 0 &&
>>>> +           u16_strlen(info->file_name) == 2) {
> 
> u16_strcmp(info->file_name, u"..") will yield the same result with less 
> effort.
> 
>>>> +        u32 i;
>>>> +        int len = u16_strlen(info->bo->current_path);
>>>> +
>>>> +        for (i = len - 2; i > 0; i--) {
>>>> +            if (info->bo->current_path[i] == u'\\')
> 
> "a\b.efi" is a valid file name on ext4. How do we handle it?
> Do we ensure that it is not selected?
> Or would we just ignore this border case and accept unexpected behavior?
> 
>>>> +                break;
>>>> +        }
>>>> +
>>>> +        if (i == 0)
>>>> +            info->bo->current_path[0] = u'\0';
>>>> +        else
>>>> +            info->bo->current_path[i + 1] = u'\0';
>>>> +    } else {
>>>> +        size_t new_len;
>>>> +
>>>> +        new_len = u16_strlen(info->bo->current_path) +
>>>> +                     u16_strlen(info->file_name) + 1;
>>>> +        if (new_len > EFI_BOOTMENU_FILE_PATH_MAX) {
> 
> %s/>/>=/ and save the + 1 above.
> 
>>>> +            /* TODO: show error notification to user */
>>>> +            log_err("file path is too long\n");
>>>> +            return EFI_INVALID_PARAMETER;
>>>> +        }
>>>> +        u16_strlcat(info->bo->current_path, info->file_name,
>>>> +                EFI_BOOTMENU_FILE_PATH_MAX);
>>>> +        if (info->is_directory) {
>>>> +            /*
>>>> +             * Remainig buffer should have enough space to contain 
>>>> u"\\" and
>>>> +             * at least one character for file name
>>>> +             */
>>>> +            if (new_len + 2 > EFI_BOOTMENU_FILE_PATH_MAX) {
> 
> %s/>/>=/
> 
> Best regards
> 
> Heinrich
> 
>>>> +                log_err("directory path is too long\n");
>>>> +                return EFI_INVALID_PARAMETER;
>>>> +            }
>>>> +            u16_strlcat(info->bo->current_path, u"\\",
>>>> +                    EFI_BOOTMENU_FILE_PATH_MAX);
>>>> +        } else {
>>>> +            info->bo->file_selected = true;
>>>> +        }
>>>> +    }
>>>> +    return EFI_SUCCESS;
>>>> +}
>>>> +
>>>> +static efi_status_t efi_bootmenu_select_volume(struct 
>>>> efi_bootmenu_boot_option *bo)
>>>> +{
>>>> +    u32 i;
>>>> +    efi_status_t ret;
>>>> +    efi_uintn_t count;
>>>> +    struct efi_handler *handler;
>>>> +    struct efi_device_path *device_path;
>>>> +    efi_handle_t *volume_handles = NULL;
>>>> +    struct efi_simple_file_system_protocol *v;
>>>> +    struct efi_bootmenu_item *menu_item, *iter;
>>>> +
>>>> +    ret = efi_locate_handle_buffer_int(BY_PROTOCOL, 
>>>> &efi_simple_file_system_protocol_guid,
>>>> +                       NULL, &count, (efi_handle_t 
>>>> **)&volume_handles);
>>>> +    if (ret != EFI_SUCCESS)
>>>> +        return ret;
>>>> +
>>>> +    menu_item = calloc(count + 1, sizeof(struct efi_bootmenu_item));
>>>> +    if (!menu_item) {
>>>> +        ret = EFI_OUT_OF_RESOURCES;
>>>> +        goto out1;
>>>> +    }
>>>> +
>>>> +    iter = menu_item;
>>>> +    for (i = 0; i < count; i++) {
>>>> +        u16 *dev_name, *p;
>>>> +        struct efi_block_io *block_io;
>>>> +        char buf[BOOTMENU_DEVICE_NAME_MAX];
>>>> +        struct efi_bootmenu_volume_entry_data *info;
>>>> +
>>>> +        ret = efi_search_protocol(volume_handles[i],
>>>> +                      &efi_simple_file_system_protocol_guid, 
>>>> &handler);
>>>> +        if (ret != EFI_SUCCESS)
>>>> +            continue;
>>>> +        ret = efi_protocol_open(handler, (void **)&v, efi_root, NULL,
>>>> +                    EFI_OPEN_PROTOCOL_GET_PROTOCOL);
>>>> +        if (ret != EFI_SUCCESS)
>>>> +            continue;
>>>> +
>>>> +        ret = efi_search_protocol(volume_handles[i], 
>>>> &efi_guid_device_path, &handler);
>>>> +        if (ret != EFI_SUCCESS)
>>>> +            continue;
>>>> +        ret = efi_protocol_open(handler, (void **)&device_path,
>>>> +                    efi_root, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
>>>> +        if (ret != EFI_SUCCESS)
>>>> +            continue;
>>>> +
>>>> +        ret = efi_search_protocol(volume_handles[i], 
>>>> &efi_block_io_guid, &handler);
>>>> +        if (ret != EFI_SUCCESS)
>>>> +            continue;
>>>> +        ret = efi_protocol_open(handler, (void **)&block_io,
>>>> +                    efi_root, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
>>>> +        if (ret != EFI_SUCCESS)
>>>> +            continue;
>>>> +
>>>> +        info = calloc(1, sizeof(struct 
>>>> efi_bootmenu_volume_entry_data));
>>>> +        if (!info) {
>>>> +            ret = EFI_OUT_OF_RESOURCES;
>>>> +            goto out2;
>>>> +        }
>>>> +
>>>> +        efi_disk_get_device_name(block_io, buf, 
>>>> BOOTMENU_DEVICE_NAME_MAX);
>>>> +        dev_name = calloc(1, (strlen(buf) + 1) * sizeof(u16));
>>>> +        if (!dev_name) {
>>>> +            free(info);
>>>> +            ret = EFI_OUT_OF_RESOURCES;
>>>> +            goto out2;
>>>> +        }
>>>> +        p = dev_name;
>>>> +        utf8_utf16_strncpy(&p, buf, strlen(buf));
>>>> +
>>>> +        info->v = v;
>>>> +        info->dp = device_path;
>>>> +        info->bo = bo;
>>>> +        iter->title = dev_name;
>>>> +        iter->func = efi_bootmenu_volume_selected;
>>>> +        iter->data = info;
>>>> +        iter++;
>>>> +    }
>>>> +
>>>> +    iter->title = u16_strdup(u"Quit");
>>>> +    iter->func = NULL;
>>>> +    iter->data = NULL;
>>>> +    count += 1;
>>>> +
>>>> +    ret = efi_bootmenu_process_common(menu_item, count, -1);
>>>> +
>>>> +out2:
>>>> +    iter = menu_item;
>>>> +    for (i = 0; i < count; i++) {
>>>> +        struct efi_bootmenu_volume_entry_data *p;
>>>> +
>>>> +        p = (struct efi_bootmenu_volume_entry_data *)(iter->data);
>>>> +        free(iter->title);
>>>> +        free(p);
>>>> +        iter++;
>>>> +    }
>>>> +
>>>> +    free(menu_item);
>>>> +
>>>> +out1:
>>>> +    efi_free_pool(volume_handles);
>>>> +
>>>> +    return ret;
>>>> +}
>>>> +
>>>> +static efi_status_t efi_bootmenu_select_file(struct 
>>>> efi_bootmenu_boot_option *bo,
>>>> +                         struct efi_file_handle *root)
>>>> +{
>>>> +    u32 i;
>>>> +    struct efi_file_info *buf;
>>>> +    u32 count = 0;
>>>> +    efi_uintn_t len;
>>>> +    efi_status_t ret;
>>>> +    struct efi_file_handle *f;
>>>> +    struct efi_bootmenu_item *menu_item, *iter;
>>>> +
>>>> +    buf = calloc(1, sizeof(struct efi_file_info) + 
>>>> EFI_BOOTMENU_FILE_PATH_BUF_SIZE);
>>>> +    if (!buf)
>>>> +        return EFI_OUT_OF_RESOURCES;
>>>> +
>>>> +    while (!bo->file_selected) {
>>>> +        count = 0;
>>>> +
>>>> +        ret = efi_file_open_int(root, &f, bo->current_path, 
>>>> EFI_FILE_MODE_READ, 0);
>>>> +        if (ret != EFI_SUCCESS)
>>>> +            return ret;
>>>> +
>>>> +        /* calculate directory information total count */
>>>> +        for (;;) {
>>>> +            len = sizeof(struct efi_file_info) + 
>>>> EFI_BOOTMENU_FILE_PATH_BUF_SIZE;
>>>> +            ret = efi_file_read_int(f, &len, buf);
>>>> +            if (ret != EFI_SUCCESS || len == 0)
>>>> +                break;
>>>> +
>>>> +            count++;
>>>> +        }
>>>> +
>>>> +        menu_item = calloc(count + 1, sizeof(struct 
>>>> efi_bootmenu_item));
>>>> +        if (!menu_item) {
>>>> +            efi_file_close_int(f);
>>>> +            ret = EFI_OUT_OF_RESOURCES;
>>>> +            goto out;
>>>> +        }
>>>> +
>>>> +        /* read directory and construct menu structure */
>>>> +        efi_file_setpos_int(f, 0);
>>>> +        iter = menu_item;
>>>> +        for (i = 0; i < count; i++) {
>>>> +            u16 *name;
>>>> +            int name_len;
>>>> +            struct efi_bootmenu_file_entry_data *info;
>>>> +
>>>> +            len = sizeof(struct efi_file_info) + 
>>>> EFI_BOOTMENU_FILE_PATH_BUF_SIZE;
>>>> +            ret = efi_file_read_int(f, &len, buf);
>>>> +            if (ret != EFI_SUCCESS || len == 0)
>>>> +                goto err;
>>>> +
>>>> +            info = calloc(1, sizeof(struct 
>>>> efi_bootmenu_file_entry_data));
>>>> +            if (!info) {
>>>> +                ret = EFI_OUT_OF_RESOURCES;
>>>> +                goto err;
>>>> +            }
>>>> +
>>>> +            if (buf->attribute & EFI_FILE_DIRECTORY) {
>>>> +                /* append u'/' at the end of directory name */
>>>> +                name_len = u16_strsize(buf->file_name) + sizeof(u16);
>>>> +                name = calloc(1, name_len);
>>>> +                if (!name) {
>>>> +                    ret = EFI_OUT_OF_RESOURCES;
>>>> +                    goto err;
>>>> +                }
>>>> +                u16_strcpy(name, buf->file_name);
>>>> +                name[u16_strlen(buf->file_name)] = u'/';
>>>> +
>>>> +                info->is_directory = true;
>>>> +            } else {
>>>> +                name_len = u16_strsize(buf->file_name);
>>>> +                name = calloc(1, name_len);
>>>> +                if (!name) {
>>>> +                    ret = EFI_OUT_OF_RESOURCES;
>>>> +                    goto err;
>>>> +                }
>>>> +                u16_strcpy(name, buf->file_name);
>>>> +            }
>>>> +
>>>> +            info->file_name = u16_strdup(buf->file_name);
>>>> +            info->bo = bo;
>>>> +            iter->title = name;
>>>> +            iter->func = efi_bootmenu_file_selected;
>>>> +            iter->data = info;
>>>> +            iter++;
>>>> +        }
>>>> +
>>>> +        /* add "Quit" entry */
>>>> +        iter->title = u"Quit";
>>>> +        iter->func = NULL;
>>>> +        iter->data = NULL;
>>>> +        count += 1;
>>>> +
>>>> +        ret = efi_bootmenu_process_common(menu_item, count, -1);
>>>> +err:
>>>> +        efi_file_close_int(f);
>>>> +        iter = menu_item;
>>>> +        for (i = 0; i < count - 1; i++, iter++) {
>>>> +            free(((struct efi_bootmenu_file_entry_data 
>>>> *)(iter->data))->file_name);
>>>> +            free(iter->title);
>>>> +            free(iter->data);
>>>> +        }
>>>> +
>>>> +        free(menu_item);
>>>> +
>>>> +        if (ret != EFI_SUCCESS)
>>>> +            break;
>>>> +    }
>>>> +
>>>> +out:
>>>> +    free(buf);
>>>> +    return ret;
>>>> +}
>>>> +
>>>> +static efi_status_t efi_bootmenu_boot_add_enter_name(struct 
>>>> efi_bootmenu_boot_option *bo)
>>>> +{
>>>> +    efi_status_t ret;
>>>> +
>>>> +    printf(ANSI_CURSOR_POSITION, 2, 1);
>>>> +    puts("  *** U-Boot EFI Boot Manager Menu ***");
>>>> +    printf(ANSI_CURSOR_POSITION, 4, 1);
>>>> +    puts("  enter name:");
>>>> +
>>>> +    printf(ANSI_CURSOR_POSITION, 8, 1);
>>>> +    puts("  ENTER to complete, ESC/CTRL+C to quit");
>>>> +
>>>> +    ret = efi_console_get_u16_string(cin, cout, bo->boot_name,
>>>> +                     EFI_BOOTMENU_BOOT_NAME_MAX, NULL, 4, 15);
>>>> +
>>>> +    return ret;
>>>> +}
>>>> +
>>>> +static efi_status_t efi_bootmenu_select_file_handler(struct 
>>>> efi_bootmenu_boot_option *bo)
>>>> +{
>>>> +    efi_status_t ret;
>>>> +    struct efi_file_handle *root;
>>>> +
>>>> +    bo->file_selected = false;
>>>> +
>>>> +    while (!bo->file_selected) {
>>>> +        bo->current_volume = NULL;
>>>> +        memset(bo->current_path, 0, sizeof(bo->current_path));
>>>> +
>>>> +        ret = efi_bootmenu_select_volume(bo);
>>>> +        if (ret != EFI_SUCCESS)
>>>> +            return ret;
>>>> +
>>>> +        if (!bo->current_volume)
>>>> +            return EFI_INVALID_PARAMETER;
>>>> +
>>>> +        ret = efi_open_volume_int(bo->current_volume, &root);
>>>> +        if (ret != EFI_SUCCESS)
>>>> +            return ret;
>>>> +
>>>> +        ret = efi_bootmenu_select_file(bo, root);
>>>> +        if (ret != EFI_SUCCESS)
>>>> +            return ret;
>>>> +    }
>>>> +
>>>> +    ret = efi_bootmenu_boot_add_enter_name(bo);
>>>> +
>>>> +    return ret;
>>>> +}
>>>> +
>>>> +efi_status_t efi_bootmenu_get_unused_bootoption(u16 *buf,
>>>> +                        efi_uintn_t buf_size, u32 *index)
>>>> +{
>>>> +    u32 i;
>>>> +    efi_status_t ret;
>>>> +    efi_uintn_t size;
>>>> +
>>>> +    if (buf_size < u16_strsize(u"Boot####"))
>>>> +        return EFI_BUFFER_TOO_SMALL;
>>>> +
>>>> +    for (i = 0; i <= 0xFFFF; i++) {
>>>> +        size = 0;
>>>> +        efi_create_indexed_name(buf, buf_size, "Boot", i);
>>>> +        ret = efi_get_variable_int(buf, &efi_global_variable_guid,
>>>> +                       NULL, &size, NULL, NULL);
>>>> +        if (ret == EFI_BUFFER_TOO_SMALL)
>>>> +            continue;
>>>> +        else
>>>> +            break;
>>>> +    }
>>>> +
>>>> +    if (i > 0xFFFF)
>>>> +        return EFI_OUT_OF_RESOURCES;
>>>> +
>>>> +    *index = i;
>>>> +
>>>> +    return EFI_SUCCESS;
>>>> +}
>>>> +
>>>> +static efi_status_t efi_bootmenu_set_boot_option(u16 *var_name, 
>>>> struct efi_device_path *dp,
>>>> +                         u16 *label, char *optional_data)
>>>> +{
>>>> +    void *p = NULL;
>>>> +    efi_status_t ret;
>>>> +    efi_uintn_t size;
>>>> +    struct efi_load_option lo;
>>>> +
>>>> +    lo.file_path = dp;
>>>> +    lo.file_path_length = efi_dp_size(dp) + sizeof(END);
>>>> +    lo.attributes = LOAD_OPTION_ACTIVE;
>>>> +    lo.optional_data = optional_data;
>>>> +    lo.label = label;
>>>> +
>>>> +    size = efi_serialize_load_option(&lo, (u8 **)&p);
>>>> +    if (!size)
>>>> +        return EFI_INVALID_PARAMETER;
>>>> +
>>>> +    ret = efi_set_variable_int(var_name, &efi_global_variable_guid,
>>>> +                   EFI_VARIABLE_NON_VOLATILE |
>>>> +                   EFI_VARIABLE_BOOTSERVICE_ACCESS |
>>>> +                   EFI_VARIABLE_RUNTIME_ACCESS,
>>>> +                   size, p, false);
>>>> +    free(p);
>>>> +
>>>> +    return ret;
>>>> +}
>>>> +
>>>> +efi_status_t efi_bootmenu_append_bootorder(u16 index)
>>>> +{
>>>> +    u16 *bootorder;
>>>> +    efi_status_t ret;
>>>> +    u16 *new_bootorder = NULL;
>>>> +    efi_uintn_t last, size, new_size;
>>>> +
>>>> +    /* append new boot option */
>>>> +    bootorder = efi_get_var(u"BootOrder", 
>>>> &efi_global_variable_guid, &size);
>>>> +    last = size / sizeof(u16);
>>>> +    new_size = size + sizeof(u16);
>>>> +    new_bootorder = calloc(1, new_size);
>>>> +    if (!new_bootorder) {
>>>> +        ret = EFI_OUT_OF_RESOURCES;
>>>> +        goto out;
>>>> +    }
>>>> +    memcpy(new_bootorder, bootorder, size);
>>>> +    new_bootorder[last] = index;
>>>> +
>>>> +    ret = efi_set_variable_int(u"BootOrder", 
>>>> &efi_global_variable_guid,
>>>> +                   EFI_VARIABLE_NON_VOLATILE |
>>>> +                   EFI_VARIABLE_BOOTSERVICE_ACCESS |
>>>> +                   EFI_VARIABLE_RUNTIME_ACCESS,
>>>> +                   new_size, new_bootorder, false);
>>>> +    if (ret != EFI_SUCCESS)
>>>> +        goto out;
>>>> +
>>>> +out:
>>>> +    free(bootorder);
>>>> +    free(new_bootorder);
>>>> +
>>>> +    return ret;
>>>> +}
>>>> +
>>>> +static efi_status_t efi_bootmenu_process_add_boot_option(void 
>>>> *data, bool *exit)
>>>> +{
>>>> +    u32 index;
>>>> +    u16 var_name[9];
>>>> +    char *buf = NULL;
>>>> +    efi_status_t ret;
>>>> +    char *iter = NULL;
>>>> +    efi_uintn_t dp_size, fp_size;
>>>> +    struct efi_bootmenu_boot_option bo;
>>>> +    struct efi_device_path_file_path *fp;
>>>> +
>>>> +    ret = efi_bootmenu_get_unused_bootoption(var_name, 
>>>> sizeof(var_name),
>>>> +                         &index);
>>>> +    if (ret != EFI_SUCCESS)
>>>> +        return ret;
>>>> +
>>>> +    bo.current_path = calloc(1, EFI_BOOTMENU_FILE_PATH_BUF_SIZE);
>>>> +    if (!bo.current_path)
>>>> +        goto out;
>>>> +
>>>> +    bo.boot_name = calloc(1, EFI_BOOTMENU_BOOT_NAME_MAX * 
>>>> sizeof(u16));
>>>> +    if (!bo.boot_name)
>>>> +        goto out;
>>>> +
>>>> +    ret = efi_bootmenu_select_file_handler(&bo);
>>>> +    if (ret != EFI_SUCCESS)
>>>> +        goto out;
>>>> +
>>>> +    dp_size = efi_dp_size(bo.dp_volume);
>>>> +    fp_size = sizeof(struct efi_device_path) +
>>>> +          ((u16_strlen(bo.current_path) + 1) * sizeof(u16));
>>>> +    buf = calloc(1, dp_size + fp_size + sizeof(END));
>>>> +    if (!buf)
>>>> +        goto out;
>>>> +
>>>> +    iter = buf;
>>>> +    memcpy(iter, bo.dp_volume, dp_size);
>>>> +    iter += dp_size;
>>>> +
>>>> +    fp = (struct efi_device_path_file_path *)iter;
>>>> +    fp->dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE;
>>>> +    fp->dp.sub_type = DEVICE_PATH_SUB_TYPE_FILE_PATH;
>>>> +    fp->dp.length = (u16)fp_size;
>>>> +    u16_strcpy(fp->str, bo.current_path);
>>>> +    iter += fp_size;
>>>> +    *((struct efi_device_path *)iter) = END;
>>>> +
>>>> +    ret = efi_bootmenu_set_boot_option(var_name, (struct 
>>>> efi_device_path *)buf,
>>>> +                       bo.boot_name, NULL);
>>>> +    if (ret != EFI_SUCCESS)
>>>> +        goto out;
>>>> +
>>>> +    efi_bootmenu_append_bootorder((u16)index);
>>>> +    if (ret != EFI_SUCCESS)
>>>> +        goto out;
>>>> +
>>>> +out:
>>>> +    free(buf);
>>>> +    free(bo.boot_name);
>>>> +    free(bo.current_path);
>>>> +
>>>> +    return ret;
>>>> +}
>>>> +
>>>> +static efi_status_t efi_bootmenu_init(void)
>>>> +{
>>>> +    efi_status_t ret;
>>>> +    struct efi_handler *handler;
>>>> +
>>>> +    ret = efi_search_protocol(efi_root, 
>>>> &efi_guid_text_input_protocol, &handler);
>>>> +    if (ret != EFI_SUCCESS)
>>>> +        return ret;
>>>> +
>>>> +    ret = efi_protocol_open(handler, (void **)&cin, efi_root, NULL,
>>>> +                EFI_OPEN_PROTOCOL_GET_PROTOCOL);
>>>> +    if (ret != EFI_SUCCESS)
>>>> +        return ret;
>>>> +
>>>> +    ret = efi_search_protocol(efi_root, 
>>>> &efi_guid_text_output_protocol, &handler);
>>>> +    if (ret != EFI_SUCCESS)
>>>> +        return ret;
>>>> +
>>>> +    ret = efi_protocol_open(handler, (void **)&cout, efi_root, NULL,
>>>> +                EFI_OPEN_PROTOCOL_GET_PROTOCOL);
>>>> +    if (ret != EFI_SUCCESS)
>>>> +        return ret;
>>>> +
>>>> +    return ret;
>>>> +}
>>>> +
>>>> +static const struct efi_bootmenu_item maintenance_menu_items[] = {
>>>> +    {u"Add Boot Option", efi_bootmenu_process_add_boot_option},
>>>> +    {u"Quit", NULL},
>>>> +};
>>>> +
>>>> +efi_status_t efi_bootmenu_show_maintenance_menu(void)
>>>> +{
>>>> +    efi_status_t ret;
>>>> +
>>>> +    ret = efi_bootmenu_init();
>>>> +    if (ret != EFI_SUCCESS)
>>>> +        return ret;
>>>> +
>>>> +    return efi_bootmenu_process_common(maintenance_menu_items,
>>>> +                      ARRAY_SIZE(maintenance_menu_items),
>>>> +                      -1);
>>>> +}
>>>> diff --git a/lib/efi_loader/efi_boottime.c 
>>>> b/lib/efi_loader/efi_boottime.c
>>>> index 4da64b5d29..1233418e77 100644
>>>> --- a/lib/efi_loader/efi_boottime.c
>>>> +++ b/lib/efi_loader/efi_boottime.c
>>>> @@ -2453,6 +2453,35 @@ static efi_status_t EFIAPI 
>>>> efi_protocols_per_handle(
>>>>       return EFI_EXIT(EFI_SUCCESS);
>>>>   }
>>>>
>>>> +efi_status_t efi_locate_handle_buffer_int(enum 
>>>> efi_locate_search_type search_type,
>>>> +                      const efi_guid_t *protocol, void *search_key,
>>>> +                      efi_uintn_t *no_handles, efi_handle_t **buffer)
>>>> +{
>>>> +    efi_status_t r;
>>>> +    efi_uintn_t buffer_size = 0;
>>>> +
>>>> +    if (!no_handles || !buffer) {
>>>> +        r = EFI_INVALID_PARAMETER;
>>>> +        goto out;
>>>> +    }
>>>> +    *no_handles = 0;
>>>> +    *buffer = NULL;
>>>> +    r = efi_locate_handle(search_type, protocol, search_key, 
>>>> &buffer_size,
>>>> +                  *buffer);
>>>> +    if (r != EFI_BUFFER_TOO_SMALL)
>>>> +        goto out;
>>>> +    r = efi_allocate_pool(EFI_BOOT_SERVICES_DATA, buffer_size,
>>>> +                  (void **)buffer);
>>>> +    if (r != EFI_SUCCESS)
>>>> +        goto out;
>>>> +    r = efi_locate_handle(search_type, protocol, search_key, 
>>>> &buffer_size,
>>>> +                  *buffer);
>>>> +    if (r == EFI_SUCCESS)
>>>> +        *no_handles = buffer_size / sizeof(efi_handle_t);
>>>> +out:
>>>> +    return r;
>>>> +}
>>>> +
>>>>   /**
>>>>    * efi_locate_handle_buffer() - locate handles implementing a 
>>>> protocol
>>>>    * @search_type: selection criterion
>>>> @@ -2474,30 +2503,13 @@ efi_status_t EFIAPI efi_locate_handle_buffer(
>>>>               efi_uintn_t *no_handles, efi_handle_t **buffer)
>>>>   {
>>>>       efi_status_t r;
>>>> -    efi_uintn_t buffer_size = 0;
>>>>
>>>>       EFI_ENTRY("%d, %pUs, %p, %p, %p", search_type, protocol, 
>>>> search_key,
>>>>             no_handles, buffer);
>>>>
>>>> -    if (!no_handles || !buffer) {
>>>> -        r = EFI_INVALID_PARAMETER;
>>>> -        goto out;
>>>> -    }
>>>> -    *no_handles = 0;
>>>> -    *buffer = NULL;
>>>> -    r = efi_locate_handle(search_type, protocol, search_key, 
>>>> &buffer_size,
>>>> -                  *buffer);
>>>> -    if (r != EFI_BUFFER_TOO_SMALL)
>>>> -        goto out;
>>>> -    r = efi_allocate_pool(EFI_BOOT_SERVICES_DATA, buffer_size,
>>>> -                  (void **)buffer);
>>>> -    if (r != EFI_SUCCESS)
>>>> -        goto out;
>>>> -    r = efi_locate_handle(search_type, protocol, search_key, 
>>>> &buffer_size,
>>>> -                  *buffer);
>>>> -    if (r == EFI_SUCCESS)
>>>> -        *no_handles = buffer_size / sizeof(efi_handle_t);
>>>> -out:
>>>> +    r = efi_locate_handle_buffer_int(search_type, protocol, 
>>>> search_key,
>>>> +                     no_handles, buffer);
>>>> +
>>>>       return EFI_EXIT(r);
>>>>   }
>>>>
>>>> diff --git a/lib/efi_loader/efi_console.c 
>>>> b/lib/efi_loader/efi_console.c
>>>> index ba68a15017..f5002e1c99 100644
>>>> --- a/lib/efi_loader/efi_console.c
>>>> +++ b/lib/efi_loader/efi_console.c
>>>> @@ -5,6 +5,7 @@
>>>>    *  Copyright (c) 2016 Alexander Graf
>>>>    */
>>>>
>>>> +#include <ansi.h>
>>>>   #include <common.h>
>>>>   #include <charset.h>
>>>>   #include <malloc.h>
>>>> @@ -1312,3 +1313,83 @@ out_of_memory:
>>>>       printf("ERROR: Out of memory\n");
>>>>       return r;
>>>>   }
>>>> +
>>>> +/**
>>>> + * efi_console_get_u16_string() - get user input string
>>>> + *
>>>> + * @cin:        protocol interface to EFI_SIMPLE_TEXT_INPUT_PROTOCOL
>>>> + * @cout:        protocol interface to EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
>>>> + * @buf:        buffer to store user input string in UTF16
>>>> + * @size:        buffer size including NULL terminator
>>>> + * @filter_func:    callback to filter user input
>>>> + * @row:        row number to locate user input form
>>>> + * @col:        column number to locate user input form
>>>> + * Return:        status code
>>>> + */
>>>> +efi_status_t efi_console_get_u16_string(struct 
>>>> efi_simple_text_input_protocol *cin,
>>>> +                    struct efi_simple_text_output_protocol *cout,
>>>> +                    u16 *buf, efi_uintn_t size,
>>>> +                    efi_console_filter_func filter_func,
>>>> +                    int row, int col)
>>>> +{
>>>> +    efi_status_t ret;
>>>> +    efi_uintn_t len = 0;
>>>> +    struct efi_input_key key;
>>>> +
>>>> +    printf(ANSI_CURSOR_POSITION, row, col);
>>>> +    puts(ANSI_CLEAR_LINE_TO_END);
>>>> +    puts(ANSI_CURSOR_SHOW);
>>>> +
>>>> +    ret = EFI_CALL(cin->reset(cin, false));
>>>> +    if (ret != EFI_SUCCESS)
>>>> +        return ret;
>>>> +
>>>> +    for (;;) {
>>>> +        do {
>>>> +            ret = EFI_CALL(cin->read_key_stroke(cin, &key));
>>>> +            mdelay(10);
>>>> +        } while (ret == EFI_NOT_READY);
>>>> +
>>>> +        if (key.unicode_char == u'\b') {
>>>> +            if (len > 0)
>>>> +                buf[--len] = u'\0';
>>>> +
>>>> +            printf(ANSI_CURSOR_POSITION, row, col);
>>>> +            ret = EFI_CALL(cout->output_string(cout, buf));
>>>> +            if (ret != EFI_SUCCESS)
>>>> +                return ret;
>>>> +
>>>> +            puts(ANSI_CLEAR_LINE_TO_END);
>>>> +            continue;
>>>> +        } else if (key.unicode_char == u'\r') {
>>>> +            if (len == 0) /* no user input */
>>>> +                continue;
>>>> +
>>>> +            buf[len] = u'\0';
>>>> +            return EFI_SUCCESS;
>>>> +        } else if (key.unicode_char == 0x3 || key.scan_code == 23) {
>>>> +            return EFI_ABORTED;
>>>> +        } else if (key.unicode_char < 0x20) {
>>>> +            /* ignore control codes other than Ctrl+C, '\r' and 
>>>> '\b' */
>>>> +            continue;
>>>> +        } else if (key.scan_code != 0) {
>>>> +            /* only accept single ESC press for cancel */
>>>> +            continue;
>>>> +        }
>>>> +
>>>> +        if (filter_func) {
>>>> +            if (filter_func(&key) != EFI_SUCCESS)
>>>> +                continue;
>>>> +        }
>>>> +
>>>> +        if (len >= (size - 1))
>>>> +            continue;
>>>> +
>>>> +        buf[len] = key.unicode_char;
>>>> +        len++;
>>>> +        printf(ANSI_CURSOR_POSITION, row, col);
>>>> +        ret = EFI_CALL(cout->output_string(cout, buf));
>>>> +        if (ret != EFI_SUCCESS)
>>>> +            return ret;
>>>> +    }
>>>> +}
>>>> diff --git a/lib/efi_loader/efi_disk.c b/lib/efi_loader/efi_disk.c
>>>> index 8fb5b2363c..58736a8a5b 100644
>>>> --- a/lib/efi_loader/efi_disk.c
>>>> +++ b/lib/efi_loader/efi_disk.c
>>>> @@ -750,3 +750,14 @@ efi_status_t efi_disk_init(void)
>>>>
>>>>       return EFI_SUCCESS;
>>>>   }
>>>> +
>>>> +efi_status_t efi_disk_get_device_name(struct efi_block_io *this, 
>>>> char *buf, int size)
>>>> +{
>>>> +    struct efi_disk_obj *diskobj;
>>>> +
>>>> +    diskobj = container_of(this, struct efi_disk_obj, ops);
>>>> +
>>>> +    snprintf(buf, size, "%s%d:%d", diskobj->ifname, 
>>>> diskobj->dev_index, diskobj->part);
>>>
>>> A space would improve readability and better match U-Boot syntax.
>>> %s/%s%d:%d/%s %d:%d/
>>>
>>> I guess for MMC we are only supporting booting form the user partition.
>>> Otherwise the information would be incomplete.
>>>
>>> Best regards
>>>
>>> Heinrich
>>>
>>>> +
>>>> +    return EFI_SUCCESS;
>>>> +}
>>>> diff --git a/lib/efi_loader/efi_file.c b/lib/efi_loader/efi_file.c
>>>> index 7a7077e6d0..c96a7f7ca3 100644
>>>> --- a/lib/efi_loader/efi_file.c
>>>> +++ b/lib/efi_loader/efi_file.c
>>>> @@ -246,10 +246,10 @@ error:
>>>>       return NULL;
>>>>   }
>>>>
>>>> -static efi_status_t efi_file_open_int(struct efi_file_handle *this,
>>>> -                      struct efi_file_handle **new_handle,
>>>> -                      u16 *file_name, u64 open_mode,
>>>> -                      u64 attributes)
>>>> +efi_status_t efi_file_open_int(struct efi_file_handle *this,
>>>> +                   struct efi_file_handle **new_handle,
>>>> +                   u16 *file_name, u64 open_mode,
>>>> +                   u64 attributes)
>>>>   {
>>>>       struct file_handle *fh = to_fh(this);
>>>>       efi_status_t ret;
>>>> @@ -369,11 +369,17 @@ static efi_status_t file_close(struct 
>>>> file_handle *fh)
>>>>       return EFI_SUCCESS;
>>>>   }
>>>>
>>>> -static efi_status_t EFIAPI efi_file_close(struct efi_file_handle 
>>>> *file)
>>>> +efi_status_t efi_file_close_int(struct efi_file_handle *file)
>>>>   {
>>>>       struct file_handle *fh = to_fh(file);
>>>> +
>>>> +    return file_close(fh);
>>>> +}
>>>> +
>>>> +static efi_status_t EFIAPI efi_file_close(struct efi_file_handle 
>>>> *file)
>>>> +{
>>>>       EFI_ENTRY("%p", file);
>>>> -    return EFI_EXIT(file_close(fh));
>>>> +    return EFI_EXIT(efi_file_close_int(file));
>>>>   }
>>>>
>>>>   static efi_status_t EFIAPI efi_file_delete(struct efi_file_handle 
>>>> *file)
>>>> @@ -562,8 +568,8 @@ static efi_status_t dir_read(struct file_handle 
>>>> *fh, u64 *buffer_size,
>>>>       return EFI_SUCCESS;
>>>>   }
>>>>
>>>> -static efi_status_t efi_file_read_int(struct efi_file_handle *this,
>>>> -                      efi_uintn_t *buffer_size, void *buffer)
>>>> +efi_status_t efi_file_read_int(struct efi_file_handle *this,
>>>> +                   efi_uintn_t *buffer_size, void *buffer)
>>>>   {
>>>>       struct file_handle *fh = to_fh(this);
>>>>       efi_status_t ret = EFI_SUCCESS;
>>>> @@ -773,24 +779,11 @@ out:
>>>>       return EFI_EXIT(ret);
>>>>   }
>>>>
>>>> -/**
>>>> - * efi_file_setpos() - set current position in file
>>>> - *
>>>> - * This function implements the SetPosition service of the EFI file 
>>>> protocol.
>>>> - * See the UEFI spec for details.
>>>> - *
>>>> - * @file:    file handle
>>>> - * @pos:    new file position
>>>> - * Return:    status code
>>>> - */
>>>> -static efi_status_t EFIAPI efi_file_setpos(struct efi_file_handle 
>>>> *file,
>>>> -                       u64 pos)
>>>> +efi_status_t efi_file_setpos_int(struct efi_file_handle *file, u64 
>>>> pos)
>>>>   {
>>>>       struct file_handle *fh = to_fh(file);
>>>>       efi_status_t ret = EFI_SUCCESS;
>>>>
>>>> -    EFI_ENTRY("%p, %llu", file, pos);
>>>> -
>>>>       if (fh->isdir) {
>>>>           if (pos != 0) {
>>>>               ret = EFI_UNSUPPORTED;
>>>> @@ -812,6 +805,28 @@ static efi_status_t EFIAPI 
>>>> efi_file_setpos(struct efi_file_handle *file,
>>>>       fh->offset = pos;
>>>>
>>>>   error:
>>>> +    return ret;
>>>> +}
>>>> +
>>>> +/**
>>>> + * efi_file_setpos() - set current position in file
>>>> + *
>>>> + * This function implements the SetPosition service of the EFI file 
>>>> protocol.
>>>> + * See the UEFI spec for details.
>>>> + *
>>>> + * @file:    file handle
>>>> + * @pos:    new file position
>>>> + * Return:    status code
>>>> + */
>>>> +static efi_status_t EFIAPI efi_file_setpos(struct efi_file_handle 
>>>> *file,
>>>> +                       u64 pos)
>>>> +{
>>>> +    efi_status_t ret = EFI_SUCCESS;
>>>> +
>>>> +    EFI_ENTRY("%p, %llu", file, pos);
>>>> +
>>>> +    ret = efi_file_setpos_int(file, pos);
>>>> +
>>>>       return EFI_EXIT(ret);
>>>>   }
>>>>
>>>> @@ -1138,17 +1153,23 @@ struct efi_file_handle 
>>>> *efi_file_from_path(struct efi_device_path *fp)
>>>>       return f;
>>>>   }
>>>>
>>>> +efi_status_t efi_open_volume_int(struct 
>>>> efi_simple_file_system_protocol *this,
>>>> +                 struct efi_file_handle **root)
>>>> +{
>>>> +    struct file_system *fs = to_fs(this);
>>>> +
>>>> +    *root = file_open(fs, NULL, NULL, 0, 0);
>>>> +
>>>> +    return EFI_SUCCESS;
>>>> +}
>>>> +
>>>>   static efi_status_t EFIAPI
>>>>   efi_open_volume(struct efi_simple_file_system_protocol *this,
>>>>           struct efi_file_handle **root)
>>>>   {
>>>> -    struct file_system *fs = to_fs(this);
>>>> -
>>>>       EFI_ENTRY("%p, %p", this, root);
>>>>
>>>> -    *root = file_open(fs, NULL, NULL, 0, 0);
>>>> -
>>>> -    return EFI_EXIT(EFI_SUCCESS);
>>>> +    return EFI_EXIT(efi_open_volume_int(this, root));
>>>>   }
>>>>
>>>>   struct efi_simple_file_system_protocol *
>>>
>>
> 


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

* Re: [PATCH v5 13/17] efi_loader: menu-driven addition of UEFI boot option
  2022-05-06 17:30         ` Heinrich Schuchardt
@ 2022-05-06 18:10           ` Mark Kettenis
  2022-05-06 18:16             ` Heinrich Schuchardt
  0 siblings, 1 reply; 58+ messages in thread
From: Mark Kettenis @ 2022-05-06 18:10 UTC (permalink / raw)
  To: Heinrich Schuchardt
  Cc: masahisa.kojima, ilias.apalodimas, sjg, takahiro.akashi,
	francois.ozog, u-boot

> Date: Fri, 6 May 2022 19:30:51 +0200
> From: Heinrich Schuchardt <xypron.glpk@gmx.de>
> 
> On 4/30/22 14:49, Heinrich Schuchardt wrote:
> > On 4/29/22 12:56, Heinrich Schuchardt wrote:
> >> On 4/28/22 18:33, Heinrich Schuchardt wrote:
> >>> On 4/28/22 10:09, Masahisa Kojima wrote:
> >>>> This commit supports the menu-driven UEFI boot option addition.
> >>>> User can select the block device volume having
> >>>> efi_simple_file_system_protocol and select the file corresponding
> >>>> to the Boot#### variable. Then user enter the label of the BOOT####
> >>>> variable in utf8.
> >>>>
> >>>> Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
> 
> This patch creates unexpected behavior:
> 
> In the console I entered:
> 
> setenv bootmenu_0 foo=echo foo
> setenv bootmenu_1 bar=echo bar
> bootmenu 20
> 
> Nothing here relates to UEFI but the menu shows:
> 
>        foo
>        bar
>        UEFI Boot Manager Maintenance
>        Quit
> 
> Please, don't show 'UEFI Boot Manager Maintenance' if we are not in the 
> boot manager.

Not sure what you mean with this.  The "bootefi bootmgr" command
simply looks at EFI variables and immediately exits if "BootOrder"
isn't set.  So the EFI varaibles need to be modified beforehands.

Do you mean that 'UEFI Boot Manager Maintenance' should only be
included in the menu if any "Boot####" EFI variables exist (either
because they have been explicitly set or because we have generated
them for the "removable media" device paths)?

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

* Re: [PATCH v5 13/17] efi_loader: menu-driven addition of UEFI boot option
  2022-05-06 18:10           ` Mark Kettenis
@ 2022-05-06 18:16             ` Heinrich Schuchardt
  2022-05-09  9:27               ` Masahisa Kojima
  0 siblings, 1 reply; 58+ messages in thread
From: Heinrich Schuchardt @ 2022-05-06 18:16 UTC (permalink / raw)
  To: Mark Kettenis
  Cc: masahisa.kojima, ilias.apalodimas, sjg, takahiro.akashi,
	francois.ozog, u-boot



Am 6. Mai 2022 20:10:58 MESZ schrieb Mark Kettenis <mark.kettenis@xs4all.nl>:
>> Date: Fri, 6 May 2022 19:30:51 +0200
>> From: Heinrich Schuchardt <xypron.glpk@gmx.de>
>> 
>> On 4/30/22 14:49, Heinrich Schuchardt wrote:
>> > On 4/29/22 12:56, Heinrich Schuchardt wrote:
>> >> On 4/28/22 18:33, Heinrich Schuchardt wrote:
>> >>> On 4/28/22 10:09, Masahisa Kojima wrote:
>> >>>> This commit supports the menu-driven UEFI boot option addition.
>> >>>> User can select the block device volume having
>> >>>> efi_simple_file_system_protocol and select the file corresponding
>> >>>> to the Boot#### variable. Then user enter the label of the BOOT####
>> >>>> variable in utf8.
>> >>>>
>> >>>> Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
>> 
>> This patch creates unexpected behavior:
>> 
>> In the console I entered:
>> 
>> setenv bootmenu_0 foo=echo foo
>> setenv bootmenu_1 bar=echo bar
>> bootmenu 20
>> 
>> Nothing here relates to UEFI but the menu shows:
>> 
>>        foo
>>        bar
>>        UEFI Boot Manager Maintenance
>>        Quit
>> 
>> Please, don't show 'UEFI Boot Manager Maintenance' if we are not in the 
>> boot manager.
>
>Not sure what you mean with this.  The "bootefi bootmgr" command
>simply looks at EFI variables and immediately exits if "BootOrder"
>isn't set.  So the EFI varaibles need to be modified beforehands.
>
>Do you mean that 'UEFI Boot Manager Maintenance' should only be
>included in the menu if any "Boot####" EFI variables exist (either
>because they have been explicitly set or because we have generated
>them for the "removable media" device paths)?


The bootmenu command can be used in many contexts. Not all are related to booting.

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

* Re: [PATCH v5 16/17] bootmenu: add removable media entries
  2022-04-28 16:53   ` Heinrich Schuchardt
@ 2022-05-09  8:23     ` Masahisa Kojima
  2022-05-09 13:01       ` Heinrich Schuchardt
  0 siblings, 1 reply; 58+ messages in thread
From: Masahisa Kojima @ 2022-05-09  8:23 UTC (permalink / raw)
  To: Heinrich Schuchardt
  Cc: Ilias Apalodimas, Simon Glass, Takahiro Akashi, Francois Ozog,
	Mark Kettenis, u-boot

On Fri, 29 Apr 2022 at 01:53, Heinrich Schuchardt <xypron.glpk@gmx.de> wrote:
>
> On 4/28/22 10:09, Masahisa Kojima wrote:
> > UEFI specification requires booting from removal media using
> > a architecture-specific default image name such as BOOTAA64.EFI.
> > This commit adds the removable media entries into bootmenu,
> > so that user can select the removable media and boot with
> > default image.
> >
> > The bootmenu automatically enumerates the possible bootable
> > media devices supporting EFI_SIMPLE_FILE_SYSTEM_PROTOCOL,
> > add it as new UEFI boot option(BOOT####) and update BootOrder
> > variable. This automatically generated UEFI boot option
> > has the dedicated guid in the optional_data to distinguish it from
> > the UEFI boot option user adds manually.
> >
> > This commit also provides the BOOT#### variable maintenance feature.
> > Depending on the system hardware setup, some devices
> > may not exist at a later system boot, so bootmenu checks the
> > available device in each bootmenu invocation and automatically
> > removes the BOOT#### variable corrensponding to the non-existent
> > media device.
> >
> > Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
> > ---
> > Changes in v5:
> > - Return EFI_SUCCESS if there is no BootOrder defined
> > - correctly handle the case if no removable device found
> > - use guid to identify the automatically generated entry by bootmenu
> >
> > Newly created in v4
> >
> >   cmd/bootmenu.c                            |  94 +++++++++++++++
> >   include/efi_loader.h                      |  20 ++++
> >   lib/efi_loader/efi_bootmenu_maintenance.c | 139 ++++++++++++++++++++++
> >   3 files changed, 253 insertions(+)
> >
> > diff --git a/cmd/bootmenu.c b/cmd/bootmenu.c
> > index 860cb83182..970db3ee01 100644
> > --- a/cmd/bootmenu.c
> > +++ b/cmd/bootmenu.c
> > @@ -396,6 +396,89 @@ static int is_blk_device_available(char *token)
> >       return -ENODEV;
> >   }
> >
> > +/**
> > + * prepare_media_device_entry() - generate the media device entries
> > + *
> > + * This function enumerates all devices supporting EFI_SIMPLE_FILE_SYSTEM_PROTOCOL
> > + * and generate the bootmenu entries.
> > + * This function also provide the BOOT#### variable maintenance for
> > + * the media device entries.
> > + *   - Automatically create the BOOT#### variable for the newly detected device,
> > + *     this BOOT#### variable is distinguished by the special GUID
> > + *     stored in the EFI_LOAD_OPTION.optional_data
> > + *   - If the device is not attached to the system, the associated BOOT#### variable
> > + *     is automatically deleted.
> > + *
> > + * Return:   status code
> > + */
> > +static efi_status_t prepare_media_device_entry(void)
> > +{
> > +     u32 i;
> > +     efi_status_t ret;
> > +     efi_uintn_t count;
> > +     efi_handle_t *volume_handles = NULL;
> > +     struct efi_bootmenu_media_boot_option *opt = NULL;
> > +
> > +     ret = efi_locate_handle_buffer_int(BY_PROTOCOL, &efi_simple_file_system_protocol_guid,
> > +                                        NULL, &count, (efi_handle_t **)&volume_handles);
> > +     if (ret != EFI_SUCCESS)
> > +             return ret;
> > +
> > +     opt = calloc(count, sizeof(struct efi_bootmenu_media_boot_option));
> > +     if (!opt)
> > +             goto out;
> > +
> > +     /* enumerate all devices supporting EFI_SIMPLE_FILE_SYSTEM_PROTOCOL */
> > +     ret = efi_bootmenu_enumerate_boot_option(opt, volume_handles, count);
> > +     if (ret != EFI_SUCCESS)
> > +             goto out;
> > +
> > +     /*
> > +      * System hardware configuration may vary depending on the user setup.
> > +      * The boot option is automatically added by the bootmenu.
> > +      * If the device is not attached to the system, the boot option needs
> > +      * to be deleted.
> > +      */
> > +     ret = efi_bootmenu_delete_invalid_boot_option(opt, count);
> > +     if (ret != EFI_SUCCESS)
> > +             goto out;
> > +
> > +     /* add non-existent boot option */
> > +     for (i = 0; i < count; i++) {
> > +             u32 boot_index;
> > +             u16 var_name[9];
> > +
> > +             if (!opt[i].exist) {
> > +                     ret = efi_bootmenu_get_unused_bootoption(var_name, sizeof(var_name),
> > +                                                              &boot_index);
> > +                     if (ret != EFI_SUCCESS)
> > +                             goto out;
> > +
> > +                     ret = efi_set_variable_int(var_name, &efi_global_variable_guid,
> > +                                                EFI_VARIABLE_NON_VOLATILE |
> > +                                                EFI_VARIABLE_BOOTSERVICE_ACCESS |
> > +                                                EFI_VARIABLE_RUNTIME_ACCESS,
> > +                                                opt[i].size, opt[i].lo, false);
> > +                     if (ret != EFI_SUCCESS)
> > +                             goto out;
> > +
> > +                     ret = efi_bootmenu_append_bootorder(boot_index);
> > +                     if (ret != EFI_SUCCESS)
> > +                             goto out;
> > +             }
> > +     }
> > +
> > +out:
> > +     if (opt) {
> > +             for (i = 0; i < count; i++)
> > +                     free(opt[i].lo);
> > +     }
> > +     free(opt);
> > +     efi_free_pool(volume_handles);
> > +
> > +     return ret;
> > +}
> > +
> >   /**
> >    * prepare_distro_boot_entry() - generate the distro boot entries
> >    *
> > @@ -500,6 +583,7 @@ static int prepare_distro_boot_entry(struct bootmenu_data *menu,
> >   static struct bootmenu_data *bootmenu_create(int delay)
> >   {
> >       int ret;
> > +     efi_status_t efi_ret;
> >       unsigned short int i = 0;
> >       struct bootmenu_data *menu;
> >       struct bootmenu_entry *iter = NULL;
> > @@ -523,6 +607,16 @@ static struct bootmenu_data *bootmenu_create(int delay)
> >               goto cleanup;
> >
> >       if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR)) {
> > +             if (i < MAX_DYNAMIC_ENTRY) {
> > +                     /*
> > +                      * UEFI specification requires booting from removal media using
> > +                      * a architecture-specific default image name such as BOOTAA64.EFI.
> > +                      */
> > +                     efi_ret = prepare_media_device_entry();
> > +                     if (efi_ret != EFI_SUCCESS && efi_ret != EFI_NOT_FOUND)
> > +                             goto cleanup;
> > +             }
> > +
> >               if (i < MAX_DYNAMIC_ENTRY) {
> >                       ret = prepare_uefi_bootorder_entry(menu, &iter, &i);
> >                       if (ret < 0 && ret != -ENOENT)
> > diff --git a/include/efi_loader.h b/include/efi_loader.h
> > index 533618341b..bef492ccb9 100644
> > --- a/include/efi_loader.h
> > +++ b/include/efi_loader.h
> > @@ -928,6 +928,22 @@ struct efi_signature_store {
> >   struct x509_certificate;
> >   struct pkcs7_message;
> >
> > +/**
> > + * struct efi_bootmenu_media_boot_option - boot option for (removable) media device
> > + *
> > + * This structure is used to enumerate possible boot option
> > + *
> > + * @lo:              Serialized load option data
> > + * @size:    Size of serialized load option data
> > + * @exist:   Flag to indicate the load option already exists
> > + *           in Non-volatile load option
> > + */
> > +struct efi_bootmenu_media_boot_option {
> > +     void *lo;
> > +     efi_uintn_t size;
> > +     bool exist;
> > +};
> > +
> >   bool efi_signature_lookup_digest(struct efi_image_regions *regs,
> >                                struct efi_signature_store *db,
> >                                bool dbx);
> > @@ -1076,6 +1092,10 @@ efi_status_t efi_console_get_u16_string
> >   efi_status_t efi_bootmenu_get_unused_bootoption(u16 *buf,
> >                                               efi_uintn_t buf_size, u32 *index);
> >   efi_status_t efi_bootmenu_append_bootorder(u16 index);
> > +efi_status_t efi_bootmenu_enumerate_boot_option(struct efi_bootmenu_media_boot_option *opt,
> > +                                             efi_handle_t *volume_handles, efi_status_t count);
> > +efi_status_t efi_bootmenu_delete_invalid_boot_option(struct efi_bootmenu_media_boot_option *opt,
> > +                                                  efi_status_t count);
> >
> >   efi_status_t efi_disk_get_device_name(struct efi_block_io *this, char *buf, int size);
> >
> > diff --git a/lib/efi_loader/efi_bootmenu_maintenance.c b/lib/efi_loader/efi_bootmenu_maintenance.c
> > index 8c3f94c695..33b37fd11a 100644
> > --- a/lib/efi_loader/efi_bootmenu_maintenance.c
> > +++ b/lib/efi_loader/efi_bootmenu_maintenance.c
> > @@ -26,6 +26,13 @@ static struct efi_simple_text_output_protocol *cout;
> >   #define EFI_BOOTMENU_BOOT_NAME_MAX 32
> >   #define EFI_BOOT_ORDER_MAX_SIZE_IN_DECIMAL 6
> >
> > +#define EFI_BOOTMENU_AUTO_GENERATED_ENTRY_GUID \
> > +     EFI_GUID(0x38c1acc1, 0x9fc0, 0x41f0, \
> > +              0xb9, 0x01, 0xfa, 0x74, 0xd6, 0xd6, 0xe4, 0xde)
> > +
> > +static const efi_guid_t efi_guid_bootmenu_auto_generated =
> > +             EFI_BOOTMENU_AUTO_GENERATED_ENTRY_GUID;
> > +
> >   typedef efi_status_t (*efi_bootmenu_entry_func)(void *data, bool *exit);
> >
> >   /**
> > @@ -1104,3 +1111,135 @@ efi_status_t efi_bootmenu_show_maintenance_menu(void)
> >                                         ARRAY_SIZE(maintenance_menu_items),
> >                                         -1);
> >   }
> > +
> > +efi_status_t efi_bootmenu_enumerate_boot_option(struct efi_bootmenu_media_boot_option *opt,
> > +                                             efi_handle_t *volume_handles, efi_status_t count)
> > +{
> > +     u32 i;
> > +     struct efi_handler *handler;
> > +     efi_status_t ret = EFI_SUCCESS;
> > +
> > +     for (i = 0; i < count; i++) {
> > +             char *optional_data;
> > +             u16 *dev_name, *p;
> > +             struct efi_load_option lo;
> > +             struct efi_block_io *block_io;
> > +             char buf[BOOTMENU_DEVICE_NAME_MAX];
> > +             struct efi_device_path *device_path;
> > +
> > +             ret = efi_search_protocol(volume_handles[i], &efi_guid_device_path, &handler);
> > +             if (ret != EFI_SUCCESS)
> > +                     continue;
> > +             ret = efi_protocol_open(handler, (void **)&device_path,
> > +                                     efi_root, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
> > +             if (ret != EFI_SUCCESS)
> > +                     continue;
> > +
> > +             ret = efi_search_protocol(volume_handles[i], &efi_block_io_guid, &handler);
> > +             if (ret != EFI_SUCCESS)
> > +                     continue;
> > +             ret = efi_protocol_open(handler, (void **)&block_io,
> > +                                     efi_root, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
> > +             if (ret != EFI_SUCCESS)
> > +                     continue;
> > +
> > +             efi_disk_get_device_name(block_io, buf, BOOTMENU_DEVICE_NAME_MAX);
> > +             dev_name = calloc(1, (strlen(buf) + 1) * sizeof(u16));
> > +             if (!dev_name) {
> > +                     ret = EFI_OUT_OF_RESOURCES;
> > +                     goto out;
> > +             }
> > +             p = dev_name;
> > +             utf8_utf16_strncpy(&p, buf, strlen(buf));
> > +
> > +             lo.label = dev_name;
> > +             lo.attributes = LOAD_OPTION_ACTIVE;
> > +             lo.file_path = device_path;
> > +             lo.file_path_length = efi_dp_size(device_path) + sizeof(END);
> > +             /*
> > +              * Set the dedicated guid to optional_data, it is used to identify
> > +              * the boot option that automatically generated by the bootmenu.
> > +              * efi_serialize_load_option() expects optional_data is null-terminated
> > +              * utf8 string, so set the "dummystr" string to allocate enough space
> > +              * to store guid, instead of realloc the load_option.
> > +              *
> > +              * This will allocate 16 bytes for guid plus trailing 0x0000.
> > +              * The guid does not require trailing 0x0000, but it is for safety
> > +              * in case some program handle the optional_data as u16 string.
> > +              */
> > +             lo.optional_data = "dummystr";
>
> Why do you want to reserve 18 bytes when 16 are enough for the GUID?

OK, I will only allocate 16 bytes for GUID.

>
> > +             opt[i].size = efi_serialize_load_option(&lo, (u8 **)&opt[i].lo);
> > +             if (!opt[i].size) {
> > +                     ret = EFI_OUT_OF_RESOURCES;
> > +                     free(dev_name);
> > +                     goto out;
> > +             }
> > +             /* set the guid */
> > +             optional_data = (char *)opt[i].lo + (opt[i].size - u16_strsize(u"dummystr"));
> > +             memcpy(optional_data, &efi_guid_bootmenu_auto_generated, sizeof(efi_guid_t));
> > +             free(dev_name);
> > +     }
> > +
> > +out:
> > +     return ret;
> > +}
> > +
> > +efi_status_t efi_bootmenu_delete_invalid_boot_option(struct efi_bootmenu_media_boot_option *opt,
> > +                                                  efi_status_t count)
> > +{
> > +     u16 *bootorder;
> > +     u32 i, j;
> > +     efi_status_t ret;
> > +     efi_uintn_t num, size, bootorder_size;
> > +     void *load_option;
> > +     struct efi_load_option lo;
> > +     u16 varname[] = u"Boot####";
> > +
> > +     bootorder = efi_get_var(u"BootOrder", &efi_global_variable_guid, &bootorder_size);
> > +     if (!bootorder)
> > +             return EFI_SUCCESS; /* BootOrder is not defined, nothing to do */
> > +
> > +     num = bootorder_size / sizeof(u16);
> > +     for (i = 0; i < num;) {
> > +             efi_uintn_t tmp;
> > +
> > +             efi_create_indexed_name(varname, sizeof(varname),
> > +                                     "Boot", bootorder[i]);
> > +             load_option = efi_get_var(varname, &efi_global_variable_guid, &size);
> > +             if (!load_option)
> > +                     goto next;
> > +
> > +             tmp = size;
>
> This copying of variables is superfluous. Just keep size.

I don't catch your point.
This efi_bootmenu_delete_invalid_boot_option() function
does not copy the variable, only keeps the variable size.

>
> > +             ret = efi_deserialize_load_option(&lo, load_option, &tmp);
> > +             if (ret != EFI_SUCCESS)
> > +                     goto next;
> > +
> > +             if (guidcmp(lo.optional_data, &efi_guid_bootmenu_auto_generated) == 0) {
>
> You must avoid a buffer overrun. Check size >= 16.

Yes, I need to check the size of optional_data.

Thanks,
Masahisa Kojima

>
> Best regards
>
> Heinrich
>
> > +                     for (j = 0; j < count; j++) {
> > +                             if (memcmp(opt[j].lo, load_option, size) == 0) {
> > +                                     opt[j].exist = true;
> > +                                     break;
> > +                             }
> > +                     }
> > +
> > +                     if (j == count) {
> > +                             ret = delete_boot_option(bootorder, i, bootorder_size);
> > +                             if (ret != EFI_SUCCESS) {
> > +                                     free(load_option);
> > +                                     goto out;
> > +                             }
> > +
> > +                             num--;
> > +                             bootorder_size -= sizeof(u16);
> > +                             free(load_option);
> > +                             continue;
> > +                     }
> > +             }
> > +next:
> > +             free(load_option);
> > +             i++;
> > +     }
> > +
> > +out:
> > +     return ret;
> > +}
>

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

* Re: [PATCH v5 07/17] bootmenu: flush input buffer before waiting user key input
  2022-04-29 19:46   ` Heinrich Schuchardt
@ 2022-05-09  8:33     ` Masahisa Kojima
  0 siblings, 0 replies; 58+ messages in thread
From: Masahisa Kojima @ 2022-05-09  8:33 UTC (permalink / raw)
  To: Heinrich Schuchardt
  Cc: Ilias Apalodimas, Simon Glass, Takahiro Akashi, Francois Ozog,
	Mark Kettenis, u-boot

Hi Heinrich,

On Sat, 30 Apr 2022 at 04:46, Heinrich Schuchardt <xypron.glpk@gmx.de> wrote:
>
> On 4/28/22 10:09, Masahisa Kojima wrote:
> > Before waiting user key input to stop autoboot, the input buffer
> > must be flushed.
>
> I am not sure if this is desirable.
>
> The default time for autoboot stopping is just 2 seconds. So it is nice
> to be able to type ahead.
>
> On the other hand I have seen boards not booting because after the
> serial driver was initialized it found some random byte in the input buffer.

OK, I agree. I drop this patch.

Thanks,
Masahisa Kojima

>
> I would prefer flushing of input in serial_init().
>
> Best regards
>
> Heinrich
>
> >
> > Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
> > ---
> > Change in v5:
> > - newly created, split into separate patch
> >
> >   cmd/bootmenu.c | 4 ++++
> >   1 file changed, 4 insertions(+)
> >
> > diff --git a/cmd/bootmenu.c b/cmd/bootmenu.c
> > index d573487272..9a32a18b19 100644
> > --- a/cmd/bootmenu.c
> > +++ b/cmd/bootmenu.c
> > @@ -87,6 +87,10 @@ static void bootmenu_autoboot_loop(struct bootmenu_data *menu,
> >       int i, c;
> >
> >       if (menu->delay > 0) {
> > +             /* flush input */
> > +             while (tstc())
> > +                     getchar();
> > +
> >               printf(ANSI_CURSOR_POSITION, menu->count + 5, 1);
> >               printf("  Hit any key to stop autoboot: %2d ", menu->delay);
> >       }
>

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

* Re: [PATCH v5 08/17] bootmenu: update bootmenu_entry structure
  2022-05-01 20:54     ` Heinrich Schuchardt
@ 2022-05-09  8:54       ` Masahisa Kojima
  0 siblings, 0 replies; 58+ messages in thread
From: Masahisa Kojima @ 2022-05-09  8:54 UTC (permalink / raw)
  To: Heinrich Schuchardt
  Cc: u-boot, Ilias Apalodimas, Simon Glass, Takahiro Akashi,
	Francois Ozog, Mark Kettenis

On Mon, 2 May 2022 at 05:54, Heinrich Schuchardt <xypron.glpk@gmx.de> wrote:
>
> On 4/29/22 21:51, Heinrich Schuchardt wrote:
> > On 4/28/22 10:09, Masahisa Kojima wrote:
> >> This is a preparation for succeeding addition of uefi boot
> >> and distro boot menu entries into bootmenu.
> >> The bootmenu_entry title is updated to u16 string because
> >> uefi use u16 string. This commit also factors out the function
> >> to prepare the entries generated by "bootmenu_x" U-Boot environment
> >> variable.
> >>
> >> This commit also updates the bootmenu entry title.
> >> The entry derived from "bootmenu_x" U-Boot environment variable
> >> has the "bootmenu_xx" prefix as below.
> >>
> >>    *** U-Boot Boot Menu ***
> >>
> >>       bootmenu_00   : Boot 1. kernel
> >>       bootmenu_01   : Boot 2. kernel
> >>       bootmenu_02   : Reset board
> >>
> >> Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
> >> ---
> >> Changes in v5:
> >> - split into the separate patch
> >> - add function description comment
> >>
> >>   cmd/bootmenu.c | 110 +++++++++++++++++++++++++++++++++++--------------
> >>   1 file changed, 78 insertions(+), 32 deletions(-)
> >>
> >> diff --git a/cmd/bootmenu.c b/cmd/bootmenu.c
> >> index 9a32a18b19..15ad621c9f 100644
> >> --- a/cmd/bootmenu.c
> >> +++ b/cmd/bootmenu.c
> >> @@ -3,6 +3,7 @@
> >>    * (C) Copyright 2011-2013 Pali Rohár <pali@kernel.org>
> >>    */
> >>
> >> +#include <charset.h>
> >>   #include <common.h>
> >>   #include <command.h>
> >>   #include <ansi.h>
> >> @@ -24,11 +25,18 @@
> >>    */
> >>   #define MAX_ENV_SIZE    (9 + 2 + 1)
> >>
> >> +enum boot_type {
> >> +    BOOTMENU_TYPE_NONE = 0,
> >> +    BOOTMENU_TYPE_BOOTMENU,
> >> +};
> >> +
> >>   struct bootmenu_entry {
> >>       unsigned short int num;        /* unique number 0 .. MAX_COUNT */
> >>       char key[3];            /* key identifier of number */
> >> -    char *title;            /* title of entry */
> >> +    u16 *title;            /* title of entry */
> >>       char *command;            /* hush command of entry */
> >> +    enum boot_type type;        /* boot type of entry */
> >> +    u16 bootorder;            /* order for each boot type */
> >>       struct bootmenu_data *menu;    /* this bootmenu */
> >>       struct bootmenu_entry *next;    /* next menu entry (num+1) */
> >>   };
> >> @@ -75,7 +83,10 @@ static void bootmenu_print_entry(void *data)
> >>       if (reverse)
> >>           puts(ANSI_COLOR_REVERSE);
> >>
> >> -    puts(entry->title);
> >> +    if (entry->type == BOOTMENU_TYPE_BOOTMENU)
> >> +        printf("bootmenu_%02d   : %ls", entry->bootorder, entry->title);
>
> This "bootmen_%02d" strings conveys not information of interest. Please
> remove this hunk.
>
> >> +    else
> >> +        printf("%ls", entry->title);
> >>
> >>       if (reverse)
> >>           puts(ANSI_COLOR_RESET);
> >> @@ -279,31 +290,32 @@ static void bootmenu_destroy(struct
> >> bootmenu_data *menu)
> >>       free(menu);
> >>   }
> >>
> >> -static struct bootmenu_data *bootmenu_create(int delay)
> >> +/**
> >> + * prepare_bootmenu_entry() - generate the bootmenu_xx entries
> >> + *
> >> + * This function read the "bootmenu_x" U-Boot environment variable
> >> + * and generate the bootmenu entries.
> >> + *
> >> + * @menu:    pointer to the bootmenu structure
> >> + * @current:    pointer to the last bootmenu entry list
> >> + * @index:    pointer to the index of the last bootmenu entry,
> >> + *        the number of bootmenu entry is added by this function
> >> + * Return:    1 on success, negative value on error
> >> + */
> >> +static int prepare_bootmenu_entry(struct bootmenu_data *menu,
> >> +                  struct bootmenu_entry **current,
> >> +                  unsigned short int *index)
> >>   {
> >> -    unsigned short int i = 0;
> >> -    const char *option;
> >> -    struct bootmenu_data *menu;
> >> -    struct bootmenu_entry *iter = NULL;
> >> -
> >>       int len;
> >>       char *sep;
> >> -    char *default_str;
> >> -    struct bootmenu_entry *entry;
> >> -
> >> -    menu = malloc(sizeof(struct bootmenu_data));
> >> -    if (!menu)
> >> -        return NULL;
> >> -
> >> -    menu->delay = delay;
> >> -    menu->active = 0;
> >> -    menu->first = NULL;
> >> -
> >> -    default_str = env_get("bootmenu_default");
> >> -    if (default_str)
> >> -        menu->active = (int)simple_strtol(default_str, NULL, 10);
> >> +    const char *option;
> >> +    unsigned short int i = *index;
> >> +    struct bootmenu_entry *entry = NULL;
> >> +    struct bootmenu_entry *iter = *current;
> >>
> >>       while ((option = bootmenu_getoption(i))) {
> >> +        u16 *buf;
> >> +
> >>           sep = strchr(option, '=');
> >>           if (!sep) {
> >>               printf("Invalid bootmenu entry: %s\n", option);
> >> @@ -312,23 +324,23 @@ static struct bootmenu_data *bootmenu_create(int
> >> delay)
> >>
> >>           entry = malloc(sizeof(struct bootmenu_entry));
> >>           if (!entry)
> >> -            goto cleanup;
> >> +            return -ENOMEM;
> >>
> >>           len = sep-option;
> >> -        entry->title = malloc(len + 1);
> >> +        buf = calloc(1, (len + 1) * sizeof(u16));
> >> +        entry->title = buf;
> >>           if (!entry->title) {
> >>               free(entry);
> >> -            goto cleanup;
> >> +            return -ENOMEM;
> >>           }
> >> -        memcpy(entry->title, option, len);
> >> -        entry->title[len] = 0;
> >> +        utf8_utf16_strncpy(&buf, option, len);
> >>
> >>           len = strlen(sep + 1);
> >>           entry->command = malloc(len + 1);
> >>           if (!entry->command) {
> >>               free(entry->title);
> >>               free(entry);
> >> -            goto cleanup;
> >> +            return -ENOMEM;
> >>           }
> >>           memcpy(entry->command, sep + 1, len);
> >>           entry->command[len] = 0;
> >> @@ -337,6 +349,8 @@ static struct bootmenu_data *bootmenu_create(int
> >> delay)
> >>
> >>           entry->num = i;
> >>           entry->menu = menu;
> >> +        entry->type = BOOTMENU_TYPE_BOOTMENU;
> >> +        entry->bootorder = i;
> >>           entry->next = NULL;
> >>
> >>           if (!iter)
> >> @@ -351,13 +365,44 @@ static struct bootmenu_data *bootmenu_create(int
> >> delay)
> >>               break;
> >>       }
> >>
> >> +    *index = i;
> >> +    *current = iter;
> >> +
> >> +    return 1;
> >> +}
> >> +
> >> +static struct bootmenu_data *bootmenu_create(int delay)
> >> +{
> >> +    int ret;
> >> +    unsigned short int i = 0;
> >> +    struct bootmenu_data *menu;
> >> +    struct bootmenu_entry *iter = NULL;
> >> +    struct bootmenu_entry *entry;
> >> +    char *default_str;
> >> +
> >> +    menu = malloc(sizeof(struct bootmenu_data));
> >> +    if (!menu)
> >> +        return NULL;
> >> +
> >> +    menu->delay = delay;
> >> +    menu->active = 0;
> >> +    menu->first = NULL;
> >> +
> >> +    default_str = env_get("bootmenu_default");
> >> +    if (default_str)
> >> +        menu->active = (int)simple_strtol(default_str, NULL, 10);
> >> +
> >> +    ret = prepare_bootmenu_entry(menu, &iter, &i);
> >> +    if (ret < 0)
> >> +        goto cleanup;
> >> +
> >>       /* Add U-Boot console entry at the end */
> >>       if (i <= MAX_COUNT - 1) {
> >>           entry = malloc(sizeof(struct bootmenu_entry));
> >>           if (!entry)
> >>               goto cleanup;
> >>
> >> -        entry->title = strdup("U-Boot console");
> >> +        entry->title = u16_strdup(u"U-Boot console");
> >>           if (!entry->title) {
> >>               free(entry);
> >>               goto cleanup;
> >> @@ -374,6 +419,7 @@ static struct bootmenu_data *bootmenu_create(int
> >> delay)
> >>
> >>           entry->num = i;
> >>           entry->menu = menu;
> >> +        entry->type = BOOTMENU_TYPE_NONE;
> >>           entry->next = NULL;
> >>
> >>           if (!iter)
> >> @@ -431,7 +477,7 @@ static void bootmenu_show(int delay)
> >>   {
> >>       int init = 0;
> >>       void *choice = NULL;
> >> -    char *title = NULL;
> >> +    u16 *title = NULL;
> >>       char *command = NULL;
> >>       struct menu *menu;
> >>       struct bootmenu_data *bootmenu;
> >> @@ -482,7 +528,7 @@ static void bootmenu_show(int delay)
> >>
> >>       if (menu_get_choice(menu, &choice) == 1) {
> >>           iter = choice;
> >> -        title = strdup(iter->title);
> >> +        title = u16_strdup(iter->title);
> >
> > We must still be able to use the bootmenu command outside of the UEFI
> > sub-system. There wide strings will not be used.

OK, I will consider supporting a non-UEFI system.

> >
> > We need a testcase for this.
>
> I have already provided a patch with this.

Thank you very much for adding test cases.

Regards,
Masahisa Kojima

>
> Best regards
>
> Heinrich
>
> >
> > Best regards
> >
> > Heinrich
> >
> >>           command = strdup(iter->command);
> >>       }
> >>
> >> @@ -497,7 +543,7 @@ cleanup:
> >>       }
> >>
> >>       if (title && command) {
> >> -        debug("Starting entry '%s'\n", title);
> >> +        debug("Starting entry '%ls'\n", title);
> >>           free(title);
> >>           run_command(command, 0);
> >>           free(command);
> >
>

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

* Re: [PATCH v5 09/17] bootmenu: add UEFI boot entry into bootmenu
  2022-05-01 21:44   ` Heinrich Schuchardt
@ 2022-05-09  8:59     ` Masahisa Kojima
  0 siblings, 0 replies; 58+ messages in thread
From: Masahisa Kojima @ 2022-05-09  8:59 UTC (permalink / raw)
  To: Heinrich Schuchardt
  Cc: Ilias Apalodimas, Simon Glass, Takahiro Akashi, Francois Ozog,
	Mark Kettenis, u-boot

Hi Heinrich,

On Mon, 2 May 2022 at 06:44, Heinrich Schuchardt <xypron.glpk@gmx.de> wrote:
>
> On 4/28/22 10:09, Masahisa Kojima wrote:
> > This commit adds the UEFI related menu entries
> > into the bootmenu.
> >
> > User can select which UEFI "Boot####" option to execute
> > from bootmenu, then bootmenu sets the "BootNext" UEFI
> > variable and invoke efi bootmgr. The efi bootmgr
> > will handle the "BootNext" UEFI variable.
> >
> > If the "BootNext" UEFI variable is preset and efi bootmgr is enabled,
> > bootmenu invokes efi bootmgr to handle "BootNext" as first priority.
> >
> > The UEFI boot entry has the "UEFI BOOTXXXX" prefix as below.
>
> This prefix provides no value.
>
> >
> >    *** U-Boot Boot Menu ***
> >
> >       UEFI BOOT0000 : debian
> >       UEFI BOOT0001 : ubuntu
> >
> > Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
> > ---
> > Changes in v5:
> > - split into the separate patch
> > - add function description comment
> > - remove non-volatile attribute for BootNext variable to minimize
> >    the access to the non-volatile storage
> >
> >   cmd/bootmenu.c | 155 ++++++++++++++++++++++++++++++++++++++++++++++++-
> >   1 file changed, 154 insertions(+), 1 deletion(-)
> >
> > diff --git a/cmd/bootmenu.c b/cmd/bootmenu.c
> > index 15ad621c9f..da688e6213 100644
> > --- a/cmd/bootmenu.c
> > +++ b/cmd/bootmenu.c
> > @@ -7,6 +7,8 @@
> >   #include <common.h>
> >   #include <command.h>
> >   #include <ansi.h>
> > +#include <efi_loader.h>
> > +#include <efi_variable.h>
> >   #include <env.h>
> >   #include <log.h>
> >   #include <menu.h>
> > @@ -28,6 +30,7 @@
> >   enum boot_type {
> >       BOOTMENU_TYPE_NONE = 0,
> >       BOOTMENU_TYPE_BOOTMENU,
> > +     BOOTMENU_TYPE_UEFI_BOOT_OPTION,
> >   };
> >
> >   struct bootmenu_entry {
> > @@ -85,6 +88,8 @@ static void bootmenu_print_entry(void *data)
> >
> >       if (entry->type == BOOTMENU_TYPE_BOOTMENU)
> >               printf("bootmenu_%02d   : %ls", entry->bootorder, entry->title);
> > +     else if (entry->type == BOOTMENU_TYPE_UEFI_BOOT_OPTION)
> > +             printf("UEFI BOOT%04X : %ls", entry->bootorder, entry->title);
>
> Please, remove this hunk.

Thank you for requesting to merge this patch into u-boot/master.
You have removed the "UEFI BOOTXXXX" prefix from
the bootmenu title, but the commit message still says that there is
"UEFI BOOTXXXX" prefix for UEFI entries.

Thanks,
Masahisa Kojima

>
> Best regards
>
> Heinrich
>
> >       else
> >               printf("%ls", entry->title);
> >
> > @@ -371,6 +376,95 @@ static int prepare_bootmenu_entry(struct bootmenu_data *menu,
> >       return 1;
> >   }
> >
> > +/**
> > + * prepare_uefi_bootorder_entry() - generate the uefi bootmenu entries
> > + *
> > + * This function read the "BootOrder" UEFI variable
> > + * and generate the bootmenu entries in the order of "BootOrder".
> > + *
> > + * @menu:    pointer to the bootmenu structure
> > + * @current: pointer to the last bootmenu entry list
> > + * @index:   pointer to the index of the last bootmenu entry,
> > + *           the number of uefi entry is added by this function
> > + * Return:   1 on success, negative value on error
> > + */
> > +static int prepare_uefi_bootorder_entry(struct bootmenu_data *menu,
> > +                                     struct bootmenu_entry **current,
> > +                                     unsigned short int *index)
> > +{
> > +     u16 *bootorder;
> > +     efi_status_t ret;
> > +     unsigned short j;
> > +     efi_uintn_t num, size;
> > +     void *load_option;
> > +     struct efi_load_option lo;
> > +     u16 varname[] = u"Boot####";
> > +     unsigned short int i = *index;
> > +     struct bootmenu_entry *entry = NULL;
> > +     struct bootmenu_entry *iter = *current;
> > +
> > +     bootorder = efi_get_var(u"BootOrder", &efi_global_variable_guid, &size);
> > +     if (!bootorder)
> > +             return -ENOENT;
> > +
> > +     num = size / sizeof(u16);
> > +     for (j = 0; j < num; j++) {
> > +             entry = malloc(sizeof(struct bootmenu_entry));
> > +             if (!entry)
> > +                     return -ENOMEM;
> > +
> > +             efi_create_indexed_name(varname, sizeof(varname),
> > +                                     "Boot", bootorder[j]);
> > +             load_option = efi_get_var(varname, &efi_global_variable_guid, &size);
> > +             if (!load_option)
> > +                     continue;
> > +
> > +             ret = efi_deserialize_load_option(&lo, load_option, &size);
> > +             if (ret != EFI_SUCCESS) {
> > +                     log_warning("Invalid load option for %ls\n", varname);
> > +                     free(load_option);
> > +                     free(entry);
> > +                     continue;
> > +             }
> > +
> > +             if (lo.attributes & LOAD_OPTION_ACTIVE) {
> > +                     entry->title = u16_strdup(lo.label);
> > +                     if (!entry->title) {
> > +                             free(load_option);
> > +                             free(entry);
> > +                             free(bootorder);
> > +                             return -ENOMEM;
> > +                     }
> > +                     entry->command = strdup("bootefi bootmgr");
> > +                     sprintf(entry->key, "%d", i);
> > +                     entry->num = i;
> > +                     entry->menu = menu;
> > +                     entry->type = BOOTMENU_TYPE_UEFI_BOOT_OPTION;
> > +                     entry->bootorder = bootorder[j];
> > +                     entry->next = NULL;
> > +
> > +                     if (!iter)
> > +                             menu->first = entry;
> > +                     else
> > +                             iter->next = entry;
> > +
> > +                     iter = entry;
> > +                     i++;
> > +             }
> > +
> > +             free(load_option);
> > +
> > +             if (i == MAX_COUNT - 1)
> > +                     break;
> > +     }
> > +
> > +     free(bootorder);
> > +     *index = i;
> > +     *current = iter;
> > +
> > +     return 1;
> > +}
> > +
> >   static struct bootmenu_data *bootmenu_create(int delay)
> >   {
> >       int ret;
> > @@ -396,6 +490,14 @@ static struct bootmenu_data *bootmenu_create(int delay)
> >       if (ret < 0)
> >               goto cleanup;
> >
> > +     if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR)) {
> > +             if (i < MAX_COUNT - 1) {
> > +                     ret = prepare_uefi_bootorder_entry(menu, &iter, &i);
> > +                     if (ret < 0 && ret != -ENOENT)
> > +                             goto cleanup;
> > +             }
> > +     }
> > +
> >       /* Add U-Boot console entry at the end */
> >       if (i <= MAX_COUNT - 1) {
> >               entry = malloc(sizeof(struct bootmenu_entry));
> > @@ -473,6 +575,31 @@ static void menu_display_statusline(struct menu *m)
> >       puts(ANSI_CLEAR_LINE);
> >   }
> >
> > +static void handle_uefi_bootnext(void)
> > +{
> > +     u16 bootnext;
> > +     efi_status_t ret;
> > +     efi_uintn_t size;
> > +
> > +     /* Initialize EFI drivers */
> > +     ret = efi_init_obj_list();
> > +     if (ret != EFI_SUCCESS) {
> > +             log_err("Error: Cannot initialize UEFI sub-system, r = %lu\n",
> > +                     ret & ~EFI_ERROR_MASK);
> > +
> > +             return;
> > +     }
> > +
> > +     /* If UEFI BootNext variable is set, boot the BootNext load option */
> > +     size = sizeof(u16);
> > +     ret = efi_get_variable_int(u"BootNext",
> > +                                &efi_global_variable_guid,
> > +                                NULL, &size, &bootnext, NULL);
> > +     if (ret == EFI_SUCCESS)
> > +             /* BootNext does exist here, try to boot */
> > +             run_command("bootefi bootmgr", 0);
> > +}
> > +
> >   static void bootmenu_show(int delay)
> >   {
> >       int init = 0;
> > @@ -482,8 +609,12 @@ static void bootmenu_show(int delay)
> >       struct menu *menu;
> >       struct bootmenu_data *bootmenu;
> >       struct bootmenu_entry *iter;
> > +     efi_status_t efi_ret = EFI_SUCCESS;
> >       char *option, *sep;
> >
> > +     if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR))
> > +             handle_uefi_bootnext();
> > +
> >       /* If delay is 0 do not create menu, just run first entry */
> >       if (delay == 0) {
> >               option = bootmenu_getoption(0);
> > @@ -532,6 +663,27 @@ static void bootmenu_show(int delay)
> >               command = strdup(iter->command);
> >       }
> >
> > +     /*
> > +      * If the selected entry is UEFI BOOT####, set the BootNext variable.
> > +      * Then uefi bootmgr is invoked by the preset command in iter->command.
> > +      */
> > +     if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR)) {
> > +             if (iter->type == BOOTMENU_TYPE_UEFI_BOOT_OPTION) {
> > +                     /*
> > +                      * UEFI specification requires BootNext variable needs non-volatile
> > +                      * attribute, but this BootNext is only used inside of U-Boot and
> > +                      * removed by efi bootmgr once BootNext is processed.
> > +                      * So this BootNext can be volatile.
> > +                      */
> > +                     efi_ret = efi_set_variable_int(u"BootNext", &efi_global_variable_guid,
> > +                                                    EFI_VARIABLE_BOOTSERVICE_ACCESS |
> > +                                                    EFI_VARIABLE_RUNTIME_ACCESS,
> > +                                                    sizeof(u16), &iter->bootorder, false);
> > +                     if (efi_ret != EFI_SUCCESS)
> > +                             goto cleanup;
> > +             }
> > +     }
> > +
> >   cleanup:
> >       menu_destroy(menu);
> >       bootmenu_destroy(bootmenu);
> > @@ -545,7 +697,8 @@ cleanup:
> >       if (title && command) {
> >               debug("Starting entry '%ls'\n", title);
> >               free(title);
> > -             run_command(command, 0);
> > +             if (efi_ret == EFI_SUCCESS)
> > +                     run_command(command, 0);
> >               free(command);
> >       }
> >
>

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

* Re: [PATCH v5 13/17] efi_loader: menu-driven addition of UEFI boot option
  2022-05-06 18:16             ` Heinrich Schuchardt
@ 2022-05-09  9:27               ` Masahisa Kojima
  2022-05-09 12:56                 ` Heinrich Schuchardt
  0 siblings, 1 reply; 58+ messages in thread
From: Masahisa Kojima @ 2022-05-09  9:27 UTC (permalink / raw)
  To: Heinrich Schuchardt
  Cc: Mark Kettenis, ilias.apalodimas, sjg, takahiro.akashi,
	francois.ozog, u-boot

On Sat, 7 May 2022 at 03:16, Heinrich Schuchardt <xypron.glpk@gmx.de> wrote:
>
>
>
> Am 6. Mai 2022 20:10:58 MESZ schrieb Mark Kettenis <mark.kettenis@xs4all.nl>:
> >> Date: Fri, 6 May 2022 19:30:51 +0200
> >> From: Heinrich Schuchardt <xypron.glpk@gmx.de>
> >>
> >> On 4/30/22 14:49, Heinrich Schuchardt wrote:
> >> > On 4/29/22 12:56, Heinrich Schuchardt wrote:
> >> >> On 4/28/22 18:33, Heinrich Schuchardt wrote:
> >> >>> On 4/28/22 10:09, Masahisa Kojima wrote:
> >> >>>> This commit supports the menu-driven UEFI boot option addition.
> >> >>>> User can select the block device volume having
> >> >>>> efi_simple_file_system_protocol and select the file corresponding
> >> >>>> to the Boot#### variable. Then user enter the label of the BOOT####
> >> >>>> variable in utf8.
> >> >>>>
> >> >>>> Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
> >>
> >> This patch creates unexpected behavior:
> >>
> >> In the console I entered:
> >>
> >> setenv bootmenu_0 foo=echo foo
> >> setenv bootmenu_1 bar=echo bar
> >> bootmenu 20
> >>
> >> Nothing here relates to UEFI but the menu shows:
> >>
> >>        foo
> >>        bar
> >>        UEFI Boot Manager Maintenance
> >>        Quit
> >>
> >> Please, don't show 'UEFI Boot Manager Maintenance' if we are not in the
> >> boot manager.
> >
> >Not sure what you mean with this.  The "bootefi bootmgr" command
> >simply looks at EFI variables and immediately exits if "BootOrder"
> >isn't set.  So the EFI varaibles need to be modified beforehands.
> >
> >Do you mean that 'UEFI Boot Manager Maintenance' should only be
> >included in the menu if any "Boot####" EFI variables exist (either
> >because they have been explicitly set or because we have generated
> >them for the "removable media" device paths)?
>
>
> The bootmenu command can be used in many contexts. Not all are related to booting.

I am still not sure the meaning of "don't show 'UEFI Boot Manager
Maintenance' if we are not in the boot manager."
What do you mean "in the boot manager"?
The bootmenu already supports to enumerate the UEFI boot options,
so it is some kind of boot manager.

Thanks,
Masahisa Kojima

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

* Re: [PATCH v5 13/17] efi_loader: menu-driven addition of UEFI boot option
  2022-05-09  9:27               ` Masahisa Kojima
@ 2022-05-09 12:56                 ` Heinrich Schuchardt
  0 siblings, 0 replies; 58+ messages in thread
From: Heinrich Schuchardt @ 2022-05-09 12:56 UTC (permalink / raw)
  To: Masahisa Kojima
  Cc: Mark Kettenis, ilias.apalodimas, sjg, takahiro.akashi,
	francois.ozog, u-boot

On 5/9/22 11:27, Masahisa Kojima wrote:
> On Sat, 7 May 2022 at 03:16, Heinrich Schuchardt <xypron.glpk@gmx.de> wrote:
>>
>>
>>
>> Am 6. Mai 2022 20:10:58 MESZ schrieb Mark Kettenis <mark.kettenis@xs4all.nl>:
>>>> Date: Fri, 6 May 2022 19:30:51 +0200
>>>> From: Heinrich Schuchardt <xypron.glpk@gmx.de>
>>>>
>>>> On 4/30/22 14:49, Heinrich Schuchardt wrote:
>>>>> On 4/29/22 12:56, Heinrich Schuchardt wrote:
>>>>>> On 4/28/22 18:33, Heinrich Schuchardt wrote:
>>>>>>> On 4/28/22 10:09, Masahisa Kojima wrote:
>>>>>>>> This commit supports the menu-driven UEFI boot option addition.
>>>>>>>> User can select the block device volume having
>>>>>>>> efi_simple_file_system_protocol and select the file corresponding
>>>>>>>> to the Boot#### variable. Then user enter the label of the BOOT####
>>>>>>>> variable in utf8.
>>>>>>>>
>>>>>>>> Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
>>>>
>>>> This patch creates unexpected behavior:
>>>>
>>>> In the console I entered:
>>>>
>>>> setenv bootmenu_0 foo=echo foo
>>>> setenv bootmenu_1 bar=echo bar
>>>> bootmenu 20
>>>>
>>>> Nothing here relates to UEFI but the menu shows:
>>>>
>>>>         foo
>>>>         bar
>>>>         UEFI Boot Manager Maintenance
>>>>         Quit
>>>>
>>>> Please, don't show 'UEFI Boot Manager Maintenance' if we are not in the
>>>> boot manager.
>>>
>>> Not sure what you mean with this.  The "bootefi bootmgr" command
>>> simply looks at EFI variables and immediately exits if "BootOrder"
>>> isn't set.  So the EFI varaibles need to be modified beforehands.
>>>
>>> Do you mean that 'UEFI Boot Manager Maintenance' should only be
>>> included in the menu if any "Boot####" EFI variables exist (either
>>> because they have been explicitly set or because we have generated
>>> them for the "removable media" device paths)?
>>
>>
>> The bootmenu command can be used in many contexts. Not all are related to booting.
>
> I am still not sure the meaning of "don't show 'UEFI Boot Manager
> Maintenance' if we are not in the boot manager."
> What do you mean "in the boot manager"?

The bootmenu command is used to show a generic menu. It should only show
the menu items selected via the bootmenu_* variables or parameters of
the bootmenu command.

With CONFIG_AUTOBOOT_MENU_SHOW=y a special menu is shown instead of the
normal console countdown. Here you can show auto-generated entries.

Best regards

Heinrich


> The bootmenu already supports to enumerate the UEFI boot options,
> so it is some kind of boot manager.
>
> Thanks,
> Masahisa Kojima


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

* Re: [PATCH v5 16/17] bootmenu: add removable media entries
  2022-05-09  8:23     ` Masahisa Kojima
@ 2022-05-09 13:01       ` Heinrich Schuchardt
  2022-05-16  9:20         ` Masahisa Kojima
  0 siblings, 1 reply; 58+ messages in thread
From: Heinrich Schuchardt @ 2022-05-09 13:01 UTC (permalink / raw)
  To: Masahisa Kojima
  Cc: Ilias Apalodimas, Simon Glass, Takahiro Akashi, Francois Ozog,
	Mark Kettenis, u-boot

On 5/9/22 10:23, Masahisa Kojima wrote:
> On Fri, 29 Apr 2022 at 01:53, Heinrich Schuchardt <xypron.glpk@gmx.de> wrote:
>>
>> On 4/28/22 10:09, Masahisa Kojima wrote:
>>> UEFI specification requires booting from removal media using
>>> a architecture-specific default image name such as BOOTAA64.EFI.
>>> This commit adds the removable media entries into bootmenu,
>>> so that user can select the removable media and boot with
>>> default image.
>>>
>>> The bootmenu automatically enumerates the possible bootable
>>> media devices supporting EFI_SIMPLE_FILE_SYSTEM_PROTOCOL,
>>> add it as new UEFI boot option(BOOT####) and update BootOrder
>>> variable. This automatically generated UEFI boot option
>>> has the dedicated guid in the optional_data to distinguish it from
>>> the UEFI boot option user adds manually.
>>>
>>> This commit also provides the BOOT#### variable maintenance feature.
>>> Depending on the system hardware setup, some devices
>>> may not exist at a later system boot, so bootmenu checks the
>>> available device in each bootmenu invocation and automatically
>>> removes the BOOT#### variable corrensponding to the non-existent
>>> media device.
>>>
>>> Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
>>> ---
>>> Changes in v5:
>>> - Return EFI_SUCCESS if there is no BootOrder defined
>>> - correctly handle the case if no removable device found
>>> - use guid to identify the automatically generated entry by bootmenu
>>>
>>> Newly created in v4
>>>
>>>    cmd/bootmenu.c                            |  94 +++++++++++++++
>>>    include/efi_loader.h                      |  20 ++++
>>>    lib/efi_loader/efi_bootmenu_maintenance.c | 139 ++++++++++++++++++++++
>>>    3 files changed, 253 insertions(+)
>>>
>>> diff --git a/cmd/bootmenu.c b/cmd/bootmenu.c
>>> index 860cb83182..970db3ee01 100644
>>> --- a/cmd/bootmenu.c
>>> +++ b/cmd/bootmenu.c
>>> @@ -396,6 +396,89 @@ static int is_blk_device_available(char *token)
>>>        return -ENODEV;
>>>    }
>>>
>>> +/**
>>> + * prepare_media_device_entry() - generate the media device entries
>>> + *
>>> + * This function enumerates all devices supporting EFI_SIMPLE_FILE_SYSTEM_PROTOCOL
>>> + * and generate the bootmenu entries.
>>> + * This function also provide the BOOT#### variable maintenance for
>>> + * the media device entries.
>>> + *   - Automatically create the BOOT#### variable for the newly detected device,
>>> + *     this BOOT#### variable is distinguished by the special GUID
>>> + *     stored in the EFI_LOAD_OPTION.optional_data
>>> + *   - If the device is not attached to the system, the associated BOOT#### variable
>>> + *     is automatically deleted.
>>> + *
>>> + * Return:   status code
>>> + */
>>> +static efi_status_t prepare_media_device_entry(void)
>>> +{
>>> +     u32 i;
>>> +     efi_status_t ret;
>>> +     efi_uintn_t count;
>>> +     efi_handle_t *volume_handles = NULL;
>>> +     struct efi_bootmenu_media_boot_option *opt = NULL;
>>> +
>>> +     ret = efi_locate_handle_buffer_int(BY_PROTOCOL, &efi_simple_file_system_protocol_guid,
>>> +                                        NULL, &count, (efi_handle_t **)&volume_handles);
>>> +     if (ret != EFI_SUCCESS)
>>> +             return ret;
>>> +
>>> +     opt = calloc(count, sizeof(struct efi_bootmenu_media_boot_option));
>>> +     if (!opt)
>>> +             goto out;
>>> +
>>> +     /* enumerate all devices supporting EFI_SIMPLE_FILE_SYSTEM_PROTOCOL */
>>> +     ret = efi_bootmenu_enumerate_boot_option(opt, volume_handles, count);
>>> +     if (ret != EFI_SUCCESS)
>>> +             goto out;
>>> +
>>> +     /*
>>> +      * System hardware configuration may vary depending on the user setup.
>>> +      * The boot option is automatically added by the bootmenu.
>>> +      * If the device is not attached to the system, the boot option needs
>>> +      * to be deleted.
>>> +      */
>>> +     ret = efi_bootmenu_delete_invalid_boot_option(opt, count);
>>> +     if (ret != EFI_SUCCESS)
>>> +             goto out;
>>> +
>>> +     /* add non-existent boot option */
>>> +     for (i = 0; i < count; i++) {
>>> +             u32 boot_index;
>>> +             u16 var_name[9];
>>> +
>>> +             if (!opt[i].exist) {
>>> +                     ret = efi_bootmenu_get_unused_bootoption(var_name, sizeof(var_name),
>>> +                                                              &boot_index);
>>> +                     if (ret != EFI_SUCCESS)
>>> +                             goto out;
>>> +
>>> +                     ret = efi_set_variable_int(var_name, &efi_global_variable_guid,
>>> +                                                EFI_VARIABLE_NON_VOLATILE |
>>> +                                                EFI_VARIABLE_BOOTSERVICE_ACCESS |
>>> +                                                EFI_VARIABLE_RUNTIME_ACCESS,
>>> +                                                opt[i].size, opt[i].lo, false);
>>> +                     if (ret != EFI_SUCCESS)
>>> +                             goto out;
>>> +
>>> +                     ret = efi_bootmenu_append_bootorder(boot_index);
>>> +                     if (ret != EFI_SUCCESS)
>>> +                             goto out;
>>> +             }
>>> +     }
>>> +
>>> +out:
>>> +     if (opt) {
>>> +             for (i = 0; i < count; i++)
>>> +                     free(opt[i].lo);
>>> +     }
>>> +     free(opt);
>>> +     efi_free_pool(volume_handles);
>>> +
>>> +     return ret;
>>> +}
>>> +
>>>    /**
>>>     * prepare_distro_boot_entry() - generate the distro boot entries
>>>     *
>>> @@ -500,6 +583,7 @@ static int prepare_distro_boot_entry(struct bootmenu_data *menu,
>>>    static struct bootmenu_data *bootmenu_create(int delay)
>>>    {
>>>        int ret;
>>> +     efi_status_t efi_ret;
>>>        unsigned short int i = 0;
>>>        struct bootmenu_data *menu;
>>>        struct bootmenu_entry *iter = NULL;
>>> @@ -523,6 +607,16 @@ static struct bootmenu_data *bootmenu_create(int delay)
>>>                goto cleanup;
>>>
>>>        if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR)) {
>>> +             if (i < MAX_DYNAMIC_ENTRY) {
>>> +                     /*
>>> +                      * UEFI specification requires booting from removal media using
>>> +                      * a architecture-specific default image name such as BOOTAA64.EFI.
>>> +                      */
>>> +                     efi_ret = prepare_media_device_entry();
>>> +                     if (efi_ret != EFI_SUCCESS && efi_ret != EFI_NOT_FOUND)
>>> +                             goto cleanup;
>>> +             }
>>> +
>>>                if (i < MAX_DYNAMIC_ENTRY) {
>>>                        ret = prepare_uefi_bootorder_entry(menu, &iter, &i);
>>>                        if (ret < 0 && ret != -ENOENT)
>>> diff --git a/include/efi_loader.h b/include/efi_loader.h
>>> index 533618341b..bef492ccb9 100644
>>> --- a/include/efi_loader.h
>>> +++ b/include/efi_loader.h
>>> @@ -928,6 +928,22 @@ struct efi_signature_store {
>>>    struct x509_certificate;
>>>    struct pkcs7_message;
>>>
>>> +/**
>>> + * struct efi_bootmenu_media_boot_option - boot option for (removable) media device
>>> + *
>>> + * This structure is used to enumerate possible boot option
>>> + *
>>> + * @lo:              Serialized load option data
>>> + * @size:    Size of serialized load option data
>>> + * @exist:   Flag to indicate the load option already exists
>>> + *           in Non-volatile load option
>>> + */
>>> +struct efi_bootmenu_media_boot_option {
>>> +     void *lo;
>>> +     efi_uintn_t size;
>>> +     bool exist;
>>> +};
>>> +
>>>    bool efi_signature_lookup_digest(struct efi_image_regions *regs,
>>>                                 struct efi_signature_store *db,
>>>                                 bool dbx);
>>> @@ -1076,6 +1092,10 @@ efi_status_t efi_console_get_u16_string
>>>    efi_status_t efi_bootmenu_get_unused_bootoption(u16 *buf,
>>>                                                efi_uintn_t buf_size, u32 *index);
>>>    efi_status_t efi_bootmenu_append_bootorder(u16 index);
>>> +efi_status_t efi_bootmenu_enumerate_boot_option(struct efi_bootmenu_media_boot_option *opt,
>>> +                                             efi_handle_t *volume_handles, efi_status_t count);
>>> +efi_status_t efi_bootmenu_delete_invalid_boot_option(struct efi_bootmenu_media_boot_option *opt,
>>> +                                                  efi_status_t count);
>>>
>>>    efi_status_t efi_disk_get_device_name(struct efi_block_io *this, char *buf, int size);
>>>
>>> diff --git a/lib/efi_loader/efi_bootmenu_maintenance.c b/lib/efi_loader/efi_bootmenu_maintenance.c
>>> index 8c3f94c695..33b37fd11a 100644
>>> --- a/lib/efi_loader/efi_bootmenu_maintenance.c
>>> +++ b/lib/efi_loader/efi_bootmenu_maintenance.c
>>> @@ -26,6 +26,13 @@ static struct efi_simple_text_output_protocol *cout;
>>>    #define EFI_BOOTMENU_BOOT_NAME_MAX 32
>>>    #define EFI_BOOT_ORDER_MAX_SIZE_IN_DECIMAL 6
>>>
>>> +#define EFI_BOOTMENU_AUTO_GENERATED_ENTRY_GUID \
>>> +     EFI_GUID(0x38c1acc1, 0x9fc0, 0x41f0, \
>>> +              0xb9, 0x01, 0xfa, 0x74, 0xd6, 0xd6, 0xe4, 0xde)
>>> +
>>> +static const efi_guid_t efi_guid_bootmenu_auto_generated =
>>> +             EFI_BOOTMENU_AUTO_GENERATED_ENTRY_GUID;
>>> +
>>>    typedef efi_status_t (*efi_bootmenu_entry_func)(void *data, bool *exit);
>>>
>>>    /**
>>> @@ -1104,3 +1111,135 @@ efi_status_t efi_bootmenu_show_maintenance_menu(void)
>>>                                          ARRAY_SIZE(maintenance_menu_items),
>>>                                          -1);
>>>    }
>>> +
>>> +efi_status_t efi_bootmenu_enumerate_boot_option(struct efi_bootmenu_media_boot_option *opt,
>>> +                                             efi_handle_t *volume_handles, efi_status_t count)
>>> +{
>>> +     u32 i;
>>> +     struct efi_handler *handler;
>>> +     efi_status_t ret = EFI_SUCCESS;
>>> +
>>> +     for (i = 0; i < count; i++) {
>>> +             char *optional_data;
>>> +             u16 *dev_name, *p;
>>> +             struct efi_load_option lo;
>>> +             struct efi_block_io *block_io;
>>> +             char buf[BOOTMENU_DEVICE_NAME_MAX];
>>> +             struct efi_device_path *device_path;
>>> +
>>> +             ret = efi_search_protocol(volume_handles[i], &efi_guid_device_path, &handler);
>>> +             if (ret != EFI_SUCCESS)
>>> +                     continue;
>>> +             ret = efi_protocol_open(handler, (void **)&device_path,
>>> +                                     efi_root, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
>>> +             if (ret != EFI_SUCCESS)
>>> +                     continue;
>>> +
>>> +             ret = efi_search_protocol(volume_handles[i], &efi_block_io_guid, &handler);
>>> +             if (ret != EFI_SUCCESS)
>>> +                     continue;
>>> +             ret = efi_protocol_open(handler, (void **)&block_io,
>>> +                                     efi_root, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
>>> +             if (ret != EFI_SUCCESS)
>>> +                     continue;
>>> +
>>> +             efi_disk_get_device_name(block_io, buf, BOOTMENU_DEVICE_NAME_MAX);
>>> +             dev_name = calloc(1, (strlen(buf) + 1) * sizeof(u16));
>>> +             if (!dev_name) {
>>> +                     ret = EFI_OUT_OF_RESOURCES;
>>> +                     goto out;
>>> +             }
>>> +             p = dev_name;
>>> +             utf8_utf16_strncpy(&p, buf, strlen(buf));
>>> +
>>> +             lo.label = dev_name;
>>> +             lo.attributes = LOAD_OPTION_ACTIVE;
>>> +             lo.file_path = device_path;
>>> +             lo.file_path_length = efi_dp_size(device_path) + sizeof(END);
>>> +             /*
>>> +              * Set the dedicated guid to optional_data, it is used to identify
>>> +              * the boot option that automatically generated by the bootmenu.
>>> +              * efi_serialize_load_option() expects optional_data is null-terminated
>>> +              * utf8 string, so set the "dummystr" string to allocate enough space
>>> +              * to store guid, instead of realloc the load_option.
>>> +              *
>>> +              * This will allocate 16 bytes for guid plus trailing 0x0000.
>>> +              * The guid does not require trailing 0x0000, but it is for safety
>>> +              * in case some program handle the optional_data as u16 string.
>>> +              */
>>> +             lo.optional_data = "dummystr";
>>
>> Why do you want to reserve 18 bytes when 16 are enough for the GUID?
>
> OK, I will only allocate 16 bytes for GUID.
>
>>
>>> +             opt[i].size = efi_serialize_load_option(&lo, (u8 **)&opt[i].lo);
>>> +             if (!opt[i].size) {
>>> +                     ret = EFI_OUT_OF_RESOURCES;
>>> +                     free(dev_name);
>>> +                     goto out;
>>> +             }
>>> +             /* set the guid */
>>> +             optional_data = (char *)opt[i].lo + (opt[i].size - u16_strsize(u"dummystr"));
>>> +             memcpy(optional_data, &efi_guid_bootmenu_auto_generated, sizeof(efi_guid_t));
>>> +             free(dev_name);
>>> +     }
>>> +
>>> +out:
>>> +     return ret;
>>> +}
>>> +
>>> +efi_status_t efi_bootmenu_delete_invalid_boot_option(struct efi_bootmenu_media_boot_option *opt,
>>> +                                                  efi_status_t count)
>>> +{
>>> +     u16 *bootorder;
>>> +     u32 i, j;
>>> +     efi_status_t ret;
>>> +     efi_uintn_t num, size, bootorder_size;
>>> +     void *load_option;
>>> +     struct efi_load_option lo;
>>> +     u16 varname[] = u"Boot####";
>>> +
>>> +     bootorder = efi_get_var(u"BootOrder", &efi_global_variable_guid, &bootorder_size);
>>> +     if (!bootorder)
>>> +             return EFI_SUCCESS; /* BootOrder is not defined, nothing to do */
>>> +
>>> +     num = bootorder_size / sizeof(u16);
>>> +     for (i = 0; i < num;) {
>>> +             efi_uintn_t tmp;
>>> +
>>> +             efi_create_indexed_name(varname, sizeof(varname),
>>> +                                     "Boot", bootorder[i]);
>>> +             load_option = efi_get_var(varname, &efi_global_variable_guid, &size);
>>> +             if (!load_option)
>>> +                     goto next;
>>> +
>>> +             tmp = size;
>>
>> This copying of variables is superfluous. Just keep size.
>
> I don't catch your point.
> This efi_bootmenu_delete_invalid_boot_option() function
> does not copy the variable, only keeps the variable size.

There is no need to copy the value of variable size to a new variable
tmp. You can remove the variable tmp and continue using variable size
below as both variables are of the same type.

Best regards

Heinrich

>
>>
>>> +             ret = efi_deserialize_load_option(&lo, load_option, &tmp);
>>> +             if (ret != EFI_SUCCESS)
>>> +                     goto next;
>>> +
>>> +             if (guidcmp(lo.optional_data, &efi_guid_bootmenu_auto_generated) == 0) {
>>
>> You must avoid a buffer overrun. Check size >= 16.
>
> Yes, I need to check the size of optional_data.
>
> Thanks,
> Masahisa Kojima
>
>>
>> Best regards
>>
>> Heinrich
>>
>>> +                     for (j = 0; j < count; j++) {
>>> +                             if (memcmp(opt[j].lo, load_option, size) == 0) {
>>> +                                     opt[j].exist = true;
>>> +                                     break;
>>> +                             }
>>> +                     }
>>> +
>>> +                     if (j == count) {
>>> +                             ret = delete_boot_option(bootorder, i, bootorder_size);
>>> +                             if (ret != EFI_SUCCESS) {
>>> +                                     free(load_option);
>>> +                                     goto out;
>>> +                             }
>>> +
>>> +                             num--;
>>> +                             bootorder_size -= sizeof(u16);
>>> +                             free(load_option);
>>> +                             continue;
>>> +                     }
>>> +             }
>>> +next:
>>> +             free(load_option);
>>> +             i++;
>>> +     }
>>> +
>>> +out:
>>> +     return ret;
>>> +}
>>


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

* Re: [PATCH v5 10/17] bootmenu: add distro boot entry
  2022-05-01 21:48   ` Heinrich Schuchardt
@ 2022-05-12  8:44     ` Takahiro Akashi
  2022-05-12 10:39       ` Heinrich Schuchardt
  0 siblings, 1 reply; 58+ messages in thread
From: Takahiro Akashi @ 2022-05-12  8:44 UTC (permalink / raw)
  To: Heinrich Schuchardt
  Cc: Masahisa Kojima, u-boot, Ilias Apalodimas, Simon Glass,
	Francois Ozog, Mark Kettenis

On Sun, May 01, 2022 at 11:48:40PM +0200, Heinrich Schuchardt wrote:
> On 4/28/22 10:09, Masahisa Kojima wrote:
> > This commit adds the distro_boot entries into the bootmenu.
> > The bootmenu read the "boot_targets" U-Boot environment variable
> > and enumerate it.
> > User can select the distro boot entry, then bootmenu executes
> > "run bootcmd_xxx" command.
> > 
> > The bootmenu also checks the existing block devices and network
> > option("dhcp" and "pxe") availability, then filter out
> > the "boot_targets" appeared in bootmenu.
> > 
> > The bootmenu example is as follows, distro boot entry has the
> > "distro_boot" prefix.
> > 
> >    *** U-Boot Boot Menu ***
> > 
> >       distro_boot   : usb0
> >       distro_boot   : scsi0
> >       distro_boot   : virtio0
> >       distro_boot   : dhcp
> 
> You will be creating UEFI boot options for all block devices anyway.
> We should avoid duplicate entries. Please, drop this patch.

Nak
A distro entry should work, searching not only for a default UEFI file
but also sysboot (extlinux.conf) and boot scripts.

> Best regards
> 
> heinrich
> 
> > 
> > Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
> > ---
> > Changes in v5:
> > - split into the separate patch
> > - add function description comment
> > - handle the case boot_targets variable is empty
> > - filter out the non-exist device entry
> > 
> >   cmd/bootmenu.c | 177 +++++++++++++++++++++++++++++++++++++++++++++++++
> >   1 file changed, 177 insertions(+)
> > 
> > diff --git a/cmd/bootmenu.c b/cmd/bootmenu.c
> > index da688e6213..afe42b8041 100644
> > --- a/cmd/bootmenu.c
> > +++ b/cmd/bootmenu.c
> > @@ -7,6 +7,7 @@
> >   #include <common.h>
> >   #include <command.h>
> >   #include <ansi.h>
> > +#include <dm.h>
> >   #include <efi_loader.h>
> >   #include <efi_variable.h>
> >   #include <env.h>
> > @@ -14,6 +15,7 @@
> >   #include <menu.h>
> >   #include <watchdog.h>
> >   #include <malloc.h>
> > +#include <linux/ctype.h>
> >   #include <linux/delay.h>
> >   #include <linux/string.h>
> > 
> > @@ -31,6 +33,7 @@ enum boot_type {
> >   	BOOTMENU_TYPE_NONE = 0,
> >   	BOOTMENU_TYPE_BOOTMENU,
> >   	BOOTMENU_TYPE_UEFI_BOOT_OPTION,
> > +	BOOTMENU_TYPE_DISTRO_BOOT,
> >   };
> > 
> >   struct bootmenu_entry {
> > @@ -90,6 +93,8 @@ static void bootmenu_print_entry(void *data)
> >   		printf("bootmenu_%02d   : %ls", entry->bootorder, entry->title);
> >   	else if (entry->type == BOOTMENU_TYPE_UEFI_BOOT_OPTION)
> >   		printf("UEFI BOOT%04X : %ls", entry->bootorder, entry->title);
> > +	else if (entry->type == BOOTMENU_TYPE_DISTRO_BOOT)
> > +		printf("distro_boot   : %ls", entry->title);
> >   	else
> >   		printf("%ls", entry->title);
> > 
> > @@ -465,6 +470,172 @@ static int prepare_uefi_bootorder_entry(struct bootmenu_data *menu,
> >   	return 1;
> >   }
> > 
> > +static int is_blk_device_available(char *token)
> > +{
> > +	struct driver *d = ll_entry_start(struct driver, driver);
> > +	const int n_ents = ll_entry_count(struct driver, driver);
> > +	struct driver *entry;
> > +	struct udevice *udev;
> > +	struct uclass *uc;
> > +	struct blk_desc *desc;
> > +	int ret, i;
> > +	const char *if_type_name;
> > +
> > +	ret = uclass_get(UCLASS_BLK, &uc);
> > +	if (ret)
> > +		return -ENODEV;
> > +
> > +	for (entry = d; entry < d + n_ents; entry++) {
> > +		if (entry->id != UCLASS_BLK)
> > +			continue;
> > +		i = 0;
> > +		uclass_foreach_dev(udev, uc) {
> > +			if (udev->driver != entry)
> > +				continue;
> > +			desc = dev_get_uclass_plat(udev);
> > +			if_type_name = blk_get_if_type_name(desc->if_type);
> > +			if (strncmp(token, if_type_name, strlen(if_type_name)) == 0) {
> > +				char *p;
> > +				int j, len;
> > +				int devnum = 0;
> > +
> > +				p = token + strlen(if_type_name);
> > +				len = strlen(p);
> > +				if (!len)
> > +					continue; /* no device number */
> > +
> > +				for (j = 0; j < len; j++) {
> > +					if (!isdigit(*p)) {
> > +						/* invalid device number */
> > +						devnum = INT_MAX;
> > +						break;
> > +					}
> > +					devnum = (devnum * 10) + (*p++ - '0');
> > +				}
> > +
> > +				if (devnum == INT_MAX)
> > +					continue;
> > +
> > +				if (devnum == desc->devnum)
> > +					return 1;
> > +			}
> > +		}
> > +	}
> > +
> > +	if (strncmp(token, "dhcp", strlen("dhcp")) == 0) {
> > +		if (IS_ENABLED(CONFIG_CMD_DHCP))
> > +			return 1;
> > +	}
> > +
> > +	if (strncmp(token, "pxe", strlen("pxe")) == 0) {
> > +		if (IS_ENABLED(CONFIG_CMD_PXE))
> > +			return 1;
> > +	}
> > +
> > +	return -ENODEV;
> > +}
> > +
> > +/**
> > + * prepare_distro_boot_entry() - generate the distro boot entries
> > + *
> > + * This function read the "boot_targets" U-Boot environment variable
> > + * and generate the bootmenu entries.
> > + *
> > + * @menu:	pointer to the bootmenu structure
> > + * @current:	pointer to the last bootmenu entry list
> > + * @index:	pointer to the index of the last bootmenu entry,
> > + *		the number of uefi entry is added by this function
> > + * Return:	1 on success, negative value on error
> > + */
> > +static int prepare_distro_boot_entry(struct bootmenu_data *menu,
> > +				     struct bootmenu_entry **current,
> > +				     unsigned short int *index)
> > +{
> > +	char *p;
> > +	int len;
> > +	char *token;
> > +	char *boot_targets;
> > +	unsigned short int i = *index;
> > +	struct bootmenu_entry *entry = NULL;
> > +	struct bootmenu_entry *iter = *current;
> > +
> > +	/* list the distro boot "boot_targets" */
> > +	boot_targets = env_get("boot_targets");
> > +	if (!boot_targets)
> > +		return -ENOENT;
> > +
> > +	len = strlen(boot_targets);
> > +	if (!len)
> > +		return -ENOENT;
> > +
> > +	p = calloc(1, len + 1);
> > +	strlcpy(p, boot_targets, len);
> > +
> > +	token = strtok(p, " ");
> > +
> > +	do {
> > +		u16 *buf;
> > +		char *command;
> > +		int command_size;
> > +
> > +		if (is_blk_device_available(token) != 1) {
> > +			token = strtok(NULL, " ");
> > +			continue;
> > +		}
> > +
> > +		entry = malloc(sizeof(struct bootmenu_entry));
> > +		if (!entry) {
> > +			free(p);
> > +			return -ENOMEM;
> > +		}
> > +
> > +		len = strlen(token);
> > +		buf = calloc(1, (len + 1) * sizeof(u16));
> > +		entry->title = buf;
> > +		if (!entry->title) {
> > +			free(entry);
> > +			free(p);
> > +			return -ENOMEM;
> > +		}
> > +		utf8_utf16_strncpy(&buf, token, len);
> > +		sprintf(entry->key, "%d", i);
> > +		entry->num = i;
> > +		entry->menu = menu;
> > +
> > +		command_size = sizeof("run bootcmd_") + len;
> > +		command = calloc(1, command_size);
> > +		if (!command) {
> > +			free(entry->title);
> > +			free(entry);
> > +			free(p);
> > +			return -ENOMEM;
> > +		}
> > +		snprintf(command, command_size, "run bootcmd_%s", token);
> > +		entry->command = command;
> > +		entry->type = BOOTMENU_TYPE_DISTRO_BOOT;
> > +		entry->next = NULL;
> > +
> > +		if (!iter)
> > +			menu->first = entry;
> > +		else
> > +			iter->next = entry;
> > +
> > +		iter = entry;
> > +		i++;
> > +
> > +		if (i == MAX_COUNT - 1)
> > +			break;
> > +
> > +		token = strtok(NULL, " ");
> > +	} while (token);
> > +
> > +	free(p);
> > +	*index = i;
> > +	*current = iter;
> > +
> > +	return 1;
> > +}
> > +
> >   static struct bootmenu_data *bootmenu_create(int delay)
> >   {
> >   	int ret;
> > @@ -498,6 +669,12 @@ static struct bootmenu_data *bootmenu_create(int delay)
> >   		}
> >   	}
> > 
> > +	if (i < MAX_COUNT - 1) {
> > +		ret = prepare_distro_boot_entry(menu, &iter, &i);
> > +		if (ret < 0 && ret != -ENOENT)
> > +			goto cleanup;
> > +	}
> > +
> >   	/* Add U-Boot console entry at the end */
> >   	if (i <= MAX_COUNT - 1) {
> >   		entry = malloc(sizeof(struct bootmenu_entry));
> 

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

* Re: [PATCH v5 06/17] efi_loader: bootmgr: add booting from removable media
  2022-05-05 12:47       ` Mark Kettenis
@ 2022-05-12  9:12         ` AKASHI Takahiro
  2022-05-12 10:34           ` Heinrich Schuchardt
  0 siblings, 1 reply; 58+ messages in thread
From: AKASHI Takahiro @ 2022-05-12  9:12 UTC (permalink / raw)
  To: xypron.glpk
  Cc: Mark Kettenis, masahisa.kojima, ilias.apalodimas, sjg,
	francois.ozog, kettenis, Peter.Hoyes, narmstrong, andre.przywara,
	u-boot

Heinrich,

On Thu, May 05, 2022 at 02:47:35PM +0200, Mark Kettenis wrote:
> > Date: Thu, 5 May 2022 14:05:04 +0200 (CEST)
> > From: Mark Kettenis <mark.kettenis@xs4all.nl>
> > 
> > > Date: Fri, 29 Apr 2022 19:03:22 +0200
> > > From: Heinrich Schuchardt <xypron.glpk@gmx.de>
> > > 
> > > On 4/28/22 10:09, Masahisa Kojima wrote:
> > > > From: AKASHI Takahiro <takahiro.akashi@linaro.org>
> > > >
> > > > Under the current implementation, booting from removable media using
> > > > a architecture-specific default image name, say BOOTAA64.EFI, is
> > > > supported only in distro_bootcmd script. See the commit 74522c898b35
> > > > ("efi_loader: Add distro boot script for removable media").
> > > >
> > > > This is, however, half-baked implementation because
> > > > 1) UEFI specification requires this feature to be implemented as part
> > > >     of Boot Manager's responsibility:
> > > >
> > > >    3 - Boot Manager
> > > >    3.5.1 Boot via the Simple File Protocol
> > > >    When booting via the EFI_SIMPLE_FILE_SYSTEM_PROTOCOL, the FilePath will
> > > >    start with a device path that points to the device that implements the
> > > >    EFI_SIMPLE_FILE_SYSTEM_PROTOCOL or the EFI_BLOCK_IO_PROTOCOL. The next
> > > >    part of the FilePath may point to the file name, including
> > > >    subdirectories, which contain the bootable image. If the file name is
> > > >    a null device path, the file name must be generated from the rules
> > > >    defined below.
> > > >    ...
> > > >    3.5.1.1 Removable Media Boot Behavior
> > > >    To generate a file name when none is present in the FilePath, the
> > > >    firmware must append a default file name in the form
> > > >    \EFI\BOOT\BOOT{machine type short-name}.EFI ...
> > > >
> > > > 2) So (1) entails the hehavior that the user's preference of boot media
> > > >     order should be determined by Boot#### and BootOrder variables.
> > > >
> > > > With this patch, the semantics mentioned above is fully implemented.
> > > > For example, if you want to boot the system from USB and SCSI in this
> > > > order,
> > > > * define Boot0001 which contains only a device path to the USB device
> > > >    (without any file path/name)
> > > > * define Boot0002 which contains only a device path to the SCSI device,
> > > > and
> > > > * set BootOrder to Boot0001:Boot0002
> > > >
> > > > To avoid build error for sandbox, default file name "BOOTSANDBOX.efi"
> > > > is defined even if it is out of scope of UEFI specification.
> > > >
> > > > Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> > > > Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
> > > > ---
> > > > Changes in v5:
> > > > - add default file name definition for SANDBOX to avoid build error
> > > >
> > > > Changes from original version:
> > > > - create new include file "efi_default_filename.h" to
> > > >    avoid conflict with config_distro_bootcmd.h
> > > > - modify the target pointer of efi_free_pool(), expand_media_path() should
> > > >    only free the pointer allocated by efi_dp_from_file() function.
> > > >   include/config_distro_bootcmd.h | 14 +--------
> > > >   include/efi_default_filename.h  | 33 ++++++++++++++++++++++
> > > >   lib/efi_loader/efi_bootmgr.c    | 50 ++++++++++++++++++++++++++++++++-
> > > >   3 files changed, 83 insertions(+), 14 deletions(-)
> > > >   create mode 100644 include/efi_default_filename.h
> > > >
> > > > diff --git a/include/config_distro_bootcmd.h b/include/config_distro_bootcmd.h
> > > > index c55023889c..6a3110f27b 100644
> > > > --- a/include/config_distro_bootcmd.h
> > > > +++ b/include/config_distro_bootcmd.h
> > > > @@ -91,19 +91,7 @@
> > > >   #endif
> > > >
> > > >   #ifdef CONFIG_EFI_LOADER
> > > > -#if defined(CONFIG_ARM64)
> > > > -#define BOOTEFI_NAME "bootaa64.efi"
> > > > -#elif defined(CONFIG_ARM)
> > > > -#define BOOTEFI_NAME "bootarm.efi"
> > > > -#elif defined(CONFIG_X86_RUN_32BIT)
> > > > -#define BOOTEFI_NAME "bootia32.efi"
> > > > -#elif defined(CONFIG_X86_RUN_64BIT)
> > > > -#define BOOTEFI_NAME "bootx64.efi"
> > > > -#elif defined(CONFIG_ARCH_RV32I)
> > > > -#define BOOTEFI_NAME "bootriscv32.efi"
> > > > -#elif defined(CONFIG_ARCH_RV64I)
> > > > -#define BOOTEFI_NAME "bootriscv64.efi"
> > > > -#endif
> > > > +#include <efi_default_filename.h>
> > > >   #endif
> > > >
> > > >   #ifdef BOOTEFI_NAME
> > > > diff --git a/include/efi_default_filename.h b/include/efi_default_filename.h
> > > > new file mode 100644
> > > > index 0000000000..cb2ef9e131
> > > > --- /dev/null
> > > > +++ b/include/efi_default_filename.h
> > > > @@ -0,0 +1,33 @@
> > > > +/* SPDX-License-Identifier: GPL-2.0+ */
> > > > +/*
> > > > + * Default boot file name when none is present in the FilePath.
> > > > + * This is defined in the UEFI specification.
> > > > + *
> > > > + * Copyright (c) 2022, Linaro Limited
> > > > + */
> > > > +#ifndef _EFI_DEFAULT_FILENAME_H
> > > > +#define _EFI_DEFAULT_FILENAME_H
> > > > +
> > > > +#if defined(CONFIG_ARM64)
> > > > +#define BOOTEFI_NAME "BOOTAA64.EFI"
> > > > +#elif defined(CONFIG_ARM)
> > > > +#define BOOTEFI_NAME "BOOTARM.EFI"
> > > > +#elif defined(CONFIG_X86_64)
> > > > +#define BOOTEFI_NAME "BOOTX64.EFI"
> > > > +#elif defined(CONFIG_X86)
> > > > +#define BOOTEFI_NAME "BOOTIA32.EFI"
> > > > +#elif defined(CONFIG_ARCH_RV32I)
> > > > +#define BOOTEFI_NAME "BOOTRISCV32.EFI"
> > > > +#elif defined(CONFIG_ARCH_RV64I)
> > > > +#define BOOTEFI_NAME "BOOTRISCV64.EFI"
> > > > +#elif defined(CONFIG_SANDBOX)
> > > > +/*
> > > > + * SANDBOX is not defined in UEFI specification, but
> > > > + * this definition avoids build failure for SANDBOX.
> > > > + */
> > > > +#define BOOTEFI_NAME "BOOTSANDBOX.EFI"
> > > 
> > > The sandbox should boot the default binary for the host architecture:
> > > 
> > > #ifndef _EFI_DEFAULT_FILENAME_H
> > > #define _EFI_DEFAULT_FILENAME_H
> > > 
> > > #include <host_arch.h>
> > > 
> > > #undef BOOTEFI_NAME
> > > 
> > > #if HOST_ARCH == HOST_ARCH_X86_64
> > > #define BOOTEFI_NAME "BOOTX64.EFI"
> > > #endif
> > > 
> > > #if HOST_ARCH == HOST_ARCH_X86
> > > #define BOOTEFI_NAME "BOOTIA32.EFI"
> > > #endif
> > > 
> > > #if HOST_ARCH == HOST_ARCH_AARCH64
> > > #define BOOTEFI_NAME "BOOTAA64.EFI"
> > > #endif
> > > 
> > > #if HOST_ARCH == HOST_ARCH_ARM
> > > #define BOOTEFI_NAME "BOOTARM.EFI"
> > > #endif
> > > 
> > > #if HOST_ARCH == HOST_ARCH_RISCV32
> > > #define BOOTEFI_NAME "BOOTRISCV32.EFI"
> > > #endif
> > > 
> > > #if HOST_ARCH == HOST_ARCH_RISCV64
> > > #define BOOTEFI_NAME "BOOTRISCV64.EFI"
> > > #endif
> > > 
> > > #ifndef BOOTEFI_NAME
> > > #error Unsupported UEFI architecture
> > > #endif
> > > 
> > > #endif
> > 
> > Maybe sanbox is special, but using the host architecture for actual
> > boards makes no sense.  I see this has made its way into master
> > already, but when I cross-build for apple_m1_defconfig on an amd64
> > machine I end up with:
> > 
> >     $ strings ./lib/efi_loader/efi_bootmgr.o | grep BOOT
> >     /EFI/BOOT/BOOTX64.EFI
> > 
> > The original diff that used CONFIG_ARM64, CONFIG_ARM, etc, did this
> > right.
> 
> Hmm, forget about that.  The problem is in the sed expression in the
> toplevel Makefile that is used to MK_ARCH that ends up setting
> HOST_ARCH incorrectly.  Using a variable named HOST_ARCH to hold the
> target architecture doesn't help understanding what's going wrong here
> of course...

I can nod to Mark's comment here.
Even though HOST_ARCH might work, it's quite confusing.
Apparently it looks wrong as Mark said.

Please address this comment properly as you modified my patch.

What I basically recommend is:
* In (top-dir)/Makefile
  - s/HOST_ARCH/MK_ARCH/g
  - add
      if (CONFIG_SANDBOX)
          HOST_ARCH = $(MK_ARCH)
  - remove "undefine MK_ARCH"
* In efi_default_filename.h
  - s/HOST_ARCH/MK_ARCH/g

This way, HOST_ARCH is only used in sandbox-related code.

-Takahiro Akashi

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

* Re: [PATCH v5 06/17] efi_loader: bootmgr: add booting from removable media
  2022-05-12  9:12         ` AKASHI Takahiro
@ 2022-05-12 10:34           ` Heinrich Schuchardt
  0 siblings, 0 replies; 58+ messages in thread
From: Heinrich Schuchardt @ 2022-05-12 10:34 UTC (permalink / raw)
  To: AKASHI Takahiro
  Cc: Mark Kettenis, masahisa.kojima, ilias.apalodimas, sjg,
	francois.ozog, kettenis, Peter.Hoyes, narmstrong, andre.przywara,
	u-boot

Am 12. Mai 2022 11:12:47 MESZ schrieb AKASHI Takahiro <takahiro.akashi@linaro.org>:
>Heinrich,
>
>On Thu, May 05, 2022 at 02:47:35PM +0200, Mark Kettenis wrote:
>> > Date: Thu, 5 May 2022 14:05:04 +0200 (CEST)
>> > From: Mark Kettenis <mark.kettenis@xs4all.nl>
>> > 
>> > > Date: Fri, 29 Apr 2022 19:03:22 +0200
>> > > From: Heinrich Schuchardt <xypron.glpk@gmx.de>
>> > > 
>> > > On 4/28/22 10:09, Masahisa Kojima wrote:
>> > > > From: AKASHI Takahiro <takahiro.akashi@linaro.org>
>> > > >
>> > > > Under the current implementation, booting from removable media using
>> > > > a architecture-specific default image name, say BOOTAA64.EFI, is
>> > > > supported only in distro_bootcmd script. See the commit 74522c898b35
>> > > > ("efi_loader: Add distro boot script for removable media").
>> > > >
>> > > > This is, however, half-baked implementation because
>> > > > 1) UEFI specification requires this feature to be implemented as part
>> > > >     of Boot Manager's responsibility:
>> > > >
>> > > >    3 - Boot Manager
>> > > >    3.5.1 Boot via the Simple File Protocol
>> > > >    When booting via the EFI_SIMPLE_FILE_SYSTEM_PROTOCOL, the FilePath will
>> > > >    start with a device path that points to the device that implements the
>> > > >    EFI_SIMPLE_FILE_SYSTEM_PROTOCOL or the EFI_BLOCK_IO_PROTOCOL. The next
>> > > >    part of the FilePath may point to the file name, including
>> > > >    subdirectories, which contain the bootable image. If the file name is
>> > > >    a null device path, the file name must be generated from the rules
>> > > >    defined below.
>> > > >    ...
>> > > >    3.5.1.1 Removable Media Boot Behavior
>> > > >    To generate a file name when none is present in the FilePath, the
>> > > >    firmware must append a default file name in the form
>> > > >    \EFI\BOOT\BOOT{machine type short-name}.EFI ...
>> > > >
>> > > > 2) So (1) entails the hehavior that the user's preference of boot media
>> > > >     order should be determined by Boot#### and BootOrder variables.
>> > > >
>> > > > With this patch, the semantics mentioned above is fully implemented.
>> > > > For example, if you want to boot the system from USB and SCSI in this
>> > > > order,
>> > > > * define Boot0001 which contains only a device path to the USB device
>> > > >    (without any file path/name)
>> > > > * define Boot0002 which contains only a device path to the SCSI device,
>> > > > and
>> > > > * set BootOrder to Boot0001:Boot0002
>> > > >
>> > > > To avoid build error for sandbox, default file name "BOOTSANDBOX.efi"
>> > > > is defined even if it is out of scope of UEFI specification.
>> > > >
>> > > > Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
>> > > > Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
>> > > > ---
>> > > > Changes in v5:
>> > > > - add default file name definition for SANDBOX to avoid build error
>> > > >
>> > > > Changes from original version:
>> > > > - create new include file "efi_default_filename.h" to
>> > > >    avoid conflict with config_distro_bootcmd.h
>> > > > - modify the target pointer of efi_free_pool(), expand_media_path() should
>> > > >    only free the pointer allocated by efi_dp_from_file() function.
>> > > >   include/config_distro_bootcmd.h | 14 +--------
>> > > >   include/efi_default_filename.h  | 33 ++++++++++++++++++++++
>> > > >   lib/efi_loader/efi_bootmgr.c    | 50 ++++++++++++++++++++++++++++++++-
>> > > >   3 files changed, 83 insertions(+), 14 deletions(-)
>> > > >   create mode 100644 include/efi_default_filename.h
>> > > >
>> > > > diff --git a/include/config_distro_bootcmd.h b/include/config_distro_bootcmd.h
>> > > > index c55023889c..6a3110f27b 100644
>> > > > --- a/include/config_distro_bootcmd.h
>> > > > +++ b/include/config_distro_bootcmd.h
>> > > > @@ -91,19 +91,7 @@
>> > > >   #endif
>> > > >
>> > > >   #ifdef CONFIG_EFI_LOADER
>> > > > -#if defined(CONFIG_ARM64)
>> > > > -#define BOOTEFI_NAME "bootaa64.efi"
>> > > > -#elif defined(CONFIG_ARM)
>> > > > -#define BOOTEFI_NAME "bootarm.efi"
>> > > > -#elif defined(CONFIG_X86_RUN_32BIT)
>> > > > -#define BOOTEFI_NAME "bootia32.efi"
>> > > > -#elif defined(CONFIG_X86_RUN_64BIT)
>> > > > -#define BOOTEFI_NAME "bootx64.efi"
>> > > > -#elif defined(CONFIG_ARCH_RV32I)
>> > > > -#define BOOTEFI_NAME "bootriscv32.efi"
>> > > > -#elif defined(CONFIG_ARCH_RV64I)
>> > > > -#define BOOTEFI_NAME "bootriscv64.efi"
>> > > > -#endif
>> > > > +#include <efi_default_filename.h>
>> > > >   #endif
>> > > >
>> > > >   #ifdef BOOTEFI_NAME
>> > > > diff --git a/include/efi_default_filename.h b/include/efi_default_filename.h
>> > > > new file mode 100644
>> > > > index 0000000000..cb2ef9e131
>> > > > --- /dev/null
>> > > > +++ b/include/efi_default_filename.h
>> > > > @@ -0,0 +1,33 @@
>> > > > +/* SPDX-License-Identifier: GPL-2.0+ */
>> > > > +/*
>> > > > + * Default boot file name when none is present in the FilePath.
>> > > > + * This is defined in the UEFI specification.
>> > > > + *
>> > > > + * Copyright (c) 2022, Linaro Limited
>> > > > + */
>> > > > +#ifndef _EFI_DEFAULT_FILENAME_H
>> > > > +#define _EFI_DEFAULT_FILENAME_H
>> > > > +
>> > > > +#if defined(CONFIG_ARM64)
>> > > > +#define BOOTEFI_NAME "BOOTAA64.EFI"
>> > > > +#elif defined(CONFIG_ARM)
>> > > > +#define BOOTEFI_NAME "BOOTARM.EFI"
>> > > > +#elif defined(CONFIG_X86_64)
>> > > > +#define BOOTEFI_NAME "BOOTX64.EFI"
>> > > > +#elif defined(CONFIG_X86)
>> > > > +#define BOOTEFI_NAME "BOOTIA32.EFI"
>> > > > +#elif defined(CONFIG_ARCH_RV32I)
>> > > > +#define BOOTEFI_NAME "BOOTRISCV32.EFI"
>> > > > +#elif defined(CONFIG_ARCH_RV64I)
>> > > > +#define BOOTEFI_NAME "BOOTRISCV64.EFI"
>> > > > +#elif defined(CONFIG_SANDBOX)
>> > > > +/*
>> > > > + * SANDBOX is not defined in UEFI specification, but
>> > > > + * this definition avoids build failure for SANDBOX.
>> > > > + */
>> > > > +#define BOOTEFI_NAME "BOOTSANDBOX.EFI"
>> > > 
>> > > The sandbox should boot the default binary for the host architecture:
>> > > 
>> > > #ifndef _EFI_DEFAULT_FILENAME_H
>> > > #define _EFI_DEFAULT_FILENAME_H
>> > > 
>> > > #include <host_arch.h>
>> > > 
>> > > #undef BOOTEFI_NAME
>> > > 
>> > > #if HOST_ARCH == HOST_ARCH_X86_64
>> > > #define BOOTEFI_NAME "BOOTX64.EFI"
>> > > #endif
>> > > 
>> > > #if HOST_ARCH == HOST_ARCH_X86
>> > > #define BOOTEFI_NAME "BOOTIA32.EFI"
>> > > #endif
>> > > 
>> > > #if HOST_ARCH == HOST_ARCH_AARCH64
>> > > #define BOOTEFI_NAME "BOOTAA64.EFI"
>> > > #endif
>> > > 
>> > > #if HOST_ARCH == HOST_ARCH_ARM
>> > > #define BOOTEFI_NAME "BOOTARM.EFI"
>> > > #endif
>> > > 
>> > > #if HOST_ARCH == HOST_ARCH_RISCV32
>> > > #define BOOTEFI_NAME "BOOTRISCV32.EFI"
>> > > #endif
>> > > 
>> > > #if HOST_ARCH == HOST_ARCH_RISCV64
>> > > #define BOOTEFI_NAME "BOOTRISCV64.EFI"
>> > > #endif
>> > > 
>> > > #ifndef BOOTEFI_NAME
>> > > #error Unsupported UEFI architecture
>> > > #endif
>> > > 
>> > > #endif
>> > 
>> > Maybe sanbox is special, but using the host architecture for actual
>> > boards makes no sense.  I see this has made its way into master
>> > already, but when I cross-build for apple_m1_defconfig on an amd64
>> > machine I end up with:
>> > 
>> >     $ strings ./lib/efi_loader/efi_bootmgr.o | grep BOOT
>> >     /EFI/BOOT/BOOTX64.EFI
>> > 
>> > The original diff that used CONFIG_ARM64, CONFIG_ARM, etc, did this
>> > right.
>> 
>> Hmm, forget about that.  The problem is in the sed expression in the
>> toplevel Makefile that is used to MK_ARCH that ends up setting
>> HOST_ARCH incorrectly.  Using a variable named HOST_ARCH to hold the
>> target architecture doesn't help understanding what's going wrong here
>> of course...
>
>I can nod to Mark's comment here.
>Even though HOST_ARCH might work, it's quite confusing.
>Apparently it looks wrong as Mark said.
>
>Please address this comment properly as you modified my patch.
>
>What I basically recommend is:
>* In (top-dir)/Makefile
>  - s/HOST_ARCH/MK_ARCH/g
>  - add
>      if (CONFIG_SANDBOX)
>          HOST_ARCH = $(MK_ARCH)
>  - remove "undefine MK_ARCH"
>* In efi_default_filename.h
>  - s/HOST_ARCH/MK_ARCH/g
>
>This way, HOST_ARCH is only used in sandbox-related code.

TARGET_ARCH would be a better variable name.

Best regards

Heinrich

>
>-Takahiro Akashi


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

* Re: [PATCH v5 10/17] bootmenu: add distro boot entry
  2022-05-12  8:44     ` Takahiro Akashi
@ 2022-05-12 10:39       ` Heinrich Schuchardt
  2022-05-12 11:42         ` Mark Kettenis
  0 siblings, 1 reply; 58+ messages in thread
From: Heinrich Schuchardt @ 2022-05-12 10:39 UTC (permalink / raw)
  To: Takahiro Akashi
  Cc: Masahisa Kojima, u-boot, Ilias Apalodimas, Simon Glass,
	Francois Ozog, Mark Kettenis

Am 12. Mai 2022 10:44:28 MESZ schrieb Takahiro Akashi <takahiro.akashi@linaro.org>:
>On Sun, May 01, 2022 at 11:48:40PM +0200, Heinrich Schuchardt wrote:
>> On 4/28/22 10:09, Masahisa Kojima wrote:
>> > This commit adds the distro_boot entries into the bootmenu.
>> > The bootmenu read the "boot_targets" U-Boot environment variable
>> > and enumerate it.
>> > User can select the distro boot entry, then bootmenu executes
>> > "run bootcmd_xxx" command.
>> > 
>> > The bootmenu also checks the existing block devices and network
>> > option("dhcp" and "pxe") availability, then filter out
>> > the "boot_targets" appeared in bootmenu.
>> > 
>> > The bootmenu example is as follows, distro boot entry has the
>> > "distro_boot" prefix.
>> > 
>> >    *** U-Boot Boot Menu ***
>> > 
>> >       distro_boot   : usb0
>> >       distro_boot   : scsi0
>> >       distro_boot   : virtio0
>> >       distro_boot   : dhcp
>> 
>> You will be creating UEFI boot options for all block devices anyway.
>> We should avoid duplicate entries. Please, drop this patch.
>
>Nak
>A distro entry should work, searching not only for a default UEFI file
>but also sysboot (extlinux.conf) and boot scripts.

Nobody expects this in an UEFI boot process.

Simon's work on bootflows tries to separate different bootflows.

So adding entries should depend on the configured bootflow.

Best regards

Heinrich


>
>> Best regards
>> 
>> heinrich
>> 
>> > 
>> > Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
>> > ---
>> > Changes in v5:
>> > - split into the separate patch
>> > - add function description comment
>> > - handle the case boot_targets variable is empty
>> > - filter out the non-exist device entry
>> > 
>> >   cmd/bootmenu.c | 177 +++++++++++++++++++++++++++++++++++++++++++++++++
>> >   1 file changed, 177 insertions(+)
>> > 
>> > diff --git a/cmd/bootmenu.c b/cmd/bootmenu.c
>> > index da688e6213..afe42b8041 100644
>> > --- a/cmd/bootmenu.c
>> > +++ b/cmd/bootmenu.c
>> > @@ -7,6 +7,7 @@
>> >   #include <common.h>
>> >   #include <command.h>
>> >   #include <ansi.h>
>> > +#include <dm.h>
>> >   #include <efi_loader.h>
>> >   #include <efi_variable.h>
>> >   #include <env.h>
>> > @@ -14,6 +15,7 @@
>> >   #include <menu.h>
>> >   #include <watchdog.h>
>> >   #include <malloc.h>
>> > +#include <linux/ctype.h>
>> >   #include <linux/delay.h>
>> >   #include <linux/string.h>
>> > 
>> > @@ -31,6 +33,7 @@ enum boot_type {
>> >   	BOOTMENU_TYPE_NONE = 0,
>> >   	BOOTMENU_TYPE_BOOTMENU,
>> >   	BOOTMENU_TYPE_UEFI_BOOT_OPTION,
>> > +	BOOTMENU_TYPE_DISTRO_BOOT,
>> >   };
>> > 
>> >   struct bootmenu_entry {
>> > @@ -90,6 +93,8 @@ static void bootmenu_print_entry(void *data)
>> >   		printf("bootmenu_%02d   : %ls", entry->bootorder, entry->title);
>> >   	else if (entry->type == BOOTMENU_TYPE_UEFI_BOOT_OPTION)
>> >   		printf("UEFI BOOT%04X : %ls", entry->bootorder, entry->title);
>> > +	else if (entry->type == BOOTMENU_TYPE_DISTRO_BOOT)
>> > +		printf("distro_boot   : %ls", entry->title);
>> >   	else
>> >   		printf("%ls", entry->title);
>> > 
>> > @@ -465,6 +470,172 @@ static int prepare_uefi_bootorder_entry(struct bootmenu_data *menu,
>> >   	return 1;
>> >   }
>> > 
>> > +static int is_blk_device_available(char *token)
>> > +{
>> > +	struct driver *d = ll_entry_start(struct driver, driver);
>> > +	const int n_ents = ll_entry_count(struct driver, driver);
>> > +	struct driver *entry;
>> > +	struct udevice *udev;
>> > +	struct uclass *uc;
>> > +	struct blk_desc *desc;
>> > +	int ret, i;
>> > +	const char *if_type_name;
>> > +
>> > +	ret = uclass_get(UCLASS_BLK, &uc);
>> > +	if (ret)
>> > +		return -ENODEV;
>> > +
>> > +	for (entry = d; entry < d + n_ents; entry++) {
>> > +		if (entry->id != UCLASS_BLK)
>> > +			continue;
>> > +		i = 0;
>> > +		uclass_foreach_dev(udev, uc) {
>> > +			if (udev->driver != entry)
>> > +				continue;
>> > +			desc = dev_get_uclass_plat(udev);
>> > +			if_type_name = blk_get_if_type_name(desc->if_type);
>> > +			if (strncmp(token, if_type_name, strlen(if_type_name)) == 0) {
>> > +				char *p;
>> > +				int j, len;
>> > +				int devnum = 0;
>> > +
>> > +				p = token + strlen(if_type_name);
>> > +				len = strlen(p);
>> > +				if (!len)
>> > +					continue; /* no device number */
>> > +
>> > +				for (j = 0; j < len; j++) {
>> > +					if (!isdigit(*p)) {
>> > +						/* invalid device number */
>> > +						devnum = INT_MAX;
>> > +						break;
>> > +					}
>> > +					devnum = (devnum * 10) + (*p++ - '0');
>> > +				}
>> > +
>> > +				if (devnum == INT_MAX)
>> > +					continue;
>> > +
>> > +				if (devnum == desc->devnum)
>> > +					return 1;
>> > +			}
>> > +		}
>> > +	}
>> > +
>> > +	if (strncmp(token, "dhcp", strlen("dhcp")) == 0) {
>> > +		if (IS_ENABLED(CONFIG_CMD_DHCP))
>> > +			return 1;
>> > +	}
>> > +
>> > +	if (strncmp(token, "pxe", strlen("pxe")) == 0) {
>> > +		if (IS_ENABLED(CONFIG_CMD_PXE))
>> > +			return 1;
>> > +	}
>> > +
>> > +	return -ENODEV;
>> > +}
>> > +
>> > +/**
>> > + * prepare_distro_boot_entry() - generate the distro boot entries
>> > + *
>> > + * This function read the "boot_targets" U-Boot environment variable
>> > + * and generate the bootmenu entries.
>> > + *
>> > + * @menu:	pointer to the bootmenu structure
>> > + * @current:	pointer to the last bootmenu entry list
>> > + * @index:	pointer to the index of the last bootmenu entry,
>> > + *		the number of uefi entry is added by this function
>> > + * Return:	1 on success, negative value on error
>> > + */
>> > +static int prepare_distro_boot_entry(struct bootmenu_data *menu,
>> > +				     struct bootmenu_entry **current,
>> > +				     unsigned short int *index)
>> > +{
>> > +	char *p;
>> > +	int len;
>> > +	char *token;
>> > +	char *boot_targets;
>> > +	unsigned short int i = *index;
>> > +	struct bootmenu_entry *entry = NULL;
>> > +	struct bootmenu_entry *iter = *current;
>> > +
>> > +	/* list the distro boot "boot_targets" */
>> > +	boot_targets = env_get("boot_targets");
>> > +	if (!boot_targets)
>> > +		return -ENOENT;
>> > +
>> > +	len = strlen(boot_targets);
>> > +	if (!len)
>> > +		return -ENOENT;
>> > +
>> > +	p = calloc(1, len + 1);
>> > +	strlcpy(p, boot_targets, len);
>> > +
>> > +	token = strtok(p, " ");
>> > +
>> > +	do {
>> > +		u16 *buf;
>> > +		char *command;
>> > +		int command_size;
>> > +
>> > +		if (is_blk_device_available(token) != 1) {
>> > +			token = strtok(NULL, " ");
>> > +			continue;
>> > +		}
>> > +
>> > +		entry = malloc(sizeof(struct bootmenu_entry));
>> > +		if (!entry) {
>> > +			free(p);
>> > +			return -ENOMEM;
>> > +		}
>> > +
>> > +		len = strlen(token);
>> > +		buf = calloc(1, (len + 1) * sizeof(u16));
>> > +		entry->title = buf;
>> > +		if (!entry->title) {
>> > +			free(entry);
>> > +			free(p);
>> > +			return -ENOMEM;
>> > +		}
>> > +		utf8_utf16_strncpy(&buf, token, len);
>> > +		sprintf(entry->key, "%d", i);
>> > +		entry->num = i;
>> > +		entry->menu = menu;
>> > +
>> > +		command_size = sizeof("run bootcmd_") + len;
>> > +		command = calloc(1, command_size);
>> > +		if (!command) {
>> > +			free(entry->title);
>> > +			free(entry);
>> > +			free(p);
>> > +			return -ENOMEM;
>> > +		}
>> > +		snprintf(command, command_size, "run bootcmd_%s", token);
>> > +		entry->command = command;
>> > +		entry->type = BOOTMENU_TYPE_DISTRO_BOOT;
>> > +		entry->next = NULL;
>> > +
>> > +		if (!iter)
>> > +			menu->first = entry;
>> > +		else
>> > +			iter->next = entry;
>> > +
>> > +		iter = entry;
>> > +		i++;
>> > +
>> > +		if (i == MAX_COUNT - 1)
>> > +			break;
>> > +
>> > +		token = strtok(NULL, " ");
>> > +	} while (token);
>> > +
>> > +	free(p);
>> > +	*index = i;
>> > +	*current = iter;
>> > +
>> > +	return 1;
>> > +}
>> > +
>> >   static struct bootmenu_data *bootmenu_create(int delay)
>> >   {
>> >   	int ret;
>> > @@ -498,6 +669,12 @@ static struct bootmenu_data *bootmenu_create(int delay)
>> >   		}
>> >   	}
>> > 
>> > +	if (i < MAX_COUNT - 1) {
>> > +		ret = prepare_distro_boot_entry(menu, &iter, &i);
>> > +		if (ret < 0 && ret != -ENOENT)
>> > +			goto cleanup;
>> > +	}
>> > +
>> >   	/* Add U-Boot console entry at the end */
>> >   	if (i <= MAX_COUNT - 1) {
>> >   		entry = malloc(sizeof(struct bootmenu_entry));
>> 


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

* Re: [PATCH v5 10/17] bootmenu: add distro boot entry
  2022-05-12 10:39       ` Heinrich Schuchardt
@ 2022-05-12 11:42         ` Mark Kettenis
  0 siblings, 0 replies; 58+ messages in thread
From: Mark Kettenis @ 2022-05-12 11:42 UTC (permalink / raw)
  To: Heinrich Schuchardt
  Cc: takahiro.akashi, masahisa.kojima, u-boot, ilias.apalodimas, sjg,
	francois.ozog

> Date: Thu, 12 May 2022 12:39:39 +0200
> From: Heinrich Schuchardt <xypron.glpk@gmx.de>
> 
> Am 12. Mai 2022 10:44:28 MESZ schrieb Takahiro Akashi <takahiro.akashi@linaro.org>:
> >On Sun, May 01, 2022 at 11:48:40PM +0200, Heinrich Schuchardt wrote:
> >> On 4/28/22 10:09, Masahisa Kojima wrote:
> >> > This commit adds the distro_boot entries into the bootmenu.
> >> > The bootmenu read the "boot_targets" U-Boot environment variable
> >> > and enumerate it.
> >> > User can select the distro boot entry, then bootmenu executes
> >> > "run bootcmd_xxx" command.
> >> > 
> >> > The bootmenu also checks the existing block devices and network
> >> > option("dhcp" and "pxe") availability, then filter out
> >> > the "boot_targets" appeared in bootmenu.
> >> > 
> >> > The bootmenu example is as follows, distro boot entry has the
> >> > "distro_boot" prefix.
> >> > 
> >> >    *** U-Boot Boot Menu ***
> >> > 
> >> >       distro_boot   : usb0
> >> >       distro_boot   : scsi0
> >> >       distro_boot   : virtio0
> >> >       distro_boot   : dhcp
> >> 
> >> You will be creating UEFI boot options for all block devices anyway.
> >> We should avoid duplicate entries. Please, drop this patch.
> >
> >Nak
> >A distro entry should work, searching not only for a default UEFI file
> >but also sysboot (extlinux.conf) and boot scripts.
> 
> Nobody expects this in an UEFI boot process.

Not sure about that.  Some x86 firmware implementations present a boot
menu that displays both UEFI and "legacy" boot entries.

> Simon's work on bootflows tries to separate different bootflows.

And I'm still not convinced it does so in a sensible way.  I can't
really test the bootflows since the implementation is incomplete.

The bootmenu I saw when I tested things a few weeks ago made sense to
me.  It presented all the relevant options (UEFI and distroboot for
the relevant boot devices) on a system without any of the UEFI
BootXXXX options set.  Some Linux distros still don't support the UEFI
bootflow (or at least don't support it very well), so distro_boot is
still important.

> So adding entries should depend on the configured bootflow.

But in my opinion, U-Boot should not depend on explicitly configured
bootflows.

Cheers,

Mark

> >> > 
> >> > Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
> >> > ---
> >> > Changes in v5:
> >> > - split into the separate patch
> >> > - add function description comment
> >> > - handle the case boot_targets variable is empty
> >> > - filter out the non-exist device entry
> >> > 
> >> >   cmd/bootmenu.c | 177 +++++++++++++++++++++++++++++++++++++++++++++++++
> >> >   1 file changed, 177 insertions(+)
> >> > 
> >> > diff --git a/cmd/bootmenu.c b/cmd/bootmenu.c
> >> > index da688e6213..afe42b8041 100644
> >> > --- a/cmd/bootmenu.c
> >> > +++ b/cmd/bootmenu.c
> >> > @@ -7,6 +7,7 @@
> >> >   #include <common.h>
> >> >   #include <command.h>
> >> >   #include <ansi.h>
> >> > +#include <dm.h>
> >> >   #include <efi_loader.h>
> >> >   #include <efi_variable.h>
> >> >   #include <env.h>
> >> > @@ -14,6 +15,7 @@
> >> >   #include <menu.h>
> >> >   #include <watchdog.h>
> >> >   #include <malloc.h>
> >> > +#include <linux/ctype.h>
> >> >   #include <linux/delay.h>
> >> >   #include <linux/string.h>
> >> > 
> >> > @@ -31,6 +33,7 @@ enum boot_type {
> >> >   	BOOTMENU_TYPE_NONE = 0,
> >> >   	BOOTMENU_TYPE_BOOTMENU,
> >> >   	BOOTMENU_TYPE_UEFI_BOOT_OPTION,
> >> > +	BOOTMENU_TYPE_DISTRO_BOOT,
> >> >   };
> >> > 
> >> >   struct bootmenu_entry {
> >> > @@ -90,6 +93,8 @@ static void bootmenu_print_entry(void *data)
> >> >   		printf("bootmenu_%02d   : %ls", entry->bootorder, entry->title);
> >> >   	else if (entry->type == BOOTMENU_TYPE_UEFI_BOOT_OPTION)
> >> >   		printf("UEFI BOOT%04X : %ls", entry->bootorder, entry->title);
> >> > +	else if (entry->type == BOOTMENU_TYPE_DISTRO_BOOT)
> >> > +		printf("distro_boot   : %ls", entry->title);
> >> >   	else
> >> >   		printf("%ls", entry->title);
> >> > 
> >> > @@ -465,6 +470,172 @@ static int prepare_uefi_bootorder_entry(struct bootmenu_data *menu,
> >> >   	return 1;
> >> >   }
> >> > 
> >> > +static int is_blk_device_available(char *token)
> >> > +{
> >> > +	struct driver *d = ll_entry_start(struct driver, driver);
> >> > +	const int n_ents = ll_entry_count(struct driver, driver);
> >> > +	struct driver *entry;
> >> > +	struct udevice *udev;
> >> > +	struct uclass *uc;
> >> > +	struct blk_desc *desc;
> >> > +	int ret, i;
> >> > +	const char *if_type_name;
> >> > +
> >> > +	ret = uclass_get(UCLASS_BLK, &uc);
> >> > +	if (ret)
> >> > +		return -ENODEV;
> >> > +
> >> > +	for (entry = d; entry < d + n_ents; entry++) {
> >> > +		if (entry->id != UCLASS_BLK)
> >> > +			continue;
> >> > +		i = 0;
> >> > +		uclass_foreach_dev(udev, uc) {
> >> > +			if (udev->driver != entry)
> >> > +				continue;
> >> > +			desc = dev_get_uclass_plat(udev);
> >> > +			if_type_name = blk_get_if_type_name(desc->if_type);
> >> > +			if (strncmp(token, if_type_name, strlen(if_type_name)) == 0) {
> >> > +				char *p;
> >> > +				int j, len;
> >> > +				int devnum = 0;
> >> > +
> >> > +				p = token + strlen(if_type_name);
> >> > +				len = strlen(p);
> >> > +				if (!len)
> >> > +					continue; /* no device number */
> >> > +
> >> > +				for (j = 0; j < len; j++) {
> >> > +					if (!isdigit(*p)) {
> >> > +						/* invalid device number */
> >> > +						devnum = INT_MAX;
> >> > +						break;
> >> > +					}
> >> > +					devnum = (devnum * 10) + (*p++ - '0');
> >> > +				}
> >> > +
> >> > +				if (devnum == INT_MAX)
> >> > +					continue;
> >> > +
> >> > +				if (devnum == desc->devnum)
> >> > +					return 1;
> >> > +			}
> >> > +		}
> >> > +	}
> >> > +
> >> > +	if (strncmp(token, "dhcp", strlen("dhcp")) == 0) {
> >> > +		if (IS_ENABLED(CONFIG_CMD_DHCP))
> >> > +			return 1;
> >> > +	}
> >> > +
> >> > +	if (strncmp(token, "pxe", strlen("pxe")) == 0) {
> >> > +		if (IS_ENABLED(CONFIG_CMD_PXE))
> >> > +			return 1;
> >> > +	}
> >> > +
> >> > +	return -ENODEV;
> >> > +}
> >> > +
> >> > +/**
> >> > + * prepare_distro_boot_entry() - generate the distro boot entries
> >> > + *
> >> > + * This function read the "boot_targets" U-Boot environment variable
> >> > + * and generate the bootmenu entries.
> >> > + *
> >> > + * @menu:	pointer to the bootmenu structure
> >> > + * @current:	pointer to the last bootmenu entry list
> >> > + * @index:	pointer to the index of the last bootmenu entry,
> >> > + *		the number of uefi entry is added by this function
> >> > + * Return:	1 on success, negative value on error
> >> > + */
> >> > +static int prepare_distro_boot_entry(struct bootmenu_data *menu,
> >> > +				     struct bootmenu_entry **current,
> >> > +				     unsigned short int *index)
> >> > +{
> >> > +	char *p;
> >> > +	int len;
> >> > +	char *token;
> >> > +	char *boot_targets;
> >> > +	unsigned short int i = *index;
> >> > +	struct bootmenu_entry *entry = NULL;
> >> > +	struct bootmenu_entry *iter = *current;
> >> > +
> >> > +	/* list the distro boot "boot_targets" */
> >> > +	boot_targets = env_get("boot_targets");
> >> > +	if (!boot_targets)
> >> > +		return -ENOENT;
> >> > +
> >> > +	len = strlen(boot_targets);
> >> > +	if (!len)
> >> > +		return -ENOENT;
> >> > +
> >> > +	p = calloc(1, len + 1);
> >> > +	strlcpy(p, boot_targets, len);
> >> > +
> >> > +	token = strtok(p, " ");
> >> > +
> >> > +	do {
> >> > +		u16 *buf;
> >> > +		char *command;
> >> > +		int command_size;
> >> > +
> >> > +		if (is_blk_device_available(token) != 1) {
> >> > +			token = strtok(NULL, " ");
> >> > +			continue;
> >> > +		}
> >> > +
> >> > +		entry = malloc(sizeof(struct bootmenu_entry));
> >> > +		if (!entry) {
> >> > +			free(p);
> >> > +			return -ENOMEM;
> >> > +		}
> >> > +
> >> > +		len = strlen(token);
> >> > +		buf = calloc(1, (len + 1) * sizeof(u16));
> >> > +		entry->title = buf;
> >> > +		if (!entry->title) {
> >> > +			free(entry);
> >> > +			free(p);
> >> > +			return -ENOMEM;
> >> > +		}
> >> > +		utf8_utf16_strncpy(&buf, token, len);
> >> > +		sprintf(entry->key, "%d", i);
> >> > +		entry->num = i;
> >> > +		entry->menu = menu;
> >> > +
> >> > +		command_size = sizeof("run bootcmd_") + len;
> >> > +		command = calloc(1, command_size);
> >> > +		if (!command) {
> >> > +			free(entry->title);
> >> > +			free(entry);
> >> > +			free(p);
> >> > +			return -ENOMEM;
> >> > +		}
> >> > +		snprintf(command, command_size, "run bootcmd_%s", token);
> >> > +		entry->command = command;
> >> > +		entry->type = BOOTMENU_TYPE_DISTRO_BOOT;
> >> > +		entry->next = NULL;
> >> > +
> >> > +		if (!iter)
> >> > +			menu->first = entry;
> >> > +		else
> >> > +			iter->next = entry;
> >> > +
> >> > +		iter = entry;
> >> > +		i++;
> >> > +
> >> > +		if (i == MAX_COUNT - 1)
> >> > +			break;
> >> > +
> >> > +		token = strtok(NULL, " ");
> >> > +	} while (token);
> >> > +
> >> > +	free(p);
> >> > +	*index = i;
> >> > +	*current = iter;
> >> > +
> >> > +	return 1;
> >> > +}
> >> > +
> >> >   static struct bootmenu_data *bootmenu_create(int delay)
> >> >   {
> >> >   	int ret;
> >> > @@ -498,6 +669,12 @@ static struct bootmenu_data *bootmenu_create(int delay)
> >> >   		}
> >> >   	}
> >> > 
> >> > +	if (i < MAX_COUNT - 1) {
> >> > +		ret = prepare_distro_boot_entry(menu, &iter, &i);
> >> > +		if (ret < 0 && ret != -ENOENT)
> >> > +			goto cleanup;
> >> > +	}
> >> > +
> >> >   	/* Add U-Boot console entry at the end */
> >> >   	if (i <= MAX_COUNT - 1) {
> >> >   		entry = malloc(sizeof(struct bootmenu_entry));
> >> 
> 
> 

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

* Re: [PATCH v5 16/17] bootmenu: add removable media entries
  2022-05-09 13:01       ` Heinrich Schuchardt
@ 2022-05-16  9:20         ` Masahisa Kojima
  0 siblings, 0 replies; 58+ messages in thread
From: Masahisa Kojima @ 2022-05-16  9:20 UTC (permalink / raw)
  To: Heinrich Schuchardt
  Cc: Ilias Apalodimas, Simon Glass, Takahiro Akashi, Francois Ozog,
	Mark Kettenis, u-boot

Hi Heinrich,

On Mon, 9 May 2022 at 22:01, Heinrich Schuchardt <xypron.glpk@gmx.de> wrote:
>
> On 5/9/22 10:23, Masahisa Kojima wrote:
> > On Fri, 29 Apr 2022 at 01:53, Heinrich Schuchardt <xypron.glpk@gmx.de> wrote:
> >>
> >> On 4/28/22 10:09, Masahisa Kojima wrote:
> >>> UEFI specification requires booting from removal media using
> >>> a architecture-specific default image name such as BOOTAA64.EFI.
> >>> This commit adds the removable media entries into bootmenu,
> >>> so that user can select the removable media and boot with
> >>> default image.
> >>>
> >>> The bootmenu automatically enumerates the possible bootable
> >>> media devices supporting EFI_SIMPLE_FILE_SYSTEM_PROTOCOL,
> >>> add it as new UEFI boot option(BOOT####) and update BootOrder
> >>> variable. This automatically generated UEFI boot option
> >>> has the dedicated guid in the optional_data to distinguish it from
> >>> the UEFI boot option user adds manually.
> >>>
> >>> This commit also provides the BOOT#### variable maintenance feature.
> >>> Depending on the system hardware setup, some devices
> >>> may not exist at a later system boot, so bootmenu checks the
> >>> available device in each bootmenu invocation and automatically
> >>> removes the BOOT#### variable corrensponding to the non-existent
> >>> media device.
> >>>
> >>> Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
> >>> ---
> >>> Changes in v5:
> >>> - Return EFI_SUCCESS if there is no BootOrder defined
> >>> - correctly handle the case if no removable device found
> >>> - use guid to identify the automatically generated entry by bootmenu
> >>>
> >>> Newly created in v4
> >>>
> >>>    cmd/bootmenu.c                            |  94 +++++++++++++++
> >>>    include/efi_loader.h                      |  20 ++++
> >>>    lib/efi_loader/efi_bootmenu_maintenance.c | 139 ++++++++++++++++++++++
> >>>    3 files changed, 253 insertions(+)
> >>>
> >>> diff --git a/cmd/bootmenu.c b/cmd/bootmenu.c
> >>> index 860cb83182..970db3ee01 100644
> >>> --- a/cmd/bootmenu.c
> >>> +++ b/cmd/bootmenu.c
> >>> @@ -396,6 +396,89 @@ static int is_blk_device_available(char *token)
> >>>        return -ENODEV;
> >>>    }
> >>>
> >>> +/**
> >>> + * prepare_media_device_entry() - generate the media device entries
> >>> + *
> >>> + * This function enumerates all devices supporting EFI_SIMPLE_FILE_SYSTEM_PROTOCOL
> >>> + * and generate the bootmenu entries.
> >>> + * This function also provide the BOOT#### variable maintenance for
> >>> + * the media device entries.
> >>> + *   - Automatically create the BOOT#### variable for the newly detected device,
> >>> + *     this BOOT#### variable is distinguished by the special GUID
> >>> + *     stored in the EFI_LOAD_OPTION.optional_data
> >>> + *   - If the device is not attached to the system, the associated BOOT#### variable
> >>> + *     is automatically deleted.
> >>> + *
> >>> + * Return:   status code
> >>> + */
> >>> +static efi_status_t prepare_media_device_entry(void)
> >>> +{
> >>> +     u32 i;
> >>> +     efi_status_t ret;
> >>> +     efi_uintn_t count;
> >>> +     efi_handle_t *volume_handles = NULL;
> >>> +     struct efi_bootmenu_media_boot_option *opt = NULL;
> >>> +
> >>> +     ret = efi_locate_handle_buffer_int(BY_PROTOCOL, &efi_simple_file_system_protocol_guid,
> >>> +                                        NULL, &count, (efi_handle_t **)&volume_handles);
> >>> +     if (ret != EFI_SUCCESS)
> >>> +             return ret;
> >>> +
> >>> +     opt = calloc(count, sizeof(struct efi_bootmenu_media_boot_option));
> >>> +     if (!opt)
> >>> +             goto out;
> >>> +
> >>> +     /* enumerate all devices supporting EFI_SIMPLE_FILE_SYSTEM_PROTOCOL */
> >>> +     ret = efi_bootmenu_enumerate_boot_option(opt, volume_handles, count);
> >>> +     if (ret != EFI_SUCCESS)
> >>> +             goto out;
> >>> +
> >>> +     /*
> >>> +      * System hardware configuration may vary depending on the user setup.
> >>> +      * The boot option is automatically added by the bootmenu.
> >>> +      * If the device is not attached to the system, the boot option needs
> >>> +      * to be deleted.
> >>> +      */
> >>> +     ret = efi_bootmenu_delete_invalid_boot_option(opt, count);
> >>> +     if (ret != EFI_SUCCESS)
> >>> +             goto out;
> >>> +
> >>> +     /* add non-existent boot option */
> >>> +     for (i = 0; i < count; i++) {
> >>> +             u32 boot_index;
> >>> +             u16 var_name[9];
> >>> +
> >>> +             if (!opt[i].exist) {
> >>> +                     ret = efi_bootmenu_get_unused_bootoption(var_name, sizeof(var_name),
> >>> +                                                              &boot_index);
> >>> +                     if (ret != EFI_SUCCESS)
> >>> +                             goto out;
> >>> +
> >>> +                     ret = efi_set_variable_int(var_name, &efi_global_variable_guid,
> >>> +                                                EFI_VARIABLE_NON_VOLATILE |
> >>> +                                                EFI_VARIABLE_BOOTSERVICE_ACCESS |
> >>> +                                                EFI_VARIABLE_RUNTIME_ACCESS,
> >>> +                                                opt[i].size, opt[i].lo, false);
> >>> +                     if (ret != EFI_SUCCESS)
> >>> +                             goto out;
> >>> +
> >>> +                     ret = efi_bootmenu_append_bootorder(boot_index);
> >>> +                     if (ret != EFI_SUCCESS)
> >>> +                             goto out;
> >>> +             }
> >>> +     }
> >>> +
> >>> +out:
> >>> +     if (opt) {
> >>> +             for (i = 0; i < count; i++)
> >>> +                     free(opt[i].lo);
> >>> +     }
> >>> +     free(opt);
> >>> +     efi_free_pool(volume_handles);
> >>> +
> >>> +     return ret;
> >>> +}
> >>> +
> >>>    /**
> >>>     * prepare_distro_boot_entry() - generate the distro boot entries
> >>>     *
> >>> @@ -500,6 +583,7 @@ static int prepare_distro_boot_entry(struct bootmenu_data *menu,
> >>>    static struct bootmenu_data *bootmenu_create(int delay)
> >>>    {
> >>>        int ret;
> >>> +     efi_status_t efi_ret;
> >>>        unsigned short int i = 0;
> >>>        struct bootmenu_data *menu;
> >>>        struct bootmenu_entry *iter = NULL;
> >>> @@ -523,6 +607,16 @@ static struct bootmenu_data *bootmenu_create(int delay)
> >>>                goto cleanup;
> >>>
> >>>        if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR)) {
> >>> +             if (i < MAX_DYNAMIC_ENTRY) {
> >>> +                     /*
> >>> +                      * UEFI specification requires booting from removal media using
> >>> +                      * a architecture-specific default image name such as BOOTAA64.EFI.
> >>> +                      */
> >>> +                     efi_ret = prepare_media_device_entry();
> >>> +                     if (efi_ret != EFI_SUCCESS && efi_ret != EFI_NOT_FOUND)
> >>> +                             goto cleanup;
> >>> +             }
> >>> +
> >>>                if (i < MAX_DYNAMIC_ENTRY) {
> >>>                        ret = prepare_uefi_bootorder_entry(menu, &iter, &i);
> >>>                        if (ret < 0 && ret != -ENOENT)
> >>> diff --git a/include/efi_loader.h b/include/efi_loader.h
> >>> index 533618341b..bef492ccb9 100644
> >>> --- a/include/efi_loader.h
> >>> +++ b/include/efi_loader.h
> >>> @@ -928,6 +928,22 @@ struct efi_signature_store {
> >>>    struct x509_certificate;
> >>>    struct pkcs7_message;
> >>>
> >>> +/**
> >>> + * struct efi_bootmenu_media_boot_option - boot option for (removable) media device
> >>> + *
> >>> + * This structure is used to enumerate possible boot option
> >>> + *
> >>> + * @lo:              Serialized load option data
> >>> + * @size:    Size of serialized load option data
> >>> + * @exist:   Flag to indicate the load option already exists
> >>> + *           in Non-volatile load option
> >>> + */
> >>> +struct efi_bootmenu_media_boot_option {
> >>> +     void *lo;
> >>> +     efi_uintn_t size;
> >>> +     bool exist;
> >>> +};
> >>> +
> >>>    bool efi_signature_lookup_digest(struct efi_image_regions *regs,
> >>>                                 struct efi_signature_store *db,
> >>>                                 bool dbx);
> >>> @@ -1076,6 +1092,10 @@ efi_status_t efi_console_get_u16_string
> >>>    efi_status_t efi_bootmenu_get_unused_bootoption(u16 *buf,
> >>>                                                efi_uintn_t buf_size, u32 *index);
> >>>    efi_status_t efi_bootmenu_append_bootorder(u16 index);
> >>> +efi_status_t efi_bootmenu_enumerate_boot_option(struct efi_bootmenu_media_boot_option *opt,
> >>> +                                             efi_handle_t *volume_handles, efi_status_t count);
> >>> +efi_status_t efi_bootmenu_delete_invalid_boot_option(struct efi_bootmenu_media_boot_option *opt,
> >>> +                                                  efi_status_t count);
> >>>
> >>>    efi_status_t efi_disk_get_device_name(struct efi_block_io *this, char *buf, int size);
> >>>
> >>> diff --git a/lib/efi_loader/efi_bootmenu_maintenance.c b/lib/efi_loader/efi_bootmenu_maintenance.c
> >>> index 8c3f94c695..33b37fd11a 100644
> >>> --- a/lib/efi_loader/efi_bootmenu_maintenance.c
> >>> +++ b/lib/efi_loader/efi_bootmenu_maintenance.c
> >>> @@ -26,6 +26,13 @@ static struct efi_simple_text_output_protocol *cout;
> >>>    #define EFI_BOOTMENU_BOOT_NAME_MAX 32
> >>>    #define EFI_BOOT_ORDER_MAX_SIZE_IN_DECIMAL 6
> >>>
> >>> +#define EFI_BOOTMENU_AUTO_GENERATED_ENTRY_GUID \
> >>> +     EFI_GUID(0x38c1acc1, 0x9fc0, 0x41f0, \
> >>> +              0xb9, 0x01, 0xfa, 0x74, 0xd6, 0xd6, 0xe4, 0xde)
> >>> +
> >>> +static const efi_guid_t efi_guid_bootmenu_auto_generated =
> >>> +             EFI_BOOTMENU_AUTO_GENERATED_ENTRY_GUID;
> >>> +
> >>>    typedef efi_status_t (*efi_bootmenu_entry_func)(void *data, bool *exit);
> >>>
> >>>    /**
> >>> @@ -1104,3 +1111,135 @@ efi_status_t efi_bootmenu_show_maintenance_menu(void)
> >>>                                          ARRAY_SIZE(maintenance_menu_items),
> >>>                                          -1);
> >>>    }
> >>> +
> >>> +efi_status_t efi_bootmenu_enumerate_boot_option(struct efi_bootmenu_media_boot_option *opt,
> >>> +                                             efi_handle_t *volume_handles, efi_status_t count)
> >>> +{
> >>> +     u32 i;
> >>> +     struct efi_handler *handler;
> >>> +     efi_status_t ret = EFI_SUCCESS;
> >>> +
> >>> +     for (i = 0; i < count; i++) {
> >>> +             char *optional_data;
> >>> +             u16 *dev_name, *p;
> >>> +             struct efi_load_option lo;
> >>> +             struct efi_block_io *block_io;
> >>> +             char buf[BOOTMENU_DEVICE_NAME_MAX];
> >>> +             struct efi_device_path *device_path;
> >>> +
> >>> +             ret = efi_search_protocol(volume_handles[i], &efi_guid_device_path, &handler);
> >>> +             if (ret != EFI_SUCCESS)
> >>> +                     continue;
> >>> +             ret = efi_protocol_open(handler, (void **)&device_path,
> >>> +                                     efi_root, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
> >>> +             if (ret != EFI_SUCCESS)
> >>> +                     continue;
> >>> +
> >>> +             ret = efi_search_protocol(volume_handles[i], &efi_block_io_guid, &handler);
> >>> +             if (ret != EFI_SUCCESS)
> >>> +                     continue;
> >>> +             ret = efi_protocol_open(handler, (void **)&block_io,
> >>> +                                     efi_root, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
> >>> +             if (ret != EFI_SUCCESS)
> >>> +                     continue;
> >>> +
> >>> +             efi_disk_get_device_name(block_io, buf, BOOTMENU_DEVICE_NAME_MAX);
> >>> +             dev_name = calloc(1, (strlen(buf) + 1) * sizeof(u16));
> >>> +             if (!dev_name) {
> >>> +                     ret = EFI_OUT_OF_RESOURCES;
> >>> +                     goto out;
> >>> +             }
> >>> +             p = dev_name;
> >>> +             utf8_utf16_strncpy(&p, buf, strlen(buf));
> >>> +
> >>> +             lo.label = dev_name;
> >>> +             lo.attributes = LOAD_OPTION_ACTIVE;
> >>> +             lo.file_path = device_path;
> >>> +             lo.file_path_length = efi_dp_size(device_path) + sizeof(END);
> >>> +             /*
> >>> +              * Set the dedicated guid to optional_data, it is used to identify
> >>> +              * the boot option that automatically generated by the bootmenu.
> >>> +              * efi_serialize_load_option() expects optional_data is null-terminated
> >>> +              * utf8 string, so set the "dummystr" string to allocate enough space
> >>> +              * to store guid, instead of realloc the load_option.
> >>> +              *
> >>> +              * This will allocate 16 bytes for guid plus trailing 0x0000.
> >>> +              * The guid does not require trailing 0x0000, but it is for safety
> >>> +              * in case some program handle the optional_data as u16 string.
> >>> +              */
> >>> +             lo.optional_data = "dummystr";
> >>
> >> Why do you want to reserve 18 bytes when 16 are enough for the GUID?
> >
> > OK, I will only allocate 16 bytes for GUID.
> >
> >>
> >>> +             opt[i].size = efi_serialize_load_option(&lo, (u8 **)&opt[i].lo);
> >>> +             if (!opt[i].size) {
> >>> +                     ret = EFI_OUT_OF_RESOURCES;
> >>> +                     free(dev_name);
> >>> +                     goto out;
> >>> +             }
> >>> +             /* set the guid */
> >>> +             optional_data = (char *)opt[i].lo + (opt[i].size - u16_strsize(u"dummystr"));
> >>> +             memcpy(optional_data, &efi_guid_bootmenu_auto_generated, sizeof(efi_guid_t));
> >>> +             free(dev_name);
> >>> +     }
> >>> +
> >>> +out:
> >>> +     return ret;
> >>> +}
> >>> +
> >>> +efi_status_t efi_bootmenu_delete_invalid_boot_option(struct efi_bootmenu_media_boot_option *opt,
> >>> +                                                  efi_status_t count)
> >>> +{
> >>> +     u16 *bootorder;
> >>> +     u32 i, j;
> >>> +     efi_status_t ret;
> >>> +     efi_uintn_t num, size, bootorder_size;
> >>> +     void *load_option;
> >>> +     struct efi_load_option lo;
> >>> +     u16 varname[] = u"Boot####";
> >>> +
> >>> +     bootorder = efi_get_var(u"BootOrder", &efi_global_variable_guid, &bootorder_size);
> >>> +     if (!bootorder)
> >>> +             return EFI_SUCCESS; /* BootOrder is not defined, nothing to do */
> >>> +
> >>> +     num = bootorder_size / sizeof(u16);
> >>> +     for (i = 0; i < num;) {
> >>> +             efi_uintn_t tmp;
> >>> +
> >>> +             efi_create_indexed_name(varname, sizeof(varname),
> >>> +                                     "Boot", bootorder[i]);
> >>> +             load_option = efi_get_var(varname, &efi_global_variable_guid, &size);
> >>> +             if (!load_option)
> >>> +                     goto next;
> >>> +
> >>> +             tmp = size;
> >>
> >> This copying of variables is superfluous. Just keep size.
> >
> > I don't catch your point.
> > This efi_bootmenu_delete_invalid_boot_option() function
> > does not copy the variable, only keeps the variable size.
>
> There is no need to copy the value of variable size to a new variable
> tmp. You can remove the variable tmp and continue using variable size
> below as both variables are of the same type.

tmp is used for whole efi_load_option structure comparison,
so I need to save the original size.

Thanks,
Masahisa Kojima

>
> Best regards
>
> Heinrich
>
> >
> >>
> >>> +             ret = efi_deserialize_load_option(&lo, load_option, &tmp);
> >>> +             if (ret != EFI_SUCCESS)
> >>> +                     goto next;
> >>> +
> >>> +             if (guidcmp(lo.optional_data, &efi_guid_bootmenu_auto_generated) == 0) {
> >>
> >> You must avoid a buffer overrun. Check size >= 16.
> >
> > Yes, I need to check the size of optional_data.
> >
> > Thanks,
> > Masahisa Kojima
> >
> >>
> >> Best regards
> >>
> >> Heinrich
> >>
> >>> +                     for (j = 0; j < count; j++) {
> >>> +                             if (memcmp(opt[j].lo, load_option, size) == 0) {
> >>> +                                     opt[j].exist = true;
> >>> +                                     break;
> >>> +                             }
> >>> +                     }
> >>> +
> >>> +                     if (j == count) {
> >>> +                             ret = delete_boot_option(bootorder, i, bootorder_size);
> >>> +                             if (ret != EFI_SUCCESS) {
> >>> +                                     free(load_option);
> >>> +                                     goto out;
> >>> +                             }
> >>> +
> >>> +                             num--;
> >>> +                             bootorder_size -= sizeof(u16);
> >>> +                             free(load_option);
> >>> +                             continue;
> >>> +                     }
> >>> +             }
> >>> +next:
> >>> +             free(load_option);
> >>> +             i++;
> >>> +     }
> >>> +
> >>> +out:
> >>> +     return ret;
> >>> +}
> >>
>

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

end of thread, other threads:[~2022-05-16  9:21 UTC | newest]

Thread overview: 58+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-04-28  8:09 [PATCH v5 00/17] enable menu-driven boot device selection Masahisa Kojima
2022-04-28  8:09 ` [PATCH v5 01/17] lib/charset: add u16_strlcat() function Masahisa Kojima
2022-04-29 19:36   ` Heinrich Schuchardt
2022-04-28  8:09 ` [PATCH v5 02/17] test: unit test for u16_strlcat() Masahisa Kojima
2022-04-28  8:09 ` [PATCH v5 03/17] menu: always show the menu regardless of the number of entry Masahisa Kojima
2022-05-01 15:37   ` Heinrich Schuchardt
2022-04-28  8:09 ` [PATCH v5 04/17] menu: menu_get_choice() return -ENOENT if menu item is empty Masahisa Kojima
2022-04-29 19:38   ` Heinrich Schuchardt
2022-04-28  8:09 ` [PATCH v5 05/17] efi_loader: export efi_locate_device_handle() Masahisa Kojima
2022-05-01 18:53   ` Heinrich Schuchardt
2022-05-04  9:17     ` Ilias Apalodimas
2022-04-28  8:09 ` [PATCH v5 06/17] efi_loader: bootmgr: add booting from removable media Masahisa Kojima
2022-04-29 17:03   ` Heinrich Schuchardt
2022-05-05 12:05     ` Mark Kettenis
2022-05-05 12:20       ` Heinrich Schuchardt
2022-05-05 12:35         ` Heinrich Schuchardt
2022-05-05 13:25           ` Mark Kettenis
2022-05-05 12:47       ` Mark Kettenis
2022-05-12  9:12         ` AKASHI Takahiro
2022-05-12 10:34           ` Heinrich Schuchardt
2022-04-28  8:09 ` [PATCH v5 07/17] bootmenu: flush input buffer before waiting user key input Masahisa Kojima
2022-04-29 19:46   ` Heinrich Schuchardt
2022-05-09  8:33     ` Masahisa Kojima
2022-04-28  8:09 ` [PATCH v5 08/17] bootmenu: update bootmenu_entry structure Masahisa Kojima
2022-04-29 19:51   ` Heinrich Schuchardt
2022-05-01 20:54     ` Heinrich Schuchardt
2022-05-09  8:54       ` Masahisa Kojima
2022-04-28  8:09 ` [PATCH v5 09/17] bootmenu: add UEFI boot entry into bootmenu Masahisa Kojima
2022-05-01 21:44   ` Heinrich Schuchardt
2022-05-09  8:59     ` Masahisa Kojima
2022-04-28  8:09 ` [PATCH v5 10/17] bootmenu: add distro boot entry Masahisa Kojima
2022-05-01 21:48   ` Heinrich Schuchardt
2022-05-12  8:44     ` Takahiro Akashi
2022-05-12 10:39       ` Heinrich Schuchardt
2022-05-12 11:42         ` Mark Kettenis
2022-04-28  8:09 ` [PATCH v5 11/17] bootmenu: add Kconfig option not to enter U-Boot console Masahisa Kojima
2022-04-29  8:50   ` Mark Kettenis
2022-04-28  8:09 ` [PATCH v5 12/17] bootmenu: factor out the user input handling Masahisa Kojima
2022-04-28  8:09 ` [PATCH v5 13/17] efi_loader: menu-driven addition of UEFI boot option Masahisa Kojima
2022-04-28 16:33   ` Heinrich Schuchardt
2022-04-29 10:56     ` Heinrich Schuchardt
2022-04-30 12:49       ` Heinrich Schuchardt
2022-05-06 17:30         ` Heinrich Schuchardt
2022-05-06 18:10           ` Mark Kettenis
2022-05-06 18:16             ` Heinrich Schuchardt
2022-05-09  9:27               ` Masahisa Kojima
2022-05-09 12:56                 ` Heinrich Schuchardt
2022-04-28  8:09 ` [PATCH v5 14/17] efi_loader: menu-driven deletion of UEFI boot variable Masahisa Kojima
2022-04-28  8:09 ` [PATCH v5 15/17] efi_loader: menu-driven update of UEFI bootorder variable Masahisa Kojima
2022-04-28  8:09 ` [PATCH v5 16/17] bootmenu: add removable media entries Masahisa Kojima
2022-04-28 16:53   ` Heinrich Schuchardt
2022-05-09  8:23     ` Masahisa Kojima
2022-05-09 13:01       ` Heinrich Schuchardt
2022-05-16  9:20         ` Masahisa Kojima
2022-04-28  8:09 ` [PATCH v5 17/17] doc:bootmenu: add UEFI boot and distro boot support description Masahisa Kojima
2022-04-28 16:31 ` [PATCH v5 00/17] enable menu-driven boot device selection Heinrich Schuchardt
2022-04-28 16:58   ` Heinrich Schuchardt
2022-04-29  8:45 ` Mark Kettenis

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.