diff options
Diffstat (limited to 'roms/SLOF/lib/libtpm')
-rw-r--r-- | roms/SLOF/lib/libtpm/Makefile | 51 | ||||
-rw-r--r-- | roms/SLOF/lib/libtpm/Readme | 57 | ||||
-rw-r--r-- | roms/SLOF/lib/libtpm/sha.c | 232 | ||||
-rw-r--r-- | roms/SLOF/lib/libtpm/sha.h | 23 | ||||
-rw-r--r-- | roms/SLOF/lib/libtpm/sha256.c | 246 | ||||
-rw-r--r-- | roms/SLOF/lib/libtpm/sha512.c | 285 | ||||
-rw-r--r-- | roms/SLOF/lib/libtpm/sha_test.h | 59 | ||||
-rw-r--r-- | roms/SLOF/lib/libtpm/tcgbios.c | 1477 | ||||
-rw-r--r-- | roms/SLOF/lib/libtpm/tcgbios.h | 45 | ||||
-rw-r--r-- | roms/SLOF/lib/libtpm/tcgbios_int.h | 317 | ||||
-rwxr-xr-x | roms/SLOF/lib/libtpm/test.sh | 31 | ||||
-rw-r--r-- | roms/SLOF/lib/libtpm/tpm.code | 208 | ||||
-rw-r--r-- | roms/SLOF/lib/libtpm/tpm.in | 32 | ||||
-rw-r--r-- | roms/SLOF/lib/libtpm/tpm_drivers.c | 436 | ||||
-rw-r--r-- | roms/SLOF/lib/libtpm/tpm_drivers.h | 82 |
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 */ |