From: r-vdp Date: Sun, 11 Jan 2026 18:49:34 +0000 (+0100) Subject: systemd-boot: add a preferred setting that's similar to default but avoids booting... X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=450e0dce02d754d7af599dd99ab40b9363072760;p=thirdparty%2Fsystemd.git systemd-boot: add a preferred setting that's similar to default but avoids booting known-bad entries Motivation: Currently, when setting the default boot pattern, boot assessment status is not taken into account. This means that with boot assessment enabled, when an explicit boot entry is configured as the default entry using an EFI var, as is common for instance in A/B boot schemes, the configured entry will be booted indefinitly, regardless of the entry's boot assessment status. In order to allow for this use case in combination with boot assessment, we introduce a new `preferred` keyword, both in the config file and in the bootctl CLI, that acts very similar to the existing `default` keyword but takes boot assessment into account and never selects any entries that have been marked as bad. If the preferred pattern does not resolve to any bootable entry, and a default pattern is also specified, then the default pattern will be considered next, and we may then still select a known-bad entry to be booted. Fixes: https://github.com/systemd/systemd/issues/31215 Fixes: https://github.com/systemd/systemd/issues/40192 --- diff --git a/docs/BOOT.md b/docs/BOOT.md index 0912d0c9082..96f92d722a7 100644 --- a/docs/BOOT.md +++ b/docs/BOOT.md @@ -101,7 +101,8 @@ Some EFI variables control the loader or exported the loaders state to the start | EFI Variables | |---------------|------------------------|-------------------------------| -| LoaderEntryDefault | entry identifier to select as default at bootup | non-volatile | +| LoaderEntryDefault | entry identifier to select as default at bootup, ignoring boot assessment | non-volatile | +| LoaderEntryPreferred | entry identifier to select as default at bootup, respecting boot assessment | non-volatile | | LoaderEntrySysFail | sysfail entry identifier | non-volatile | | LoaderSysFailReason | system failure reason | volatile | | LoaderConfigTimeout | timeout in seconds to show the menu | non-volatile | diff --git a/docs/BOOT_LOADER_INTERFACE.md b/docs/BOOT_LOADER_INTERFACE.md index a2e70fc429a..5c2e74f2901 100644 --- a/docs/BOOT_LOADER_INTERFACE.md +++ b/docs/BOOT_LOADER_INTERFACE.md @@ -61,8 +61,24 @@ Variables will be listed below using the Linux efivarfs naming, The list should be in the order the entries are shown on screen during boot. See below regarding the recommended vocabulary for boot loader entry identifiers. +* The EFI variable `LoaderEntryPreferred-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f` + contains the preferred boot loader entry to use. + This takes boot assessment into account by not selecting boot entries that have + been marked as bad, + see Automatic Boot Assessment + for more details on boot assessment. + If no entry was selected by the preferred setting (from either the EFI var or + the config file), then the boot loader will look at the default setting, which + does not skip entries that were marked as bad. + It contains a NUL-terminated boot loader entry identifier. + * The EFI variable `LoaderEntryDefault-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f` contains the default boot loader entry to use. + This ignores boot assessment and can select boot entries that have been marked + as bad by boot assessment, + see Automatic Boot Assessment + for more details on boot assessment as well as the documentation on the + `LoaderEntryPreferred` EFI var. It contains a NUL-terminated boot loader entry identifier. * The EFI variable `LoaderEntrySysFail-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f` @@ -83,10 +99,10 @@ Variables will be listed below using the Linux efivarfs naming, contains the default boot loader entry to use for a single following boot. It is set by the OS in order to request booting into a specific menu entry on the following boot. - When set overrides `LoaderEntryDefault`. + When set overrides `LoaderEntryPreferred` and `LoaderEntryDefault`. It is removed automatically after being read by the boot loader, to ensure it only takes effect a single time. - This value is formatted the same way as `LoaderEntryDefault`. + This value is formatted the same way as `LoaderEntryDefault` and `LoaderEntryPreferred`. * The EFI variable `LoaderEntrySelected-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f` contains the boot loader entry identifier that was booted. @@ -126,6 +142,7 @@ Variables will be listed below using the Linux efivarfs naming, [Boot Loader Specification](https://uapi-group.org/specifications/specs/boot_loader_specification). * `1 << 18` → The boot loader reports active TPM2 PCR banks in the EFI variable `LoaderTpm2ActivePcrBanks-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f`. + * `1 << 19` → The boot loader supports the `LoaderEntryPreferred` variable when set. * The EFI variable `LoaderSystemToken-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f` contains binary random data, @@ -168,7 +185,8 @@ variables. While boot loader entries may be named relatively freely, it's highly recommended to follow these rules when picking identifiers for the entries, so that programs (and users) can derive basic context and meaning from the identifiers -as passed in `LoaderEntries`, `LoaderEntryDefault`, `LoaderEntryOneShot`, `LoaderEntrySelected`, +as passed in `LoaderEntries`, `LoaderEntryPreferred`, `LoaderEntryDefault`, +`LoaderEntryOneShot`, `LoaderEntrySelected`, and possibly show nicely localized names for them in UIs. 1. When boot loader entries are defined through the diff --git a/man/bootctl.xml b/man/bootctl.xml index 77c824df95a..0c46edb6105 100644 --- a/man/bootctl.xml +++ b/man/bootctl.xml @@ -132,13 +132,17 @@ + ID ID ID ID Sets the default boot loader entry. Takes a single boot loader entry ID string or a glob pattern as argument. The command will set the default entry only for the next boot, - the will set it persistently for all future boots. The command + will set it persistently for all future boots, + is like , + but is aware of boot assessment and will skip boot entries that have their tries-left counter set to zero. + The command will set the boot loader entry to be used in case of a system failure. System failure (SysFail) boot entries can optionally modify the automatic selection order in the event of a failure, such as a boot firmware update failure with the failure status recorded in the EFI system table. diff --git a/man/systemd-boot.xml b/man/systemd-boot.xml index 78ef02768fa..dab10ed8ef1 100644 --- a/man/systemd-boot.xml +++ b/man/systemd-boot.xml @@ -147,11 +147,20 @@ d - Make selected entry the default + Make selected entry the preferred boot entry An EFI variable is set to allow this setting to persist. - + + + + + D + Make selected entry the default boot entry + + An EFI variable is set to allow this setting to persist. + + @@ -472,6 +481,7 @@ + LoaderEntryPreferred LoaderEntryDefault LoaderEntrySysFail LoaderEntryOneShot @@ -479,6 +489,8 @@ The identifier of the default boot loader entry. Can be set in the OS and the boot loader. LoaderEntryOneShot sets the default entry for the next boot only, while LoaderEntryDefault sets it persistently for all future boots. + LoaderEntryPreferred is like LoaderEntryDefault but additionally + takes into account boot assessment and skips boot entries with a tries-left counter equal to zero. bootctl1's and commands make use of these variables. The boot loader modifies LoaderEntryDefault on request, when the diff --git a/src/boot/boot.c b/src/boot/boot.c index d7fa50fe0f8..cdf36b95203 100644 --- a/src/boot/boot.c +++ b/src/boot/boot.c @@ -133,7 +133,9 @@ typedef struct { uint64_t timeout_sec_config; uint64_t timeout_sec_efivar; char16_t *entry_default_config; + char16_t *entry_preferred_config; char16_t *entry_default_efivar; + char16_t *entry_preferred_efivar; char16_t *entry_oneshot; char16_t *entry_saved; char16_t *entry_sysfail; @@ -150,6 +152,8 @@ typedef struct { bool force_menu; bool use_saved_entry; bool use_saved_entry_efivar; + bool use_saved_entry_preferred; + bool use_saved_entry_preferred_efivar; bool beep; bool sysfail_occurred; int64_t console_mode; @@ -328,8 +332,12 @@ static void print_status(Config *config, char16_t *loaded_image_path) { if (config->entry_default_config) printf(" default (config): %ls\n", config->entry_default_config); + if (config->entry_preferred_config) + printf(" preferred (config): %ls\n", config->entry_preferred_config); if (config->entry_default_efivar) printf(" default (EFI var): %ls\n", config->entry_default_efivar); + if (config->entry_preferred_efivar) + printf(" preferred (EFI var): %ls\n", config->entry_preferred_efivar); if (config->entry_oneshot) printf(" default (one-shot): %ls\n", config->entry_oneshot); if (config->entry_sysfail) @@ -760,7 +768,25 @@ static bool menu_run( action = ACTION_QUIT; break; + /* Set/unset the preferred entry */ case KEYPRESS(0, 0, 'd'): + if (config->idx_default_efivar != idx_highlight) { + free(config->entry_preferred_efivar); + config->entry_preferred_efivar = xstrdup16(config->entries[idx_highlight]->id); + config->idx_default_efivar = idx_highlight; + status = xstrdup16(u"Preferred boot entry selected."); + } else { + config->entry_preferred_efivar = mfree(config->entry_preferred_efivar); + config->idx_default_efivar = IDX_INVALID; + status = xstrdup16(u"Preferred boot entry cleared."); + } + config->entry_default_efivar = mfree(config->entry_default_efivar); + config->use_saved_entry_efivar = false; + config->use_saved_entry_preferred_efivar = false; + refresh = true; + break; + + /* Set/unset the default entry */ case KEYPRESS(0, 0, 'D'): if (config->idx_default_efivar != idx_highlight) { free(config->entry_default_efivar); @@ -772,7 +798,9 @@ static bool menu_run( config->idx_default_efivar = IDX_INVALID; status = xstrdup16(u"Default boot entry cleared."); } + config->entry_preferred_efivar = mfree(config->entry_preferred_efivar); config->use_saved_entry_efivar = false; + config->use_saved_entry_preferred_efivar = false; refresh = true; break; @@ -919,8 +947,17 @@ static bool menu_run( /* Update EFI vars after we left the menu to reduce NVRAM writes. */ - if (default_efivar_saved != config->idx_default_efivar) - efivar_set_str16(MAKE_GUID_PTR(LOADER), u"LoaderEntryDefault", config->entry_default_efivar, EFI_VARIABLE_NON_VOLATILE); + if (default_efivar_saved != config->idx_default_efivar) { + if (config->entry_preferred_efivar) + efivar_set_str16(MAKE_GUID_PTR(LOADER), u"LoaderEntryPreferred", config->entry_preferred_efivar, EFI_VARIABLE_NON_VOLATILE); + else + efivar_unset(MAKE_GUID_PTR(LOADER), u"LoaderEntryPreferred", EFI_VARIABLE_NON_VOLATILE); + + if (config->entry_default_efivar) + efivar_set_str16(MAKE_GUID_PTR(LOADER), u"LoaderEntryDefault", config->entry_default_efivar, EFI_VARIABLE_NON_VOLATILE); + else + efivar_unset(MAKE_GUID_PTR(LOADER), u"LoaderEntryDefault", EFI_VARIABLE_NON_VOLATILE); + } if (console_mode_efivar_saved != config->console_mode_efivar) { if (config->console_mode_efivar == CONSOLE_MODE_KEEP) @@ -1071,6 +1108,14 @@ static void config_defaults_load_from_file(Config *config, char *content) { free(config->entry_default_config); config->entry_default_config = xstr8_to_16(value); + } else if (streq8(key, "preferred")) { + if (value[0] == '@' && !strcaseeq8(value, "@saved")) { + log_warning("Unsupported special entry identifier, ignoring: %s", value); + continue; + } + free(config->entry_preferred_config); + config->entry_preferred_config = xstr8_to_16(value); + } else if (streq8(key, "editor")) { if (!parse_boolean(value, &config->editor)) log_warning("Error parsing 'editor' config option, ignoring: %s", value); @@ -1571,18 +1616,24 @@ static void config_load_defaults(Config *config, EFI_FILE *root_dir) { (void) efivar_unset(MAKE_GUID_PTR(LOADER), u"LoaderEntryOneShot", EFI_VARIABLE_NON_VOLATILE); (void) efivar_get_str16(MAKE_GUID_PTR(LOADER), u"LoaderEntryDefault", &config->entry_default_efivar); + (void) efivar_get_str16(MAKE_GUID_PTR(LOADER), u"LoaderEntryPreferred", &config->entry_preferred_efivar); (void) efivar_get_str16(MAKE_GUID_PTR(LOADER), u"LoaderEntrySysFail", &config->entry_sysfail); strtolower16(config->entry_default_config); strtolower16(config->entry_default_efivar); + strtolower16(config->entry_preferred_config); + strtolower16(config->entry_preferred_efivar); strtolower16(config->entry_oneshot); strtolower16(config->entry_saved); strtolower16(config->entry_sysfail); config->use_saved_entry = streq16(config->entry_default_config, u"@saved"); config->use_saved_entry_efivar = streq16(config->entry_default_efivar, u"@saved"); - if (config->use_saved_entry || config->use_saved_entry_efivar) + config->use_saved_entry_preferred = streq16(config->entry_preferred_config, u"@saved"); + config->use_saved_entry_preferred_efivar = streq16(config->entry_preferred_efivar, u"@saved"); + if (config->use_saved_entry || config->use_saved_entry_efivar || config->use_saved_entry_preferred || config->use_saved_entry_preferred_efivar) (void) efivar_get_str16(MAKE_GUID_PTR(LOADER), u"LoaderEntryLastBooted", &config->entry_saved); + } static bool valid_type1_filename(const char16_t *fname) { @@ -1745,7 +1796,7 @@ static int boot_entry_compare(const BootEntry *a, const BootEntry *b) { return CMP(a->tries_done, b->tries_done); } -static size_t config_find_entry(Config *config, const char16_t *pattern) { +static size_t config_find_entry(Config *config, const char16_t *pattern, const bool check_assessment) { assert(config); /* We expect pattern and entry IDs to be already case folded. */ @@ -1754,7 +1805,7 @@ static size_t config_find_entry(Config *config, const char16_t *pattern) { return IDX_INVALID; for (size_t i = 0; i < config->n_entries; i++) - if (efi_fnmatch(pattern, config->entries[i]->id)) + if (efi_fnmatch(pattern, config->entries[i]->id) && (!check_assessment || config->entries[i]->tries_left != 0)) return i; return IDX_INVALID; @@ -1785,20 +1836,47 @@ static void config_select_default_entry(Config *config) { assert(config); if (config->sysfail_occurred) { - i = config_find_entry(config, config->entry_sysfail); + i = config_find_entry(config, config->entry_sysfail, /* check_assessment= */ false); if (i != IDX_INVALID) { config->idx_default = i; return; } } - i = config_find_entry(config, config->entry_oneshot); + i = config_find_entry(config, config->entry_oneshot, /* check_assessment= */ false); if (i != IDX_INVALID) { config->idx_default = i; return; } - i = config_find_entry(config, config->use_saved_entry_efivar ? config->entry_saved : config->entry_default_efivar); + /* Try to match the preferred entry pattern */ + + i = config_find_entry(config, config->use_saved_entry_preferred_efivar ? config->entry_saved : config->entry_preferred_efivar, /* check_assessment= */ true); + if (i != IDX_INVALID) { + config->idx_default = i; + config->idx_default_efivar = i; + return; + } + + i = config_find_entry(config, config->entry_preferred_config, /* check_assessment= */ true); + if (i != IDX_INVALID) { + config->idx_default = i; + return; + } + + if (config->use_saved_entry_preferred) + /* No need to do the same thing twice. */ + i = config->use_saved_entry_preferred_efivar ? IDX_INVALID : config_find_entry(config, config->entry_saved, /* check_assessment= */ true); + else + i = config_find_entry(config, config->entry_preferred_config, /* check_assessment= */ true); + if (i != IDX_INVALID) { + config->idx_default = i; + return; + } + + /* Try to match the default pattern */ + + i = config_find_entry(config, config->use_saved_entry_efivar ? config->entry_saved : config->entry_default_efivar, /* check_assessment= */ false); if (i != IDX_INVALID) { config->idx_default = i; config->idx_default_efivar = i; @@ -1807,9 +1885,9 @@ static void config_select_default_entry(Config *config) { if (config->use_saved_entry) /* No need to do the same thing twice. */ - i = config->use_saved_entry_efivar ? IDX_INVALID : config_find_entry(config, config->entry_saved); + i = config->use_saved_entry_efivar ? IDX_INVALID : config_find_entry(config, config->entry_saved, /* check_assessment= */ false); else - i = config_find_entry(config, config->entry_default_config); + i = config_find_entry(config, config->entry_default_config, /* check_assessment= */ false); if (i != IDX_INVALID) { config->idx_default = i; return; @@ -2811,6 +2889,8 @@ static void config_free(Config *config) { free(config->entries); free(config->entry_default_config); free(config->entry_default_efivar); + free(config->entry_preferred_config); + free(config->entry_preferred_efivar); free(config->entry_oneshot); free(config->entry_saved); free(config->entry_sysfail); @@ -2932,6 +3012,7 @@ static void export_loader_variables( EFI_LOADER_FEATURE_CONFIG_TIMEOUT | EFI_LOADER_FEATURE_CONFIG_TIMEOUT_ONE_SHOT | EFI_LOADER_FEATURE_ENTRY_DEFAULT | + EFI_LOADER_FEATURE_ENTRY_PREFERRED | EFI_LOADER_FEATURE_ENTRY_ONESHOT | EFI_LOADER_FEATURE_BOOT_COUNTING | EFI_LOADER_FEATURE_XBOOTLDR | diff --git a/src/bootctl/bootctl-install.c b/src/bootctl/bootctl-install.c index 50ab7b602c4..1a8d5ffb30c 100644 --- a/src/bootctl/bootctl-install.c +++ b/src/bootctl/bootctl-install.c @@ -1861,6 +1861,7 @@ static int remove_loader_variables(void) { EFI_LOADER_VARIABLE_STR("LoaderConfigConsoleMode"), EFI_LOADER_VARIABLE_STR("LoaderConfigTimeout"), EFI_LOADER_VARIABLE_STR("LoaderConfigTimeoutOneShot"), + EFI_LOADER_VARIABLE_STR("LoaderEntryPreferred"), EFI_LOADER_VARIABLE_STR("LoaderEntryDefault"), EFI_LOADER_VARIABLE_STR("LoaderEntrySysFail"), EFI_LOADER_VARIABLE_STR("LoaderEntryLastBooted"), diff --git a/src/bootctl/bootctl-set-efivar.c b/src/bootctl/bootctl-set-efivar.c index 82770dd6c6c..81eb354aea9 100644 --- a/src/bootctl/bootctl-set-efivar.c +++ b/src/bootctl/bootctl-set-efivar.c @@ -171,6 +171,9 @@ int verb_set_efivar(int argc, char *argv[], void *userdata) { if (streq(argv[0], "set-default")) { variable = EFI_LOADER_VARIABLE_STR("LoaderEntryDefault"); arg_parser = parse_loader_entry_target_arg; + } else if (streq(argv[0], "set-preferred")) { + variable = EFI_LOADER_VARIABLE_STR("LoaderEntryPreferred"); + arg_parser = parse_loader_entry_target_arg; } else if (streq(argv[0], "set-sysfail")) { variable = EFI_LOADER_VARIABLE_STR("LoaderEntrySysFail"); arg_parser = parse_loader_entry_target_arg; diff --git a/src/bootctl/bootctl-status.c b/src/bootctl/bootctl-status.c index 67b590b76f1..1b471816cf4 100644 --- a/src/bootctl/bootctl-status.c +++ b/src/bootctl/bootctl-status.c @@ -408,7 +408,8 @@ int verb_status(int argc, char *argv[], void *userdata) { { EFI_STUB_FEATURE_MULTI_PROFILE_UKI, "Stub understands profile selector" }, }; _cleanup_free_ char *fw_type = NULL, *fw_info = NULL, *loader = NULL, *loader_path = NULL, *stub = NULL, *stub_path = NULL, - *current_entry = NULL, *oneshot_entry = NULL, *default_entry = NULL, *sysfail_entry = NULL, *sysfail_reason = NULL; + *current_entry = NULL, *oneshot_entry = NULL, *preferred_entry = NULL, *default_entry = NULL, *sysfail_entry = NULL, + *sysfail_reason = NULL; uint64_t loader_features = 0, stub_features = 0; int have; @@ -422,6 +423,7 @@ int verb_status(int argc, char *argv[], void *userdata) { (void) efi_stub_get_features(&stub_features); (void) efi_get_variable_string_and_warn(EFI_LOADER_VARIABLE_STR("LoaderEntrySelected"), ¤t_entry); (void) efi_get_variable_string_and_warn(EFI_LOADER_VARIABLE_STR("LoaderEntryOneShot"), &oneshot_entry); + (void) efi_get_variable_string_and_warn(EFI_LOADER_VARIABLE_STR("LoaderEntryPreferred"), &preferred_entry); (void) efi_get_variable_string_and_warn(EFI_LOADER_VARIABLE_STR("LoaderEntryDefault"), &default_entry); (void) efi_get_variable_string_and_warn(EFI_LOADER_VARIABLE_STR("LoaderEntrySysFail"), &sysfail_entry); (void) efi_get_variable_string_and_warn(EFI_LOADER_VARIABLE_STR("LoaderSysFailReason"), &sysfail_reason); @@ -509,6 +511,8 @@ int verb_status(int argc, char *argv[], void *userdata) { if (current_entry) printf(" Current Entry: %s\n", current_entry); + if (preferred_entry) + printf(" Preferred Entry: %s\n", preferred_entry); if (default_entry) printf(" Default Entry: %s\n", default_entry); if (oneshot_entry && !streq_ptr(oneshot_entry, default_entry)) diff --git a/src/bootctl/bootctl.c b/src/bootctl/bootctl.c index e586c7f0dab..7e59fd01681 100644 --- a/src/bootctl/bootctl.c +++ b/src/bootctl/bootctl.c @@ -702,6 +702,7 @@ static int bootctl_main(int argc, char *argv[]) { { "unlink", 2, 2, 0, verb_unlink }, { "cleanup", VERB_ANY, 1, 0, verb_cleanup }, { "set-default", 2, 2, 0, verb_set_efivar }, + { "set-preferred", 2, 2, 0, verb_set_efivar }, { "set-oneshot", 2, 2, 0, verb_set_efivar }, { "set-timeout", 2, 2, 0, verb_set_efivar }, { "set-timeout-oneshot", 2, 2, 0, verb_set_efivar }, diff --git a/src/fundamental/efivars-fundamental.h b/src/fundamental/efivars-fundamental.h index df94a736e1c..15be52119a0 100644 --- a/src/fundamental/efivars-fundamental.h +++ b/src/fundamental/efivars-fundamental.h @@ -28,6 +28,7 @@ #define EFI_LOADER_FEATURE_TYPE1_UKI (UINT64_C(1) << 16) #define EFI_LOADER_FEATURE_TYPE1_UKI_URL (UINT64_C(1) << 17) #define EFI_LOADER_FEATURE_TPM2_ACTIVE_PCR_BANKS (UINT64_C(1) << 18) +#define EFI_LOADER_FEATURE_ENTRY_PREFERRED (UINT64_C(1) << 19) /* Features of the stub, i.e. systemd-stub */ #define EFI_STUB_FEATURE_REPORT_BOOT_PARTITION (UINT64_C(1) << 0) diff --git a/src/shared/bootspec.c b/src/shared/bootspec.c index b6b5d6e50b2..89dfae70c96 100644 --- a/src/shared/bootspec.c +++ b/src/shared/bootspec.c @@ -462,9 +462,11 @@ int boot_config_load_type1( void boot_config_free(BootConfig *config) { assert(config); + free(config->preferred_pattern); free(config->default_pattern); free(config->entry_oneshot); + free(config->entry_preferred); free(config->entry_default); free(config->entry_selected); free(config->entry_sysfail); @@ -515,6 +517,8 @@ int boot_loader_read_conf(BootConfig *config, FILE *file, const char *path) { continue; } + if (streq(field, "preferred")) + r = free_and_strdup(&config->preferred_pattern, p); if (streq(field, "default")) r = free_and_strdup(&config->default_pattern, p); else if (STR_IN_SET(field, "timeout", "editor", "auto-entries", "auto-firmware", @@ -1390,6 +1394,15 @@ static int boot_entries_select_default(const BootConfig *config) { } } + if (config->entry_preferred) { + i = boot_config_find(config, config->entry_preferred); + if (i >= 0) { + log_debug("Found default: id \"%s\" is matched by LoaderEntryPreferred", + config->entries[i].id); + return i; + } + } + if (config->entry_default) { i = boot_config_find(config, config->entry_default); if (i >= 0) { @@ -1399,6 +1412,15 @@ static int boot_entries_select_default(const BootConfig *config) { } } + if (config->preferred_pattern) { + i = boot_config_find(config, config->preferred_pattern); + if (i >= 0) { + log_debug("Found preferred: id \"%s\" is matched by pattern \"%s\"", + config->entries[i].id, config->preferred_pattern); + return i; + } + } + if (config->default_pattern) { i = boot_config_find(config, config->default_pattern); if (i >= 0) { @@ -1438,6 +1460,12 @@ static int boot_load_efi_entry_pointers(BootConfig *config, bool skip_efivars) { if (r < 0 && !IN_SET(r, -ENOENT, -ENODATA)) log_warning_errno(r, "Failed to read EFI variable \"LoaderEntryOneShot\", ignoring: %m"); + r = efi_get_variable_string(EFI_LOADER_VARIABLE_STR("LoaderEntryPreferred"), &config->entry_preferred); + if (r == -ENOMEM) + return log_oom(); + if (r < 0 && !IN_SET(r, -ENOENT, -ENODATA)) + log_warning_errno(r, "Failed to read EFI variable \"LoaderEntryPreferred\", ignoring: %m"); + r = efi_get_variable_string(EFI_LOADER_VARIABLE_STR("LoaderEntryDefault"), &config->entry_default); if (r == -ENOMEM) return log_oom(); diff --git a/src/shared/bootspec.h b/src/shared/bootspec.h index 3bd516c75df..f325dcae251 100644 --- a/src/shared/bootspec.h +++ b/src/shared/bootspec.h @@ -73,8 +73,10 @@ typedef struct BootConfig { int loader_conf_status; /* 0 → before loading, 1 → loaded, negative → error. */ char *default_pattern; + char *preferred_pattern; char *entry_oneshot; + char *entry_preferred; char *entry_default; char *entry_selected; char *entry_sysfail;