All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v6 0/6] enable menu-driven boot device selection
@ 2022-05-16 11:00 Masahisa Kojima
  2022-05-16 11:00 ` [PATCH v6 1/6] efi_loader: menu-driven addition of UEFI boot option Masahisa Kojima
                   ` (6 more replies)
  0 siblings, 7 replies; 18+ messages in thread
From: Masahisa Kojima @ 2022-05-16 11:00 UTC (permalink / raw)
  To: u-boot
  Cc: Heinrich Schuchardt, Ilias Apalodimas, Simon Glass,
	Takahiro Akashi, Francois Ozog, Mark Kettenis, Masahisa Kojima

Most of the patches are merged into u-boot/master, this series
contains the remaining patches to support menu-driven
UEFI boot variable maintenance and removable media support.

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

Masahisa Kojima (6):
  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 description for UEFI boot support
  lib/charset: fix compile warnings

 cmd/bootmenu.c                            |  167 ++-
 doc/usage/cmd/bootmenu.rst                |   55 +
 include/charset.h                         |    2 +-
 include/efi_loader.h                      |   60 +
 lib/efi_loader/Makefile                   |    3 +
 lib/efi_loader/efi_bootmenu_maintenance.c | 1282 +++++++++++++++++++++
 lib/efi_loader/efi_bootmgr.c              |    4 +
 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 +-
 11 files changed, 1737 insertions(+), 55 deletions(-)
 create mode 100644 lib/efi_loader/efi_bootmenu_maintenance.c

-- 
2.17.1


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

* [PATCH v6 1/6] efi_loader: menu-driven addition of UEFI boot option
  2022-05-16 11:00 [PATCH v6 0/6] enable menu-driven boot device selection Masahisa Kojima
@ 2022-05-16 11:00 ` Masahisa Kojima
  2022-05-25  1:38   ` Takahiro Akashi
  2022-05-16 11:00 ` [PATCH v6 2/6] efi_loader: menu-driven deletion of UEFI boot variable Masahisa Kojima
                   ` (5 subsequent siblings)
  6 siblings, 1 reply; 18+ messages in thread
From: Masahisa Kojima @ 2022-05-16 11:00 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 v6:
- fix typos
- modify volume name to match U-Boot syntax
- compile in CONFIG_EFI_LOADER=n and CONFIG_CMD_BOOTEFI_BOOTMGR=n
- simplify u16_strncmp() usage
- support "a\b.efi" file path, use link list to handle filepath
- modify length check condition
- UEFI related menu items only appears with CONFIG_AUTOBOOT_MENU_SHOW=y

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                            |  73 +-
 include/efi_loader.h                      |  37 +
 lib/efi_loader/Makefile                   |   3 +
 lib/efi_loader/efi_bootmenu_maintenance.c | 906 ++++++++++++++++++++++
 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, 1184 insertions(+), 54 deletions(-)
 create mode 100644 lib/efi_loader/efi_bootmenu_maintenance.c

diff --git a/cmd/bootmenu.c b/cmd/bootmenu.c
index 8859eebea5..4b846332b0 100644
--- a/cmd/bootmenu.c
+++ b/cmd/bootmenu.c
@@ -19,6 +19,12 @@
 
 /* maximum bootmenu entries */
 #define MAX_COUNT	99
+#if defined(CONFIG_CMD_BOOTEFI_BOOTMGR) && defined(CONFIG_AUTOBOOT_MENU_SHOW)
+#define STATIC_ENTRY 2
+#else
+#define STATIC_ENTRY 1
+#endif
+#define MAX_DYNAMIC_ENTRY (MAX_COUNT - STATIC_ENTRY)
 
 /* maximal size of bootmenu env
  *  9 = strlen("bootmenu_")
@@ -38,10 +44,11 @@ enum boot_type {
 	BOOTMENU_TYPE_NONE = 0,
 	BOOTMENU_TYPE_BOOTMENU,
 	BOOTMENU_TYPE_UEFI_BOOT_OPTION,
+	BOOTMENU_TYPE_UEFI_MAINTENANCE,
 };
 
 struct bootmenu_entry {
-	unsigned short int num;		/* unique number 0 .. MAX_COUNT */
+	unsigned short int num;		/* unique number 0 .. (MAX_COUNT - 1) */
 	char key[3];			/* key identifier of number */
 	u16 *title;			/* title of entry */
 	char *command;			/* hush command of entry */
@@ -55,7 +62,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);
@@ -217,7 +224,7 @@ static int prepare_bootmenu_entry(struct bootmenu_data *menu,
 		iter = entry;
 		++i;
 
-		if (i == MAX_COUNT - 1)
+		if (i == MAX_DYNAMIC_ENTRY)
 			break;
 	}
 
@@ -305,7 +312,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;
 	}
 
@@ -341,14 +348,51 @@ 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) {
+	if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR) && IS_ENABLED(CONFIG_AUTOBOOT_MENU_SHOW)) {
+		if (i < MAX_DYNAMIC_ENTRY) {
 			ret = prepare_uefi_bootorder_entry(menu, &iter, &i);
 			if (ret < 0 && ret != -ENOENT)
 				goto cleanup;
 		}
 	}
 
+	if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR) && IS_ENABLED(CONFIG_AUTOBOOT_MENU_SHOW)) {
+		/* 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));
@@ -520,6 +564,14 @@ static enum bootmenu_ret bootmenu_show(int delay)
 		title = u16_strdup(iter->title);
 		command = strdup(iter->command);
 
+		if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR)) {
+			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;
@@ -610,6 +662,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;
@@ -624,7 +677,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 733ee03cd7..49f326e585 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;
 
@@ -314,6 +317,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[];
@@ -877,6 +883,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
  *
@@ -1048,4 +1056,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..807e9a4319 100644
--- a/lib/efi_loader/Makefile
+++ b/lib/efi_loader/Makefile
@@ -77,6 +77,9 @@ 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
+ifeq ($(CONFIG_CMD_BOOTEFI_BOOTMGR),y)
+obj-$(CONFIG_CMD_BOOTMENU) += efi_bootmenu_maintenance.o
+endif
 
 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..e5124a8a21
--- /dev/null
+++ b/lib/efi_loader/efi_bootmenu_maintenance.c
@@ -0,0 +1,906 @@
+// 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_selection_data {
+	u16 bootorder_index;
+	void *load_option;
+	int *selected;
+};
+
+struct efi_bootmenu_filepath_info {
+	u16 *name;
+	struct list_head list;
+};
+
+struct efi_bootmenu_boot_option {
+	struct efi_simple_file_system_protocol *current_volume;
+	struct efi_device_path *dp_volume;
+	u16 *current_path;
+	struct list_head filepath_list;
+	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 drawn (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_strcmp(info->file_name, u".") == 0 &&
+	    u16_strlen(info->file_name) == 1) {
+		/* stay current path */
+	} else if (u16_strcmp(info->file_name, u"..") == 0 &&
+		   u16_strlen(info->file_name) == 2) {
+		struct efi_bootmenu_filepath_info *iter;
+		struct list_head *pos, *n;
+		int is_last;
+
+		memset(info->bo->current_path, 0, EFI_BOOTMENU_FILE_PATH_BUF_SIZE);
+		list_for_each_safe(pos, n, &info->bo->filepath_list) {
+			iter = list_entry(pos, struct efi_bootmenu_filepath_info, list);
+
+			is_last = list_is_last(&iter->list, &info->bo->filepath_list);
+			if (is_last) {
+				list_del(&iter->list);
+				free(iter->name);
+				free(iter);
+				break;
+			}
+			u16_strlcat(info->bo->current_path, iter->name,
+				    EFI_BOOTMENU_FILE_PATH_MAX);
+			u16_strlcat(info->bo->current_path, u"\\",
+				    EFI_BOOTMENU_FILE_PATH_MAX);
+		}
+	} else {
+		size_t new_len;
+		struct efi_bootmenu_filepath_info *filepath;
+
+		new_len = u16_strlen(info->bo->current_path) +
+				     u16_strlen(info->file_name);
+		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);
+
+		filepath = calloc(1, sizeof(struct efi_bootmenu_filepath_info));
+		if (!filepath)
+			return EFI_OUT_OF_RESOURCES;
+
+		filepath->name = u16_strdup(info->file_name);
+		if (!filepath->name) {
+			free(filepath);
+			return EFI_OUT_OF_RESOURCES;
+		}
+		list_add_tail(&filepath->list, &info->bo->filepath_list);
+
+		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, EFI_BOOTMENU_FILE_PATH_BUF_SIZE);
+
+		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;
+	struct list_head *pos, *n;
+	efi_uintn_t dp_size, fp_size;
+	struct efi_bootmenu_boot_option bo;
+	struct efi_device_path_file_path *fp;
+	struct efi_bootmenu_filepath_info *item;
+
+	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;
+
+	INIT_LIST_HEAD(&bo.filepath_list);
+
+	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);
+
+	list_for_each_safe(pos, n, &bo.filepath_list) {
+		item = list_entry(pos, struct efi_bootmenu_filepath_info, list);
+		list_del(&item->list);
+		free(item->name);
+		free(item);
+	}
+
+	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 60a3fc85ac..cf9622ed1a 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 f5b462fb16..01576c8ed2 100644
--- a/lib/efi_loader/efi_disk.c
+++ b/lib/efi_loader/efi_disk.c
@@ -751,3 +751,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] 18+ messages in thread

