Attention is currently required from: Michał Żygowski, Maciej Pijanowski, Christian Walter, Krystian Hebel.
Hello Michał Żygowski, Maciej Pijanowski, Krystian Hebel,
I'd like you to do a code review. Please visit
https://review.coreboot.org/c/coreboot/+/68749
to review the following change.
Change subject: [WIP] util/cbmem: add parsing of TPM logs per standard ......................................................................
[WIP] util/cbmem: add parsing of TPM logs per standard
coreboot is made to export the range allocated for the log. The range is helpful as there is no easy way to determine the size of the log from its header without parsing vendor info.
Change-Id: Ib76dc7dec56dd1789a219539a1ac05a958f47a5c Ticket: https://ticket.coreboot.org/issues/425 Signed-off-by: Krystian Hebel krystian.hebel@3mdeb.com Signed-off-by: Michał Żygowski michal.zygowski@3mdeb.com Signed-off-by: Sergii Dmytruk sergii.dmytruk@3mdeb.com --- M src/commonlib/bsd/include/commonlib/bsd/cbmem_id.h M src/commonlib/include/commonlib/coreboot_tables.h M src/commonlib/include/commonlib/tpm_log_defs.h M src/lib/coreboot_table.c M src/security/tpm/tspi.h M src/security/tpm/tspi/log-tpm12.c M src/security/tpm/tspi/log-tpm2.c M util/cbmem/cbmem.c 8 files changed, 339 insertions(+), 2 deletions(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/49/68749/1
diff --git a/src/commonlib/bsd/include/commonlib/bsd/cbmem_id.h b/src/commonlib/bsd/include/commonlib/bsd/cbmem_id.h index 6970c63..4c56e0a 100644 --- a/src/commonlib/bsd/include/commonlib/bsd/cbmem_id.h +++ b/src/commonlib/bsd/include/commonlib/bsd/cbmem_id.h @@ -62,6 +62,7 @@ #define CBMEM_ID_TCPA_LOG 0x54435041 /* TPM log in coreboot-specific format */ #define CBMEM_ID_TCPA_TCG_LOG 0x54445041 /* TPM1 ACPI log */ #define CBMEM_ID_TPM_LOG 0x54504D4C /* TPM log per specification (either one) */ +#define CBMEM_ID_TPM_LOG_REF 0x544C4752 /* Memory range allocated for CBMEM_ID_TPM_LOG */ #define CBMEM_ID_TIMESTAMP 0x54494d45 #define CBMEM_ID_TPM2_TCG_LOG 0x54504d32 /* TPM2 ACPI log */ #define CBMEM_ID_TPM_PPI 0x54505049 @@ -139,6 +140,7 @@ { CBMEM_ID_TCPA_LOG, "TCPA LOG " }, \ { CBMEM_ID_TCPA_TCG_LOG, "TCPA TCGLOG" }, \ { CBMEM_ID_TPM_LOG, "TPM STD LOG" }, \ + { CBMEM_ID_TPM_LOG_REF, "TPM LOG REF" }, \ { CBMEM_ID_TIMESTAMP, "TIME STAMP " }, \ { CBMEM_ID_TPM2_TCG_LOG, "TPM2 TCGLOG" }, \ { CBMEM_ID_TPM_PPI, "TPM PPI " }, \ diff --git a/src/commonlib/include/commonlib/coreboot_tables.h b/src/commonlib/include/commonlib/coreboot_tables.h index 3f7ff2d..ce215cd 100644 --- a/src/commonlib/include/commonlib/coreboot_tables.h +++ b/src/commonlib/include/commonlib/coreboot_tables.h @@ -87,6 +87,7 @@ LB_TAG_TYPE_C_INFO = 0x0042, LB_TAG_ACPI_RSDP = 0x0043, LB_TAG_PCIE = 0x0044, + LB_TAG_TPM_STD_LOG = 0x0045, /* The following options are CMOS-related */ LB_TAG_CMOS_OPTION_TABLE = 0x00c8, LB_TAG_OPTION = 0x00c9, diff --git a/src/commonlib/include/commonlib/tpm_log_defs.h b/src/commonlib/include/commonlib/tpm_log_defs.h index 18dc82b..97746b4 100644 --- a/src/commonlib/include/commonlib/tpm_log_defs.h +++ b/src/commonlib/include/commonlib/tpm_log_defs.h @@ -6,8 +6,29 @@ #include <stdint.h> #include <commonlib/helpers.h>
+#define TCPA_SPEC_ID_EVENT_SIGNATURE "Spec ID Event00" #define TCG_EFI_SPEC_ID_EVENT_SIGNATURE "Spec ID Event03"
+struct tcpa_log_entry { + uint32_t pcr; + uint32_t event_type; + uint8_t digest[20]; + uint32_t event_data_size; + uint8_t event[0]; +} __packed; + +struct tcpa_spec_entry { + struct tcpa_log_entry entry; + uint8_t signature[16]; + uint32_t platform_class; + uint8_t spec_version_minor; + uint8_t spec_version_major; + uint8_t spec_errata; + uint8_t reserved; + uint8_t vendor_info_size; + uint8_t vendor_info[0]; +} __packed; + #define TPM2_ALG_ERROR 0x0000 #define TPM2_ALG_HMAC 0x0005 #define TPM2_ALG_NULL 0x0010 @@ -45,6 +66,28 @@ #define EV_NONHOST_INFO 0x00000011 #define EV_OMIT_BOOT_DEVICE_EVENTS 0x00000012
+union tpm_hash_digest { + uint8_t sha1[SHA1_DIGEST_SIZE]; + uint8_t sha256[SHA256_DIGEST_SIZE]; + uint8_t sm3_256[SM3_256_DIGEST_SIZE]; + uint8_t sha384[SHA384_DIGEST_SIZE]; + uint8_t sha512[SHA512_DIGEST_SIZE]; +}; + +struct tpm_hash_algorithm { + uint16_t hashAlg; + union tpm_hash_digest digest; +} __packed; + +struct tcg_pcr_event2_header { + uint32_t pcr_index; + uint32_t event_type; + uint32_t digest_count; + uint8_t digests[0]; + /* uint32_t event_size; */ + /* uint8_t event[0]; */ +} __packed; + struct tpm_digest_sizes { uint16_t alg_id; uint16_t digest_size; @@ -67,4 +110,26 @@ /* uint8_t vendor_info[vendor_info_size]; */ } __packed;
+static const char *tpm_event_types[] __maybe_unused = { + [EV_PREBOOT_CERT] = "Reserved", + [EV_POST_CODE] = "POST code", + [EV_UNUSED] = "Unused", + [EV_NO_ACTION] = "No action", + [EV_SEPARATOR] = "Separator", + [EV_ACTION] = "Action", + [EV_EVENT_TAG] = "Event tag", + [EV_S_CRTM_CONTENTS] = "S-CRTM contents", + [EV_S_CRTM_VERSION] = "S-CRTM version", + [EV_CPU_MICROCODE] = "CPU microcode", + [EV_PLATFORM_CONFIG_FLAGS] = "Platform configuration flags", + [EV_TABLE_OF_DEVICES] = "Table of devices", + [EV_COMPACT_HASH] = "Compact hash", + [EV_IPL] = "IPL", + [EV_IPL_PARTITION_DATA] = "IPL partition data", + [EV_NONHOST_CODE] = "Non-host code", + [EV_NONHOST_CONFIG] = "Non-host configuration", + [EV_NONHOST_INFO] = "Non-host information", + [EV_OMIT_BOOT_DEVICE_EVENTS] = "Omit boot device events", +}; + #endif diff --git a/src/lib/coreboot_table.c b/src/lib/coreboot_table.c index 2a7ccc5..d4b3bd9 100644 --- a/src/lib/coreboot_table.c +++ b/src/lib/coreboot_table.c @@ -23,6 +23,7 @@ #include <spi_flash.h> #include <smmstore.h> #include <types.h> +#include <security/tpm/tspi.h>
#if CONFIG(USE_OPTION_TABLE) #include <option_table.h> @@ -289,6 +290,25 @@ } }
+static void lb_tpm_std_log(struct lb_header *header) +{ + struct lb_range *lb_range; + const struct tcpa_log_ref *range = cbmem_find(CBMEM_ID_TPM_LOG_REF); + + if (range == NULL) + return; /* The section is not present */ + + lb_range = (struct lb_range *)lb_new_record(header); + if (lb_range == NULL) { + printk(BIOS_ERR, "No more room in coreboot table!\n"); + return; + } + lb_range->tag = LB_TAG_TPM_STD_LOG; + lb_range->size = sizeof(*lb_range); + lb_range->range_start = range->start; + lb_range->range_size = range->size; +} + static struct lb_mainboard *lb_mainboard(struct lb_header *header) { struct lb_record *rec; @@ -488,6 +508,9 @@ /* Serialize resource map into mem table types (LB_MEM_*) */ bootmem_write_memory_table(lb_memory(head));
+ /* Record reference to TPM log composed according to specification (either one) */ + lb_tpm_std_log(head); + /* Record our motherboard */ lb_mainboard(head);
diff --git a/src/security/tpm/tspi.h b/src/security/tpm/tspi.h index 20b97f4..fe2a396 100644 --- a/src/security/tpm/tspi.h +++ b/src/security/tpm/tspi.h @@ -18,6 +18,11 @@ #define TPM_PCR_MAX_LEN 64 #define HASH_DATA_CHUNK_SIZE 1024
+struct tcpa_log_ref { + uint64_t start; + uint32_t size; +}; + /** * Get the pointer to the single instance of global * tcpa log data, and initialize it when necessary diff --git a/src/security/tpm/tspi/log-tpm12.c b/src/security/tpm/tspi/log-tpm12.c index f110978..ab9197f 100644 --- a/src/security/tpm/tspi/log-tpm12.c +++ b/src/security/tpm/tspi/log-tpm12.c @@ -26,6 +26,7 @@ if (cbmem_possibly_online()) { size_t tcpa_log_len; struct spec_id_event_data *hdr; + struct tcpa_log_ref *tcpa_ref;
tclt = cbmem_find(CBMEM_ID_TPM_LOG); if (tclt) @@ -51,6 +52,10 @@
tclt->max_entries = MAX_TCPA_LOG_ENTRIES; tclt->num_entries = 0; + + tcpa_ref = cbmem_add(CBMEM_ID_TPM_LOG_REF, sizeof(*tcpa_ref)); + tcpa_ref->start = (uintptr_t)tclt; + tcpa_ref->size = tcpa_log_len; }
return tclt; diff --git a/src/security/tpm/tspi/log-tpm2.c b/src/security/tpm/tspi/log-tpm2.c index 1d911db..e5b6bd5 100644 --- a/src/security/tpm/tspi/log-tpm2.c +++ b/src/security/tpm/tspi/log-tpm2.c @@ -48,6 +48,7 @@ if (cbmem_possibly_online()) { size_t tcpa_log_len; struct tcg_efi_spec_id_event *hdr; + struct tcpa_log_ref *tcpa_ref;
tclt = cbmem_find(CBMEM_ID_TPM_LOG); if (tclt) @@ -77,6 +78,10 @@ tclt->vendor_info_size = sizeof(tclt->max_entries) + sizeof(tclt->num_entries); tclt->max_entries = MAX_TCPA_LOG_ENTRIES; tclt->num_entries = 0; + + tcpa_ref = cbmem_add(CBMEM_ID_TPM_LOG_REF, sizeof(*tcpa_ref)); + tcpa_ref->start = (uintptr_t)tclt; + tcpa_ref->size = tcpa_log_len; }
return tclt; diff --git a/util/cbmem/cbmem.c b/util/cbmem/cbmem.c index c537a7a..694c9e6 100644 --- a/util/cbmem/cbmem.c +++ b/util/cbmem/cbmem.c @@ -22,6 +22,7 @@ #include <commonlib/loglevel.h> #include <commonlib/timestamp_serialized.h> #include <commonlib/tcpa_log_serialized.h> +#include <commonlib/tpm_log_defs.h> #include <commonlib/coreboot_tables.h>
#ifdef __OpenBSD__ @@ -268,6 +269,7 @@ static struct lb_cbmem_ref timestamps; static struct lb_cbmem_ref console; static struct lb_cbmem_ref tcpa_log; +static struct lb_range tpm_std_log; static struct lb_memory_range cbmem;
/* This is a work-around for a nasty problem introduced by initially having @@ -291,6 +293,13 @@ return ret; }
+static struct lb_range parse_range(const struct lb_range *range) +{ + struct lb_range ret; + aligned_memcpy(&ret, range, sizeof(ret)); + return ret; +} + static void parse_memory_tags(const struct lb_memory *mem) { int num_entries; @@ -346,6 +355,11 @@ debug(" Found TSC info.\n"); tsc_freq_khz = ((struct lb_tsc_info *)lbr_p)->freq_khz; continue; + case LB_TAG_TPM_STD_LOG: { + debug(" Found TPM standard log table.\n"); + tpm_std_log = parse_range((struct lb_range *)lbr_p); + continue; + } case LB_TAG_FORWARD: { int ret; /* @@ -843,6 +857,202 @@ unmap_memory(×tamp_mapping); }
+static void print_hex(uint8_t *hex, size_t len) +{ + unsigned int i; + for (i = 0; i < len; i++) + printf("%02x", *(hex + i)); + printf("\n"); +} + +static void parse_tpm12_log(const struct tcpa_spec_entry *spec_log) +{ + static uint8_t zero_block[sizeof(struct tcpa_spec_entry)]; + + uintptr_t current; + uint32_t counter = 0; + + printf("TCPA log:\n"); + printf("\tSpecification: %d.%d%d", + spec_log->spec_version_major, + spec_log->spec_version_minor, + spec_log->spec_errata); + printf("\tPlatform class: %s\n", + le32toh(spec_log->platform_class) == 0 ? "PC Client" : + le32toh(spec_log->platform_class) == 1 ? "Server" : "Unknown"); + + current = (uintptr_t)&spec_log->vendor_info[spec_log->vendor_info_size]; + while (memcmp((const void *)current, (const void *)zero_block, sizeof(zero_block))) { + uint32_t len; + struct tcpa_log_entry *log_entry = (void *)current; + uint32_t event_type = le32toh(log_entry->event_type); + + printf("TCPA log entry %u:\n", ++counter); + printf("\tPCR: %d\n", le32toh(log_entry->pcr)); + if (event_type >= ARRAY_SIZE(tpm_event_types)) + printf("\tEvent type: Unknown (0x%x)\n", event_type); + else + printf("\tEvent type: %s\n", tpm_event_types[event_type]); + printf("\tDigest: "); + print_hex(log_entry->digest, SHA1_DIGEST_SIZE); + current += sizeof(struct tcpa_log_entry); + len = le32toh(log_entry->event_data_size); + if (len != 0) { + current += len; + printf("\tEvent data: %.*s\n", len, log_entry->event); + } else { + printf("\tEvent data not provided\n"); + } + } +} + +static uint32_t print_tpm2_digests(struct tcg_pcr_event2_header *log_entry) +{ + unsigned int i; + uintptr_t current = (uintptr_t)log_entry->digests; + + for (i = 0; i < le32toh(log_entry->digest_count); i++) { + struct tpm_hash_algorithm *hash = (struct tpm_hash_algorithm *)current; + switch (le16toh(hash->hashAlg)) { + case TPM2_ALG_SHA1: + printf("\t\t SHA1: "); + print_hex(hash->digest.sha1, SHA1_DIGEST_SIZE); + current += sizeof(hash->hashAlg) + SHA1_DIGEST_SIZE; + break; + case TPM2_ALG_SHA256: + printf("\t\t SHA256: "); + print_hex(hash->digest.sha256, SHA256_DIGEST_SIZE); + current += sizeof(hash->hashAlg) + SHA256_DIGEST_SIZE; + break; + case TPM2_ALG_SHA384: + printf("\t\t SHA384: "); + print_hex(hash->digest.sha384, SHA384_DIGEST_SIZE); + current += sizeof(hash->hashAlg) + SHA384_DIGEST_SIZE; + break; + case TPM2_ALG_SHA512: + printf("\t\t SHA512: "); + print_hex(hash->digest.sha512, SHA512_DIGEST_SIZE); + current += sizeof(hash->hashAlg) + SHA512_DIGEST_SIZE; + break; + case TPM2_ALG_SM3_256: + printf("\t\t SM3: "); + print_hex(hash->digest.sm3_256, SM3_256_DIGEST_SIZE); + current += sizeof(hash->hashAlg) + SM3_256_DIGEST_SIZE; + break; + default: + die("Unknown hash algorithm\n"); + } + } + + return current - (uintptr_t)&log_entry->digest_count; +} + +static void parse_tpm2_log(const struct tcg_efi_spec_id_event *tpm2_log) +{ + static uint8_t zero_block[12]; /* Only PCR index, event type and digest count */ + + uintptr_t current; + uint32_t counter = 0; + + printf("TPM2 log:\n"); + printf("\tSpecification: %d.%d%d\n", + tpm2_log->spec_version_major, + tpm2_log->spec_version_minor, + tpm2_log->spec_errata); + printf("\tPlatform class: %s\n", + le32toh(tpm2_log->platform_class) == 0 ? "PC Client" : + le32toh(tpm2_log->platform_class) == 1 ? "Server" : "Unknown"); + + /* Start after the first variable-sized part of the header */ + current = (uintptr_t)&tpm2_log->digest_sizes[le32toh(tpm2_log->num_of_algorithms)]; + /* current is at `uint8_t vendor_info_size` here */ + current += 1 + *(uint8_t *)current; + + while (memcmp((const void *)current, (const void *)zero_block, sizeof(zero_block))) { + uint32_t len; + struct tcg_pcr_event2_header *log_entry = (void *)current; + uint32_t event_type = le32toh(log_entry->event_type); + + printf("TPM2 log entry %u:\n", ++counter); + printf("\tPCR: %d\n", le32toh(log_entry->pcr_index)); + if (event_type >= ARRAY_SIZE(tpm_event_types)) + printf("\tEvent type: Unknown (0x%x)\n", event_type); + else + printf("\tEvent type: %s\n", tpm_event_types[event_type]); + + current = (uintptr_t)&log_entry->digest_count; + if (le32toh(log_entry->digest_count) > 0) { + printf("\tDigests:\n"); + current += print_tpm2_digests(log_entry); + } else { + printf("\tNo digests in this log entry\n"); + current += sizeof(log_entry->digest_count); + } + /* Now event size and event are left to be parsed */ + len = le32toh(*(uint32_t *)current); + current += sizeof(uint32_t); + if (len != 0) { + printf("\tEvent data: %d %.*s\n", len, len, (const char *)current); + current += len; + } else { + printf("\tEvent data not provided\n"); + } + } +} + +/* Dump the tcpa log table in format from specification */ +static void dump_tcpa_std_log(void) +{ + const struct tcpa_spec_entry *tspec_entry; + const struct tcg_efi_spec_id_event *tcg_spec_entry; + uint64_t addr; + size_t size; + struct mapping log_mapping; + + if (tpm_std_log.tag != LB_TAG_TPM_STD_LOG) { + fprintf(stderr, "No TPM log found in coreboot table.\n"); + return; + } + + addr = tpm_std_log.range_start; + size = tpm_std_log.range_size; + + tspec_entry = map_memory(&log_mapping, addr, size); + if (!tspec_entry) + die("Unable to map TCPA log header\n"); + + if (!strcmp((const char *)tspec_entry->signature, TCPA_SPEC_ID_EVENT_SIGNATURE)) { + if (tspec_entry->spec_version_major == 1 && + tspec_entry->spec_version_minor == 2 && + tspec_entry->spec_errata >= 1 && + le32toh(tspec_entry->entry.event_type) == EV_NO_ACTION) { + parse_tpm12_log(tspec_entry); + } else { + fprintf(stderr, "Unknown TCPA log specification\n"); + } + unmap_memory(&log_mapping); + return; + } + + unmap_memory(&log_mapping); + tcg_spec_entry = map_memory(&log_mapping, addr, size); + + if (!strcmp((const char *)tcg_spec_entry->signature, TCG_EFI_SPEC_ID_EVENT_SIGNATURE)) { + if (tcg_spec_entry->spec_version_major == 2 && + tcg_spec_entry->spec_version_minor == 0 && + le32toh(tcg_spec_entry->event_type) == EV_NO_ACTION) { + parse_tpm2_log(tcg_spec_entry); + } else { + fprintf(stderr, "Unknown TPM2 log specification.\n"); + } + unmap_memory(&log_mapping); + return; + } + + fprintf(stderr, "Unknown TPM log specification: %s\n", + (const char *)tcg_spec_entry->signature); +} + /* dump the tcpa log table */ static void dump_tcpa_log(void) { @@ -1676,8 +1886,12 @@ if (timestamp_type != TIMESTAMPS_PRINT_NONE) dump_timestamps(timestamp_type);
- if (print_tcpa_log) - dump_tcpa_log(); + if (print_tcpa_log) { + if (tpm_std_log.tag != LB_TAG_UNUSED) + dump_tcpa_std_log(); + else + dump_tcpa_log(); + }
unmap_memory(&lbtable_mapping);