mirror of
https://github.com/morgan9e/systemd
synced 2026-04-14 00:14:32 +09:00
sd-boot: add support for a sysfail entry (#34856)
Add support for a sysfail boot entry. Sysfail boot entries can be used
for optional tweaking the automatic selection order in case a failure
state of the system in some form is detected (boot firmware failure
etc).
The EFI variable `LoaderEntrySysFail` contains the sysfail boot loader
entry to use. It can be set using bootctl:
```
$ bootctl set-sysfail sysfail.conf
```
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.
`sysfail_check()` expected to be extented to support possibleconditions
when we should boot sysfail("recovery") boot entry.
Also add support for using a sysfail boot entry in case of UEFI firmware
capsule update failure [1]. The status of a firmware update is obtained
from the EFI System Resource Table (ESRT), which provides an optional
mechanism for identifying device and system firmware resources for the
purposes of targeting firmware updates to those resources.
Current implementation uses the value of LastAttemptStatus field from
ESRT, which describes the result of the last firmware update attempt for
the firmware resource entry. The field is updated each time an
`UpdateCapsule()` is attempted for an ESRT entry and is preserved across
reboots (non-volatile).
This can be be used in setups with support for A/B OTA updates, where
the boot firmware and Linux/RootFS might be updated synchronously.
The check is activated by adding "sysfail-firmware-upd" to loader.conf
[1]
https://uefi.org/specs/UEFI/2.10/23_Firmware_Update_and_Reporting.html
This commit is contained in:
@@ -102,6 +102,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 |
|
||||
| LoaderEntrySysFail | sysfail entry identifier | non-volatile |
|
||||
| LoaderSysFailReason | system failure reason | volatile |
|
||||
| LoaderConfigTimeout | timeout in seconds to show the menu | non-volatile |
|
||||
| LoaderEntryOneShot | entry identifier to select at the next and only the next bootup | non-volatile |
|
||||
| LoaderDeviceIdentifier | list of identifiers of the volume the loader was started from | volatile |
|
||||
|
||||
@@ -58,6 +58,18 @@ variables. All EFI variables use the vendor UUID
|
||||
* The EFI variable `LoaderEntryDefault` contains the default boot loader entry
|
||||
to use. It contains a NUL-terminated boot loader entry identifier.
|
||||
|
||||
* The EFI variable `LoaderEntrySysFail` specifies 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. If a system failure occurs and `LoaderEntrySysFail` is set,
|
||||
systemd-boot will use this boot entry and store the actual SysFail reason in
|
||||
the `LoaderSysFailReason` EFI variable.
|
||||
|
||||
* The EFI variable `LoaderSysFailReason` contains the system failure reason.
|
||||
This variable is used in cooperation with `LoaderEntrySysFail` boot entry.
|
||||
If system failure doesn't occur `LoaderSysFailReason` is not set at all.
|
||||
|
||||
* Similarly, the EFI variable `LoaderEntryOneShot` 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
|
||||
|
||||
@@ -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>
|
||||
|
||||
52
man/systemd-boot-clear-sysfail.service.xml
Normal file
52
man/systemd-boot-clear-sysfail.service.xml
Normal 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>
|
||||
@@ -461,6 +461,7 @@
|
||||
|
||||
<varlistentry>
|
||||
<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. Set primarily by the OS and read by the boot
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "shim.h"
|
||||
#include "smbios.h"
|
||||
#include "strv-fundamental.h"
|
||||
#include "sysfail.h"
|
||||
#include "ticks.h"
|
||||
#include "tpm2-pcr.h"
|
||||
#include "uki.h"
|
||||
@@ -136,6 +137,7 @@ typedef struct {
|
||||
char16_t *entry_default_efivar;
|
||||
char16_t *entry_oneshot;
|
||||
char16_t *entry_saved;
|
||||
char16_t *entry_sysfail;
|
||||
bool editor;
|
||||
bool auto_entries;
|
||||
bool auto_firmware;
|
||||
@@ -149,6 +151,7 @@ typedef struct {
|
||||
bool use_saved_entry;
|
||||
bool use_saved_entry_efivar;
|
||||
bool beep;
|
||||
bool sysfail_occured;
|
||||
int64_t console_mode;
|
||||
int64_t console_mode_efivar;
|
||||
} Config;
|
||||
@@ -328,6 +331,8 @@ static void print_status(Config *config, char16_t *loaded_image_path) {
|
||||
printf(" default (EFI var): %ls\n", config->entry_default_efivar);
|
||||
if (config->entry_oneshot)
|
||||
printf(" default (one-shot): %ls\n", config->entry_oneshot);
|
||||
if (config->entry_sysfail)
|
||||
printf(" sysfail: %ls\n", config->entry_sysfail);
|
||||
if (config->entry_saved)
|
||||
printf(" saved entry: %ls\n", config->entry_saved);
|
||||
printf(" editor: %ls\n", yes_no(config->editor));
|
||||
@@ -1499,11 +1504,13 @@ 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"LoaderEntrySysFail", &config->entry_sysfail);
|
||||
|
||||
strtolower16(config->entry_default_config);
|
||||
strtolower16(config->entry_default_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");
|
||||
@@ -1686,11 +1693,38 @@ static size_t config_find_entry(Config *config, const char16_t *pattern) {
|
||||
return IDX_INVALID;
|
||||
}
|
||||
|
||||
static bool sysfail_process(Config *config) {
|
||||
SysFailType sysfail_type;
|
||||
|
||||
assert(config);
|
||||
|
||||
sysfail_type = sysfail_check();
|
||||
if (sysfail_type == SYSFAIL_NO_FAILURE)
|
||||
return false;
|
||||
|
||||
/* Store reason string in LoaderSysFailReason EFI variable */
|
||||
const char16_t *reason_str = sysfail_get_error_str(sysfail_type);
|
||||
if (reason_str)
|
||||
(void) efivar_set_str16(MAKE_GUID_PTR(LOADER), u"LoaderSysFailReason", reason_str, 0);
|
||||
|
||||
config->sysfail_occured = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void config_select_default_entry(Config *config) {
|
||||
size_t i;
|
||||
|
||||
assert(config);
|
||||
|
||||
if (config->sysfail_occured) {
|
||||
i = config_find_entry(config, config->entry_sysfail);
|
||||
if (i != IDX_INVALID) {
|
||||
config->idx_default = i;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
i = config_find_entry(config, config->entry_oneshot);
|
||||
if (i != IDX_INVALID) {
|
||||
config->idx_default = i;
|
||||
@@ -2685,6 +2719,7 @@ static void config_free(Config *config) {
|
||||
free(config->entry_default_efivar);
|
||||
free(config->entry_oneshot);
|
||||
free(config->entry_saved);
|
||||
free(config->entry_sysfail);
|
||||
}
|
||||
|
||||
static void config_write_entries_to_variable(Config *config) {
|
||||
@@ -2983,6 +3018,7 @@ static EFI_STATUS run(EFI_HANDLE image) {
|
||||
_cleanup_free_ char16_t *loaded_image_path = NULL;
|
||||
(void) device_path_to_str(loaded_image->FilePath, &loaded_image_path);
|
||||
config_load_all_entries(&config, loaded_image, loaded_image_path, root_dir);
|
||||
(void) sysfail_process(&config);
|
||||
|
||||
if (config.n_entries == 0)
|
||||
return log_error_status(
|
||||
|
||||
@@ -126,6 +126,16 @@ typedef uint64_t EFI_PHYSICAL_ADDRESS;
|
||||
|
||||
#define EFI_CUSTOM_MODE_ENABLE_GUID \
|
||||
GUID_DEF(0xc076ec0c, 0x7028, 0x4399, 0xa0, 0x72, 0x71, 0xee, 0x5c, 0x44, 0x8b, 0x9f)
|
||||
#define EFI_SYSTEM_RESOURCE_TABLE_GUID \
|
||||
GUID_DEF(0xb122a263, 0x3661, 0x4f68, 0x99, 0x29, 0x78, 0xf8, 0xb0, 0xd6, 0x21, 0x80)
|
||||
|
||||
/* EFI System Resource Table (ESRT) Firmware Type Definitions */
|
||||
#define ESRT_FW_TYPE_UNKNOWN 0x00000000U
|
||||
#define ESRT_FW_TYPE_SYSTEMFIRMWARE 0x00000001U
|
||||
#define ESRT_FW_TYPE_DEVICEFIRMWARE 0x00000002U
|
||||
#define ESRT_FW_TYPE_UEFIDRIVER 0x00000003U
|
||||
|
||||
#define LAST_ATTEMPT_STATUS_SUCCESS 0x00000000U
|
||||
|
||||
#define EVT_TIMER 0x80000000U
|
||||
#define EVT_RUNTIME 0x40000000U
|
||||
@@ -426,6 +436,23 @@ typedef struct {
|
||||
} *ConfigurationTable;
|
||||
} EFI_SYSTEM_TABLE;
|
||||
|
||||
typedef struct {
|
||||
EFI_GUID FwClass;
|
||||
uint32_t FwType;
|
||||
uint32_t FwVersion;
|
||||
uint32_t LowestSupportedFwVersion;
|
||||
uint32_t CapsuleFlags;
|
||||
uint32_t LastAttemptVersion;
|
||||
uint32_t LastAttemptStatus;
|
||||
} EFI_SYSTEM_RESOURCE_ENTRY;
|
||||
|
||||
typedef struct {
|
||||
uint32_t FwResourceCount;
|
||||
uint32_t FwResourceCountMax;
|
||||
uint64_t FwResourceVersion;
|
||||
EFI_SYSTEM_RESOURCE_ENTRY Entries[];
|
||||
} EFI_SYSTEM_RESOURCE_TABLE;
|
||||
|
||||
extern EFI_SYSTEM_TABLE *ST;
|
||||
extern EFI_BOOT_SERVICES *BS;
|
||||
extern EFI_RUNTIME_SERVICES *RT;
|
||||
|
||||
@@ -308,6 +308,7 @@ libefi_sources = files(
|
||||
'secure-boot.c',
|
||||
'shim.c',
|
||||
'smbios.c',
|
||||
'sysfail.c',
|
||||
'ticks.c',
|
||||
'url-discovery.c',
|
||||
'util.c',
|
||||
|
||||
39
src/boot/sysfail.c
Normal file
39
src/boot/sysfail.c
Normal file
@@ -0,0 +1,39 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "sysfail.h"
|
||||
#include "util.h"
|
||||
|
||||
static bool firmware_update_has_failed(void) {
|
||||
const EFI_SYSTEM_RESOURCE_TABLE *esrt_table;
|
||||
const EFI_SYSTEM_RESOURCE_ENTRY *esrt_entries;
|
||||
|
||||
esrt_table = find_configuration_table(MAKE_GUID_PTR(EFI_SYSTEM_RESOURCE_TABLE));
|
||||
if (!esrt_table)
|
||||
return false;
|
||||
|
||||
esrt_entries = esrt_table->Entries;
|
||||
|
||||
FOREACH_ARRAY(esrt_entry, esrt_entries, esrt_table->FwResourceCount)
|
||||
if (esrt_entry->FwType == ESRT_FW_TYPE_SYSTEMFIRMWARE)
|
||||
return esrt_entry->LastAttemptStatus != LAST_ATTEMPT_STATUS_SUCCESS;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
SysFailType sysfail_check(void) {
|
||||
if (firmware_update_has_failed())
|
||||
return SYSFAIL_FIRMWARE_UPDATE;
|
||||
|
||||
return SYSFAIL_NO_FAILURE;
|
||||
}
|
||||
|
||||
const char16_t* sysfail_get_error_str(SysFailType fail_type) {
|
||||
switch (fail_type) {
|
||||
case SYSFAIL_NO_FAILURE:
|
||||
return NULL;
|
||||
case SYSFAIL_FIRMWARE_UPDATE:
|
||||
return u"firmware-updare-failure";
|
||||
default:
|
||||
assert_not_reached();
|
||||
}
|
||||
}
|
||||
13
src/boot/sysfail.h
Normal file
13
src/boot/sysfail.h
Normal file
@@ -0,0 +1,13 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include "efivars-fundamental.h"
|
||||
|
||||
typedef enum SysFailType {
|
||||
SYSFAIL_NO_FAILURE,
|
||||
SYSFAIL_FIRMWARE_UPDATE,
|
||||
_SYSFAIL_MAX,
|
||||
} SysFailType;
|
||||
|
||||
SysFailType sysfail_check(void);
|
||||
const char16_t* sysfail_get_error_str(SysFailType fail_type);
|
||||
@@ -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")) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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"), ¤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("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");
|
||||
}
|
||||
|
||||
@@ -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 },
|
||||
{}
|
||||
|
||||
@@ -457,6 +457,7 @@ void boot_config_free(BootConfig *config) {
|
||||
free(config->entry_oneshot);
|
||||
free(config->entry_default);
|
||||
free(config->entry_selected);
|
||||
free(config->entry_sysfail);
|
||||
|
||||
FOREACH_ARRAY(i, config->entries, config->n_entries)
|
||||
boot_entry_free(i);
|
||||
@@ -1437,6 +1438,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 \"LoaderEntrySelected\", ignoring: %m");
|
||||
|
||||
r = efi_get_variable_string(EFI_LOADER_VARIABLE_STR("LoaderEntrySysFail"), &config->entry_sysfail);
|
||||
if (r == -ENOMEM)
|
||||
return log_oom();
|
||||
if (r < 0 && !IN_SET(r, -ENOENT, -ENODATA))
|
||||
log_warning_errno(r, "Failed to read EFI variable \"LoaderEntrySysFail\", ignoring: %m");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -80,6 +80,7 @@ typedef struct BootConfig {
|
||||
char *entry_oneshot;
|
||||
char *entry_default;
|
||||
char *entry_selected;
|
||||
char *entry_sysfail;
|
||||
|
||||
BootEntry *entries;
|
||||
size_t n_entries;
|
||||
|
||||
@@ -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'],
|
||||
|
||||
29
units/systemd-boot-clear-sysfail.service
Normal file
29
units/systemd-boot-clear-sysfail.service
Normal 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
|
||||
Reference in New Issue
Block a user