* [PATCH v6 2/6] efi_loader: menu-driven deletion of UEFI boot variable
  2022-05-16 11:00 [PATCH v6 0/6] enable menu-driven boot device selection Masahisa Kojima
  2022-05-16 11:00 ` [PATCH v6 1/6] efi_loader: menu-driven addition of UEFI boot option Masahisa Kojima
@ 2022-05-16 11:00 ` Masahisa Kojima
  2022-05-16 11:00 ` [PATCH v6 3/6] efi_loader: menu-driven update of UEFI bootorder variable Masahisa Kojima
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 18+ messages in thread
From: Masahisa Kojima @ 2022-05-16 11:00 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>
---
(no update sinch v5)

changes in v5:
- split into the separate patch

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

diff --git a/lib/efi_loader/efi_bootmenu_maintenance.c b/lib/efi_loader/efi_bootmenu_maintenance.c
index e5124a8a21..96306cd2cc 100644
--- a/lib/efi_loader/efi_bootmenu_maintenance.c
+++ b/lib/efi_loader/efi_bootmenu_maintenance.c
@@ -334,6 +334,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;
@@ -861,6 +944,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;
@@ -889,6 +1024,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] 18+ messages in thread

* [PATCH v6 3/6] efi_loader: menu-driven update of UEFI bootorder variable
  2022-05-16 11:00 [PATCH v6 0/6] enable menu-driven boot device selection Masahisa Kojima
  2022-05-16 11:00 ` [PATCH v6 1/6] efi_loader: menu-driven addition of UEFI boot option Masahisa Kojima
  2022-05-16 11:00 ` [PATCH v6 2/6] efi_loader: menu-driven deletion of UEFI boot variable Masahisa Kojima
@ 2022-05-16 11:00 ` Masahisa Kojima
  2022-05-24 10:42   ` Heinrich Schuchardt
  2022-05-16 11:00 ` [PATCH v6 4/6] bootmenu: add removable media entries Masahisa Kojima
                   ` (3 subsequent siblings)
  6 siblings, 1 reply; 18+ messages in thread
From: Masahisa Kojima @ 2022-05-16 11:00 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>
---
(no update since v5)

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 96306cd2cc..be67fca95f 100644
--- a/lib/efi_loader/efi_bootmenu_maintenance.c
+++ b/lib/efi_loader/efi_bootmenu_maintenance.c
@@ -746,6 +746,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;
@@ -996,6 +1046,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;
@@ -1025,6 +1126,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] 18+ messages in thread

* [PATCH v6 4/6] bootmenu: add removable media entries
  2022-05-16 11:00 [PATCH v6 0/6] enable menu-driven boot device selection Masahisa Kojima
                   ` (2 preceding siblings ...)
  2022-05-16 11:00 ` [PATCH v6 3/6] efi_loader: menu-driven update of UEFI bootorder variable Masahisa Kojima
@ 2022-05-16 11:00 ` Masahisa Kojima
  2022-05-16 11:00 ` [PATCH v6 5/6] doc:bootmenu: add description for UEFI boot support Masahisa Kojima
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 18+ messages in thread
From: Masahisa Kojima @ 2022-05-16 11:00 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 optional_data is
removed when the efi bootmgr loads the selected UEFI boot option.

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 v6:
- optional_data size is changed to 16bytes
- check the load option size before comparison
- remove guid included in optional_data of auto generated
  entry when loading

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                      |  23 ++++
 lib/efi_loader/efi_bootmenu_maintenance.c | 138 ++++++++++++++++++++++
 lib/efi_loader/efi_bootmgr.c              |   4 +
 4 files changed, 259 insertions(+)

diff --git a/cmd/bootmenu.c b/cmd/bootmenu.c
index 4b846332b0..911086c97e 100644
--- a/cmd/bootmenu.c
+++ b/cmd/bootmenu.c
@@ -234,6 +234,89 @@ static int prepare_bootmenu_entry(struct bootmenu_data *menu,
 	return 1;
 }
 
+/**
+ * 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_uefi_bootorder_entry() - generate the uefi bootmenu entries
  *
@@ -326,6 +409,7 @@ static int prepare_uefi_bootorder_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;
@@ -349,6 +433,16 @@ static struct bootmenu_data *bootmenu_create(int delay)
 		goto cleanup;
 
 	if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR) && IS_ENABLED(CONFIG_AUTOBOOT_MENU_SHOW)) {
+		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 49f326e585..48485e8da9 100644
--- a/include/efi_loader.h
+++ b/include/efi_loader.h
@@ -252,6 +252,9 @@ extern const struct efi_hii_string_protocol efi_hii_string;
 
 uint16_t *efi_dp_str(struct efi_device_path *dp);
 
+/* GUID for the auto generated boot menu entry */
+extern const efi_guid_t efi_guid_bootmenu_auto_generated;
+
 /* GUID of the U-Boot root node */
 extern const efi_guid_t efi_u_boot_guid;
 #ifdef CONFIG_SANDBOX
@@ -934,6 +937,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);
@@ -1082,6 +1101,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 be67fca95f..58f2999c61 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)
+
+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);
 
 /**
@@ -1142,3 +1149,134 @@ 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 "1234567" string to allocate enough space
+		 * to store guid, instead of realloc the load_option.
+		 */
+		lo.optional_data = "1234567";
+		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"1234567"));
+		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, &size);
+		if (ret != EFI_SUCCESS)
+			goto next;
+
+		if (size >= sizeof(efi_guid_bootmenu_auto_generated)) {
+			if (guidcmp(lo.optional_data, &efi_guid_bootmenu_auto_generated) == 0) {
+				for (j = 0; j < count; j++) {
+					if (opt[j].size == tmp &&
+					    memcmp(opt[j].lo, load_option, tmp) == 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;
+}
diff --git a/lib/efi_loader/efi_bootmgr.c b/lib/efi_loader/efi_bootmgr.c
index 631a25d76e..ca2fbd26ce 100644
--- a/lib/efi_loader/efi_bootmgr.c
+++ b/lib/efi_loader/efi_bootmgr.c
@@ -146,6 +146,10 @@ static efi_status_t try_load_entry(u16 n, efi_handle_t *handle,
 	}
 
 	/* Set load options */
+	if (size >= sizeof(efi_guid_t) &&
+	    !guidcmp(lo.optional_data, &efi_guid_bootmenu_auto_generated))
+		size = 0;
+
 	if (size) {
 		*load_options = malloc(size);
 		if (!*load_options) {
-- 
2.17.1


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

* [PATCH v6 5/6] doc:bootmenu: add description for UEFI boot support
  2022-05-16 11:00 [PATCH v6 0/6] enable menu-driven boot device selection Masahisa Kojima
                   ` (3 preceding siblings ...)
  2022-05-16 11:00 ` [PATCH v6 4/6] bootmenu: add removable media entries Masahisa Kojima
@ 2022-05-16 11:00 ` Masahisa Kojima
  2022-05-18  1:31   ` Takahiro Akashi
  2022-05-16 11:00 ` [PATCH v6 6/6] lib/charset: fix compile warnings Masahisa Kojima
  2022-06-04  6:33 ` [PATCH v6 0/6] enable menu-driven boot device selection Heinrich Schuchardt
  6 siblings, 1 reply; 18+ messages in thread
From: Masahisa Kojima @ 2022-05-16 11:00 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
for boot device selection.

This commit adds the description how the UEFI 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 v6:
- remove distro boot related contents because the distro boot
support in bootmenu is dropped
- update uefi entry example
- add [delay] argument of bootmenu command
- add description to enable uefi boot entry

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 | 55 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 55 insertions(+)

diff --git a/doc/usage/cmd/bootmenu.rst b/doc/usage/cmd/bootmenu.rst
index 9430f8c9aa..6b154d9655 100644
--- a/doc/usage/cmd/bootmenu.rst
+++ b/doc/usage/cmd/bootmenu.rst
@@ -4,6 +4,16 @@
 bootmenu command
 ================
 
+Synopsis
+--------
+
+::
+
+    bootmenu [delay]
+
+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
@@ -79,6 +89,35 @@ 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.
+This auto generated entry is named as "<interface> <devnum>:<part>" format.
+(e.g. "usb 0:1")
+
+Example bootmenu is as below::
+
+    *** U-Boot Boot Menu ***
+
+       mmc 0:1
+       mmc 0:2
+       debian
+       nvme 0:1
+       ubuntu
+       nvme 0:2
+       usb 0:2
+
+Configuration
+-------------
+
 The "bootmenu" command is enabled by::
 
     CONFIG_CMD_BOOTMENU=y
@@ -88,3 +127,19 @@ To run the bootmenu at startup add these additional settings::
     CONFIG_AUTOBOOT_KEYED=y
     CONFIG_BOOTDELAY=30
     CONFIG_AUTOBOOT_MENU_SHOW=y
+
+UEFI boot variable enumeration is enabled by::
+
+    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
+
+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"
-- 
2.17.1


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

* [PATCH v6 6/6] lib/charset: fix compile warnings
  2022-05-16 11:00 [PATCH v6 0/6] enable menu-driven boot device selection Masahisa Kojima
                   ` (4 preceding siblings ...)
  2022-05-16 11:00 ` [PATCH v6 5/6] doc:bootmenu: add description for UEFI boot support Masahisa Kojima
@ 2022-05-16 11:00 ` Masahisa Kojima
  2022-06-04  6:33 ` [PATCH v6 0/6] enable menu-driven boot device selection Heinrich Schuchardt
  6 siblings, 0 replies; 18+ messages in thread
From: Masahisa Kojima @ 2022-05-16 11:00 UTC (permalink / raw)
  To: u-boot
  Cc: Heinrich Schuchardt, Ilias Apalodimas, Simon Glass,
	Takahiro Akashi, Francois Ozog, Mark Kettenis, Masahisa Kojima

This commit fixes the following compile warnings
for the documentation.

./include/charset.h:276: warning: Function parameter or member 'size' not described in 'u16_strlcat'
./include/charset.h:276: warning: Excess function parameter 'count' description in 'u16_strlcat'

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

 include/charset.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/include/charset.h b/include/charset.h
index 20abfbe752..e900fd789a 100644
--- a/include/charset.h
+++ b/include/charset.h
@@ -273,7 +273,7 @@ u16 *u16_strdup(const void *src);
  * Return:		required size including trailing 0x0000 in u16 words
  *			If return value >= count, truncation occurred.
  */
-size_t u16_strlcat(u16 *dest, const u16 *src, size_t size);
+size_t u16_strlcat(u16 *dest, const u16 *src, size_t count);
 
 /**
  * utf16_to_utf8() - Convert an utf16 string to utf8
-- 
2.17.1


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

* Re: [PATCH v6 5/6] doc:bootmenu: add description for UEFI boot support
  2022-05-16 11:00 ` [PATCH v6 5/6] doc:bootmenu: add description for UEFI boot support Masahisa Kojima
@ 2022-05-18  1:31   ` Takahiro Akashi
  2022-05-18  5:27     ` Masahisa Kojima
  0 siblings, 1 reply; 18+ messages in thread
From: Takahiro Akashi @ 2022-05-18  1:31 UTC (permalink / raw)
  To: Masahisa Kojima
  Cc: u-boot, Heinrich Schuchardt, Ilias Apalodimas, Simon Glass,
	Francois Ozog, Mark Kettenis, Bin Meng

Kojima-san,

On Mon, May 16, 2022 at 08:00:41PM +0900, Masahisa Kojima wrote:
> The bootmenu enumerates the UEFI boot options
> for boot device selection.
> 
> This commit adds the description how the UEFI 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 v6:
> - remove distro boot related contents because the distro boot
> support in bootmenu is dropped

? I rejected it in my comment.
I don't think we have enough consensus yet.

> - update uefi entry example
> - add [delay] argument of bootmenu command
> - add description to enable uefi boot entry
> 
> 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 | 55 ++++++++++++++++++++++++++++++++++++++
>  1 file changed, 55 insertions(+)
> 
> diff --git a/doc/usage/cmd/bootmenu.rst b/doc/usage/cmd/bootmenu.rst
> index 9430f8c9aa..6b154d9655 100644
> --- a/doc/usage/cmd/bootmenu.rst
> +++ b/doc/usage/cmd/bootmenu.rst
> @@ -4,6 +4,16 @@
>  bootmenu command
>  ================
>  
> +Synopsis
> +--------
> +
> +::
> +
> +    bootmenu [delay]
> +
> +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
> @@ -79,6 +89,35 @@ 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".

To be strict, the bootmenu command *does not* create UEFI boot variables
(except ones for removable media).
So, I would suggest you to modify the text like:

If enabled, the bootmenu command will automatically generate and add
UEFI-related boot menu entries for
* possible bootable media with default file names
* user-defined UEFI boot options

Then,

> +The bootmenu automatically enumerates the possible bootable
> +media devices supporting EFI_SIMPLE_FILE_SYSTEM_PROTOCOL.
> +This auto generated entry is named as "<interface> <devnum>:<part>" format.
> +(e.g. "usb 0:1")
> +

> 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".
> +
> +Example bootmenu is as below::
> +
> +    *** U-Boot Boot Menu ***
> +
> +       mmc 0:1
> +       mmc 0:2
> +       debian
> +       nvme 0:1
> +       ubuntu
> +       nvme 0:2
> +       usb 0:2

If I understand your patch correctly, menus are initially sorted out
in the order of
* user-defined bootmenu_xxx variables
* removable bootable media
* user-defined UEFI boot options

The example above looks odd.

That said, it would be worth mentioning that users can changes the order
among UEFI-related menus. (Right?)

In addition, please describe the default action if users do not select
anything.

-Takahiro Akashi


> +
> +Configuration
> +-------------
> +
>  The "bootmenu" command is enabled by::
>  
>      CONFIG_CMD_BOOTMENU=y
> @@ -88,3 +127,19 @@ To run the bootmenu at startup add these additional settings::
>      CONFIG_AUTOBOOT_KEYED=y
>      CONFIG_BOOTDELAY=30
>      CONFIG_AUTOBOOT_MENU_SHOW=y
> +
> +UEFI boot variable enumeration is enabled by::
> +
> +    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
> +
> +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"
> -- 
> 2.17.1
> 

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

* Re: [PATCH v6 5/6] doc:bootmenu: add description for UEFI boot support
  2022-05-18  1:31   ` Takahiro Akashi
@ 2022-05-18  5:27     ` Masahisa Kojima
  0 siblings, 0 replies; 18+ messages in thread
