[RFC PATCH v3 2/2] bootmenu: add UEFI and disto_boot entries

Masahisa Kojima masahisa.kojima at linaro.org
Tue Mar 8 15:07:45 CET 2022


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

For UEFI, user can select which UEFI "Boot####" option
to execute, call UEFI bootmgr and UEFI boot variable
maintenance menu. UEFI bootmgr entry is required to
correctly handle "BootNext" variable.

For distro_boot, user can select the boot device
included in "boot_targets" u-boot environment variable.

The menu example is as follows.

  *** U-Boot Boot Menu ***

     Boot 1. kernel (bootmenu_0)
     Boot 2. kernel (bootmenu_1)
     Reset board (bootmenu_2)
     debian (BOOT0000)
     ubuntu (BOOT0001)
     UEFI Boot Manager
     usb0
     scsi0
     virtio0
     dhcp
     UEFI Boot Manager Maintenance
     U-Boot console

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

Signed-off-by: Masahisa Kojima <masahisa.kojima at linaro.org>
---
Changes in v3:
- newly created

 cmd/bootmenu.c | 268 +++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 259 insertions(+), 9 deletions(-)

diff --git a/cmd/bootmenu.c b/cmd/bootmenu.c
index 409ef9a848..a8dc50dcaa 100644
--- a/cmd/bootmenu.c
+++ b/cmd/bootmenu.c
@@ -3,9 +3,12 @@
  * (C) Copyright 2011-2013 Pali Rohár <pali at kernel.org>
  */
 
+#include <charset.h>
 #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>
@@ -24,11 +27,20 @@
  */
 #define MAX_ENV_SIZE	(9 + 2 + 1)
 
+enum boot_type {
+	BOOT_TYPE_NONE = 0,
+	BOOT_TYPE_BOOTMENU,
+	BOOT_TYPE_UEFI,
+	BOOT_TYPE_DISTRO_BOOT,
+};
+
 struct bootmenu_entry {
 	unsigned short int num;		/* unique number 0 .. MAX_COUNT */
 	char key[3];			/* key identifier of number */
-	char *title;			/* title of entry */
+	u16 *title;			/* title of entry */
 	char *command;			/* hush command of entry */
+	enum boot_type type;
+	u16 bootorder;
 	struct bootmenu_data *menu;	/* this bootmenu */
 	struct bootmenu_entry *next;	/* next menu entry (num+1) */
 };
@@ -75,7 +87,12 @@ static void bootmenu_print_entry(void *data)
 	if (reverse)
 		puts(ANSI_COLOR_REVERSE);
 
-	puts(entry->title);
+	if (entry->type == BOOT_TYPE_BOOTMENU)
+		printf("%ls (bootmenu_%d)", entry->title, entry->bootorder);
+	else if (entry->type == BOOT_TYPE_UEFI)
+		printf("%ls (BOOT%04X)", entry->title, entry->bootorder);
+	else
+		printf("%ls", entry->title);
 
 	if (reverse)
 		puts(ANSI_COLOR_RESET);
