[PATCH v5 09/17] bootmenu: add UEFI boot entry into bootmenu

Heinrich Schuchardt xypron.glpk at gmx.de
Sun May 1 23:44:31 CEST 2022


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

This prefix provides no value.

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

Please, remove this hunk.

Best regards

Heinrich

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



More information about the U-Boot mailing list