162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2016 IBM Corporation
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Authors:
662306a36Sopenharmony_ci *      Nayna Jain <nayna@linux.vnet.ibm.com>
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Access to TPM 2.0 event log as written by Firmware.
962306a36Sopenharmony_ci * It assumes that writer of event log has followed TCG Specification
1062306a36Sopenharmony_ci * for Family "2.0" and written the event data in little endian.
1162306a36Sopenharmony_ci * With that, it doesn't need any endian conversion for structure
1262306a36Sopenharmony_ci * content.
1362306a36Sopenharmony_ci */
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include <linux/seq_file.h>
1662306a36Sopenharmony_ci#include <linux/fs.h>
1762306a36Sopenharmony_ci#include <linux/security.h>
1862306a36Sopenharmony_ci#include <linux/module.h>
1962306a36Sopenharmony_ci#include <linux/slab.h>
2062306a36Sopenharmony_ci#include <linux/tpm_eventlog.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#include "../tpm.h"
2362306a36Sopenharmony_ci#include "common.h"
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci/*
2662306a36Sopenharmony_ci * calc_tpm2_event_size() - calculate the event size, where event
2762306a36Sopenharmony_ci * is an entry in the TPM 2.0 event log. The event is of type Crypto
2862306a36Sopenharmony_ci * Agile Log Entry Format as defined in TCG EFI Protocol Specification
2962306a36Sopenharmony_ci * Family "2.0".
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci * @event: event whose size is to be calculated.
3262306a36Sopenharmony_ci * @event_header: the first event in the event log.
3362306a36Sopenharmony_ci *
3462306a36Sopenharmony_ci * Returns size of the event. If it is an invalid event, returns 0.
3562306a36Sopenharmony_ci */
3662306a36Sopenharmony_cistatic size_t calc_tpm2_event_size(struct tcg_pcr_event2_head *event,
3762306a36Sopenharmony_ci				   struct tcg_pcr_event *event_header)
3862306a36Sopenharmony_ci{
3962306a36Sopenharmony_ci	return __calc_tpm2_event_size(event, event_header, false);
4062306a36Sopenharmony_ci}
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cistatic void *tpm2_bios_measurements_start(struct seq_file *m, loff_t *pos)
4362306a36Sopenharmony_ci{
4462306a36Sopenharmony_ci	struct tpm_chip *chip = m->private;
4562306a36Sopenharmony_ci	struct tpm_bios_log *log = &chip->log;
4662306a36Sopenharmony_ci	void *addr = log->bios_event_log;
4762306a36Sopenharmony_ci	void *limit = log->bios_event_log_end;
4862306a36Sopenharmony_ci	struct tcg_pcr_event *event_header;
4962306a36Sopenharmony_ci	struct tcg_pcr_event2_head *event;
5062306a36Sopenharmony_ci	size_t size;
5162306a36Sopenharmony_ci	int i;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	event_header = addr;
5462306a36Sopenharmony_ci	size = struct_size(event_header, event, event_header->event_size);
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	if (*pos == 0) {
5762306a36Sopenharmony_ci		if (addr + size < limit) {
5862306a36Sopenharmony_ci			if ((event_header->event_type == 0) &&
5962306a36Sopenharmony_ci			    (event_header->event_size == 0))
6062306a36Sopenharmony_ci				return NULL;
6162306a36Sopenharmony_ci			return SEQ_START_TOKEN;
6262306a36Sopenharmony_ci		}
6362306a36Sopenharmony_ci	}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	if (*pos > 0) {
6662306a36Sopenharmony_ci		addr += size;
6762306a36Sopenharmony_ci		event = addr;
6862306a36Sopenharmony_ci		size = calc_tpm2_event_size(event, event_header);
6962306a36Sopenharmony_ci		if ((addr + size >=  limit) || (size == 0))
7062306a36Sopenharmony_ci			return NULL;
7162306a36Sopenharmony_ci	}
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	for (i = 0; i < (*pos - 1); i++) {
7462306a36Sopenharmony_ci		event = addr;
7562306a36Sopenharmony_ci		size = calc_tpm2_event_size(event, event_header);
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci		if ((addr + size >= limit) || (size == 0))
7862306a36Sopenharmony_ci			return NULL;
7962306a36Sopenharmony_ci		addr += size;
8062306a36Sopenharmony_ci	}
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	return addr;
8362306a36Sopenharmony_ci}
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_cistatic void *tpm2_bios_measurements_next(struct seq_file *m, void *v,
8662306a36Sopenharmony_ci					 loff_t *pos)
8762306a36Sopenharmony_ci{
8862306a36Sopenharmony_ci	struct tcg_pcr_event *event_header;
8962306a36Sopenharmony_ci	struct tcg_pcr_event2_head *event;
9062306a36Sopenharmony_ci	struct tpm_chip *chip = m->private;
9162306a36Sopenharmony_ci	struct tpm_bios_log *log = &chip->log;
9262306a36Sopenharmony_ci	void *limit = log->bios_event_log_end;
9362306a36Sopenharmony_ci	size_t event_size;
9462306a36Sopenharmony_ci	void *marker;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	(*pos)++;
9762306a36Sopenharmony_ci	event_header = log->bios_event_log;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	if (v == SEQ_START_TOKEN) {
10062306a36Sopenharmony_ci		event_size = struct_size(event_header, event,
10162306a36Sopenharmony_ci					 event_header->event_size);
10262306a36Sopenharmony_ci		marker = event_header;
10362306a36Sopenharmony_ci	} else {
10462306a36Sopenharmony_ci		event = v;
10562306a36Sopenharmony_ci		event_size = calc_tpm2_event_size(event, event_header);
10662306a36Sopenharmony_ci		if (event_size == 0)
10762306a36Sopenharmony_ci			return NULL;
10862306a36Sopenharmony_ci		marker = event;
10962306a36Sopenharmony_ci	}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	marker = marker + event_size;
11262306a36Sopenharmony_ci	if (marker >= limit)
11362306a36Sopenharmony_ci		return NULL;
11462306a36Sopenharmony_ci	v = marker;
11562306a36Sopenharmony_ci	event = v;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	event_size = calc_tpm2_event_size(event, event_header);
11862306a36Sopenharmony_ci	if (((v + event_size) >= limit) || (event_size == 0))
11962306a36Sopenharmony_ci		return NULL;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	return v;
12262306a36Sopenharmony_ci}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_cistatic void tpm2_bios_measurements_stop(struct seq_file *m, void *v)
12562306a36Sopenharmony_ci{
12662306a36Sopenharmony_ci}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_cistatic int tpm2_binary_bios_measurements_show(struct seq_file *m, void *v)
12962306a36Sopenharmony_ci{
13062306a36Sopenharmony_ci	struct tpm_chip *chip = m->private;
13162306a36Sopenharmony_ci	struct tpm_bios_log *log = &chip->log;
13262306a36Sopenharmony_ci	struct tcg_pcr_event *event_header = log->bios_event_log;
13362306a36Sopenharmony_ci	struct tcg_pcr_event2_head *event = v;
13462306a36Sopenharmony_ci	void *temp_ptr;
13562306a36Sopenharmony_ci	size_t size;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	if (v == SEQ_START_TOKEN) {
13862306a36Sopenharmony_ci		size = struct_size(event_header, event,
13962306a36Sopenharmony_ci				   event_header->event_size);
14062306a36Sopenharmony_ci		temp_ptr = event_header;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci		if (size > 0)
14362306a36Sopenharmony_ci			seq_write(m, temp_ptr, size);
14462306a36Sopenharmony_ci	} else {
14562306a36Sopenharmony_ci		size = calc_tpm2_event_size(event, event_header);
14662306a36Sopenharmony_ci		temp_ptr = event;
14762306a36Sopenharmony_ci		if (size > 0)
14862306a36Sopenharmony_ci			seq_write(m, temp_ptr, size);
14962306a36Sopenharmony_ci	}
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	return 0;
15262306a36Sopenharmony_ci}
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ciconst struct seq_operations tpm2_binary_b_measurements_seqops = {
15562306a36Sopenharmony_ci	.start = tpm2_bios_measurements_start,
15662306a36Sopenharmony_ci	.next = tpm2_bios_measurements_next,
15762306a36Sopenharmony_ci	.stop = tpm2_bios_measurements_stop,
15862306a36Sopenharmony_ci	.show = tpm2_binary_bios_measurements_show,
15962306a36Sopenharmony_ci};
160