bootctl: configure a sysfail entry

You can configure the sysfail boot entry using the bootctl command:
$ bootctl set-sysfail sysfail.conf

The value will be stored in the `LoaderEntrySysFail` EFI variable.

The `LoaderEntrySysFail` EFI variable would be unset automatically
during next boot by `systemd-boot-clear-sysfail.service` if no
system failure occured, otherwise it would be kept as it is and a system
failure reason will be saved to `LoaderSysFailReason` EFI variable.

Signed-off-by: Igor Opaniuk <igor.opaniuk@foundries.io>
This commit is contained in:
Igor Opaniuk
2025-03-24 15:33:16 +01:00
parent 004e3e4082
commit 2857a83975
10 changed files with 122 additions and 12 deletions

View File

@@ -134,10 +134,14 @@
<varlistentry>
<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.</para>
the <option>set-default</option> will set it persistently for all future boots. 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>
<para><command>bootctl list</command> can be used to list available boot loader entries and their
IDs.</para>
@@ -146,8 +150,9 @@
<option>@oneshot</option> or <option>@current</option>, which correspond to the current default boot loader
entry for all future boots, the current default boot loader entry for the next boot, and the currently booted
boot loader entry. These special IDs are resolved to the current values of the EFI variables
<varname>LoaderEntryDefault</varname>, <varname>LoaderEntryOneShot</varname> and <varname>LoaderEntrySelected</varname>,
see <ulink url="https://uapi-group.org/specifications/specs/boot_loader_specification">Boot Loader Specification</ulink> for details.
<varname>LoaderEntryDefault</varname>, <varname>LoaderEntrySysFail</varname>, <varname>LoaderEntryOneShot</varname>
and <varname>LoaderEntrySelected</varname>, see <ulink url="https://uapi-group.org/specifications/specs/boot_loader_specification">
Boot Loader Specification</ulink> for details.
These special IDs are primarily useful as a quick way to persistently make the currently booted boot loader
entry the default choice, or to upgrade the default boot loader entry for the next boot to the default boot
loader entry for all future boots, but may be used for other operations too.</para>

View File

@@ -0,0 +1,52 @@
<?xml version='1.0'?> <!--*-nxml-*-->
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
<refentry id="systemd-boot-clear-sysfail.service" conditional='ENABLE_BOOTLOADER'
xmlns:xi="http://www.w3.org/2001/XInclude">
<refentryinfo>
<title>systemd-boot-clear-sysfail.service</title>
<productname>systemd</productname>
</refentryinfo>
<refmeta>
<refentrytitle>systemd-boot-clear-sysfail.service</refentrytitle>
<manvolnum>8</manvolnum>
</refmeta>
<refnamediv>
<refname>systemd-boot-clear-sysfail.service</refname>
<refpurpose>Clear LoaderEntrySysFail entry </refpurpose>
</refnamediv>
<refsynopsisdiv>
<para><filename>systemd-boot-clear-sysfail.service</filename></para>
</refsynopsisdiv>
<refsect1>
<title>Description</title>
<para><filename>systemd-boot-clear-sysfail.service</filename> is a system service that automatically clears the
'LoaderEntrySysFail' boot loader entry if the boot was successful and the 'LoaderSysFailReason' EFI variable,
which indicates the reason for the system failure, is not set. </para>
<para>The <filename>systemd-boot-random-seed.service</filename> unit invokes the <command>bootctl --graceful
set-sysfail ""</command> command, which clears the LoaderEntrySysFail entry. The service is conditionalized
so that it is run only when a LoaderSysFailReason entry is not set.</para><para>For further details see
<citerefentry><refentrytitle>bootctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>, regarding
the command this service invokes.</para>
</refsect1>
<refsect1>
<title>See Also</title>
<para><simplelist type="inline">
<member><citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>bootctl</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry></member>
</simplelist></para>
</refsect1>
</refentry>

View File

@@ -19,6 +19,7 @@ enable machines.target
enable getty@.service
enable systemd-boot-clear-sysfail.service
enable systemd-boot-update.service
enable systemd-confext.service
enable systemd-homed.service

View File

