From: Igor Opaniuk Date: Mon, 24 Mar 2025 14:30:49 +0000 (+0100) Subject: sd-boot: add support for a sysfail entry X-Git-Tag: v258-rc1~637^2~2 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=004e3e40825027f803225876b96617d6129766cb;p=thirdparty%2Fsystemd.git sd-boot: add support for a sysfail entry 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` holds the boot loader entry to be used in the event of a system failure. If a failure occurs, the reason will be stored in the `LoaderSysFailReason` EFI variable. sysfail_check() expected to be extented to support possible conditions when we should boot sysfail("recovery") boot entry. Signed-off-by: Igor Opaniuk --- diff --git a/docs/BOOT.md b/docs/BOOT.md index a9666583a16..c72b31456b3 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 e264c2cc3c1..d4d9a773702 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/systemd-boot.xml b/man/systemd-boot.xml index 6da27145810..d0912739d18 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/src/boot/boot.c b/src/boot/boot.c index ab043deca95..133f16acfe9 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/sysfail.c b/src/boot/sysfail.c new file mode 100644 index 00000000000..6bbbffe98d3 --- /dev/null +++ b/src/boot/sysfail.c @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "sysfail.h" +#include "util.h" + +SysFailType sysfail_check(void) { + return SYSFAIL_NO_FAILURE; +} + +const char16_t* sysfail_get_error_str(SysFailType fail_type) { + return NULL; +} diff --git a/src/boot/sysfail.h b/src/boot/sysfail.h new file mode 100644 index 00000000000..4ccb54828b7 --- /dev/null +++ b/src/boot/sysfail.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include "efivars-fundamental.h" + +typedef enum SysFailType { + SYSFAIL_NO_FAILURE, + _SYSFAIL_MAX, +} SysFailType; + +SysFailType sysfail_check(void); +const char16_t* sysfail_get_error_str(SysFailType fail_type); diff --git a/src/shared/bootspec.c b/src/shared/bootspec.c index e5ca2ccbb3c..5dd8d452ab3 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 95675214b2a..1d114e10d4a 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;