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

Alexey Kardashevskiy aik at ozlabs.ru
Mon Jan 20 19:09:15 AEDT 2020



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 at 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 at linux.ibm.com
> + *     Kevin O'Connor, kevin at koconnor.net
> + *****************************************************************************/
> +
> +/*
> + *  Implementation of the TPM BIOS extension according to the specification
> + *  described in the IBM VTPM Firmware document 


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
> 

-- 
Alexey


More information about the SLOF mailing list