1
0
mirror of https://github.com/systemd/systemd.git synced 2024-10-26 08:55:40 +03:00
This commit is contained in:
Igor Opaniuk 2024-10-25 20:37:05 +02:00 committed by GitHub
commit d2e628b91e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 114 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

@ -20,6 +20,7 @@
#include "proto/device-path.h"
#include "proto/simple-text-io.h"
#include "random-seed.h"
#include "recovery.h"
#include "sbat.h"
#include "secure-boot.h"
#include "shim.h"
@ -88,6 +89,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;
@ -97,6 +99,7 @@ typedef struct {
bool auto_poweroff;
bool auto_reboot;
bool reboot_for_bitlocker;
bool fallback_firmware_failure;
secure_boot_enroll secure_boot_enroll;
bool force_menu;
bool use_saved_entry;
@ -525,6 +528,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)
@ -538,6 +543,7 @@ static void print_status(Config *config, char16_t *loaded_image_path) {
printf(" auto-reboot: %ls\n", yes_no(config->auto_reboot));
printf(" beep: %ls\n", yes_no(config->beep));
printf(" reboot-for-bitlocker: %ls\n", yes_no(config->reboot_for_bitlocker));
printf(" fallback-firmware-failure: %ls\n", yes_no(config->fallback_firmware_failure));
switch (config->secure_boot_enroll) {
case ENROLL_OFF:
@ -1275,6 +1281,10 @@ static void config_defaults_load_from_file(Config *config, char *content) {
if (!parse_boolean(value, &config->reboot_for_bitlocker))
log_error("Error parsing 'reboot-for-bitlocker' config option, ignoring: %s",
value);
} else if (streq8(key, "fallback-firmware-failure")) {
if (!parse_boolean(value, &config->fallback_firmware_failure))
log_error("Error parsing 'fallback-firmware-failure' config option, ignoring: %s",
value);
} else if (streq8(key, "secure-boot-enroll")) {
if (streq8(value, "manual"))
@ -1634,8 +1644,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 +1789,29 @@ 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);
if (config->fallback_firmware_failure) {
return recovery_check_firmware_failure();
}
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 +2673,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

@ -144,6 +144,16 @@ typedef struct {
GUID_DEF(0xd719b2cb, 0x3d3a, 0x4596, 0xa3, 0xbc, 0xda, 0xd0, 0x0e, 0x67, 0x65, 0x6f)
#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 0x00000000
#define ESRT_FW_TYPE_SYSTEMFIRMWARE 0x00000001
#define ESRT_FW_TYPE_DEVICEFIRMWARE 0x00000002
#define ESRT_FW_TYPE_UEFIDRIVER 0x00000003
#define LAST_ATTEMPT_STATUS_SUCCESS 0x00000000
#define EVT_TIMER 0x80000000U
#define EVT_RUNTIME 0x40000000U
@ -458,6 +468,22 @@ 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_TABLE;
extern EFI_SYSTEM_TABLE *ST;
extern EFI_BOOT_SERVICES *BS;
extern EFI_RUNTIME_SERVICES *RT;

View File

@ -267,6 +267,7 @@ libefi_sources = files(
'measure.c',
'part-discovery.c',
'pe.c',
'recovery.c',
'random-seed.c',
'secure-boot.c',
'shim.c',

23
src/boot/efi/recovery.c Normal file
View File

@ -0,0 +1,23 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "recovery.h"
#include "util.h"
#define FALLBACK_BOOT_ENTRY L"ostree-1-lmp.conf\0"
bool recovery_check_firmware_failure(void) {
EFI_SYSTEM_RESOURCE_TABLE *esrt_table = NULL;
EFI_SYSTEM_RESOURCE_ENTRY *esrt_entry = NULL;
esrt_table = find_configuration_table(MAKE_GUID_PTR(EFI_SYSTEM_RESOURCE_TABLE));
if (esrt_table) {
esrt_entry = (void *)(esrt_table + 1);
for (uint32_t i = 0; i < esrt_table->FwResourceCount; i++, esrt_entry++) {
if (esrt_entry->FwType == ESRT_FW_TYPE_SYSTEMFIRMWARE) {
return (esrt_entry->LastAttemptStatus != LAST_ATTEMPT_STATUS_SUCCESS);
}
}
}
return false;
}

7
src/boot/efi/recovery.h Normal file
View File

@ -0,0 +1,7 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include <efi.h>
#include "efivars-fundamental.h"
bool recovery_check_firmware_failure(void);

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;