From af1a266670d040d2f4083ff309d732d648afba2a Mon Sep 17 00:00:00 2001
From: Angelos Mouzakitis <a.mouzakitis@virtualopensystems.com>
Date: Tue, 10 Oct 2023 14:33:42 +0000
Subject: Add submodule dependency files

Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec
---
 roms/SLOF/lib/libtpm/tcgbios.c | 1477 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 1477 insertions(+)
 create mode 100644 roms/SLOF/lib/libtpm/tcgbios.c

(limited to 'roms/SLOF/lib/libtpm/tcgbios.c')

diff --git a/roms/SLOF/lib/libtpm/tcgbios.c b/roms/SLOF/lib/libtpm/tcgbios.c
new file mode 100644
index 000000000..e43745e42
--- /dev/null
+++ b/roms/SLOF/lib/libtpm/tcgbios.c
@@ -0,0 +1,1477 @@
+/*****************************************************************************
+ * Copyright (c) 2015-2020 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ *     IBM Corporation - initial implementation
+ *     Stefan Berger, stefanb@linux.ibm.com
+ *     Kevin O'Connor, kevin@koconnor.net
+ *****************************************************************************/
+
+/*
+ *  Implementation of the TPM BIOS extension according to the specification
+ *  described in the IBM VTPM Firmware document and the TCG Specification
+ *  that can be found here under the following link:
+ *  https://trustedcomputinggroup.org/resource/pc-client-work-group-specific-implementation-specification-for-conventional-bios/
+ */
+
+#include <stddef.h>
+#include <stdlib.h>
+
+#include "types.h"
+#include "byteorder.h"
+#include "tpm_drivers.h"
+#include "string.h"
+#include "tcgbios.h"
+#include "tcgbios_int.h"
+#include "stdio.h"
+#include "sha.h"
+#include "helpers.h"
+#include "version.h"
+#include "OF.h"
+#include "libelf.h"
+
+#undef TCGBIOS_DEBUG
+//#define TCGBIOS_DEBUG
+#ifdef TCGBIOS_DEBUG
+#define dprintf(_x ...) do { printf("TCGBIOS: " _x); } while(0)
+#else
+#define dprintf(_x ...)
+#endif
+
+static struct {
+	unsigned tpm_probed:1;
+	unsigned tpm_found:1;
+	unsigned tpm_working:1;
+
+	/* base address of the log area */
+	uint8_t *log_base;
+
+	/* size of the logging area */
+	size_t log_area_size;
+
+	/* where to write the next log entry to */
+	uint8_t *log_area_next_entry;
+
+	/* PCR selection as received from TPM */
+	uint32_t tpm20_pcr_selection_size;
+	struct tpml_pcr_selection *tpm20_pcr_selection;
+} tpm_state;
+
+#define TPM2_ALG_SHA1_FLAG          (1 << 0)
+#define TPM2_ALG_SHA256_FLAG        (1 << 1)
+#define TPM2_ALG_SHA384_FLAG        (1 << 2)
+#define TPM2_ALG_SHA512_FLAG        (1 << 3)
+#define TPM2_ALG_SM3_256_FLAG       (1 << 4)
+#define TPM2_ALG_SHA3_256_FLAG      (1 << 5)
+#define TPM2_ALG_SHA3_384_FLAG      (1 << 6)
+#define TPM2_ALG_SHA3_512_FLAG      (1 << 7)
+
+static const uint8_t ZeroGuid[16] = { 0 };
+
+static UEFI_GPT_DATA *uefi_gpt_data;
+static size_t uefi_gpt_data_size;
+
+/*
+ * TPM 2 logs are written in little endian format.
+ */
+static inline uint32_t log32_to_cpu(uint32_t val)
+{
+	return le32_to_cpu(val);
+}
+
+static inline uint32_t cpu_to_log32(uint32_t val)
+{
+	return cpu_to_le32(val);
+}
+
+static inline uint16_t cpu_to_log16(uint16_t val)
+{
+	return cpu_to_le16(val);
+}
+
+/********************************************************
+  Extensions for TCG-enabled BIOS
+ *******************************************************/
+
+static void probe_tpm(void)
+{
+	tpm_state.tpm_probed = true;
+	tpm_state.tpm_found = spapr_is_vtpm_present();
+	tpm_state.tpm_working = tpm_state.tpm_found;
+}
+
+/****************************************************************
+ * Digest formatting
+ ****************************************************************/
+
+/* A 'struct tpm_log_entry' is a local data structure containing a
+ * 'TCG_PCR_EVENT2_Header' followed by space for the maximum supported
+ * digest. The digest is a series of TPMT_HA structs on tpm2.0.
+ */
+struct tpm_log_entry {
+	TCG_PCR_EVENT2_Header hdr;
+	uint8_t pad[sizeof(struct TPML_DIGEST_VALUES)
+	   + 8 * sizeof(struct TPMT_HA)
+	   + SHA1_BUFSIZE + SHA256_BUFSIZE + SHA384_BUFSIZE
+	   + SHA512_BUFSIZE + SM3_256_BUFSIZE + SHA3_256_BUFSIZE
+	   + SHA3_384_BUFSIZE + SHA3_512_BUFSIZE];
+} __attribute__((packed));
+
+static const struct hash_parameters {
+	uint16_t hashalg;
+	uint8_t  hashalg_flag;
+	uint8_t  hash_buffersize;
+	const char *name;
+	void (*hashfunc)(const uint8_t *data, uint32_t length, uint8_t *hash);
+} hash_parameters[] = {
+	{
+		.hashalg = TPM2_ALG_SHA1,
+		.hashalg_flag = TPM2_ALG_SHA1_FLAG,
+		.hash_buffersize = SHA1_BUFSIZE,
+		.name = "SHA1",
+		.hashfunc = sha1,
+	}, {
+		.hashalg = TPM2_ALG_SHA256,
+		.hashalg_flag = TPM2_ALG_SHA256_FLAG,
+		.hash_buffersize = SHA256_BUFSIZE,
+		.name = "SHA256",
+		.hashfunc = sha256,
+	}, {
+		.hashalg = TPM2_ALG_SHA384,
+		.hashalg_flag = TPM2_ALG_SHA384_FLAG,
+		.hash_buffersize = SHA384_BUFSIZE,
+		.name = "SHA384",
+		.hashfunc = sha384,
+	}, {
+		.hashalg = TPM2_ALG_SHA512,
+		.hashalg_flag = TPM2_ALG_SHA512_FLAG,
+		.hash_buffersize = SHA512_BUFSIZE,
+		.name = "SHA512",
+		.hashfunc = sha512,
+	}, {
+		.hashalg = TPM2_ALG_SM3_256,
+		.hashalg_flag = TPM2_ALG_SM3_256_FLAG,
+		.hash_buffersize = SM3_256_BUFSIZE,
+		.name = "SM3-256",
+	}, {
+		.hashalg = TPM2_ALG_SHA3_256,
+		.hashalg_flag = TPM2_ALG_SHA3_256_FLAG,
+		.hash_buffersize = SHA3_256_BUFSIZE,
+		.name = "SHA3-256",
+	}, {
+		.hashalg = TPM2_ALG_SHA3_384,
+		.hashalg_flag = TPM2_ALG_SHA3_384_FLAG,
+		.hash_buffersize = SHA3_384_BUFSIZE,
+		.name = "SHA3-384",
+	}, {
+		.hashalg = TPM2_ALG_SHA3_512,
+		.hashalg_flag = TPM2_ALG_SHA3_512_FLAG,
+		.hash_buffersize = SHA3_512_BUFSIZE,
+		.name = "SHA3-512",
+	}
+};
+
+static const struct hash_parameters *tpm20_find_by_hashalg(uint16_t hashAlg)
+{
+	unsigned i;
+
+	for (i = 0; i < ARRAY_SIZE(hash_parameters); i++) {
+		if (hash_parameters[i].hashalg == hashAlg)
+			return &hash_parameters[i];
+	}
+	return NULL;
+}
+
+static const struct hash_parameters *
+tpm20_find_by_hashalg_flag(uint16_t hashalg_flag)
+{
+	unsigned i;
+
+	for (i = 0; i < ARRAY_SIZE(hash_parameters); i++) {
+		if (hash_parameters[i].hashalg_flag == hashalg_flag)
+			return &hash_parameters[i];
+	}
+	return NULL;
+}
+
+static inline int tpm20_get_hash_buffersize(uint16_t hashAlg)
+{
+	const struct hash_parameters *hp = tpm20_find_by_hashalg(hashAlg);
+
+	if (hp)
+		return hp->hash_buffersize;
+	return -1;
+}
+
+static inline uint8_t tpm20_hashalg_to_flag(uint16_t hashAlg)
+{
+	const struct hash_parameters *hp = tpm20_find_by_hashalg(hashAlg);
+
+	if (hp)
+		return hp->hashalg_flag;
+	return 0;
+}
+
+static uint16_t tpm20_hashalg_flag_to_hashalg(uint8_t hashalg_flag)
+{
+	const struct hash_parameters *hp;
+
+	hp = tpm20_find_by_hashalg_flag(hashalg_flag);
+	if (hp)
+		return hp->hashalg;
+	return 0;
+}
+
+static const char * tpm20_hashalg_flag_to_name(uint8_t hashalg_flag)
+{
+	const struct hash_parameters *hp;
+
+	hp = tpm20_find_by_hashalg_flag(hashalg_flag);
+	if (hp)
+		return hp->name;
+	return NULL;
+}
+
+static void tpm2_hash_data(uint16_t hashAlg,
+                           const uint8_t *data, uint32_t data_len,
+                           uint8_t *hash)
+{
+	unsigned i;
+
+	for (i = 0; i < ARRAY_SIZE(hash_parameters); i++) {
+		if (hash_parameters[i].hashalg == hashAlg) {
+			if (hash_parameters[i].hashfunc) {
+				hash_parameters[i].hashfunc(data, data_len,
+							    hash);
+			} else {
+				memset(hash, 0xff,
+				       hash_parameters[i].hash_buffersize);
+			}
+		}
+	}
+}
+
+/*
+ * Build the TPM2 TPML_DIGEST_VALUES data structure from the given hash.
+ * Follow the PCR bank configuration of the TPM and write the same hash
+ * in either truncated or zero-padded form in the areas of all the other
+ * hashes. For example, write the sha256 hash in the area of the sha384
+ * hash and fill the remaining bytes with zeros. Or truncate the sha256
+ * hash when writing it in the area of the sha1 hash.
+ *
+ * le: the log entry to build the digest in
+ * hashdata: the data to hash
+ * hashdata_len: the length of the hashdata
+ * bigEndian: whether to build in big endian format for the TPM or log
+ *            little endian for the log (TPM 2.0)
+ *
+ * Returns the digest size; -1 on fatal error
+ */
+static int tpm20_build_digest(struct tpm_log_entry *le,
+                              const uint8_t *hashdata, uint32_t hashdata_len,
+			      bool bigEndian)
+{
+	struct tpms_pcr_selection *sel;
+	void *nsel, *end;
+	void *dest = le->hdr.digests + sizeof(struct TPML_DIGEST_VALUES);
+	uint32_t count, numAlgs;
+	struct TPMT_HA *v;
+	struct TPML_DIGEST_VALUES *vs;
+
+	sel = tpm_state.tpm20_pcr_selection->selections;
+	end = (void *)tpm_state.tpm20_pcr_selection +
+		tpm_state.tpm20_pcr_selection_size;
+
+	for (count = 0, numAlgs = 0;
+	     count < be32_to_cpu(tpm_state.tpm20_pcr_selection->count);
+	     count++) {
+		int hsize;
+		uint8_t sizeOfSelect = sel->sizeOfSelect;
+
+		nsel = (void*)sel + sizeof(*sel) + sizeOfSelect;
+		if (nsel > end)
+			break;
+
+		/* PCR 0-7 unused ? -- skip */
+		if (!sizeOfSelect || sel->pcrSelect[0] == 0) {
+			sel = nsel;
+			continue;
+		}
+
+		hsize = tpm20_get_hash_buffersize(be16_to_cpu(sel->hashAlg));
+		if (hsize < 0) {
+			dprintf("TPM is using an unsupported hash: %d\n",
+				be16_to_cpu(sel->hashAlg));
+			return -1;
+		}
+
+		/* buffer size sanity check before writing */
+		v = dest;
+		if (dest + sizeof(*v) + hsize > (void*)le + sizeof(*le)) {
+			dprintf("tpm_log_entry is too small\n");
+			return -1;
+		}
+
+		if (bigEndian)
+			v->hashAlg = sel->hashAlg;
+		else
+			v->hashAlg = cpu_to_le16(be16_to_cpu(sel->hashAlg));
+
+		tpm2_hash_data(be16_to_cpu(sel->hashAlg), hashdata, hashdata_len,
+			       v->hash);
+
+		dest += sizeof(*v) + hsize;
+		sel = nsel;
+
+		numAlgs++;
+	}
+
+	if (sel != end) {
+		dprintf("Malformed pcr selection structure fron TPM\n");
+		return -1;
+	}
+
+	vs = (void*)le->hdr.digests;
+	if (bigEndian)
+		vs->count = cpu_to_be32(numAlgs);
+	else
+		vs->count = cpu_to_le32(numAlgs);
+
+	return dest - (void*)le->hdr.digests;
+}
+
+/****************************************************************
+ * TPM hardware command wrappers
+ ****************************************************************/
+
+/* Helper function for sending TPM commands that take a single
+ * optional parameter (0, 1, or 2 bytes) and have no special response.
+ */
+static int
+tpm_simple_cmd(uint8_t locty, uint32_t ordinal, int param_size, uint16_t param,
+	       enum tpm_duration_type to_t)
+{
+	struct {
+		struct tpm_req_header trqh;
+		uint16_t param;
+	} __attribute__((packed)) req = {
+		.trqh.totlen = cpu_to_be32(sizeof(req.trqh) + param_size),
+		.trqh.tag = cpu_to_be16(TPM2_ST_NO_SESSIONS),
+		.trqh.ordinal = cpu_to_be32(ordinal),
+	};
+	uint8_t obuffer[64];
+	struct tpm_rsp_header *trsh = (void *)obuffer;
+	uint32_t obuffer_len = sizeof(obuffer);
+	int ret;
+
+	switch (param_size) {
+	case 2:
+		req.param = cpu_to_be16(param);
+		break;
+	case 1:
+		*(uint8_t *)&req.param = param;
+		break;
+	}
+
+	memset(obuffer, 0, sizeof(obuffer));
+	ret = spapr_transmit(locty, &req.trqh, obuffer, &obuffer_len, to_t);
+	ret = ret ? -1 : (int) be32_to_cpu(trsh->errcode);
+	dprintf("Return from tpm_simple_cmd(%x, %x) = %x\n",
+		ordinal, param, ret);
+
+	return ret;
+}
+
+static int
+tpm20_getcapability(uint32_t capability, uint32_t property, uint32_t count,
+	            struct tpm_rsp_header *rsp, uint32_t rsize)
+{
+	struct tpm2_req_getcapability trg = {
+		.hdr.tag = cpu_to_be16(TPM2_ST_NO_SESSIONS),
+		.hdr.totlen = cpu_to_be32(sizeof(trg)),
+		.hdr.ordinal = cpu_to_be32(TPM2_CC_GetCapability),
+		.capability = cpu_to_be32(capability),
+		.property = cpu_to_be32(property),
+		.propertycount = cpu_to_be32(count),
+	};
+	uint32_t resp_size = rsize;
+	int ret;
+
+	ret = spapr_transmit(0, &trg.hdr, rsp, &resp_size,
+			     TPM_DURATION_TYPE_SHORT);
+	ret = (ret ||
+	       rsize < be32_to_cpu(rsp->totlen)) ? -1
+						 : (int) be32_to_cpu(rsp->errcode);
+
+	dprintf("TCGBIOS: Return value from sending TPM2_CC_GetCapability = 0x%08x\n",
+		ret);
+
+	return ret;
+}
+
+static int
+tpm20_get_pcrbanks(void)
+{
+	uint8_t buffer[128];
+	uint32_t size;
+	struct tpm2_res_getcapability *trg =
+		(struct tpm2_res_getcapability *)&buffer;
+	uint32_t resplen;
+	int ret;
+
+	ret = tpm20_getcapability(TPM2_CAP_PCRS, 0, 8, &trg->hdr,
+				  sizeof(buffer));
+	if (ret)
+		return ret;
+
+	/* defend against (broken) TPM sending packets that are too short */
+	resplen = be32_to_cpu(trg->hdr.totlen);
+	if (resplen <= offset_of(struct tpm2_res_getcapability, data))
+		return -1;
+
+	size = resplen - offset_of(struct tpm2_res_getcapability, data);
+	/* we need a valid tpml_pcr_selection up to and including sizeOfSelect*/
+	if (size < offset_of(struct tpml_pcr_selection, selections) +
+		   offset_of(struct tpms_pcr_selection, pcrSelect))
+		return -1;
+
+	tpm_state.tpm20_pcr_selection = SLOF_alloc_mem(size);
+	if (tpm_state.tpm20_pcr_selection) {
+		memcpy(tpm_state.tpm20_pcr_selection, &trg->data, size);
+		tpm_state.tpm20_pcr_selection_size = size;
+	} else {
+		printf("TCGBIOS: Failed to allocated %u bytes.\n", size);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int tpm20_extend(struct tpm_log_entry *le, int digest_len)
+{
+	struct tpm2_req_extend tmp_tre = {
+		.hdr.tag     = cpu_to_be16(TPM2_ST_SESSIONS),
+		.hdr.totlen  = cpu_to_be32(0),
+		.hdr.ordinal = cpu_to_be32(TPM2_CC_PCR_Extend),
+		.pcrindex    = cpu_to_be32(log32_to_cpu(le->hdr.pcrindex)),
+		.authblocksize = cpu_to_be32(sizeof(tmp_tre.authblock)),
+		.authblock = {
+			.handle = cpu_to_be32(TPM2_RS_PW),
+			.noncesize = cpu_to_be16(0),
+			.contsession = TPM2_YES,
+			.pwdsize = cpu_to_be16(0),
+		},
+	};
+	uint8_t buffer[sizeof(tmp_tre) + sizeof(le->pad)];
+	struct tpm2_req_extend *tre = (struct tpm2_req_extend *)buffer;
+	struct tpm_rsp_header rsp;
+	uint32_t resp_length = sizeof(rsp);
+	int ret;
+
+	memcpy(tre, &tmp_tre, sizeof(tmp_tre));
+	memcpy(&tre->digest[0], le->hdr.digests, digest_len);
+
+	tre->hdr.totlen = cpu_to_be32(sizeof(tmp_tre) + digest_len);
+
+	ret = spapr_transmit(0, &tre->hdr, &rsp, &resp_length,
+			     TPM_DURATION_TYPE_SHORT);
+	if (ret || resp_length != sizeof(rsp) || rsp.errcode)
+		return -1;
+
+	return 0;
+}
+
+static int tpm20_stirrandom(void)
+{
+	struct tpm2_req_stirrandom stir = {
+		.hdr.tag = cpu_to_be16(TPM2_ST_NO_SESSIONS),
+		.hdr.totlen = cpu_to_be32(sizeof(stir)),
+		.hdr.ordinal = cpu_to_be32(TPM2_CC_StirRandom),
+		.size = cpu_to_be16(sizeof(stir.stir)),
+		.stir = rand(),
+	};
+	struct tpm_rsp_header rsp;
+	uint32_t resp_length = sizeof(rsp);
+	int ret = spapr_transmit(0, &stir.hdr, &rsp, &resp_length,
+				 TPM_DURATION_TYPE_SHORT);
+
+	if (ret || resp_length != sizeof(rsp) || rsp.errcode)
+		ret = -1;
+
+	dprintf("TCGBIOS: Return value from sending TPM2_CC_StirRandom = 0x%08x\n",
+		ret);
+
+	return ret;
+}
+
+static int tpm20_getrandom(uint8_t *buf, uint16_t buf_len)
+{
+	struct tpm2_res_getrandom rsp;
+	struct tpm2_req_getrandom trgr = {
+		.hdr.tag = cpu_to_be16(TPM2_ST_NO_SESSIONS),
+		.hdr.totlen = cpu_to_be32(sizeof(trgr)),
+		.hdr.ordinal = cpu_to_be32(TPM2_CC_GetRandom),
+		.bytesRequested = cpu_to_be16(buf_len),
+	};
+	uint32_t resp_length = sizeof(rsp);
+	int ret;
+
+	if (buf_len > sizeof(rsp.rnd.buffer))
+		return -1;
+
+	ret = spapr_transmit(0, &trgr.hdr, &rsp, &resp_length,
+			     TPM_DURATION_TYPE_MEDIUM);
+	if (ret || resp_length != sizeof(rsp) || rsp.hdr.errcode)
+		ret = -1;
+	else
+		memcpy(buf, rsp.rnd.buffer, buf_len);
+
+	dprintf("TCGBIOS: Return value from sending TPM2_CC_GetRandom = 0x%08x\n",
+		ret);
+
+	return ret;
+}
+
+static int tpm20_hierarchychangeauth(uint8_t auth[20])
+{
+	struct tpm2_req_hierarchychangeauth trhca = {
+		.hdr.tag = cpu_to_be16(TPM2_ST_SESSIONS),
+		.hdr.totlen = cpu_to_be32(sizeof(trhca)),
+		.hdr.ordinal = cpu_to_be32(TPM2_CC_HierarchyChangeAuth),
+		.authhandle = cpu_to_be32(TPM2_RH_PLATFORM),
+		.authblocksize = cpu_to_be32(sizeof(trhca.authblock)),
+		.authblock = {
+			.handle = cpu_to_be32(TPM2_RS_PW),
+			.noncesize = cpu_to_be16(0),
+			.contsession = TPM2_YES,
+			.pwdsize = cpu_to_be16(0),
+		},
+		.newAuth = {
+			.size = cpu_to_be16(sizeof(trhca.newAuth.buffer)),
+		},
+	};
+	struct tpm_rsp_header rsp;
+	uint32_t resp_length = sizeof(rsp);
+	int ret;
+
+	memcpy(trhca.newAuth.buffer, auth, sizeof(trhca.newAuth.buffer));
+
+	ret = spapr_transmit(0, &trhca.hdr, &rsp, &resp_length,
+			     TPM_DURATION_TYPE_MEDIUM);
+	if (ret || resp_length != sizeof(rsp) || rsp.errcode)
+		ret = -1;
+
+	dprintf("TCGBIOS: Return value from sending TPM2_CC_HierarchyChangeAuth = 0x%08x\n",
+		ret);
+
+	return ret;
+}
+
+static int tpm20_hierarchycontrol(uint32_t hierarchy, uint8_t state)
+{
+	struct tpm2_req_hierarchycontrol trh = {
+		.hdr.tag = cpu_to_be16(TPM2_ST_SESSIONS),
+		.hdr.totlen = cpu_to_be32(sizeof(trh)),
+		.hdr.ordinal = cpu_to_be32(TPM2_CC_HierarchyControl),
+		.authhandle = cpu_to_be32(TPM2_RH_PLATFORM),
+		.authblocksize = cpu_to_be32(sizeof(trh.authblock)),
+		.authblock = {
+			.handle = cpu_to_be32(TPM2_RS_PW),
+			.noncesize = cpu_to_be16(0),
+			.contsession = TPM2_YES,
+			.pwdsize = cpu_to_be16(0),
+		},
+		.enable = cpu_to_be32(hierarchy),
+		.state = state,
+	};
+	struct tpm_rsp_header rsp;
+	uint32_t resp_length = sizeof(rsp);
+	int ret;
+
+	ret = spapr_transmit(0, &trh.hdr, &rsp, &resp_length,
+			     TPM_DURATION_TYPE_MEDIUM);
+	if (ret || resp_length != sizeof(rsp) || rsp.errcode)
+		ret = -1;
+
+	dprintf("TCGBIOS: Return value from sending TPM2_CC_HierarchyControl = 0x%08x\n",
+		ret);
+
+	return ret;
+}
+
+/****************************************************************
+ * Setup and Measurements
+ ****************************************************************/
+
+bool tpm_is_working(void)
+{
+	if (!tpm_state.tpm_probed)
+		probe_tpm();
+
+	return tpm_state.tpm_working;
+}
+
+static void tpm_set_failure(void)
+{
+	tpm20_hierarchycontrol(TPM2_RH_ENDORSEMENT, TPM2_NO);
+	tpm20_hierarchycontrol(TPM2_RH_OWNER, TPM2_NO);
+
+	tpm_state.tpm_working = false;
+}
+
+/*
+ * Extend the OFDT log with the given entry by copying the
+ * entry data into the log.
+ *
+ * @pcpes: Pointer to the structure to be copied into the log
+ * @event: The event to be appended to 'pcpes'
+ * @event_length: The length of the event
+ *
+ * Returns 0 on success, an error code otherwise.
+ */
+static uint32_t tpm_log_event_long(TCG_PCR_EVENT2_Header *entry,
+				   int digest_len,
+				   const void *event, uint32_t event_length)
+{
+	size_t size, logsize;
+	void *dest;
+	TCG_PCR_EVENT2_Trailer *t;
+
+	dprintf("log base address = %p, next entry = %p\n",
+		tpm_state.log_base, tpm_state.log_area_next_entry);
+
+	if (tpm_state.log_area_next_entry == NULL)
+		return TCGBIOS_LOGOVERFLOW;
+
+	size = sizeof(*entry) + digest_len +
+	       sizeof(TCG_PCR_EVENT2_Trailer) + event_length;
+	logsize = (tpm_state.log_area_next_entry + size -
+	           tpm_state.log_base);
+	if (logsize > tpm_state.log_area_size) {
+		dprintf("TCGBIOS: LOG OVERFLOW: size = %zu\n", size);
+		return TCGBIOS_LOGOVERFLOW;
+	}
+
+	dest = tpm_state.log_area_next_entry;
+	memcpy(dest, entry, sizeof(*entry) + digest_len);
+
+	t = dest + sizeof(*entry) + digest_len;
+	t->eventdatasize = cpu_to_log32(event_length);
+	if (event_length)
+		memcpy(t->event, event, event_length);
+
+	tpm_state.log_area_next_entry += size;
+
+	return 0;
+}
+
+/* Add an entry at the start of the log describing digest formats
+ */
+static int tpm20_write_EfiSpecIdEventStruct(void)
+{
+	struct {
+		struct TCG_EfiSpecIdEventStruct hdr;
+		uint32_t pad[sizeof(struct tpm_log_entry) +
+		             sizeof(uint8_t)];
+	} event = {
+		.hdr.signature = "Spec ID Event03",
+		.hdr.platformClass = TPM_TCPA_ACPI_CLASS_CLIENT,
+		.hdr.specVersionMinor = 0,
+		.hdr.specVersionMajor = 2,
+		.hdr.specErrata = 2,
+		.hdr.uintnSize = 2,
+	};
+	struct tpms_pcr_selection *sel;
+	void *nsel, *end;
+	unsigned event_size;
+	uint8_t *vendorInfoSize;
+	struct tpm_log_entry le = {
+		.hdr.eventtype = cpu_to_log32(EV_NO_ACTION),
+	};
+	uint32_t count, numAlgs;
+
+	sel = tpm_state.tpm20_pcr_selection->selections;
+	end = (void*)tpm_state.tpm20_pcr_selection +
+	      tpm_state.tpm20_pcr_selection_size;
+
+	for (count = 0, numAlgs = 0;
+	     count < be32_to_cpu(tpm_state.tpm20_pcr_selection->count);
+	     count++) {
+		int hsize;
+		uint8_t sizeOfSelect = sel->sizeOfSelect;
+
+		nsel = (void*)sel + sizeof(*sel) + sizeOfSelect;
+		if (nsel > end)
+			break;
+
+		/* PCR 0-7 unused ? -- skip */
+		if (!sizeOfSelect || sel->pcrSelect[0] == 0) {
+			sel = nsel;
+			continue;
+		}
+
+		hsize = tpm20_get_hash_buffersize(be16_to_cpu(sel->hashAlg));
+		if (hsize < 0) {
+			dprintf("TPM is using an unsupported hash: %d\n",
+				be16_to_cpu(sel->hashAlg));
+			return -1;
+		}
+
+		event_size = offset_of(struct TCG_EfiSpecIdEventStruct,
+				       digestSizes[count+1]);
+		if (event_size > sizeof(event) - sizeof(uint8_t)) {
+			dprintf("EfiSpecIdEventStruct pad too small\n");
+			return -1;
+		}
+
+		event.hdr.digestSizes[numAlgs].algorithmId =
+			cpu_to_log16(be16_to_cpu(sel->hashAlg));
+		event.hdr.digestSizes[numAlgs].digestSize = cpu_to_log16(hsize);
+		numAlgs++;
+
+		sel = nsel;
+	}
+
+	if (sel != end) {
+		dprintf("Malformed pcr selection structure fron TPM\n");
+		return -1;
+	}
+
+	event.hdr.numberOfAlgorithms = cpu_to_log32(numAlgs);
+	event_size = offset_of(struct TCG_EfiSpecIdEventStruct,
+			       digestSizes[numAlgs]);
+	vendorInfoSize = (void*)&event + event_size;
+	*vendorInfoSize = 0;
+	event_size += sizeof(*vendorInfoSize);
+
+	return tpm_log_event_long(&le.hdr, SHA1_BUFSIZE, &event, event_size);
+}
+
+static int tpm20_startup(void)
+{
+	int ret;
+
+	ret = tpm_simple_cmd(0, TPM2_CC_Startup,
+			     2, TPM2_SU_CLEAR, TPM_DURATION_TYPE_SHORT);
+	dprintf("TCGBIOS: Return value from sending TPM2_CC_Startup(SU_CLEAR) = 0x%08x\n",
+		ret);
+
+	if (ret)
+		goto err_exit;
+
+	ret = tpm_simple_cmd(0, TPM2_CC_SelfTest,
+			     1, TPM2_YES, TPM_DURATION_TYPE_LONG);
+
+	dprintf("TCGBIOS: Return value from sending TPM2_CC_SELF_TEST = 0x%08x\n",
+		ret);
+
+	if (ret)
+		goto err_exit;
+
+	ret = tpm20_get_pcrbanks();
+	if (ret)
+		goto err_exit;
+
+	/* the log parameters will be passed from Forth layer */
+
+	return 0;
+
+err_exit:
+	dprintf("TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__);
+
+	tpm_set_failure();
+	return -1;
+}
+
+uint32_t tpm_start(void)
+{
+	probe_tpm();
+
+	if (!tpm_is_working()) {
+		dprintf("%s: Machine does not have a working TPM\n",
+			__func__);
+		return TCGBIOS_FATAL_COM_ERROR;
+	}
+
+	return tpm20_startup();
+}
+
+void tpm_finalize(void)
+{
+	spapr_vtpm_finalize();
+}
+
+static void tpm20_prepboot(void)
+{
+	uint8_t auth[20];
+	int ret;
+
+	ret = tpm20_stirrandom();
+	if (ret)
+		 goto err_exit;
+
+	ret = tpm20_getrandom(&auth[0], sizeof(auth));
+	if (ret)
+		goto err_exit;
+
+	ret = tpm20_hierarchychangeauth(auth);
+	if (ret)
+		goto err_exit;
+
+	return;
+
+err_exit:
+	dprintf("TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__);
+
+	tpm_set_failure();
+}
+
+/*
+ * Prepare TPM for boot; this function has to be called before
+ * the firmware transitions to the boot loader.
+ */
+uint32_t tpm_leave_firmware(void)
+{
+	tpm20_prepboot();
+
+	return 0;
+}
+
+/****************************************************************
+ * Forth interface
+ ****************************************************************/
+
+void tpm_set_log_parameters(void *addr, size_t size)
+{
+	int ret;
+
+	dprintf("Log is at 0x%llx; size is %zu bytes\n",
+		(uint64_t)addr, size);
+	tpm_state.log_base = addr;
+	tpm_state.log_area_next_entry = addr;
+	tpm_state.log_area_size = size;
+
+	ret = tpm20_write_EfiSpecIdEventStruct();
+	if (ret)
+		tpm_set_failure();
+}
+
+uint32_t tpm_get_logsize(void)
+{
+	uint32_t logsize = tpm_state.log_area_next_entry - tpm_state.log_base;
+
+	dprintf("log size: %u\n", logsize);
+
+	return logsize;
+}
+
+/*
+ * Add a measurement to the log;
+ *
+ * Input parameters:
+ *  @pcrindex : PCR to extend
+ *  @event_type : type of event
+ *  @info : pointer to info (i.e., string) to be added to the log as-is
+ *  @info_length: length of the info
+ *  @hashdata : pointer to data to be hashed
+ *  @hashdata_length: length of the data
+ *
+ */
+static uint32_t tpm_add_measurement_to_log(uint32_t pcrindex,
+					   uint32_t eventtype,
+					   const char *info,
+					   uint32_t infolen,
+					   const uint8_t *hashdata,
+					   uint32_t hashdatalen)
+{
+	struct tpm_log_entry le = {
+		.hdr.pcrindex = cpu_to_log32(pcrindex),
+		.hdr.eventtype = cpu_to_log32(eventtype),
+	};
+	int digest_len;
+	int ret;
+
+	digest_len = tpm20_build_digest(&le, hashdata, hashdatalen, true);
+	if (digest_len < 0)
+		return TCGBIOS_GENERAL_ERROR;
+	ret = tpm20_extend(&le, digest_len);
+	if (ret) {
+		tpm_set_failure();
+		return TCGBIOS_COMMAND_ERROR;
+	}
+	tpm20_build_digest(&le, hashdata, hashdatalen, false);
+	return tpm_log_event_long(&le.hdr, digest_len, info, infolen);
+}
+
+/*
+ * Measure the contents of a buffer into the given PCR and log it with the
+ * given eventtype. If is_elf is true, try to determine the size of the
+ * ELF file in the buffer and use its size rather than the much larger data
+ * buffer it is held in. In case of failure to detect the ELF file size,
+ * log an error.
+ *
+ * Input parameters:
+ *  @pcrindex : PCR to extend
+ *  @eventtype : type of event
+ *  @data: the buffer to measure
+ *  @datalen: length of the buffer
+ *  @desc: The description to log
+ *  @desclen: The length of the description
+ *  @is_elf: Whether data buffer holds an ELF file and we should determine
+ *           the original file size.
+ *
+ *  Returns 0 on success, an error code otherwise.
+ */
+uint32_t tpm_hash_log_extend_event_buffer(uint32_t pcrindex, uint32_t eventtype,
+					  const void *data, uint64_t datalen,
+					  const char *desc, uint32_t desclen,
+					  bool is_elf)
+{
+	long len;
+	char buf[256];
+
+	if (is_elf) {
+		len = elf_get_file_size(data, datalen);
+		if (len > 0) {
+			datalen = len;
+		} else {
+			snprintf(buf, sizeof(buf), "BAD ELF FILE: %s", desc);
+			return tpm_add_measurement_to_log(pcrindex, eventtype,
+					  buf, strlen(buf),
+					  (uint8_t *)buf, strlen(buf));
+		}
+	}
+	return tpm_add_measurement_to_log(pcrindex, eventtype,
+					  desc, desclen,
+					  data, datalen);
+}
+
+uint32_t tpm_2hash_ext_log(uint32_t pcrindex,
+			   uint32_t eventtype,
+			   const char *info, uint32_t infolen,
+			   const void *data, uint64_t datalen)
+{
+	uint32_t ret;
+
+	ret = tpm_add_measurement_to_log(pcrindex, eventtype,
+					 info, infolen,
+					 data, datalen);
+	if (!ret)
+		return (uint32_t)-1; // TRUE
+	return 0; // FALSE
+}
+
+/*
+ * Add an EV_ACTION measurement to the list of measurements
+ */
+static uint32_t tpm_add_action(uint32_t pcrIndex, const char *string)
+{
+	uint32_t len = strlen(string);
+
+	return tpm_add_measurement_to_log(pcrIndex, EV_ACTION,
+					  string, len, (uint8_t *)string, len);
+}
+
+/*
+ * Add event separators for a range of PCRs
+ */
+uint32_t tpm_add_event_separators(uint32_t start_pcr, uint32_t end_pcr)
+{
+	static const uint8_t evt_separator[] = {0xff,0xff,0xff,0xff};
+	uint32_t pcrIndex;
+	int rc;
+
+	if (!tpm_is_working())
+		return TCGBIOS_GENERAL_ERROR;
+
+	if (start_pcr >= 24 || start_pcr > end_pcr)
+		return TCGBIOS_INVALID_INPUT_PARA;
+
+	/* event separators need to be extended and logged for PCRs 0-7 */
+	for (pcrIndex = start_pcr; pcrIndex <= end_pcr; pcrIndex++) {
+		rc = tpm_add_measurement_to_log(pcrIndex, EV_SEPARATOR,
+						(const char *)evt_separator,
+						sizeof(evt_separator),
+						evt_separator,
+						sizeof(evt_separator));
+		if (rc)
+			return rc;
+	}
+
+	return 0;
+}
+
+uint32_t tpm_measure_bcv_mbr(uint32_t bootdrv, const uint8_t *addr,
+			     uint32_t length)
+{
+	uint32_t rc;
+	const char *string;
+
+	if (!tpm_is_working())
+		return TCGBIOS_GENERAL_ERROR;
+
+	if (length < 0x200)
+		return TCGBIOS_INVALID_INPUT_PARA;
+
+	string = "Booting BCV device 00h (Floppy)";
+	if (bootdrv == BCV_DEVICE_HDD)
+		string = "Booting BCV device 80h (HDD)";
+
+	rc = tpm_add_action(4, string);
+	if (rc)
+		return rc;
+
+	/*
+	 * equivalent to: dd if=/dev/hda ibs=1 count=440 | sha256sum
+	 */
+	string = "MBR";
+	rc = tpm_add_measurement_to_log(4, EV_IPL,
+					string, strlen(string),
+					addr, 0x1b8);
+	if (rc)
+		return rc;
+
+	/*
+	 * equivalent to: dd if=/dev/hda ibs=1 count=72 skip=440 | sha256sum
+	 */
+	string = "MBR PARTITION TABLE";
+	return tpm_add_measurement_to_log(5, EV_IPL_PARTITION_DATA,
+					  string, strlen(string),
+					  addr + 0x1b8, 0x48);
+}
+
+/*
+ * This is the first function to call when measuring a GPT table.
+ * It allocates memory for the data to log which are 'measured' later on.
+ */
+void tpm_gpt_set_lba1(const uint8_t *addr, uint32_t length)
+{
+	if (!tpm_is_working())
+		return;
+
+	SLOF_free_mem(uefi_gpt_data, uefi_gpt_data_size);
+
+	uefi_gpt_data_size = sizeof(UEFI_GPT_DATA);
+	uefi_gpt_data = SLOF_alloc_mem(uefi_gpt_data_size);
+	if (!uefi_gpt_data)
+		return;
+
+	memcpy(&uefi_gpt_data->EfiPartitionHeader,
+	       addr, MIN(sizeof(uefi_gpt_data->EfiPartitionHeader), length));
+	uefi_gpt_data->NumberOfPartitions = 0;
+}
+
+/*
+ * This function adds a GPT entry to the data to measure. It must
+ * be called after tpm_gpt_set_lba1.
+ */
+void tpm_gpt_add_entry(const uint8_t *addr, uint32_t length)
+{
+	size_t sz;
+	UEFI_PARTITION_ENTRY *upe = (void *)addr;
+	void *tmp;
+
+	if (!tpm_is_working() ||
+	    !uefi_gpt_data ||
+	    length < sizeof(*upe) ||
+	    !memcmp(upe->partTypeGuid, ZeroGuid, sizeof(ZeroGuid)))
+		return;
+
+	sz = offset_of(UEFI_GPT_DATA, Partitions) +
+	       (uefi_gpt_data->NumberOfPartitions + 1)
+	       * sizeof(UEFI_PARTITION_ENTRY);
+	if (sz > uefi_gpt_data_size) {
+		tmp = SLOF_alloc_mem(sz);
+		if (!tmp)
+			goto err_no_mem;
+
+		memcpy(tmp, uefi_gpt_data, uefi_gpt_data_size);
+		SLOF_free_mem(uefi_gpt_data, uefi_gpt_data_size);
+		uefi_gpt_data = tmp;
+		uefi_gpt_data_size = sz;
+	}
+
+	memcpy(&uefi_gpt_data->Partitions[uefi_gpt_data->NumberOfPartitions],
+	       addr,
+	       sizeof(UEFI_PARTITION_ENTRY));
+	uefi_gpt_data->NumberOfPartitions++;
+
+	return;
+
+err_no_mem:
+	SLOF_free_mem(uefi_gpt_data, uefi_gpt_data_size);
+	uefi_gpt_data_size = 0;
+	uefi_gpt_data = NULL;
+}
+
+/*
+ * tpm_measure_gpt finally measures the GPT table and adds an entry
+ * to the log.
+ */
+uint32_t tpm_measure_gpt(void)
+{
+	size_t sz;
+
+	if (!tpm_is_working())
+		return TCGBIOS_GENERAL_ERROR;
+
+	sz = offset_of(UEFI_GPT_DATA, Partitions) +
+	     uefi_gpt_data->NumberOfPartitions * sizeof(UEFI_PARTITION_ENTRY);
+
+	return tpm_add_measurement_to_log(5, EV_EFI_GPT_EVENT,
+					  (const char *)uefi_gpt_data, sz,
+					  (const uint8_t *)uefi_gpt_data, sz);
+}
+
+uint32_t tpm_measure_scrtm(void)
+{
+	uint32_t rc, i;
+	char *slof_text_start = (char *)&_slof_text;
+	uint32_t slof_text_length = (long)&_slof_text_end - (long)&_slof_text;
+	const char *scrtm = "S-CRTM Contents";
+#define _TT(a, x) a##x
+#define _T(a, x) _TT(a, x)
+	unsigned short ucs2_version[] = _T(L, RELEASE);
+
+	dprintf("Measure S-CRTM Version: addr = %p, length = %d\n",
+		ucs2_version, ucs2_length);
+
+	for (i = 0; i < ARRAY_SIZE(ucs2_version); ++i)
+	    ucs2_version[i] = cpu_to_le16(ucs2_version[i]);
+
+	rc = tpm_add_measurement_to_log(0, EV_S_CRTM_VERSION,
+					(char *)ucs2_version,
+					sizeof(ucs2_version),
+					(uint8_t *)ucs2_version,
+					sizeof(ucs2_version));
+	if (rc)
+		return rc;
+
+	dprintf("Measure S-CRTM Content (text): start = %p, length = %d\n",
+		slof_text_start, slof_text_length);
+
+	rc = tpm_add_measurement_to_log(0, EV_S_CRTM_CONTENTS,
+					scrtm, strlen(scrtm),
+					(uint8_t *)slof_text_start,
+					slof_text_length);
+
+	return rc;
+}
+
+/*
+ * tpm_driver_get_failure_reason: Function for interfacing with the firmware
+ *                                API
+ */
+uint32_t tpm_driver_get_failure_reason(void)
+{
+	/* do not check for a working TPM here */
+	if (!tpm_state.tpm_found)
+		return VTPM_DRV_STATE_INVALID;
+
+	return spapr_vtpm_get_error();
+}
+
+/*
+ * tpm_driver_set_failure_reason: Function for interfacing with the firmware
+ *                                API
+ */
+void tpm_driver_set_failure_reason(uint32_t errcode)
+{
+	if (!tpm_state.tpm_found)
+		return;
+
+	spapr_vtpm_set_error(errcode);
+}
+
+/****************************************************************
+ * TPM Configuration Menu
+ ****************************************************************/
+
+static int
+tpm20_get_suppt_pcrbanks(uint8_t *suppt_pcrbanks, uint8_t *active_pcrbanks)
+{
+	struct tpms_pcr_selection *sel;
+	void *end;
+
+	*suppt_pcrbanks = 0;
+	*active_pcrbanks = 0;
+
+	sel = tpm_state.tpm20_pcr_selection->selections;
+	end = (void*)tpm_state.tpm20_pcr_selection +
+		tpm_state.tpm20_pcr_selection_size;
+
+	while (1) {
+		uint16_t hashalg;
+		uint8_t hashalg_flag;
+		unsigned i;
+		uint8_t sizeOfSelect = sel->sizeOfSelect;
+		void *nsel = (void*)sel + sizeof(*sel) + sizeOfSelect;
+
+		if (nsel > end)
+			return 0;
+
+		hashalg = be16_to_cpu(sel->hashAlg);
+		hashalg_flag = tpm20_hashalg_to_flag(hashalg);
+
+		*suppt_pcrbanks |= hashalg_flag;
+
+		for (i = 0; i < sizeOfSelect; i++) {
+			if (sel->pcrSelect[i]) {
+				*active_pcrbanks |= hashalg_flag;
+				break;
+			}
+		}
+
+		sel = nsel;
+	}
+}
+
+static int
+tpm20_set_pcrbanks(uint32_t active_banks)
+{
+	struct tpm2_req_pcr_allocate trpa = {
+		.hdr.tag = cpu_to_be16(TPM2_ST_SESSIONS),
+		.hdr.ordinal = cpu_to_be32(TPM2_CC_PCR_Allocate),
+		.authhandle = cpu_to_be32(TPM2_RH_PLATFORM),
+		.authblocksize = cpu_to_be32(sizeof(trpa.authblock)),
+		.authblock = {
+			.handle = cpu_to_be32(TPM2_RS_PW),
+			.noncesize = cpu_to_be16(0),
+			.contsession = TPM2_YES,
+			.pwdsize = cpu_to_be16(0),
+		},
+	};
+	struct tpms_pcr_selection3 {
+		uint16_t hashAlg;
+		uint8_t sizeOfSelect;
+		uint8_t pcrSelect[3];
+	} tps[ARRAY_SIZE(trpa.tpms_pcr_selections)];
+	int i = 0;
+	uint8_t hashalg_flag = TPM2_ALG_SHA1_FLAG;
+	uint8_t dontcare, suppt_banks;
+	struct tpm_rsp_header rsp;
+	uint32_t resp_length = sizeof(rsp);
+	uint16_t hashalg;
+	int ret;
+
+	tpm20_get_suppt_pcrbanks(&suppt_banks, &dontcare);
+
+	while (hashalg_flag) {
+		if ((hashalg_flag & suppt_banks)) {
+			hashalg = tpm20_hashalg_flag_to_hashalg(hashalg_flag);
+
+			if (hashalg) {
+				uint8_t mask = 0;
+
+				tps[i].hashAlg = cpu_to_be16(hashalg);
+				tps[i].sizeOfSelect = 3;
+
+				if (active_banks & hashalg_flag)
+					mask = 0xff;
+
+				tps[i].pcrSelect[0] = mask;
+				tps[i].pcrSelect[1] = mask;
+				tps[i].pcrSelect[2] = mask;
+				i++;
+			}
+		}
+		hashalg_flag <<= 1;
+	}
+
+	trpa.count = cpu_to_be32(i);
+	memcpy(trpa.tpms_pcr_selections, tps, i * sizeof(tps[0]));
+	trpa.hdr.totlen = cpu_to_be32(offset_of(struct tpm2_req_pcr_allocate,
+						tpms_pcr_selections) +
+				      i * sizeof(tps[0]));
+
+	ret = spapr_transmit(0, &trpa.hdr, &rsp, &resp_length,
+			     TPM_DURATION_TYPE_SHORT);
+	ret = ret ? -1 : (int) be32_to_cpu(rsp.errcode);
+
+	return ret;
+}
+
+static int tpm20_activate_pcrbanks(uint32_t active_banks)
+{
+	int ret;
+
+	ret = tpm20_set_pcrbanks(active_banks);
+	if (!ret)
+		ret = tpm_simple_cmd(0, TPM2_CC_Shutdown,
+				     2, TPM2_SU_CLEAR, TPM_DURATION_TYPE_SHORT);
+	if (!ret)
+		SLOF_reset();
+	return ret;
+}
+
+static int
+tpm20_clearcontrol(uint8_t disable)
+{
+	struct tpm2_req_clearcontrol trc = {
+		.hdr.tag     = cpu_to_be16(TPM2_ST_SESSIONS),
+		.hdr.totlen  = cpu_to_be32(sizeof(trc)),
+		.hdr.ordinal = cpu_to_be32(TPM2_CC_ClearControl),
+		.authhandle = cpu_to_be32(TPM2_RH_PLATFORM),
+		.authblocksize = cpu_to_be32(sizeof(trc.authblock)),
+		.authblock = {
+			.handle = cpu_to_be32(TPM2_RS_PW),
+			.noncesize = cpu_to_be16(0),
+			.contsession = TPM2_YES,
+			.pwdsize = cpu_to_be16(0),
+		},
+		.disable = disable,
+	};
+	struct tpm_rsp_header rsp;
+	uint32_t resp_length = sizeof(rsp);
+	int ret;
+
+	ret = spapr_transmit(0, &trc.hdr, &rsp, &resp_length,
+			     TPM_DURATION_TYPE_SHORT);
+	if (ret || resp_length != sizeof(rsp) || rsp.errcode)
+		ret = -1;
+
+	dprintf("TCGBIOS: Return value from sending TPM2_CC_ClearControl = 0x%08x\n",
+		ret);
+
+	return ret;
+}
+
+static int
+tpm20_clear(void)
+{
+	struct tpm2_req_clear trq = {
+		.hdr.tag	 = cpu_to_be16(TPM2_ST_SESSIONS),
+		.hdr.totlen  = cpu_to_be32(sizeof(trq)),
+		.hdr.ordinal = cpu_to_be32(TPM2_CC_Clear),
+		.authhandle = cpu_to_be32(TPM2_RH_PLATFORM),
+		.authblocksize = cpu_to_be32(sizeof(trq.authblock)),
+		.authblock = {
+			.handle = cpu_to_be32(TPM2_RS_PW),
+			.noncesize = cpu_to_be16(0),
+			.contsession = TPM2_YES,
+			.pwdsize = cpu_to_be16(0),
+		},
+	};
+	struct tpm_rsp_header rsp;
+	uint32_t resp_length = sizeof(rsp);
+	int ret;
+
+	ret = spapr_transmit(0, &trq.hdr, &rsp, &resp_length,
+			     TPM_DURATION_TYPE_MEDIUM);
+	if (ret || resp_length != sizeof(rsp) || rsp.errcode)
+		ret = -1;
+
+	dprintf("TCGBIOS: Return value from sending TPM2_CC_Clear = 0x%08x\n",
+		ret);
+
+	return ret;
+}
+
+static int tpm20_menu_change_active_pcrbanks(void)
+{
+	uint8_t active_banks, suppt_banks, activate_banks;
+
+	tpm20_get_suppt_pcrbanks(&suppt_banks, &active_banks);
+
+	activate_banks = active_banks;
+
+	while (1) {
+		uint8_t hashalg_flag = TPM2_ALG_SHA1_FLAG;
+		uint8_t i = 0;
+		uint8_t flagnum;
+		int show = 0;
+
+		printf("\nToggle active PCR banks by pressing number key\n\n");
+
+		while (hashalg_flag) {
+			uint8_t flag = hashalg_flag & suppt_banks;
+			const char *hashname = tpm20_hashalg_flag_to_name(flag);
+
+			i++;
+			if (hashname) {
+				printf("  %d: %s", i, hashname);
+				if (activate_banks & hashalg_flag)
+					printf(" (enabled)");
+				printf("\n");
+			}
+
+			hashalg_flag <<= 1;
+		}
+		printf("\n"
+		       "ESC: return to previous menu without changes\n");
+		if (activate_banks)
+			printf("a  : activate selection\n");
+
+		while (!show) {
+			int key_code = SLOF_get_keystroke();
+
+			switch (key_code) {
+			case ~0:
+				continue;
+			case 27: /* ESC */
+				printf("\n");
+				return -1;
+			case '1' ... '5': /* keys 1 .. 5 */
+				flagnum = key_code - '0';
+				if (flagnum > i)
+					continue;
+				if (suppt_banks & (1 << (flagnum - 1))) {
+					activate_banks ^= 1 << (flagnum - 1);
+					show = 1;
+				}
+				break;
+			case 'a': /* a */
+				if (activate_banks)
+					tpm20_activate_pcrbanks(activate_banks);
+			}
+		}
+	}
+}
+
+void tpm20_menu(void)
+{
+	int key_code;
+	int waitkey;
+	int ret;
+
+	for (;;) {
+		printf("1. Clear TPM\n");
+		printf("2. Change active PCR banks\n");
+
+		printf("\nIf not change is desired or if this menu was reached by "
+		       "mistake, press ESC to\ncontinue the boot.\n");
+
+		waitkey = 1;
+
+		while (waitkey) {
+			key_code = SLOF_get_keystroke();
+			switch (key_code) {
+			case 27:
+				// ESC
+				return;
+			case '1':
+				ret = tpm20_clearcontrol(false);
+				if (!ret)
+					ret = tpm20_clear();
+				if (ret)
+					printf("An error occurred clearing "
+					       "the TPM: 0x%x\n",
+					       ret);
+				break;
+			case '2':
+				tpm20_menu_change_active_pcrbanks();
+				waitkey = 0;
+				continue;
+			default:
+				continue;
+			}
+
+			waitkey = 0;
+		}
+	}
+}
-- 
cgit