| 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 |
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 <ulink url="https://systemd.io/AUTOMATIC_BOOT_ASSESSMENT">Automatic Boot Assessment</ulink>
+ 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 <ulink url="https://systemd.io/AUTOMATIC_BOOT_ASSESSMENT">Automatic Boot Assessment</ulink>
+ 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`
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.
[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,
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
<variablelist>
<varlistentry>
+ <term><option>set-preferred</option> <replaceable>ID</replaceable></term>
<term><option>set-default</option> <replaceable>ID</replaceable></term>
<term><option>set-oneshot</option> <replaceable>ID</replaceable></term>
<term><option>set-sysfail</option> <replaceable>ID</replaceable></term>
<listitem><para>Sets the default boot loader entry. Takes a single boot loader entry ID string or a glob
pattern as argument. The <option>set-oneshot</option> command will set the default entry only for the next boot,
- the <option>set-default</option> will set it persistently for all future boots. The <option>set-sysfail</option> command
+ <option>set-default</option> will set it persistently for all future boots,
+ <option>set-preferred</option> is like <option>set-default</option>,
+ but is aware of boot assessment and will skip boot entries that have their tries-left counter set to zero.
+ The <option>set-sysfail</option> 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.</para>
<varlistentry>
<term><keycap>d</keycap></term>
- <listitem><para>Make selected entry the default</para>
+ <listitem><para>Make selected entry the preferred boot entry</para>
<para>An EFI variable is set to allow this setting to persist.</para>
- <xi:include href="version-info.xml" xpointer="v239"/></listitem>
+ <xi:include href="version-info.xml" xpointer="v260"/></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><keycap>D</keycap></term>
+ <listitem><para>Make selected entry the default boot entry</para>
+
+ <para>An EFI variable is set to allow this setting to persist.</para>
+
+ <xi:include href="version-info.xml" xpointer="v260"/></listitem>
</varlistentry>
<varlistentry>
</varlistentry>
<varlistentry>
+ <term><varname>LoaderEntryPreferred</varname></term>
<term><varname>LoaderEntryDefault</varname></term>
<term><varname>LoaderEntrySysFail</varname></term>
<term><varname>LoaderEntryOneShot</varname></term>
<listitem><para>The identifier of the default boot loader entry. Can be set in the OS and the boot
loader. <varname>LoaderEntryOneShot</varname> sets the default entry for the next boot only, while
<varname>LoaderEntryDefault</varname> sets it persistently for all future boots.
+ <varname>LoaderEntryPreferred</varname> is like <varname>LoaderEntryDefault</varname> but additionally
+ takes into account boot assessment and skips boot entries with a tries-left counter equal to zero.
<citerefentry><refentrytitle>bootctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>'s
<option>set-default</option> and <option>set-oneshot</option> commands make use of these variables.
The boot loader modifies <varname>LoaderEntryDefault</varname> on request, when the
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;
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;
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)
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);
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;
/* 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)
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);
(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) {
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. */
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;
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;
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;
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);
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 |
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"),
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;
{ 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;
(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);
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))
{ "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 },
#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)
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);
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",
}
}
+ 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) {
}
}
+ 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) {
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();
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;