aboutsummaryrefslogtreecommitdiff
path: root/cmd/bootmenu.c
diff options
context:
space:
mode:
authorMasahisa Kojima <masahisa.kojima@linaro.org>2022-04-28 17:09:42 +0900
committerHeinrich Schuchardt <heinrich.schuchardt@canonical.com>2022-05-03 21:39:22 +0200
commitc606c0059f7f81d06ed316e9cd654ffbbad84ad5 (patch)
treecdb582419f6aa5525e922aa8a8dbe99c0d9b2b95 /cmd/bootmenu.c
parenta3d0aa87acbec4b983f54c24a6e7fd8e9e7b9728 (diff)
downloadu-boot-c606c0059f7f81d06ed316e9cd654ffbbad84ad5.zip
u-boot-c606c0059f7f81d06ed316e9cd654ffbbad84ad5.tar.gz
u-boot-c606c0059f7f81d06ed316e9cd654ffbbad84ad5.tar.bz2
bootmenu: add UEFI boot entry into bootmenu
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. *** U-Boot Boot Menu *** UEFI BOOT0000 : debian UEFI BOOT0001 : ubuntu Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
Diffstat (limited to 'cmd/bootmenu.c')
-rw-r--r--cmd/bootmenu.c153
1 files changed, 152 insertions, 1 deletions
diff --git a/cmd/bootmenu.c b/cmd/bootmenu.c
index 4707559..2244a0f 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 {
@@ -358,6 +361,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;
@@ -383,6 +475,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));
@@ -460,6 +560,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;
@@ -469,8 +594,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);
@@ -519,6 +648,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);
@@ -532,7 +682,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);
}