:: commit 6c5e1866f6250ac0400fc766afd680d206edbca3

Mintsuki <mintsuki@protonmail.com> — 2026-04-29 23:26

parents: f812c16c4f

protos/linux: Publish TPM event log as LINUX_EFI_TPM_EVENT_LOG configuration table

diff --git a/common/protos/linux.c b/common/protos/linux.c
new file mode 100644
index 00000000..ae1346fe
--- /dev/null
+++ b/common/protos/linux.c
@@ -0,0 +1,97 @@
+#if defined (UEFI)
+
+#include <stdint.h>
+#include <stddef.h>
+#include <stdbool.h>
+#include <efi.h>
+#include <efi/protocol/efitcg2.h>
+#include <protos/linux.h>
+#include <lib/misc.h>
+#include <lib/tpm.h>
+#include <lib/print.h>
+#include <lib/libc.h>
+
+#define LINUX_EFI_TPM_EVENT_LOG_GUID \
+    { 0xb7799cb0, 0xeca2, 0x4943, { 0x96, 0x67, 0x1f, 0xae, 0x07, 0xb7, 0x47, 0xfa } }
+
+struct linux_efi_tpm_eventlog {
+    uint32_t size;
+    uint32_t final_events_preboot_size;
+    uint8_t  version;
+    uint8_t  log[];
+} __attribute__((packed));
+
+// Wrap the captured TCG event log in Linux's linux_efi_tpm_eventlog framing
+// and publish it as the LINUX_EFI_TPM_EVENT_LOG configuration table for the
+// kernel's TPM driver. Limine bypasses the EFI stub that would normally do
+// this, and the ACPI TPM2 fallback path the kernel uses otherwise is
+// unreliable on common firmware.
+void linux_install_efi_tpm_event_log(void) {
+    uint32_t format;
+    void *log_addr;
+    size_t log_size;
+    if (!tpm_get_event_log(&format, &log_addr, &log_size)) {
+        return;
+    }
+
+    // Walk the firmware's EFI_TCG2_FINAL_EVENTS_TABLE so the kernel can
+    // deduplicate any pre-boot events firmware migrated there.
+    uint32_t final_events_preboot_size = 0;
+    if (format > EFI_TCG2_EVENT_LOG_FORMAT_TCG_1_2) {
+        EFI_TCG2_FINAL_EVENTS_TABLE *final_events = NULL;
+        EFI_GUID final_events_guid = EFI_TCG2_FINAL_EVENTS_TABLE_GUID;
+        for (UINTN i = 0; i < gST->NumberOfTableEntries; i++) {
+            if (memcmp(&gST->ConfigurationTable[i].VendorGuid,
+                       &final_events_guid, sizeof(EFI_GUID)) == 0) {
+                final_events = gST->ConfigurationTable[i].VendorTable;
+                break;
+            }
+        }
+        if (final_events != NULL && final_events->NumberOfEvents > 0) {
+            const uint8_t *base = final_events->Events;
+            uint64_t remaining = final_events->NumberOfEvents;
+            while (remaining > 0) {
+                const void *header = base + final_events_preboot_size;
+                uint32_t ev_size = tpm_calc_event_size(header, log_addr);
+                if (ev_size == 0) {
+                    break;
+                }
+                final_events_preboot_size += ev_size;
+                remaining--;
+            }
+        }
+    }
+
+    UINTN total_size = sizeof(struct linux_efi_tpm_eventlog) + log_size;
+    struct linux_efi_tpm_eventlog *log_tbl = NULL;
+    EFI_STATUS status = gBS->AllocatePool(EfiACPIReclaimMemory, total_size,
+                                          (void **)&log_tbl);
+    if (status != EFI_SUCCESS) {
+        printv("linux: failed to allocate event log table: %X\n", (uint64_t)status);
+        return;
+    }
+
+    memset(log_tbl, 0, total_size);
+    log_tbl->size = (uint32_t)log_size;
+    log_tbl->final_events_preboot_size = final_events_preboot_size;
+    log_tbl->version = (uint8_t)format;
+    if (log_size > 0) {
+        memcpy(log_tbl->log, log_addr, log_size);
+    }
+
+    EFI_GUID linux_log_guid = LINUX_EFI_TPM_EVENT_LOG_GUID;
+    status = gBS->InstallConfigurationTable(&linux_log_guid, log_tbl);
+    if (status != EFI_SUCCESS) {
+        printv("linux: failed to install event log table: %X\n", (uint64_t)status);
+        gBS->FreePool(log_tbl);
+        return;
+    }
+
+    tpm_release_event_log();
+
+    printv("linux: installed event log (%u bytes, format TCG_%s) as configuration table\n",
+           log_tbl->size,
+           format == EFI_TCG2_EVENT_LOG_FORMAT_TCG_2 ? "2" : "1.2");
+}
+
+#endif
diff --git a/common/protos/linux.h b/common/protos/linux.h
index f214c25d..3b2f6c09 100644
--- a/common/protos/linux.h
+++ b/common/protos/linux.h
@@ -70,4 +70,8 @@ struct screen_info {
 
 noreturn void linux_load(char *config, char *cmdline);
 
+#if defined (UEFI)
+void linux_install_efi_tpm_event_log(void);
+#endif
+
 #endif
diff --git a/common/protos/linux_risc.c b/common/protos/linux_risc.c
index c1f7a30d..bdb8f07e 100644
--- a/common/protos/linux_risc.c
+++ b/common/protos/linux_risc.c
@@ -345,6 +345,7 @@ static void prepare_efi_tables(struct boot_param *p, char *config) {
         }
     }
 
+    linux_install_efi_tpm_event_log();
     efi_exit_boot_services();
 }
 
diff --git a/common/protos/linux_x86.c b/common/protos/linux_x86.c
index 2bc5c0ea..b506c823 100644
--- a/common/protos/linux_x86.c
+++ b/common/protos/linux_x86.c
@@ -625,6 +625,7 @@ no_fb:;
     // UEFI
     ///////////////////////////////////////
 #if defined (UEFI)
+    linux_install_efi_tpm_event_log();
     efi_exit_boot_services();
 
 #if defined (__x86_64__)
tab: 248 wrap: offon