Merge pull request #29441 from evelikov/no-input-delay
sd-boot: add "menu-disabled" option to "timeout", disabling the 100ms input polling
This commit is contained in:
@@ -31,11 +31,13 @@ variables. All EFI variables use the vendor UUID
|
||||
* The EFI variable `LoaderConfigTimeout` contains the boot menu timeout
|
||||
currently in use. It may be modified both by the boot loader and by the
|
||||
host. The value should be formatted as numeric, NUL-terminated, decimal
|
||||
string, in UTF-16. The time is specified in seconds. A value of `menu-force`
|
||||
string, in UTF-16. The time is specified in seconds. In addition some
|
||||
non-numeric string values are also accepted. A value of `menu-force`
|
||||
will disable the timeout and show the menu indefinitely. If set to `0` or
|
||||
`menu-hidden` the default entry is booted immediately without showing a menu.
|
||||
The boot loader should provide a way to interrupt this by for example
|
||||
listening for key presses for a brief moment before booting.
|
||||
Unless a value of `menu-disabled` is set, the boot loader should provide a
|
||||
way to interrupt this by for example listening for key presses for a brief
|
||||
moment before booting.
|
||||
|
||||
* Similarly, the EFI variable `LoaderConfigTimeoutOneShot` contains a boot menu
|
||||
timeout for a single following boot. It is set by the OS in order to request
|
||||
@@ -79,6 +81,7 @@ variables. All EFI variables use the vendor UUID
|
||||
* `1 << 4` → The boot loader supports boot counting as described in [Automatic Boot Assessment](AUTOMATIC_BOOT_ASSESSMENT.md).
|
||||
* `1 << 5` → The boot loader supports looking for boot menu entries in the Extended Boot Loader Partition.
|
||||
* `1 << 6` → The boot loader supports passing a random seed to the OS.
|
||||
* `1 << 13` → The boot loader honours `menu-disabled` option when set.
|
||||
|
||||
* The EFI variable `LoaderSystemToken` contains binary random data,
|
||||
persistently set by the OS installer. Boot loaders that support passing
|
||||
|
||||
+5
-4
@@ -177,10 +177,10 @@
|
||||
<citerefentry><refentrytitle>systemd.time</refentrytitle><manvolnum>7</manvolnum></citerefentry>
|
||||
for details about the syntax of time spans.</para>
|
||||
|
||||
<para>If this is set to <option>menu-hidden</option> or <option>0</option> no menu is shown and
|
||||
the default entry will be booted immediately, while setting this to <option>menu-force</option>
|
||||
disables the timeout while always showing the menu. When an empty string ("") is specified the
|
||||
bootloader will revert to its default menu timeout.</para>
|
||||
<para>If this is set to <option>menu-disabled</option> or <option>menu-hidden</option> or
|
||||
<option>0</option>, no menu is shown and the default entry will be booted immediately, while
|
||||
setting this to <option>menu-force</option> disables the timeout while always showing the menu.
|
||||
When an empty string ("") is specified the bootloader will revert to its default menu timeout.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v250"/></listitem>
|
||||
</varlistentry>
|
||||
@@ -555,6 +555,7 @@ Current Boot Loader: ← details about sd-boot or anothe
|
||||
✓ Support for passing random seed to OS
|
||||
✓ Load drop-in drivers
|
||||
✓ Boot loader sets ESP information
|
||||
✓ Menu can be disabled
|
||||
ESP: /dev/disk/by-partuuid/01234567-89ab-cdef-dead-beef00000000
|
||||
File: └─/EFI/systemd/systemd-bootx64.efi
|
||||
|
||||
|
||||
+3
-2
@@ -138,8 +138,9 @@
|
||||
will be stored as an EFI variable in that case, overriding this option.
|
||||
</para>
|
||||
|
||||
<para>If set to <literal>menu-hidden</literal> or <literal>0</literal> (the default) no menu
|
||||
is shown and the default entry will be booted immediately. The menu can be shown
|
||||
<para>If set to <literal>menu-disabled</literal> or <literal>menu-hidden</literal> or <literal>0</literal>
|
||||
(the default), no menu is shown and the default entry will be booted immediately. Unless
|
||||
<literal>menu-disabled</literal> is used, the menu can be shown
|
||||
by pressing and holding a key before systemd-boot is launched. Setting this to
|
||||
<literal>menu-force</literal> disables the timeout while always showing the menu.</para>
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "bootctl.h"
|
||||
#include "bootctl-set-efivar.h"
|
||||
#include "efivars.h"
|
||||
#include "efi-loader.h"
|
||||
#include "stdio-util.h"
|
||||
#include "utf8.h"
|
||||
#include "virt.h"
|
||||
@@ -14,17 +15,35 @@ static int parse_timeout(const char *arg1, char16_t **ret_timeout, size_t *ret_t
|
||||
char utf8[DECIMAL_STR_MAX(usec_t)];
|
||||
char16_t *encoded;
|
||||
usec_t timeout;
|
||||
bool menu_disabled = false;
|
||||
int r;
|
||||
|
||||
assert(arg1);
|
||||
assert(ret_timeout);
|
||||
assert(ret_timeout_size);
|
||||
|
||||
assert_cc(STRLEN("menu-disabled") < ELEMENTSOF(utf8));
|
||||
|
||||
/* Note: Since there is no way to query if the booloader supports the string tokens, we explicitly
|
||||
* set their numerical value(s) instead. This means that some of the sd-boot internal ABI has leaked
|
||||
* although the ship has sailed and the side-effects are self-contained.
|
||||
*/
|
||||
if (streq(arg1, "menu-force"))
|
||||
timeout = USEC_INFINITY;
|
||||
else if (streq(arg1, "menu-hidden"))
|
||||
timeout = 0;
|
||||
else {
|
||||
else if (streq(arg1, "menu-disabled")) {
|
||||
uint64_t loader_features = 0;
|
||||
|
||||
(void) efi_loader_get_features(&loader_features);
|
||||
if (!(loader_features & EFI_LOADER_FEATURE_MENU_DISABLE)) {
|
||||
if (!arg_graceful)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Loader does not support 'menu-disabled': %m");
|
||||
|
||||
log_warning("Loader does not support 'menu-disabled', setting anyway.");
|
||||
}
|
||||
menu_disabled = true;
|
||||
} else {
|
||||
r = parse_time(arg1, &timeout, USEC_PER_SEC);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse timeout '%s': %m", arg1);
|
||||
@@ -32,7 +51,10 @@ static int parse_timeout(const char *arg1, char16_t **ret_timeout, size_t *ret_t
|
||||
log_warning("Timeout is too long and will be treated as 'menu-force' instead.");
|
||||
}
|
||||
|
||||
xsprintf(utf8, USEC_FMT, MIN(timeout / USEC_PER_SEC, UINT32_MAX));
|
||||
if (menu_disabled)
|
||||
xsprintf(utf8, "menu-disabled");
|
||||
else
|
||||
xsprintf(utf8, USEC_FMT, MIN(timeout / USEC_PER_SEC, UINT32_MAX));
|
||||
|
||||
encoded = utf8_to_utf16(utf8, SIZE_MAX);
|
||||
if (!encoded)
|
||||
|
||||
@@ -372,6 +372,7 @@ int verb_status(int argc, char *argv[], void *userdata) {
|
||||
{ EFI_LOADER_FEATURE_DEVICETREE, "Support Type #1 devicetree field" },
|
||||
{ EFI_LOADER_FEATURE_SECUREBOOT_ENROLL, "Enroll SecureBoot keys" },
|
||||
{ EFI_LOADER_FEATURE_RETAIN_SHIM, "Retain SHIM protocols" },
|
||||
{ EFI_LOADER_FEATURE_MENU_DISABLE, "Menu can be disabled" },
|
||||
};
|
||||
static const struct {
|
||||
uint64_t flag;
|
||||
|
||||
+49
-21
@@ -79,9 +79,9 @@ typedef struct {
|
||||
size_t n_entries;
|
||||
size_t idx_default;
|
||||
size_t idx_default_efivar;
|
||||
uint32_t timeout_sec; /* Actual timeout used (efi_main() override > efivar > config). */
|
||||
uint32_t timeout_sec_config;
|
||||
uint32_t timeout_sec_efivar;
|
||||
uint64_t timeout_sec; /* Actual timeout used (efi_main() override > efivar > config). */
|
||||
uint64_t timeout_sec_config;
|
||||
uint64_t timeout_sec_efivar;
|
||||
char16_t *entry_default_config;
|
||||
char16_t *entry_default_efivar;
|
||||
char16_t *entry_oneshot;
|
||||
@@ -101,16 +101,27 @@ typedef struct {
|
||||
int64_t console_mode_efivar;
|
||||
} Config;
|
||||
|
||||
/* These values have been chosen so that the transitions the user sees could
|
||||
* employ unsigned over-/underflow like this:
|
||||
* efivar unset ↔ force menu ↔ no timeout/skip menu ↔ 1 s ↔ 2 s ↔ … */
|
||||
/* These values have been chosen so that the transitions the user sees could employ unsigned over-/underflow
|
||||
* like this:
|
||||
* efivar unset ↔ force menu ↔ no timeout/skip menu ↔ 1 s ↔ 2 s ↔ …
|
||||
*
|
||||
* Note: all the values below are ABI, so they are not allowed to change. The bootctl tool sets the numerical
|
||||
* value of TIMEOUT_MENU_FORCE and TIMEOUT_MENU_HIDDEN, instead of the string for compatibility reasons.
|
||||
*
|
||||
* The other values may be set by systemd-boot itself and changing those will lead to functional regression
|
||||
* when new version of systemd-boot is installed.
|
||||
*
|
||||
* All the 64bit values are not ABI and will never be written to an efi variable.
|
||||
*/
|
||||
enum {
|
||||
TIMEOUT_MIN = 1,
|
||||
TIMEOUT_MAX = UINT32_MAX - 2U,
|
||||
TIMEOUT_UNSET = UINT32_MAX - 1U,
|
||||
TIMEOUT_MENU_FORCE = UINT32_MAX,
|
||||
TIMEOUT_MENU_HIDDEN = 0,
|
||||
TIMEOUT_TYPE_MAX = UINT32_MAX,
|
||||
TIMEOUT_MIN = 1,
|
||||
TIMEOUT_MAX = UINT32_MAX - 2U,
|
||||
TIMEOUT_UNSET = UINT32_MAX - 1U,
|
||||
TIMEOUT_MENU_FORCE = UINT32_MAX,
|
||||
TIMEOUT_MENU_HIDDEN = 0,
|
||||
TIMEOUT_TYPE_MAX = UINT32_MAX,
|
||||
TIMEOUT_MENU_DISABLED = (uint64_t)UINT32_MAX + 1U,
|
||||
TIMEOUT_TYPE_MAX64 = UINT64_MAX,
|
||||
};
|
||||
|
||||
enum {
|
||||
@@ -403,6 +414,9 @@ static char16_t* update_timeout_efivar(Config *config, bool inc) {
|
||||
case TIMEOUT_UNSET:
|
||||
config->timeout_sec = inc ? TIMEOUT_MENU_FORCE : TIMEOUT_UNSET;
|
||||
break;
|
||||
case TIMEOUT_MENU_DISABLED:
|
||||
config->timeout_sec = inc ? TIMEOUT_MIN : TIMEOUT_MENU_FORCE;
|
||||
break;
|
||||
case TIMEOUT_MENU_FORCE:
|
||||
config->timeout_sec = inc ? TIMEOUT_MENU_HIDDEN : TIMEOUT_MENU_FORCE;
|
||||
break;
|
||||
@@ -418,12 +432,14 @@ static char16_t* update_timeout_efivar(Config *config, bool inc) {
|
||||
switch (config->timeout_sec) {
|
||||
case TIMEOUT_UNSET:
|
||||
return xstrdup16(u"Menu timeout defined by configuration file.");
|
||||
case TIMEOUT_MENU_DISABLED:
|
||||
assert_not_reached();
|
||||
case TIMEOUT_MENU_FORCE:
|
||||
return xstrdup16(u"Timeout disabled, menu will always be shown.");
|
||||
case TIMEOUT_MENU_HIDDEN:
|
||||
return xstrdup16(u"Menu disabled. Hold down key at bootup to show menu.");
|
||||
return xstrdup16(u"Menu hidden. Hold down key at bootup to show menu.");
|
||||
default:
|
||||
return xasprintf("Menu timeout set to %u s.", config->timeout_sec_efivar);
|
||||
return xasprintf("Menu timeout set to %u s.", (uint32_t)config->timeout_sec_efivar);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -447,16 +463,18 @@ static bool ps_continue(void) {
|
||||
!IN_SET(key, KEYPRESS(0, SCAN_ESC, 0), KEYPRESS(0, 0, 'q'), KEYPRESS(0, 0, 'Q'));
|
||||
}
|
||||
|
||||
static void print_timeout_status(const char *label, uint32_t t) {
|
||||
static void print_timeout_status(const char *label, uint64_t t) {
|
||||
switch (t) {
|
||||
case TIMEOUT_UNSET:
|
||||
return;
|
||||
case TIMEOUT_MENU_DISABLED:
|
||||
return (void) printf("%s: menu-disabled\n", label);
|
||||
case TIMEOUT_MENU_FORCE:
|
||||
return (void) printf("%s: menu-force\n", label);
|
||||
case TIMEOUT_MENU_HIDDEN:
|
||||
return (void) printf("%s: menu-hidden\n", label);
|
||||
default:
|
||||
return (void) printf("%s: %u s\n", label, t);
|
||||
return (void) printf("%s: %u s\n", label, (uint32_t)t);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -651,7 +669,7 @@ static bool menu_run(
|
||||
size_t x_start = 0, y_start = 0, y_status = 0, x_max, y_max;
|
||||
_cleanup_(strv_freep) char16_t **lines = NULL;
|
||||
_cleanup_free_ char16_t *clearline = NULL, *separator = NULL, *status = NULL;
|
||||
uint32_t timeout_efivar_saved = config->timeout_sec_efivar;
|
||||
uint64_t timeout_efivar_saved = config->timeout_sec_efivar;
|
||||
uint32_t timeout_remain = config->timeout_sec == TIMEOUT_MENU_FORCE ? 0 : config->timeout_sec;
|
||||
int64_t console_mode_initial = ST->ConOut->Mode->Mode, console_mode_efivar_saved = config->console_mode_efivar;
|
||||
size_t default_efivar_saved = config->idx_default_efivar;
|
||||
@@ -1097,6 +1115,8 @@ static bool menu_run(
|
||||
case TIMEOUT_UNSET:
|
||||
efivar_unset(MAKE_GUID_PTR(LOADER), u"LoaderConfigTimeout", EFI_VARIABLE_NON_VOLATILE);
|
||||
break;
|
||||
case TIMEOUT_MENU_DISABLED:
|
||||
assert_not_reached();
|
||||
case TIMEOUT_MENU_FORCE:
|
||||
efivar_set(MAKE_GUID_PTR(LOADER), u"LoaderConfigTimeout", u"menu-force", EFI_VARIABLE_NON_VOLATILE);
|
||||
break;
|
||||
@@ -1104,6 +1124,7 @@ static bool menu_run(
|
||||
efivar_set(MAKE_GUID_PTR(LOADER), u"LoaderConfigTimeout", u"menu-hidden", EFI_VARIABLE_NON_VOLATILE);
|
||||
break;
|
||||
default:
|
||||
assert(config->timeout_sec_efivar < UINT32_MAX);
|
||||
efivar_set_uint_string(MAKE_GUID_PTR(LOADER), u"LoaderConfigTimeout",
|
||||
config->timeout_sec_efivar, EFI_VARIABLE_NON_VOLATILE);
|
||||
}
|
||||
@@ -1176,7 +1197,9 @@ static void config_defaults_load_from_file(Config *config, char *content) {
|
||||
|
||||
while ((line = line_get_key_value(content, " \t", &pos, &key, &value)))
|
||||
if (streq8(key, "timeout")) {
|
||||
if (streq8( value, "menu-force"))
|
||||
if (streq8(value, "menu-disabled"))
|
||||
config->timeout_sec_config = TIMEOUT_MENU_DISABLED;
|
||||
else if (streq8(value, "menu-force"))
|
||||
config->timeout_sec_config = TIMEOUT_MENU_FORCE;
|
||||
else if (streq8(value, "menu-hidden"))
|
||||
config->timeout_sec_config = TIMEOUT_MENU_HIDDEN;
|
||||
@@ -1492,7 +1515,7 @@ static void boot_entry_add_type1(
|
||||
TAKE_PTR(entry);
|
||||
}
|
||||
|
||||
static EFI_STATUS efivar_get_timeout(const char16_t *var, uint32_t *ret_value) {
|
||||
static EFI_STATUS efivar_get_timeout(const char16_t *var, uint64_t *ret_value) {
|
||||
_cleanup_free_ char16_t *value = NULL;
|
||||
EFI_STATUS err;
|
||||
|
||||
@@ -1503,6 +1526,10 @@ static EFI_STATUS efivar_get_timeout(const char16_t *var, uint32_t *ret_value) {
|
||||
if (err != EFI_SUCCESS)
|
||||
return err;
|
||||
|
||||
if (streq16(value, u"menu-disabled")) {
|
||||
*ret_value = TIMEOUT_MENU_DISABLED;
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
if (streq16(value, u"menu-force")) {
|
||||
*ret_value = TIMEOUT_MENU_FORCE;
|
||||
return EFI_SUCCESS;
|
||||
@@ -2503,6 +2530,7 @@ static void export_variables(
|
||||
EFI_LOADER_FEATURE_DEVICETREE |
|
||||
EFI_LOADER_FEATURE_SECUREBOOT_ENROLL |
|
||||
EFI_LOADER_FEATURE_RETAIN_SHIM |
|
||||
EFI_LOADER_FEATURE_MENU_DISABLE |
|
||||
0;
|
||||
|
||||
_cleanup_free_ char16_t *infostr = NULL, *typestr = NULL;
|
||||
@@ -2659,9 +2687,9 @@ static EFI_STATUS run(EFI_HANDLE image) {
|
||||
"No loader found. Configuration files in \\loader\\entries\\*.conf are needed.");
|
||||
|
||||
/* select entry or show menu when key is pressed or timeout is set */
|
||||
if (config.force_menu || config.timeout_sec > 0)
|
||||
if (config.force_menu || !IN_SET(config.timeout_sec, TIMEOUT_MENU_HIDDEN, TIMEOUT_MENU_DISABLED))
|
||||
menu = true;
|
||||
else {
|
||||
else if (config.timeout_sec != TIMEOUT_MENU_DISABLED) {
|
||||
uint64_t key;
|
||||
|
||||
/* Block up to 100ms to give firmware time to get input working. */
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#define EFI_LOADER_FEATURE_DEVICETREE (UINT64_C(1) << 10)
|
||||
#define EFI_LOADER_FEATURE_SECUREBOOT_ENROLL (UINT64_C(1) << 11)
|
||||
#define EFI_LOADER_FEATURE_RETAIN_SHIM (UINT64_C(1) << 12)
|
||||
#define EFI_LOADER_FEATURE_MENU_DISABLE (UINT64_C(1) << 13)
|
||||
|
||||
/* Features of the stub, i.e. systemd-stub */
|
||||
#define EFI_STUB_FEATURE_REPORT_BOOT_PARTITION (UINT64_C(1) << 0)
|
||||
|
||||
Reference in New Issue
Block a user