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.
This commit is contained in:
Jan Janssen 2021-10-20 12:15:03 +02:00 committed by Lennart Poettering
parent 784c249f41
commit e6cab77eca
3 changed files with 53 additions and 22 deletions

View file

@ -188,8 +188,9 @@
<varlistentry>
<term>auto-firmware</term>
<listitem><para>Takes a boolean argument. Enable (the default) or disable
the "Reboot into firmware" entry.</para></listitem>
<listitem><para>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 <keycap>f</keycap> key.</para></listitem>
</varlistentry>
<varlistentry>

View file

@ -178,6 +178,15 @@
<term><keycap>F1</keycap></term>
<listitem><para>Show a help screen</para></listitem>
</varlistentry>
<varlistentry>
<term><keycap>f</keycap></term>
<listitem><para>Reboot into firmware interface.</para>
<para>For compatibility with the keybindings of several firmware implementations this operation
may also be reached with <keycap>F2</keycap>, <keycap>F10</keycap>, <keycap>Del</keycap> and
<keycap>Esc</keycap>.</para></listitem>
</varlistentry>
</variablelist>
<para>The following keys may be pressed during bootup or in the boot menu to directly boot a specific

View file

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