util/cbmem: add parsing of TPM logs per specs
CBMEM can contain log in different forms (at most one is present): - coreboot-specific format (CBMEM_ID_TPM_CB_LOG exported as LB_TAG_TPM_CB_LOG) - TPM1.2 format (CBMEM_ID_TCPA_TCG_LOG) - TPM2 format (CBMEM_ID_TPM2_TCG_LOG) The last two follow specifications by Trusted Computing Group, but until now cbmem couldn't print them. These formats were added not so long ago in: - commit 4191dbf0c9a5 ("security/tpm: add TPM log format as per 1.2 spec") - commit 53db677586e3 ("security/tpm: add TPM log format as per 2.0 spec") These changes make cbmem utility check for existence of TPM1.2/TPM2 logs in CBMEM and add code necessary for parsing and printing of their entries. TEST=`cbmem -L` for CONFIG_TPM1=y case TCPA log: Specification: 1.21 Platform class: PC Client TCPA log entry 1: PCR: 2 Event type: Action Digest: 5622416ea417186aa1ac32b32c527ac09009fb5e Event data: FMAP: FMAP TEST=`cbmem -L` for CONFIG_TPM2=y case TPM2 log: Specification: 2.00 Platform class: PC Client TPM2 log entry 1: PCR: 2 Event type: Action Digests: SHA256: 68d27f08cb261463a6d004524333ac5db1a3c2166721785a6061327b6538657c Event data: FMAP: FMAP 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> Reviewed-on: https://review.coreboot.org/c/coreboot/+/68749 Reviewed-by: Paul Menzel <paulepanter@mailbox.org> Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
This commit is contained in:
parent
6d169aabbd
commit
6da62684de
@ -9,6 +9,26 @@
|
||||
#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
|
||||
@ -56,6 +76,28 @@ struct spec_id_event_data {
|
||||
uint8_t vendor_info_size;
|
||||
} __packed;
|
||||
|
||||
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;
|
||||
@ -78,4 +120,26 @@ struct tcg_efi_spec_id_event {
|
||||
/* 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
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include <assert.h>
|
||||
#include <regex.h>
|
||||
#include <commonlib/bsd/cbmem_id.h>
|
||||
#include <commonlib/bsd/tpm_log_defs.h>
|
||||
#include <commonlib/loglevel.h>
|
||||
#include <commonlib/timestamp_serialized.h>
|
||||
#include <commonlib/tpm_log_serialized.h>
|
||||
@ -841,6 +842,227 @@ static void timestamp_add_now(uint32_t timestamp_id)
|
||||
unmap_memory(×tamp_mapping);
|
||||
}
|
||||
|
||||
static bool can_print(const uint8_t *data, size_t len)
|
||||
{
|
||||
unsigned int i;
|
||||
for (i = 0; i < len; i++) {
|
||||
if (!isprint(data[i]) && !isspace(data[i])) {
|
||||
/* If printable prefix is followed by zeroes, this is a valid string */
|
||||
for (; i < len; i++) {
|
||||
if (data[i] != 0)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void print_hex_string(const uint8_t *hex, size_t len)
|
||||
{
|
||||
unsigned int i;
|
||||
for (i = 0; i < len; i++)
|
||||
printf("%02x", hex[i]);
|
||||
}
|
||||
|
||||
static void print_hex_line(const uint8_t *hex, size_t len)
|
||||
{
|
||||
print_hex_string(hex, len);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static void print_event_type(uint32_t event_type)
|
||||
{
|
||||
unsigned int known_event_count = ARRAY_SIZE(tpm_event_types);
|
||||
if (event_type >= known_event_count)
|
||||
printf("Unknown (0x%x >= %u)", event_type, known_event_count);
|
||||
else
|
||||
printf("%s", tpm_event_types[event_type]);
|
||||
}
|
||||
|
||||
static void parse_tpm12_log(const struct tcpa_spec_entry *spec_log)
|
||||
{
|
||||
const uint8_t zero_block[sizeof(struct tcpa_spec_entry)] = {0};
|
||||
|
||||
uintptr_t current;
|
||||
uint32_t counter = 0;
|
||||
|
||||
printf("TCPA log:\n");
|
||||
printf("\tSpecification: %d.%d%d\n",
|
||||
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));
|
||||
printf("\tEvent type: ");
|
||||
print_event_type(event_type);
|
||||
printf("\n");
|
||||
printf("\tDigest: ");
|
||||
print_hex_line(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: ");
|
||||
if (can_print(log_entry->event, len))
|
||||
printf("%.*s\n", len, log_entry->event);
|
||||
else
|
||||
print_hex_line(log_entry->event, len);
|
||||
} 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_line(hash->digest.sha1, SHA1_DIGEST_SIZE);
|
||||
current += sizeof(hash->hashAlg) + SHA1_DIGEST_SIZE;
|
||||
break;
|
||||
case TPM2_ALG_SHA256:
|
||||
printf("\t\t SHA256: ");
|
||||
print_hex_line(hash->digest.sha256, SHA256_DIGEST_SIZE);
|
||||
current += sizeof(hash->hashAlg) + SHA256_DIGEST_SIZE;
|
||||
break;
|
||||
case TPM2_ALG_SHA384:
|
||||
printf("\t\t SHA384: ");
|
||||
print_hex_line(hash->digest.sha384, SHA384_DIGEST_SIZE);
|
||||
current += sizeof(hash->hashAlg) + SHA384_DIGEST_SIZE;
|
||||
break;
|
||||
case TPM2_ALG_SHA512:
|
||||
printf("\t\t SHA512: ");
|
||||
print_hex_line(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_line(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)
|
||||
{
|
||||
const uint8_t zero_block[12] = {0}; /* 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));
|
||||
printf("\tEvent type: ");
|
||||
print_event_type(event_type);
|
||||
printf("\n");
|
||||
|
||||
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: %.*s\n", len, (const char *)current);
|
||||
current += len;
|
||||
} else {
|
||||
printf("\tEvent data not provided\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Dump the TPM log table in format defined by specifications */
|
||||
static void dump_tpm_std_log(uint64_t addr, size_t size)
|
||||
{
|
||||
const void *event_log;
|
||||
const struct tcpa_spec_entry *tspec_entry;
|
||||
const struct tcg_efi_spec_id_event *tcg_spec_entry;
|
||||
struct mapping log_mapping;
|
||||
|
||||
event_log = map_memory(&log_mapping, addr, size);
|
||||
if (!event_log)
|
||||
die("Unable to map TPM eventlog\n");
|
||||
|
||||
tspec_entry = event_log;
|
||||
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 TPM1.2 log specification\n");
|
||||
}
|
||||
unmap_memory(&log_mapping);
|
||||
return;
|
||||
}
|
||||
|
||||
tcg_spec_entry = event_log;
|
||||
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",
|
||||
(int)sizeof(tcg_spec_entry->signature),
|
||||
(const char *)tcg_spec_entry->signature);
|
||||
|
||||
unmap_memory(&log_mapping);
|
||||
}
|
||||
|
||||
/* dump the TPM CB log table */
|
||||
static void dump_tpm_cb_log(void)
|
||||
{
|
||||
@ -872,16 +1094,25 @@ static void dump_tpm_cb_log(void)
|
||||
const struct tpm_cb_log_entry *tce = &tclt_p->entries[i];
|
||||
|
||||
printf(" PCR-%u ", tce->pcr);
|
||||
|
||||
for (uint32_t j = 0; j < tce->digest_length; j++)
|
||||
printf("%02x", tce->digest[j]);
|
||||
|
||||
print_hex_string(tce->digest, tce->digest_length);
|
||||
printf(" %s [%s]\n", tce->digest_type, tce->name);
|
||||
}
|
||||
|
||||
unmap_memory(&log_mapping);
|
||||
}
|
||||
|
||||
static void dump_tpm_log(void)
|
||||
{
|
||||
uint64_t start;
|
||||
size_t size;
|
||||
|
||||
if (!find_cbmem_entry(CBMEM_ID_TCPA_TCG_LOG, &start, &size) ||
|
||||
!find_cbmem_entry(CBMEM_ID_TPM2_TCG_LOG, &start, &size))
|
||||
dump_tpm_std_log(start, size);
|
||||
else
|
||||
dump_tpm_cb_log();
|
||||
}
|
||||
|
||||
struct cbmem_console {
|
||||
u32 size;
|
||||
u32 cursor;
|
||||
@ -1675,7 +1906,7 @@ int main(int argc, char** argv)
|
||||
dump_timestamps(timestamp_type);
|
||||
|
||||
if (print_tcpa_log)
|
||||
dump_tpm_cb_log();
|
||||
dump_tpm_log();
|
||||
|
||||
unmap_memory(&lbtable_mapping);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user