diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c index ba9cbe0de97..38eba3c5e9d 100644 --- a/src/boot/efi/boot.c +++ b/src/boot/efi/boot.c @@ -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; } diff --git a/src/boot/efi/efi.h b/src/boot/efi/efi.h index e1042635b9c..f38af3a0444 100644 --- a/src/boot/efi/efi.h +++ b/src/boot/efi/efi.h @@ -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; diff --git a/src/boot/efi/meson.build b/src/boot/efi/meson.build index 0109793b7a9..8110a4d5e77 100644 --- a/src/boot/efi/meson.build +++ b/src/boot/efi/meson.build @@ -267,6 +267,7 @@ libefi_sources = files( 'measure.c', 'part-discovery.c', 'pe.c', + 'recovery.c', 'random-seed.c', 'secure-boot.c', 'shim.c', diff --git a/src/boot/efi/recovery.c b/src/boot/efi/recovery.c new file mode 100644 index 00000000000..883abb2bd84 --- /dev/null +++ b/src/boot/efi/recovery.c @@ -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; +} \ No newline at end of file diff --git a/src/boot/efi/recovery.h b/src/boot/efi/recovery.h new file mode 100644 index 00000000000..40a14f7ac5c --- /dev/null +++ b/src/boot/efi/recovery.h @@ -0,0 +1,7 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include +#include "efivars-fundamental.h" + +bool recovery_check_firmware_failure(void); \ No newline at end of file