@@ -307,6 +307,7 @@ libefi_sources = files(
'secure-boot.c',
'shim.c',
'smbios.c',
'sysfail.c',
'ticks.c',
'url-discovery.c',
'util.c',

View File

@@ -1224,6 +1224,7 @@ static int remove_loader_variables(void) {
EFI_LOADER_VARIABLE_STR("LoaderConfigTimeout"),
EFI_LOADER_VARIABLE_STR("LoaderConfigTimeoutOneShot"),
EFI_LOADER_VARIABLE_STR("LoaderEntryDefault"),
EFI_LOADER_VARIABLE_STR("LoaderEntrySysFail"),
EFI_LOADER_VARIABLE_STR("LoaderEntryLastBooted"),
EFI_LOADER_VARIABLE_STR("LoaderEntryOneShot"),
EFI_LOADER_VARIABLE_STR("LoaderSystemToken")) {

View File

@@ -89,6 +89,11 @@ static int parse_loader_entry_target_arg(const char *arg1, char16_t **ret_target
if (r < 0)
return log_error_errno(r, "Failed to get EFI variable 'LoaderEntryDefault': %m");
} else if (streq(arg1, "@sysfail")) {
r = efi_get_variable(EFI_LOADER_VARIABLE_STR("LoaderEntrySysFail"), NULL, (void *) ret_target, ret_target_size);
if (r < 0)
return log_error_errno(r, "Failed to get EFI variable 'LoaderEntrySysFail': %m");
} else if (arg1[0] == '@' && !streq(arg1, "@saved"))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unsupported special entry identifier: %s", arg1);
else {
@@ -144,6 +149,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-sysfail")) {
variable = EFI_LOADER_VARIABLE_STR("LoaderEntrySysFail");
arg_parser = parse_loader_entry_target_arg;
} else if (streq(argv[0], "set-oneshot")) {
variable = EFI_LOADER_VARIABLE_STR("LoaderEntryOneShot");
arg_parser = parse_loader_entry_target_arg;

View File

@@ -420,7 +420,7 @@ 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;
*current_entry = NULL, *oneshot_entry = NULL, *default_entry = NULL, *sysfail_entry = NULL, *sysfail_reason = NULL;
uint64_t loader_features = 0, stub_features = 0;
int have;
@@ -435,6 +435,8 @@ int verb_status(int argc, char *argv[], void *userdata) {
(void) efi_get_variable_string_and_warn(EFI_LOADER_VARIABLE_STR("LoaderEntrySelected"), &current_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("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);
SecureBootMode secure = efi_get_secure_boot_mode();
printf("%sSystem:%s\n", ansi_underline(), ansi_normal());
@@ -484,7 +486,7 @@ int verb_status(int argc, char *argv[], void *userdata) {
if (loader) {
printf("%sCurrent Boot Loader:%s\n", ansi_underline(), ansi_normal());
printf(" Product: %s%s%s\n", ansi_highlight(), loader, ansi_normal());
printf(" Product: %s%s%s\n", ansi_highlight(), loader, ansi_normal());
for (size_t i = 0; i < ELEMENTSOF(loader_flags); i++)
print_yes_no_line(i == 0, FLAGS_SET(loader_features, loader_flags[i].flag), loader_flags[i].name);
@@ -502,23 +504,28 @@ int verb_status(int argc, char *argv[], void *userdata) {
SD_ID128_FORMAT_VAL(loader_partition_uuid),
SD_ID128_FORMAT_VAL(esp_uuid));
printf(" Partition: /dev/disk/by-partuuid/" SD_ID128_UUID_FORMAT_STR "\n",
printf(" Partition: /dev/disk/by-partuuid/" SD_ID128_UUID_FORMAT_STR "\n",
SD_ID128_FORMAT_VAL(loader_partition_uuid));
} else if (loader_path)
printf(" Partition: n/a\n");
printf(" Partition: n/a\n");
if (loader_path)
printf(" Loader: %s%s\n", glyph(GLYPH_TREE_RIGHT), strna(loader_path));
printf(" Loader: %s%s\n", glyph(GLYPH_TREE_RIGHT), strna(loader_path));
if (loader_url)
printf(" Net Boot URL: %s\n", loader_url);
printf(" Net Boot URL: %s\n", loader_url);
if (sysfail_entry)
printf("SysFail Reason: %s\n", sysfail_reason);
if (current_entry)
printf("Current Entry: %s\n", current_entry);
printf(" Current Entry: %s\n", current_entry);
if (default_entry)
printf("Default Entry: %s\n", default_entry);
printf(" Default Entry: %s\n", default_entry);
if (oneshot_entry && !streq_ptr(oneshot_entry, default_entry))
printf("OneShot Entry: %s\n", oneshot_entry);
printf(" OneShot Entry: %s\n", oneshot_entry);
if (sysfail_entry)
printf(" SysFail Entry: %s\n", sysfail_entry);
printf("\n");
}

View File

@@ -259,6 +259,7 @@ static int help(int argc, char *argv[], void *userdata) {
"\n%3$sBoot Loader Interface Commands:%4$s\n"
" set-default ID Set default boot loader entry\n"
" set-oneshot ID Set default boot loader entry, for next boot only\n"
" set-sysfail ID Set boot loader entry used in case of a system failure\n"
" set-timeout SECONDS Set the menu timeout\n"
" set-timeout-oneshot SECONDS\n"
" Set the menu timeout for the next boot only\n"
@@ -660,6 +661,7 @@ static int bootctl_main(int argc, char *argv[]) {
{ "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 },
{ "set-sysfail", 2, 2, 0, verb_set_efivar },
{ "random-seed", VERB_ANY, 1, 0, verb_random_seed },
{ "reboot-to-firmware", VERB_ANY, 2, 0, verb_reboot_to_firmware },
{}

View File

@@ -286,6 +286,10 @@ units = [
'conditions' : ['ENABLE_BOOTLOADER'],
'symlinks' : ['sysinit.target.wants/'],
},
{
'file' : 'systemd-boot-clear-sysfail.service',
'conditions' : ['ENABLE_BOOTLOADER'],
},
{
'file' : 'systemd-boot-update.service',
'conditions' : ['ENABLE_BOOTLOADER'],

View File

@@ -0,0 +1,29 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
#
# This file is part of systemd.
#
# systemd is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
[Unit]
Description=Clear SysFail Entry If The Boot Is Successful
Documentation=man:systemd-boot-sysfail.service(8)
DefaultDependencies=no
Conflicts=shutdown.target
Before=sysinit.target shutdown.target
ConditionPathExists=!/etc/initrd-release
# If LoaderSysFailReason is set we should not clear SysFail entry
ConditionPathExists=!/sys/firmware/efi/efivars/LoaderSysFailReason-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f
ConditionPathExists=/sys/firmware/efi/efivars/LoaderEntrySysFail-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=bootctl --graceful set-sysfail ""
[Install]
WantedBy=sysinit.target