From: Masahisa Kojima @ 2022-05-18  5:27 UTC (permalink / raw)
  To: Takahiro Akashi, Masahisa Kojima, u-boot, Heinrich Schuchardt,
	Ilias Apalodimas, Simon Glass, Francois Ozog, Mark Kettenis,
	Bin Meng

Hi Akashi-san,

On Wed, 18 May 2022 at 10:31, Takahiro Akashi
<takahiro.akashi@linaro.org> wrote:
>
> Kojima-san,
>
> On Mon, May 16, 2022 at 08:00:41PM +0900, Masahisa Kojima wrote:
> > The bootmenu enumerates the UEFI boot options
> > for boot device selection.
> >
> > This commit adds the description how the UEFI 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 v6:
> > - remove distro boot related contents because the distro boot
> > support in bootmenu is dropped
>
> ? I rejected it in my comment.
> I don't think we have enough consensus yet.

I am aware of your comment and discussion in U-Boot ML.

In my opinion, I doubt that the distro boot users use "bootmenu"
to select the device to invoke "run bootcmd_xxx". I guess no.
And my primary goal is disabling the U-Boot console for UEFI based
system. Distro boot users can still live without the U-Boot console
without bootmenu support.

Distro boot support in bootmenu is not planned initially at my side,
but it was added during code review process, then it is
(temporary) dropped in the end. I don't have a strong opinion about it.

Anyway, distro boot support in bootmenu is an individual patch,
I am happy to re-send if needed.

>
> > - update uefi entry example
> > - add [delay] argument of bootmenu command
> > - add description to enable uefi boot entry
> >
> > 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 | 55 ++++++++++++++++++++++++++++++++++++++
> >  1 file changed, 55 insertions(+)
> >
> > diff --git a/doc/usage/cmd/bootmenu.rst b/doc/usage/cmd/bootmenu.rst
> > index 9430f8c9aa..6b154d9655 100644
> > --- a/doc/usage/cmd/bootmenu.rst
> > +++ b/doc/usage/cmd/bootmenu.rst
> > @@ -4,6 +4,16 @@
> >  bootmenu command
> >  ================
> >
> > +Synopsis
> > +--------
> > +
> > +::
> > +
> > +    bootmenu [delay]
> > +
> > +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
> > @@ -79,6 +89,35 @@ 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".
>
> To be strict, the bootmenu command *does not* create UEFI boot variables
> (except ones for removable media).
> So, I would suggest you to modify the text like:
>
> If enabled, the bootmenu command will automatically generate and add
> UEFI-related boot menu entries for
> * possible bootable media with default file names
> * user-defined UEFI boot options
>
> Then,
>
> > +The bootmenu automatically enumerates the possible bootable
> > +media devices supporting EFI_SIMPLE_FILE_SYSTEM_PROTOCOL.
> > +This auto generated entry is named as "<interface> <devnum>:<part>" format.
> > +(e.g. "usb 0:1")
> > +

Thank you, I agree.

>
> > 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".
> > +
> > +Example bootmenu is as below::
> > +
> > +    *** U-Boot Boot Menu ***
> > +
> > +       mmc 0:1
> > +       mmc 0:2
> > +       debian
> > +       nvme 0:1
> > +       ubuntu
> > +       nvme 0:2
> > +       usb 0:2
>
> If I understand your patch correctly, menus are initially sorted out
> in the order of
> * user-defined bootmenu_xxx variables
> * removable bootable media
> * user-defined UEFI boot options
>
> The example above looks odd.

The example is intended and same as actual behavior.

Sorry for confusing you, the function name and description of
prepare_media_device_entry() is wrong. This function just checks
the removable bootable media, then updates UEFI boot
variables(BOOTXXXX and BootOrder).

prepare_uefi_bootorder_entry() enumerates the UEFI related
entries in the order of BootOrder. So removable bootable media
entries and user-defined UEFI boot option entries are mixed in the
bootmenu.

>
> That said, it would be worth mentioning that users can changes the order
> among UEFI-related menus. (Right?)

OK.

>
> In addition, please describe the default action if users do not select
> anything.

Yes, It should be noted.

Thanks,
Masahisa Kojima

>
> -Takahiro Akashi
>
>
> > +
> > +Configuration
> > +-------------
> > +
> >  The "bootmenu" command is enabled by::
> >
> >      CONFIG_CMD_BOOTMENU=y
> > @@ -88,3 +127,19 @@ To run the bootmenu at startup add these additional settings::
> >      CONFIG_AUTOBOOT_KEYED=y
> >      CONFIG_BOOTDELAY=30
> >      CONFIG_AUTOBOOT_MENU_SHOW=y
> > +
> > +UEFI boot variable enumeration is enabled by::
> > +
> > +    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
> > +
> > +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"
> > --
> > 2.17.1
> >

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

* Re: [PATCH v6 3/6] efi_loader: menu-driven update of UEFI bootorder variable
  2022-05-16 11:00 ` [PATCH v6 3/6] efi_loader: menu-driven update of UEFI bootorder variable Masahisa Kojima
@ 2022-05-24 10:42   ` Heinrich Schuchardt
  2022-05-26  7:49     ` Masahisa Kojima
  0 siblings, 1 reply; 18+ messages in thread
From: Heinrich Schuchardt @ 2022-05-24 10:42 UTC (permalink / raw)
  To: Masahisa Kojima
  Cc: Ilias Apalodimas, Simon Glass, Takahiro Akashi, Francois Ozog,
	Mark Kettenis, u-boot

On 5/16/22 13:00, Masahisa Kojima wrote:
> This commit adds the menu-driven update of UEFI bootorder
> variable.
>
> Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
> ---
> (no update since v5)
>
> 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 96306cd2cc..be67fca95f 100644
> --- a/lib/efi_loader/efi_bootmenu_maintenance.c
> +++ b/lib/efi_loader/efi_bootmenu_maintenance.c
> @@ -746,6 +746,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};

After selecting an item why can we simply use the up and down key to
move the selected item?

> +
> +	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;
> @@ -996,6 +1046,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));

After updating an item we should be in the
efi_bootmenu_show_boot_selection() screen again.

Best regards

Heinrich

> +	} 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;
> @@ -1025,6 +1126,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},
>   };
>


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

* Re: [PATCH v6 1/6] efi_loader: menu-driven addition of UEFI boot option
  2022-05-16 11:00 ` [PATCH v6 1/6] efi_loader: menu-driven addition of UEFI boot option Masahisa Kojima
@ 2022-05-25  1:38   ` Takahiro Akashi
  2022-05-26  7:37     ` Heinrich Schuchardt
  2022-06-06  0:39     ` Masahisa Kojima
  0 siblings, 2 replies; 18+ messages in thread
From: Takahiro Akashi @ 2022-05-25  1:38 UTC (permalink / raw)
  To: Masahisa Kojima
  Cc: u-boot, Heinrich Schuchardt, Ilias Apalodimas, Simon Glass,
	Francois Ozog, Mark Kettenis

On Mon, May 16, 2022 at 08:00:37PM +0900, 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 v6:
> - fix typos
> - modify volume name to match U-Boot syntax
> - compile in CONFIG_EFI_LOADER=n and CONFIG_CMD_BOOTEFI_BOOTMGR=n

Is this correct?

> - simplify u16_strncmp() usage
> - support "a\b.efi" file path, use link list to handle filepath
> - modify length check condition
> - UEFI related menu items only appears with CONFIG_AUTOBOOT_MENU_SHOW=y

Why?
I think that the feature is useful even without AUTOBOOT.
As you know, efidebug is seen as a debugging tool and is not expected
to be enabled in production systems.

So the feature you're adding is the only available UI for boot manager.
What I recommend is
- to create a boot manager maintenance as a standalone U-Boot command,
- to add an bootmenu entry for invoking the command

> 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                            |  73 +-
>  include/efi_loader.h                      |  37 +
>  lib/efi_loader/Makefile                   |   3 +
>  lib/efi_loader/efi_bootmenu_maintenance.c | 906 ++++++++++++++++++++++

I would say that this file should be moved under /cmd as the code does not
implement any UEFI specification semantics, but simply provides helper
functions for bootmenu command.

Or I recommend that the boot manager be implemented as a standalone command
(as I insisted serveral times before) and the related maintenance feature
be invoked as follows:
   => efibootmanager -i (i for interactive)

>  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, 1184 insertions(+), 54 deletions(-)
>  create mode 100644 lib/efi_loader/efi_bootmenu_maintenance.c
> 
> diff --git a/cmd/bootmenu.c b/cmd/bootmenu.c
> index 8859eebea5..4b846332b0 100644
> --- a/cmd/bootmenu.c
> +++ b/cmd/bootmenu.c
> @@ -19,6 +19,12 @@
>  
>  /* maximum bootmenu entries */
>  #define MAX_COUNT	99
> +#if defined(CONFIG_CMD_BOOTEFI_BOOTMGR) && defined(CONFIG_AUTOBOOT_MENU_SHOW)
> +#define STATIC_ENTRY 2
> +#else
> +#define STATIC_ENTRY 1
> +#endif
> +#define MAX_DYNAMIC_ENTRY (MAX_COUNT - STATIC_ENTRY)
>  
>  /* maximal size of bootmenu env
>   *  9 = strlen("bootmenu_")
> @@ -38,10 +44,11 @@ enum boot_type {
>  	BOOTMENU_TYPE_NONE = 0,
>  	BOOTMENU_TYPE_BOOTMENU,
>  	BOOTMENU_TYPE_UEFI_BOOT_OPTION,
> +	BOOTMENU_TYPE_UEFI_MAINTENANCE,
>  };
>  
>  struct bootmenu_entry {
> -	unsigned short int num;		/* unique number 0 .. MAX_COUNT */
> +	unsigned short int num;		/* unique number 0 .. (MAX_COUNT - 1) */
>  	char key[3];			/* key identifier of number */
>  	u16 *title;			/* title of entry */
>  	char *command;			/* hush command of entry */
> @@ -55,7 +62,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);
> @@ -217,7 +224,7 @@ static int prepare_bootmenu_entry(struct bootmenu_data *menu,
>  		iter = entry;
>  		++i;
>  
> -		if (i == MAX_COUNT - 1)
> +		if (i == MAX_DYNAMIC_ENTRY)
>  			break;
>  	}
>  
> @@ -305,7 +312,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;
>  	}
>  
> @@ -341,14 +348,51 @@ 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) {
> +	if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR) && IS_ENABLED(CONFIG_AUTOBOOT_MENU_SHOW)) {
> +		if (i < MAX_DYNAMIC_ENTRY) {
>  			ret = prepare_uefi_bootorder_entry(menu, &iter, &i);
>  			if (ret < 0 && ret != -ENOENT)
>  				goto cleanup;
>  		}
>  	}
>  
> +	if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR) && IS_ENABLED(CONFIG_AUTOBOOT_MENU_SHOW)) {
> +		/* 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 'u16' is a matter, why not define the type of 'title' as u8 and you can always use
utf16_to_utf8() for the setting the value to this field.

> +			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));
> @@ -520,6 +564,14 @@ static enum bootmenu_ret bootmenu_show(int delay)
>  		title = u16_strdup(iter->title);
>  		command = strdup(iter->command);
>  
> +		if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR)) {
> +			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;
> @@ -610,6 +662,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;
> @@ -624,7 +677,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 733ee03cd7..49f326e585 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;
>  
> @@ -314,6 +317,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[];
> @@ -877,6 +883,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
>   *
> @@ -1048,4 +1056,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..807e9a4319 100644
> --- a/lib/efi_loader/Makefile
> +++ b/lib/efi_loader/Makefile
> @@ -77,6 +77,9 @@ 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
> +ifeq ($(CONFIG_CMD_BOOTEFI_BOOTMGR),y)
> +obj-$(CONFIG_CMD_BOOTMENU) += efi_bootmenu_maintenance.o
> +endif
>  
>  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..e5124a8a21
> --- /dev/null
> +++ b/lib/efi_loader/efi_bootmenu_maintenance.c
> @@ -0,0 +1,906 @@
> +// 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_selection_data {
> +	u16 bootorder_index;
> +	void *load_option;
> +	int *selected;
> +};
> +
> +struct efi_bootmenu_filepath_info {
> +	u16 *name;
> +	struct list_head list;
> +};
> +
> +struct efi_bootmenu_boot_option {
> +	struct efi_simple_file_system_protocol *current_volume;
> +	struct efi_device_path *dp_volume;
> +	u16 *current_path;
> +	struct list_head filepath_list;
> +	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),
> +};

FYI, 'END' is also defined as a static variable in efi_device_path.c

> +
> +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 drawn (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;
> +		}

I don't fully understand how bootmenu works with key strokes,
but I think that this kind of key handling is quite generic
and put it as a common helper function.

> +	}
> +
> +	/* 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_strcmp(info->file_name, u".") == 0 &&
> +	    u16_strlen(info->file_name) == 1) {
> +		/* stay current path */
> +	} else if (u16_strcmp(info->file_name, u"..") == 0 &&
> +		   u16_strlen(info->file_name) == 2) {
> +		struct efi_bootmenu_filepath_info *iter;
> +		struct list_head *pos, *n;
> +		int is_last;
> +
> +		memset(info->bo->current_path, 0, EFI_BOOTMENU_FILE_PATH_BUF_SIZE);
> +		list_for_each_safe(pos, n, &info->bo->filepath_list) {
> +			iter = list_entry(pos, struct efi_bootmenu_filepath_info, list);
> +
> +			is_last = list_is_last(&iter->list, &info->bo->filepath_list);
> +			if (is_last) {
> +				list_del(&iter->list);
> +				free(iter->name);
> +				free(iter);
> +				break;
> +			}
> +			u16_strlcat(info->bo->current_path, iter->name,
> +				    EFI_BOOTMENU_FILE_PATH_MAX);
> +			u16_strlcat(info->bo->current_path, u"\\",
> +				    EFI_BOOTMENU_FILE_PATH_MAX);
> +		}
> +	} else {
> +		size_t new_len;
> +		struct efi_bootmenu_filepath_info *filepath;
> +
> +		new_len = u16_strlen(info->bo->current_path) +
> +				     u16_strlen(info->file_name);
> +		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);
> +
> +		filepath = calloc(1, sizeof(struct efi_bootmenu_filepath_info));
> +		if (!filepath)
> +			return EFI_OUT_OF_RESOURCES;
> +
> +		filepath->name = u16_strdup(info->file_name);
> +		if (!filepath->name) {
> +			free(filepath);
> +			return EFI_OUT_OF_RESOURCES;
> +		}
> +		list_add_tail(&filepath->list, &info->bo->filepath_list);
> +
> +		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;
> +}

I prefer to see a menu like:
  BOOTxxxx
  title:
  file:
  option(arguments):
  set:
  cancel:
rather than being prompted one by one.

Then users can select any of items in arbitrary order to define them
or to quit the menu.

This way, the same menu can be re-used to *modify* an existing entry.

> +
> +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, EFI_BOOTMENU_FILE_PATH_BUF_SIZE);
> +
> +		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;
> +	struct list_head *pos, *n;
> +	efi_uintn_t dp_size, fp_size;
> +	struct efi_bootmenu_boot_option bo;
> +	struct efi_device_path_file_path *fp;
> +	struct efi_bootmenu_filepath_info *item;
> +
> +	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;
> +
> +	INIT_LIST_HEAD(&bo.filepath_list);
> +
> +	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;

This if statement doesn't make sense.

> +
> +out:
> +	free(buf);
> +	free(bo.boot_name);
> +	free(bo.current_path);
> +
> +	list_for_each_safe(pos, n, &bo.filepath_list) {
> +		item = list_entry(pos, struct efi_bootmenu_filepath_info, list);
> +		list_del(&item->list);
> +		free(item->name);
> +		free(item);
> +	}
> +
> +	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 60a3fc85ac..cf9622ed1a 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 f5b462fb16..01576c8ed2 100644
> --- a/lib/efi_loader/efi_disk.c
> +++ b/lib/efi_loader/efi_disk.c
> @@ -751,3 +751,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;
> +}

Please note that not all the BLOCK_IO interfaces have corresponding diskobj's,
in particular, efi_driver/efi_block_device.c.

-Takahiro Akashi

> 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	[flat|nested] 18+ messages in thread

* Re: [PATCH v6 1/6] efi_loader: menu-driven addition of UEFI boot option
  2022-05-25  1:38   ` Takahiro Akashi