@@ -87,6 +104,10 @@ static void bootmenu_autoboot_loop(struct bootmenu_data *menu,
 	int i, c;
 
 	if (menu->delay > 0) {
+		/* flush input */
+		while (tstc())
+			getchar();
+
 		printf(ANSI_CURSOR_POSITION, menu->count + 5, 1);
 		printf("  Hit any key to stop autoboot: %2d ", menu->delay);
 	}
@@ -300,6 +321,8 @@ static struct bootmenu_data *bootmenu_create(int delay)
 		menu->active = (int)simple_strtol(default_str, NULL, 10);
 
 	while ((option = bootmenu_getoption(i))) {
+		u16 *buf;
+
 		sep = strchr(option, '=');
 		if (!sep) {
 			printf("Invalid bootmenu entry: %s\n", option);
@@ -311,13 +334,13 @@ static struct bootmenu_data *bootmenu_create(int delay)
 			goto cleanup;
 
 		len = sep-option;
-		entry->title = malloc(len + 1);
+		buf = calloc(1, (len + 1) * sizeof(u16));
+		entry->title = buf;
 		if (!entry->title) {
 			free(entry);
 			goto cleanup;
 		}
-		memcpy(entry->title, option, len);
-		entry->title[len] = 0;
+		utf8_utf16_strncpy(&buf, option, len);
 
 		len = strlen(sep + 1);
 		entry->command = malloc(len + 1);
@@ -333,6 +356,190 @@ static struct bootmenu_data *bootmenu_create(int delay)
 
 		entry->num = i;
 		entry->menu = menu;
+		entry->type = BOOT_TYPE_BOOTMENU;
+		entry->bootorder = i;
+		entry->next = NULL;
+
+		if (!iter)
+			menu->first = entry;
+		else
+			iter->next = entry;
+
+		iter = entry;
+		++i;
+
+		if (i == MAX_COUNT - 1)
+			break;
+	}
+
+{
+	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####";
+
+	/* 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);
+		goto cleanup;
+	}
+
+	bootorder = efi_get_var(u"BootOrder", &efi_global_variable_guid, &size);
+	if (!bootorder)
+		goto bootmgr;
+
+	num = size / sizeof(u16);
+	for (j = 0; j < num; j++) {
+		entry = malloc(sizeof(struct bootmenu_entry));
+		if (!entry)
+			goto cleanup;
+
+		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);
+			continue;
+		}
+
+		if (lo.attributes & LOAD_OPTION_ACTIVE) {
+			char *command;
+			int command_size;
+
+			entry->title = u16_strdup(lo.label);
+			if (!entry->title) {
+				free(load_option);
+				free(entry);
+				goto cleanup;
+			}
+			command_size = strlen("bootefi bootindex XXXX") + 1;
+			command = calloc(1, command_size);
+			if (!command) {
+				free(entry->title);
+				free(load_option);
+				free(entry);
+				goto cleanup;
+			}
+			snprintf(command, command_size, "bootefi bootindex %X", bootorder[j]);
+			entry->command = command;
+			sprintf(entry->key, "%d", i);
+			entry->num = i;
+			entry->menu = menu;
+			entry->type = BOOT_TYPE_UEFI;
+			entry->bootorder = bootorder[j];
+			entry->next = NULL;
+
+			if (!iter)
+				menu->first = entry;
+			else
+				iter->next = entry;
+
+			iter = entry;
+			++i;
+		}
+
+		if (i == MAX_COUNT - 1)
+			break;
+	}
+	free(bootorder);
+}
+
+bootmgr:
+	/* Add UEFI Boot Manager entry if available */
+	if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR)) {
+		if (i <= MAX_COUNT - 1) {
+			entry = malloc(sizeof(struct bootmenu_entry));
+			if (!entry)
+				goto cleanup;
+
+			entry->title = u16_strdup(u"UEFI Boot Manager");
+			if (!entry->title) {
+				free(entry);
+				goto cleanup;
+			}
+
+			entry->command = strdup("bootefi bootmgr");
+			if (!entry->command) {
+				free(entry->title);
+				free(entry);
+				goto cleanup;
+			}
+
+			sprintf(entry->key, "%d", i);
+
+			entry->num = i;
+			entry->menu = menu;
+			entry->type = BOOT_TYPE_NONE;
+			entry->next = NULL;
+
+			if (!iter)
+				menu->first = entry;
+			else
+				iter->next = entry;
+
+			iter = entry;
+			++i;
+		}
+	}
+
+{
+	char *p;
+	char *token;
+	char *boot_targets;
+	int len;
+
+	/* list the distro boot "boot_targets" */
+	boot_targets = env_get("boot_targets");
+	if (!boot_targets)
+		goto exit_boot_targets;
+
+	len = strlen(boot_targets);
+	p = calloc(1, len + 1);
+	strncpy(p, boot_targets, len);
+
+	token = strtok(p, " ");
+
+	do {
+		u16 *buf;
+		char *command;
+		int command_size;
+
+		entry = malloc(sizeof(struct bootmenu_entry));
+		if (!entry)
+			goto cleanup;
+
+		len = strlen(token);
+		buf = calloc(1, (len + 1) * sizeof(u16));
+		entry->title = buf;
+		if (!entry->title) {
+			free(entry);
+			goto cleanup;
+		}
+		utf8_utf16_strncpy(&buf, token,len);
+		sprintf(entry->key, "%d", i);
+		entry->num = i;
+		entry->menu = menu;
+
+		command_size = strlen("run bootcmd_") + len + 1;
+		command = calloc(1, command_size);
+		if (!command) {
+			free(entry->title);
+			free(entry);
+			goto cleanup;
+		}
+		snprintf(command, command_size, "run bootcmd_%s", token);
+		entry->command = command;
+		entry->type = BOOT_TYPE_DISTRO_BOOT;
 		entry->next = NULL;
 
 		if (!iter)
@@ -345,6 +552,48 @@ static struct bootmenu_data *bootmenu_create(int delay)
 
 		if (i == MAX_COUNT - 1)
 			break;
+
+		token = strtok(NULL, " ");
+	} while (token);
+
+	free(p);
+}
+
+exit_boot_targets:
+
+	/* Add UEFI Boot Manager Maintenance entry */
+	if (i <= MAX_COUNT - 1) {
+		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("echo TODO: Not implemented");
+		if (!entry->command) {
+			free(entry->title);
+			free(entry);
+			goto cleanup;
+		}
+
+		sprintf(entry->key, "%d", i);
+
+		entry->num = i;
+		entry->menu = menu;
+		entry->type = BOOT_TYPE_NONE;
+		entry->next = NULL;
+
+		if (!iter)
+			menu->first = entry;
+		else
+			iter->next = entry;
+
+		iter = entry;
+		++i;
 	}
 
 	/* Add U-Boot console entry at the end */
@@ -353,7 +602,7 @@ static struct bootmenu_data *bootmenu_create(int delay)
 		if (!entry)
 			goto cleanup;
 
-		entry->title = strdup("U-Boot console");
+		entry->title = u16_strdup(u"U-Boot console");
 		if (!entry->title) {
 			free(entry);
 			goto cleanup;
@@ -370,6 +619,7 @@ static struct bootmenu_data *bootmenu_create(int delay)
 
 		entry->num = i;
 		entry->menu = menu;
+		entry->type = BOOT_TYPE_NONE;
 		entry->next = NULL;
 
 		if (!iter)
@@ -427,7 +677,7 @@ static void bootmenu_show(int delay)
 {
 	int init = 0;
 	void *choice = NULL;
-	char *title = NULL;
+	u16 *title = NULL;
 	char *command = NULL;
 	struct menu *menu;
 	struct bootmenu_data *bootmenu;
@@ -478,7 +728,7 @@ static void bootmenu_show(int delay)
 
 	if (menu_get_choice(menu, &choice)) {
 		iter = choice;
-		title = strdup(iter->title);
+		title = u16_strdup(iter->title);
 		command = strdup(iter->command);
 	}
 
@@ -493,7 +743,7 @@ cleanup:
 	}
 
 	if (title && command) {
-		debug("Starting entry '%s'\n", title);
+		debug("Starting entry '%ls'\n", title);
 		free(title);
 		run_command(command, 0);
 		free(command);
-- 
2.17.1



More information about the U-Boot mailing list