/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __LINUX_TPM_EVENTLOG_H__
#define __LINUX_TPM_EVENTLOG_H__
#include <linux/tpm.h>
#define TCG_EVENT_NAME_LEN_MAX 255
#define MAX_TEXT_EVENT 1000 /* Max event string length */
#define ACPI_TCPA_SIG "TCPA" /* 0x41504354 /'TCPA' */
#define EFI_TCG2_EVENT_LOG_FORMAT_TCG_1_2 0x1
#define EFI_TCG2_EVENT_LOG_FORMAT_TCG_2 0x2
#ifdef CONFIG_PPC64
#define do_endian_conversion(x) be32_to_cpu(x)
#else
#define do_endian_conversion(x) x
#endif
enum bios_platform_class {
BIOS_CLIENT = 0x00,
BIOS_SERVER = 0x01,
};
struct tcpa_event {
u32 pcr_index;
u32 event_type;
u8 pcr_value[20]; /* SHA1 */
u32 event_size;
u8 event_data[];
};
enum tcpa_event_types {
PREBOOT = 0,
POST_CODE,
UNUSED,
NO_ACTION,
SEPARATOR,
ACTION,
EVENT_TAG,
SCRTM_CONTENTS,
SCRTM_VERSION,
CPU_MICROCODE,
PLATFORM_CONFIG_FLAGS,
TABLE_OF_DEVICES,
COMPACT_HASH,
IPL,
IPL_PARTITION_DATA,
NONHOST_CODE,
NONHOST_CONFIG,
NONHOST_INFO,
};
struct tcpa_pc_event {
u32 event_id;
u32 event_size;
u8 event_data[];
};
enum tcpa_pc_event_ids {
SMBIOS = 1,
BIS_CERT,
POST_BIOS_ROM,
ESCD,
CMOS,
NVRAM,
OPTION_ROM_EXEC,
OPTION_ROM_CONFIG,
OPTION_ROM_MICROCODE = 10,
S_CRTM_VERSION,
S_CRTM_CONTENTS,
POST_CONTENTS,
HOST_TABLE_OF_DEVICES,
};
/* http://www.trustedcomputinggroup.org/tcg-efi-protocol-specification/ */
struct tcg_efi_specid_event_algs {
u16 alg_id;
u16 digest_size;
} __packed;
#define TCG_SPECID_SIG "Spec ID Event03"
struct tcg_efi_specid_event_head {
u8 signature[16];
u32 platform_class;
u8 spec_version_minor;
u8 spec_version_major;
u8 spec_errata;
u8 uintnsize;
u32 num_algs;
struct tcg_efi_specid_event_algs digest_sizes[];
} __packed;
struct tcg_pcr_event {
u32 pcr_idx;
u32 event_type;
u8 digest[20];
u32 event_size;
u8 event[];
} __packed;
struct tcg_event_field {
u32 event_size;
u8 event[];
} __packed;
struct tcg_pcr_event2_head {
u32 pcr_idx;
u32 event_type;
u32 count;
struct tpm_digest digests[];
} __packed;
struct tcg_algorithm_size {
u16 algorithm_id;
u16 algorithm_size;
};
struct tcg_algorithm_info {
u8 signature[16];
u32 platform_class;
u8 spec_version_minor;
u8 spec_version_major;
u8 spec_errata;
u8 uintn_size;
u32 number_of_algorithms;
struct tcg_algorithm_size digest_sizes[];
};
#ifndef TPM_MEMREMAP
#define TPM_MEMREMAP(start, size) NULL
#endif
#ifndef TPM_MEMUNMAP
#define TPM_MEMUNMAP(start, size) do{} while(0)
#endif
/**
* __calc_tpm2_event_size - calculate the size of a TPM2 event log entry
* @event: Pointer to the event whose size should be calculated
* @event_header: Pointer to the initial event containing the digest lengths
* @do_mapping: Whether or not the event needs to be mapped
*
* The TPM2 event log format can contain multiple digests corresponding to
* separate PCR banks, and also contains a variable length of the data that
* was measured. This requires knowledge of how long each digest type is,
* and this information is contained within the first event in the log.
*
* We calculate the length by examining the number of events, and then looking
* at each event in turn to determine how much space is used for events in
* total. Once we've done this we know the offset of the data length field,
* and can calculate the total size of the event.
*
* Return: size of the event on success, 0 on failure
*/
static __always_inline int __calc_tpm2_event_size(struct tcg_pcr_event2_head *event,
struct tcg_pcr_event *event_header,
bool do_mapping)
{
struct tcg_efi_specid_event_head *efispecid;
struct tcg_event_field *event_field;
void *mapping = NULL;
int mapping_size;
void *marker;
void *marker_start;
u32 halg_size;
size_t size;
u16 halg;
int i;
int j;
u32 count, event_type;
const u8 zero_digest[sizeof(event_header->digest)] = {0};
marker = event;
marker_start = marker;
marker = marker + sizeof(event->pcr_idx) + sizeof(event->event_type)
+ sizeof(event->count);
/* Map the event header */
if (do_mapping) {
mapping_size = marker - marker_start;
mapping = TPM_MEMREMAP((unsigned long)marker_start,
mapping_size);
if (!mapping) {
size = 0;
goto out;
}
} else {
mapping = marker_start;
}
event = (struct tcg_pcr_event2_head *)mapping;
/*
* The loop below will unmap these fields if the log is larger than
* one page, so save them here for reference:
*/
count = READ_ONCE(event->count);
event_type = READ_ONCE(event->event_type);
/* Verify that it's the log header */
if (event_header->pcr_idx != 0 ||
event_header->event_type != NO_ACTION ||
memcmp(event_header->digest, zero_digest, sizeof(zero_digest))) {
size = 0;
goto out;
}
efispecid = (struct tcg_efi_specid_event_head *)event_header->event;
/*
* Perform validation of the event in order to identify malformed
* events. This function may be asked to parse arbitrary byte sequences
* immediately following a valid event log. The caller expects this
* function to recognize that the byte sequence is not a valid event
* and to return an event size of 0.
*/
if (memcmp(efispecid->signature, TCG_SPECID_SIG,
sizeof(TCG_SPECID_SIG)) ||
!efispecid->num_algs || count != efispecid->num_algs) {
size = 0;
goto out;
}
for (i = 0; i < count; i++) {
halg_size = sizeof(event->digests[i].alg_id);
/* Map the digest's algorithm identifier */
if (do_mapping) {
TPM_MEMUNMAP(mapping, mapping_size);
mapping_size = halg_size;
mapping = TPM_MEMREMAP((unsigned long)marker,
mapping_size);
if (!mapping) {
size = 0;
goto out;
}
} else {
mapping = marker;
}
memcpy(&halg, mapping, halg_size);
marker = marker + halg_size;
for (j = 0; j < efispecid->num_algs; j++) {
if (halg == efispecid->digest_sizes[j].alg_id) {
marker +=
efispecid->digest_sizes[j].digest_size;
break;
}
}
/* Algorithm without known length. Such event is unparseable. */
if (j == efispecid->num_algs) {
size = 0;
goto out;
}
}
/*
* Map the event size - we don't read from the event itself, so
* we don't need to map it
*/
if (do_mapping) {
TPM_MEMUNMAP(mapping, mapping_size);
mapping_size += sizeof(event_field->event_size);
mapping = TPM_MEMREMAP((unsigned long)marker,
mapping_size);
if (!mapping) {
size = 0;
goto out;
}
} else {
mapping = marker;
}
event_field = (struct tcg_event_field *)mapping;
marker = marker + sizeof(event_field->event_size)
+ event_field->event_size;
size = marker - marker_start;
if (event_type == 0 && event_field->event_size == 0)
size = 0;
out:
if (do_mapping)
TPM_MEMUNMAP(mapping, mapping_size);
return size;
}
#endif