[SLOF] [PATCH v6 4/7] tpm: Add TPM CRQ driver implementation

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



On 16/01/2020 07:00, Stefan Berger wrote:
> This patch adds a TPM driver for the CRQ interface as used by
> the QEMU PAPR implementation.



> Also add a Readme that explains the benefits and installation procedure
> for the vTPM.
> 
> Signed-off-by: Stefan Berger <stefanb at linux.ibm.com>
> ---
>  board-qemu/Makefile      |   2 +-
>  include/helpers.h        |   1 +
>  lib/Makefile             |   2 +-
>  lib/libtpm/Makefile      |  50 +++++
>  lib/libtpm/Readme        |  95 ++++++++
>  lib/libtpm/tcgbios_int.h |  30 +++
>  lib/libtpm/tpm_drivers.c | 466 +++++++++++++++++++++++++++++++++++++++
>  lib/libtpm/tpm_drivers.h |  82 +++++++
>  slof/helpers.c           |   6 +
>  9 files changed, 732 insertions(+), 2 deletions(-)
>  create mode 100644 lib/libtpm/Makefile
>  create mode 100644 lib/libtpm/Readme
>  create mode 100644 lib/libtpm/tcgbios_int.h
>  create mode 100644 lib/libtpm/tpm_drivers.c
>  create mode 100644 lib/libtpm/tpm_drivers.h
> 
> diff --git a/board-qemu/Makefile b/board-qemu/Makefile
> index 61a1367..f419202 100644
> --- a/board-qemu/Makefile
> +++ b/board-qemu/Makefile
> @@ -15,7 +15,7 @@ BOARD_TARGETS = tools_build romfs_build stage1 subdirs
>  SUBDIRS = slof
>  
>  COMMON_LIBS = libc libbootmsg libbases libnvram libelf libhvcall libvirtio \
> -              libusb libveth libe1k libnet libbootmenu
> +              libusb libveth libe1k libnet libbootmenu libtpm
>  
>  all: $(BOARD_TARGETS)
>  	$(MAKE) boot_rom.bin
> diff --git a/include/helpers.h b/include/helpers.h
> index 9a06a5c..2f460d6 100644
> --- a/include/helpers.h
> +++ b/include/helpers.h
> @@ -44,6 +44,7 @@ extern int SLOF_get_property(const char *node, const char *propname,
>                               char **addr, int *len);
>  extern int SLOF_get_keystroke(void);
>  extern void SLOF_reset(void);
> +extern unsigned long SLOF_get_vtpm_unit(void);
>  
>  #define offset_of(type, member) ((long) &((type *)0)->member)
>  #define container_of(ptr, type, member) ({                      \
> diff --git a/lib/Makefile b/lib/Makefile
> index 1e8bb62..7369894 100644
> --- a/lib/Makefile
> +++ b/lib/Makefile
> @@ -11,7 +11,7 @@
>  # ****************************************************************************/
>  
>  SUBDIRS = libc libipmi libbootmsg libbases libnvram libelf libhvcall libvirtio \
> -          libusb libveth libe1k libbcm libnet libbootmenu
> +          libusb libveth libe1k libbcm libnet libbootmenu libtpm
>  
>  all:  subdirs
>  
> diff --git a/lib/libtpm/Makefile b/lib/libtpm/Makefile
> new file mode 100644
> index 0000000..ff19e1c
> --- /dev/null
> +++ b/lib/libtpm/Makefile
> @@ -0,0 +1,50 @@
> +# *****************************************************************************
> +# * Copyright (c) 2015 IBM Corporation
> +# * All rights reserved.
> +# * This program and the accompanying materials
> +# * are made available under the terms of the BSD License
> +# * which accompanies this distribution, and is available at
> +# * http://www.opensource.org/licenses/bsd-license.php
> +# *
> +# * Contributors:
> +# *     IBM Corporation - initial implementation
> +# ****************************************************************************/
> +
> +TOPCMNDIR ?= ../..
> +
> +CPPFLAGS = -I../libc/include $(CPUARCHDEF) -I$(INCLBRDDIR) \
> +	   -I$(INCLCMNDIR) -I$(INCLCMNDIR)/$(CPUARCH) -I$(SLOFCMNDIR)
> +CPPFLAGS += -I../libhvcall
> +
> +LDFLAGS = -nostdlib
> +
> +TARGET = ../libtpm.a
> +
> +
> +all: $(TARGET)
> +
> +SRCS = tpm_drivers.c
> +
> +OBJS = $(SRCS:%.c=%.o)
> +
> +$(TARGET): $(OBJS)
> +	$(AR) -rc $@ $(OBJS)
> +	$(RANLIB) $@
> +
> +clean:
> +	$(RM) $(TARGET) $(OBJS)
> +
> +distclean: clean
> +	$(RM) Makefile.dep
> +
> +
> +# Rules for creating the dependency file:
> +depend:
> +	$(RM) Makefile.dep
> +	$(MAKE) Makefile.dep
> +
> +Makefile.dep: Makefile
> +	$(CC) -M $(CPPFLAGS) $(CFLAGS) $(SRCS) $(SRCSS) > Makefile.dep
> +
> +# Include dependency file if available:
> +-include Makefile.dep
> diff --git a/lib/libtpm/Readme b/lib/libtpm/Readme
> new file mode 100644
> index 0000000..50f5e95
> --- /dev/null
> +++ b/lib/libtpm/Readme
> @@ -0,0 +1,95 @@
> +This directory hosts (v)TPM related code.
> +
> +Background:
> +-----------
> +
> +A TPM is a crypto chip that is found in many systems. Besides it offering
> +a secure key store, among other functionality, it is also used to implement
> +'trusted boot'. This is realized by code in the firmware measuring parts of the
> +firmware's code and data as well as system data, such as the boot block, and
> +logging these measurements and storing (extending) them in the TPM's platform
> +configuration register (PCR).
> +
> +The benefits of having a TPM (or vTPM) in a system are:
> +
> +- enablement of trusted boot; this allow us to eventually extend the chain of
> +  trust from the hypervisor to the guests
> +- enablement of attestation so that one can verify what software is running on
> +  a machine (OpenPTS, OpenAttestation)
> +- provides TPM functionality to VMs, which includes a standardized mechanism
> +  to store keys and other blobs (Linux trusted keys, GNU TLS's TPM extensions)
> +
> +
> +QEMU/KVM + SLOF support:
> +------------------------
> +
> +To enable a vTPM with QEMU, the following steps need to be followed
> +
> +- build a recent version of libtpms
> +
> +  #> git clone https://github.com/stefanberger/libtpms
> +  #> cd libtpms
> +  #> ./autogen.sh --prefix=/usr --with-tpm2 --with-openssl
> +
> +  The following step may require to install dependencies
> +
> +  #> make
> +  #> make check
> +  #> make install
> +
> +- build swtpm
> +make[2]: *** No rule to make target 'tcgbios.h', needed by 'paflof.o'.  Stop

> +  #> git clone https://github.com/stefanberger/swtpm
> +  #> cd swtpm
> +  #> ./autogen.sh --prefix=/usr --with-openssl
> +
> +  The following step may require to install dependencies
> +
> +  #> ./configure --prefix=/usr --with-openssl
> +  #> make
> +  #> make check
> +  #> make install


None of the step above actually enables "a vTPM with QEMU". And I am
pretty sure they won't be needed either in the field, I'd think libvirt
will do what swtpm/libtpms do.


> +
> +- build QEMU with vTPM support:
> +
> +  #> git clone https://github.com/stefanberger/qemu-tpm
> +  #> cd qemu-tpm
> +
> +  The PPC64 patches are currently in the tpm-next+spapr.v8 branch
> +  #> git checkout origin/tpm-next+spapr.v8 -b tpm-next+spapr.v8
> +
> +  The following step may require to install dependencies
> +
> +  #> ./configure --prefix=/usr --enable-kvm --target-list="ppc64-softmmu"
> +  #> make
> +  #> make install


All this can be reduces to "QEMU has vTPM support since the v5.0.0
release", or similar.


> +
> +To start a QEMU VM with an attached vTPM (swtpm), run the following commands
> +as 'root'. 

Why as 'root'? Will swtpm forward these requests to an actual tpm
device? How is it expected to work on the real system?


> The following will setup the vTPM so that its state will be stored
> +in /tmp/mytpm1. A unique directory for each VM instance with attached vTPM
> +should be provided. Whenever QEMU is started, the swtpm has to be started
> +before it. The './boot_rom.bin' represents SLOF with vTPM extensions built-in.
> +
> +  #> mkdir -p /tmp/mytpm1
> +  #> swtpm socket --tpm2 --tpmstate dir=/tmp \
> +       --ctrl type=unixio,path=/tmp/mytpm1/ctrl.sock
> +
> +  In another terminal:
> +
> +  #> qemu-system-ppc64 \
> +      -enable-kvm \
> +      -boot menu=on \
> +      --chardev socket,id=chrtpm,path=/tmp/mytpm1/ctrl.sock \
> +      -tpmdev emulator,id=tpm0,chardev=chrtpm \
> +      -device tpm-spapr,tpmdev=tpm0 \
> +      -vnc 0.0.0.0:2 \
> +      [...]
> +
> +     Add hard disk and other parameters as needed.
> +
> +Notes:
> +  - The Linux kernel in the VM must have the tpm_ibmvtpm module available
> +    or built-in.
> +
> +  - 'swtpm_ioctl --unix /tmp/ctrl.sock -s' can be used to gracefully shut
> +    down the vTPM.
> diff --git a/lib/libtpm/tcgbios_int.h b/lib/libtpm/tcgbios_int.h
> new file mode 100644
> index 0000000..835bd36
> --- /dev/null
> +++ b/lib/libtpm/tcgbios_int.h
> @@ -0,0 +1,30 @@
> +/*****************************************************************************
> + * Copyright (c) 2015-2020 IBM Corporation
> + * All rights reserved.
> + * This program and the accompanying materials
> + * are made available under the terms of the BSD License
> + * which accompanies this distribution, and is available at
> + * http://www.opensource.org/licenses/bsd-license.php
> + *
> + * Contributors:
> + *     IBM Corporation - initial implementation
> + *****************************************************************************/
> +
> +#ifndef TCGBIOS_INT_H
> +#define TCGBIOS_INT_H
> +
> +#include <stdint.h>
> +
> +struct tpm_req_header {
> +	uint16_t tag;
> +	uint32_t totlen;
> +	uint32_t ordinal;
> +} __attribute__((packed));
> +
> +struct tpm_rsp_header {
> +	uint16_t tag;
> +	uint32_t totlen;
> +	uint32_t errcode;
> +} __attribute__((packed));
> +
> +#endif /* TCGBIOS_INT_H */
> diff --git a/lib/libtpm/tpm_drivers.c b/lib/libtpm/tpm_drivers.c
> new file mode 100644
> index 0000000..c1335d7
> --- /dev/null
> +++ b/lib/libtpm/tpm_drivers.c
> @@ -0,0 +1,466 @@
> +/*****************************************************************************
> + * Copyright (c) 2015-2020 IBM Corporation
> + * All rights reserved.
> + * This program and the accompanying materials
> + * are made available under the terms of the BSD License
> + * which accompanies this distribution, and is available at
> + * http://www.opensource.org/licenses/bsd-license.php
> + *
> + * Contributors:
> + *     IBM Corporation - initial implementation
> + *****************************************************************************/
> +
> +#include <stdlib.h>
> +#include <stdio.h>
> +#include <stdbool.h>
> +
> +#include "string.h"
> +#include "helpers.h"
> +#include "byteorder.h"
> +#include "tcgbios_int.h"
> +#include "tpm_drivers.h"
> +#include "libhvcall.h"
> +#include "paflof.h"
> +
> +#undef PAPR_VTPM_DEBUG
> +//#define PAPR_VTPM_DEBUG
> +#ifdef PAPR_VTPM_DEBUG
> +#define dprintf(_x ...) do { printf("VTPM CRQ: " _x); } while(0)
> +#else
> +#define dprintf(_x ...)
> +#endif
> +
> +#define MIN(a, b) ((a) > (b) ? (b) : (a))
> +
> +/* layout of the command request queue for vTPM */


Is every single packed struct added by this patch defined as a big endian?

Or everything with the "tpm2_" prefix is little endian, and the rest is
big endian?


> +struct crq {
> +	uint8_t valid;
> +	uint8_t msg;
> +	uint16_t len;
> +	uint32_t data;
> +	uint64_t reserved;
> +} __attribute__((packed));
> +
> +#define PAPR_VTPM_INIT_CRQ_COMMAND      0xC0
> +#define PAPR_VTPM_VALID_COMMAND         0x80
> +#define PAPR_VTPM_MSG_RESULT            0x80
> +
> +/* crq.msg request types when crq.valid = PAPR_VTPM_INIT_CRQ_COMMAND */
> +#define PAPR_VTPM_INIT_CRQ_RESULT       0x1
> +
> +/* crq.msg request types when crq.valid = PAPR_VTPM_VALID_COMMAND */
> +#define PAPR_VTPM_GET_VERSION           0x1
> +#define PAPR_VTPM_TPM_COMMAND           0x2
> +#define PAPR_VTPM_GET_RTCE_BUFFER_SIZE  0x3
> +
> +#define TPM2_DEFAULT_DURATION_SHORT       750000 /* us */
> +#define TPM2_DEFAULT_DURATION_MEDIUM     2000000 /* us */
> +#define TPM2_DEFAULT_DURATION_LONG       2000000 /* us */
> +
> +static const uint32_t tpm2_durations[3] = {
> +	TPM2_DEFAULT_DURATION_SHORT,
> +	TPM2_DEFAULT_DURATION_MEDIUM,
> +	TPM2_DEFAULT_DURATION_LONG,
> +};
> +
> +#define QUEUE_SIZE 4096
> +
> +/* state of the PAPR CRQ VTPM driver */
> +static struct spapr_vtpm_driver_state {

s/spapr_vtpm_driver_state// as this name is not used anyway anywhere.


> +	/* whether it driver been initialized */
> +	bool initialized;
> +
> +	/* unit number */
> +	unsigned long unit;
> +
> +	/* CRQ queue address and size */
> +	unsigned char *qaddr;
> +	unsigned long qsize;


The whole struct is static so you could simply do qaddr[QUEUE_SIZE] and
drop @qsize and allocation.

> +
> +	/* current q_entry */
> +	unsigned int curr_q_entry;
> +
> +	/* current response CRQ */
> +	struct crq *response;
> +
> +	/* power firmware defined state and error code */
> +	vtpm_drv_state driver_state;
> +	vtpm_drv_error driver_error;
> +
> +	/* size of buffer supported by hypervisor */
> +	unsigned int buffer_size;
> +
> +        /* buffer for commands and responses */
> +        char *buffer;


Indentation broke.

> +
> +	/* version of the TPM we talk to -- from CRQ message */
> +	uint32_t tpm_version;

It is always v2.0 for now.


> +} spapr_vtpm = {
> +	.qsize = QUEUE_SIZE,
> +	.driver_state = VTPM_DRV_STATE_INVALID,
> +	.driver_error = VTPM_DRV_ERROR_NO_FAILURE,
> +};
> +
> +static void vtpm_drv_state_set(vtpm_drv_state s, vtpm_drv_error e)



Some functions have "vtpm_" prefix, some "spapr_", some none (such as
"get_crq").


> +{
> +	spapr_vtpm.driver_state = s;
> +	spapr_vtpm.driver_error = e;
> +}
> +
> +static vtpm_drv_error vtpm_drv_error_get(void)
> +{
> +	return spapr_vtpm.driver_error;
> +}
> +
> +static struct crq* get_crq(void *qaddr, unsigned long q_entry)

Please s/crq* get_crq/crq *get_crq/g  as everywhere else.


> +{
> +	return &((struct crq *)qaddr)[q_entry];
> +}
> +
> +/*
> + * Get the crq where the response will be found. This
> + * function will clear the CRQ's valid field and advance
> + * the entry counter to the next entry.
> + */
> +static struct crq *get_response_crq(void)
> +{
> +	struct crq *crq;
> +
> +	dprintf("curr_q_entry = %d\n", spapr_vtpm.curr_q_entry);
> +
> +	crq = get_crq(spapr_vtpm.qaddr, spapr_vtpm.curr_q_entry);
> +	memset(crq, 0, sizeof(*crq));
> +
> +	spapr_vtpm.curr_q_entry += 1;
> +	if (spapr_vtpm.curr_q_entry == (spapr_vtpm.qsize / sizeof(struct crq)))
> +		spapr_vtpm.curr_q_entry = 0;
> +
> +	return crq;
> +}
> +
> +/*
> + * Send a message via CRQ and wait for the response
> + */
> +static bool spapr_send_crq_and_wait(unsigned long unit,
> +				    struct crq *crq,
> +				    struct crq **response,
> +				    unsigned timeout,
> +				    vtpm_drv_state state1,
> +				    vtpm_drv_state state2)
> +{
> +	long rc;
> +	unsigned i;
> +
> +	*response = get_response_crq();
> +
> +	vtpm_drv_state_set(state1, VTPM_DRV_ERROR_NO_FAILURE);
> +
> +	rc = hv_send_crq(unit, (uint64_t *)crq);
> +	if (rc != H_SUCCESS) {
> +		vtpm_drv_state_set(VTPM_DRV_STATE_WAIT_INIT,
> +				   VTPM_DRV_ERROR_TPM_CRQ_ERROR);
> +		return false;
> +	}
> +
> +	vtpm_drv_state_set(state2, VTPM_DRV_ERROR_NO_FAILURE);
> +
> +	for (i = 0; i < timeout; i += 1000) {
> +		if (((*response)->valid & PAPR_VTPM_MSG_RESULT))
> +			return true;
> +		SLOF_usleep(1000);
> +	}
> +
> +	vtpm_drv_state_set(VTPM_DRV_STATE_FAILURE,
> +			   VTPM_DRV_ERROR_WAIT_TIMEOUT);
> +
> +	dprintf("Received no response from CRQ\n");
> +	return false;
> +}
> +
> +/*
> + * Get parameters from the CRQ
> + */
> +static bool spapr_vtpm_get_params(void)
> +{
> +	struct crq crq, *response;
> +	static bool completed = false; /* only once */
> +
> +	if (completed)
> +		return true;
> +
> +	/* get the TPM version */
> +	crq.valid = PAPR_VTPM_VALID_COMMAND;
> +	crq.msg = PAPR_VTPM_GET_VERSION;
> +
> +	if (!spapr_send_crq_and_wait(spapr_vtpm.unit, &crq, &response, 10,
> +				     VTPM_DRV_STATE_SEND_GET_VERSION,
> +				     VTPM_DRV_STATE_WAIT_VERSION)) {
> +		printf("%s: Failure getting TPM version from CRQ\n", __func__);
> +		return false;
> +	}
> +
> +	vtpm_drv_state_set(VTPM_DRV_STATE_CHECK_VERSION,
> +			   VTPM_DRV_ERROR_NO_FAILURE);
> +
> +	spapr_vtpm.tpm_version = be32_to_cpu(response->data);
> +	dprintf("TPM backend version: %d\n", spapr_vtpm.tpm_version);
> +
> +	/* get the TPM's buffer size */
> +	crq.valid = PAPR_VTPM_VALID_COMMAND;
> +	crq.msg = PAPR_VTPM_GET_RTCE_BUFFER_SIZE;
> +
> +	if (!spapr_send_crq_and_wait(spapr_vtpm.unit, &crq, &response, 10,
> +				     VTPM_DRV_STATE_SEND_BUFSIZE_REQ,
> +				     VTPM_DRV_STATE_WAIT_BUFSIZE)) {
> +		printf("%s: Failure getting RTCE buffer size from CRQ\n",
> +		       __func__);
> +		return false;
> +	}
> +
> +	vtpm_drv_state_set(VTPM_DRV_STATE_ALLOC_RTCE_BUF,
> +			   VTPM_DRV_ERROR_NO_FAILURE);
> +
> +	dprintf("RTCE buffer size: %u\n", be16_to_cpu(response->len));
> +	spapr_vtpm.buffer_size = be16_to_cpu(response->len);
> +	if (spapr_vtpm.buffer_size < 1024) {
> +		printf("%s: RTCE buffer size of %u bytes is too small. "
> +		       "Minimum is 1024 bytes.\n", __func__,
> +		       spapr_vtpm.buffer_size);
> +		vtpm_drv_state_set(VTPM_DRV_STATE_FAILURE,
> +				   VTPM_DRV_ERROR_BAD_RTCE_SIZE);
> +		return false;
> +	}
> +	spapr_vtpm.buffer = SLOF_alloc_mem(spapr_vtpm.buffer_size);
> +	if (!spapr_vtpm.buffer) {
> +		printf("%s: Could not allocate buffer of size %u.\n",
> +		       __func__, spapr_vtpm.buffer_size);
> +		vtpm_drv_state_set(VTPM_DRV_STATE_FAILURE,
> +				   VTPM_DRV_ERROR_BAD_RTCE_SIZE);
> +		return false;
> +	}
> +
> +	completed = true;
> +
> +	return true;
> +}
> +
> +static bool spapr_vtpm_activate(void)
> +{
> +	long rc;
> +	struct crq crq, *response;
> +
> +	if (vtpm_drv_error_get() != VTPM_DRV_ERROR_NO_FAILURE) {
> +		printf("%s: CRQ: In failure mode\n", __func__);
> +		return false;
> +	}
> +
> +	vtpm_drv_state_set(VTPM_DRV_STATE_REG_CRQ,
> +			   VTPM_DRV_ERROR_NO_FAILURE);
> +
> +	rc = hv_reg_crq(spapr_vtpm.unit, (unsigned long)spapr_vtpm.qaddr,
> +			spapr_vtpm.qsize);
> +	if (rc != H_SUCCESS) {
> +		vtpm_drv_state_set(VTPM_DRV_STATE_WAIT_INIT,
> +				   VTPM_DRV_ERROR_UNEXPECTED_REG_ERROR);
> +		printf("%s: CRQ registration failed\n", __func__);
> +		return false;
> +	}
> +
> +	/* we always start with curr_q_entry 0 */
> +	spapr_vtpm.curr_q_entry = 0;
> +
> +	if (!spapr_vtpm.initialized) {
> +
> +		crq.valid = PAPR_VTPM_INIT_CRQ_COMMAND;
> +		crq.msg = PAPR_VTPM_INIT_CRQ_RESULT;
> +
> +		if (!spapr_send_crq_and_wait(spapr_vtpm.unit,
> +					     &crq,
> +					     &response,
> +					     10,
> +					     VTPM_DRV_STATE_SEND_INIT,
> +					     VTPM_DRV_STATE_WAIT_INIT_COMP)) {
> +			printf("%s: Initializing CRQ failed\n", __func__);
> +			goto err_exit;
> +		}
> +		dprintf("Successfully initialized CRQ\n");
> +
> +		spapr_vtpm.initialized = true;
> +	}
> +
> +	if (spapr_vtpm_get_params())
> +		return true;
> +
> +err_exit:
> +	hv_free_crq(spapr_vtpm.unit);
> +	spapr_vtpm.unit = 0;
> +
> +	return false;
> +}
> +
> +void spapr_vtpm_finalize(void)
> +{
> +	if (spapr_vtpm.unit)
> +		hv_free_crq(spapr_vtpm.unit);


spapr_vtpm.unit = 0; as above?

> +}
> +
> +/*
> + * Check whether we have a CRQ underneath us; if we do, the CRQ will
> + * be left open.
> + */
> +static bool spapr_vtpm_probe(void)
> +{
> +	if (!spapr_vtpm.qaddr) {
> +		spapr_vtpm.qaddr = SLOF_alloc_mem(spapr_vtpm.qsize);
> +		if (!spapr_vtpm.qaddr) {
> +			printf("%s: Unable to allocate memory\n", __func__);
> +			return false;
> +		}
> +		memset(spapr_vtpm.qaddr, 0, spapr_vtpm.qsize);
> +
> +		dprintf("getting FORTH vtpm-unit\n");
> +		spapr_vtpm.unit = SLOF_get_vtpm_unit();
> +		if (!spapr_vtpm.unit) {
> +			printf("%s: Could not get valid vtpm-unit\n", __func__);
> +			return false;
> +		}
> +	}
> +
> +	dprintf("vtpm_unit = %lx, buffer = %p\n",
> +		spapr_vtpm.unit, spapr_vtpm.qaddr);
> +
> +	if (!spapr_vtpm_activate())
> +		return false;
> +
> +	return true;
> +}
> +
> +static bool spapr_vtpm_senddata(const uint8_t *const data, uint32_t len)
> +{
> +	struct crq crq;
> +	long rc;
> +
> +	if (vtpm_drv_error_get() != VTPM_DRV_ERROR_NO_FAILURE) {
> +		printf("%s: VTPM CRQ: In failure mode\n", __func__);
> +		return false;
> +	}
> +
> +	if (len > spapr_vtpm.buffer_size) {
> +		printf("%s: VTPM CRQ: Send buffer too large: %u > %u\n",
> +		       __func__, len, spapr_vtpm.buffer_size);
> +		return false;
> +	}
> +
> +	spapr_vtpm.response = get_response_crq();
> +	spapr_vtpm.response->data = (uint64_t)spapr_vtpm.buffer;
> +	/* response CRQ has been set and valid field cleared */


Where does "response CRQ has been set" happen? Is it hv_reg_crq() from
spapr_vtpm_activate()? If that so, then I guess we can just drop all
three "response CRQ has been set".


> +
> +	crq.valid = PAPR_VTPM_VALID_COMMAND;
> +	crq.msg = PAPR_VTPM_TPM_COMMAND;
> +	crq.len = cpu_to_be16(len);
> +	crq.data = (uint64_t)spapr_vtpm.buffer;
> +	memcpy(spapr_vtpm.buffer, data, MIN(len, spapr_vtpm.buffer_size));
> +
> +	vtpm_drv_state_set(VTPM_DRV_STATE_SEND_TPM_CMD,
> +			   VTPM_DRV_ERROR_NO_FAILURE);
> +
> +	rc = hv_send_crq(spapr_vtpm.unit, (uint64_t *)&crq);
> +
> +	if (rc == H_SUCCESS) {
> +		vtpm_drv_state_set(VTPM_DRV_STATE_WAIT_TPM_RSP,
> +				   VTPM_DRV_ERROR_NO_FAILURE);
> +	} else {
> +		vtpm_drv_state_set(VTPM_DRV_STATE_WAIT_INIT,
> +				   VTPM_DRV_ERROR_UNEXPECTED_SEND_ERROR);
> +	}

No need in curly braces here.

> +
> +	return (rc == H_SUCCESS);
> +}
> +
> +static bool spapr_vtpm_waitresponseready(enum tpm_duration_type to_t)
> +{
> +	uint32_t timeout = tpm2_durations[to_t];
> +	int i;
> +
> +	if (vtpm_drv_error_get() != VTPM_DRV_ERROR_NO_FAILURE) {
> +		printf("%s: VTPM CRQ: In failure mode\n", __func__);
> +		return false;
> +	}
> +
> +	/* response CRQ has been set */
> +
> +	for (i = 0; i < timeout; i += 1000) {
> +		if (spapr_vtpm.response->valid & PAPR_VTPM_MSG_RESULT) {
> +			/* TPM responded: move to Send tpm-cmd state */
> +			vtpm_drv_state_set(VTPM_DRV_STATE_SEND_TPM_CMD,
> +					   VTPM_DRV_ERROR_NO_FAILURE);
> +			dprintf("Received response to TPM command\n");
> +			return true;
> +		}
> +		SLOF_usleep(1000);
> +	}
> +
> +	vtpm_drv_state_set(VTPM_DRV_STATE_FAILURE,
> +			   VTPM_DRV_ERROR_WAIT_TIMEOUT);
> +
> +	dprintf("Received NO response to TPM command");
> +
> +	return false;
> +}
> +
> +static bool spapr_vtpm_readresponse(uint8_t *buffer, uint32_t *len)
> +{
> +	uint32_t length;
> +
> +	if (vtpm_drv_error_get() != VTPM_DRV_ERROR_NO_FAILURE) {
> +		printf("%s: VTPM CRQ: In failure mode\n", __func__);
> +		return false;
> +	}
> +
> +	/* response CRQ has been set */
> +	length = MIN(*len, be32_to_cpu(spapr_vtpm.response->len));
> +
> +	memcpy(buffer, (void *)(uint64_t)spapr_vtpm.response->data, length);
> +
> +	dprintf("Length of copied response: %d\n", length);
> +
> +	spapr_vtpm.response = NULL;
> +	*len = length;
> +
> +	return true;
> +}
> +
> +vtpm_drv_error spapr_vtpm_get_error(void)
> +{
> +	return vtpm_drv_error_get();
> +}
> +
> +void spapr_vtpm_set_error(vtpm_drv_error errcode)
> +{
> +	spapr_vtpm.driver_error = errcode;
> +}
> +
> +/**** higher layer interface ****/
> +
> +bool spapr_is_vtpm_present(void)
> +{
> +	bool rc = false;
> +
> +	if (spapr_vtpm_probe()) {
> +		rc = true;
> +	}
> +
> +	return rc;
> +}
> +
> +int tpmhw_transmit(uint8_t locty, struct tpm_req_header *req,
> +                   void *respbuffer, uint32_t *respbufferlen,
> +                   enum tpm_duration_type to_t)
> +{
> +	if (!spapr_vtpm_senddata((uint8_t *)req, be32_to_cpu(req->totlen)) ||
> +	    !spapr_vtpm_waitresponseready(to_t) ||
> +	    !spapr_vtpm_readresponse(respbuffer, respbufferlen) ||
> +	    *respbufferlen < sizeof(struct tpm_rsp_header))
> +		return -1;
> +	return 0;
> +}
> diff --git a/lib/libtpm/tpm_drivers.h b/lib/libtpm/tpm_drivers.h
> new file mode 100644
> index 0000000..1de1532
> --- /dev/null
> +++ b/lib/libtpm/tpm_drivers.h
> @@ -0,0 +1,82 @@
> +/*****************************************************************************
> + * Copyright (c) 2015-2020 IBM Corporation
> + * All rights reserved.
> + * This program and the accompanying materials
> + * are made available under the terms of the BSD License
> + * which accompanies this distribution, and is available at
> + * http://www.opensource.org/licenses/bsd-license.php
> + *
> + * Contributors:
> + *     IBM Corporation - initial implementation
> + *****************************************************************************/
> +
> +#ifndef TPM_DRIVERS_H
> +#define TPM_DRIVERS_H
> +
> +#include <stdint.h>
> +#include <stdbool.h>
> +#include <unistd.h>
> +
> +#include "tcgbios_int.h"
> +
> +enum tpm_duration_type {
> +	TPM_DURATION_TYPE_SHORT = 0,
> +	TPM_DURATION_TYPE_MEDIUM,
> +	TPM_DURATION_TYPE_LONG,
> +};
> +
> +/* firmware driver states */
> +typedef enum {
> +	VTPM_DRV_STATE_INVALID = 0,
> +	VTPM_DRV_STATE_INIT_CALLED = 1,
> +	VTPM_DRV_STATE_REG_CRQ = 2,
> +	VTPM_DRV_STATE_WAIT_INIT = 3,
> +	VTPM_DRV_STATE_SEND_INIT = 4,
> +	VTPM_DRV_STATE_FAILURE = 5,
> +	VTPM_DRV_STATE_WAIT_INIT_COMP = 6,
> +	VTPM_DRV_STATE_SEND_INIT_COMP = 7,
> +	VTPM_DRV_STATE_SEND_GET_VERSION = 8,
> +	VTPM_DRV_STATE_WAIT_VERSION = 9,
> +	VTPM_DRV_STATE_CHECK_VERSION = 10,
> +	VTPM_DRV_STATE_SEND_BUFSIZE_REQ = 11,
> +	VTPM_DRV_STATE_WAIT_BUFSIZE = 12,
> +	VTPM_DRV_STATE_ALLOC_RTCE_BUF = 13,
> +	VTPM_DRV_STATE_SEND_TPM_CMD = 14,
> +	VTPM_DRV_STATE_WAIT_TPM_RSP = 15,
> +} vtpm_drv_state;
> +
> +/* firmware driver errors */
> +typedef enum {
> +	VTPM_DRV_ERROR_NO_FAILURE = -1,
> +	VTPM_DRV_ERROR_NOT_FOUND_TIMEOUT = 0,
> +	VTPM_DRV_ERROR_UNEXPECTED_REG_ERROR = 1,
> +	VTPM_DRV_ERROR_PARTNER_FAILED = 2,
> +	VTPM_DRV_ERROR_UNEXPECTED_TSP_ERROR = 3,
> +	VTPM_DRV_ERROR_TPM_PROTOCOL_ERROR = 4,
> +	VTPM_DRV_ERROR_WAIT_TIMEOUT = 5,
> +	VTPM_DRV_ERROR_UNEXPECTED_SEND_ERROR = 6,
> +	VTPM_DRV_ERROR_CRQ_OPEN_FAIL = 7,
> +	VTPM_DRV_ERROR_BAD_STATE = 8,
> +	VTPM_DRV_ERROR_TPM_FAIL = 9,
> +	VTPM_DRV_ERROR_TPM_CRQ_ERROR = 10,
> +	VTPM_DRV_ERROR_BAD_VERSION = 11,
> +	VTPM_DRV_ERROR_BAD_RTCE_SIZE = 12,
> +	VTPM_DRV_ERROR_SML_FAILURE = 13,
> +	VTPM_DRV_ERROR_SML_HANDED_OVER = 14,
> +} vtpm_drv_error;
> +
> +/* the max. buffer size by the external TPM is 4k */
> +#define PAPR_VTPM_MAX_BUFFER_SIZE       4096
> +
> +/* exported functions */
> +bool spapr_is_vtpm_present(void);
> +void spapr_vtpm_finalize(void);
> +vtpm_drv_error spapr_vtpm_get_error(void);
> +void spapr_vtpm_set_error(vtpm_drv_error errcode);
> +
> +struct tpm_req_header;
> +int tpmhw_transmit(uint8_t locty, struct tpm_req_header *req,
> +                   void *respbuffer, uint32_t *respbufferlen,
> +                   enum tpm_duration_type to_t);
> +
> +#endif /* TPM_DRIVERS_H */
> diff --git a/slof/helpers.c b/slof/helpers.c
> index 9d37bc3..64023b2 100644
> --- a/slof/helpers.c
> +++ b/slof/helpers.c
> @@ -235,3 +235,9 @@ void SLOF_reset(void)
>  {
>  	forth_eval("reset-all");
>  }
> +
> +unsigned long SLOF_get_vtpm_unit(void)
> +{
> +	forth_eval("vtpm-unit");
> +	return forth_pop();
> +}
> 

-- 
Alexey


More information about the SLOF mailing list