[SLOF] [PATCH v7 6/8] tcgbios: Add TPM 2.0 support and firmware API

Kevin O'Connor kevin at koconnor.net
Sat Feb 15 02:36:17 AEDT 2020


On Tue, Jan 21, 2020 at 03:01:45PM -0500, Stefan Berger wrote:
> This patch adds TPM 2.0 support along with the firmware API that Linux
> uses to transfer the firmware log.
> 
> The firmware API follows the "PFW Virtual TPM Driver" specification.
> The API has callers in existing Linux code (prom_init.c) from TPM 1.2
> times but the API also works for TPM 2.0 without modifications.
> 
> The TPM 2.0 support logs PCR extensions of measurements of code and data.
> For this part we follow the TCG specification "TCG PC Client
> Platform Firmware Profile Specification" (section "Event Logging").
> 
> Other relevant specs for the construction of TPM commands are:
> - Trusted Platform Module Library; Part 2 Structures
> - Trusted Platform Module Library; Part 3 Commands
> 
> Signed-off-by: Stefan Berger <stefanb at linux.ibm.com>

Signed-off-by: Kevin O'Connor <kevin at koconnor.net>

-Kevin


> ---
>  board-qemu/slof/Makefile            |  13 +-
>  board-qemu/slof/tree.fs             |   3 +
>  board-qemu/slof/vio-vtpm-cdriver.fs | 105 ++++
>  board-qemu/slof/vtpm-sml.fs         |  68 ++
>  include/helpers.h                   |   1 +
>  lib/libtpm/Makefile                 |   2 +-
>  lib/libtpm/tcgbios.c                | 925 ++++++++++++++++++++++++++++
>  lib/libtpm/tcgbios.h                |  32 +
>  lib/libtpm/tcgbios_int.h            | 241 ++++++++
>  lib/libtpm/tpm.code                 | 130 ++++
>  lib/libtpm/tpm.in                   |  26 +
>  slof/fs/packages/disk-label.fs      |   9 +
>  slof/fs/start-up.fs                 |   5 +
>  13 files changed, 1556 insertions(+), 4 deletions(-)
>  create mode 100644 board-qemu/slof/vio-vtpm-cdriver.fs
>  create mode 100644 board-qemu/slof/vtpm-sml.fs
>  create mode 100644 lib/libtpm/tcgbios.c
>  create mode 100644 lib/libtpm/tcgbios.h
>  create mode 100644 lib/libtpm/tpm.code
>  create mode 100644 lib/libtpm/tpm.in
> 
> diff --git a/board-qemu/slof/Makefile b/board-qemu/slof/Makefile
> index d7ed2d7..a8cff6d 100644
> --- a/board-qemu/slof/Makefile
> +++ b/board-qemu/slof/Makefile
> @@ -22,7 +22,8 @@ CPPFLAGS = -I$(LIBCMNDIR)/libbootmsg -I$(LIBCMNDIR)/libhvcall \
>  	   -I$(LIBCMNDIR)/libvirtio -I$(LIBCMNDIR)/libnvram \
>  	   -I$(LIBCMNDIR)/libusb -I$(LIBCMNDIR)/libveth \
>  	   -I$(LIBCMNDIR)/libe1k -I$(LIBCMNDIR)/libnet \
> -	   -I$(LIBCMNDIR)/libbootmenu
> +	   -I$(LIBCMNDIR)/libbootmenu -I$(LIBCMNDIR)/libtpm
> +
>  SLOF_LIBS = \
>  	$(LIBCMNDIR)/libbootmsg.a \
>  	$(LIBCMNDIR)/libelf.a \
> @@ -33,7 +34,9 @@ SLOF_LIBS = \
>  	$(LIBCMNDIR)/libveth.a \
>  	$(LIBCMNDIR)/libe1k.a \
>  	$(LIBCMNDIR)/libnet.a \
> -	$(LIBCMNDIR)/libbootmenu.a
> +	$(LIBCMNDIR)/libbootmenu.a \
> +	$(LIBCMNDIR)/libtpm.a
> +
>  BOARD_SLOF_IN = \
>  	$(LIBCMNDIR)/libhvcall/hvcall.in \
>  	$(LIBCMNDIR)/libvirtio/virtio.in \
> @@ -45,7 +48,9 @@ BOARD_SLOF_IN = \
>  	$(LIBCMNDIR)/libveth/veth.in \
>  	$(LIBCMNDIR)/libe1k/e1k.in \
>  	$(LIBCMNDIR)/libnet/libnet.in \
> -	$(LIBCMNDIR)/libbootmenu/bootmenu.in
> +	$(LIBCMNDIR)/libbootmenu/bootmenu.in \
> +	$(LIBCMNDIR)/libtpm/tpm.in
> +
>  BOARD_SLOF_CODE = $(BOARD_SLOF_IN:%.in=%.code)
>  
>  include $(SLOFCMNDIR)/Makefile.inc
> @@ -83,6 +88,7 @@ VIO_FFS_FILES = \
>  	$(SLOFBRDDIR)/pci-device_1af4_1050.fs \
>  	$(SLOFBRDDIR)/vio-hvterm.fs \
>  	$(SLOFBRDDIR)/vio-vscsi.fs \
> +	$(SLOFBRDDIR)/vio-vtpm-cdriver.fs \
>  	$(SLOFBRDDIR)/vio-veth.fs \
>  	$(SLOFBRDDIR)/rtas-nvram.fs \
>  	$(SLOFBRDDIR)/virtio-net.fs \
> @@ -114,6 +120,7 @@ OF_FFS_FILES = \
>  	$(SLOFBRDDIR)/default-font.bin \
>  	$(SLOFBRDDIR)/pci-phb.fs \
>  	$(SLOFBRDDIR)/rtas.fs \
> +	$(SLOFBRDDIR)/vtpm-sml.fs \
>  	$(SLOFBRDDIR)/pci-device_1234_1111.fs \
>  	$(SLOFBRDDIR)/pci-device_1013_00b8.fs \
>  	$(SLOFBRDDIR)/pci-device_8086_100e.fs \
> diff --git a/board-qemu/slof/tree.fs b/board-qemu/slof/tree.fs
> index d95fde3..7b34125 100644
> --- a/board-qemu/slof/tree.fs
> +++ b/board-qemu/slof/tree.fs
> @@ -87,6 +87,9 @@ include fbuffer.fs
>  	    2dup " qemu,spapr-nvram" strequal IF
>  	    	" rtas-nvram.fs" included
>  	    THEN
> +	    2dup " IBM,vtpm20" strequal IF
> +                " vio-vtpm-cdriver.fs" included
> +	    THEN
>              2drop
>         THEN
>         peer
> diff --git a/board-qemu/slof/vio-vtpm-cdriver.fs b/board-qemu/slof/vio-vtpm-cdriver.fs
> new file mode 100644
> index 0000000..7a2ffc3
> --- /dev/null
> +++ b/board-qemu/slof/vio-vtpm-cdriver.fs
> @@ -0,0 +1,105 @@
> +\ *****************************************************************************
> +\ * 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
> +\ ****************************************************************************/
> +
> +." Populating " pwd cr
> +
> +false VALUE vtpm-debug?
> +0     VALUE vtpm-unit
> +
> +0     VALUE    log-base
> +40000 CONSTANT LOG-SIZE   \ 256k per VTPM FW spec.
> +
> +e     CONSTANT VTPM_DRV_ERROR_SML_HANDED_OVER
> +
> +LOG-SIZE BUFFER: log-base
> +
> +\ firmware API call
> +: sml-get-allocated-size ( -- buffer-size)
> +   LOG-SIZE
> +;
> +
> +\ firmware API call
> +: sml-get-handover-size ( -- size)
> +   tpm-get-logsize
> +;
> +
> +\ firmware API call
> +: sml-handover ( dest size -- )
> +   log-base    ( dest size src )
> +   -rot        ( src dest size )
> +   move
> +
> +   VTPM_DRV_ERROR_SML_HANDED_OVER tpm-driver-set-failure-reason
> +;
> +
> +\ firmware API call
> +: get-failure-reason ( -- reason )
> +   tpm-driver-get-failure-reason           ( reason )
> +;
> +
> +0 0 s" ibm,sml-efi-reformat-supported" property
> +
> +\ firmware API call
> +: reformat-sml-to-efi-alignment ( -- success )
> +   true
> +;
> +
> +: open true ;
> +: close ;
> +
> +: vtpm-cleanup ( -- )
> +   vtpm-debug? IF ." VTPM: Disabling RTAS bypass" cr THEN
> +   tpm-finalize
> +   \ Disable TCE bypass
> +   vtpm-unit 0 rtas-set-tce-bypass
> +;
> +
> +: vtpm-init ( -- success )
> +   0 0 get-node open-node ?dup 0= IF false EXIT THEN
> +   my-self >r
> +   dup to my-self
> +
> +   vtpm-debug? IF ." VTPM: Initializing for c-driver" cr THEN
> +
> +   my-unit to vtpm-unit
> +
> +   \ Enable TCE bypass special qemu feature
> +   vtpm-unit 1 rtas-set-tce-bypass
> +
> +   \ Have TCE bypass cleaned up
> +   ['] vtpm-cleanup add-quiesce-xt
> +
> +   \ close temporary node
> +   close-node
> +   r> to my-self
> +
> +   tpm-start ?dup 0= IF
> +      vtpm-debug? IF ." VTPM: Success from tpm-start" cr THEN
> +      true
> +   ELSE
> +      ." VTPM: Error code from tpm-start: " . cr
> +      false
> +   THEN
> +;
> +
> +\ inititialize unit and set RTAS bypass
> +vtpm-init IF
> +   \ pass logbase and size to the C driver; we may only do this after
> +   \ init of the lower levels since this calls needs to know the PCR banks
> +   \ when setting up the log
> +   log-base LOG-SIZE tpm-set-log-parameters
> +   s" vtpm-sml.fs" included
> +ELSE
> +   ." VTPM: vtpm-init failed" cr
> +THEN
> +
> +
> diff --git a/board-qemu/slof/vtpm-sml.fs b/board-qemu/slof/vtpm-sml.fs
> new file mode 100644
> index 0000000..8e2ab6d
> --- /dev/null
> +++ b/board-qemu/slof/vtpm-sml.fs
> @@ -0,0 +1,68 @@
> +\ *****************************************************************************
> +\ * 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
> +\ ****************************************************************************/
> +
> +" /" find-device
> +
> +new-device
> +
> +false VALUE    vtpm-debug?
> +
> +\ create /ibm,vtpm
> +s" ibm,vtpm" 2dup device-name device-type
> +
> +\
> +\ only internal API calls
> +\
> +
> +: separator-event ( start-pcr end-pcr -- )
> +    tpm-add-event-separators                   ( errcode )
> +    ?dup IF
> +        ." VTPM: Error code from tpm-add-event-separators: " . cr
> +    THEN
> +;
> +
> +80 CONSTANT BCV_DEVICE_HDD
> +
> +: measure-hdd-mbr ( addr length -- )
> +    0 7 separator-event
> +    BCV_DEVICE_HDD                             ( addr length bootdrv )
> +    -rot                                       ( bootdrv addr length )
> +    tpm-measure-bcv-mbr                        ( errcode )
> +    ?dup IF
> +        ." VTPM: Error code from tpm-measure-hdd: " . cr
> +    THEN
> +;
> +
> +: leave-firmware ( -- )
> +    tpm-leave-firmware                         ( errcode )
> +    ?dup IF
> +        ." VTPM: Error code from tpm-leave-firmware: " . cr
> +    THEN
> +;
> +
> +: measure-scrtm ( -- )
> +    tpm-measure-scrtm                          ( errcode )
> +    ?dup IF
> +        ." VTPM: Error code from tpm-measure-scrtm: " . cr
> +    THEN
> +;
> +
> +: open  true ;
> +: close ;
> +
> +finish-device
> +device-end
> +
> +s" /ibm,vtpm" find-node ?dup IF
> +  s" measure-scrtm" rot $call-static
> +THEN
> +
> diff --git a/include/helpers.h b/include/helpers.h
> index 2f460d6..47b2674 100644
> --- a/include/helpers.h
> +++ b/include/helpers.h
> @@ -50,5 +50,6 @@ extern unsigned long SLOF_get_vtpm_unit(void);
>  #define container_of(ptr, type, member) ({                      \
>  			const typeof(((type *)0)->member)* struct_ptr = (ptr); \
>  			(type *)((char *)struct_ptr - offset_of(type, member)); })
> +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
>  
>  #endif
> diff --git a/lib/libtpm/Makefile b/lib/libtpm/Makefile
> index 8fa781e..bcfe88d 100644
> --- a/lib/libtpm/Makefile
> +++ b/lib/libtpm/Makefile
> @@ -23,7 +23,7 @@ TARGET = ../libtpm.a
>  
>  all: $(TARGET)
>  
> -SRCS = tpm_drivers.c sha256.c
> +SRCS = tpm_drivers.c sha256.c tcgbios.c
>  
>  OBJS = $(SRCS:%.c=%.o)
>  
> diff --git a/lib/libtpm/tcgbios.c b/lib/libtpm/tcgbios.c
> new file mode 100644
> index 0000000..d81d392
> --- /dev/null
> +++ b/lib/libtpm/tcgbios.c
> @@ -0,0 +1,925 @@
> +/*****************************************************************************
> + * 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 at linux.ibm.com
> + *     Kevin O'Connor, kevin at 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 "sha256.h"
> +#include "helpers.h"
> +#include "version.h"
> +#include "OF.h"
> +
> +#undef TCGBIOS_DEBUG
> +//#define TCGBIOS_DEBUG
> +#ifdef TCGBIOS_DEBUG
> +#define dprintf(_x ...) do { printf("TCGBIOS: " _x); } while(0)
> +#else
> +#define dprintf(_x ...)
> +#endif
> +
> +#define MIN(a, b) ((a) < (b) ? (a) : (b))
> +
> +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;
> +
> +/*
> + * 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)
> +	   + 5 * sizeof(struct TPMT_HA)
> +	   + SHA1_BUFSIZE + SHA256_BUFSIZE + SHA384_BUFSIZE
> +	   + SHA512_BUFSIZE + SM3_256_BUFSIZE];
> +} __attribute__((packed));
> +
> +static const struct hash_parameters {
> +	uint16_t hashalg;
> +	uint8_t  hash_buffersize;
> +} hash_parameters[] = {
> +	{
> +		.hashalg = TPM2_ALG_SHA1,
> +		.hash_buffersize = SHA1_BUFSIZE,
> +	}, {
> +		.hashalg = TPM2_ALG_SHA256,
> +		.hash_buffersize = SHA256_BUFSIZE,
> +	}, {
> +		.hashalg = TPM2_ALG_SHA384,
> +		.hash_buffersize = SHA384_BUFSIZE,
> +	}, {
> +		.hashalg = TPM2_ALG_SHA512,
> +		.hash_buffersize = SHA512_BUFSIZE,
> +	}, {
> +		.hashalg = TPM2_ALG_SM3_256,
> +		.hash_buffersize = SM3_256_BUFSIZE,
> +	}
> +};
> +
> +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 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;
> +}
> +
> +/*
> + * 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
> + * sha1: the sha1 hash value to use
> + * 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 *sha256,
> +			      bool bigEndian)
> +{
> +	struct tpms_pcr_selection *sel;
> +	void *nsel, *end;
> +	void *dest = le->hdr.digests + sizeof(struct TPML_DIGEST_VALUES);
> +	uint32_t count;
> +	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;
> +	     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;
> +
> +		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));
> +
> +		memset(v->hash, 0, hsize);
> +		memcpy(v->hash, sha256,
> +		       hsize < SHA256_BUFSIZE ? hsize : SHA256_BUFSIZE);
> +
> +		dest += sizeof(*v) + hsize;
> +		sel = nsel;
> +	}
> +
> +	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(count);
> +	else
> +		vs->count = cpu_to_le32(count);
> +
> +	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 : 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
> +						 : 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[256];
> +	} event = {
> +		.hdr.signature = "Spec ID Event03",
> +		.hdr.platformClass = TPM_TCPA_ACPI_CLASS_CLIENT,
> +		.hdr.specVersionMinor = 0,
> +		.hdr.specVersionMajor = 2,
> +		.hdr.specErrata = 0,
> +		.hdr.uintnSize = 2,
> +	};
> +	struct tpms_pcr_selection *sel;
> +	void *nsel, *end;
> +	int event_size;
> +	uint32_t *vendorInfoSize;
> +	struct tpm_log_entry le = {
> +		.hdr.eventtype = cpu_to_log32(EV_NO_ACTION),
> +	};
> +	uint32_t count;
> +
> +	sel = tpm_state.tpm20_pcr_selection->selections;
> +	end = (void*)tpm_state.tpm20_pcr_selection +
> +	      tpm_state.tpm20_pcr_selection_size;
> +
> +	for (count = 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;
> +
> +		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(uint32_t)) {
> +			dprintf("EfiSpecIdEventStruct pad too small\n");
> +			return -1;
> +		}
> +
> +		event.hdr.digestSizes[count].algorithmId =
> +			cpu_to_log16(be16_to_cpu(sel->hashAlg));
> +		event.hdr.digestSizes[count].digestSize = cpu_to_log16(hsize);
> +
> +		sel = nsel;
> +	}
> +
> +	if (sel != end) {
> +		dprintf("Malformed pcr selection structure fron TPM\n");
> +		return -1;
> +	}
> +
> +	event.hdr.numberOfAlgorithms = cpu_to_log32(count);
> +	event_size = offset_of(struct TCG_EfiSpecIdEventStruct,
> +			       digestSizes[count]);
> +	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)
> +{
> +	uint8_t hash[SHA256_BUFSIZE];
> +	struct tpm_log_entry le = {
> +		.hdr.pcrindex = cpu_to_log32(pcrindex),
> +		.hdr.eventtype = cpu_to_log32(eventtype),
> +	};
> +	int digest_len;
> +	int ret;
> +
> +	sha256(hashdata, hashdatalen, hash);
> +	digest_len = tpm20_build_digest(&le, hash, 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, hash, false);
> +	return tpm_log_event_long(&le.hdr, digest_len, info, infolen);
> +}
> +
> +/*
> + * 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,
> +						NULL, 0,
> +						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);
> +}
> +
> +uint32_t tpm_measure_scrtm(void)
> +{
> +	uint32_t rc;
> +	char *version_start = strstr((char *)&print_version, "FW Version");
> +	char *version_end;
> +	uint32_t version_length;
> +	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";
> +
> +	version_end = strchr(version_start, '\r');
> +	version_length = version_end - version_start;
> +
> +	dprintf("Measure S-CRTM Version: addr = %p, length = %d\n",
> +		version_start, version_length);
> +
> +	rc = tpm_add_measurement_to_log(0, EV_S_CRTM_VERSION,
> +					version_start, version_length,
> +					(uint8_t *)version_start,
> +					version_length);
> +	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);
> +}
> diff --git a/lib/libtpm/tcgbios.h b/lib/libtpm/tcgbios.h
> new file mode 100644
> index 0000000..e9f9c36
> --- /dev/null
> +++ b/lib/libtpm/tcgbios.h
> @@ -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
> + *****************************************************************************/
> +
> +#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);
> +
> +#endif /* TCGBIOS_H */
> diff --git a/lib/libtpm/tcgbios_int.h b/lib/libtpm/tcgbios_int.h
> index 835bd36..6892e6f 100644
> --- a/lib/libtpm/tcgbios_int.h
> +++ b/lib/libtpm/tcgbios_int.h
> @@ -15,6 +15,83 @@
>  
>  #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 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
> +
> +/*
> + * 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[0];
> +	/*
> +	uint8_t vendorInfoSize;
> +	uint8_t vendorInfo[0];
> +	*/
> +} __attribute__((packed));
> +
> +/* Input and Output headers for all TPM commands */
>  struct tpm_req_header {
>  	uint16_t tag;
>  	uint32_t totlen;
> @@ -27,4 +104,168 @@ struct tpm_rsp_header {
>  	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
> +
> +/* 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/lib/libtpm/tpm.code b/lib/libtpm/tpm.code
> new file mode 100644
> index 0000000..05f4547
> --- /dev/null
> +++ b/lib/libtpm/tpm.code
> @@ -0,0 +1,130 @@
> +/******************************************************************************
> + * 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
> diff --git a/lib/libtpm/tpm.in b/lib/libtpm/tpm.in
> new file mode 100644
> index 0000000..22713e4
> --- /dev/null
> +++ b/lib/libtpm/tpm.in
> @@ -0,0 +1,26 @@
> +/******************************************************************************
> + * 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)
> diff --git a/slof/fs/packages/disk-label.fs b/slof/fs/packages/disk-label.fs
> index 8859fb0..252a44e 100644
> --- a/slof/fs/packages/disk-label.fs
> +++ b/slof/fs/packages/disk-label.fs
> @@ -338,6 +338,14 @@ CONSTANT /gpt-part-entry
>     dup c@ eb = swap 2+ c@ 90 = and
>  ;
>  
> +: measure-mbr ( addr length -- )
> +   s" /ibm,vtpm" find-node ?dup IF
> +      s" measure-hdd-mbr" rot $call-static
> +   ELSE
> +      2drop
> +   THEN
> +;
> +
>  \ NOTE: block-size is always 512 bytes for DOS partition tables.
>  
>  : load-from-dos-boot-partition ( addr -- size )
> @@ -361,6 +369,7 @@ CONSTANT /gpt-part-entry
>              block-size * to part-offset
>              0 0 seek drop            ( addr offset )
>              block-size * read        ( size )
> +            block block-size measure-mbr
>              UNLOOP EXIT
>           ELSE
>              2drop                    ( addr )
> diff --git a/slof/fs/start-up.fs b/slof/fs/start-up.fs
> index 7020f5c..c1f931a 100644
> --- a/slof/fs/start-up.fs
> +++ b/slof/fs/start-up.fs
> @@ -56,6 +56,11 @@
>  ;
>  
>  : (boot?) ( -- )
> +   \ last step before we boot we give up physical presence on the TPM
> +   s" /ibm,vtpm" find-node ?dup IF
> +      s" leave-firmware" rot $call-static
> +   THEN
> +
>     of-prompt? not auto-boot? and IF
>        (boot)
>     THEN
> -- 
> 2.24.1
> 


More information about the SLOF mailing list