diff --git a/docs/BOOT.md b/docs/BOOT.md index a9666583a1..c72b31456b 100644 --- a/docs/BOOT.md +++ b/docs/BOOT.md @@ -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 | diff --git a/docs/BOOT_LOADER_INTERFACE.md b/docs/BOOT_LOADER_INTERFACE.md index e264c2cc3c..d4d9a77370 100644 --- a/docs/BOOT_LOADER_INTERFACE.md +++ b/docs/BOOT_LOADER_INTERFACE.md @@ -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 diff --git a/man/bootctl.xml b/man/bootctl.xml index 91d572e643..f1968a0ba7 100644 --- a/man/bootctl.xml +++ b/man/bootctl.xml @@ -134,10 +134,14 @@ 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 will set it persistently for all future boots. 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. bootctl list can be used to list available boot loader entries and their IDs. @@ -146,8 +150,9 @@ or , 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 - LoaderEntryDefault, LoaderEntryOneShot and LoaderEntrySelected, - see Boot Loader Specification for details. + LoaderEntryDefault, LoaderEntrySysFail, LoaderEntryOneShot + and LoaderEntrySelected, see + Boot Loader Specification 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. diff --git a/man/systemd-boot-clear-sysfail.service.xml b/man/systemd-boot-clear-sysfail.service.xml new file mode 100644 index 0000000000..2249486377 --- /dev/null +++ b/man/systemd-boot-clear-sysfail.service.xml @@ -0,0 +1,52 @@ + + + + + + + + systemd-boot-clear-sysfail.service + systemd + + + + systemd-boot-clear-sysfail.service + 8 + + + + systemd-boot-clear-sysfail.service + Clear LoaderEntrySysFail entry + + + + systemd-boot-clear-sysfail.service + + + + Description + + systemd-boot-clear-sysfail.service 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. + + The systemd-boot-random-seed.service unit invokes the bootctl --graceful + set-sysfail "" command, which clears the LoaderEntrySysFail entry. The service is conditionalized + so that it is run only when a LoaderSysFailReason entry is not set.For further details see + bootctl1, regarding + the command this service invokes. + + + + + See Also + + systemd1 + bootctl1 + systemd-boot7 + + + + diff --git a/man/systemd-boot.xml b/man/systemd-boot.xml index 6da2714581..d0912739d1 100644 --- a/man/systemd-boot.xml +++ b/man/systemd-boot.xml @@ -461,6 +461,7 @@ LoaderEntryDefault + LoaderEntrySysFail LoaderEntryOneShot The identifier of the default boot loader entry. Set primarily by the OS and read by the boot diff --git a/presets/90-systemd.preset b/presets/90-systemd.preset index 9c13e9c3de..56f9e93706 100644 --- a/presets/90-systemd.preset +++ b/presets/90-systemd.preset @@ -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 diff --git a/src/boot/boot.c b/src/boot/boot.c index ab043deca9..133f16acfe 100644 --- a/src/boot/boot.c +++ b/src/boot/boot.c @@ -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( diff --git a/src/boot/efi.h b/src/boot/efi.h index 9cac172072..f35d0692bf 100644 --- a/src/boot/efi.h +++ b/src/boot/efi.h @@ -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; diff --git a/src/boot/meson.build b/src/boot/meson.build index 098f9435b8..42201af909 100644 --- a/src/boot/meson.build +++ b/src/boot/meson.build @@ -308,6 +308,7 @@ libefi_sources = files( 'secure-boot.c', 'shim.c', 'smbios.c', + 'sysfail.c', 'ticks.c', 'url-discovery.c', 'util.c', diff --git a/src/boot/sysfail.c b/src/boot/sysfail.c new file mode 100644 index 0000000000..02e36bae7f --- /dev/null +++ b/src/boot/sysfail.c @@ -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(); + } +} diff --git a/src/boot/sysfail.h b/src/boot/sysfail.h new file mode 100644 index 0000000000..aafdf790be --- /dev/null +++ b/src/boot/sysfail.h @@ -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); diff --git a/src/bootctl/bootctl-install.c b/src/bootctl/bootctl-install.c index 8927464c46..7b083c4ad9 100644 --- a/src/bootctl/bootctl-install.c +++ b/src/bootctl/bootctl-install.c @@ -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")) { diff --git a/src/bootctl/bootctl-set-efivar.c b/src/bootctl/bootctl-set-efivar.c index d53458c491..ead81ff3e0 100644 --- a/src/bootctl/bootctl-set-efivar.c +++ b/src/bootctl/bootctl-set-efivar.c @@ -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; diff --git a/src/bootctl/bootctl-status.c b/src/bootctl/bootctl-status.c index 38fe5273be..081f5d48f0 100644 --- a/src/bootctl/bootctl-status.c +++ b/src/bootctl/bootctl-status.c @@ -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"); } diff --git a/src/bootctl/bootctl.c b/src/bootctl/bootctl.c index 047b79254d..790ab2b653 100644 --- a/src/bootctl/bootctl.c +++ b/src/bootctl/bootctl.c @@ -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 }, {} diff --git a/src/shared/bootspec.c b/src/shared/bootspec.c index e5ca2ccbb3..5dd8d452ab 100644 --- a/src/shared/bootspec.c +++ b/src/shared/bootspec.c @@ -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; } diff --git a/src/shared/bootspec.h b/src/shared/bootspec.h index 95675214b2..1d114e10d4 100644 --- a/src/shared/bootspec.h +++ b/src/shared/bootspec.h @@ -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; diff --git a/units/meson.build b/units/meson.build index 3dd135942d..a8b0040562 100644 --- a/units/meson.build +++ b/units/meson.build @@ -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'], diff --git a/units/systemd-boot-clear-sysfail.service b/units/systemd-boot-clear-sysfail.service new file mode 100644 index 0000000000..c80735970d --- /dev/null +++ b/units/systemd-boot-clear-sysfail.service @@ -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