From e6cab77eca8f6556f381c348b0452b526a752ab7 Mon Sep 17 00:00:00 2001 From: Jan Janssen Date: Wed, 20 Oct 2021 12:15:03 +0200 Subject: [PATCH] sd-boot: Add keys to reboot into firmware interface This is useful if the auto-firmware setting has been disabled. The keys used here are based on what the majority of firmware employ in the wild. This also ensures there's a chance for the user to discover this in case they were too slow during POST or simply used the wrong ones. --- man/loader.conf.xml | 5 ++-- man/systemd-boot.xml | 9 +++++++ src/boot/efi/boot.c | 61 +++++++++++++++++++++++++++++--------------- 3 files changed, 53 insertions(+), 22 deletions(-) diff --git a/man/loader.conf.xml b/man/loader.conf.xml index 26e83dfcab8..3a954a3ce94 100644 --- a/man/loader.conf.xml +++ b/man/loader.conf.xml @@ -188,8 +188,9 @@ auto-firmware - Takes a boolean argument. Enable (the default) or disable - the "Reboot into firmware" entry. + A boolean controlling the presence of the "Reboot into firmware" entry + (enabled by default). If this is disabled, the firmware interface may still be reached + by using the f key. diff --git a/man/systemd-boot.xml b/man/systemd-boot.xml index b7b06bd5eb2..3fad947b0d2 100644 --- a/man/systemd-boot.xml +++ b/man/systemd-boot.xml @@ -178,6 +178,15 @@ F1 Show a help screen + + + f + Reboot into firmware interface. + + For compatibility with the keybindings of several firmware implementations this operation + may also be reached with F2, F10, Del and + Esc. + The following keys may be pressed during bootup or in the boot menu to directly boot a specific diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c index ba097bf20ce..729e199fcc4 100644 --- a/src/boot/efi/boot.c +++ b/src/boot/efi/boot.c @@ -543,6 +543,24 @@ static void print_status(Config *config, CHAR16 *loaded_image_path) { } } +static EFI_STATUS reboot_into_firmware(void) { + UINT64 osind = 0; + EFI_STATUS err; + + if (!(get_os_indications_supported() & EFI_OS_INDICATIONS_BOOT_TO_FW_UI)) + return log_error_status_stall(EFI_UNSUPPORTED, L"Reboot to firmware interface not supported."); + + (void) efivar_get_uint64_le(EFI_GLOBAL_GUID, L"OsIndications", &osind); + osind |= EFI_OS_INDICATIONS_BOOT_TO_FW_UI; + + err = efivar_set_uint64_le(EFI_GLOBAL_GUID, L"OsIndications", osind, EFI_VARIABLE_NON_VOLATILE); + if (EFI_ERROR(err)) + return log_error_status_stall(err, L"Error setting OsIndications: %r", err); + + err = RT->ResetSystem(EfiResetCold, EFI_SUCCESS, 0, NULL); + return log_error_status_stall(err, L"Error calling ResetSystem: %r", err); +} + static BOOLEAN menu_run( Config *config, ConfigEntry **chosen_entry, @@ -566,7 +584,7 @@ static BOOLEAN menu_run( UINT32 timeout_efivar_saved = config->timeout_sec_efivar; UINT32 timeout_remain = config->timeout_sec == TIMEOUT_MENU_FORCE ? 0 : config->timeout_sec; INT16 idx; - BOOLEAN exit = FALSE, run = TRUE; + BOOLEAN exit = FALSE, run = TRUE, firmware_setup = FALSE; INT64 console_mode_initial = ST->ConOut->Mode->Mode, console_mode_efivar_saved = config->console_mode_efivar; graphics_mode(FALSE); @@ -738,6 +756,13 @@ static BOOLEAN menu_run( idx_highlight_prev = idx_highlight; + if (firmware_setup) { + firmware_setup = FALSE; + if (key == KEYPRESS(0, 0, CHAR_CARRIAGE_RETURN)) + reboot_into_firmware(); + continue; + } + switch (key) { case KEYPRESS(0, SCAN_UP, 0): case KEYPRESS(0, 0, 'k'): @@ -794,7 +819,7 @@ static BOOLEAN menu_run( case KEYPRESS(0, 0, 'h'): case KEYPRESS(0, 0, 'H'): case KEYPRESS(0, 0, '?'): - /* This must stay below 80 characters! Q/v/Ctrl+l deliberately not advertised. */ + /* This must stay below 80 characters! Q/v/Ctrl+l/f deliberately not advertised. */ status = StrDuplicate(L"(d)efault (t/T)timeout (e)dit (r/R)resolution (p)rint (h)elp"); break; @@ -888,6 +913,20 @@ static BOOLEAN menu_run( new_mode = TRUE; break; + case KEYPRESS(0, 0, 'f'): + case KEYPRESS(0, 0, 'F'): + case KEYPRESS(0, SCAN_F2, 0): /* Most vendors. */ + case KEYPRESS(0, SCAN_F10, 0): /* HP and Lenovo. */ + case KEYPRESS(0, SCAN_DELETE, 0): /* Same as F2. */ + case KEYPRESS(0, SCAN_ESC, 0): /* HP. */ + if (get_os_indications_supported() & EFI_OS_INDICATIONS_BOOT_TO_FW_UI) { + firmware_setup = TRUE; + /* Let's make sure the user really wants to do this. */ + status = PoolPrint(L"Press Enter to reboot into firmware interface."); + } else + status = PoolPrint(L"Reboot into firmware interface not supported."); + break; + default: /* jump with a hotkey directly to a matching entry */ idx = entry_lookup_key(config, idx_highlight+1, KEYCHAR(key)); @@ -2180,24 +2219,6 @@ out_unload: return err; } -static EFI_STATUS reboot_into_firmware(void) { - UINT64 old, new; - EFI_STATUS err; - - new = EFI_OS_INDICATIONS_BOOT_TO_FW_UI; - - err = efivar_get_uint64_le(EFI_GLOBAL_GUID, L"OsIndications", &old); - if (!EFI_ERROR(err)) - new |= old; - - err = efivar_set_uint64_le(EFI_GLOBAL_GUID, L"OsIndications", new, EFI_VARIABLE_NON_VOLATILE); - if (EFI_ERROR(err)) - return err; - - err = RT->ResetSystem(EfiResetCold, EFI_SUCCESS, 0, NULL); - return log_error_status_stall(err, L"Error calling ResetSystem: %r", err); -} - static void config_free(Config *config) { assert(config); for (UINTN i = 0; i < config->entry_count; i++)