mirror of
https://github.com/systemd/systemd.git
synced 2025-01-04 09:18:12 +03:00
efi/boot: use fallback entry in case of UEFI firmware update failure
Add support for using a fallback boot entry in case of UEFI firmware capsule update failure [1]. The status of a firmware update is obtained from the EFI System Resource Table (ESRT), which provides an optional mechanism for identifying device and system firmware resources for the purposes of targeting firmware updates to those resources. Current implementation uses the value of LastAttemptStatus field from ESRT, which describes the result of the last firmware update attempt for the firmware resource entry. The field is updated each time an UpdateCapsule() is attempted for an ESRT entry and is preserved across reboots (non-volatile). This can be be used in setups with support for A/B OTA updates, where the boot firmware and Linux/RootFS are updated synchronously. The check is activated by adding "fallback-firmware-failure" to loader.conf [1] https://uefi.org/specs/UEFI/2.10/23_Firmware_Update_and_Reporting.html Signed-off-by: Igor Opaniuk <igor.opaniuk@foundries.io>
This commit is contained in:
parent
c2327afbfe
commit
e2e42056fd
@ -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"
|
||||
@ -98,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;
|
||||
@ -541,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:
|
||||
@ -1278,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"))
|
||||
@ -1785,6 +1792,10 @@ static size_t config_find_entry(Config *config, const char16_t *pattern) {
|
||||
static bool config_is_fallback_required(Config *config) {
|
||||
assert(config);
|
||||
|
||||
if (config->fallback_firmware_failure) {
|
||||
return recovery_check_firmware_failure();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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
23
src/boot/efi/recovery.c
Normal 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
7
src/boot/efi/recovery.h
Normal 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);
|
Loading…
Reference in New Issue
Block a user