diff mbox series

[v6,6/7] tcgbios: Add TPM 2.0 support and firmware API

Message ID 20200115200048.15137-7-stefanb@linux.ibm.com
State Superseded
Headers show
Series Add vTPM 2.0 support to SLOF | expand

Commit Message

Stefan Berger Jan. 15, 2020, 8 p.m. UTC
This patch adds TPM 2.0 support along with the firmware API
that Linux uses to transfer the firmware log.

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
---
 board-qemu/slof/Makefile            |  13 +-
 board-qemu/slof/tree.fs             |   3 +
 board-qemu/slof/vio-vtpm-cdriver.fs | 134 ++++
 board-qemu/slof/vtpm-sml.fs         | 115 ++++
 include/helpers.h                   |   1 +
 lib/libtpm/Makefile                 |   2 +-
 lib/libtpm/tcgbios.c                | 918 ++++++++++++++++++++++++++++
 lib/libtpm/tcgbios.h                |  32 +
 lib/libtpm/tcgbios_int.h            | 240 ++++++++
 lib/libtpm/tpm.code                 | 130 ++++
 lib/libtpm/tpm.in                   |  26 +
 slof/fs/packages/disk-label.fs      |  10 +-
 slof/fs/start-up.fs                 |   5 +
 13 files changed, 1624 insertions(+), 5 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

Comments

Alexey Kardashevskiy Jan. 20, 2020, 8:09 a.m. UTC | #1
On 16/01/2020 07:00, Stefan Berger wrote:
> This patch adds TPM 2.0 support along with the firmware API
> that Linux uses to transfer the firmware log.
> 
> Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
> ---
>  board-qemu/slof/Makefile            |  13 +-
>  board-qemu/slof/tree.fs             |   3 +
>  board-qemu/slof/vio-vtpm-cdriver.fs | 134 ++++
>  board-qemu/slof/vtpm-sml.fs         | 115 ++++
>  include/helpers.h                   |   1 +
>  lib/libtpm/Makefile                 |   2 +-
>  lib/libtpm/tcgbios.c                | 918 ++++++++++++++++++++++++++++
>  lib/libtpm/tcgbios.h                |  32 +
>  lib/libtpm/tcgbios_int.h            | 240 ++++++++
>  lib/libtpm/tpm.code                 | 130 ++++
>  lib/libtpm/tpm.in                   |  26 +
>  slof/fs/packages/disk-label.fs      |  10 +-
>  slof/fs/start-up.fs                 |   5 +
>  13 files changed, 1624 insertions(+), 5 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 \


s/vio-vtpm-cdriver.fs/vio-vtpm.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..8d17d0e
> --- /dev/null
> +++ b/board-qemu/slof/vio-vtpm-cdriver.fs
> @@ -0,0 +1,134 @@
> +\ *****************************************************************************
> +\ * 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
> +
> +false VALUE vtpm-debug?
> +0     VALUE vtpm-unit
> +0     VALUE vtpm-ihandle
> +
> +: setup-alias
> +    " ibm,vtpm" find-alias 0= IF
> +        " ibm,vtpm" get-node node>path set-alias
> +    ELSE
> +        drop
> +    THEN
> +;
> +
> +: vtpm-cleanup ( )
> +    vtpm-debug? IF ." VTPM: Disabling RTAS bypass" cr THEN
> +    tpm-finalize
> +    vtpm-unit 0 rtas-set-tce-bypass
> +;
> +
> +: vtpm-init ( -- true | false )
> +    0 0 get-node open-node ?dup 0= IF 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
> +
> +    tpm-start ?dup 0= IF
> +        vtpm-debug? IF ." VTPM: Success from tpm-start" cr THEN
> +        setup-alias
> +    ELSE
> +        ." VTPM: Error code from tpm-start: " . cr
> +    THEN
> +
> +    close-node
> +    r> to my-self
> +;
> +
> +\ forward a call to /ibm,vtpm, which implements the function with the
> +\ given name
> +: vtpm-call-forward ( arg ... arg name namelen -- ret ... ret failure? )
> +    \ assign /ibm,vtpm node to vtpm-ihandle, if not assigned
> +    vtpm-ihandle 0= IF
> +        s" /ibm,vtpm" open-dev to vtpm-ihandle


Why does not "open" do this? Is this vtpm supposed to run even before
the client tries using the vtpm services? It does not look like it.


> +    THEN
> +
> +    vtpm-ihandle 0<> IF
> +        vtpm-ihandle                   ( arg ... arg name namelen ihandle )
> +        $call-method                   ( ret ... ret )
> +        false                          ( ret ... ret false )
> +    ELSE
> +        true                           ( true )
> +    THEN
> +;
> +
> +\ firmware API call
> +: sml-get-allocated-size ( -- buffer-size)
> +    " sml-get-allocated-size" vtpm-call-forward IF
> +        \ vtpm-call-forward failed
> +        0
> +    THEN
> +;
> +
> +\ firmware API call
> +: sml-get-handover-size ( -- size)
> +    " sml-get-handover-size" vtpm-call-forward IF
> +        \ vtpm-call-forward failed
> +        0
> +    THEN
> +;
> +
> +\ firmware API call
> +: sml-handover ( dest size -- )
> +    " sml-handover" vtpm-call-forward IF
> +        \ vtpm-call-forward failed; clean up stack
> +        2drop
> +    THEN
> +;
> +
> +\ firmware API call
> +: get-failure-reason ( -- reason )
> +    " get-failure-reason" vtpm-call-forward IF
> +        \ vtpm-call-forward failed; return a value
> +        0 \ invalid
> +    THEN
> +;
> +
> +0 0 s" ibm,sml-efi-reformat-supported" property
> +
> +\ firmware API call
> +: reformat-sml-to-efi-alignment ( -- success )
> +    " reformat-sml-to-efi-alignment" vtpm-call-forward IF
> +        false
> +    THEN
> +;
> +
> +: open ( )
> +    vtpm-debug? IF ." VTPM: vTPM open()" cr THEN
> +    true
> +;
> +
> +: close ( )
> +    vtpm-debug? IF ." VTPM: vTPM close()" cr THEN
> +;
> +
> +\ setup alias and the RTAS bypass
> +vtpm-init
> +
> +\ setup the log
> +include vtpm-sml.fs
> +
> +s" /ibm,vtpm" find-node ?dup IF
> +  s" measure-scrtm" rot $call-static
> +THEN


The above 22 lines confuse me a lot.
Why vtpm-sml.fs?
Why "open" does not open?
Why vtpm-init is not in "open"?
Why the device methods are in vtpm-sml.fs?

The Linux finds the device, opens it and calls methods (passing
ihandle), why this complication?

I am missing the point in all of this and 2 lines commit log does not
help at all.




> diff --git a/board-qemu/slof/vtpm-sml.fs b/board-qemu/slof/vtpm-sml.fs
> new file mode 100644
> index 0000000..865dce6
> --- /dev/null
> +++ b/board-qemu/slof/vtpm-sml.fs
> @@ -0,0 +1,115 @@
> +\ *****************************************************************************
> +\ * 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
> +\ ****************************************************************************/
> +
> +\ KVM/qemu TPM Stored Measurement Log (SML) entries in /ibm,vtpm
> +
> +" /" find-device
> +
> +new-device
> +
> +false VALUE    vtpm-debug?
> +0     VALUE    log-base
> +40000 CONSTANT LOG-SIZE   \ 256k per VTPM FW spec.


What is this spec's name exactly? It may not be available to the public
but I could try and get it in IBM internally.


> +
> +e     CONSTANT VTPM_DRV_ERROR_SML_HANDED_OVER
> +
> +LOG-SIZE BUFFER: log-base
> +
> +\ create /ibm,vtpm
> +s" ibm,vtpm" 2dup device-name device-type
> +
> +\ convey logbase and size to the C driver
> +log-base LOG-SIZE tpm-set-log-parameters
> +
> +: sml-get-allocated-size ( -- buffer-size)
> +    vtpm-debug? IF
> +        ." Call to sml-get-allocated-size; size = 0x" LOG-SIZE . cr
> +    THEN
> +    LOG-SIZE
> +;
> +
> +: sml-get-handover-size ( -- size )
> +    tpm-get-logsize
> +    vtpm-debug? IF
> +        ." Call to sml-get-handover-size; size = 0x" dup . cr
> +    THEN
> +;
> +
> +: sml-handover ( dest size -- )
> +    vtpm-debug? IF
> +        2dup
> +        ." Call to sml-handover; size = 0x" . ." dest = " . cr
> +    THEN
> +    log-base        ( dest size src )
> +    -rot            ( src dest size )
> +    move
> +
> +    VTPM_DRV_ERROR_SML_HANDED_OVER tpm-driver-set-failure-reason
> +;
> +
> +: get-failure-reason ( -- reason )
> +    tpm-driver-get-failure-reason                  ( reason )
> +    vtpm-debug? IF
> +        ." VTPM: Return value from tpm-driver-get-failure-reason: " dup . cr
> +    THEN
> +;
> +
> +: reformat-sml-to-efi-alignment ( -- success? )
> +    vtpm-debug? IF
> +        ." Call to reformat-sml-to-efi-alignment" cr
> +    THEN
> +    \ a no-op since already byte aligned
> +    true
> +;
> +
> +\
> +\ 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 -- )
> +    0 7 separator-event
> +    200 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
> 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 7efad28..24eacc1 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..4e81a42
> --- /dev/null
> +++ b/lib/libtpm/tcgbios.c
> @@ -0,0 +1,918 @@
> +/*****************************************************************************
> + * 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 


Is this "A Protocol for VTPM Communications" from
M_vtpm_protocol_v0r3.pdf or something else?


> and the TCG Specification
> + *  that can be found here under the following link:
> + *  http://www.trustedcomputinggroup.org/resources/pc_client_work_group_specific_implementation_specification_for_conventional_bios


The link is broken.


> + */
> +
> +#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))
> +
> +struct tpm_state {
> +	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;
> +};
> +
> +static struct tpm_state tpm_state;

You do not pass a pointer to tpm_state anywhere (it would great if you
did - this way a reader could tell what functions actually need it) so
you do not need "struct tpm_state" type, can be just "struct tpm_state {
... } 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
> + ****************************************************************/
> +
> +static uint32_t tpm20_pcr_selection_size;
> +static struct tpml_pcr_selection *tpm20_pcr_selection;


Any reason these are not in tpm_state?


> +
> +/* A 'struct tpm_log_entry' is a local data structure containing a
> + * 'tpm_log_header' followed by space for the maximum supported
> + * digest. The digest is a series of tpm2_digest_value structs on tpm2.0.
> + */

This comment is rather useless, what I would really be interested to is
the endianness (little endian because it is v2.0?) and the name of the
spec which defines it.