@ 2022-05-26  7:37     ` Heinrich Schuchardt
  2022-06-06  0:45       ` Masahisa Kojima
  2022-06-06  0:39     ` Masahisa Kojima
  1 sibling, 1 reply; 18+ messages in thread
From: Heinrich Schuchardt @ 2022-05-26  7:37 UTC (permalink / raw)
  To: Masahisa Kojima
  Cc: Takahiro Akashi, Mark Kettenis, u-boot, Ilias Apalodimas,
	Simon Glass, Francois Ozog

On 5/25/22 03:38, Takahiro Akashi wrote:
> On Mon, May 16, 2022 at 08:00:37PM +0900, 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 v6:
>> - fix typos
>> - modify volume name to match U-Boot syntax
>> - compile in CONFIG_EFI_LOADER=n and CONFIG_CMD_BOOTEFI_BOOTMGR=n
>
> Is this correct?
>
>> - simplify u16_strncmp() usage
>> - support "a\b.efi" file path, use link list to handle filepath
>> - modify length check condition
>> - UEFI related menu items only appears with CONFIG_AUTOBOOT_MENU_SHOW=y
>
> Why?
> I think that the feature is useful even without AUTOBOOT.
> As you know, efidebug is seen as a debugging tool and is not expected
> to be enabled in production systems.
>
> So the feature you're adding is the only available UI for boot manager.
> What I recommend is
> - to create a boot manager maintenance as a standalone U-Boot command,
> - to add an bootmenu entry for invoking the command
>
>> 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                            |  73 +-
>>   include/efi_loader.h                      |  37 +
>>   lib/efi_loader/Makefile                   |   3 +
>>   lib/efi_loader/efi_bootmenu_maintenance.c | 906 ++++++++++++++++++++++
>
> I would say that this file should be moved under /cmd as the code does not
> implement any UEFI specification semantics, but simply provides helper
> functions for bootmenu command.
>
> Or I recommend that the boot manager be implemented as a standalone command
> (as I insisted serveral times before) and the related maintenance feature
> be invoked as follows:
>     => efibootmanager -i (i for interactive)
>
>>   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, 1184 insertions(+), 54 deletions(-)
>>   create mode 100644 lib/efi_loader/efi_bootmenu_maintenance.c
>>
>> diff --git a/cmd/bootmenu.c b/cmd/bootmenu.c
>> index 8859eebea5..4b846332b0 100644
>> --- a/cmd/bootmenu.c
>> +++ b/cmd/bootmenu.c
>> @@ -19,6 +19,12 @@
>>
>>   /* maximum bootmenu entries */
>>   #define MAX_COUNT	99
>> +#if defined(CONFIG_CMD_BOOTEFI_BOOTMGR) && defined(CONFIG_AUTOBOOT_MENU_SHOW)
>> +#define STATIC_ENTRY 2
>> +#else
>> +#define STATIC_ENTRY 1
>> +#endif
>> +#define MAX_DYNAMIC_ENTRY (MAX_COUNT - STATIC_ENTRY)
>>
>>   /* maximal size of bootmenu env
>>    *  9 = strlen("bootmenu_")
>> @@ -38,10 +44,11 @@ enum boot_type {
>>   	BOOTMENU_TYPE_NONE = 0,
>>   	BOOTMENU_TYPE_BOOTMENU,
>>   	BOOTMENU_TYPE_UEFI_BOOT_OPTION,
>> +	BOOTMENU_TYPE_UEFI_MAINTENANCE,
>>   };
>>
>>   struct bootmenu_entry {
>> -	unsigned short int num;		/* unique number 0 .. MAX_COUNT */
>> +	unsigned short int num;		/* unique number 0 .. (MAX_COUNT - 1) */
>>   	char key[3];			/* key identifier of number */
>>   	u16 *title;			/* title of entry */
>>   	char *command;			/* hush command of entry */
>> @@ -55,7 +62,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);
>> @@ -217,7 +224,7 @@ static int prepare_bootmenu_entry(struct bootmenu_data *menu,
>>   		iter = entry;
>>   		++i;
>>
>> -		if (i == MAX_COUNT - 1)
>> +		if (i == MAX_DYNAMIC_ENTRY)
>>   			break;
>>   	}
>>
>> @@ -305,7 +312,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;
>>   	}
>>
>> @@ -341,14 +348,51 @@ 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) {
>> +	if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR) && IS_ENABLED(CONFIG_AUTOBOOT_MENU_SHOW)) {
>> +		if (i < MAX_DYNAMIC_ENTRY) {
>>   			ret = prepare_uefi_bootorder_entry(menu, &iter, &i);
>>   			if (ret < 0 && ret != -ENOENT)
>>   				goto cleanup;
>>   		}
>>   	}
>>
>> +	if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR) && IS_ENABLED(CONFIG_AUTOBOOT_MENU_SHOW)) {
>> +		/* 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 'u16' is a matter, why not define the type of 'title' as u8 and you can always use
> utf16_to_utf8() for the setting the value to this field.
>
>> +			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));
>> @@ -520,6 +564,14 @@ static enum bootmenu_ret bootmenu_show(int delay)
>>   		title = u16_strdup(iter->title);
>>   		command = strdup(iter->command);
>>
>> +		if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR)) {
>> +			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;
>> @@ -610,6 +662,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;
>> @@ -624,7 +677,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 733ee03cd7..49f326e585 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;
>>
>> @@ -314,6 +317,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[];
>> @@ -877,6 +883,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
>>    *
>> @@ -1048,4 +1056,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..807e9a4319 100644
>> --- a/lib/efi_loader/Makefile
>> +++ b/lib/efi_loader/Makefile
>> @@ -77,6 +77,9 @@ 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
>> +ifeq ($(CONFIG_CMD_BOOTEFI_BOOTMGR),y)
>> +obj-$(CONFIG_CMD_BOOTMENU) += efi_bootmenu_maintenance.o
>> +endif
>>
>>   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..e5124a8a21
>> --- /dev/null
>> +++ b/lib/efi_loader/efi_bootmenu_maintenance.c
>> @@ -0,0 +1,906 @@
>> +// 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_selection_data {
>> +	u16 bootorder_index;
>> +	void *load_option;
>> +	int *selected;
>> +};
>> +
>> +struct efi_bootmenu_filepath_info {
>> +	u16 *name;
>> +	struct list_head list;
>> +};
>> +
>> +struct efi_bootmenu_boot_option {
>> +	struct efi_simple_file_system_protocol *current_volume;
>> +	struct efi_device_path *dp_volume;
>> +	u16 *current_path;
>> +	struct list_head filepath_list;
>> +	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),
>> +};
>
> FYI, 'END' is also defined as a static variable in efi_device_path.c
>
>> +
>> +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 drawn (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("     ");

Code size of U-Boot is critical on many board. Please, minimize the
number of function calls. You can set the column number in the printf
statement above.

>> +
>> +	if (reverse)
>> +		puts(ANSI_COLOR_REVERSE);
>> +
>> +	printf("%ls", entry->title);
>> +
>> +	if (reverse)
>> +		puts(ANSI_COLOR_RESET);

The if is not strictly needed. Just add ANSI_COLOR_RESET to the printf
statement above.

>> +}
>> +
>> +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);

Please, minimize the number of function calls. This can be a single
printf() call instread of 14 function invocations.

Best regards

Heinrich

>> +}
>> +
>> +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;
>> +		}
>
> I don't fully understand how bootmenu works with key strokes,
> but I think that this kind of key handling is quite generic
> and put it as a common helper function.
>
>> +	}
>> +
>> +	/* 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_strcmp(info->file_name, u".") == 0 &&
>> +	    u16_strlen(info->file_name) == 1) {
>> +		/* stay current path */
>> +	} else if (u16_strcmp(info->file_name, u"..") == 0 &&
>> +		   u16_strlen(info->file_name) == 2) {
>> +		struct efi_bootmenu_filepath_info *iter;
>> +		struct list_head *pos, *n;
>> +		int is_last;
>> +
>> +		memset(info->bo->current_path, 0, EFI_BOOTMENU_FILE_PATH_BUF_SIZE);
>> +		list_for_each_safe(pos, n, &info->bo->filepath_list) {
>> +			iter = list_entry(pos, struct efi_bootmenu_filepath_info, list);
>> +
>> +			is_last = list_is_last(&iter->list, &info->bo->filepath_list);
>> +			if (is_last) {
>> +				list_del(&iter->list);
>> +				free(iter->name);
>> +				free(iter);
>> +				break;
>> +			}
>> +			u16_strlcat(info->bo->current_path, iter->name,
>> +				    EFI_BOOTMENU_FILE_PATH_MAX);
>> +			u16_strlcat(info->bo->current_path, u"\\",
>> +				    EFI_BOOTMENU_FILE_PATH_MAX);
>> +		}
>> +	} else {
>> +		size_t new_len;
>> +		struct efi_bootmenu_filepath_info *filepath;
>> +
>> +		new_len = u16_strlen(info->bo->current_path) +
>> +				     u16_strlen(info->file_name);
>> +		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);
>> +
>> +		filepath = calloc(1, sizeof(struct efi_bootmenu_filepath_info));
>> +		if (!filepath)
>> +			return EFI_OUT_OF_RESOURCES;
>> +
>> +		filepath->name = u16_strdup(info->file_name);
>> +		if (!filepath->name) {
>> +			free(filepath);
>> +			return EFI_OUT_OF_RESOURCES;
>> +		}
>> +		list_add_tail(&filepath->list, &info->bo->filepath_list);
>> +
>> +		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;
>> +}
>
> I prefer to see a menu like:
>    BOOTxxxx
>    title:
>    file:
>    option(arguments):
>    set:
>    cancel:
> rather than being prompted one by one.
>
> Then users can select any of items in arbitrary order to define them
> or to quit the menu.
>
> This way, the same menu can be re-used to *modify* an existing entry.
>
>> +
>> +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, EFI_BOOTMENU_FILE_PATH_BUF_SIZE);
>> +
>> +		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;
>> +	struct list_head *pos, *n;
>> +	efi_uintn_t dp_size, fp_size;
>> +	struct efi_bootmenu_boot_option bo;
>> +	struct efi_device_path_file_path *fp;
>> +	struct efi_bootmenu_filepath_info *item;
>> +
>> +	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;
>> +
>> +	INIT_LIST_HEAD(&bo.filepath_list);
>> +
>> +	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;
>
> This if statement doesn't make sense.
>
>> +
>> +out:
>> +	free(buf);
>> +	free(bo.boot_name);
>> +	free(bo.current_path);
>> +
>> +	list_for_each_safe(pos, n, &bo.filepath_list) {
>> +		item = list_entry(pos, struct efi_bootmenu_filepath_info, list);
>> +		list_del(&item->list);
>> +		free(item->name);
>> +		free(item);
>> +	}
>> +
>> +	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 60a3fc85ac..cf9622ed1a 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 f5b462fb16..01576c8ed2 100644
>> --- a/lib/efi_loader/efi_disk.c
>> +++ b/lib/efi_loader/efi_disk.c
>> @@ -751,3 +751,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;
>> +}
>
> Please note that not all the BLOCK_IO interfaces have corresponding diskobj's,
> in particular, efi_driver/efi_block_device.c.
>
> -Takahiro Akashi
>
>> 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	[flat|nested] 18+ messages in thread

* Re: [PATCH v6 3/6] efi_loader: menu-driven update of UEFI bootorder variable
  2022-05-24 10:42   ` Heinrich Schuchardt
