]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
sd-boot: use sysfail entry for UEFI firmware update failure 34856/head
authorIgor Opaniuk <igor.opaniuk@foundries.io>
Thu, 23 Jan 2025 12:31:04 +0000 (13:31 +0100)
committerIgor Opaniuk <igor.opaniuk@foundries.io>
Mon, 12 May 2025 13:37:47 +0000 (15:37 +0200)
Add support for using a sysfail 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 might be updated synchronously.

[1] https://uefi.org/specs/UEFI/2.10/23_Firmware_Update_and_Reporting.html
Signed-off-by: Igor Opaniuk <igor.opaniuk@foundries.io>
src/boot/efi.h
src/boot/sysfail.c
src/boot/sysfail.h

index 9cac17207212ad2c7586d74ae85116a9ec54e6c7..f35d0692bf8a3f1f16ccd66972b481b4c753b001 100644 (file)
@@ -126,6 +126,16 @@ typedef uint64_t EFI_PHYSICAL_ADDRESS;
 
 #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        0x00000000U
+#define ESRT_FW_TYPE_SYSTEMFIRMWARE 0x00000001U
+#define ESRT_FW_TYPE_DEVICEFIRMWARE 0x00000002U
+#define ESRT_FW_TYPE_UEFIDRIVER     0x00000003U
+
+#define LAST_ATTEMPT_STATUS_SUCCESS 0x00000000U
 
 #define EVT_TIMER                         0x80000000U
 #define EVT_RUNTIME                       0x40000000U
@@ -426,6 +436,23 @@ 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_ENTRY Entries[];
+} EFI_SYSTEM_RESOURCE_TABLE;
+
 extern EFI_SYSTEM_TABLE *ST;
 extern EFI_BOOT_SERVICES *BS;
 extern EFI_RUNTIME_SERVICES *RT;
index 6bbbffe98d32573ea3d5b8dfa5c56f57ad11cb2f..02e36bae7f7296086f7ed78e9c539920a9c04043 100644 (file)
@@ -3,10 +3,37 @@
 #include "sysfail.h"
 #include "util.h"
 
+static bool firmware_update_has_failed(void) {
+        const EFI_SYSTEM_RESOURCE_TABLE *esrt_table;
+        const EFI_SYSTEM_RESOURCE_ENTRY *esrt_entries;
+
+        esrt_table = find_configuration_table(MAKE_GUID_PTR(EFI_SYSTEM_RESOURCE_TABLE));
+        if (!esrt_table)
+                return false;
+
+        esrt_entries = esrt_table->Entries;
+
+        FOREACH_ARRAY(esrt_entry, esrt_entries, esrt_table->FwResourceCount)
+                if (esrt_entry->FwType == ESRT_FW_TYPE_SYSTEMFIRMWARE)
+                        return esrt_entry->LastAttemptStatus != LAST_ATTEMPT_STATUS_SUCCESS;
+
+        return false;
+}
+
 SysFailType sysfail_check(void) {
+        if (firmware_update_has_failed())
+                return SYSFAIL_FIRMWARE_UPDATE;
+
         return SYSFAIL_NO_FAILURE;
 }
 
 const char16_t* sysfail_get_error_str(SysFailType fail_type) {
-        return NULL;
+        switch (fail_type) {
+        case SYSFAIL_NO_FAILURE:
+                return NULL;
+        case SYSFAIL_FIRMWARE_UPDATE:
+                return u"firmware-updare-failure";
+        default:
+                assert_not_reached();
+        }
 }
index 4ccb54828b7f60216530ecf430712c8c176deeac..aafdf790be3ebf39e21eb287ebe99f92442cbf97 100644 (file)
@@ -5,6 +5,7 @@
 
 typedef enum SysFailType {
         SYSFAIL_NO_FAILURE,
+        SYSFAIL_FIRMWARE_UPDATE,
         _SYSFAIL_MAX,
 } SysFailType;