> +struct tpm_log_entry {
> +	struct tpm_log_header hdr;
> +	uint8_t pad[sizeof(struct tpm2_digest_values)
> +	   + 5 * sizeof(struct tpm2_digest_value)
> +	   + SHA1_BUFSIZE + SHA256_BUFSIZE + SHA384_BUFSIZE
> +	   + SHA512_BUFSIZE + SM3_256_BUFSIZE];
> +} __attribute__((packed));
> +
> +static const struct hash_parameters {
> +	uint16_t hashalg;
> +	uint8_t  hashalg_flag;
> +	uint8_t  hash_buffersize;
> +	const char *name;

name and hashalg_flag are not used.


> +} hash_parameters[] = {
> +	{
> +		.hashalg = TPM2_ALG_SHA1,


TPM2_ALG_xxx are basically 1<<index_in_this_array and can be dropped,
can't they?


> +		.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 int
> +tpm20_get_hash_buffersize(uint16_t hashAlg)
> +{
> +	unsigned i;
> +
> +	for (i = 0; i < ARRAY_SIZE(hash_parameters); i++) {
> +		if (hash_parameters[i].hashalg == hashAlg)
> +			return hash_parameters[i].hash_buffersize;
> +	}
> +	return -1;

Just make it return (const struct hash_parameters *) as 7/7 adds one
helper like this for every field of hash_parameters.


> +}
> +
> +/*
> + * Build the TPM2 tpm2_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 sha1 hash in the area of the sha256
> + * hash and fill the remaining bytes with zeros. Or truncate the sha256
> + * hash when writing it in the area of the sha1 hash.

It must be correct since you did this but what is the point of this? If
the digest does not fit, fill it with 0xBAADF00D and return an error?


> + *
> + * 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)


Is not it always little endian as it is v2.0 only?

> +{
> +	struct tpms_pcr_selection *sel;
> +	void *nsel, *end;
> +	void *dest = le->hdr.digest + sizeof(struct tpm2_digest_values);
> +	uint32_t count;
> +	struct tpm2_digest_value *v;
> +	struct tpm2_digest_values *vs;
> +
> +	if (!tpm20_pcr_selection)


This is allocated at tpm20_startup() and if allocation failed, then we
should not get this far, or can we?


> +		return -1;
> +
> +	sel = tpm20_pcr_selection->selections;
> +	end = (void *)tpm20_pcr_selection + tpm20_pcr_selection_size;
> +
> +	for (count = 0; count < be32_to_cpu(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.digest;
> +	if (bigEndian)
> +		vs->count = cpu_to_be32(count);
> +	else
> +		vs->count = cpu_to_le32(count);
> +
> +	return dest - (void*)le->hdr.digest;
> +}
> +
> +/****************************************************************
> + * 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 = tpmhw_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 = tpmhw_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;
> +
> +	int 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 */
> +	uint32_t resplen = be32_to_cpu(trg->hdr.totlen);


-Werror=declaration-after-statement is not  enforced in SLOF (may be I
should fix it...) but we try follow the linux coding style here in SLOF
:) There are few spots like this.



> +	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;
> +
> +	tpm20_pcr_selection = SLOF_alloc_mem(size);
> +	if (tpm20_pcr_selection) {
> +		memcpy(tpm20_pcr_selection, &trg->data, size);
> +		tpm20_pcr_selection_size = size;
> +	} else {
> +		printf("TCGBIOS: Failed to allocated %u bytes.\n", size);
> +		ret = -1;

return -1;

> +	}
> +
> +	return ret;

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;
> +
> +	memcpy(tre, &tmp_tre, sizeof(tmp_tre));
> +	memcpy(&tre->digest[0], le->hdr.digest, digest_len);
> +
> +	tre->hdr.totlen = cpu_to_be32(sizeof(tmp_tre) + digest_len);
> +
> +	struct tpm_rsp_header rsp;
> +	uint32_t resp_length = sizeof(rsp);
> +	int ret = tpmhw_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 = tpmhw_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);
> +
> +	if (buf_len > sizeof(rsp.rnd.buffer))
> +		return -1;
> +
> +	int ret = tpmhw_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)),
> +		},
> +	};
> +	memcpy(trhca.newAuth.buffer, auth, sizeof(trhca.newAuth.buffer));
> +
> +	struct tpm_rsp_header rsp;
> +	uint32_t resp_length = sizeof(rsp);
> +	int ret = tpmhw_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)
> +{
> +	/* we will try to deactivate the TPM now - ignoring all errors */


Is the comment correct? Neither the name of the function nor the
structure below suggest deactivation, I may also be missing the point here.


> +	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 = tpmhw_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(struct tpm_log_header *entry,
> +				   int digest_len,
> +				   const void *event, uint32_t event_length)
> +{
> +	size_t size, logsize;
> +	void *dest;
> +
> +	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(struct tpm_log_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);
> +	struct tpm_log_trailer *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)
> +{
> +	if (!tpm20_pcr_selection)
> +		return -1;
> +
> +	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 = tpm20_pcr_selection->selections;
> +	void *nsel, *end = (void*)tpm20_pcr_selection + tpm20_pcr_selection_size;
> +	int event_size;
> +	uint32_t *vendorInfoSize;
> +	struct tpm_log_entry le = {
> +		.hdr.eventtype = cpu_to_log32(EV_NO_ACTION),
> +	};
> +	uint32_t count;
> +
> +	for (count = 0;
> +	     count < be32_to_cpu(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 = 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.


I do not see any caller of this one.


> + */
> +uint32_t tpm_leave_firmware(void)

static?


> +{
> +	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;
> +
> +	sha256(hashdata, hashdatalen, hash);
> +	digest_len = tpm20_build_digest(&le, hash, true);
> +	if (digest_len < 0)
> +		return TCGBIOS_GENERAL_ERROR;
> +	int 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 rc = 0;
> +	uint32_t pcrIndex;
> +
> +	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)
> +			break;

return 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 | sha1sum
> +	 */
> +	string = "MBR";


What about GPT (I am not extemely familiar with MBR/GPT but I see my
ubuntu images use GPT these days)?


> +	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 | sha1sum
> +	 */
> +	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_data_start = (char *)&_slof_data;
> +	char *slof_text_start = (char *)&_slof_text;
> +	uint32_t slof_data_length = (long)&_slof_data_end - (long)&_slof_data;
> +	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 (data): start = %p, length = %d\n",
> +		slof_data_start, slof_data_length);
> +
> +	rc = tpm_add_measurement_to_log(0, EV_S_CRTM_CONTENTS,
> +					scrtm, strlen(scrtm),
> +					(uint8_t *)slof_data_start,
> +					slof_data_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..3cc4f46 100644
> --- a/lib/libtpm/tcgbios_int.h
> +++ b/lib/libtpm/tcgbios_int.h
> @@ -15,6 +15,94 @@
>  
>  #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 */
> +#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 SHA1_BUFSIZE                    20
> +#define SHA256_BUFSIZE                  32
> +#define SHA384_BUFSIZE                  48
> +#define SHA512_BUFSIZE                  64
> +#define SM3_256_BUFSIZE                 32
> +
> +#define BCV_DEVICE_HDD     0x80
> +
> +struct tpm2_digest_value {
> +	uint16_t hashAlg;
> +	uint8_t hash[0]; /* size depends on hashAlg */
> +} __attribute__((packed));
> +
> +struct tpm2_digest_values {
> +	uint32_t count;
> +	struct tpm2_digest_value digest[0];
> +} __attribute__((packed));
> +
> +/* Each entry in the TPM log contains: a tpm_log_header, a variable
> + * length digest, a tpm_log_trailer, and a variable length event.  The
> + * 'digest' matches what is sent to the TPM hardware via the Extend
> + * command.  On TPM1.2 the digest is a SHA1 hash; on TPM2.0 the digest
> + * contains a tpm2_digest_values struct followed by a variable number
> + * of tpm2_digest_value structs (as specified by the hardware via the
> + * TPM2_CAP_PCRS request).
> + */
> +struct tpm_log_header {
> +	uint32_t pcrindex;
> +	uint32_t eventtype;
> +	uint8_t digest[0];
> +} __attribute__((packed));
> +
> +struct tpm_log_trailer {
> +	uint32_t eventdatasize;
> +	uint8_t event[0];
> +} __attribute__((packed));
> +
> +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 blocks for the TCG BIOS commands */
> +
> +/* PCClient_PCREventStruct -- format of log entries; compatible with x86 */
> +struct pcpes {
> +	uint32_t pcrindex;
> +	uint32_t eventtype;
> +	uint8_t digest[SHA1_BUFSIZE];
> +	uint32_t eventdatasize;
> +	uint32_t event;
> +} __attribute__((packed));
> +
>  struct tpm_req_header {
>  	uint16_t tag;
>  	uint32_t totlen;
> @@ -27,4 +115,156 @@ struct tpm_rsp_header {
>  	uint32_t errcode;
>  } __attribute__((packed));
>  
> +/****************************************************************
> + * TPM v2.0 hardware 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_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)
> +
> +/* 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 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));
> +
> +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));
> +


Please separate somehow what is defined for SLOF only (is
TPM2_ALG_SHA1_FLAG/etc?) and for everything else - what comes from what
spec.



>  #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()              */
> +/************************************************/


What is calling this one? Now I am really not sure they all are actually
have a user. Thanks,


> +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..77bb0f5 100644
> --- a/slof/fs/packages/disk-label.fs
> +++ b/slof/fs/packages/disk-label.fs
> @@ -550,7 +550,15 @@ B9E5                CONSTANT GPT-BASIC-DATA-PARTITION-2
>  \ load from a bootable partition
>  : load-from-boot-partition ( addr -- size )
>     debug-disk-label? IF ." Trying DOS boot " .s cr THEN
> -   dup load-from-dos-boot-partition ?dup 0 <> IF nip EXIT THEN
> +   dup load-from-dos-boot-partition ?dup 0 <> IF
> +      nip
> +      block s" /ibm,vtpm" find-node ?dup IF
> +         s" measure-hdd-mbr" rot $call-static
> +      ELSE
> +         drop
> +      THEN
> +      EXIT
> +   THEN
>  
>     debug-disk-label? IF ." Trying CHRP boot " .s cr THEN
>     1 disk-chrp-boot !
> 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
>
Stefan Berger Jan. 20, 2020, 5:08 p.m. UTC | #2
On 1/20/20 3:09 AM, Alexey Kardashevskiy wrote:
>
> On 16/01/2020 07:00, Stefan Berger wrote:
>> This patch adds TPM 2.0 support along with the firmware API
>> that Linux uses to transfer the firmware log.
>>
>> Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
>> ---
>>   board-qemu/slof/Makefile            |  13 +-
>>   board-qemu/slof/tree.fs             |   3 +
>>   board-qemu/slof/vio-vtpm-cdriver.fs | 134 ++++
>>   board-qemu/slof/vtpm-sml.fs         | 115 ++++
>>   include/helpers.h                   |   1 +
>>   lib/libtpm/Makefile                 |   2 +-
>>   lib/libtpm/tcgbios.c                | 918 ++++++++++++++++++++++++++++
>>   lib/libtpm/tcgbios.h                |  32 +
>>   lib/libtpm/tcgbios_int.h            | 240 ++++++++
>>   lib/libtpm/tpm.code                 | 130 ++++
>>   lib/libtpm/tpm.in                   |  26 +
>>   slof/fs/packages/disk-label.fs      |  10 +-
>>   slof/fs/start-up.fs                 |   5 +
>>   13 files changed, 1624 insertions(+), 5 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 \
>
> s/vio-vtpm-cdriver.fs/vio-vtpm.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..8d17d0e
>> --- /dev/null
>> +++ b/board-qemu/slof/vio-vtpm-cdriver.fs
>> @@ -0,0 +1,134 @@
>> +\ *****************************************************************************
>> +\ * 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
>> +
>> +false VALUE vtpm-debug?
>> +0     VALUE vtpm-unit
>> +0     VALUE vtpm-ihandle
>> +
>> +: setup-alias
>> +    " ibm,vtpm" find-alias 0= IF
>> +        " ibm,vtpm" get-node node>path set-alias
>> +    ELSE
>> +        drop
>> +    THEN
>> +;
>> +
>> +: vtpm-cleanup ( )
>> +    vtpm-debug? IF ." VTPM: Disabling RTAS bypass" cr THEN
>> +    tpm-finalize
>> +    vtpm-unit 0 rtas-set-tce-bypass
>> +;
>> +
>> +: vtpm-init ( -- true | false )
>> +    0 0 get-node open-node ?dup 0= IF 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
>> +
>> +    tpm-start ?dup 0= IF
>> +        vtpm-debug? IF ." VTPM: Success from tpm-start" cr THEN
>> +        setup-alias
>> +    ELSE
>> +        ." VTPM: Error code from tpm-start: " . cr
>> +    THEN
>> +
>> +    close-node
>> +    r> to my-self
>> +;
>> +
>> +\ forward a call to /ibm,vtpm, which implements the function with the
>> +\ given name
>> +: vtpm-call-forward ( arg ... arg name namelen -- ret ... ret failure? )
>> +    \ assign /ibm,vtpm node to vtpm-ihandle, if not assigned
>> +    vtpm-ihandle 0= IF
>> +        s" /ibm,vtpm" open-dev to vtpm-ihandle
>
> Why does not "open" do this? Is this vtpm supposed to run even before
> the client tries using the vtpm services? It does not look like it.

Initialization of the vTPM is supposed to happen before any client can 
talk to the firmware.


>
>
>> +    THEN
>> +
>> +    vtpm-ihandle 0<> IF
>> +        vtpm-ihandle                   ( arg ... arg name namelen ihandle )
>> +        $call-method                   ( ret ... ret )
>> +        false                          ( ret ... ret false )
>> +    ELSE
>> +        true                           ( true )
>> +    THEN
>> +;
>> +
>> +\ firmware API call
>> +: sml-get-allocated-size ( -- buffer-size)
>> +    " sml-get-allocated-size" vtpm-call-forward IF
>> +        \ vtpm-call-forward failed
>> +        0
>> +    THEN
>> +;
>> +
>> +\ firmware API call
>> +: sml-get-handover-size ( -- size)
>> +    " sml-get-handover-size" vtpm-call-forward IF
>> +        \ vtpm-call-forward failed
>> +        0
>> +    THEN
>> +;
>> +
>> +\ firmware API call
>> +: sml-handover ( dest size -- )
>> +    " sml-handover" vtpm-call-forward IF
>> +        \ vtpm-call-forward failed; clean up stack
>> +        2drop
>> +    THEN
>> +;
>> +
>> +\ firmware API call
>> +: get-failure-reason ( -- reason )
>> +    " get-failure-reason" vtpm-call-forward IF
>> +        \ vtpm-call-forward failed; return a value
>> +        0 \ invalid
>> +    THEN
>> +;
>> +
>> +0 0 s" ibm,sml-efi-reformat-supported" property
>> +
>> +\ firmware API call
>> +: reformat-sml-to-efi-alignment ( -- success )
>> +    " reformat-sml-to-efi-alignment" vtpm-call-forward IF
>> +        false
>> +    THEN
>> +;
>> +
>> +: open ( )
>> +    vtpm-debug? IF ." VTPM: vTPM open()" cr THEN
>> +    true
>> +;
>> +
>> +: close ( )
>> +    vtpm-debug? IF ." VTPM: vTPM close()" cr THEN
>> +;
>> +
>> +\ setup alias and the RTAS bypass
>> +vtpm-init
>> +
>> +\ setup the log
>> +include vtpm-sml.fs
>> +
>> +s" /ibm,vtpm" find-node ?dup IF
>> +  s" measure-scrtm" rot $call-static
>> +THEN
>
> The above 22 lines confuse me a lot.
> Why vtpm-sml.fs?

You mean why does vtpm-sml.fs exist at all?Or why not just put all vTPM 
code into one file?


> Why "open" does not open?
> Why vtpm-init is not in "open"?
> Why the device methods are in vtpm-sml.fs?
>
> The Linux finds the device, opens it and calls methods (passing
> ihandle), why this complication?
>
> I am missing the point in all of this and 2 lines commit log does not
> help at all.
>
>
>
>
>> diff --git a/board-qemu/slof/vtpm-sml.fs b/board-qemu/slof/vtpm-sml.fs
>> new file mode 100644
>> index 0000000..865dce6
>> --- /dev/null
>> +++ b/board-qemu/slof/vtpm-sml.fs
>> @@ -0,0 +1,115 @@
>> +\ *****************************************************************************
>> +\ * 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
>> +\ ****************************************************************************/
>> +
>> +\ KVM/qemu TPM Stored Measurement Log (SML) entries in /ibm,vtpm
>> +
>> +" /" find-device
>> +
>> +new-device
>> +
>> +false VALUE    vtpm-debug?
>> +0     VALUE    log-base
>> +40000 CONSTANT LOG-SIZE   \ 256k per VTPM FW spec.
>
> What is this spec's name exactly? It may not be available to the public
> but I could try and get it in IBM internally.


"PFW Virtual TPM Driver" Version 1.1 (or later)


--- /dev/null
>> +++ b/lib/libtpm/tcgbios.c
>> @@ -0,0 +1,918 @@
>> +/*****************************************************************************
>> + * 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
>
> Is this "A Protocol for VTPM Communications" from
> M_vtpm_protocol_v0r3.pdf or something else?

The title of the document is 'PFW Virtual TPM Driver'. 
PFW_CTPM_CLDD_1.1-1.pdf is the file I have.



>
>
>> and the TCG Specification
>> + *  that can be found here under the following link:
>> + *  http://www.trustedcomputinggroup.org/resources/pc_client_work_group_specific_implementation_specification_for_conventional_bios
>
> The link is broken.


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))
>> +
>> +struct tpm_state {
>> +	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;
>> +};
>> +
>> +static struct tpm_state tpm_state;
> You do not pass a pointer to tpm_state anywhere (it would great if you
> did - this way a reader could tell what functions actually need it) so
> you do not need "struct tpm_state" type, can be just "struct tpm_state {
> ... } tpm_state;"


It's all global variables collected in one structure. I removed the name 
of the structure.



>
>
>
>
>> +
>> +/*
>> + * 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
>> + ****************************************************************/
>> +
>> +static uint32_t tpm20_pcr_selection_size;
>> +static struct tpml_pcr_selection *tpm20_pcr_selection;
>
> Any reason these are not in tpm_state?

I will move them there.


>
>
>> +
>> +/* A 'struct tpm_log_entry' is a local data structure containing a
>> + * 'tpm_log_header' followed by space for the maximum supported
>> + * digest. The digest is a series of tpm2_digest_value structs on tpm2.0.
>> + */
> This comment is rather useless, what I would really be interested to is
> the endianness (little endian because it is v2.0?) and the name of the
> spec which defines it.

PPC64 TPM 2.0 logging is supposed to be compatible with client code 
written for x86_64 Linux. While TPM 1.2 logs on PPC64 were big endian, 
TPM 2.0 logs are now little endian. I am cc'in Christopher Engel who may 
have a more recent spec. and told me this is how it's supposed to work.



>
>
>> +struct tpm_log_entry {
>> +	struct tpm_log_header hdr;
>> +	uint8_t pad[sizeof(struct tpm2_digest_values)
>> +	   + 5 * sizeof(struct tpm2_digest_value)
>> +	   + SHA1_BUFSIZE + SHA256_BUFSIZE + SHA384_BUFSIZE
>> +	   + SHA512_BUFSIZE + SM3_256_BUFSIZE];
>> +} __attribute__((packed));
>> +
>> +static const struct hash_parameters {
>> +	uint16_t hashalg;
>> +	uint8_t  hashalg_flag;
>> +	uint8_t  hash_buffersize;
>> +	const char *name;
> name and hashalg_flag are not used.

True. This occurred when re-splitting the patch.


>
>> +} hash_parameters[] = {
>> +	{
>> +		.hashalg = TPM2_ALG_SHA1,
>
> TPM2_ALG_xxx are basically 1<<index_in_this_array and can be dropped,
> can't they?


No:

lib/libtpm/tcgbios_int.h:#define TPM2_ALG_SHA1 0x0004
lib/libtpm/tcgbios_int.h:#define TPM2_ALG_SHA256 0x000b
lib/libtpm/tcgbios_int.h:#define TPM2_ALG_SHA384 0x000c
lib/libtpm/tcgbios_int.h:#define TPM2_ALG_SHA512 0x000d
lib/libtpm/tcgbios_int.h:#define TPM2_ALG_SM3_256 0x0012


>
>
>> +		.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 int
>> +tpm20_get_hash_buffersize(uint16_t hashAlg)
>> +{
>> +	unsigned i;
>> +
>> +	for (i = 0; i < ARRAY_SIZE(hash_parameters); i++) {
>> +		if (hash_parameters[i].hashalg == hashAlg)
>> +			return hash_parameters[i].hash_buffersize;
>> +	}
>> +	return -1;
> Just make it return (const struct hash_parameters *) as 7/7 adds one
> helper like this for every field of hash_parameters.


OK.


>
>
>> +}
>> +
>> +/*
>> + * Build the TPM2 tpm2_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 sha1 hash in the area of the sha256
>> + * hash and fill the remaining bytes with zeros. Or truncate the sha256
>> + * hash when writing it in the area of the sha1 hash.
> It must be correct since you did this but what is the point of this? If
> the digest does not fit, fill it with 0xBAADF00D and return an error?

A TPM 2 has typically 24 platform control registers (PCRs). These 
registers are storing each a hash (rather than an integer). Basically 
there is one operation on a hash, which is an 'extend':

pcr1 = sha256(pcr1 || hash)

So it takes the current value of PCR 1 and concatenates it with the 
'hash' to extend it and hashes this concatenation and assigns the result 
to PCR 1.

A TPM 2 may have several hash banks, such as for sha1, sha256, sha384, 
sha512, sm3-256 etc.  So there may be multiple PCR 1 registers, one for 
the sha1 bank, one for the sha256 bank etc.

For the logging of measurements we are only using one hash, that is 
sha256 now, and extend this in PCR1 of sha1, sha256, etc. We do this for 
all the 'active' PCR banks basically. We log the 'hash' value that a PCR 
was extended so that someone looking at the current PCR value can use 
the log to replay (simulate) the extensions of the PCRs. Since we are 
now using sha256 for hashing we will need to truncate the sha256 when 
logging it for the sha1 bank and zero pad it for sha384 since sha384 has 
more bytes. There is the integrity measurement architecture (IMA) in 
Linux that basically does the same thing:



>
>
>> + *
>> + * 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)
>
> Is not it always little endian as it is v2.0 only?

There are callers for this function who want to send the resulting data 
structures to the TPM. Other callers want this for the log written in 
little endian.


>
>> +{
>> +	struct tpms_pcr_selection *sel;
>> +	void *nsel, *end;
>> +	void *dest = le->hdr.digest + sizeof(struct tpm2_digest_values);
>> +	uint32_t count;
>> +	struct tpm2_digest_value *v;
>> +	struct tpm2_digest_values *vs;
>> +
>> +	if (!tpm20_pcr_selection)
>
> This is allocated at tpm20_startup() and if allocation failed, then we
> should not get this far, or can we?


It's a safety net. Cannot keep ?



> +
> +static int
> +tpm20_get_pcrbanks(void)
> +{
> +	uint8_t buffer[128];
> +	uint32_t size;
> +	struct tpm2_res_getcapability *trg =
> +		(struct tpm2_res_getcapability *)&buffer;
> +
> +	int 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 */
> +	uint32_t resplen = be32_to_cpu(trg->hdr.totlen);
>
> -Werror=declaration-after-statement is not  enforced in SLOF (may be I
> should fix it...) but we try follow the linux coding style here in SLOF
> :) There are few spots like this.


I fixed these occurrences.


>
>
>
>> +	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;
>> +
>> +	tpm20_pcr_selection = SLOF_alloc_mem(size);
>> +	if (tpm20_pcr_selection) {
>> +		memcpy(tpm20_pcr_selection, &trg->data, size);
>> +		tpm20_pcr_selection_size = size;
>> +	} else {
>> +		printf("TCGBIOS: Failed to allocated %u bytes.\n", size);
>> +		ret = -1;
> return -1;
>
>> +	}
>> +
>> +	return ret;
> return 0;


Fixed.


>
>> +}
>> +
>> +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;
>> +
>> +	memcpy(tre, &tmp_tre, sizeof(tmp_tre));
>> +	memcpy(&tre->digest[0], le->hdr.digest, digest_len);
>> +
>> +	tre->hdr.totlen = cpu_to_be32(sizeof(tmp_tre) + digest_len);
>> +
>> +	struct tpm_rsp_header rsp;
>> +	uint32_t resp_length = sizeof(rsp);
>> +	int ret = tpmhw_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 = tpmhw_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);
>> +
>> +	if (buf_len > sizeof(rsp.rnd.buffer))
>> +		return -1;
>> +
>> +	int ret = tpmhw_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)),
>> +		},
>> +	};
>> +	memcpy(trhca.newAuth.buffer, auth, sizeof(trhca.newAuth.buffer));
>> +
>> +	struct tpm_rsp_header rsp;
>> +	uint32_t resp_length = sizeof(rsp);
>> +	int ret = tpmhw_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)
>> +{
>> +	/* we will try to deactivate the TPM now - ignoring all errors */
>
> Is the comment correct? Neither the name of the function nor the
> structure below suggest deactivation, I may also be missing the point here.


I dropped the comment. A caller controlled hierarchy is disabled with 
this function.

 From the spec:

"This command enables and disables use of a hierarchy and its associated 
NV storage. The command allows phEnable, phEnableNV, shEnable, and 
ehEnable to be changed when the proper authorization is provided."


>
>> +	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 = tpmhw_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(struct tpm_log_header *entry,
>> +				   int digest_len,
>> +				   const void *event, uint32_t event_length)
>> +{
>> +	size_t size, logsize;
>> +	void *dest;
>> +
>> +	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(struct tpm_log_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);
>> +	struct tpm_log_trailer *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)
>> +{
>> +	if (!tpm20_pcr_selection)
>> +		return -1;
>> +
>> +	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 = tpm20_pcr_selection->selections;
>> +	void *nsel, *end = (void*)tpm20_pcr_selection + tpm20_pcr_selection_size;
>> +	int event_size;
>> +	uint32_t *vendorInfoSize;
>> +	struct tpm_log_entry le = {
>> +		.hdr.eventtype = cpu_to_log32(EV_NO_ACTION),
>> +	};
>> +	uint32_t count;
>> +
>> +	for (count = 0;
>> +	     count < be32_to_cpu(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 = 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.
>
> I do not see any caller of this one.
>
>
>> + */
>> +uint32_t tpm_leave_firmware(void)
> static?
>
>
>> +{
>> +	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;
>> +
>> +	sha256(hashdata, hashdatalen, hash);
>> +	digest_len = tpm20_build_digest(&le, hash, true);
>> +	if (digest_len < 0)
>> +		return TCGBIOS_GENERAL_ERROR;
>> +	int 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 rc = 0;
>> +	uint32_t pcrIndex;
>> +
>> +	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)
>> +			break;
> return 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 | sha1sum
>> +	 */
>> +	string = "MBR";
>
> What about GPT (I am not extemely familiar with MBR/GPT but I see my
> ubuntu images use GPT these days)?
>
>
>> +	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 | sha1sum
>> +	 */
>> +	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_data_start = (char *)&_slof_data;
>> +	char *slof_text_start = (char *)&_slof_text;
>> +	uint32_t slof_data_length = (long)&_slof_data_end - (long)&_slof_data;
>> +	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 (data): start = %p, length = %d\n",
>> +		slof_data_start, slof_data_length);
>> +
>> +	rc = tpm_add_measurement_to_log(0, EV_S_CRTM_CONTENTS,
>> +					scrtm, strlen(scrtm),
>> +					(uint8_t *)slof_data_start,
>> +					slof_data_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..3cc4f46 100644
>> --- a/lib/libtpm/tcgbios_int.h
>> +++ b/lib/libtpm/tcgbios_int.h
>> @@ -15,6 +15,94 @@
>>   
>>   #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 */
>> +#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 SHA1_BUFSIZE                    20
>> +#define SHA256_BUFSIZE                  32
>> +#define SHA384_BUFSIZE                  48
>> +#define SHA512_BUFSIZE                  64
>> +#define SM3_256_BUFSIZE                 32
>> +
>> +#define BCV_DEVICE_HDD     0x80
>> +
>> +struct tpm2_digest_value {
>> +	uint16_t hashAlg;
>> +	uint8_t hash[0]; /* size depends on hashAlg */
>> +} __attribute__((packed));
>> +
>> +struct tpm2_digest_values {
>> +	uint32_t count;
>> +	struct tpm2_digest_value digest[0];
>> +} __attribute__((packed));
>> +
>> +/* Each entry in the TPM log contains: a tpm_log_header, a variable
>> + * length digest, a tpm_log_trailer, and a variable length event.  The
>> + * 'digest' matches what is sent to the TPM hardware via the Extend
>> + * command.  On TPM1.2 the digest is a SHA1 hash; on TPM2.0 the digest
>> + * contains a tpm2_digest_values struct followed by a variable number
>> + * of tpm2_digest_value structs (as specified by the hardware via the
>> + * TPM2_CAP_PCRS request).
>> + */
>> +struct tpm_log_header {
>> +	uint32_t pcrindex;
>> +	uint32_t eventtype;
>> +	uint8_t digest[0];
>> +} __attribute__((packed));
>> +
>> +struct tpm_log_trailer {
>> +	uint32_t eventdatasize;
>> +	uint8_t event[0];
>> +} __attribute__((packed));
>> +
>> +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 blocks for the TCG BIOS commands */
>> +
>> +/* PCClient_PCREventStruct -- format of log entries; compatible with x86 */
>> +struct pcpes {
>> +	uint32_t pcrindex;
>> +	uint32_t eventtype;
>> +	uint8_t digest[SHA1_BUFSIZE];
>> +	uint32_t eventdatasize;
>> +	uint32_t event;
>> +} __attribute__((packed));
>> +
>>   struct tpm_req_header {
>>   	uint16_t tag;
>>   	uint32_t totlen;
>> @@ -27,4 +115,156 @@ struct tpm_rsp_header {
>>   	uint32_t errcode;
>>   } __attribute__((packed));
>>   
>> +/****************************************************************
>> + * TPM v2.0 hardware 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_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)
>> +
>> +/* 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 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));
>> +
>> +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));
>> +
>
> Please separate somehow what is defined for SLOF only (is
> TPM2_ALG_SHA1_FLAG/etc?) and for everything else - what comes from what
> spec.
>
>
>
>>   #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()              */
>> +/************************************************/
>
> What is calling this one? Now I am really not sure they all are actually
> have a user. Thanks,
>
>
>> +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..77bb0f5 100644
>> --- a/slof/fs/packages/disk-label.fs
>> +++ b/slof/fs/packages/disk-label.fs
>> @@ -550,7 +550,15 @@ B9E5                CONSTANT GPT-BASIC-DATA-PARTITION-2
>>   \ load from a bootable partition
>>   : load-from-boot-partition ( addr -- size )
>>      debug-disk-label? IF ." Trying DOS boot " .s cr THEN
>> -   dup load-from-dos-boot-partition ?dup 0 <> IF nip EXIT THEN
>> +   dup load-from-dos-boot-partition ?dup 0 <> IF
>> +      nip
>> +      block s" /ibm,vtpm" find-node ?dup IF
>> +         s" measure-hdd-mbr" rot $call-static
>> +      ELSE
>> +         drop
>> +      THEN
>> +      EXIT
>> +   THEN
>>   
>>      debug-disk-label? IF ." Trying CHRP boot " .s cr THEN
>>      1 disk-chrp-boot !
>> 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
>>
Stefan Berger Jan. 20, 2020, 6:22 p.m. UTC | #3
On 1/20/20 12:08 PM, Stefan Berger wrote:
> On 1/20/20 3:09 AM, Alexey Kardashevskiy wrote:
>>

I accidentally pressed the send button before. So here are more responses.

>
>>> +
>>> +\ firmware API call
>>> +: get-failure-reason ( -- reason )
>>> +    " get-failure-reason" vtpm-call-forward IF
>>> +        \ vtpm-call-forward failed; return a value
>>> +        0 \ invalid
>>> +    THEN
>>> +;
>>> +
>>> +0 0 s" ibm,sml-efi-reformat-supported" property
>>> +
>>> +\ firmware API call
>>> +: reformat-sml-to-efi-alignment ( -- success )
>>> +    " reformat-sml-to-efi-alignment" vtpm-call-forward IF
>>> +        false
>>> +    THEN
>>> +;
>>> +
>>> +: open ( )
>>> +    vtpm-debug? IF ." VTPM: vTPM open()" cr THEN
>>> +    true
>>> +;
>>> +
>>> +: close ( )
>>> +    vtpm-debug? IF ." VTPM: vTPM close()" cr THEN
>>> +;
>>> +
>>> +\ setup alias and the RTAS bypass
>>> +vtpm-init
>>> +
>>> +\ setup the log
>>> +include vtpm-sml.fs
>>> +
>>> +s" /ibm,vtpm" find-node ?dup IF
>>> +  s" measure-scrtm" rot $call-static
>>> +THEN
>>
>> The above 22 lines confuse me a lot.
>> Why vtpm-sml.fs?
>
> You mean why does vtpm-sml.fs exist at all?Or why not just put all 
> vTPM code into one file?
>
>
>> Why "open" does not open?
>> Why vtpm-init is not in "open"?
>> Why the device methods are in vtpm-sml.fs?


The straight forward answer is that I have some 'challenges' with the 
FORTH part. I do not completely understand how to organize the code 
there. What is there right now is at least 'working'.


>>
>> The Linux finds the device, opens it and calls methods (passing
>> ihandle), why this complication?


Grub could call the API as well. I have removed all the other APIs now 
since the removal of TPM 1.2 support since I only knew of callers of the 
API from 'trusted' grub code supporting TPM 1.2. It doesn't support TPM 2.


>>
>> I am missing the point in all of this and 2 lines commit log does not
>> help at all.
>>




>> +
>> +/*
>> + * Prepare TPM for boot; this function has to be called before
>> + * the firmware transitions to the boot loader.
>>
>> I do not see any caller of this one.
>>
>>
>>> + */
>>> +uint32_t tpm_leave_firmware(void)
>> static?


No, FORTH is calling it.

board-qemu/slof/vtpm-sml.fs-: leave-firmware ( -- )
board-qemu/slof/vtpm-sml.fs: tpm-leave-firmware                         
( errcode )
board-qemu/slof/vtpm-sml.fs-    ?dup IF
board-qemu/slof/vtpm-sml.fs:        ." VTPM: Error code from 
tpm-leave-firmware: " . cr
board-qemu/slof/vtpm-sml.fs-    THEN
board-qemu/slof/vtpm-sml.fs-;


>>
>>
>>> +/*
>>> + * 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 rc = 0;
>>> +    uint32_t pcrIndex;
>>> +
>>> +    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)
>>> +            break;
>> return rc;


Fixed.



>>
>>
>>> +    }
>>> +
>>> +    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 | sha1sum
>>> +     */
>>> +    string = "MBR";
>>
>> What about GPT (I am not extemely familiar with MBR/GPT but I see my
>> ubuntu images use GPT these days)?


I have a patch on top of this series that does measure GPT. I will post 
on the next round.

+
>>> +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));
>>> +
>>
>> Please separate somehow what is defined for SLOF only (is
>> TPM2_ALG_SHA1_FLAG/etc?) and for everything else - what comes from what
>> spec.


All are TPM 2 definitions. Most are related to commands, others are 
related to logging.

I will try to leave some references to names of specs.



>> +
>>> +/************************************************/
>>> +/* Check whether the TPM is working             */
>>> +/* SLOF:   tpm-is-working  ( -- true | false )  */
>>> +/* LIBTPM: bool = tpm_is_working()              */
>>> +/************************************************/
>>
>> What is calling this one? Now I am really not sure they all are actually
>> have a user. Thanks,


There is a user for this one.

board-qemu/slof/vtpm-sml.fs-
board-qemu/slof/vtpm-sml.fs-: vtpm-menu
board-qemu/slof/vtpm-sml.fs:    tpm-is-working IF
board-qemu/slof/vtpm-sml.fs-        tpm20-menu
board-qemu/slof/vtpm-sml.fs-    THEN
Alexey Kardashevskiy Jan. 22, 2020, 2:24 a.m. UTC | #4
On 21/01/2020 04:08, Stefan Berger wrote:
> On 1/20/20 3:09 AM, Alexey Kardashevskiy wrote:
>>
>> On 16/01/2020 07:00, Stefan Berger wrote:
>>> This patch adds TPM 2.0 support along with the firmware API
>>> that Linux uses to transfer the firmware log.
>>>
>>> Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
>>> ---
>>>   board-qemu/slof/Makefile            |  13 +-
>>>   board-qemu/slof/tree.fs             |   3 +
>>>   board-qemu/slof/vio-vtpm-cdriver.fs | 134 ++++
>>>   board-qemu/slof/vtpm-sml.fs         | 115 ++++
>>>   include/helpers.h                   |   1 +
>>>   lib/libtpm/Makefile                 |   2 +-
>>>   lib/libtpm/tcgbios.c                | 918 ++++++++++++++++++++++++++++
>>>   lib/libtpm/tcgbios.h                |  32 +
>>>   lib/libtpm/tcgbios_int.h            | 240 ++++++++
>>>   lib/libtpm/tpm.code                 | 130 ++++
>>>   lib/libtpm/tpm.in                   |  26 +
>>>   slof/fs/packages/disk-label.fs      |  10 +-
>>>   slof/fs/start-up.fs                 |   5 +
>>>   13 files changed, 1624 insertions(+), 5 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 \
>>
>> s/vio-vtpm-cdriver.fs/vio-vtpm.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..8d17d0e
>>> --- /dev/null
>>> +++ b/board-qemu/slof/vio-vtpm-cdriver.fs
>>> @@ -0,0 +1,134 @@
>>> +\
>>> *****************************************************************************
>>>
>>> +\ * 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
>>> +
>>> +false VALUE vtpm-debug?
>>> +0     VALUE vtpm-unit
>>> +0     VALUE vtpm-ihandle
>>> +
>>> +: setup-alias
>>> +    " ibm,vtpm" find-alias 0= IF
>>> +        " ibm,vtpm" get-node node>path set-alias
>>> +    ELSE
>>> +        drop
>>> +    THEN
>>> +;
>>> +
>>> +: vtpm-cleanup ( )
>>> +    vtpm-debug? IF ." VTPM: Disabling RTAS bypass" cr THEN
>>> +    tpm-finalize
>>> +    vtpm-unit 0 rtas-set-tce-bypass
>>> +;
>>> +
>>> +: vtpm-init ( -- true | false )
>>> +    0 0 get-node open-node ?dup 0= IF 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
>>> +
>>> +    tpm-start ?dup 0= IF
>>> +        vtpm-debug? IF ." VTPM: Success from tpm-start" cr THEN
>>> +        setup-alias
>>> +    ELSE
>>> +        ." VTPM: Error code from tpm-start: " . cr
>>> +    THEN
>>> +
>>> +    close-node
>>> +    r> to my-self
>>> +;
>>> +
>>> +\ forward a call to /ibm,vtpm, which implements the function with the
>>> +\ given name
>>> +: vtpm-call-forward ( arg ... arg name namelen -- ret ... ret
>>> failure? )
>>> +    \ assign /ibm,vtpm node to vtpm-ihandle, if not assigned
>>> +    vtpm-ihandle 0= IF
>>> +        s" /ibm,vtpm" open-dev to vtpm-ihandle
>>
>> Why does not "open" do this? Is this vtpm supposed to run even before
>> the client tries using the vtpm services? It does not look like it.
> 
> Initialization of the vTPM is supposed to happen before any client can
> talk to the firmware.


And how doing it in "open" breaks this rule? If the client does not open
the node, it cannot use the vtpm.



> 
>>
>>
>>> +    THEN
>>> +
>>> +    vtpm-ihandle 0<> IF
>>> +        vtpm-ihandle                   ( arg ... arg name namelen
>>> ihandle )
>>> +        $call-method                   ( ret ... ret )
>>> +        false                          ( ret ... ret false )
>>> +    ELSE
>>> +        true                           ( true )
>>> +    THEN
>>> +;
>>> +
>>> +\ firmware API call
>>> +: sml-get-allocated-size ( -- buffer-size)
>>> +    " sml-get-allocated-size" vtpm-call-forward IF
>>> +        \ vtpm-call-forward failed
>>> +        0
>>> +    THEN
>>> +;
>>> +
>>> +\ firmware API call
>>> +: sml-get-handover-size ( -- size)
>>> +    " sml-get-handover-size" vtpm-call-forward IF
>>> +        \ vtpm-call-forward failed
>>> +        0
>>> +    THEN
>>> +;
>>> +
>>> +\ firmware API call
>>> +: sml-handover ( dest size -- )
>>> +    " sml-handover" vtpm-call-forward IF
>>> +        \ vtpm-call-forward failed; clean up stack
>>> +        2drop
>>> +    THEN
>>> +;
>>> +
>>> +\ firmware API call
>>> +: get-failure-reason ( -- reason )
>>> +    " get-failure-reason" vtpm-call-forward IF
>>> +        \ vtpm-call-forward failed; return a value
>>> +        0 \ invalid
>>> +    THEN
>>> +;
>>> +
>>> +0 0 s" ibm,sml-efi-reformat-supported" property
>>> +
>>> +\ firmware API call
>>> +: reformat-sml-to-efi-alignment ( -- success )
>>> +    " reformat-sml-to-efi-alignment" vtpm-call-forward IF
>>> +        false
>>> +    THEN
>>> +;
>>> +
>>> +: open ( )
>>> +    vtpm-debug? IF ." VTPM: vTPM open()" cr THEN
>>> +    true
>>> +;
>>> +
>>> +: close ( )
>>> +    vtpm-debug? IF ." VTPM: vTPM close()" cr THEN
>>> +;
>>> +
>>> +\ setup alias and the RTAS bypass
>>> +vtpm-init
>>> +
>>> +\ setup the log
>>> +include vtpm-sml.fs
>>> +
>>> +s" /ibm,vtpm" find-node ?dup IF
>>> +  s" measure-scrtm" rot $call-static
>>> +THEN
>>
>> The above 22 lines confuse me a lot.
>> Why vtpm-sml.fs?
> 
> You mean why does vtpm-sml.fs exist at all?Or why not just put all vTPM
> code into one file?

Yes.



> 
>> Why "open" does not open?
>> Why vtpm-init is not in "open"?
>> Why the device methods are in vtpm-sml.fs?
>>
>> The Linux finds the device, opens it and calls methods (passing
>> ihandle), why this complication?
>>
>> I am missing the point in all of this and 2 lines commit log does not
>> help at all.
>>
>>
>>
>>
>>> diff --git a/board-qemu/slof/vtpm-sml.fs b/board-qemu/slof/vtpm-sml.fs
>>> new file mode 100644
>>> index 0000000..865dce6
>>> --- /dev/null
>>> +++ b/board-qemu/slof/vtpm-sml.fs
>>> @@ -0,0 +1,115 @@
>>> +\
>>> *****************************************************************************
>>>
>>> +\ * 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
>>> +\
>>> ****************************************************************************/
>>>
>>> +
>>> +\ KVM/qemu TPM Stored Measurement Log (SML) entries in /ibm,vtpm
>>> +
>>> +" /" find-device
>>> +
>>> +new-device
>>> +
>>> +false VALUE    vtpm-debug?
>>> +0     VALUE    log-base
>>> +40000 CONSTANT LOG-SIZE   \ 256k per VTPM FW spec.
>>
>> What is this spec's name exactly? It may not be available to the public
>> but I could try and get it in IBM internally.
> 
> 
> "PFW Virtual TPM Driver" Version 1.1 (or later)


Put this to the commit log.


> 
> 
> --- /dev/null
>>> +++ b/lib/libtpm/tcgbios.c
>>> @@ -0,0 +1,918 @@
>>> +/*****************************************************************************
>>>
>>> + * 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
>>
>> Is this "A Protocol for VTPM Communications" from
>> M_vtpm_protocol_v0r3.pdf or something else?
> 
> The title of the document is 'PFW Virtual TPM Driver'.
> PFW_CTPM_CLDD_1.1-1.pdf is the file I have.
> 

Cool, save it in the comment or commit log.


> 
>>
>>
>>> and the TCG Specification
>>> + *  that can be found here under the following link:
>>> + * 
>>> http://www.trustedcomputinggroup.org/resources/pc_client_work_group_specific_implementation_specification_for_conventional_bios
>>>
>>
>> The link is broken.
> 
> 
> https://trustedcomputinggroup.org/resource/pc-client-work-group-specific-implementation-specification-for-conventional-bios/


and this one.

> 
> 
> 
> 
>>
>>
>>> + */
>>> +
>>> +#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))
>>> +
>>> +struct tpm_state {
>>> +    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;
>>> +};
>>> +
>>> +static struct tpm_state tpm_state;
>> You do not pass a pointer to tpm_state anywhere (it would great if you
>> did - this way a reader could tell what functions actually need it) so
>> you do not need "struct tpm_state" type, can be just "struct tpm_state {
>> ... } tpm_state;"
> 
> 
> It's all global variables collected in one structure. I removed the name
> of the structure.
> 
> 
> 
>>
>>
>>
>>
>>> +
>>> +/*
>>> + * 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
>>> + ****************************************************************/
>>> +
>>> +static uint32_t tpm20_pcr_selection_size;
>>> +static struct tpml_pcr_selection *tpm20_pcr_selection;
>>
>> Any reason these are not in tpm_state?
> 
> I will move them there.
> 
> 
>>
>>
>>> +
>>> +/* A 'struct tpm_log_entry' is a local data structure containing a
>>> + * 'tpm_log_header' followed by space for the maximum supported
>>> + * digest. The digest is a series of tpm2_digest_value structs on
>>> tpm2.0.
>>> + */
>> This comment is rather useless, what I would really be interested to is
>> the endianness (little endian because it is v2.0?) and the name of the
>> spec which defines it.
> 
> PPC64 TPM 2.0 logging is supposed to be compatible with client code
> written for x86_64 Linux. While TPM 1.2 logs on PPC64 were big endian,
> TPM 2.0 logs are now little endian. I am cc'in Christopher Engel who may
> have a more recent spec. and told me this is how it's supposed to work.
> 
> 
> 
>>
>>
>>> +struct tpm_log_entry {
>>> +    struct tpm_log_header hdr;
>>> +    uint8_t pad[sizeof(struct tpm2_digest_values)
>>> +       + 5 * sizeof(struct tpm2_digest_value)
>>> +       + SHA1_BUFSIZE + SHA256_BUFSIZE + SHA384_BUFSIZE
>>> +       + SHA512_BUFSIZE + SM3_256_BUFSIZE];
>>> +} __attribute__((packed));
>>> +
>>> +static const struct hash_parameters {
>>> +    uint16_t hashalg;
>>> +    uint8_t  hashalg_flag;
>>> +    uint8_t  hash_buffersize;
>>> +    const char *name;
>> name and hashalg_flag are not used.
> 
> True. This occurred when re-splitting the patch.
> 
> 
>>
>>> +} hash_parameters[] = {
>>> +    {
>>> +        .hashalg = TPM2_ALG_SHA1,
>>
>> TPM2_ALG_xxx are basically 1<<index_in_this_array and can be dropped,
>> can't they?
> 
> 
> No:
> 
> lib/libtpm/tcgbios_int.h:#define TPM2_ALG_SHA1 0x0004
> lib/libtpm/tcgbios_int.h:#define TPM2_ALG_SHA256 0x000b
> lib/libtpm/tcgbios_int.h:#define TPM2_ALG_SHA384 0x000c
> lib/libtpm/tcgbios_int.h:#define TPM2_ALG_SHA512 0x000d
> lib/libtpm/tcgbios_int.h:#define TPM2_ALG_SM3_256 0x0012

Oh right, I confused this with the other set:

#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)


It is not used by this patch, 7/7 uses it and even then it would make
more sense to define these as indexes into the hash_parameters[]. Or
just drop.


> 
> 
>>
>>
>>> +        .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 int
>>> +tpm20_get_hash_buffersize(uint16_t hashAlg)
>>> +{
>>> +    unsigned i;
>>> +
>>> +    for (i = 0; i < ARRAY_SIZE(hash_parameters); i++) {
>>> +        if (hash_parameters[i].hashalg == hashAlg)
>>> +            return hash_parameters[i].hash_buffersize;
>>> +    }
>>> +    return -1;
>> Just make it return (const struct hash_parameters *) as 7/7 adds one
>> helper like this for every field of hash_parameters.
> 
> 
> OK.
> 
> 
>>
>>
>>> +}
>>> +
>>> +/*
>>> + * Build the TPM2 tpm2_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 sha1 hash in the area of the sha256
>>> + * hash and fill the remaining bytes with zeros. Or truncate the sha256
>>> + * hash when writing it in the area of the sha1 hash.
>> It must be correct since you did this but what is the point of this? If
>> the digest does not fit, fill it with 0xBAADF00D and return an error?
> 
> A TPM 2 has typically 24 platform control registers (PCRs). These
> registers are storing each a hash (rather than an integer). Basically
> there is one operation on a hash, which is an 'extend':
> 
> pcr1 = sha256(pcr1 || hash)
> 
> So it takes the current value of PCR 1 and concatenates it with the
> 'hash' to extend it and hashes this concatenation and assigns the result
> to PCR 1.
> 
> A TPM 2 may have several hash banks, such as for sha1, sha256, sha384,
> sha512, sm3-256 etc.  So there may be multiple PCR 1 registers, one for
> the sha1 bank, one for the sha256 bank etc.
> 
> For the logging of measurements we are only using one hash, that is
> sha256 now, and extend this in PCR1 of sha1, sha256, etc. We do this for
> all the 'active' PCR banks basically.

Do we have to do this for all active PCR banks? Sorry for stupid
questions :)

> We log the 'hash' value that a PCR
> was extended so that someone looking at the current PCR value can use
> the log to replay (simulate) the extensions of the PCRs. Since we are
> now using sha256 for hashing we will need to truncate the sha256 when
> logging it for the sha1 bank and zero pad it for sha384 since sha384 has
> more bytes. There is the integrity measurement architecture (IMA) in
> Linux that basically does the same thing:

Something is missing after ":"?


> 
> 
> 
>>
>>
>>> + *
>>> + * 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)
>>
>> Is not it always little endian as it is v2.0 only?
> 
> There are callers for this function who want to send the resulting data
> structures to the TPM. Other callers want this for the log written in
> little endian.


The function prototype suggests it is always for the log which is always
little endian as it is v2.0 only.


> 
>>
>>> +{
>>> +    struct tpms_pcr_selection *sel;
>>> +    void *nsel, *end;
>>> +    void *dest = le->hdr.digest + sizeof(struct tpm2_digest_values);
>>> +    uint32_t count;
>>> +    struct tpm2_digest_value *v;
>>> +    struct tpm2_digest_values *vs;
>>> +
>>> +    if (!tpm20_pcr_selection)
>>
>> This is allocated at tpm20_startup() and if allocation failed, then we
>> should not get this far, or can we?
> 
> 
> It's a safety net. Cannot keep ?

It is moving into tpm_state and you already have
tpm_probed/tpm_found/..., some are tested in various places, how many
nets do you really need?
Alexey Kardashevskiy Jan. 22, 2020, 3:16 a.m. UTC | #5
On 21/01/2020 05:22, Stefan Berger wrote:
> On 1/20/20 12:08 PM, Stefan Berger wrote:
>> On 1/20/20 3:09 AM, Alexey Kardashevskiy wrote:
>>>
> 
> I accidentally pressed the send button before. So here are more responses.
> 
>>
>>>> +
>>>> +\ firmware API call
>>>> +: get-failure-reason ( -- reason )
>>>> +    " get-failure-reason" vtpm-call-forward IF
>>>> +        \ vtpm-call-forward failed; return a value
>>>> +        0 \ invalid
>>>> +    THEN
>>>> +;
>>>> +
>>>> +0 0 s" ibm,sml-efi-reformat-supported" property
>>>> +
>>>> +\ firmware API call
>>>> +: reformat-sml-to-efi-alignment ( -- success )
>>>> +    " reformat-sml-to-efi-alignment" vtpm-call-forward IF
>>>> +        false
>>>> +    THEN
>>>> +;
>>>> +
>>>> +: open ( )
>>>> +    vtpm-debug? IF ." VTPM: vTPM open()" cr THEN
>>>> +    true
>>>> +;
>>>> +
>>>> +: close ( )
>>>> +    vtpm-debug? IF ." VTPM: vTPM close()" cr THEN
>>>> +;
>>>> +
>>>> +\ setup alias and the RTAS bypass
>>>> +vtpm-init
>>>> +
>>>> +\ setup the log
>>>> +include vtpm-sml.fs
>>>> +
>>>> +s" /ibm,vtpm" find-node ?dup IF
>>>> +  s" measure-scrtm" rot $call-static
>>>> +THEN
>>>
>>> The above 22 lines confuse me a lot.
>>> Why vtpm-sml.fs?
>>
>> You mean why does vtpm-sml.fs exist at all?Or why not just put all
>> vTPM code into one file?
>>
>>
>>> Why "open" does not open?
>>> Why vtpm-init is not in "open"?
>>> Why the device methods are in vtpm-sml.fs?
> 
> 
> The straight forward answer is that I have some 'challenges' with the
> FORTH part. I do not completely understand how to organize the code
> there. What is there right now is at least 'working'.
> 
> 
>>>
>>> The Linux finds the device, opens it and calls methods (passing
>>> ihandle), why this complication?
> 
> 
> Grub could call the API as well. I have removed all the other APIs now
> since the removal of TPM 1.2 support since I only knew of callers of the
> API from 'trusted' grub code supporting TPM 1.2. It doesn't support TPM 2.


Then why are we ditching v1.2? I asked multiple times what needs v1.2
and grub never came up and if this is the only version what grub
supports - we need it, no? Or we somehow are going to bypass grub
completely and it is ok?


> 
>>>
>>> I am missing the point in all of this and 2 lines commit log does not
>>> help at all.
>>>
> 
> 
> 
> 
>>> +
>>> +/*
>>> + * Prepare TPM for boot; this function has to be called before
>>> + * the firmware transitions to the boot loader.
>>>
>>> I do not see any caller of this one.
>>>
>>>
>>>> + */
>>>> +uint32_t tpm_leave_firmware(void)
>>> static?
> 
> 
> No, FORTH is calling it.

ah yes. Forth is one great write-only language :)


> 
> board-qemu/slof/vtpm-sml.fs-: leave-firmware ( -- )
> board-qemu/slof/vtpm-sml.fs: tpm-leave-firmware                        
> ( errcode )
> board-qemu/slof/vtpm-sml.fs-    ?dup IF
> board-qemu/slof/vtpm-sml.fs:        ." VTPM: Error code from
> tpm-leave-firmware: " . cr
> board-qemu/slof/vtpm-sml.fs-    THEN
> board-qemu/slof/vtpm-sml.fs-;
> 
> 
>>>
>>>
>>>> +/*
>>>> + * 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 rc = 0;
>>>> +    uint32_t pcrIndex;
>>>> +
>>>> +    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)
>>>> +            break;
>>> return rc;
> 
> 
> Fixed.
> 
> 
> 
>>>
>>>
>>>> +    }
>>>> +
>>>> +    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 | sha1sum
>>>> +     */
>>>> +    string = "MBR";
>>>
>>> What about GPT (I am not extemely familiar with MBR/GPT but I see my
>>> ubuntu images use GPT these days)?
> 
> 
> I have a patch on top of this series that does measure GPT. I will post
> on the next round.
> 
> +
>>>> +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));
>>>> +
>>>
>>> Please separate somehow what is defined for SLOF only (is
>>> TPM2_ALG_SHA1_FLAG/etc?) and for everything else - what comes from what
>>> spec.
> 
> 
> All are TPM 2 definitions. Most are related to commands, others are
> related to logging.
> 
> I will try to leave some references to names of specs.
> 
> 
> 
>>> +
>>>> +/************************************************/
>>>> +/* Check whether the TPM is working             */
>>>> +/* SLOF:   tpm-is-working  ( -- true | false )  */
>>>> +/* LIBTPM: bool = tpm_is_working()              */
>>>> +/************************************************/
>>>
>>> What is calling this one? Now I am really not sure they all are actually
>>> have a user. Thanks,
> 
> 
> There is a user for this one.


Then this should go to 7/7?


> 
> board-qemu/slof/vtpm-sml.fs-
> board-qemu/slof/vtpm-sml.fs-: vtpm-menu
> board-qemu/slof/vtpm-sml.fs:    tpm-is-working IF
> board-qemu/slof/vtpm-sml.fs-        tpm20-menu
> board-qemu/slof/vtpm-sml.fs-    THEN
> 
>
Stefan Berger Jan. 22, 2020, 2:35 p.m. UTC | #6
On 1/21/20 9:24 PM, Alexey Kardashevskiy wrote:
>
> On 21/01/2020 04:08, Stefan Berger wrote:
>> On 1/20/20 3:09 AM, Alexey Kardashevskiy wrote:
>>> On 16/01/2020 07:00, Stefan Berger wrote:
>>>> This patch adds TPM 2.0 support along with the firmware API
>>>> that Linux uses to transfer the firmware log.
>>>>
>>>> Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
>>>>
>>>> +;
>>>> +
>>>> +\ forward a call to /ibm,vtpm, which implements the function with the
>>>> +\ given name
>>>> +: vtpm-call-forward ( arg ... arg name namelen -- ret ... ret
>>>> failure? )
>>>> +    \ assign /ibm,vtpm node to vtpm-ihandle, if not assigned
>>>> +    vtpm-ihandle 0= IF
>>>> +        s" /ibm,vtpm" open-dev to vtpm-ihandle
>>> Why does not "open" do this? Is this vtpm supposed to run even before
>>> the client tries using the vtpm services? It does not look like it.
>> Initialization of the vTPM is supposed to happen before any client can
>> talk to the firmware.
>
> And how doing it in "open" breaks this rule? If the client does not open
> the node, it cannot use the vtpm.


The firmware is supposed to take measurements while it is running. 
Before it can do that it needs to initialize the TPM, so that's why we 
are doing this without any higher level client coming in through the 
API. Further, when the user configures the TPM via the menu the device 
has to have been initialized already.


>
>
>
>>>
>>>> +    THEN
>>>> +
>>>> +    vtpm-ihandle 0<> IF
>>>> +        vtpm-ihandle                   ( arg ... arg name namelen
>>>> ihandle )
>>>> +        $call-method                   ( ret ... ret )
>>>> +        false                          ( ret ... ret false )
>>>> +    ELSE
>>>> +        true                           ( true )
>>>> +    THEN
>>>> +;
>>>> +
>>>> +\ firmware API call
>>>> +: sml-get-allocated-size ( -- buffer-size)
>>>> +    " sml-get-allocated-size" vtpm-call-forward IF
>>>> +        \ vtpm-call-forward failed
>>>> +        0
>>>> +    THEN
>>>> +;
>>>> +
>>>> +\ firmware API call
>>>> +: sml-get-handover-size ( -- size)
>>>> +    " sml-get-handover-size" vtpm-call-forward IF
>>>> +        \ vtpm-call-forward failed
>>>> +        0
>>>> +    THEN
>>>> +;
>>>> +
>>>> +\ firmware API call
>>>> +: sml-handover ( dest size -- )
>>>> +    " sml-handover" vtpm-call-forward IF
>>>> +        \ vtpm-call-forward failed; clean up stack
>>>> +        2drop
>>>> +    THEN
>>>> +;
>>>> +
>>>> +\ firmware API call
>>>> +: get-failure-reason ( -- reason )
>>>> +    " get-failure-reason" vtpm-call-forward IF
>>>> +        \ vtpm-call-forward failed; return a value
>>>> +        0 \ invalid
>>>> +    THEN
>>>> +;
>>>> +
>>>> +0 0 s" ibm,sml-efi-reformat-supported" property
>>>> +
>>>> +\ firmware API call
>>>> +: reformat-sml-to-efi-alignment ( -- success )
>>>> +    " reformat-sml-to-efi-alignment" vtpm-call-forward IF
>>>> +        false
>>>> +    THEN
>>>> +;
>>>> +
>>>> +: open ( )
>>>> +    vtpm-debug? IF ." VTPM: vTPM open()" cr THEN
>>>> +    true
>>>> +;
>>>> +
>>>> +: close ( )
>>>> +    vtpm-debug? IF ." VTPM: vTPM close()" cr THEN
>>>> +;
>>>> +
>>>> +\ setup alias and the RTAS bypass
>>>> +vtpm-init
>>>> +
>>>> +\ setup the log
>>>> +include vtpm-sml.fs
>>>> +
>>>> +s" /ibm,vtpm" find-node ?dup IF
>>>> +  s" measure-scrtm" rot $call-static
>>>> +THEN
>>> The above 22 lines confuse me a lot.
>>> Why vtpm-sml.fs?
>> You mean why does vtpm-sml.fs exist at all?Or why not just put all vTPM
>> code into one file?
> Yes.

Have a look at v7. I would agree to switching the names of the files now 
as things have moved around, but keeping the code in different files 
seems to make it 'cleaner' -- at least to my eyes. As said, I do have 
some challenges with the Forth code and it's not clear to me what is the 
better code. Back then when I wrote this I derived it from from the 
vscsi code, which still does things similar today:

https://github.com/stefanberger/SLOF-tpm/blob/master/board-qemu/slof/vio-vscsi.fs#L534


vtpm-init is similar to vscsi-init-and-scan. Right or wrong, it couldn't 
tell, but at least derived from some existing code that does it similarly.


>> "PFW Virtual TPM Driver" Version 1.1 (or later)
>
> Put this to the commit log.


See v7: https://lists.ozlabs.org/pipermail/slof/2020-January/002506.html


>> The title of the document is 'PFW Virtual TPM Driver'.
>> PFW_CTPM_CLDD_1.1-1.pdf is the file I have.
>>
> Cool, save it in the comment or commit log.


v7 got it.


>
>
>>>
>>>> and the TCG Specification
>>>> + *  that can be found here under the following link:
>>>> + *
>>>> http://www.trustedcomputinggroup.org/resources/pc_client_work_group_specific_implementation_specification_for_conventional_bios
>>>>
>>> The link is broken.
>>
>> https://trustedcomputinggroup.org/resource/pc-client-work-group-specific-implementation-specification-for-conventional-bios/
>
> and this one.


Updated the link in the file.


>>>> +        .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 int
>>>> +tpm20_get_hash_buffersize(uint16_t hashAlg)
>>>> +{
>>>> +    unsigned i;
>>>> +
>>>> +    for (i = 0; i < ARRAY_SIZE(hash_parameters); i++) {
>>>> +        if (hash_parameters[i].hashalg == hashAlg)
>>>> +            return hash_parameters[i].hash_buffersize;
>>>> +    }
>>>> +    return -1;
>>> Just make it return (const struct hash_parameters *) as 7/7 adds one
>>> helper like this for every field of hash_parameters.
>>
>> OK.
>>
>>
>>>
>>>> +}
>>>> +
>>>> +/*
>>>> + * Build the TPM2 tpm2_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 sha1 hash in the area of the sha256
>>>> + * hash and fill the remaining bytes with zeros. Or truncate the sha256
>>>> + * hash when writing it in the area of the sha1 hash.
>>> It must be correct since you did this but what is the point of this? If
>>> the digest does not fit, fill it with 0xBAADF00D and return an error?
>> A TPM 2 has typically 24 platform control registers (PCRs). These
>> registers are storing each a hash (rather than an integer). Basically
>> there is one operation on a hash, which is an 'extend':
>>
>> pcr1 = sha256(pcr1 || hash)
>>
>> So it takes the current value of PCR 1 and concatenates it with the
>> 'hash' to extend it and hashes this concatenation and assigns the result
>> to PCR 1.
>>
>> A TPM 2 may have several hash banks, such as for sha1, sha256, sha384,
>> sha512, sm3-256 etc.  So there may be multiple PCR 1 registers, one for
>> the sha1 bank, one for the sha256 bank etc.
>>
>> For the logging of measurements we are only using one hash, that is
>> sha256 now, and extend this in PCR1 of sha1, sha256, etc. We do this for
>> all the 'active' PCR banks basically.
> Do we have to do this for all active PCR banks? Sorry for stupid
> questions :)

Page 96 in pdf "TCG PC Client Platform Firmware Profile"

https://trustedcomputinggroup.org/wp-content/uploads/TCG_PCClientSpecPlat_TPM_2p0_1p04_pub.pdf

There SHALL be a Hash algorithm in the TCG  PCClientPCREvent structure 
for  all allocated PCR banks.

There MAY be Hash algorithms in the TCG_PCClientPCREvent structure that 
do not correspond to allocated PCR banks.  See Section 11 Predictive 
Event Logs.


We chose to do both in SeaBIOS. From what I am hearing other firmwares 
may do the same and follow the 'MAY'.


>
>> We log the 'hash' value that a PCR
>> was extended so that someone looking at the current PCR value can use
>> the log to replay (simulate) the extensions of the PCRs. Since we are
>> now using sha256 for hashing we will need to truncate the sha256 when
>> logging it for the sha1 bank and zero pad it for sha384 since sha384 has
>> more bytes. There is the integrity measurement architecture (IMA) in
>> Linux that basically does the same thing:
> Something is missing after ":"?

IMA takes one measurement of a file and extends all active PCR banks 
with this hash. Depending on the algorithm used for hashing versus the 
hash algorithm of a PCR bank a hash may be trucated, fit exactly, or 
zero-padded at the end

https://elixir.bootlin.com/linux/latest/source/security/integrity/ima/ima_queue.c#L147

later on size-adjusted: 
https://elixir.bootlin.com/linux/latest/source/drivers/char/tpm/tpm2-cmd.c#L275



>
>
>>
>>
>>>
>>>> + *
>>>> + * 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)
>>> Is not it always little endian as it is v2.0 only?
>> There are callers for this function who want to send the resulting data
>> structures to the TPM. Other callers want this for the log written in
>> little endian.
>
> The function prototype suggests it is always for the log which is always
> little endian as it is v2.0 only.


This is what the parameter description says. The function can be called 
for different purposes.

* bigEndian: whether to build in big endian format for the TPM or log
*            little endian for the log (TPM 2.0)

We may need to send the result of this call to the TPM (TPM 2.0 that is) and it takes the data in big endian.


The little endian use case is for writing into the log. This is from "TCG PC Client Platfrom Firmware Profile" (pdf page 92):

https://trustedcomputinggroup.org/wp-content/uploads/TCG_PCClientSpecPlat_TPM_2p0_1p04_pub.pdf

"Note that for the digests in the crypto agile log format, although the type names from the  TPM  2.0  Library  Specification  are  used(e.g.  TPML_DIGEST_VALUES),  the storage of Integers in the event log is little-endian. In this case, the Count and the AlgID fields, leveraged from the TPM 2.0 Library specification,are stored as little-endian. If Platform Firmware utilizes the TPM2_PCR_Event command to hash the data,  this  command  returns  a  TPML_DIGEST_VALUES  structure  encoded  in  big-endian.    Platform Firmware  would  need  to  modify  the  TPML_DIGEST_VALUES structure so that count and AlgID fields are little-endian before adding the event to 2655the    event    log.        Likewise,    if Platform Firmware    is    preparing    the TPML_DIGEST_VALUES structure to send to the TPM using  a TPM2_PCR_Extend command, the values must be big-endian for the TPM structure.  There may be other instances  where  this  type  of  re-encoding  is  required.  This  specification  does  not produce an exhaustive list."
diff mbox series

Patch

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..8d17d0e
--- /dev/null
+++ b/board-qemu/slof/vio-vtpm-cdriver.fs
@@ -0,0 +1,134 @@ 
+\ *****************************************************************************
+\ * 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
+
+false VALUE vtpm-debug?
+0     VALUE vtpm-unit
+0     VALUE vtpm-ihandle
+
+: setup-alias
+    " ibm,vtpm" find-alias 0= IF
+        " ibm,vtpm" get-node node>path set-alias
+    ELSE
+        drop
+    THEN
+;
+
+: vtpm-cleanup ( )
+    vtpm-debug? IF ." VTPM: Disabling RTAS bypass" cr THEN
+    tpm-finalize
+    vtpm-unit 0 rtas-set-tce-bypass
+;
+
+: vtpm-init ( -- true | false )
+    0 0 get-node open-node ?dup 0= IF 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
+
+    tpm-start ?dup 0= IF
+        vtpm-debug? IF ." VTPM: Success from tpm-start" cr THEN
+        setup-alias
+    ELSE
+        ." VTPM: Error code from tpm-start: " . cr
+    THEN
+
+    close-node
+    r> to my-self
+;
+
+\ forward a call to /ibm,vtpm, which implements the function with the
+\ given name
+: vtpm-call-forward ( arg ... arg name namelen -- ret ... ret failure? )
+    \ assign /ibm,vtpm node to vtpm-ihandle, if not assigned
+    vtpm-ihandle 0= IF
+        s" /ibm,vtpm" open-dev to vtpm-ihandle
+    THEN
+
+    vtpm-ihandle 0<> IF
+        vtpm-ihandle                   ( arg ... arg name namelen ihandle )
+        $call-method                   ( ret ... ret )
+        false                          ( ret ... ret false )
+    ELSE
+        true                           ( true )
+    THEN
+;
+
+\ firmware API call
+: sml-get-allocated-size ( -- buffer-size)
+    " sml-get-allocated-size" vtpm-call-forward IF
+        \ vtpm-call-forward failed
+        0
+    THEN
+;
+
+\ firmware API call
+: sml-get-handover-size ( -- size)
+    " sml-get-handover-size" vtpm-call-forward IF
+        \ vtpm-call-forward failed
+        0
+    THEN
+;
+
+\ firmware API call
+: sml-handover ( dest size -- )
+    " sml-handover" vtpm-call-forward IF
+        \ vtpm-call-forward failed; clean up stack
+        2drop
+    THEN
+;
+
+\ firmware API call
+: get-failure-reason ( -- reason )
+    " get-failure-reason" vtpm-call-forward IF
+        \ vtpm-call-forward failed; return a value
+        0 \ invalid
+    THEN
+;
+
+0 0 s" ibm,sml-efi-reformat-supported" property
+
+\ firmware API call
+: reformat-sml-to-efi-alignment ( -- success )
+    " reformat-sml-to-efi-alignment" vtpm-call-forward IF
+        false
+    THEN
+;
+
+: open ( )
+    vtpm-debug? IF ." VTPM: vTPM open()" cr THEN
+    true
+;
+
+: close ( )
+    vtpm-debug? IF ." VTPM: vTPM close()" cr THEN
+;
+
+\ setup alias and the RTAS bypass
+vtpm-init
+
+\ setup the log
+include vtpm-sml.fs
+
+s" /ibm,vtpm" find-node ?dup IF
+  s" measure-scrtm" rot $call-static
+THEN
diff --git a/board-qemu/slof/vtpm-sml.fs b/board-qemu/slof/vtpm-sml.fs
new file mode 100644
index 0000000..865dce6
--- /dev/null
+++ b/board-qemu/slof/vtpm-sml.fs
@@ -0,0 +1,115 @@ 
+\ *****************************************************************************
+\ * 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
+\ ****************************************************************************/
+
+\ KVM/qemu TPM Stored Measurement Log (SML) entries in /ibm,vtpm
+
+" /" find-device
+
+new-device
+
+false VALUE    vtpm-debug?
+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
+
+\ create /ibm,vtpm
+s" ibm,vtpm" 2dup device-name device-type
+
+\ convey logbase and size to the C driver
+log-base LOG-SIZE tpm-set-log-parameters
+
+: sml-get-allocated-size ( -- buffer-size)
+    vtpm-debug? IF
+        ." Call to sml-get-allocated-size; size = 0x" LOG-SIZE . cr
+    THEN
+    LOG-SIZE
+;
+
+: sml-get-handover-size ( -- size )
+    tpm-get-logsize
+    vtpm-debug? IF
+        ." Call to sml-get-handover-size; size = 0x" dup . cr
+    THEN
+;
+
+: sml-handover ( dest size -- )
+    vtpm-debug? IF
+        2dup
+        ." Call to sml-handover; size = 0x" . ." dest = " . cr
+    THEN
+    log-base        ( dest size src )
+    -rot            ( src dest size )
+    move
+
+    VTPM_DRV_ERROR_SML_HANDED_OVER tpm-driver-set-failure-reason
+;
+
+: get-failure-reason ( -- reason )
+    tpm-driver-get-failure-reason                  ( reason )
+    vtpm-debug? IF
+        ." VTPM: Return value from tpm-driver-get-failure-reason: " dup . cr
+    THEN
+;
+
+: reformat-sml-to-efi-alignment ( -- success? )
+    vtpm-debug? IF
+        ." Call to reformat-sml-to-efi-alignment" cr
+    THEN
+    \ a no-op since already byte aligned
+    true
+;
+
+\
+\ 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 -- )
+    0 7 separator-event
+    200 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
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 7efad28..24eacc1 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..4e81a42
--- /dev/null
+++ b/lib/libtpm/tcgbios.c
@@ -0,0 +1,918 @@ 
+/*****************************************************************************
+ * 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:
+ *  http://www.trustedcomputinggroup.org/resources/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))
+
+struct tpm_state {
+	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;
+};
+
+static struct tpm_state 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
+ ****************************************************************/
+
+static uint32_t tpm20_pcr_selection_size;
+static struct tpml_pcr_selection *tpm20_pcr_selection;
+
+/* A 'struct tpm_log_entry' is a local data structure containing a
+ * 'tpm_log_header' followed by space for the maximum supported
+ * digest. The digest is a series of tpm2_digest_value structs on tpm2.0.
+ */
+struct tpm_log_entry {
+	struct tpm_log_header hdr;
+	uint8_t pad[sizeof(struct tpm2_digest_values)
+	   + 5 * sizeof(struct tpm2_digest_value)
+	   + SHA1_BUFSIZE + SHA256_BUFSIZE + SHA384_BUFSIZE
+	   + SHA512_BUFSIZE + SM3_256_BUFSIZE];
+} __attribute__((packed));
+
+static const struct hash_parameters {
+	uint16_t hashalg;
+	uint8_t  hashalg_flag;
+	uint8_t  hash_buffersize;
+	const char *name;
+} 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 int
+tpm20_get_hash_buffersize(uint16_t hashAlg)
+{
+	unsigned i;
+
+	for (i = 0; i < ARRAY_SIZE(hash_parameters); i++) {
+		if (hash_parameters[i].hashalg == hashAlg)
+			return hash_parameters[i].hash_buffersize;
+	}
+	return -1;
+}
+
+/*
+ * Build the TPM2 tpm2_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 sha1 hash in the area of the sha256
+ * 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.digest + sizeof(struct tpm2_digest_values);
+	uint32_t count;
+	struct tpm2_digest_value *v;
+	struct tpm2_digest_values *vs;
+
+	if (!tpm20_pcr_selection)
+		return -1;
+
+	sel = tpm20_pcr_selection->selections;
+	end = (void *)tpm20_pcr_selection + tpm20_pcr_selection_size;
+
+	for (count = 0; count < be32_to_cpu(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.digest;
+	if (bigEndian)
+		vs->count = cpu_to_be32(count);
+	else
+		vs->count = cpu_to_le32(count);
+
+	return dest - (void*)le->hdr.digest;
+}
+
+/****************************************************************
+ * 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 = tpmhw_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 = tpmhw_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;
+
+	int 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 */
+	uint32_t 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;
+
+	tpm20_pcr_selection = SLOF_alloc_mem(size);
+	if (tpm20_pcr_selection) {
+		memcpy(tpm20_pcr_selection, &trg->data, size);
+		tpm20_pcr_selection_size = size;
+	} else {
+		printf("TCGBIOS: Failed to allocated %u bytes.\n", size);
+		ret = -1;
+	}
+
+	return ret;
+}
+
+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;
+
+	memcpy(tre, &tmp_tre, sizeof(tmp_tre));
+	memcpy(&tre->digest[0], le->hdr.digest, digest_len);
+
+	tre->hdr.totlen = cpu_to_be32(sizeof(tmp_tre) + digest_len);
+
+	struct tpm_rsp_header rsp;
+	uint32_t resp_length = sizeof(rsp);
+	int ret = tpmhw_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 = tpmhw_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);
+
+	if (buf_len > sizeof(rsp.rnd.buffer))
+		return -1;
+
+	int ret = tpmhw_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)),
+		},
+	};
+	memcpy(trhca.newAuth.buffer, auth, sizeof(trhca.newAuth.buffer));
+
+	struct tpm_rsp_header rsp;
+	uint32_t resp_length = sizeof(rsp);
+	int ret = tpmhw_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)
+{
+	/* we will try to deactivate the TPM now - ignoring all errors */
+	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 = tpmhw_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(struct tpm_log_header *entry,
+				   int digest_len,
+				   const void *event, uint32_t event_length)
+{
+	size_t size, logsize;
+	void *dest;
+
+	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(struct tpm_log_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);
+	struct tpm_log_trailer *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)
+{
+	if (!tpm20_pcr_selection)
+		return -1;
+
+	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 = tpm20_pcr_selection->selections;
+	void *nsel, *end = (void*)tpm20_pcr_selection + tpm20_pcr_selection_size;
+	int event_size;
+	uint32_t *vendorInfoSize;
+	struct tpm_log_entry le = {
+		.hdr.eventtype = cpu_to_log32(EV_NO_ACTION),
+	};
+	uint32_t count;
+
+	for (count = 0;
+	     count < be32_to_cpu(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 = 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;
+
+	sha256(hashdata, hashdatalen, hash);
+	digest_len = tpm20_build_digest(&le, hash, true);
+	if (digest_len < 0)
+		return TCGBIOS_GENERAL_ERROR;
+	int 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 rc = 0;
+	uint32_t pcrIndex;
+
+	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)
+			break;
+	}
+
+	return rc;
+}
+
+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 | sha1sum
+	 */
+	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 | sha1sum
+	 */
+	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_data_start = (char *)&_slof_data;
+	char *slof_text_start = (char *)&_slof_text;
+	uint32_t slof_data_length = (long)&_slof_data_end - (long)&_slof_data;
+	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 (data): start = %p, length = %d\n",
+		slof_data_start, slof_data_length);
+
+	rc = tpm_add_measurement_to_log(0, EV_S_CRTM_CONTENTS,
+					scrtm, strlen(scrtm),
+					(uint8_t *)slof_data_start,
+					slof_data_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..3cc4f46 100644
--- a/lib/libtpm/tcgbios_int.h
+++ b/lib/libtpm/tcgbios_int.h
@@ -15,6 +15,94 @@ 
 
 #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 */
+#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 SHA1_BUFSIZE                    20
+#define SHA256_BUFSIZE                  32
+#define SHA384_BUFSIZE                  48
+#define SHA512_BUFSIZE                  64
+#define SM3_256_BUFSIZE                 32
+
+#define BCV_DEVICE_HDD     0x80
+
+struct tpm2_digest_value {
+	uint16_t hashAlg;
+	uint8_t hash[0]; /* size depends on hashAlg */
+} __attribute__((packed));
+
+struct tpm2_digest_values {
+	uint32_t count;
+	struct tpm2_digest_value digest[0];
+} __attribute__((packed));
+
+/* Each entry in the TPM log contains: a tpm_log_header, a variable
+ * length digest, a tpm_log_trailer, and a variable length event.  The
+ * 'digest' matches what is sent to the TPM hardware via the Extend
+ * command.  On TPM1.2 the digest is a SHA1 hash; on TPM2.0 the digest
+ * contains a tpm2_digest_values struct followed by a variable number
+ * of tpm2_digest_value structs (as specified by the hardware via the
+ * TPM2_CAP_PCRS request).
+ */
+struct tpm_log_header {
+	uint32_t pcrindex;
+	uint32_t eventtype;
+	uint8_t digest[0];
+} __attribute__((packed));
+
+struct tpm_log_trailer {
+	uint32_t eventdatasize;
+	uint8_t event[0];
+} __attribute__((packed));
+
+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 blocks for the TCG BIOS commands */
+
+/* PCClient_PCREventStruct -- format of log entries; compatible with x86 */
+struct pcpes {
+	uint32_t pcrindex;
+	uint32_t eventtype;
+	uint8_t digest[SHA1_BUFSIZE];
+	uint32_t eventdatasize;
+	uint32_t event;
+} __attribute__((packed));
+
 struct tpm_req_header {
 	uint16_t tag;
 	uint32_t totlen;
@@ -27,4 +115,156 @@  struct tpm_rsp_header {
 	uint32_t errcode;
 } __attribute__((packed));
 
+/****************************************************************
+ * TPM v2.0 hardware 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_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)
+
+/* 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 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));
+
+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..77bb0f5 100644
--- a/slof/fs/packages/disk-label.fs
+++ b/slof/fs/packages/disk-label.fs
@@ -550,7 +550,15 @@  B9E5                CONSTANT GPT-BASIC-DATA-PARTITION-2
 \ load from a bootable partition
 : load-from-boot-partition ( addr -- size )
    debug-disk-label? IF ." Trying DOS boot " .s cr THEN
-   dup load-from-dos-boot-partition ?dup 0 <> IF nip EXIT THEN
+   dup load-from-dos-boot-partition ?dup 0 <> IF
+      nip
+      block s" /ibm,vtpm" find-node ?dup IF
+         s" measure-hdd-mbr" rot $call-static
+      ELSE
+         drop
+      THEN
+      EXIT
+   THEN
 
    debug-disk-label? IF ." Trying CHRP boot " .s cr THEN
    1 disk-chrp-boot !
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