@ 2022-05-26  7:49     ` Masahisa Kojima
  0 siblings, 0 replies; 18+ messages in thread
From: Masahisa Kojima @ 2022-05-26  7:49 UTC (permalink / raw)
  To: Heinrich Schuchardt
  Cc: Ilias Apalodimas, Simon Glass, Takahiro Akashi, Francois Ozog,
	Mark Kettenis, u-boot

On Tue, 24 May 2022 at 19:42, Heinrich Schuchardt <xypron.glpk@gmx.de> wrote:
>
> On 5/16/22 13:00, Masahisa Kojima wrote:
> > This commit adds the menu-driven update of UEFI bootorder
> > variable.
> >
> > Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
> > ---
> > (no update since v5)
> >
> > 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 96306cd2cc..be67fca95f 100644
> > --- a/lib/efi_loader/efi_bootmenu_maintenance.c
> > +++ b/lib/efi_loader/efi_bootmenu_maintenance.c
> > @@ -746,6 +746,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};
>
> After selecting an item why can we simply use the up and down key to
> move the selected item?

OK, I will use the up and down key, as Takahiro also
requested this before.

>
> > +
> > +     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;
> > @@ -996,6 +1046,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));
>
> After updating an item we should be in the
> efi_bootmenu_show_boot_selection() screen again.

OK.

Thanks,
Masahisa Kojima

>
> Best regards
>
> Heinrich
>
> > +     } 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;
> > @@ -1025,6 +1126,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},
> >   };
> >
>

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

* Re: [PATCH v6 0/6] enable menu-driven boot device selection
  2022-05-16 11:00 [PATCH v6 0/6] enable menu-driven boot device selection Masahisa Kojima
                   ` (5 preceding siblings ...)
  2022-05-16 11:00 ` [PATCH v6 6/6] lib/charset: fix compile warnings Masahisa Kojima
@ 2022-06-04  6:33 ` Heinrich Schuchardt
  2022-06-06  0:18   ` Masahisa Kojima
  6 siblings, 1 reply; 18+ messages in thread
From: Heinrich Schuchardt @ 2022-06-04  6:33 UTC (permalink / raw)
  To: Masahisa Kojima
  Cc: Ilias Apalodimas, Simon Glass, Takahiro Akashi, Francois Ozog,
	Mark Kettenis, u-boot

On 5/16/22 13:00, Masahisa Kojima wrote:
> Most of the patches are merged into u-boot/master, this series
> contains the remaining patches to support menu-driven
> UEFI boot variable maintenance and removable media support.

This series needs to be rebased as we have switched to from utf-16
titles to utf-8 ones.

Best regards

Heinrich

>
> [Major Changes]
> - rebased to v2022.07-rc2
> - there is detailed changelog in each commit
>
> Masahisa Kojima (6):
>    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 description for UEFI boot support
>    lib/charset: fix compile warnings
>
>   cmd/bootmenu.c                            |  167 ++-
>   doc/usage/cmd/bootmenu.rst                |   55 +
>   include/charset.h                         |    2 +-
>   include/efi_loader.h                      |   60 +
>   lib/efi_loader/Makefile                   |    3 +
>   lib/efi_loader/efi_bootmenu_maintenance.c | 1282 +++++++++++++++++++++
>   lib/efi_loader/efi_bootmgr.c              |    4 +
>   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 +-
>   11 files changed, 1737 insertions(+), 55 deletions(-)
>   create mode 100644 lib/efi_loader/efi_bootmenu_maintenance.c
>


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

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

Hi Heinrich,

On Sat, 4 Jun 2022 at 15:33, Heinrich Schuchardt <xypron.glpk@gmx.de> wrote:
>
> On 5/16/22 13:00, Masahisa Kojima wrote:
> > Most of the patches are merged into u-boot/master, this series
> > contains the remaining patches to support menu-driven
> > UEFI boot variable maintenance and removable media support.
>
> This series needs to be rebased as we have switched to from utf-16
> titles to utf-8 ones.

Yes.
Anyway, as Takahiro suggested, I will implement a new U-Boot command
to invoke the UEFI variable maintenance menu.
This new command runs from bootmenu_x.

Thanks,
Masahisa Kojima

>
> Best regards
>
> Heinrich
>
> >
> > [Major Changes]
> > - rebased to v2022.07-rc2
> > - there is detailed changelog in each commit
> >
> > Masahisa Kojima (6):
> >    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 description for UEFI boot support
> >    lib/charset: fix compile warnings
> >
> >   cmd/bootmenu.c                            |  167 ++-
> >   doc/usage/cmd/bootmenu.rst                |   55 +
> >   include/charset.h                         |    2 +-
> >   include/efi_loader.h                      |   60 +
> >   lib/efi_loader/Makefile                   |    3 +
> >   lib/efi_loader/efi_bootmenu_maintenance.c | 1282 +++++++++++++++++++++
> >   lib/efi_loader/efi_bootmgr.c              |    4 +
> >   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 +-
> >   11 files changed, 1737 insertions(+), 55 deletions(-)
> >   create mode 100644 lib/efi_loader/efi_bootmenu_maintenance.c
> >
>

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

* Re: [PATCH v6 1/6] efi_loader: menu-driven addition of UEFI boot option
  2022-05-25  1:38   ` Takahiro Akashi
  2022-05-26  7:37     ` Heinrich Schuchardt
@ 2022-06-06  0:39     ` Masahisa Kojima
  2022-06-13  9:44       ` Masahisa Kojima
  1 sibling, 1 reply; 18+ messages in thread
From: Masahisa Kojima @ 2022-06-06  0:39 UTC (permalink / raw)
  To: Takahiro Akashi, Masahisa Kojima, u-boot, Heinrich Schuchardt,
	Ilias Apalodimas, Simon Glass, Francois Ozog, Mark Kettenis

On Wed, 25 May 2022 at 10:39, Takahiro Akashi
<takahiro.akashi@linaro.org> wrote:
>
> On Mon, May 16, 2022 at 08:00:37PM +0900, 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 v6:
> > - fix typos
> > - modify volume name to match U-Boot syntax
> > - compile in CONFIG_EFI_LOADER=n and CONFIG_CMD_BOOTEFI_BOOTMGR=n
>
> Is this correct?

Sorry for the lack of information. I would like to say that build test in
the following configurations.
- CONFIG_EFI_LOADER=n,  CONFIG_CMD_BOOTEFI_BOOTMGR=n
- CONFIG_EFI_LOADER=y, CONFIG_CMD_BOOTEFI_BOOTMGR=n
>
> > - simplify u16_strncmp() usage
> > - support "a\b.efi" file path, use link list to handle filepath
> > - modify length check condition
> > - UEFI related menu items only appears with CONFIG_AUTOBOOT_MENU_SHOW=y
>
> Why?
> I think that the feature is useful even without AUTOBOOT.
> As you know, efidebug is seen as a debugging tool and is not expected
> to be enabled in production systems.
>
> So the feature you're adding is the only available UI for boot manager.
> What I recommend is
> - to create a boot manager maintenance as a standalone U-Boot command,
> - to add an bootmenu entry for invoking the command

I agree.

>
> > 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                            |  73 +-
> >  include/efi_loader.h                      |  37 +
> >  lib/efi_loader/Makefile                   |   3 +
> >  lib/efi_loader/efi_bootmenu_maintenance.c | 906 ++++++++++++++++++++++
>
> I would say that this file should be moved under /cmd as the code does not
> implement any UEFI specification semantics, but simply provides helper
> functions for bootmenu command.

OK, I will create a new command for UEFI variable maintenance.

>
> Or I recommend that the boot manager be implemented as a standalone command
> (as I insisted serveral times before) and the related maintenance feature
> be invoked as follows:
>    => efibootmanager -i (i for interactive)
>
> >  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, 1184 insertions(+), 54 deletions(-)
> >  create mode 100644 lib/efi_loader/efi_bootmenu_maintenance.c
> >
> > diff --git a/cmd/bootmenu.c b/cmd/bootmenu.c
> > index 8859eebea5..4b846332b0 100644
> > --- a/cmd/bootmenu.c
> > +++ b/cmd/bootmenu.c
> > @@ -19,6 +19,12 @@
> >
> >  /* maximum bootmenu entries */
> >  #define MAX_COUNT    99
> > +#if defined(CONFIG_CMD_BOOTEFI_BOOTMGR) && defined(CONFIG_AUTOBOOT_MENU_SHOW)
> > +#define STATIC_ENTRY 2
> > +#else
> > +#define STATIC_ENTRY 1
> > +#endif
> > +#define MAX_DYNAMIC_ENTRY (MAX_COUNT - STATIC_ENTRY)
> >
> >  /* maximal size of bootmenu env
> >   *  9 = strlen("bootmenu_")
> > @@ -38,10 +44,11 @@ enum boot_type {
> >       BOOTMENU_TYPE_NONE = 0,
> >       BOOTMENU_TYPE_BOOTMENU,
> >       BOOTMENU_TYPE_UEFI_BOOT_OPTION,
> > +     BOOTMENU_TYPE_UEFI_MAINTENANCE,
> >  };
> >
> >  struct bootmenu_entry {
> > -     unsigned short int num;         /* unique number 0 .. MAX_COUNT */
> > +     unsigned short int num;         /* unique number 0 .. (MAX_COUNT - 1) */
> >       char key[3];                    /* key identifier of number */
> >       u16 *title;                     /* title of entry */
> >       char *command;                  /* hush command of entry */
> > @@ -55,7 +62,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);
> > @@ -217,7 +224,7 @@ static int prepare_bootmenu_entry(struct bootmenu_data *menu,
> >               iter = entry;
> >               ++i;
> >
> > -             if (i == MAX_COUNT - 1)
> > +             if (i == MAX_DYNAMIC_ENTRY)
> >                       break;
> >       }
> >
> > @@ -305,7 +312,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;
> >       }
> >
> > @@ -341,14 +348,51 @@ 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) {
> > +     if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR) && IS_ENABLED(CONFIG_AUTOBOOT_MENU_SHOW)) {
> > +             if (i < MAX_DYNAMIC_ENTRY) {
> >                       ret = prepare_uefi_bootorder_entry(menu, &iter, &i);
> >                       if (ret < 0 && ret != -ENOENT)
> >                               goto cleanup;
> >               }
> >       }
> >
> > +     if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR) && IS_ENABLED(CONFIG_AUTOBOOT_MENU_SHOW)) {
> > +             /* 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 'u16' is a matter, why not define the type of 'title' as u8 and you can always use
> utf16_to_utf8() for the setting the value to this field.

OK.

>
> > +                     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));
> > @@ -520,6 +564,14 @@ static enum bootmenu_ret bootmenu_show(int delay)
> >               title = u16_strdup(iter->title);
> >               command = strdup(iter->command);
> >
> > +             if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR)) {
> > +                     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;
> > @@ -610,6 +662,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;
> > @@ -624,7 +677,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 733ee03cd7..49f326e585 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;
> >
> > @@ -314,6 +317,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[];
> > @@ -877,6 +883,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
> >   *
> > @@ -1048,4 +1056,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..807e9a4319 100644
> > --- a/lib/efi_loader/Makefile
> > +++ b/lib/efi_loader/Makefile
> > @@ -77,6 +77,9 @@ 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
> > +ifeq ($(CONFIG_CMD_BOOTEFI_BOOTMGR),y)
> > +obj-$(CONFIG_CMD_BOOTMENU) += efi_bootmenu_maintenance.o
> > +endif
> >
> >  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..e5124a8a21
> > --- /dev/null
> > +++ b/lib/efi_loader/efi_bootmenu_maintenance.c
> > @@ -0,0 +1,906 @@
> > +// 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_selection_data {
> > +     u16 bootorder_index;
> > +     void *load_option;
> > +     int *selected;
> > +};
> > +
> > +struct efi_bootmenu_filepath_info {
> > +     u16 *name;
> > +     struct list_head list;
> > +};
> > +
> > +struct efi_bootmenu_boot_option {
> > +     struct efi_simple_file_system_protocol *current_volume;
> > +     struct efi_device_path *dp_volume;
> > +     u16 *current_path;
> > +     struct list_head filepath_list;
> > +     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),
> > +};
>
> FYI, 'END' is also defined as a static variable in efi_device_path.c

OK, I will move 'END' to the common include file.

>
> > +
> > +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 drawn (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;
> > +             }
>
> I don't fully understand how bootmenu works with key strokes,
> but I think that this kind of key handling is quite generic
> and put it as a common helper function.

I will consider moving this function to common.

>
> > +     }
> > +
> > +     /* 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_strcmp(info->file_name, u".") == 0 &&
> > +         u16_strlen(info->file_name) == 1) {
> > +             /* stay current path */
> > +     } else if (u16_strcmp(info->file_name, u"..") == 0 &&
> > +                u16_strlen(info->file_name) == 2) {
> > +             struct efi_bootmenu_filepath_info *iter;
> > +             struct list_head *pos, *n;
> > +             int is_last;
> > +
> > +             memset(info->bo->current_path, 0, EFI_BOOTMENU_FILE_PATH_BUF_SIZE);
> > +             list_for_each_safe(pos, n, &info->bo->filepath_list) {
> > +                     iter = list_entry(pos, struct efi_bootmenu_filepath_info, list);
> > +
> > +                     is_last = list_is_last(&iter->list, &info->bo->filepath_list);
> > +                     if (is_last) {
> > +                             list_del(&iter->list);
> > +                             free(iter->name);
> > +                             free(iter);
> > +                             break;
> > +                     }
> > +                     u16_strlcat(info->bo->current_path, iter->name,
> > +                                 EFI_BOOTMENU_FILE_PATH_MAX);
> > +                     u16_strlcat(info->bo->current_path, u"\\",
> > +                                 EFI_BOOTMENU_FILE_PATH_MAX);
> > +             }
> > +     } else {
> > +             size_t new_len;
> > +             struct efi_bootmenu_filepath_info *filepath;
> > +
> > +             new_len = u16_strlen(info->bo->current_path) +
> > +                                  u16_strlen(info->file_name);
> > +             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);
> > +
> > +             filepath = calloc(1, sizeof(struct efi_bootmenu_filepath_info));
> > +             if (!filepath)
> > +                     return EFI_OUT_OF_RESOURCES;
> > +
> > +             filepath->name = u16_strdup(info->file_name);
> > +             if (!filepath->name) {
> > +                     free(filepath);
> > +                     return EFI_OUT_OF_RESOURCES;
> > +             }
> > +             list_add_tail(&filepath->list, &info->bo->filepath_list);
> > +
> > +             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;
> > +}
>
> I prefer to see a menu like:
>   BOOTxxxx
>   title:
>   file:
>   option(arguments):
>   set:
>   cancel:
> rather than being prompted one by one.
>
> Then users can select any of items in arbitrary order to define them
> or to quit the menu.
>
> This way, the same menu can be re-used to *modify* an existing entry.

I agree, I will implement like you suggested.

>
> > +
> > +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, EFI_BOOTMENU_FILE_PATH_BUF_SIZE);
> > +
> > +             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;
> > +     struct list_head *pos, *n;
> > +     efi_uintn_t dp_size, fp_size;
> > +     struct efi_bootmenu_boot_option bo;
> > +     struct efi_device_path_file_path *fp;
> > +     struct efi_bootmenu_filepath_info *item;
> > +
> > +     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;
> > +
> > +     INIT_LIST_HEAD(&bo.filepath_list);
> > +
> > +     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;
>
> This if statement doesn't make sense.

OK.

>
> > +
> > +out:
> > +     free(buf);
> > +     free(bo.boot_name);
> > +     free(bo.current_path);
> > +
> > +     list_for_each_safe(pos, n, &bo.filepath_list) {
> > +             item = list_entry(pos, struct efi_bootmenu_filepath_info, list);
> > +             list_del(&item->list);
> > +             free(item->name);
> > +             free(item);
> > +     }
> > +
> > +     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 60a3fc85ac..cf9622ed1a 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 f5b462fb16..01576c8ed2 100644
> > --- a/lib/efi_loader/efi_disk.c
> > +++ b/lib/efi_loader/efi_disk.c
> > @@ -751,3 +751,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;
> > +}
>
> Please note that not all the BLOCK_IO interfaces have corresponding diskobj's,
> in particular, efi_driver/efi_block_device.c.

I will check the case that bootmenu starts EFI application
using efi_driver/efi_block_device.c, then returns to bootmenu.

Thanks,
Masahisa Kojima

>
> -Takahiro Akashi
>
> > 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	[flat|nested] 18+ messages in thread

* Re: [PATCH v6 1/6] efi_loader: menu-driven addition of UEFI boot option
  2022-05-26  7:37     ` Heinrich Schuchardt
