]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
boot: Avoid magic values in timeout EFI vars 24434/head
authorJan Janssen <medhefgo@web.de>
Tue, 6 Sep 2022 08:46:27 +0000 (10:46 +0200)
committerJan Janssen <medhefgo@web.de>
Wed, 7 Sep 2022 10:55:55 +0000 (12:55 +0200)
docs/BOOT_LOADER_INTERFACE.md
src/boot/efi/boot.c

index f7be50421c1188385ae152b4eaddccb6160fbe61..fc9336085bd70d1d6818025903f422399da55da7 100644 (file)
@@ -31,7 +31,11 @@ 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 µs.
+  string, in UTF-16. The time is specified in seconds. 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.
 
 * 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
index ac08650c292877a17d2899460571686bd78753d5..a7ef57cb99f621786f61ecb5232af57490ad412e 100644 (file)
@@ -1037,11 +1037,20 @@ static bool menu_run(
         }
 
         if (timeout_efivar_saved != config->timeout_sec_efivar) {
-                if (config->timeout_sec_efivar == TIMEOUT_UNSET)
+                switch (config->timeout_sec_efivar) {
+                case TIMEOUT_UNSET:
                         efivar_set(LOADER_GUID, L"LoaderConfigTimeout", NULL, EFI_VARIABLE_NON_VOLATILE);
-                else
+                        break;
+                case TIMEOUT_MENU_FORCE:
+                        efivar_set(LOADER_GUID, u"LoaderConfigTimeout", u"menu-force", EFI_VARIABLE_NON_VOLATILE);
+                        break;
+                case TIMEOUT_MENU_HIDDEN:
+                        efivar_set(LOADER_GUID, u"LoaderConfigTimeout", u"menu-hidden", EFI_VARIABLE_NON_VOLATILE);
+                        break;
+                default:
                         efivar_set_uint_string(LOADER_GUID, L"LoaderConfigTimeout",
                                                config->timeout_sec_efivar, EFI_VARIABLE_NON_VOLATILE);
+                }
         }
 
         clear_screen(COLOR_NORMAL);
@@ -1532,6 +1541,34 @@ static void config_entry_add_type1(
         TAKE_PTR(entry);
 }
 
+static EFI_STATUS efivar_get_timeout(const char16_t *var, uint32_t *ret_value) {
+        _cleanup_free_ char16_t *value = NULL;
+        EFI_STATUS err;
+
+        assert(var);
+        assert(ret_value);
+
+        err = efivar_get(LOADER_GUID, var, &value);
+        if (err != EFI_SUCCESS)
+                return err;
+
+        if (streq16(value, u"menu-force")) {
+                *ret_value = TIMEOUT_MENU_FORCE;
+                return EFI_SUCCESS;
+        }
+        if (streq16(value, u"menu-hidden")) {
+                *ret_value = TIMEOUT_MENU_HIDDEN;
+                return EFI_SUCCESS;
+        }
+
+        uint64_t timeout;
+        if (!parse_number16(value, &timeout, NULL))
+                return EFI_INVALID_PARAMETER;
+
+        *ret_value = MIN(timeout, TIMEOUT_TYPE_MAX);
+        return EFI_SUCCESS;
+}
+
 static void config_load_defaults(Config *config, EFI_FILE *root_dir) {
         _cleanup_free_ char *content = NULL;
         UINTN value;
@@ -1557,20 +1594,20 @@ static void config_load_defaults(Config *config, EFI_FILE *root_dir) {
         if (err == EFI_SUCCESS)
                 config_defaults_load_from_file(config, content);
 
-        err = efivar_get_uint_string(LOADER_GUID, L"LoaderConfigTimeout", &value);
-        if (err == EFI_SUCCESS) {
-                config->timeout_sec_efivar = MIN(value, TIMEOUT_TYPE_MAX);
+        err = efivar_get_timeout(u"LoaderConfigTimeout", &config->timeout_sec_efivar);
+        if (err == EFI_SUCCESS)
                 config->timeout_sec = config->timeout_sec_efivar;
-        }
+        else if (err != EFI_NOT_FOUND)
+                log_error_stall(u"Error reading LoaderConfigTimeout EFI variable: %r", err);
 
-        err = efivar_get_uint_string(LOADER_GUID, L"LoaderConfigTimeoutOneShot", &value);
+        err = efivar_get_timeout(u"LoaderConfigTimeoutOneShot", &config->timeout_sec);
         if (err == EFI_SUCCESS) {
                 /* Unset variable now, after all it's "one shot". */
                 (void) efivar_set(LOADER_GUID, L"LoaderConfigTimeoutOneShot", NULL, EFI_VARIABLE_NON_VOLATILE);
 
-                config->timeout_sec = MIN(value, TIMEOUT_TYPE_MAX);
                 config->force_menu = true; /* force the menu when this is set */
-        }
+        } else if (err != EFI_NOT_FOUND)
+                log_error_stall(u"Error reading LoaderConfigTimeoutOneShot EFI variable: %r", err);
 
         err = efivar_get_uint_string(LOADER_GUID, L"LoaderConfigConsoleMode", &value);
         if (err == EFI_SUCCESS)