aboutsummaryrefslogtreecommitdiffstats
path: root/roms/SLOF/lib/libtpm/tcgbios.c
diff options
context:
space:
mode:
Diffstat (limited to 'roms/SLOF/lib/libtpm/tcgbios.c')
-rw-r--r--roms/SLOF/lib/libtpm/tcgbios.c1477
1 files changed, 1477 insertions, 0 deletions
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;
+ }
+ }
+}