@ 2022-06-06  0:45       ` Masahisa Kojima
  0 siblings, 0 replies; 18+ messages in thread
From: Masahisa Kojima @ 2022-06-06  0:45 UTC (permalink / raw)
  To: Heinrich Schuchardt
  Cc: Takahiro Akashi, Mark Kettenis, u-boot, Ilias Apalodimas,
	Simon Glass, Francois Ozog

On Thu, 26 May 2022 at 16:38, Heinrich Schuchardt <xypron.glpk@gmx.de> wrote:
>
> On 5/25/22 03:38, Takahiro Akashi wrote:
> > On Mon, May 16, 2022 at 08:00:37PM +0900, 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 v6:
> >> - fix typos
> >> - modify volume name to match U-Boot syntax
> >> - compile in CONFIG_EFI_LOADER=n and CONFIG_CMD_BOOTEFI_BOOTMGR=n
> >
> > Is this correct?
> >
> >> - simplify u16_strncmp() usage
> >> - support "a\b.efi" file path, use link list to handle filepath
> >> - modify length check condition
> >> - UEFI related menu items only appears with CONFIG_AUTOBOOT_MENU_SHOW=y
> >
> > Why?
> > I think that the feature is useful even without AUTOBOOT.
> > As you know, efidebug is seen as a debugging tool and is not expected
> > to be enabled in production systems.
> >
> > So the feature you're adding is the only available UI for boot manager.
> > What I recommend is
> > - to create a boot manager maintenance as a standalone U-Boot command,
> > - to add an bootmenu entry for invoking the command
> >
> >> 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                            |  73 +-
> >>   include/efi_loader.h                      |  37 +
> >>   lib/efi_loader/Makefile                   |   3 +
> >>   lib/efi_loader/efi_bootmenu_maintenance.c | 906 ++++++++++++++++++++++
> >
> > I would say that this file should be moved under /cmd as the code does not
> > implement any UEFI specification semantics, but simply provides helper
> > functions for bootmenu command.
> >
> > Or I recommend that the boot manager be implemented as a standalone command
> > (as I insisted serveral times before) and the related maintenance feature
> > be invoked as follows:
> >     => efibootmanager -i (i for interactive)
> >
> >>   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, 1184 insertions(+), 54 deletions(-)
> >>   create mode 100644 lib/efi_loader/efi_bootmenu_maintenance.c
> >>
> >> diff --git a/cmd/bootmenu.c b/cmd/bootmenu.c
> >> index 8859eebea5..4b846332b0 100644
> >> --- a/cmd/bootmenu.c
> >> +++ b/cmd/bootmenu.c
> >> @@ -19,6 +19,12 @@
> >>
> >>   /* maximum bootmenu entries */
> >>   #define MAX_COUNT  99
> >> +#if defined(CONFIG_CMD_BOOTEFI_BOOTMGR) && defined(CONFIG_AUTOBOOT_MENU_SHOW)
> >> +#define STATIC_ENTRY 2
> >> +#else
> >> +#define STATIC_ENTRY 1
> >> +#endif
> >> +#define MAX_DYNAMIC_ENTRY (MAX_COUNT - STATIC_ENTRY)
> >>
> >>   /* maximal size of bootmenu env
> >>    *  9 = strlen("bootmenu_")
> >> @@ -38,10 +44,11 @@ enum boot_type {
> >>      BOOTMENU_TYPE_NONE = 0,
> >>      BOOTMENU_TYPE_BOOTMENU,
> >>      BOOTMENU_TYPE_UEFI_BOOT_OPTION,
> >> +    BOOTMENU_TYPE_UEFI_MAINTENANCE,
> >>   };
> >>
> >>   struct bootmenu_entry {
> >> -    unsigned short int num;         /* unique number 0 .. MAX_COUNT */
> >> +    unsigned short int num;         /* unique number 0 .. (MAX_COUNT - 1) */
> >>      char key[3];                    /* key identifier of number */
> >>      u16 *title;                     /* title of entry */
> >>      char *command;                  /* hush command of entry */
> >> @@ -55,7 +62,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);
> >> @@ -217,7 +224,7 @@ static int prepare_bootmenu_entry(struct bootmenu_data *menu,
> >>              iter = entry;
> >>              ++i;
> >>
> >> -            if (i == MAX_COUNT - 1)
> >> +            if (i == MAX_DYNAMIC_ENTRY)
> >>                      break;
> >>      }
> >>
> >> @@ -305,7 +312,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;
> >>      }
> >>
> >> @@ -341,14 +348,51 @@ 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) {
> >> +    if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR) && IS_ENABLED(CONFIG_AUTOBOOT_MENU_SHOW)) {
> >> +            if (i < MAX_DYNAMIC_ENTRY) {
> >>                      ret = prepare_uefi_bootorder_entry(menu, &iter, &i);
> >>                      if (ret < 0 && ret != -ENOENT)
> >>                              goto cleanup;
> >>              }
> >>      }
> >>
> >> +    if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR) && IS_ENABLED(CONFIG_AUTOBOOT_MENU_SHOW)) {
> >> +            /* 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 'u16' is a matter, why not define the type of 'title' as u8 and you can always use
> > utf16_to_utf8() for the setting the value to this field.
> >
> >> +                    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));
> >> @@ -520,6 +564,14 @@ static enum bootmenu_ret bootmenu_show(int delay)
> >>              title = u16_strdup(iter->title);
> >>              command = strdup(iter->command);
> >>
> >> +            if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR)) {
> >> +                    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;
> >> @@ -610,6 +662,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;
> >> @@ -624,7 +677,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 733ee03cd7..49f326e585 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;
> >>
> >> @@ -314,6 +317,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[];
> >> @@ -877,6 +883,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
> >>    *
> >> @@ -1048,4 +1056,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..807e9a4319 100644
> >> --- a/lib/efi_loader/Makefile
> >> +++ b/lib/efi_loader/Makefile
> >> @@ -77,6 +77,9 @@ 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
> >> +ifeq ($(CONFIG_CMD_BOOTEFI_BOOTMGR),y)
> >> +obj-$(CONFIG_CMD_BOOTMENU) += efi_bootmenu_maintenance.o
> >> +endif
> >>
> >>   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..e5124a8a21
> >> --- /dev/null
> >> +++ b/lib/efi_loader/efi_bootmenu_maintenance.c
> >> @@ -0,0 +1,906 @@
> >> +// 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_selection_data {
> >> +    u16 bootorder_index;
> >> +    void *load_option;
> >> +    int *selected;
> >> +};
> >> +
> >> +struct efi_bootmenu_filepath_info {
> >> +    u16 *name;
> >> +    struct list_head list;
> >> +};
> >> +
> >> +struct efi_bootmenu_boot_option {
> >> +    struct efi_simple_file_system_protocol *current_volume;
> >> +    struct efi_device_path *dp_volume;
> >> +    u16 *current_path;
> >> +    struct list_head filepath_list;
> >> +    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),
> >> +};
> >
> > FYI, 'END' is also defined as a static variable in efi_device_path.c
> >
> >> +
> >> +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 drawn (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("     ");
>
> Code size of U-Boot is critical on many board. Please, minimize the
> number of function calls. You can set the column number in the printf
> statement above.

OK.

>
> >> +
> >> +    if (reverse)
> >> +            puts(ANSI_COLOR_REVERSE);
> >> +
> >> +    printf("%ls", entry->title);
> >> +
> >> +    if (reverse)
> >> +            puts(ANSI_COLOR_RESET);
>
> The if is not strictly needed. Just add ANSI_COLOR_RESET to the printf
> statement above.

Without this 'if' statement, ANSI_COLOR_RESET is sent every time
the line is drawn. I prefer to keep this 'if' statement.

>
> >> +}
> >> +
> >> +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);
>
> Please, minimize the number of function calls. This can be a single
> printf() call instread of 14 function invocations.

OK.

Thanks,
Masahisa Kojima

>
> Best regards
>
> Heinrich
>
> >> +}
> >> +
> >> +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;
> >> +            }
> >
> > I don't fully understand how bootmenu works with key strokes,
> > but I think that this kind of key handling is quite generic
> > and put it as a common helper function.
> >
> >> +    }
> >> +
> >> +    /* 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_strcmp(info->file_name, u".") == 0 &&
> >> +        u16_strlen(info->file_name) == 1) {
> >> +            /* stay current path */
> >> +    } else if (u16_strcmp(info->file_name, u"..") == 0 &&
> >> +               u16_strlen(info->file_name) == 2) {
> >> +            struct efi_bootmenu_filepath_info *iter;
> >> +            struct list_head *pos, *n;
> >> +            int is_last;
> >> +
> >> +            memset(info->bo->current_path, 0, EFI_BOOTMENU_FILE_PATH_BUF_SIZE);
> >> +            list_for_each_safe(pos, n, &info->bo->filepath_list) {
> >> +                    iter = list_entry(pos, struct efi_bootmenu_filepath_info, list);
> >> +
> >> +                    is_last = list_is_last(&iter->list, &info->bo->filepath_list);
> >> +                    if (is_last) {
> >> +                            list_del(&iter->list);
> >> +                            free(iter->name);
> >> +                            free(iter);
> >> +                            break;
> >> +                    }
> >> +                    u16_strlcat(info->bo->current_path, iter->name,
> >> +                                EFI_BOOTMENU_FILE_PATH_MAX);
> >> +                    u16_strlcat(info->bo->current_path, u"\\",
> >> +                                EFI_BOOTMENU_FILE_PATH_MAX);
> >> +            }
> >> +    } else {
> >> +            size_t new_len;
> >> +            struct efi_bootmenu_filepath_info *filepath;
> >> +
> >> +            new_len = u16_strlen(info->bo->current_path) +
> >> +                                 u16_strlen(info->file_name);
> >> +            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);
> >> +
> >> +            filepath = calloc(1, sizeof(struct efi_bootmenu_filepath_info));
> >> +            if (!filepath)
> >> +                    return EFI_OUT_OF_RESOURCES;
> >> +
> >> +            filepath->name = u16_strdup(info->file_name);
> >> +            if (!filepath->name) {
> >> +                    free(filepath);
> >> +                    return EFI_OUT_OF_RESOURCES;
> >> +            }
> >> +            list_add_tail(&filepath->list, &info->bo->filepath_list);
> >> +
> >> +            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;
> >> +}
> >
> > I prefer to see a menu like:
> >    BOOTxxxx
> >    title:
> >    file:
> >    option(arguments):
> >    set:
> >    cancel:
> > rather than being prompted one by one.
> >
> > Then users can select any of items in arbitrary order to define them
> > or to quit the menu.
> >
> > This way, the same menu can be re-used to *modify* an existing entry.
> >
> >> +
> >> +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, EFI_BOOTMENU_FILE_PATH_BUF_SIZE);
> >> +
> >> +            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;
> >> +    struct list_head *pos, *n;
> >> +    efi_uintn_t dp_size, fp_size;
> >> +    struct efi_bootmenu_boot_option bo;
> >> +    struct efi_device_path_file_path *fp;
> >> +    struct efi_bootmenu_filepath_info *item;
> >> +
> >> +    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;
> >> +
> >> +    INIT_LIST_HEAD(&bo.filepath_list);
> >> +
> >> +    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;
> >
> > This if statement doesn't make sense.
> >
> >> +
> >> +out:
> >> +    free(buf);
> >> +    free(bo.boot_name);
> >> +    free(bo.current_path);
> >> +
> >> +    list_for_each_safe(pos, n, &bo.filepath_list) {
> >> +            item = list_entry(pos, struct efi_bootmenu_filepath_info, list);
> >> +            list_del(&item->list);
> >> +            free(item->name);
> >> +            free(item);
> >> +    }
> >> +
> >> +    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 60a3fc85ac..cf9622ed1a 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 f5b462fb16..01576c8ed2 100644
> >> --- a/lib/efi_loader/efi_disk.c
> >> +++ b/lib/efi_loader/efi_disk.c
> >> @@ -751,3 +751,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;
> >> +}
> >
> > Please note that not all the BLOCK_IO interfaces have corresponding diskobj's,
> > in particular, efi_driver/efi_block_device.c.
> >
> > -Takahiro Akashi
> >
> >> 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	[flat|nested] 18+ messages in thread

* Re: [PATCH v6 1/6] efi_loader: menu-driven addition of UEFI boot option
  2022-06-06  0:39     ` Masahisa Kojima
