1
0
mirror of https://github.com/systemd/systemd.git synced 2025-01-06 17:18:12 +03:00

efi/boot: add support for a fallback entry

Add support for a fallback boot entry. Fallback boot entries can be used to
ensure that a system starts even if the default boot entry fails or
other unexpected events happen that can prevent to use the default boot
entry (boot firmware failure, failed A/B OTA update etc).

The EFI variable `LoaderEntryFallback` contains the fallback boot loader
entry to use.

config_is_fallback_required() expected to be extented to support possible
conditions when we should fall back to "recovery" boot entry.

Signed-off-by: Igor Opaniuk <igor.opaniuk@foundries.io>
This commit is contained in:
Igor Opaniuk 2024-10-22 15:22:15 +02:00
parent a95aacc851
commit c2327afbfe
9 changed files with 46 additions and 2 deletions

View File

@ -102,6 +102,7 @@ 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 |
| LoaderEntryFallback | fallback entry identifier | non-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 |

View File

@ -58,6 +58,11 @@ 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 `LoaderEntryFallback` contains the fallback boot loader entry
to use. Fallback boot entries can be used to ensure that a system starts even
if the default boot entry fails or other unexpected event happened (boot firmware
failure etc.). It contains a NUL-terminated boot loader entry identifier.
* 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

View File

@ -146,8 +146,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>LoaderEntryFallback</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

@ -451,6 +451,7 @@
<varlistentry>
<term><varname>LoaderEntryDefault</varname></term>
<term><varname>LoaderEntryFallback</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

View File

@ -1019,6 +1019,7 @@ static int remove_loader_variables(void) {
EFI_LOADER_VARIABLE(LoaderConfigTimeout),
EFI_LOADER_VARIABLE(LoaderConfigTimeoutOneShot),
EFI_LOADER_VARIABLE(LoaderEntryDefault),
EFI_LOADER_VARIABLE(LoaderEntryFallback),
EFI_LOADER_VARIABLE(LoaderEntryLastBooted),
EFI_LOADER_VARIABLE(LoaderEntryOneShot),
EFI_LOADER_VARIABLE(LoaderSystemToken)) {

View File

@ -88,6 +88,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, "@fallback")) {
r = efi_get_variable(EFI_LOADER_VARIABLE(LoaderEntryFallback), NULL, (void *) ret_target, ret_target_size);
if (r < 0)
return log_error_errno(r, "Failed to get EFI variable 'LoaderEntryFallback': %m");
} else if (arg1[0] == '@' && !streq(arg1, "@saved"))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unsupported special entry identifier: %s", arg1);
else {
@ -139,6 +144,9 @@ int verb_set_efivar(int argc, char *argv[], void *userdata) {
if (streq(argv[0], "set-default")) {
variable = EFI_LOADER_VARIABLE(LoaderEntryDefault);
arg_parser = parse_loader_entry_target_arg;
} else if (streq(argv[0], "set-fallback")) {
variable = EFI_LOADER_VARIABLE(LoaderEntryFallback);
arg_parser = parse_loader_entry_target_arg;
} else if (streq(argv[0], "set-oneshot")) {
variable = EFI_LOADER_VARIABLE(LoaderEntryOneShot);
arg_parser = parse_loader_entry_target_arg;

View File

@ -88,6 +88,7 @@ typedef struct {
uint64_t timeout_sec_config;
uint64_t timeout_sec_efivar;
char16_t *entry_default_config;
char16_t *entry_fallback;
char16_t *entry_default_efivar;
char16_t *entry_oneshot;
char16_t *entry_saved;
@ -525,6 +526,8 @@ static void print_status(Config *config, char16_t *loaded_image_path) {
if (config->entry_default_config)
printf(" default (config): %ls\n", config->entry_default_config);
if (config->entry_fallback)
printf(" default (fallback): %ls\n", config->entry_fallback);
if (config->entry_default_efivar)
printf(" default (EFI var): %ls\n", config->entry_default_efivar);
if (config->entry_oneshot)
@ -1634,8 +1637,10 @@ 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"LoaderEntryFallback", &config->entry_fallback);
strtolower16(config->entry_default_config);
strtolower16(config->entry_fallback);
strtolower16(config->entry_default_efivar);
strtolower16(config->entry_oneshot);
strtolower16(config->entry_saved);
@ -1777,11 +1782,25 @@ static size_t config_find_entry(Config *config, const char16_t *pattern) {
return IDX_INVALID;
}
static bool config_is_fallback_required(Config *config) {
assert(config);
return false;
}
static void config_select_default_entry(Config *config) {
size_t i;
assert(config);
if (config_is_fallback_required(config)) {
i = config_find_entry(config, config->entry_fallback);
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;
@ -2643,6 +2662,7 @@ static void config_free(Config *config) {
boot_entry_free(config->entries[i]);
free(config->entries);
free(config->entry_default_config);
free(config->entry_fallback);
free(config->entry_default_efivar);
free(config->entry_oneshot);
free(config->entry_saved);

View File

@ -1394,6 +1394,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 \"LoaderEntryDefault\", ignoring: %m");
r = efi_get_variable_string(EFI_LOADER_VARIABLE(LoaderEntryFallback), &config->entry_fallback);
if (r == -ENOMEM)
return log_oom();
if (r < 0 && !IN_SET(r, -ENOENT, -ENODATA))
log_warning_errno(r, "Failed to read EFI variable \"LoaderEntryFallback\", ignoring: %m");
r = efi_get_variable_string(EFI_LOADER_VARIABLE(LoaderEntrySelected), &config->entry_selected);
if (r == -ENOMEM)
return log_oom();

View File

@ -71,6 +71,7 @@ typedef struct BootConfig {
char *entry_oneshot;
char *entry_default;
char *entry_fallback;
char *entry_selected;
BootEntry *entries;