[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