@ 2022-06-13  9:44       ` Masahisa Kojima
  0 siblings, 0 replies; 18+ messages in thread
From: Masahisa Kojima @ 2022-06-13  9:44 UTC (permalink / raw)
  To: Takahiro Akashi, Masahisa Kojima, u-boot, Heinrich Schuchardt,
	Ilias Apalodimas, Simon Glass, Francois Ozog, Mark Kettenis

Hi Akashi-san,

On Mon, 6 Jun 2022 at 09:39, Masahisa Kojima <masahisa.kojima@linaro.org> wrote:
>
> On Wed, 25 May 2022 at 10:39, Takahiro Akashi
> <takahiro.akashi@linaro.org> wrote:
> >
> > On Mon, May 16, 2022 at 08:00:37PM +0900, 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 v6:
> > > - fix typos
> > > - modify volume name to match U-Boot syntax
> > > - compile in CONFIG_EFI_LOADER=n and CONFIG_CMD_BOOTEFI_BOOTMGR=n
> >
> > Is this correct?
>
> Sorry for the lack of information. I would like to say that build test in
> the following configurations.
> - CONFIG_EFI_LOADER=n,  CONFIG_CMD_BOOTEFI_BOOTMGR=n
> - CONFIG_EFI_LOADER=y, CONFIG_CMD_BOOTEFI_BOOTMGR=n
> >
> > > - simplify u16_strncmp() usage
> > > - support "a\b.efi" file path, use link list to handle filepath
> > > - modify length check condition
> > > - UEFI related menu items only appears with CONFIG_AUTOBOOT_MENU_SHOW=y
> >
> > Why?
> > I think that the feature is useful even without AUTOBOOT.
> > As you know, efidebug is seen as a debugging tool and is not expected
> > to be enabled in production systems.
> >
> > So the feature you're adding is the only available UI for boot manager.
> > What I recommend is
> > - to create a boot manager maintenance as a standalone U-Boot command,
> > - to add an bootmenu entry for invoking the command
>
> I agree.
>
> >
> > > 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                            |  73 +-
> > >  include/efi_loader.h                      |  37 +
> > >  lib/efi_loader/Makefile                   |   3 +
> > >  lib/efi_loader/efi_bootmenu_maintenance.c | 906 ++++++++++++++++++++++
> >
> > I would say that this file should be moved under /cmd as the code does not
> > implement any UEFI specification semantics, but simply provides helper
> > functions for bootmenu command.
>
> OK, I will create a new command for UEFI variable maintenance.
>
> >
> > Or I recommend that the boot manager be implemented as a standalone command
> > (as I insisted serveral times before) and the related maintenance feature
> > be invoked as follows:
> >    => efibootmanager -i (i for interactive)
> >
> > >  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, 1184 insertions(+), 54 deletions(-)
> > >  create mode 100644 lib/efi_loader/efi_bootmenu_maintenance.c
> > >
> > > diff --git a/cmd/bootmenu.c b/cmd/bootmenu.c
> > > index 8859eebea5..4b846332b0 100644
> > > --- a/cmd/bootmenu.c
> > > +++ b/cmd/bootmenu.c
> > > @@ -19,6 +19,12 @@
> > >
> > >  /* maximum bootmenu entries */
> > >  #define MAX_COUNT    99
> > > +#if defined(CONFIG_CMD_BOOTEFI_BOOTMGR) && defined(CONFIG_AUTOBOOT_MENU_SHOW)
> > > +#define STATIC_ENTRY 2
> > > +#else
> > > +#define STATIC_ENTRY 1
> > > +#endif
> > > +#define MAX_DYNAMIC_ENTRY (MAX_COUNT - STATIC_ENTRY)
> > >
> > >  /* maximal size of bootmenu env
> > >   *  9 = strlen("bootmenu_")
> > > @@ -38,10 +44,11 @@ enum boot_type {
> > >       BOOTMENU_TYPE_NONE = 0,
> > >       BOOTMENU_TYPE_BOOTMENU,
> > >       BOOTMENU_TYPE_UEFI_BOOT_OPTION,
> > > +     BOOTMENU_TYPE_UEFI_MAINTENANCE,
> > >  };
> > >
> > >  struct bootmenu_entry {
> > > -     unsigned short int num;         /* unique number 0 .. MAX_COUNT */
> > > +     unsigned short int num;         /* unique number 0 .. (MAX_COUNT - 1) */
> > >       char key[3];                    /* key identifier of number */
> > >       u16 *title;                     /* title of entry */
> > >       char *command;                  /* hush command of entry */
> > > @@ -55,7 +62,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);
> > > @@ -217,7 +224,7 @@ static int prepare_bootmenu_entry(struct bootmenu_data *menu,
> > >               iter = entry;
> > >               ++i;
> > >
> > > -             if (i == MAX_COUNT - 1)
> > > +             if (i == MAX_DYNAMIC_ENTRY)
> > >                       break;
> > >       }
> > >
> > > @@ -305,7 +312,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;
> > >       }
> > >
> > > @@ -341,14 +348,51 @@ 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) {
> > > +     if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR) && IS_ENABLED(CONFIG_AUTOBOOT_MENU_SHOW)) {
> > > +             if (i < MAX_DYNAMIC_ENTRY) {
> > >                       ret = prepare_uefi_bootorder_entry(menu, &iter, &i);
> > >                       if (ret < 0 && ret != -ENOENT)
> > >                               goto cleanup;
> > >               }
> > >       }
> > >
> > > +     if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR) && IS_ENABLED(CONFIG_AUTOBOOT_MENU_SHOW)) {
> > > +             /* 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 'u16' is a matter, why not define the type of 'title' as u8 and you can always use
> > utf16_to_utf8() for the setting the value to this field.
>
> OK.
>
> >
> > > +                     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));
> > > @@ -520,6 +564,14 @@ static enum bootmenu_ret bootmenu_show(int delay)
> > >               title = u16_strdup(iter->title);
> > >               command = strdup(iter->command);
> > >
> > > +             if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR)) {
> > > +                     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;
> > > @@ -610,6 +662,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;
> > > @@ -624,7 +677,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 733ee03cd7..49f326e585 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;
> > >
> > > @@ -314,6 +317,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[];
> > > @@ -877,6 +883,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
> > >   *
> > > @@ -1048,4 +1056,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..807e9a4319 100644
> > > --- a/lib/efi_loader/Makefile
> > > +++ b/lib/efi_loader/Makefile
> > > @@ -77,6 +77,9 @@ 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
> > > +ifeq ($(CONFIG_CMD_BOOTEFI_BOOTMGR),y)
> > > +obj-$(CONFIG_CMD_BOOTMENU) += efi_bootmenu_maintenance.o
> > > +endif
> > >
> > >  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..e5124a8a21
> > > --- /dev/null
> > > +++ b/lib/efi_loader/efi_bootmenu_maintenance.c
> > > @@ -0,0 +1,906 @@
> > > +// 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_selection_data {
> > > +     u16 bootorder_index;
> > > +     void *load_option;
> > > +     int *selected;
> > > +};
> > > +
> > > +struct efi_bootmenu_filepath_info {
> > > +     u16 *name;
> > > +     struct list_head list;
> > > +};
> > > +
> > > +struct efi_bootmenu_boot_option {
> > > +     struct efi_simple_file_system_protocol *current_volume;
> > > +     struct efi_device_path *dp_volume;
> > > +     u16 *current_path;
> > > +     struct list_head filepath_list;
> > > +     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),
> > > +};
> >
> > FYI, 'END' is also defined as a static variable in efi_device_path.c
>
> OK, I will move 'END' to the common include file.
>
> >
> > > +
> > > +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 drawn (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;
> > > +             }
> >
> > I don't fully understand how bootmenu works with key strokes,
> > but I think that this kind of key handling is quite generic
> > and put it as a common helper function.
>
> I will consider moving this function to common.
>
> >
> > > +     }
> > > +
> > > +     /* 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_strcmp(info->file_name, u".") == 0 &&
> > > +         u16_strlen(info->file_name) == 1) {
> > > +             /* stay current path */
> > > +     } else if (u16_strcmp(info->file_name, u"..") == 0 &&
> > > +                u16_strlen(info->file_name) == 2) {
> > > +             struct efi_bootmenu_filepath_info *iter;
> > > +             struct list_head *pos, *n;
> > > +             int is_last;
> > > +
> > > +             memset(info->bo->current_path, 0, EFI_BOOTMENU_FILE_PATH_BUF_SIZE);
> > > +             list_for_each_safe(pos, n, &info->bo->filepath_list) {
> > > +                     iter = list_entry(pos, struct efi_bootmenu_filepath_info, list);
> > > +
> > > +                     is_last = list_is_last(&iter->list, &info->bo->filepath_list);
> > > +                     if (is_last) {
> > > +                             list_del(&iter->list);
> > > +                             free(iter->name);
> > > +                             free(iter);
> > > +                             break;
> > > +                     }
> > > +                     u16_strlcat(info->bo->current_path, iter->name,
> > > +                                 EFI_BOOTMENU_FILE_PATH_MAX);
> > > +                     u16_strlcat(info->bo->current_path, u"\\",
> > > +                                 EFI_BOOTMENU_FILE_PATH_MAX);
> > > +             }
> > > +     } else {
> > > +             size_t new_len;
> > > +             struct efi_bootmenu_filepath_info *filepath;
> > > +
> > > +             new_len = u16_strlen(info->bo->current_path) +
> > > +                                  u16_strlen(info->file_name);
> > > +             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);
> > > +
> > > +             filepath = calloc(1, sizeof(struct efi_bootmenu_filepath_info));
> > > +             if (!filepath)
> > > +                     return EFI_OUT_OF_RESOURCES;
> > > +
> > > +             filepath->name = u16_strdup(info->file_name);
> > > +             if (!filepath->name) {
> > > +                     free(filepath);
> > > +                     return EFI_OUT_OF_RESOURCES;
> > > +             }
> > > +             list_add_tail(&filepath->list, &info->bo->filepath_list);
> > > +
> > > +             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;
> > > +}
> >
> > I prefer to see a menu like:
> >   BOOTxxxx
> >   title:
> >   file:
> >   option(arguments):
> >   set:
> >   cancel:
> > rather than being prompted one by one.
> >
> > Then users can select any of items in arbitrary order to define them
> > or to quit the menu.
> >
> > This way, the same menu can be re-used to *modify* an existing entry.
>
> I agree, I will implement like you suggested.
>
> >
> > > +
> > > +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, EFI_BOOTMENU_FILE_PATH_BUF_SIZE);
> > > +
> > > +             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;
> > > +     struct list_head *pos, *n;
> > > +     efi_uintn_t dp_size, fp_size;
> > > +     struct efi_bootmenu_boot_option bo;
> > > +     struct efi_device_path_file_path *fp;
> > > +     struct efi_bootmenu_filepath_info *item;
> > > +
> > > +     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;
> > > +
> > > +     INIT_LIST_HEAD(&bo.filepath_list);
> > > +
> > > +     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;
> >
> > This if statement doesn't make sense.
>
> OK.
>
> >
> > > +
> > > +out:
> > > +     free(buf);
> > > +     free(bo.boot_name);
> > > +     free(bo.current_path);
> > > +
> > > +     list_for_each_safe(pos, n, &bo.filepath_list) {
> > > +             item = list_entry(pos, struct efi_bootmenu_filepath_info, list);
> > > +             list_del(&item->list);
> > > +             free(item->name);
> > > +             free(item);
> > > +     }
> > > +
> > > +     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 60a3fc85ac..cf9622ed1a 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 f5b462fb16..01576c8ed2 100644
> > > --- a/lib/efi_loader/efi_disk.c
> > > +++ b/lib/efi_loader/efi_disk.c
> > > @@ -751,3 +751,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;
> > > +}
> >
> > Please note that not all the BLOCK_IO interfaces have corresponding diskobj's,
> > in particular, efi_driver/efi_block_device.c.

When I test with efi_driver/efi_block_device.c,
I could get "efiloader 0:1". So it seems no problem.

Thanks,
Masahisa Kojima

>
> I will check the case that bootmenu starts EFI application
> using efi_driver/efi_block_device.c, then returns to bootmenu.
>
> Thanks,
> Masahisa Kojima
>
> >
> > -Takahiro Akashi
> >
> > > 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	[flat|nested] 18+ messages in thread

end of thread, other threads:[~2022-06-13  9:44 UTC | newest]

Thread overview: 18+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-05-16 11:00 [PATCH v6 0/6] enable menu-driven boot device selection Masahisa Kojima
2022-05-16 11:00 ` [PATCH v6 1/6] efi_loader: menu-driven addition of UEFI boot option Masahisa Kojima
2022-05-25  1:38   ` Takahiro Akashi
2022-05-26  7:37     ` Heinrich Schuchardt
2022-06-06  0:45       ` Masahisa Kojima
2022-06-06  0:39     ` Masahisa Kojima
2022-06-13  9:44       ` Masahisa Kojima
2022-05-16 11:00 ` [PATCH v6 2/6] efi_loader: menu-driven deletion of UEFI boot variable Masahisa Kojima
2022-05-16 11:00 ` [PATCH v6 3/6] efi_loader: menu-driven update of UEFI bootorder variable Masahisa Kojima
2022-05-24 10:42   ` Heinrich Schuchardt
2022-05-26  7:49     ` Masahisa Kojima
2022-05-16 11:00 ` [PATCH v6 4/6] bootmenu: add removable media entries Masahisa Kojima
2022-05-16 11:00 ` [PATCH v6 5/6] doc:bootmenu: add description for UEFI boot support Masahisa Kojima
2022-05-18  1:31   ` Takahiro Akashi
2022-05-18  5:27     ` Masahisa Kojima
2022-05-16 11:00 ` [PATCH v6 6/6] lib/charset: fix compile warnings Masahisa Kojima
2022-06-04  6:33 ` [PATCH v6 0/6] enable menu-driven boot device selection Heinrich Schuchardt
2022-06-06  0:18   ` Masahisa Kojima

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.