
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@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@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);