aboutsummaryrefslogtreecommitdiffstats
path: root/roms/SLOF/lib/libtpm
diff options
context:
space:
mode:
Diffstat (limited to 'roms/SLOF/lib/libtpm')
-rw-r--r--roms/SLOF/lib/libtpm/Makefile51
-rw-r--r--roms/SLOF/lib/libtpm/Readme57
-rw-r--r--roms/SLOF/lib/libtpm/sha.c232
-rw-r--r--roms/SLOF/lib/libtpm/sha.h23
-rw-r--r--roms/SLOF/lib/libtpm/sha256.c246
-rw-r--r--roms/SLOF/lib/libtpm/sha512.c285
-rw-r--r--roms/SLOF/lib/libtpm/sha_test.h59
-rw-r--r--roms/SLOF/lib/libtpm/tcgbios.c1477
-rw-r--r--roms/SLOF/lib/libtpm/tcgbios.h45
-rw-r--r--roms/SLOF/lib/libtpm/tcgbios_int.h317
-rwxr-xr-xroms/SLOF/lib/libtpm/test.sh31
-rw-r--r--roms/SLOF/lib/libtpm/tpm.code208
-rw-r--r--roms/SLOF/lib/libtpm/tpm.in32
-rw-r--r--roms/SLOF/lib/libtpm/tpm_drivers.c436
-rw-r--r--roms/SLOF/lib/libtpm/tpm_drivers.h82
15 files changed, 3581 insertions, 0 deletions
diff --git a/roms/SLOF/lib/libtpm/Makefile b/roms/SLOF/lib/libtpm/Makefile
new file mode 100644
index 000000000..895dbfdfb
--- /dev/null
+++ b/roms/SLOF/lib/libtpm/Makefile
@@ -0,0 +1,51 @@
+# *****************************************************************************
+# * 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
+# ****************************************************************************/
+
+TOPCMNDIR ?= ../..
+
+CPPFLAGS = -I../libc/include $(CPUARCHDEF) -I$(INCLBRDDIR) \
+ -I$(INCLCMNDIR) -I$(INCLCMNDIR)/$(CPUARCH) -I$(SLOFCMNDIR)
+CPPFLAGS += -I../libhvcall
+CPPFLAGS += $(RELEASE)
+
+LDFLAGS = -nostdlib
+
+TARGET = ../libtpm.a
+
+
+all: $(TARGET)
+
+SRCS = tpm_drivers.c sha.c sha256.c sha512.c tcgbios.c
+
+OBJS = $(SRCS:%.c=%.o)
+
+$(TARGET): $(OBJS)
+ $(AR) -rc $@ $(OBJS)
+ $(RANLIB) $@
+
+clean:
+ $(RM) $(TARGET) $(OBJS)
+
+distclean: clean
+ $(RM) Makefile.dep
+
+
+# Rules for creating the dependency file:
+depend:
+ $(RM) Makefile.dep
+ $(MAKE) Makefile.dep
+
+Makefile.dep: Makefile
+ $(CC) -M $(CPPFLAGS) $(CFLAGS) $(SRCS) $(SRCSS) > Makefile.dep
+
+# Include dependency file if available:
+-include Makefile.dep
diff --git a/roms/SLOF/lib/libtpm/Readme b/roms/SLOF/lib/libtpm/Readme
new file mode 100644
index 000000000..2c362ac07
--- /dev/null
+++ b/roms/SLOF/lib/libtpm/Readme
@@ -0,0 +1,57 @@
+This directory hosts (v)TPM related code.
+
+Background:
+-----------
+
+A TPM is a crypto chip that is found in many systems. Besides it offering
+a secure key store, among other functionality, it is also used to implement
+'trusted boot'. This is realized by code in the firmware measuring parts of the
+firmware's code and data as well as system data, such as the boot block, and
+logging these measurements and storing (extending) them in the TPM's platform
+configuration register (PCR).
+
+The benefits of having a TPM (or vTPM) in a system are:
+
+- enablement of trusted boot; this allow us to eventually extend the chain of
+ trust from the hypervisor to the guests
+- enablement of attestation so that one can verify what software is running on
+ a machine (OpenPTS, OpenAttestation)
+- provides TPM functionality to VMs, which includes a standardized mechanism
+ to store keys and other blobs (Linux trusted keys, GNU TLS's TPM extensions)
+
+
+QEMU/KVM + SLOF support:
+------------------------
+
+vTPM for QEMU/KVM pSeries virtual machines is support in QEMU 5.0.
+
+To start a QEMU VM with an attached vTPM (swtpm), run the below shown commands.
+The following will setup the vTPM so that its state will be stored in
+/tmp/myvtpm1. A unique directory for each VM instance with attached vTPM
+must be provided. Whenever QEMU is started, the swtpm has to be started
+before it. The file 'boot_rom.bin' is SLOF with vTPM extensions built-in.
+
+ #> mkdir -p /tmp/mytpm1
+ #> swtpm socket --tpm2 --tpmstate dir=/tmp/mytpm1 \
+ --ctrl type=unixio,path=/tmp/mytpm1/swtpm-sock
+
+ In another terminal:
+
+ #> sudo qemu-system-ppc64 -display sdl \
+ -machine pseries,accel=kvm \
+ -m 1024 -bios boot_rom.bin -boot menu=on \
+ -nodefaults -device VGA -device pci-ohci -device usb-kbd \
+ -chardev socket,id=chrtpm,path=/tmp/mytpm1/swtpm-sock \
+ -tpmdev emulator,id=tpm0,chardev=chrtpm \
+ -device tpm-spapr,tpmdev=tpm0 \
+ -device spapr-vscsi,id=scsi0,reg=0x00002000 \
+ -device virtio-blk-pci,scsi=off,bus=pci.0,addr=0x3,drive=drive-virtio-disk0,id=virtio-disk0 \
+ -drive file=test.img,format=raw,if=none,id=drive-virtio-disk0
+
+Notes:
+ - The Linux kernel in the VM must have the tpm_ibmvtpm module available
+ or built-in. A recent kernel is needed that enables TPM 2.0 support
+ in this module.
+
+ - 'swtpm_ioctl --unix /tmp/mytpm1/swtpm-sock -s' can be used to gracefully
+ shut down the vTPM.
diff --git a/roms/SLOF/lib/libtpm/sha.c b/roms/SLOF/lib/libtpm/sha.c
new file mode 100644
index 000000000..902a4bac0
--- /dev/null
+++ b/roms/SLOF/lib/libtpm/sha.c
@@ -0,0 +1,232 @@
+/*****************************************************************************
+ * Copyright (c) 2015-2021 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
+ *****************************************************************************/
+
+/*
+ * See: NIST standard for SHA-1 in FIPS PUB 180-4
+ */
+
+#include "byteorder.h"
+#include "sha.h"
+#include "string.h"
+
+typedef struct _sha1_ctx {
+ uint32_t h[5];
+} sha1_ctx;
+
+#define rol(VAL, N) \
+({ \
+ uint32_t res; \
+ __asm__ ( \
+ "rotlwi %0, %1, %2\n\t" \
+ : "=r" (res) \
+ : "r" (VAL), "i" (N) \
+ ); \
+ res; \
+})
+
+static void sha1_block(uint32_t *w, sha1_ctx *ctx)
+{
+ uint32_t i;
+ uint32_t a,b,c,d,e,f;
+ uint32_t tmp;
+ uint32_t idx;
+
+ /*
+ * FIPS 180-4 4.2.1: SHA1 Constants
+ */
+ static const uint32_t sha_ko[4] = {
+ 0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xca62c1d6
+ };
+
+ /*
+ * FIPS 180-4 6.1.2: step 1
+ *
+ * 0 <= i <= 15:
+ * W(t) = M(t)
+ * 16 <= i <= 79:
+ * W(t) = ROTL(W(t-3) XOR W(t-8) XOR W(t-14) XOR W(t-16), 1)
+ */
+
+ /* w(0)..w(15) are in big endian format */
+ for (i = 0; i <= 15; i++)
+ w[i] = be32_to_cpu(w[i]);
+
+ for (i = 16; i <= 79; i++) {
+ tmp = w[i-3] ^ w[i-8] ^ w[i-14] ^ w[i-16];
+ w[i] = rol(tmp, 1);
+ }
+
+ /*
+ * step 2: a = H0, b = H1, c = H2, d = H3, e = H4.
+ */
+ a = ctx->h[0];
+ b = ctx->h[1];
+ c = ctx->h[2];
+ d = ctx->h[3];
+ e = ctx->h[4];
+
+ /*
+ * step 3: For i = 0 to 79:
+ * T = ROTL(a, 5) + f(i; b,c,d) + e + W(t) + K(t);
+ */
+ for (i = 0; i <= 79; i++) {
+ /*
+ * FIPS 180-4: 4.1.1 : definition of f(i; b,c,d)
+ */
+ if (i <= 19) {
+ /*
+ * 0 <= i <= 19:
+ * f(i; b,c,d) = (b AND c) OR ((NOT b) AND d)
+ */
+ f = (b & c) | ((b ^ 0xffffffff) & d);
+ idx = 0;
+ } else if (i <= 39) {
+ /*
+ * 20 <= i <= 39:
+ * f(i; b,c,d) = b XOR c XOR d
+ */
+ f = b ^ c ^ d;
+ idx = 1;
+ } else if (i <= 59) {
+ /*
+ * 40 <= i <= 59:
+ * f(i; b,c,d) = (b AND c) OR (b AND d) OR (c AND d)
+ */
+ f = (b & c) | (b & d) | (c & d);
+ idx = 2;
+ } else {
+ /*
+ * 60 <= i <= 79:
+ * f(i; b,c,d) = b XOR c XOR d
+ */
+ f = b ^ c ^ d;
+ idx = 3;
+ }
+
+ /*
+ * step 3:
+ * t = ROTL(a, 5) + f(t;b,c,d) + e + K(t) + W(t);
+ * e = d; d = c; c = ROTL(b, 30); b = a; a = t;
+ */
+ tmp = rol(a, 5) +
+ f +
+ e +
+ sha_ko[idx] +
+ w[i];
+ e = d;
+ d = c;
+ c = rol(b, 30);
+ b = a;
+ a = tmp;
+ }
+
+ /*
+ * step 4:
+ * H0 = a + H0, H1 = b + H1, H2 = c + H2, H3 = d + H3, H4 = e + H4
+ */
+ ctx->h[0] += a;
+ ctx->h[1] += b;
+ ctx->h[2] += c;
+ ctx->h[3] += d;
+ ctx->h[4] += e;
+}
+
+static void sha1_do(sha1_ctx *ctx, const uint8_t *data32, uint32_t length)
+{
+ uint32_t offset = 0;
+ uint16_t num;
+ uint64_t bits = 0;
+ uint32_t w[80];
+ uint64_t tmp;
+
+ /* treat data in 64-byte chunks */
+ for (offset = 0; length - offset >= 64; offset += 64) {
+ memcpy(w, data32 + offset, 64);
+ sha1_block((uint32_t *)w, ctx);
+ bits += (64 * 8);
+ }
+
+ /* last block with less than 64 bytes */
+ num = length - offset;
+ bits += (num << 3);
+
+ memcpy(w, data32 + offset, num);
+ /*
+ * FIPS 180-4 5.1: Padding the Message
+ */
+ ((uint8_t *)w)[num] = 0x80;
+ if (64 - (num + 1) > 0)
+ memset( &((uint8_t *)w)[num + 1], 0, 64 - (num + 1));
+
+ if (num >= 56) {
+ /* cannot append number of bits here */
+ sha1_block((uint32_t *)w, ctx);
+ memset(w, 0, 60);
+ }
+
+ /* write number of bits to end of block */
+ tmp = cpu_to_be64(bits);
+ memcpy(&w[14], &tmp, 8);
+
+ sha1_block(w, ctx);
+
+ /* need to switch result's endianness */
+ for (num = 0; num < 5; num++)
+ ctx->h[num] = cpu_to_be32(ctx->h[num]);
+}
+
+void sha1(const uint8_t *data, uint32_t length, uint8_t *hash)
+{
+ sha1_ctx ctx = {
+ .h = {
+ /*
+ * FIPS 180-4: 6.1.1
+ * -> 5.3.1: initial hash value
+ */
+ 0x67452301,
+ 0xefcdab89,
+ 0x98badcfe,
+ 0x10325476,
+ 0xc3d2e1f0,
+ }
+ };
+
+ sha1_do(&ctx, data, length);
+ memcpy(hash, &ctx.h[0], 20);
+}
+
+#ifdef MAIN
+
+#include "sha_test.h"
+
+int main(void)
+{
+ TESTVECTORS(data);
+ uint8_t hash[20];
+ char input[64];
+ int err = 0;
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(data); i++)
+ err |= test_hash(sha1, hash, sizeof(hash),
+ data[i], strlen(data[i]),
+ SHA1);
+
+ memset(input, 'a', sizeof(input));
+ /* cover critical input size around 56 bytes */
+ for (i = 50; i < sizeof(input); i++)
+ err |= test_hash(sha1, hash, sizeof(hash),
+ input, i, SHA1);
+
+ return err;
+}
+#endif
diff --git a/roms/SLOF/lib/libtpm/sha.h b/roms/SLOF/lib/libtpm/sha.h
new file mode 100644
index 000000000..0bb031e5d
--- /dev/null
+++ b/roms/SLOF/lib/libtpm/sha.h
@@ -0,0 +1,23 @@
+/*****************************************************************************
+ * 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
+ *****************************************************************************/
+
+#ifndef __SHA_H
+#define __SHA_H
+
+#include "types.h"
+
+void sha1(const uint8_t *data, uint32_t length, uint8_t *hash);
+void sha256(const uint8_t *data, uint32_t length, uint8_t *hash);
+void sha384(const uint8_t *data, uint32_t length, uint8_t *hash);
+void sha512(const uint8_t *data, uint32_t length, uint8_t *hash);
+
+#endif /* __SHA_H */
diff --git a/roms/SLOF/lib/libtpm/sha256.c b/roms/SLOF/lib/libtpm/sha256.c
new file mode 100644
index 000000000..79bcb8336
--- /dev/null
+++ b/roms/SLOF/lib/libtpm/sha256.c
@@ -0,0 +1,246 @@
+/*****************************************************************************
+ * 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
+ *****************************************************************************/
+
+/*
+ * See: NIST standard for SHA-256 in FIPS PUB 180-4
+ */
+
+#include "byteorder.h"
+#include "sha.h"
+#include "string.h"
+
+typedef struct _sha256_ctx {
+ uint32_t h[8];
+} sha256_ctx;
+
+#define rotr(VAL, N) \
+({ \
+ uint32_t res; \
+ __asm__ ( \
+ "rotrwi %0, %1, %2\n\t" \
+ : "=r" (res) \
+ : "r" (VAL), "i" (N) \
+ ); \
+ res; \
+})
+
+static inline uint32_t Ch(uint32_t x, uint32_t y, uint32_t z)
+{
+ return (x & y) | ((x ^ 0xffffffff) & z);
+}
+
+static inline uint32_t Maj(uint32_t x, uint32_t y, uint32_t z)
+{
+ return (x & y) | (x & z) | (y & z);
+}
+
+static inline uint32_t sum0(uint32_t x)
+{
+ return rotr(x, 2) ^ rotr(x, 13) ^ rotr(x, 22);
+}
+
+static inline uint32_t sum1(uint32_t x)
+{
+ return rotr(x, 6) ^ rotr(x, 11) ^ rotr(x, 25);
+}
+
+static inline uint32_t sigma0(uint32_t x)
+{
+ return rotr(x, 7) ^ rotr(x, 18) ^ (x >> 3);
+}
+
+static inline uint32_t sigma1(uint32_t x)
+{
+ return rotr(x, 17) ^ rotr(x, 19) ^ (x >> 10);
+}
+
+static void sha256_block(uint32_t *w, sha256_ctx *ctx)
+{
+ uint32_t t;
+ uint32_t a, b, c, d, e, f, g, h;
+ uint32_t T1, T2;
+
+ /*
+ * FIPS 180-4 4.2.2: SHA256 Constants
+ */
+ static const uint32_t sha_ko[64] = {
+ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
+ 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
+ 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
+ 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
+ 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
+ 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
+ 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
+ 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
+ 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
+ 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
+ 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
+ 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
+ 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
+ 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
+ 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
+ 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
+ };
+
+ /*
+ * FIPS 180-4 6.2.2: step 1
+ *
+ * 0 <= i <= 15:
+ * W(t) = M(t)
+ * 16 <= i <= 63:
+ * W(t) = sigma1(W(t-2)) + W(t-7) + sigma0(W(t-15)) + W(t-16)
+ */
+
+ /* w(0)..w(15) are in big endian format */
+ for (t = 0; t <= 15; t++)
+ w[t] = be32_to_cpu(w[t]);
+
+ for (t = 16; t <= 63; t++)
+ w[t] = sigma1(w[t-2]) + w[t-7] + sigma0(w[t-15]) + w[t-16];
+
+ /*
+ * step 2: a = H0, b = H1, c = H2, d = H3, e = H4, f = H5, g = H6, h = H7
+ */
+ a = ctx->h[0];
+ b = ctx->h[1];
+ c = ctx->h[2];
+ d = ctx->h[3];
+ e = ctx->h[4];
+ f = ctx->h[5];
+ g = ctx->h[6];
+ h = ctx->h[7];
+
+ /*
+ * step 3: For i = 0 to 63:
+ * T1 = h + sum1(e) + Ch(e,f,g) + K(t) + W(t);
+ * T2 = sum0(a) + Maj(a,b,c)
+ * h = g; g = f; f = e; e = d + T1; d = c; c = b; b = a; a + T1 + T2
+ */
+ for (t = 0; t <= 63; t++) {
+ T1 = h + sum1(e) + Ch(e, f, g) + sha_ko[t] + w[t];
+ T2 = sum0(a) + Maj(a, b, c);
+ h = g;
+ g = f;
+ f = e;
+ e = d + T1;
+ d = c;
+ c = b;
+ b = a;
+ a = T1 + T2;
+ }
+
+ /*
+ * step 4:
+ * H0 = a + H0, H1 = b + H1, H2 = c + H2, H3 = d + H3, H4 = e + H4
+ */
+ ctx->h[0] += a;
+ ctx->h[1] += b;
+ ctx->h[2] += c;
+ ctx->h[3] += d;
+ ctx->h[4] += e;
+ ctx->h[5] += f;
+ ctx->h[6] += g;
+ ctx->h[7] += h;
+}
+
+static void sha256_do(sha256_ctx *ctx, const uint8_t *data32, uint32_t length)
+{
+ uint32_t offset;
+ uint16_t num;
+ uint32_t bits = 0;
+ uint32_t w[64];
+ uint64_t tmp;
+
+ /* treat data in 64-byte chunks */
+ for (offset = 0; length - offset >= 64; offset += 64) {
+ memcpy(w, data32 + offset, 64);
+ sha256_block((uint32_t *)w, ctx);
+ bits += (64 * 8);
+ }
+
+ /* last block with less than 64 bytes */
+ num = length - offset;
+ bits += (num << 3);
+
+ memcpy(w, data32 + offset, num);
+ /*
+ * FIPS 180-4 5.1: Padding the Message
+ */
+ ((uint8_t *)w)[num] = 0x80;
+ if (64 - (num + 1) > 0)
+ memset( &((uint8_t *)w)[num + 1], 0, 64 - (num + 1));
+
+ if (num >= 56) {
+ /* cannot append number of bits here */
+ sha256_block((uint32_t *)w, ctx);
+ memset(w, 0, 60);
+ }
+
+ /* write number of bits to end of block */
+ tmp = cpu_to_be64(bits);
+ memcpy(&w[14], &tmp, 8);
+
+ sha256_block(w, ctx);
+
+ /* need to switch result's endianness */
+ for (num = 0; num < 8; num++)
+ ctx->h[num] = cpu_to_be32(ctx->h[num]);
+}
+
+void sha256(const uint8_t *data, uint32_t length, uint8_t *hash)
+{
+ sha256_ctx ctx = {
+ .h = {
+ /*
+ * FIPS 180-4: 6.2.1
+ * -> 5.3.3: initial hash value
+ */
+ 0x6a09e667,
+ 0xbb67ae85,
+ 0x3c6ef372,
+ 0xa54ff53a,
+ 0x510e527f,
+ 0x9b05688c,
+ 0x1f83d9ab,
+ 0x5be0cd19
+ }
+ };
+
+ sha256_do(&ctx, data, length);
+ memcpy(hash, ctx.h, sizeof(ctx.h));
+}
+
+#ifdef MAIN
+
+#include "sha_test.h"
+
+int main(void)
+{
+ TESTVECTORS(data);
+ uint8_t hash[32];
+ char input[64];
+ int err = 0;
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(data); i++)
+ err |= test_hash(sha256, hash, sizeof(hash),
+ data[i], strlen(data[i]),
+ SHA256);
+
+ memset(input, 'a', sizeof(input));
+ /* cover critical input size around 56 bytes */
+ for (i = 50; i < sizeof(input); i++)
+ err |= test_hash(sha256, hash, sizeof(hash), input, i, SHA256);
+
+ return err;
+}
+#endif
diff --git a/roms/SLOF/lib/libtpm/sha512.c b/roms/SLOF/lib/libtpm/sha512.c
new file mode 100644
index 000000000..86831abb1
--- /dev/null
+++ b/roms/SLOF/lib/libtpm/sha512.c
@@ -0,0 +1,285 @@
+/*****************************************************************************
+ * Copyright (c) 2021 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
+ *****************************************************************************/
+
+/*
+ * See: NIST standard for SHA-512 and SHA-384 in FIPS PUB 180-4 & RFC 6234
+ */
+
+#include "byteorder.h"
+#include "sha.h"
+#include "string.h"
+
+typedef struct _sha512_ctx {
+ uint64_t h[8];
+} sha512_ctx;
+
+#define rotr(VAL, N) \
+({ \
+ uint64_t res; \
+ __asm__ ( \
+ "rotrdi %0, %1, %2\n\t" \
+ : "=r" (res) \
+ : "r" (VAL), "i" (N) \
+ ); \
+ res; \
+})
+
+static inline uint64_t Ch(uint64_t x, uint64_t y, uint64_t z)
+{
+ return (x & y) ^ ((x ^ 0xffffffffffffffffULL) & z);
+}
+
+static inline uint64_t Maj(uint64_t x, uint64_t y, uint64_t z)
+{
+ return (x & y) ^ (x & z) ^ (y & z);
+}
+
+static inline uint64_t sum0(uint64_t x)
+{
+ return rotr(x, 28) ^ rotr(x, 34) ^ rotr(x, 39);
+}
+
+static inline uint64_t sum1(uint64_t x)
+{
+ return rotr(x, 14) ^ rotr(x, 18) ^ rotr(x, 41);
+}
+
+static inline uint64_t sigma0(uint64_t x)
+{
+ return rotr(x, 1) ^ rotr(x, 8) ^ (x >> 7);
+}
+
+static inline uint64_t sigma1(uint64_t x)
+{
+ return rotr(x, 19) ^ rotr(x, 61) ^ (x >> 6);
+}
+
+static void sha512_block(uint64_t *w, sha512_ctx *ctx)
+{
+ uint32_t t;
+ uint64_t a, b, c, d, e, f, g, h;
+ uint64_t T1, T2;
+
+ /*
+ * FIPS 180-4 4.2.2: SHA512 Constants
+ */
+ static const uint64_t sha_ko[80] = {
+ 0x428a2f98d728ae22, 0x7137449123ef65cd, 0xb5c0fbcfec4d3b2f, 0xe9b5dba58189dbbc,
+ 0x3956c25bf348b538, 0x59f111f1b605d019, 0x923f82a4af194f9b, 0xab1c5ed5da6d8118,
+ 0xd807aa98a3030242, 0x12835b0145706fbe, 0x243185be4ee4b28c, 0x550c7dc3d5ffb4e2,
+ 0x72be5d74f27b896f, 0x80deb1fe3b1696b1, 0x9bdc06a725c71235, 0xc19bf174cf692694,
+ 0xe49b69c19ef14ad2, 0xefbe4786384f25e3, 0x0fc19dc68b8cd5b5, 0x240ca1cc77ac9c65,
+ 0x2de92c6f592b0275, 0x4a7484aa6ea6e483, 0x5cb0a9dcbd41fbd4, 0x76f988da831153b5,
+ 0x983e5152ee66dfab, 0xa831c66d2db43210, 0xb00327c898fb213f, 0xbf597fc7beef0ee4,
+ 0xc6e00bf33da88fc2, 0xd5a79147930aa725, 0x06ca6351e003826f, 0x142929670a0e6e70,
+ 0x27b70a8546d22ffc, 0x2e1b21385c26c926, 0x4d2c6dfc5ac42aed, 0x53380d139d95b3df,
+ 0x650a73548baf63de, 0x766a0abb3c77b2a8, 0x81c2c92e47edaee6, 0x92722c851482353b,
+ 0xa2bfe8a14cf10364, 0xa81a664bbc423001, 0xc24b8b70d0f89791, 0xc76c51a30654be30,
+ 0xd192e819d6ef5218, 0xd69906245565a910, 0xf40e35855771202a, 0x106aa07032bbd1b8,
+ 0x19a4c116b8d2d0c8, 0x1e376c085141ab53, 0x2748774cdf8eeb99, 0x34b0bcb5e19b48a8,
+ 0x391c0cb3c5c95a63, 0x4ed8aa4ae3418acb, 0x5b9cca4f7763e373, 0x682e6ff3d6b2b8a3,
+ 0x748f82ee5defb2fc, 0x78a5636f43172f60, 0x84c87814a1f0ab72, 0x8cc702081a6439ec,
+ 0x90befffa23631e28, 0xa4506cebde82bde9, 0xbef9a3f7b2c67915, 0xc67178f2e372532b,
+ 0xca273eceea26619c, 0xd186b8c721c0c207, 0xeada7dd6cde0eb1e, 0xf57d4f7fee6ed178,
+ 0x06f067aa72176fba, 0x0a637dc5a2c898a6, 0x113f9804bef90dae, 0x1b710b35131c471b,
+ 0x28db77f523047d84, 0x32caab7b40c72493, 0x3c9ebe0a15c9bebc, 0x431d67c49c100d4c,
+ 0x4cc5d4becb3e42b6, 0x597f299cfc657e2a, 0x5fcb6fab3ad6faec, 0x6c44198c4a475817
+ };
+
+ /*
+ * FIPS 180-4 6.4.2: step 1
+ *
+ * 0 <= i <= 15:
+ * W(t) = M(t)
+ * 16 <= i <= 79:
+ * W(t) = sigma1(W(t-2)) + W(t-7) + sigma0(W(t-15)) + W(t-16)
+ */
+
+ /* w(0)..w(15) are in big endian format */
+ for (t = 0; t <= 15; t++)
+ w[t] = be64_to_cpu(w[t]);
+
+ for (t = 16; t <= 79; t++)
+ w[t] = sigma1(w[t-2]) + w[t-7] + sigma0(w[t-15]) + w[t-16];
+
+ /*
+ * step 2: a = H0, b = H1, c = H2, d = H3, e = H4, f = H5, g = H6, h = H7
+ */
+ a = ctx->h[0];
+ b = ctx->h[1];
+ c = ctx->h[2];
+ d = ctx->h[3];
+ e = ctx->h[4];
+ f = ctx->h[5];
+ g = ctx->h[6];
+ h = ctx->h[7];
+
+ /*
+ * step 3: For i = 0 to 79:
+ * T1 = h + sum1(e) + Ch(e,f,g) + K(t) + W(t);
+ * T2 = sum0(a) + Maj(a,b,c)
+ * h = g; g = f; f = e; e = d + T1; d = c; c = b; b = a; a + T1 + T2
+ */
+ for (t = 0; t <= 79; t++) {
+ T1 = h + sum1(e) + Ch(e, f, g) + sha_ko[t] + w[t];
+ T2 = sum0(a) + Maj(a, b, c);
+ h = g;
+ g = f;
+ f = e;
+ e = d + T1;
+ d = c;
+ c = b;
+ b = a;
+ a = T1 + T2;
+ }
+
+ /*
+ * step 4:
+ * H0 = a + H0, H1 = b + H1, H2 = c + H2, H3 = d + H3, H4 = e + H4
+ */
+ ctx->h[0] += a;
+ ctx->h[1] += b;
+ ctx->h[2] += c;
+ ctx->h[3] += d;
+ ctx->h[4] += e;
+ ctx->h[5] += f;
+ ctx->h[6] += g;
+ ctx->h[7] += h;
+}
+
+static void sha512_do(sha512_ctx *ctx, const uint8_t *data32, uint32_t length)
+{
+ uint32_t offset;
+ uint16_t num;
+ uint64_t bits = 0;
+ uint64_t w[80];
+ uint64_t tmp;
+
+ /* treat data in 128-byte/1024 bit chunks */
+ for (offset = 0; length - offset >= 128; offset += 128) {
+ memcpy(w, data32 + offset, 128);
+ sha512_block(w, ctx);
+ bits += (128 * 8);
+ }
+
+ /* last block with less than 128 bytes */
+ num = length - offset;
+ bits += (num << 3);
+
+ memcpy(w, data32 + offset, num);
+ /*
+ * FIPS 180-4 5.1: Padding the Message
+ */
+ ((uint8_t *)w)[num] = 0x80;
+ if (128 - (num + 1) > 0)
+ memset( &((uint8_t *)w)[num + 1], 0, 128 - (num + 1));
+
+ if (num >= 112) {
+ /* cannot append number of bits here;
+ * need space for 128 bits (16 bytes)
+ */
+ sha512_block((uint64_t *)w, ctx);
+ memset(w, 0, 128);
+ }
+
+ /* write number of bits to end of the block; we write 64 bits */
+ tmp = cpu_to_be64(bits);
+ memcpy(&w[15], &tmp, 8);
+
+ sha512_block(w, ctx);
+
+ /* need to switch result's endianness */
+ for (num = 0; num < 8; num++)
+ ctx->h[num] = cpu_to_be64(ctx->h[num]);
+}
+
+void sha384(const uint8_t *data, uint32_t length, uint8_t *hash)
+{
+ sha512_ctx ctx = {
+ .h = {
+ /*
+ * FIPS 180-4: 6.2.1
+ * -> 5.3.4: initial hash value
+ */
+ 0xcbbb9d5dc1059ed8,
+ 0x629a292a367cd507,
+ 0x9159015a3070dd17,
+ 0x152fecd8f70e5939,
+ 0x67332667ffc00b31,
+ 0x8eb44a8768581511,
+ 0xdb0c2e0d64f98fa7,
+ 0x47b5481dbefa4fa4
+ }
+ };
+
+ sha512_do(&ctx, data, length);
+ memcpy(hash, ctx.h, 384/8);
+}
+
+void sha512(const uint8_t *data, uint32_t length, uint8_t *hash)
+{
+ sha512_ctx ctx = {
+ .h = {
+ /*
+ * FIPS 180-4: 6.2.1
+ * -> 5.3.5: initial hash value
+ */
+ 0x6a09e667f3bcc908,
+ 0xbb67ae8584caa73b,
+ 0x3c6ef372fe94f82b,
+ 0xa54ff53a5f1d36f1,
+ 0x510e527fade682d1,
+ 0x9b05688c2b3e6c1f,
+ 0x1f83d9abfb41bd6b,
+ 0x5be0cd19137e2179
+ }
+ };
+
+ sha512_do(&ctx, data, length);
+ memcpy(hash, ctx.h, sizeof(ctx.h));
+}
+
+
+#ifdef MAIN
+
+#include "sha_test.h"
+
+int main(void)
+{
+ TESTVECTORS(data);
+ uint8_t hash512[64];
+ uint8_t hash384[48];
+ char input[128];
+ int err = 0;
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(data); i++) {
+ err |= test_hash(sha384, hash384, sizeof(hash384),
+ data[i], strlen(data[i]),
+ SHA384);
+ err |= test_hash(sha512, hash512, sizeof(hash512),
+ data[i], strlen(data[i]),
+ SHA512);
+ }
+
+ memset(input, 'a', sizeof(input));
+ /* cover critical input size around 112 bytes */
+ for (i = 110; i < sizeof(input); i++) {
+ err |= test_hash(sha384, hash384, sizeof(hash384),
+ input, i, SHA384);
+ err |= test_hash(sha512, hash512, sizeof(hash512),
+ input, i, SHA512);
+ }
+
+ return err;
+}
+#endif
diff --git a/roms/SLOF/lib/libtpm/sha_test.h b/roms/SLOF/lib/libtpm/sha_test.h
new file mode 100644
index 000000000..af82fac25
--- /dev/null
+++ b/roms/SLOF/lib/libtpm/sha_test.h
@@ -0,0 +1,59 @@
+/*****************************************************************************
+ * Copyright (c) 2021 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
+ *****************************************************************************/
+
+#ifndef SHA_TEST_H
+#define SHA_TEST_H
+
+#include <stdio.h>
+
+#include "helpers.h"
+
+/* to avoid compilation issues do not include openssl/sha.h */
+unsigned char *SHA1(const unsigned char *, size_t, unsigned char *);
+unsigned char *SHA256(const unsigned char *, size_t, unsigned char *);
+unsigned char *SHA384(const unsigned char *, size_t, unsigned char *);
+unsigned char *SHA512(const unsigned char *, size_t, unsigned char *);
+
+typedef void (*hashfunc)(const uint8_t *data, uint32_t length, uint8_t *hash);
+typedef unsigned char *(*osslhashfunc)(const unsigned char *, size_t,
+ unsigned char *);
+
+#define TESTVECTORS(NAME) \
+char *NAME[] = { \
+ "", \
+ "abc", \
+ "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", \
+ "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu" \
+};
+
+static inline int
+test_hash(hashfunc hf, uint8_t *hash, size_t hashlen,
+ const char *data, uint32_t length,
+ osslhashfunc osslhf)
+{
+ unsigned char expected[hashlen];
+ int ret = 0;
+
+ osslhf((const unsigned char *)data, length, expected);
+
+ hf((uint8_t *)data, length, hash);
+ if (!memcmp(hash, expected, hashlen)) {
+ printf("PASS: input length: %u\n", length);
+ } else {
+ printf("FAIL data: %s\n", data);
+ ret = 1;
+ }
+
+ return ret;
+}
+
+#endif /* SHA_TEST_H */
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;
+ }
+ }
+}
diff --git a/roms/SLOF/lib/libtpm/tcgbios.h b/roms/SLOF/lib/libtpm/tcgbios.h
new file mode 100644
index 000000000..021e21932
--- /dev/null
+++ b/roms/SLOF/lib/libtpm/tcgbios.h
@@ -0,0 +1,45 @@
+/*****************************************************************************
+ * 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
+ *****************************************************************************/
+
+#ifndef TCGBIOS_H
+#define TCGBIOS_H
+
+#include <stdint.h>
+#include <stdbool.h>
+
+uint32_t tpm_start(void);
+void tpm_finalize(void);
+uint32_t tpm_leave_firmware(void);
+uint32_t tpm_measure_scrtm(void);
+void tpm_set_log_parameters(void *address, size_t size);
+uint32_t tpm_get_logsize(void);
+uint32_t tpm_measure_bcv_mbr(uint32_t bootdrv, const uint8_t *addr,
+ uint32_t length);
+uint32_t tpm_add_event_separators(uint32_t start_pcr, uint32_t end_pcr);
+uint32_t tpm_driver_get_failure_reason(void);
+void tpm_driver_set_failure_reason(uint32_t errcode);
+bool tpm_is_working(void);
+void tpm20_menu(void);
+void tpm_gpt_set_lba1(const uint8_t *addr, uint32_t length);
+void tpm_gpt_add_entry(const uint8_t *addr, uint32_t length);
+uint32_t tpm_measure_gpt(void);
+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);
+uint32_t tpm_2hash_ext_log(uint32_t pcrindex,
+ uint32_t eventtype,
+ const char *info, uint32_t infolen,
+ const void *data, uint64_t datalen);
+
+#endif /* TCGBIOS_H */
diff --git a/roms/SLOF/lib/libtpm/tcgbios_int.h b/roms/SLOF/lib/libtpm/tcgbios_int.h
new file mode 100644
index 000000000..cc3845585
--- /dev/null
+++ b/roms/SLOF/lib/libtpm/tcgbios_int.h
@@ -0,0 +1,317 @@
+/*****************************************************************************
+ * 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
+ *****************************************************************************/
+
+#ifndef TCGBIOS_INT_H
+#define TCGBIOS_INT_H
+
+#include <stdint.h>
+
+/* internal error codes */
+#define TCGBIOS_OK 0x0
+#define TCGBIOS_LOGOVERFLOW 0x1
+#define TCGBIOS_GENERAL_ERROR 0x2
+#define TCGBIOS_FIRMWARE_ERROR 0x3
+#define TCGBIOS_FATAL_COM_ERROR 0x4
+#define TCGBIOS_INVALID_INPUT_PARA 0x5
+#define TCGBIOS_COMMAND_ERROR 0x6
+#define TCGBIOS_INTERFACE_SHUTDOWN 0x7
+
+/*
+ * event types from spec:
+ * TCG PC Client Specific Implementation Specification
+ * for Conventional BIOS
+ */
+#define EV_POST_CODE 1
+#define EV_NO_ACTION 3
+#define EV_SEPARATOR 4
+#define EV_ACTION 5
+#define EV_EVENT_TAG 6
+#define EV_S_CRTM_CONTENTS 7
+#define EV_S_CRTM_VERSION 8
+#define EV_IPL 13
+#define EV_IPL_PARTITION_DATA 14
+#define EV_EFI_EVENT_BASE 0x80000000
+#define EV_EFI_GPT_EVENT (EV_EFI_EVENT_BASE + 0x6)
+
+#define BCV_DEVICE_HDD 0x80
+
+/* hash sizes */
+#define SHA1_BUFSIZE 20
+#define SHA256_BUFSIZE 32
+#define SHA384_BUFSIZE 48
+#define SHA512_BUFSIZE 64
+#define SM3_256_BUFSIZE 32
+#define SHA3_256_BUFSIZE 32
+#define SHA3_384_BUFSIZE 48
+#define SHA3_512_BUFSIZE 64
+
+/*
+ * Logging for TPM 2 is specified in TCG spec "TCG PC Client Platform
+ * Firmware Profile Specification" in section "Event Logging" and sub-
+ * section "TCG_PCR_EVENT2 structure"
+ *
+ * Each entry in the TPM log contains: a TCG_PCR_EVENT2_Header, a variable
+ * length digest, a TCG_PCR_EVENT2_Trailer, and a variable length event.
+ * The 'digest' matches what is sent to the TPM hardware via the Extend
+ * command. On TPM2.0 the digest contains a TPML_DIGEST_VALUES struct
+ * followed by a variable number of TPMT_HA structs (as specified by the
+ * hardware via the TPM2_CAP_PCRS request).
+ */
+typedef struct tdTCG_PCR_EVENT2_Header {
+ uint32_t pcrindex;
+ uint32_t eventtype;
+ uint8_t digests[0];
+} __attribute__((packed)) TCG_PCR_EVENT2_Header;
+
+typedef struct tdTCG_PCR_EVENT2_Trailer {
+ uint32_t eventdatasize;
+ uint8_t event[0];
+} __attribute__((packed)) TCG_PCR_EVENT2_Trailer;
+
+struct TCG_EfiSpecIdEventStruct {
+ uint8_t signature[16];
+ uint32_t platformClass;
+#define TPM_TCPA_ACPI_CLASS_CLIENT 0
+ uint8_t specVersionMinor;
+ uint8_t specVersionMajor;
+ uint8_t specErrata;
+ uint8_t uintnSize;
+ uint32_t numberOfAlgorithms;
+ struct TCG_EfiSpecIdEventAlgorithmSize {
+ uint16_t algorithmId;
+ uint16_t digestSize;
+ } digestSizes[];
+ /*
+ uint8_t vendorInfoSize;
+ uint8_t vendorInfo[0];
+ */
+} __attribute__((packed));
+
+/* EFI related data structures for logging */
+typedef struct {
+ uint64_t signature;
+ uint32_t revision;
+ uint32_t size;
+ uint32_t crc32;
+ uint8_t reserved[4];
+} __attribute__((packed)) UEFI_TABLE_HEADER;
+
+typedef struct {
+ UEFI_TABLE_HEADER header;
+ uint64_t currentLba;
+ uint64_t backupLba;
+ uint64_t firstLba;
+ uint64_t lastLba;
+ uint8_t diskGuid[16];
+ uint64_t partEntryLba;
+ uint32_t numPartEntry;
+ uint32_t partEntrySize;
+ uint32_t partArrayCrc32;
+ uint8_t reserved[420];
+} __attribute__((packed)) UEFI_PARTITION_TABLE_HEADER;
+
+typedef struct {
+ uint8_t partTypeGuid[16];
+ uint8_t partGuid[16];
+ uint64_t firstLba;
+ uint64_t lastLba;
+ uint64_t attribute;
+ uint8_t partName[72];
+} __attribute__((packed)) UEFI_PARTITION_ENTRY;
+
+typedef struct {
+ UEFI_PARTITION_TABLE_HEADER EfiPartitionHeader;
+ uint64_t NumberOfPartitions;
+ UEFI_PARTITION_ENTRY Partitions[0];
+} __attribute__((packed)) UEFI_GPT_DATA;
+
+/* Input and Output headers for all TPM commands */
+struct tpm_req_header {
+ uint16_t tag;
+ uint32_t totlen;
+ uint32_t ordinal;
+} __attribute__((packed));
+
+struct tpm_rsp_header {
+ uint16_t tag;
+ uint32_t totlen;
+ uint32_t errcode;
+} __attribute__((packed));
+
+/****************************************************************
+ * TPM v2.0 hardware commands
+ *
+ * Relevant specs for #defines and commonly used structures:
+ * - Trusted Platform Module Library; Part 2: Structures
+ * Relevant specs for command structures:
+ * - Trusted Platform Module Library; Part 3: Commands
+ ****************************************************************/
+
+#define TPM2_NO 0
+#define TPM2_YES 1
+
+#define TPM2_SU_CLEAR 0x0000
+#define TPM2_SU_STATE 0x0001
+
+#define TPM2_RH_OWNER 0x40000001
+#define TPM2_RS_PW 0x40000009
+#define TPM2_RH_ENDORSEMENT 0x4000000b
+#define TPM2_RH_PLATFORM 0x4000000c
+
+#define TPM2_ALG_SHA1 0x0004
+#define TPM2_ALG_SHA256 0x000b
+#define TPM2_ALG_SHA384 0x000c
+#define TPM2_ALG_SHA512 0x000d
+#define TPM2_ALG_SM3_256 0x0012
+#define TPM2_ALG_SHA3_256 0x0027
+#define TPM2_ALG_SHA3_384 0x0028
+#define TPM2_ALG_SHA3_512 0x0029
+
+/* TPM 2 command tags */
+#define TPM2_ST_NO_SESSIONS 0x8001
+#define TPM2_ST_SESSIONS 0x8002
+
+/* TPM 2 commands */
+#define TPM2_CC_HierarchyControl 0x121
+#define TPM2_CC_Clear 0x126
+#define TPM2_CC_ClearControl 0x127
+#define TPM2_CC_HierarchyChangeAuth 0x129
+#define TPM2_CC_PCR_Allocate 0x12b
+#define TPM2_CC_SelfTest 0x143
+#define TPM2_CC_Startup 0x144
+#define TPM2_CC_Shutdown 0x145
+#define TPM2_CC_StirRandom 0x146
+#define TPM2_CC_GetCapability 0x17a
+#define TPM2_CC_GetRandom 0x17b
+#define TPM2_CC_PCR_Extend 0x182
+
+/* TPM 2 Capabilities */
+#define TPM2_CAP_PCRS 0x00000005
+
+/* TPM 2 data structures */
+
+struct TPMT_HA {
+ uint16_t hashAlg;
+ uint8_t hash[0]; /* size depends on hashAlg */
+} __attribute__((packed));
+
+struct TPML_DIGEST_VALUES {
+ uint32_t count;
+ struct TPMT_HA digest[0]; /* variable number of entries */
+} __attribute__((packed));
+
+struct tpm2_req_stirrandom {
+ struct tpm_req_header hdr;
+ uint16_t size;
+ uint64_t stir;
+} __attribute__((packed));
+
+struct tpm2_req_getrandom {
+ struct tpm_req_header hdr;
+ uint16_t bytesRequested;
+} __attribute__((packed));
+
+struct tpm2b_20 {
+ uint16_t size;
+ uint8_t buffer[20];
+} __attribute__((packed));
+
+struct tpm2_res_getrandom {
+ struct tpm_rsp_header hdr;
+ struct tpm2b_20 rnd;
+} __attribute__((packed));
+
+/*
+ * tpm2_authblock is used in TPM 2 commands using 'Auth. Handle'
+ */
+struct tpm2_authblock {
+ uint32_t handle;
+ uint16_t noncesize; /* always 0 */
+ uint8_t contsession; /* always TPM2_YES */
+ uint16_t pwdsize; /* always 0 */
+} __attribute__((packed));
+
+struct tpm2_req_hierarchychangeauth {
+ struct tpm_req_header hdr;
+ uint32_t authhandle;
+ uint32_t authblocksize;
+ struct tpm2_authblock authblock;
+ struct tpm2b_20 newAuth;
+} __attribute__((packed));
+
+struct tpm2_req_extend {
+ struct tpm_req_header hdr;
+ uint32_t pcrindex;
+ uint32_t authblocksize;
+ struct tpm2_authblock authblock;
+ uint8_t digest[0];
+} __attribute__((packed));
+
+struct tpm2_req_clearcontrol {
+ struct tpm_req_header hdr;
+ uint32_t authhandle;
+ uint32_t authblocksize;
+ struct tpm2_authblock authblock;
+ uint8_t disable;
+} __attribute__((packed));
+
+struct tpm2_req_clear {
+ struct tpm_req_header hdr;
+ uint32_t authhandle;
+ uint32_t authblocksize;
+ struct tpm2_authblock authblock;
+} __attribute__((packed));
+
+struct tpm2_req_hierarchycontrol {
+ struct tpm_req_header hdr;
+ uint32_t authhandle;
+ uint32_t authblocksize;
+ struct tpm2_authblock authblock;
+ uint32_t enable;
+ uint8_t state;
+} __attribute__((packed));
+
+struct tpm2_req_getcapability {
+ struct tpm_req_header hdr;
+ uint32_t capability;
+ uint32_t property;
+ uint32_t propertycount;
+} __attribute__((packed));
+
+struct tpm2_res_getcapability {
+ struct tpm_rsp_header hdr;
+ uint8_t moreData;
+ uint32_t capability;
+ uint8_t data[0]; /* capability dependent data */
+} __attribute__((packed));
+
+struct tpm2_req_pcr_allocate {
+ struct tpm_req_header hdr;
+ uint32_t authhandle;
+ uint32_t authblocksize;
+ struct tpm2_authblock authblock;
+ uint32_t count;
+ uint8_t tpms_pcr_selections[4];
+} __attribute__((packed));
+
+struct tpms_pcr_selection {
+ uint16_t hashAlg;
+ uint8_t sizeOfSelect;
+ uint8_t pcrSelect[0];
+} __attribute__((packed));
+
+struct tpml_pcr_selection {
+ uint32_t count;
+ struct tpms_pcr_selection selections[0];
+} __attribute__((packed));
+
+#endif /* TCGBIOS_INT_H */
diff --git a/roms/SLOF/lib/libtpm/test.sh b/roms/SLOF/lib/libtpm/test.sh
new file mode 100755
index 000000000..4b0567a1d
--- /dev/null
+++ b/roms/SLOF/lib/libtpm/test.sh
@@ -0,0 +1,31 @@
+#!/usr/bin/env bash
+cd $(dirname "$0")
+
+CC=${HOSTCC:-gcc}
+CFLAGS="-Wall -Wextra -Werror -I../../include -I../../slof -I../../lib/libc/include -DMAIN"
+LDFLAGS="-lcrypto"
+
+function fail() {
+ rm -f ${EXEC}
+ echo "Test failed"
+ exit 1
+}
+
+function run_test() {
+ local msg="$1"
+ local src="$2"
+
+ EXEC="./${src%%.c}-test"
+
+ echo ${msg}
+ ${CC} ${CFLAGS} ${src} -o ${EXEC} ${LDFLAGS} || exit 1
+ ${EXEC} || fail
+ rm -f ${EXEC}
+}
+
+run_test "SHA-1 test:" sha.c
+run_test "SHA-256 test:" sha256.c
+run_test "SHA-384 & SHA-512 test:" sha512.c
+
+echo "All tests passed"
+exit 0
diff --git a/roms/SLOF/lib/libtpm/tpm.code b/roms/SLOF/lib/libtpm/tpm.code
new file mode 100644
index 000000000..f5e1d3930
--- /dev/null
+++ b/roms/SLOF/lib/libtpm/tpm.code
@@ -0,0 +1,208 @@
+/******************************************************************************
+ * 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
+ *****************************************************************************/
+/*
+ * libtpm bindings for SLOF - implementation
+ */
+
+#include <tcgbios.h>
+#include <stdbool.h>
+
+/************************************************/
+/* Startup TPM code */
+/* SLOF: tpm-start ( -- errcode ) */
+/* LIBTPM: tpm_start(void) */
+/************************************************/
+PRIM(tpm_X2d_start)
+ PUSH;
+ TOS.n = tpm_start();
+MIRP
+
+/************************************************/
+/* Shutdown TPM layer before OS takes over */
+/* SLOF: tpm-finalize ( -- ) */
+/* LIBTPM: tpm_finalize(void) */
+/************************************************/
+PRIM(tpm_X2d_finalize)
+ tpm_finalize();
+MIRP
+
+/***************************************************************/
+/* Prepare TPM state for bootloader */
+/* SLOF: tpm-leave-firwmare ( -- errcode ) */
+/* LIBTPM: tpm_leave_firmware(void) */
+/***************************************************************/
+PRIM(tpm_X2d_leave_X2d_firmware)
+ PUSH;
+ TOS.n = tpm_leave_firmware();
+MIRP
+
+/*************************************************************/
+/* Convey log address and size */
+/* SLOF: tpm-set-log-parameters ( addr size -- ) */
+/* LIBTPM: tpm_set_log_parameters(void *addr, uint64_t size) */
+/*************************************************************/
+PRIM(tpm_X2d_set_X2d_log_X2d_parameters)
+ int size = TOS.u; POP;
+ void *addr = TOS.a; POP;
+ tpm_set_log_parameters(addr, size);
+MIRP
+
+/*********************************************************/
+/* Firmware API */
+/* SLOF: tpm-driver-get_failure-reason ( -- errcode) */
+/* LIBTPM: errcode = tpm_driver_get_failure_reason(void) */
+/*********************************************************/
+PRIM(tpm_X2d_driver_X2d_get_X2d_failure_X2d_reason)
+ PUSH;
+ TOS.n = tpm_driver_get_failure_reason();
+MIRP
+
+/********************************************************/
+/* Firmware API */
+/* SLOF: tpm-driver-set-failure_reason ( errcode -- ) */
+/* LIBTPM: tpm_driver_set_failure_reason(errcode) */
+/********************************************************/
+PRIM(tpm_X2d_driver_X2d_set_X2d_failure_X2d_reason)
+ int errcode = TOS.u; POP;
+ tpm_driver_set_failure_reason(errcode);
+MIRP
+
+/************************************************/
+/* Get the size of the log */
+/* SLOF: tpm-get-logsize ( -- size ) */
+/* LIBTPM: logsize = tpm_get_logsize(void) */
+/************************************************/
+PRIM(tpm_X2d_get_X2d_logsize)
+ PUSH;
+ TOS.n = tpm_get_logsize();
+MIRP
+
+/**********************************************************************/
+/* Measure and log event separators */
+/* SLOF: tpm-add-event-separators ( start-pcr end-pcr -- errcode) */
+/* LIBTPM: errcode = tpm_add_event_separators(start_pcr, end_pcr) */
+/**********************************************************************/
+PRIM(tpm_X2d_add_X2d_event_X2d_separators)
+ int end_pcr = TOS.u; POP;
+ int start_pcr = TOS.u;
+ TOS.n = tpm_add_event_separators(start_pcr, end_pcr);
+MIRP
+
+/*************************************************************************/
+/* Measure and log boot connect vector (bcv) device's master boot record */
+/* SLOF: tpm-measure-bcv-mbr ( bootdrv addr length -- errcode ) */
+/* LIBTPM: errcode = tpm_measure_bcv_mbr(bbotdrv, addr, length) */
+/*************************************************************************/
+PRIM(tpm_X2d_measure_X2d_bcv_X2d_mbr)
+ int length = TOS.u; POP;
+ void *addr = TOS.a; POP;
+ int bootdrv = TOS.u;
+ TOS.n = tpm_measure_bcv_mbr(bootdrv, addr, length);
+MIRP
+
+/************************************************/
+/* Check whether the TPM is working */
+/* SLOF: tpm-is-working ( -- true | false ) */
+/* LIBTPM: bool = tpm_is_working() */
+/************************************************/
+PRIM(tpm_X2d_is_X2d_working)
+ PUSH;
+ TOS.n = tpm_is_working();
+MIRP
+
+/************************************************/
+/* Have the S-CRTM measured */
+/* SLOF: tpm-measure-scrtm ( -- errcode ) */
+/* LIBTPM: errcode = tpm_measure_scrtm */
+/************************************************/
+PRIM(tpm_X2d_measure_X2d_scrtm)
+ PUSH;
+ TOS.n = tpm_measure_scrtm();
+MIRP
+
+/*******************************************************************/
+/* Firmware API */
+/* SLOF: tpm20-menu ( -- tpm-version ) */
+/* LIBTPM: tpm20_menu() */
+/*******************************************************************/
+PRIM(tpm20_X2d_menu)
+ tpm20_menu();
+MIRP
+
+/*************************************************************************/
+/* Set the LBA1 of the GPT */
+/* SLOF: tpm-gpt-set-lba1 ( addr length -- ) */
+/* LIBTPM: tpm_gpt_set_lba1(addr, length) */
+/*************************************************************************/
+PRIM(tpm_X2d_gpt_X2d_set_X2d_lba1)
+ int length = TOS.u; POP;
+ void *addr = TOS.a; POP;
+ tpm_gpt_set_lba1(addr, length);
+MIRP
+
+/*************************************************************************/
+/* Add a GPT table entry */
+/* SLOF: tpm-gpt-add-entry ( addr length -- ) */
+/* LIBTPM: tpm_gpt_add_entry(addr, length) */
+/*************************************************************************/
+PRIM(tpm_X2d_gpt_X2d_add_X2d_entry)
+ int length = TOS.u; POP;
+ void *addr = TOS.a; POP;
+ tpm_gpt_add_entry(addr, length);
+MIRP
+
+/*************************************************************************/
+/* Measure and log GPT EVENT */
+/* SLOF: tpm-measure-gpt ( -- errcode ) */
+/* LIBTPM: errcode = tpm_measure_gpt() */
+/*************************************************************************/
+PRIM(tpm_X2d_measure_X2d_gpt)
+ PUSH;
+ TOS.n = tpm_measure_gpt();
+MIRP
+
+/***********************************************************************************************************/
+/* Firmware API */
+/* SLOF: tpm-hash-log-extend-event-buffer ( pcr evt data-ptr data-len desc-ptr desclen is_elf -- errcode ) */
+/* LIBTPM: errcode = tpm-hash-log-extend-event-buffer */
+/***********************************************************************************************************/
+PRIM(tpm_X2d_hash_X2d_log_X2d_extend_X2d_event_X2d_buffer)
+ uint32_t is_elf = TOS.u; POP;
+ uint32_t desclen = TOS.u; POP;
+ const char *desc = TOS.a; POP;
+ uint64_t datalen = TOS.u; POP;
+ const void *data = TOS.a; POP;
+ uint32_t eventtype = TOS.u; POP;
+ uint32_t pcrindex = TOS.u;
+
+ TOS.n = tpm_hash_log_extend_event_buffer(pcrindex, eventtype,
+ data, datalen,
+ desc, desclen, is_elf);
+MIRP
+
+/****************************************************************************************/
+/* Firmware API */
+/* SLOF: tpm-2hash-ext-log ( pcr event-type info info-len data data-len -- success? ) */
+/* LIBTPM: success = tpm-2hash-ext-log */
+/****************************************************************************************/
+PRIM(tpm_X2d_2hash_X2d_ext_X2d_log)
+ uint32_t datalen = TOS.u; POP;
+ const void *data = TOS.a; POP;
+ uint64_t infolen = TOS.u; POP;
+ const char *info = TOS.a; POP;
+ uint32_t eventtype = TOS.u; POP;
+ uint32_t pcrindex = TOS.u;
+
+ TOS.u = tpm_2hash_ext_log(pcrindex, eventtype,
+ info, infolen,
+ data, datalen);
+MIRP
diff --git a/roms/SLOF/lib/libtpm/tpm.in b/roms/SLOF/lib/libtpm/tpm.in
new file mode 100644
index 000000000..2f8062473
--- /dev/null
+++ b/roms/SLOF/lib/libtpm/tpm.in
@@ -0,0 +1,32 @@
+/******************************************************************************
+ * 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
+ *****************************************************************************/
+/*
+ * libtpm bindings for SLOF - definitions
+ */
+
+cod(tpm-start)
+cod(tpm-finalize)
+cod(tpm-leave-firmware)
+cod(tpm-set-log-parameters)
+cod(tpm-get-logsize)
+cod(tpm-add-event-separators)
+cod(tpm-measure-bcv-mbr)
+cod(tpm-is-working)
+cod(tpm-measure-scrtm)
+cod(tpm-driver-get-failure-reason)
+cod(tpm-driver-set-failure-reason)
+cod(tpm20-menu)
+cod(tpm-gpt-set-lba1)
+cod(tpm-gpt-add-entry)
+cod(tpm-measure-gpt)
+cod(tpm-hash-log-extend-event-buffer)
+cod(tpm-2hash-ext-log)
diff --git a/roms/SLOF/lib/libtpm/tpm_drivers.c b/roms/SLOF/lib/libtpm/tpm_drivers.c
new file mode 100644
index 000000000..4a4fde89a
--- /dev/null
+++ b/roms/SLOF/lib/libtpm/tpm_drivers.c
@@ -0,0 +1,436 @@
+/*****************************************************************************
+ * 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
+ *****************************************************************************/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdbool.h>
+
+#include "string.h"
+#include "helpers.h"
+#include "byteorder.h"
+#include "tcgbios_int.h"
+#include "tpm_drivers.h"
+#include "libhvcall.h"
+#include "paflof.h"
+
+#undef PAPR_VTPM_DEBUG
+//#define PAPR_VTPM_DEBUG
+#ifdef PAPR_VTPM_DEBUG
+#define dprintf(_x ...) do { printf("VTPM CRQ: " _x); } while(0)
+#else
+#define dprintf(_x ...)
+#endif
+
+/* layout of the command request queue for vTPM; all fields are big endian */
+struct crq {
+ uint8_t valid;
+ uint8_t msg;
+ uint16_t len;
+ uint32_t data;
+ uint64_t reserved;
+} __attribute__((packed));
+
+#define PAPR_VTPM_INIT_CRQ_COMMAND 0xC0
+#define PAPR_VTPM_VALID_COMMAND 0x80
+#define PAPR_VTPM_MSG_RESULT 0x80
+
+/* crq.msg request types when crq.valid = PAPR_VTPM_INIT_CRQ_COMMAND */
+#define PAPR_VTPM_INIT_CRQ_RESULT 0x1
+
+/* crq.msg request types when crq.valid = PAPR_VTPM_VALID_COMMAND */
+#define PAPR_VTPM_GET_VERSION 0x1
+#define PAPR_VTPM_TPM_COMMAND 0x2
+#define PAPR_VTPM_GET_RTCE_BUFFER_SIZE 0x3
+
+#define TPM2_DEFAULT_DURATION_SHORT 750000 /* us */
+#define TPM2_DEFAULT_DURATION_MEDIUM 2000000 /* us */
+#define TPM2_DEFAULT_DURATION_LONG 2000000 /* us */
+
+static const uint32_t tpm2_durations[3] = {
+ TPM2_DEFAULT_DURATION_SHORT,
+ TPM2_DEFAULT_DURATION_MEDIUM,
+ TPM2_DEFAULT_DURATION_LONG,
+};
+
+#define QUEUE_SIZE 4096
+
+/* state of the PAPR CRQ VTPM driver */
+static struct {
+ /* whether it driver been initialized */
+ bool initialized;
+
+ /* unit number */
+ unsigned long unit;
+
+ /* CRQ queue address and size */
+ unsigned char *qaddr;
+ unsigned long qsize;
+
+ /* current q_entry */
+ unsigned int curr_q_entry;
+
+ /* current response CRQ */
+ struct crq *response;
+
+ /* power firmware defined state and error code */
+ vtpm_drv_state driver_state;
+ vtpm_drv_error driver_error;
+
+ /* size of buffer supported by hypervisor */
+ unsigned int buffer_size;
+
+ /* buffer for commands and responses */
+ char *buffer;
+} spapr_vtpm = {
+ .qsize = QUEUE_SIZE,
+ .driver_state = VTPM_DRV_STATE_INVALID,
+ .driver_error = VTPM_DRV_ERROR_NO_FAILURE,
+};
+
+static void vtpm_drv_state_set(vtpm_drv_state s, vtpm_drv_error e)
+{
+ spapr_vtpm.driver_state = s;
+ spapr_vtpm.driver_error = e;
+}
+
+static vtpm_drv_error vtpm_drv_error_get(void)
+{
+ return spapr_vtpm.driver_error;
+}
+
+static struct crq *spapr_get_crq(void *qaddr, unsigned long q_entry)
+{
+ return &((struct crq *)qaddr)[q_entry];
+}
+
+/*
+ * Get the crq where the response will be found. This
+ * function will clear the CRQ's valid field and advance
+ * the entry counter to the next entry.
+ */
+static struct crq *spapr_get_response_crq(void)
+{
+ struct crq *crq;
+
+ dprintf("curr_q_entry = %d\n", spapr_vtpm.curr_q_entry);
+
+ crq = spapr_get_crq(spapr_vtpm.qaddr, spapr_vtpm.curr_q_entry);
+ memset(crq, 0, sizeof(*crq));
+
+ spapr_vtpm.curr_q_entry += 1;
+ if (spapr_vtpm.curr_q_entry == (spapr_vtpm.qsize / sizeof(struct crq)))
+ spapr_vtpm.curr_q_entry = 0;
+
+ return crq;
+}
+
+/*
+ * Send a message via CRQ and wait for the response
+ */
+static bool spapr_send_crq_and_wait(unsigned long unit,
+ struct crq *crq,
+ struct crq **response,
+ unsigned timeout,
+ vtpm_drv_state state1,
+ vtpm_drv_state state2)
+{
+ long rc;
+ unsigned i;
+
+ *response = spapr_get_response_crq();
+
+ vtpm_drv_state_set(state1, VTPM_DRV_ERROR_NO_FAILURE);
+
+ rc = hv_send_crq(unit, (uint64_t *)&crq->valid);
+ if (rc != H_SUCCESS) {
+ vtpm_drv_state_set(VTPM_DRV_STATE_WAIT_INIT,
+ VTPM_DRV_ERROR_TPM_CRQ_ERROR);
+ return false;
+ }
+
+ vtpm_drv_state_set(state2, VTPM_DRV_ERROR_NO_FAILURE);
+
+ for (i = 0; i < timeout; i += 1000) {
+ if (((*response)->valid & PAPR_VTPM_MSG_RESULT))
+ return true;
+ SLOF_usleep(1000);
+ }
+
+ vtpm_drv_state_set(VTPM_DRV_STATE_FAILURE,
+ VTPM_DRV_ERROR_WAIT_TIMEOUT);
+
+ dprintf("Received no response from CRQ\n");
+ return false;
+}
+
+/*
+ * Get parameters from the CRQ
+ */
+static bool spapr_vtpm_get_params(void)
+{
+ struct crq crq, *response;
+ static bool completed = false; /* only once */
+
+ if (completed)
+ return true;
+
+ /* get the TPM's buffer size */
+ crq.valid = PAPR_VTPM_VALID_COMMAND;
+ crq.msg = PAPR_VTPM_GET_RTCE_BUFFER_SIZE;
+
+ if (!spapr_send_crq_and_wait(spapr_vtpm.unit, &crq, &response, 10,
+ VTPM_DRV_STATE_SEND_BUFSIZE_REQ,
+ VTPM_DRV_STATE_WAIT_BUFSIZE)) {
+ printf("%s: Failure getting RTCE buffer size from CRQ\n",
+ __func__);
+ return false;
+ }
+
+ vtpm_drv_state_set(VTPM_DRV_STATE_ALLOC_RTCE_BUF,
+ VTPM_DRV_ERROR_NO_FAILURE);
+
+ dprintf("RTCE buffer size: %u\n", be16_to_cpu(response->len));
+ spapr_vtpm.buffer_size = be16_to_cpu(response->len);
+ if (spapr_vtpm.buffer_size < 1024) {
+ printf("%s: RTCE buffer size of %u bytes is too small. "
+ "Minimum is 1024 bytes.\n", __func__,
+ spapr_vtpm.buffer_size);
+ vtpm_drv_state_set(VTPM_DRV_STATE_FAILURE,
+ VTPM_DRV_ERROR_BAD_RTCE_SIZE);
+ return false;
+ }
+ spapr_vtpm.buffer = SLOF_alloc_mem(spapr_vtpm.buffer_size);
+ if (!spapr_vtpm.buffer) {
+ printf("%s: Could not allocate buffer of size %u.\n",
+ __func__, spapr_vtpm.buffer_size);
+ vtpm_drv_state_set(VTPM_DRV_STATE_FAILURE,
+ VTPM_DRV_ERROR_BAD_RTCE_SIZE);
+ return false;
+ }
+
+ completed = true;
+
+ return true;
+}
+
+static bool spapr_vtpm_activate(void)
+{
+ long rc;
+ struct crq crq, *response;
+
+ if (vtpm_drv_error_get() != VTPM_DRV_ERROR_NO_FAILURE) {
+ printf("%s: CRQ: In failure mode\n", __func__);
+ return false;
+ }
+
+ vtpm_drv_state_set(VTPM_DRV_STATE_REG_CRQ,
+ VTPM_DRV_ERROR_NO_FAILURE);
+
+ rc = hv_reg_crq(spapr_vtpm.unit, (unsigned long)spapr_vtpm.qaddr,
+ spapr_vtpm.qsize);
+ if (rc != H_SUCCESS) {
+ vtpm_drv_state_set(VTPM_DRV_STATE_WAIT_INIT,
+ VTPM_DRV_ERROR_UNEXPECTED_REG_ERROR);
+ printf("%s: CRQ registration failed\n", __func__);
+ return false;
+ }
+
+ /* we always start with curr_q_entry 0 */
+ spapr_vtpm.curr_q_entry = 0;
+
+ if (!spapr_vtpm.initialized) {
+
+ crq.valid = PAPR_VTPM_INIT_CRQ_COMMAND;
+ crq.msg = PAPR_VTPM_INIT_CRQ_RESULT;
+
+ if (!spapr_send_crq_and_wait(spapr_vtpm.unit,
+ &crq,
+ &response,
+ 10,
+ VTPM_DRV_STATE_SEND_INIT,
+ VTPM_DRV_STATE_WAIT_INIT_COMP)) {
+ printf("%s: Initializing CRQ failed\n", __func__);
+ goto err_exit;
+ }
+ dprintf("Successfully initialized CRQ\n");
+
+ spapr_vtpm.initialized = true;
+ }
+
+ if (spapr_vtpm_get_params())
+ return true;
+
+err_exit:
+ hv_free_crq(spapr_vtpm.unit);
+ spapr_vtpm.unit = 0;
+
+ return false;
+}
+
+void spapr_vtpm_finalize(void)
+{
+ if (spapr_vtpm.unit) {
+ hv_free_crq(spapr_vtpm.unit);
+ spapr_vtpm.unit = 0;
+ }
+}
+
+/*
+ * Check whether we have a CRQ underneath us; if we do, the CRQ will
+ * be left open.
+ */
+static bool spapr_vtpm_probe(void)
+{
+ if (!spapr_vtpm.qaddr) {
+ spapr_vtpm.qaddr = SLOF_alloc_mem(spapr_vtpm.qsize);
+ if (!spapr_vtpm.qaddr) {
+ printf("%s: Unable to allocate memory\n", __func__);
+ return false;
+ }
+ memset(spapr_vtpm.qaddr, 0, spapr_vtpm.qsize);
+
+ dprintf("getting FORTH vtpm-unit\n");
+ spapr_vtpm.unit = SLOF_get_vtpm_unit();
+ if (!spapr_vtpm.unit) {
+ printf("%s: Could not get valid vtpm-unit\n", __func__);
+ return false;
+ }
+ }
+
+ dprintf("vtpm_unit = %lx, buffer = %p\n",
+ spapr_vtpm.unit, spapr_vtpm.qaddr);
+
+ if (!spapr_vtpm_activate())
+ return false;
+
+ return true;
+}
+
+static bool spapr_vtpm_senddata(const uint8_t *const data, uint32_t len)
+{
+ struct crq crq;
+ long rc;
+
+ if (vtpm_drv_error_get() != VTPM_DRV_ERROR_NO_FAILURE) {
+ printf("%s: VTPM CRQ: In failure mode\n", __func__);
+ return false;
+ }
+
+ if (len > spapr_vtpm.buffer_size) {
+ printf("%s: VTPM CRQ: Send buffer too large: %u > %u\n",
+ __func__, len, spapr_vtpm.buffer_size);
+ return false;
+ }
+
+ spapr_vtpm.response = spapr_get_response_crq();
+ spapr_vtpm.response->data = (uint64_t)spapr_vtpm.buffer;
+
+ crq.valid = PAPR_VTPM_VALID_COMMAND;
+ crq.msg = PAPR_VTPM_TPM_COMMAND;
+ crq.len = cpu_to_be16(len);
+ crq.data = (uint64_t)spapr_vtpm.buffer;
+ memcpy(spapr_vtpm.buffer, data, MIN(len, spapr_vtpm.buffer_size));
+
+ vtpm_drv_state_set(VTPM_DRV_STATE_SEND_TPM_CMD,
+ VTPM_DRV_ERROR_NO_FAILURE);
+
+ rc = hv_send_crq(spapr_vtpm.unit, (uint64_t *)&crq.valid);
+
+ if (rc == H_SUCCESS)
+ vtpm_drv_state_set(VTPM_DRV_STATE_WAIT_TPM_RSP,
+ VTPM_DRV_ERROR_NO_FAILURE);
+ else
+ vtpm_drv_state_set(VTPM_DRV_STATE_WAIT_INIT,
+ VTPM_DRV_ERROR_UNEXPECTED_SEND_ERROR);
+
+ return (rc == H_SUCCESS);
+}
+
+static bool spapr_vtpm_waitresponseready(enum tpm_duration_type to_t)
+{
+ uint32_t i, timeout = tpm2_durations[to_t];
+
+ if (vtpm_drv_error_get() != VTPM_DRV_ERROR_NO_FAILURE) {
+ printf("%s: VTPM CRQ: In failure mode\n", __func__);
+ return false;
+ }
+
+ for (i = 0; i < timeout; i += 1000) {
+ if (spapr_vtpm.response->valid & PAPR_VTPM_MSG_RESULT) {
+ /* TPM responded: move to Send tpm-cmd state */
+ vtpm_drv_state_set(VTPM_DRV_STATE_SEND_TPM_CMD,
+ VTPM_DRV_ERROR_NO_FAILURE);
+ dprintf("Received response to TPM command\n");
+ return true;
+ }
+ SLOF_usleep(1000);
+ }
+
+ vtpm_drv_state_set(VTPM_DRV_STATE_FAILURE,
+ VTPM_DRV_ERROR_WAIT_TIMEOUT);
+
+ dprintf("Received NO response to TPM command");
+
+ return false;
+}
+
+static bool spapr_vtpm_readresponse(uint8_t *buffer, uint32_t *len)
+{
+ uint32_t length;
+
+ if (vtpm_drv_error_get() != VTPM_DRV_ERROR_NO_FAILURE) {
+ printf("%s: VTPM CRQ: In failure mode\n", __func__);
+ return false;
+ }
+
+ length = MIN(*len, be32_to_cpu(spapr_vtpm.response->len));
+
+ memcpy(buffer, (void *)(uint64_t)spapr_vtpm.response->data, length);
+
+ dprintf("Length of copied response: %d\n", length);
+
+ spapr_vtpm.response = NULL;
+ *len = length;
+
+ return true;
+}
+
+/**** higher layer interface ****/
+
+vtpm_drv_error spapr_vtpm_get_error(void)
+{
+ return vtpm_drv_error_get();
+}
+
+void spapr_vtpm_set_error(vtpm_drv_error errcode)
+{
+ spapr_vtpm.driver_error = errcode;
+}
+
+bool spapr_is_vtpm_present(void)
+{
+ return spapr_vtpm_probe();
+}
+
+int spapr_transmit(uint8_t locty, struct tpm_req_header *req,
+ void *respbuffer, uint32_t *respbufferlen,
+ enum tpm_duration_type to_t)
+{
+ if (locty)
+ return -1;
+ if (!spapr_vtpm_senddata((uint8_t *)req, be32_to_cpu(req->totlen)) ||
+ !spapr_vtpm_waitresponseready(to_t) ||
+ !spapr_vtpm_readresponse(respbuffer, respbufferlen) ||
+ *respbufferlen < sizeof(struct tpm_rsp_header))
+ return -1;
+ return 0;
+}
diff --git a/roms/SLOF/lib/libtpm/tpm_drivers.h b/roms/SLOF/lib/libtpm/tpm_drivers.h
new file mode 100644
index 000000000..b5d347f49
--- /dev/null
+++ b/roms/SLOF/lib/libtpm/tpm_drivers.h
@@ -0,0 +1,82 @@
+/*****************************************************************************
+ * 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
+ *****************************************************************************/
+
+#ifndef TPM_DRIVERS_H
+#define TPM_DRIVERS_H
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <unistd.h>
+
+#include "tcgbios_int.h"
+
+enum tpm_duration_type {
+ TPM_DURATION_TYPE_SHORT = 0,
+ TPM_DURATION_TYPE_MEDIUM,
+ TPM_DURATION_TYPE_LONG,
+};
+
+/* firmware driver states */
+typedef enum {
+ VTPM_DRV_STATE_INVALID = 0,
+ VTPM_DRV_STATE_INIT_CALLED = 1,
+ VTPM_DRV_STATE_REG_CRQ = 2,
+ VTPM_DRV_STATE_WAIT_INIT = 3,
+ VTPM_DRV_STATE_SEND_INIT = 4,
+ VTPM_DRV_STATE_FAILURE = 5,
+ VTPM_DRV_STATE_WAIT_INIT_COMP = 6,
+ VTPM_DRV_STATE_SEND_INIT_COMP = 7,
+ VTPM_DRV_STATE_SEND_GET_VERSION = 8,
+ VTPM_DRV_STATE_WAIT_VERSION = 9,
+ VTPM_DRV_STATE_CHECK_VERSION = 10,
+ VTPM_DRV_STATE_SEND_BUFSIZE_REQ = 11,
+ VTPM_DRV_STATE_WAIT_BUFSIZE = 12,
+ VTPM_DRV_STATE_ALLOC_RTCE_BUF = 13,
+ VTPM_DRV_STATE_SEND_TPM_CMD = 14,
+ VTPM_DRV_STATE_WAIT_TPM_RSP = 15,
+} vtpm_drv_state;
+
+/* firmware driver errors */
+typedef enum {
+ VTPM_DRV_ERROR_NO_FAILURE = -1,
+ VTPM_DRV_ERROR_NOT_FOUND_TIMEOUT = 0,
+ VTPM_DRV_ERROR_UNEXPECTED_REG_ERROR = 1,
+ VTPM_DRV_ERROR_PARTNER_FAILED = 2,
+ VTPM_DRV_ERROR_UNEXPECTED_TSP_ERROR = 3,
+ VTPM_DRV_ERROR_TPM_PROTOCOL_ERROR = 4,
+ VTPM_DRV_ERROR_WAIT_TIMEOUT = 5,
+ VTPM_DRV_ERROR_UNEXPECTED_SEND_ERROR = 6,
+ VTPM_DRV_ERROR_CRQ_OPEN_FAIL = 7,
+ VTPM_DRV_ERROR_BAD_STATE = 8,
+ VTPM_DRV_ERROR_TPM_FAIL = 9,
+ VTPM_DRV_ERROR_TPM_CRQ_ERROR = 10,
+ VTPM_DRV_ERROR_BAD_VERSION = 11,
+ VTPM_DRV_ERROR_BAD_RTCE_SIZE = 12,
+ VTPM_DRV_ERROR_SML_FAILURE = 13,
+ VTPM_DRV_ERROR_SML_HANDED_OVER = 14,
+} vtpm_drv_error;
+
+/* the max. buffer size by the external TPM is 4k */
+#define PAPR_VTPM_MAX_BUFFER_SIZE 4096
+
+/* exported functions */
+bool spapr_is_vtpm_present(void);
+void spapr_vtpm_finalize(void);
+vtpm_drv_error spapr_vtpm_get_error(void);
+void spapr_vtpm_set_error(vtpm_drv_error errcode);
+
+struct tpm_req_header;
+int spapr_transmit(uint8_t locty, struct tpm_req_header *req,
+ void *respbuffer, uint32_t *respbufferlen,
+ enum tpm_duration_type to_t);
+
+#endif /* TPM_DRIVERS_H */