From 3f958812fd996f4c2661dfb07c526714b403ff3b Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Wed, 14 Jan 2026 13:09:10 +0100 Subject: [PATCH] sd-boot: allow setting the `timeout` via SMBIOS 11 Allow configuring the menu timeout used by sd-boot by setting `io.systemd.boot.timeout=` as SMBIOS type 11 string. This takes precedence over the setting from the config file and the EFI var. Useful for e.g. integration tests that want to control that a generic image boots faster. --- man/smbios-type-11.xml | 10 +++++++ src/boot/boot.c | 60 +++++++++++++++++++++++++++++++----------- 2 files changed, 55 insertions(+), 15 deletions(-) diff --git a/man/smbios-type-11.xml b/man/smbios-type-11.xml index 70f66fe0f61..95754333a88 100644 --- a/man/smbios-type-11.xml +++ b/man/smbios-type-11.xml @@ -94,6 +94,16 @@ + + + io.systemd.boot.timeout=TIMEOUT + + This allows configuration of the menu timeout in seconds, and is read by systemd-boot. + For details see + systemd-boot7. + + + diff --git a/src/boot/boot.c b/src/boot/boot.c index b8a9264a496..23aa36b3d16 100644 --- a/src/boot/boot.c +++ b/src/boot/boot.c @@ -128,7 +128,8 @@ typedef struct { size_t n_entries; size_t idx_default; size_t idx_default_efivar; - uint64_t timeout_sec; /* Actual timeout used (efi_main() override > efivar > config). */ + uint64_t timeout_sec; /* Actual timeout used (efi_main() override > smbios > efivar > config). */ + uint64_t timeout_sec_smbios; uint64_t timeout_sec_config; uint64_t timeout_sec_efivar; char16_t *entry_default_config; @@ -323,6 +324,7 @@ static void print_status(Config *config, char16_t *loaded_image_path) { print_timeout_status(" timeout (config)", config->timeout_sec_config); print_timeout_status(" timeout (EFI var)", config->timeout_sec_efivar); + print_timeout_status(" timeout (smbios)", config->timeout_sec_smbios); if (config->entry_default_config) printf(" default (config): %ls\n", config->entry_default_config); @@ -1006,6 +1008,41 @@ static BootEntry* boot_entry_free(BootEntry *entry) { DEFINE_TRIVIAL_CLEANUP_FUNC(BootEntry *, boot_entry_free); +static EFI_STATUS config_timeout_sec_from_string(const char *value, uint64_t *dst) { + if (streq8(value, "menu-disabled")) + *dst = TIMEOUT_MENU_DISABLED; + else if (streq8(value, "menu-force")) + *dst = TIMEOUT_MENU_DISABLED; + else if (streq8(value, "menu-hidden")) + *dst = TIMEOUT_MENU_DISABLED; + else { + uint64_t u; + if (!parse_number8(value, &u, NULL) || u > TIMEOUT_TYPE_MAX) + return EFI_INVALID_PARAMETER; + *dst = u; + } + return EFI_SUCCESS; +} + +static void config_timeout_load_from_smbios(Config *config) { + EFI_STATUS err; + + if (is_confidential_vm()) + return; /* Don't consume SMBIOS in Confidential Computing contexts */ + + const char *value = smbios_find_oem_string("io.systemd.boot.timeout=", /* after= */ NULL); + if (!value) + return; + + err = config_timeout_sec_from_string(value, &config->timeout_sec_smbios); + if (err != EFI_SUCCESS) { + log_warning_status(err, "Error parsing 'timeout' smbios option, ignoring: %s", + value); + return; + } + config->timeout_sec = config->timeout_sec_smbios; +} + static void config_defaults_load_from_file(Config *config, char *content) { char *line; size_t pos = 0; @@ -1018,20 +1055,11 @@ static void config_defaults_load_from_file(Config *config, char *content) { * shared/bootspec.c@boot_loader_read_conf() to make parsing by bootctl/logind/etc. work. */ while ((line = line_get_key_value(content, " \t", &pos, &key, &value))) if (streq8(key, "timeout")) { - 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; - else { - uint64_t u; - if (!parse_number8(value, &u, NULL) || u > TIMEOUT_TYPE_MAX) { - log_error("Error parsing 'timeout' config option, ignoring: %s", - value); - continue; - } - config->timeout_sec_config = u; + EFI_STATUS err = config_timeout_sec_from_string(value, &config->timeout_sec_config); + if (err != EFI_SUCCESS) { + log_warning_status(err, "Error parsing 'timeout' config option, ignoring: %s", + value); + continue; } config->timeout_sec = config->timeout_sec_config; @@ -1496,6 +1524,7 @@ static void config_load_defaults(Config *config, EFI_FILE *root_dir) { .console_mode_efivar = CONSOLE_MODE_KEEP, .timeout_sec_config = TIMEOUT_UNSET, .timeout_sec_efivar = TIMEOUT_UNSET, + .timeout_sec_smbios = TIMEOUT_UNSET, }; err = file_read(root_dir, u"\\loader\\loader.conf", 0, 0, &content, &content_size); @@ -1520,6 +1549,7 @@ static void config_load_defaults(Config *config, EFI_FILE *root_dir) { config->timeout_sec = config->timeout_sec_efivar; else if (err != EFI_NOT_FOUND) log_warning_status(err, "Error reading LoaderConfigTimeout EFI variable, ignoring: %m"); + config_timeout_load_from_smbios(config); err = efivar_get_timeout(u"LoaderConfigTimeoutOneShot", &config->timeout_sec); if (err == EFI_SUCCESS) { -- 2.47.3