lib/tpm: Capture TCG2 event log from EFI_TCG2_PROTOCOL
diff --git a/common/lib/tpm.c b/common/lib/tpm.c
index 7b602b17..2daac193 100644
--- a/common/lib/tpm.c
+++ b/common/lib/tpm.c
@@ -11,6 +11,42 @@
#include <lib/libc.h>
#include <mm/pmm.h>
+// TCG event log entry layouts (TCG PC Client Platform Firmware Profile).
+struct tpm_pcr_event_v1_2 {
+ uint32_t pcr_idx;
+ uint32_t event_type;
+ uint8_t digest[20];
+ uint32_t event_size;
+ uint8_t event[];
+} __attribute__((packed));
+
+struct tpm_specid_event_alg {
+ uint16_t alg_id;
+ uint16_t digest_size;
+} __attribute__((packed));
+
+struct tpm_specid_event_head {
+ uint8_t signature[16];
+ uint32_t platform_class;
+ uint8_t spec_version_minor;
+ uint8_t spec_version_major;
+ uint8_t spec_errata;
+ uint8_t uintn_size;
+ uint32_t num_algs;
+ struct tpm_specid_event_alg digest_sizes[];
+} __attribute__((packed));
+
+// Followed by `count` digests (uint16_t alg_id + variable-length digest),
+// then a uint32_t event_size and event_size bytes of event data.
+struct tpm_pcr_event2_head {
+ uint32_t pcr_idx;
+ uint32_t event_type;
+ uint32_t count;
+} __attribute__((packed));
+
+#define TCG_EV_NO_ACTION 3
+#define TCG_SPECID_SIG "Spec ID Event03"
+
static EFI_TCG2_PROTOCOL *tcg2 = NULL;
void tpm_init(void) {
@@ -66,4 +102,137 @@ void tpm_measure(uint32_t pcr, uint32_t event_type,
pmm_free(event, event_size);
}
+uint32_t tpm_calc_event_size(const void *event_p, const void *header_p) {
+ const struct tpm_pcr_event2_head *event = event_p;
+ const struct tpm_pcr_event_v1_2 *event_header = header_p;
+
+ static const uint8_t zero_digest[20] = {0};
+
+ if (event_header->pcr_idx != 0
+ || event_header->event_type != TCG_EV_NO_ACTION
+ || memcmp(event_header->digest, zero_digest, sizeof(zero_digest)) != 0) {
+ return 0;
+ }
+
+ const struct tpm_specid_event_head *efispecid =
+ (const struct tpm_specid_event_head *)event_header->event;
+
+ if (memcmp(efispecid->signature, TCG_SPECID_SIG, sizeof(TCG_SPECID_SIG)) != 0
+ || efispecid->num_algs == 0
+ || event->count != efispecid->num_algs) {
+ return 0;
+ }
+
+ const uint8_t *marker_start = (const uint8_t *)event_p;
+ const uint8_t *marker = marker_start
+ + sizeof(event->pcr_idx)
+ + sizeof(event->event_type)
+ + sizeof(event->count);
+
+ for (uint32_t i = 0; i < event->count; i++) {
+ uint16_t halg;
+ memcpy(&halg, marker, sizeof(halg));
+ marker += sizeof(halg);
+
+ uint32_t j;
+ for (j = 0; j < efispecid->num_algs; j++) {
+ if (halg == efispecid->digest_sizes[j].alg_id) {
+ marker += efispecid->digest_sizes[j].digest_size;
+ break;
+ }
+ }
+ if (j == efispecid->num_algs) {
+ return 0;
+ }
+ }
+
+ uint32_t trailing_event_size;
+ memcpy(&trailing_event_size, marker, sizeof(trailing_event_size));
+ marker += sizeof(trailing_event_size) + trailing_event_size;
+
+ if (event->event_type == 0 && trailing_event_size == 0) {
+ return 0;
+ }
+
+ return (uint32_t)(marker - marker_start);
+}
+
+static void *captured_log = NULL;
+static size_t captured_log_size = 0;
+static uint32_t captured_log_format = 0;
+static bool capture_attempted = false;
+
+// Pull the firmware event log via GetEventLog and copy the raw event bytes
+// into a bootloader-reclaimable buffer. Idempotent. Returns true if the
+// captured state is valid.
+static bool tpm_capture_event_log(void) {
+ if (capture_attempted) {
+ return captured_log != NULL;
+ }
+ capture_attempted = true;
+
+ if (tcg2 == NULL) {
+ return false;
+ }
+
+ EFI_PHYSICAL_ADDRESS log_location = 0, log_last_entry = 0;
+ BOOLEAN truncated = FALSE;
+
+ uint32_t log_format = EFI_TCG2_EVENT_LOG_FORMAT_TCG_2;
+ EFI_STATUS status = tcg2->GetEventLog(tcg2, log_format,
+ &log_location, &log_last_entry, &truncated);
+ if (status != EFI_SUCCESS || log_location == 0) {
+ log_format = EFI_TCG2_EVENT_LOG_FORMAT_TCG_1_2;
+ status = tcg2->GetEventLog(tcg2, log_format,
+ &log_location, &log_last_entry, &truncated);
+ if (status != EFI_SUCCESS || log_location == 0) {
+ return false;
+ }
+ }
+
+ uint32_t log_size = 0;
+ if (log_last_entry != 0) {
+ uint32_t last_entry_size = 0;
+ if (log_format > EFI_TCG2_EVENT_LOG_FORMAT_TCG_1_2) {
+ last_entry_size = tpm_calc_event_size(
+ (void *)(uintptr_t)log_last_entry,
+ (void *)(uintptr_t)log_location);
+ } else {
+ const struct tpm_pcr_event_v1_2 *e =
+ (const struct tpm_pcr_event_v1_2 *)(uintptr_t)log_last_entry;
+ last_entry_size = sizeof(struct tpm_pcr_event_v1_2) + e->event_size;
+ }
+ log_size = (uint32_t)(log_last_entry - log_location) + last_entry_size;
+ }
+
+ void *log_bytes = NULL;
+ if (log_size > 0) {
+ log_bytes = ext_mem_alloc(log_size);
+ memcpy(log_bytes, (void *)(uintptr_t)log_location, log_size);
+ }
+
+ captured_log = log_bytes;
+ captured_log_size = log_size;
+ captured_log_format = log_format;
+ return true;
+}
+
+bool tpm_get_event_log(uint32_t *format, void **address, size_t *size) {
+ if (!tpm_capture_event_log()) {
+ return false;
+ }
+
+ *format = captured_log_format;
+ *address = captured_log;
+ *size = captured_log_size;
+ return true;
+}
+
+void tpm_release_event_log(void) {
+ if (captured_log != NULL) {
+ pmm_free(captured_log, captured_log_size);
+ captured_log = NULL;
+ }
+}
+
#endif
diff --git a/common/lib/tpm.h b/common/lib/tpm.h
index d31d8b13..c94bc277 100644
--- a/common/lib/tpm.h
+++ b/common/lib/tpm.h
@@ -21,6 +21,22 @@ void tpm_measure(uint32_t pcr, uint32_t event_type,
const void *data, size_t data_size,
const char *description);
+// Capture the firmware TCG2 event log into bootloader-reclaimable memory
+// and expose the raw event stream. `format` receives the TCG event log
+// format identifier (1 = TCG 1.2, 2 = TCG 2.0 crypto-agile). Returns false
+// if no TPM is present or capture failed.
+bool tpm_get_event_log(uint32_t *format, void **address, size_t *size);
+
+// Free the captured event log buffer. Subsequent tpm_get_event_log() calls
+// return false.
+void tpm_release_event_log(void);
+
+// Compute the in-memory size of one TCG 2.0 crypto-agile event entry.
+// `header` must point to the spec-ID event at the start of the log; it
+// carries the per-algorithm digest sizes needed to walk variable-length
+// digest lists. Returns 0 on malformed input.
+uint32_t tpm_calc_event_size(const void *event, const void *header);
+
#endif
#endif
