mirror of
https://github.com/systemd/systemd.git
synced 2025-02-02 13:47:27 +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:
parent
a95aacc851
commit
c2327afbfe
@ -102,6 +102,7 @@ Some EFI variables control the loader or exported the loaders state to the start
|
|||||||
| EFI Variables |
|
| EFI Variables |
|
||||||
|---------------|------------------------|-------------------------------|
|
|---------------|------------------------|-------------------------------|
|
||||||
| LoaderEntryDefault | entry identifier to select as default at bootup | non-volatile |
|
| 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 |
|
| 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 |
|
| 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 |
|
| LoaderDeviceIdentifier | list of identifiers of the volume the loader was started from | volatile |
|
||||||
|
@ -58,6 +58,11 @@ variables. All EFI variables use the vendor UUID
|
|||||||
* The EFI variable `LoaderEntryDefault` contains the default boot loader entry
|
* The EFI variable `LoaderEntryDefault` contains the default boot loader entry
|
||||||
to use. It contains a NUL-terminated boot loader entry identifier.
|
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
|
* 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
|
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
|
to request booting into a specific menu entry on the following boot. When set
|
||||||
|
@ -146,8 +146,9 @@
|
|||||||
<option>@oneshot</option> or <option>@current</option>, which correspond to the current default boot loader
|
<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
|
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
|
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>,
|
<varname>LoaderEntryDefault</varname>, <varname>LoaderEntryFallback</varname>, <varname>LoaderEntryOneShot</varname>
|
||||||
see <ulink url="https://uapi-group.org/specifications/specs/boot_loader_specification">Boot Loader Specification</ulink> for details.
|
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
|
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
|
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>
|
loader entry for all future boots, but may be used for other operations too.</para>
|
||||||
|
@ -451,6 +451,7 @@
|
|||||||
|
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
<term><varname>LoaderEntryDefault</varname></term>
|
<term><varname>LoaderEntryDefault</varname></term>
|
||||||
|
<term><varname>LoaderEntryFallback</varname></term>
|
||||||
<term><varname>LoaderEntryOneShot</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
|
<listitem><para>The identifier of the default boot loader entry. Set primarily by the OS and read by the boot
|
||||||
|
@ -1019,6 +1019,7 @@ static int remove_loader_variables(void) {
|
|||||||
EFI_LOADER_VARIABLE(LoaderConfigTimeout),
|
EFI_LOADER_VARIABLE(LoaderConfigTimeout),
|
||||||
EFI_LOADER_VARIABLE(LoaderConfigTimeoutOneShot),
|
EFI_LOADER_VARIABLE(LoaderConfigTimeoutOneShot),
|
||||||
EFI_LOADER_VARIABLE(LoaderEntryDefault),
|
EFI_LOADER_VARIABLE(LoaderEntryDefault),
|
||||||
|
EFI_LOADER_VARIABLE(LoaderEntryFallback),
|
||||||
EFI_LOADER_VARIABLE(LoaderEntryLastBooted),
|
EFI_LOADER_VARIABLE(LoaderEntryLastBooted),
|
||||||
EFI_LOADER_VARIABLE(LoaderEntryOneShot),
|
EFI_LOADER_VARIABLE(LoaderEntryOneShot),
|
||||||
EFI_LOADER_VARIABLE(LoaderSystemToken)) {
|
EFI_LOADER_VARIABLE(LoaderSystemToken)) {
|
||||||
|
@ -88,6 +88,11 @@ static int parse_loader_entry_target_arg(const char *arg1, char16_t **ret_target
|
|||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Failed to get EFI variable 'LoaderEntryDefault': %m");
|
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"))
|
} else if (arg1[0] == '@' && !streq(arg1, "@saved"))
|
||||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unsupported special entry identifier: %s", arg1);
|
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unsupported special entry identifier: %s", arg1);
|
||||||
else {
|
else {
|
||||||
@ -139,6 +144,9 @@ int verb_set_efivar(int argc, char *argv[], void *userdata) {
|
|||||||
if (streq(argv[0], "set-default")) {
|
if (streq(argv[0], "set-default")) {
|
||||||
variable = EFI_LOADER_VARIABLE(LoaderEntryDefault);
|
variable = EFI_LOADER_VARIABLE(LoaderEntryDefault);
|
||||||
arg_parser = parse_loader_entry_target_arg;
|
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")) {
|
} else if (streq(argv[0], "set-oneshot")) {
|
||||||
variable = EFI_LOADER_VARIABLE(LoaderEntryOneShot);
|
variable = EFI_LOADER_VARIABLE(LoaderEntryOneShot);
|
||||||
arg_parser = parse_loader_entry_target_arg;
|
arg_parser = parse_loader_entry_target_arg;
|
||||||
|
@ -88,6 +88,7 @@ typedef struct {
|
|||||||
uint64_t timeout_sec_config;
|
uint64_t timeout_sec_config;
|
||||||
uint64_t timeout_sec_efivar;
|
uint64_t timeout_sec_efivar;
|
||||||
char16_t *entry_default_config;
|
char16_t *entry_default_config;
|
||||||
|
char16_t *entry_fallback;
|
||||||
char16_t *entry_default_efivar;
|
char16_t *entry_default_efivar;
|
||||||
char16_t *entry_oneshot;
|
char16_t *entry_oneshot;
|
||||||
char16_t *entry_saved;
|
char16_t *entry_saved;
|
||||||
@ -525,6 +526,8 @@ static void print_status(Config *config, char16_t *loaded_image_path) {
|
|||||||
|
|
||||||
if (config->entry_default_config)
|
if (config->entry_default_config)
|
||||||
printf(" default (config): %ls\n", 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)
|
if (config->entry_default_efivar)
|
||||||
printf(" default (EFI var): %ls\n", config->entry_default_efivar);
|
printf(" default (EFI var): %ls\n", config->entry_default_efivar);
|
||||||
if (config->entry_oneshot)
|
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_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"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_default_config);
|
||||||
|
strtolower16(config->entry_fallback);
|
||||||
strtolower16(config->entry_default_efivar);
|
strtolower16(config->entry_default_efivar);
|
||||||
strtolower16(config->entry_oneshot);
|
strtolower16(config->entry_oneshot);
|
||||||
strtolower16(config->entry_saved);
|
strtolower16(config->entry_saved);
|
||||||
@ -1777,11 +1782,25 @@ static size_t config_find_entry(Config *config, const char16_t *pattern) {
|
|||||||
return IDX_INVALID;
|
return IDX_INVALID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool config_is_fallback_required(Config *config) {
|
||||||
|
assert(config);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
static void config_select_default_entry(Config *config) {
|
static void config_select_default_entry(Config *config) {
|
||||||
size_t i;
|
size_t i;
|
||||||
|
|
||||||
assert(config);
|
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);
|
i = config_find_entry(config, config->entry_oneshot);
|
||||||
if (i != IDX_INVALID) {
|
if (i != IDX_INVALID) {
|
||||||
config->idx_default = i;
|
config->idx_default = i;
|
||||||
@ -2643,6 +2662,7 @@ static void config_free(Config *config) {
|
|||||||
boot_entry_free(config->entries[i]);
|
boot_entry_free(config->entries[i]);
|
||||||
free(config->entries);
|
free(config->entries);
|
||||||
free(config->entry_default_config);
|
free(config->entry_default_config);
|
||||||
|
free(config->entry_fallback);
|
||||||
free(config->entry_default_efivar);
|
free(config->entry_default_efivar);
|
||||||
free(config->entry_oneshot);
|
free(config->entry_oneshot);
|
||||||
free(config->entry_saved);
|
free(config->entry_saved);
|
||||||
|
@ -1394,6 +1394,12 @@ static int boot_load_efi_entry_pointers(BootConfig *config, bool skip_efivars) {
|
|||||||
if (r < 0 && !IN_SET(r, -ENOENT, -ENODATA))
|
if (r < 0 && !IN_SET(r, -ENOENT, -ENODATA))
|
||||||
log_warning_errno(r, "Failed to read EFI variable \"LoaderEntryDefault\", ignoring: %m");
|
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);
|
r = efi_get_variable_string(EFI_LOADER_VARIABLE(LoaderEntrySelected), &config->entry_selected);
|
||||||
if (r == -ENOMEM)
|
if (r == -ENOMEM)
|
||||||
return log_oom();
|
return log_oom();
|
||||||
|
@ -71,6 +71,7 @@ typedef struct BootConfig {
|
|||||||
|
|
||||||
char *entry_oneshot;
|
char *entry_oneshot;
|
||||||
char *entry_default;
|
char *entry_default;
|
||||||
|
char *entry_fallback;
|
||||||
char *entry_selected;
|
char *entry_selected;
|
||||||
|
|
||||||
BootEntry *entries;
|
BootEntry *entries;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user