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 <igor.opaniuk@foundries.io>
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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user