[Skiboot] [PATCH 11/15] libstb/drivers: Add I2C nuvoton driver for TPM 2.0

Stewart Smith stewart at linux.vnet.ibm.com
Tue Sep 20 18:21:19 AEST 2016


Claudio Carvalho <cclaudio at linux.vnet.ibm.com> writes:
> This adds a driver for the 'nuvoton,npct650' TPM 2.0. The driver was
> ported and adapted from hostboot tpmdd.C.
>
> Signed-off-by: Claudio Carvalho <cclaudio at linux.vnet.ibm.com>
> ---
>  libstb/Makefile.inc              |   3 +-
>  libstb/drivers/Makefile.inc      |  11 +
>  libstb/drivers/tpm_i2c_nuvoton.c | 653 +++++++++++++++++++++++++++++++++++++++
>  libstb/drivers/tpm_i2c_nuvoton.h |  22 ++
>  libstb/status_codes.h            |   6 +
>  libstb/tpm.c                     |   2 +
>  6 files changed, 696 insertions(+), 1 deletion(-)
>  create mode 100644 libstb/drivers/Makefile.inc
>  create mode 100644 libstb/drivers/tpm_i2c_nuvoton.c
>  create mode 100644 libstb/drivers/tpm_i2c_nuvoton.h
>
> diff --git a/libstb/Makefile.inc b/libstb/Makefile.inc
> index 4cffd54..693e888 100644
> --- a/libstb/Makefile.inc
> +++ b/libstb/Makefile.inc
> @@ -8,6 +8,7 @@ LIBSTB_SRCS = container.c tpm.c
>  LIBSTB_OBJS = $(LIBSTB_SRCS:%.c=%.o)
>  LIBSTB = $(LIBSTB_DIR)/built-in.o
>  
> +include $(SRC)/$(LIBSTB_DIR)/drivers/Makefile.inc
>  include $(SRC)/$(LIBSTB_DIR)/tss/Makefile.inc
>  
> -$(LIBSTB): $(LIBSTB_OBJS:%=$(LIBSTB_DIR)/%) $(TSS)
> +$(LIBSTB): $(LIBSTB_OBJS:%=$(LIBSTB_DIR)/%) $(DRIVERS) $(TSS)
> diff --git a/libstb/drivers/Makefile.inc b/libstb/drivers/Makefile.inc
> new file mode 100644
> index 0000000..12d8e82
> --- /dev/null
> +++ b/libstb/drivers/Makefile.inc
> @@ -0,0 +1,11 @@
> +# -*-Makefile-*-
> +
> +DRIVERS_DIR = libstb/drivers
> +
> +SUBDIRS += $(DRIVERS_DIR)
> +
> +DRIVERS_SRCS = tpm_i2c_nuvoton.c
> +DRIVERS_OBJS = $(DRIVERS_SRCS:%.c=%.o)
> +DRIVERS = $(DRIVERS_DIR)/built-in.o
> +
> +$(DRIVERS): $(DRIVERS_OBJS:%=$(DRIVERS_DIR)/%)
> diff --git a/libstb/drivers/tpm_i2c_nuvoton.c b/libstb/drivers/tpm_i2c_nuvoton.c
> new file mode 100644
> index 0000000..b615347
> --- /dev/null
> +++ b/libstb/drivers/tpm_i2c_nuvoton.c
> @@ -0,0 +1,653 @@
> +/* Copyright 2013-2016 IBM Corp.
> + *
> + * Licensed under the Apache License, Version 2.0 (the "License");
> + * you may not use this file except in compliance with the License.
> + * You may obtain a copy of the License at
> + *
> + *      http://www.apache.org/licenses/LICENSE-2.0
> + *
> + * Unless required by applicable law or agreed to in writing, software
> + * distributed under the License is distributed on an "AS IS" BASIS,
> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
> + * implied.
> + * See the License for the specific language governing permissions and
> + * limitations under the License.
> + */
> +
> +/****************************************************************************
> + * THIS DRIVER WAS PORTED AND ADAPTED FROM:
> + * https://github.com/open-power/hostboot/blob/master-p8/src/usr/i2c/tpmdd.C
> + ****************************************************************************/
> +
> +#include <string.h>
> +#include <timebase.h>
> +#include <timer.h>
> +#include <i2c.h>
> +#include <skiboot.h>
> +#include <opal-api.h>
> +#include <device.h>
> +
> +#include "../status_codes.h"
> +#include "../tpm.h"
> +#include "tpm_i2c_nuvoton.h"
> +
> +//#define DBG(fmt, ...) prlog(PR_DEBUG, fmt, ##__VA_ARGS__)
> +#define DBG(fmt, ...)
> +
> +#define DRIVER_NAME "i2c_tpm_nuvoton"
> +
> +/**
> + * Timings between various states or transitions within
> + * the interface protocol. See PTP spec for details.

PTP?

> + */
> +#define TCG_PTP_TIMEOUT_A	750
> +#define TCG_PTP_TIMEOUT_B	2000
> +#define TCG_PTP_TIMEOUT_C	200
> +#define TCG_PTP_TIMEOUT_D	30
> +
> +/* I2C interface offsets */
> +#define NUVOTON_TPM_STS			0x00
> +#define NUVOTON_TPM_BURST_COUNT		0x01
> +#define NUVOTON_TPM_DATA_FIFO_W		0x20
> +#define NUVOTON_TPM_DATA_FIFO_R		0x40
> +#define NUVOTON_TPM_VID_DID_RID		0x60
> +
> +/* Bit masks for the TPM STATUS register */
> +#define TCG_PTP_STS_VALID		0x80
> +#define TCG_PTP_STS_COMMAND_READY	0x40
> +#define TCG_PTP_STS_GO			0x20
> +#define TCG_PTP_STS_DATA_AVAIL		0x10
> +#define TCG_PTP_STS_EXPECT		0x08
> +#define TCG_PTP_STS_RESPONSE_RETRY	0x02
> +#define TCG_PTP_STS_ERR_VAL		0x07
> +
> +/* from hw/p8-i2c.c */
> +#define I2C_TIMEOUT_POLL_MS	4000  /* 4s/byte timeout */
> +
> +/* TPM Driver values */
> +#define MAX_STSVALID_POLLS 	5   /* Max poll of 50ms (5*10ms) */
> +#define TPM_TIMEOUT_INTERVAL	10
> +#define TPM_MAX_NACK_RETRIES	2
> +
> +static struct tpm_dev *tpm_device = NULL;
> +static const char* compat = "nuvoton,npct650";
> +
> +static void i2c_request_complete(int rc, struct i2c_request *req)
> +{
> +	*(int*)req->user_data = rc;
> +}
> +
> +/**
> + * tpm_i2c_request_send - send request to i2c bus
> + * @read_write: SMBUS_READ or SMBUS_WRITE
> + * @offset: any of the I2C interface offset defined
> + * @offset_bytes: offset size in bytes
> + * @buf: data to be read or written
> + * @buflen: buf length
> + *
> + * This interacts with skiboot i2c API to send an I2C request to the tpm
> + * device
> + *
> + * Returns: Zero on success otherwise a negative error code
> + */
> +static int tpm_i2c_request_send(int read_write, uint32_t offset,
> +			    uint32_t offset_bytes, void* buf, size_t buflen)
> +{
> +	int rc, waited, retries, timeout;
> +	struct i2c_request *req;
> +	struct i2c_bus *bus;
> +
> +	bus = i2c_find_bus_by_id(tpm_device->bus_id);
> +
> +	if (!bus) {
> +		/**
> +		 * @fwts-label TPMI2CInvalidBusID
> +		 * @fwts-advice tpm_i2c_request_send was passed an invalid bus
> +		 * ID. This indicates a tb_init() bug.
> +		 */
> +		prlog(PR_ERR, "TPM: Invalid bus_id=%x\n", tpm_device->bus_id);
> +		rc = STB_ARG_ERROR;
> +		goto out;
> +	}
> +
> +	req = i2c_alloc_req(bus);
> +
> +	if (!req) {
> +		/**
> +		 * @fwts-label TPMI2CAllocationFailed
> +		 * @fwts-advice OPAL failed to allocate memory for an
> +		 * i2c_request. This points to an OPAL bug as OPAL run out of
> +		 * memory and this should never happen.
> +		 */
> +		prlog(PR_ERR, "TPM: i2c_alloc_req failed\n");
> +		rc = STB_DRIVER_ERROR;
> +		goto out;
> +	}
> +
> +	req->dev_addr   = tpm_device->xscom_base;
> +	req->op         = read_write;
> +	req->offset     = offset;
> +	req->offset_bytes = offset_bytes;
> +	req->rw_buf     = (void*) buf;
> +	req->rw_len     = buflen;
> +	req->completion = i2c_request_complete;
> +	req->user_data = &rc;
> +
> +	timeout = (buflen + offset_bytes + 2) * I2C_TIMEOUT_POLL_MS;

4 seconds per byte...

> +
> +	for (retries = 0; retries <= TPM_MAX_NACK_RETRIES; retries++)

multiplied by two, means this function could take a *long time...

> +	{
> +		rc = 1;
> +		waited = 0;
> +		i2c_queue_req(req);
> +
> +		do {
> +			time_wait_ms(5);
> +			waited += 5;
> +		} while (waited < timeout && rc == 1);
> +
> +		if (rc == OPAL_I2C_NACK_RCVD)
> +			continue;
> +		else
> +			/* error or success */
> +			break;
> +	}



> +
> +	DBG("%s tpm req op=%x offset=%x buf=%016llx buflen=%d delay=%d/%d,"
> +	    "rc=%d\n",
> +	    (rc) ? "!!!!" : "----", req->op, req->offset,
> +	    *(uint64_t*) buf, req->rw_len, waited, timeout, rc);
> +
> +	i2c_free_req(req);
> +
> +	/* error */
> +	if (rc)
> +		rc = STB_DRIVER_ERROR;
> +
> +out:
> +	return rc;
> +}
> +
> +static int tpm_status_write_byte(uint8_t byte)
> +{
> +	uint8_t value = byte;
> +	return tpm_i2c_request_send(SMBUS_WRITE, NUVOTON_TPM_STS, 1, &value,
> +				sizeof(value));
> +}
> +
> +static int tpm_read_sts_reg_valid(uint8_t* value)
> +{
> +	int polls, rc;
> +
> +	for(polls=0; polls<=MAX_STSVALID_POLLS; polls++)
> +	{
> +		rc = tpm_i2c_request_send(SMBUS_READ, NUVOTON_TPM_STS, 1, value,
> +				      sizeof(uint8_t));

5 * 8 seconds = 40 seconds this function could take.

> +
> +		if (rc < 0)
> +			return rc;
> +
> +		if (rc == 0  && ((*value &
> +				  TCG_PTP_STS_VALID) == TCG_PTP_STS_VALID))
> +			return STB_SUCCESS;
> +
> +		// wait tpm sts reg be settled
> +		time_wait_ms(5);
> +	}
> +
> +	value = 0;
> +	/**
> +	 * @fwts-label TPMValidBitTimeout
> +	 * @fwts-advice The valid bit of the tpm status register is taking
> +	 * longer to be settled. The wait time need to be increased or the TPM
> +	 * device is not functional.
> +	 */
> +	prlog(PR_ERR, "TPM: valid bit not settled. Timeout.\n");
> +
> +	return STB_TPM_TIMEOUT;
> +}
> +
> +static bool tpm_is_command_ready(int* rc)
> +{
> +	uint8_t value = 0;
> +
> +	*rc = tpm_i2c_request_send(SMBUS_READ, NUVOTON_TPM_STS, 1, &value,
> +			       sizeof(value));
> +
> +	if (*rc == 0  &&
> +	   ((value & TCG_PTP_STS_COMMAND_READY) == TCG_PTP_STS_COMMAND_READY)){
> +		DBG("---- TPM is command ready\n");
> +		return true;
> +	}
> +
> +	return false;
> +}
> +
> +static int tpm_poll_for_command_ready(void)
> +{
> +	int rc, polls, delay;
> +
> +	/**
> +	 * The first write to command ready may just abort an
> +	 * outstanding command, so we poll twice
> +	 */
> +	for (polls=0; polls<2; polls++)
> +	{
> +		rc = tpm_status_write_byte(TCG_PTP_STS_COMMAND_READY);
> +		if (rc < 0) {
> +			return rc;
> +		}
> +
> +		for (delay = 0;
> +		     delay < TCG_PTP_TIMEOUT_B;
> +		     delay += TPM_TIMEOUT_INTERVAL)
> +		{
> +			if (tpm_is_command_ready(&rc))
> +				return rc;
> +
> +			time_wait_ms(TPM_TIMEOUT_INTERVAL);
> +		}
> +		DBG("--- Command ready polling, delay %d/%d\n",
> +		    delay, TCG_PTP_TIMEOUT_B);

This could take... 16*2 = 32 seconds.

> +	}
> +
> +	/**
> +	 * @fwts-label TPMCommandReadyBitTimeout
> +	 * @fwts-advice The command ready bit of the tpm status register is
> +	 * taking longer to be settled. The wait time need to be increased or
> +	 * the TPM device is not functional.
> +	 */
> +	prlog(PR_ERR, "TPM: command ready polling timeout\n");
> +	return STB_TPM_TIMEOUT;
> +}
> +
> +static bool tpm_is_expecting(int* rc)
> +{
> +	uint8_t value = 0;
> +
> +	*rc = tpm_read_sts_reg_valid(&value);
> +
> +	if (*rc == 0  && (( value &
> +			    TCG_PTP_STS_EXPECT) == TCG_PTP_STS_EXPECT))
> +		return true;
> +
> +	return false;
> +}
> +
> +static bool tpm_is_data_avail(int* rc)
> +{
> +	uint8_t value = 0;
> +
> +	*rc = tpm_read_sts_reg_valid(&value);
> +
> +	if (*rc == 0 && (( value &
> +			   TCG_PTP_STS_DATA_AVAIL) == TCG_PTP_STS_DATA_AVAIL))
> +		return true;
> +
> +	return false;
> +}
> +
> +static int tpm_poll_for_data_avail(void)
> +{
> +	int delay, rc;
> +
> +	/* Operation TIMEOUT_A defined by TCG spec for data available */
> +	for (delay = 0;
> +	     delay < TCG_PTP_TIMEOUT_A;
> +	     delay += TPM_TIMEOUT_INTERVAL)
> +	{
> +		if (tpm_is_data_avail(&rc)) {
> +			DBG("---- read FIFO. Data available. delay=%d/%d\n",
> +			    delay, TCG_PTP_TIMEOUT_A);
> +			return rc;
> +		}
> +
> +		time_wait_ms(TPM_TIMEOUT_INTERVAL);
> +	}
> +	/**
> +	 * @fwts-label TPMDataAvailBitTimeout
> +	 * @fwts-advice The data avail bit of the tpm status register is taking
> +	 * longer to be settled. The wait time need to be increased or the TPM
> +	 * device is not functional.
> +	 */
> +	prlog(PR_ERR, "TPM: read FIFO. Polling timeout, delay=%d/%d\n",
> +	      delay, TCG_PTP_TIMEOUT_A);
> +
> +	return STB_TPM_TIMEOUT;
> +}
> +
> +static int tpm_read_burst_count(uint8_t* burst_count)
> +{
> +	int rc = 0;
> +
> +	/* In i2C, burstCount is 1 byte */
> +	rc = tpm_i2c_request_send(SMBUS_READ, NUVOTON_TPM_BURST_COUNT, 1,
> +			      burst_count, sizeof(uint8_t));
> +
> +	DBG("---- burst_count=%d rc=%d\n", *burst_count, rc);
> +
> +	if (rc < 0)
> +		*burst_count = 0;
> +
> +	return rc;
> +}
> +
> +static int tpm_write_fifo(uint8_t* buf, size_t buflen)
> +{
> +	uint8_t burst_count = 0;
> +	int delay = 0;
> +	int rc;
> +
> +	size_t curByte = 0;
> +	uint8_t* bytePtr = buf;
> +	uint8_t* curBytePtr = NULL;
> +
> +	/**
> +	 * We will transfer the command except for the last byte
> +	 * that will be transfered separately to allow for
> +	 * overflow checking
> +	 */
> +	size_t length = buflen - 1;
> +	size_t tx_len = 0;
> +
> +	do
> +	{
> +		rc = tpm_read_burst_count(&burst_count);
> +
> +		if (rc < 0)
> +			return rc;
> +		else if (burst_count == 0)
> +		{
> +			/* Need to delay to allow the TPM time */
> +			time_wait_ms(TPM_TIMEOUT_INTERVAL);
> +			delay += TPM_TIMEOUT_INTERVAL;
> +			continue;
> +		}
> +
> +		/* Send in some data */
> +		curBytePtr = &(bytePtr[curByte]);
> +		tx_len = (curByte + burst_count > length ?
> +				(length - curByte) :
> +				burst_count);
> +
> +		rc = tpm_i2c_request_send(SMBUS_WRITE, NUVOTON_TPM_DATA_FIFO_W,
> +					  1, curBytePtr, tx_len);
> +
> +		curByte += tx_len;
> +		DBG("%s write FIFO sent %zd bytes."
> +		    " burstcount polling delay=%d/%d, rc=%d\n",
> +		    (rc) ? "!!!!" : "----", curByte, delay,
> +		    TCG_PTP_TIMEOUT_D, rc);
> +		delay = 0;
> +
> +		if (rc < 0)
> +			return rc;
> +
> +		if (!tpm_is_expecting(&rc)) {
> +			/**
> +			 * @fwts-label TPMWriteFifoOverflow1
> +			 * @fwts-advice We overflow to write to the TPM FIFO,
> +			 * the TPM is not expecting more data. This indicates a bug
> +			 * in the TPM device driver.
> +			 */
> +			prlog(PR_ERR, "TPM: write FIFO overflow1\n");
> +			return STB_TPM_OVERFLOW;
> +		}
> +
> +		/* Everything but the last byte sent? */
> +		if (curByte >= length)
> +			break;
> +
> +	/* Operation TIMEOUT_D defined by TCG spec for FIFO availability */
> +	} while (delay < TCG_PTP_TIMEOUT_D);
> +
> +	if (delay < TCG_PTP_TIMEOUT_D)
> +	{
> +		delay = 0;
> +
> +		/* Send the final byte */
> +		do
> +		{
> +			rc = tpm_read_burst_count(&burst_count);
> +
> +			if (rc < 0)
> +				return rc;
> +			else if (burst_count == 0)
> +			{
> +				/* Need to delay to allow the TPM time */
> +				time_wait_ms(TPM_TIMEOUT_INTERVAL);
> +				delay += TPM_TIMEOUT_INTERVAL;
> +				continue;
> +			}
> +
> +			/* Send in some data */
> +			curBytePtr = &(bytePtr[curByte]);
> +			rc = tpm_i2c_request_send(SMBUS_WRITE,
> +					      NUVOTON_TPM_DATA_FIFO_W, 1,
> +					      curBytePtr, 1);
> +
> +			DBG("%s write FIFO sent last byte, delay=%d/%d,"
> +			    " rc=%d\n",
> +			    (rc) ? "!!!!" : "----", delay,
> +			    TCG_PTP_TIMEOUT_D, rc);
> +
> +			/* done */
> +			break;
> +
> +			/**
> +			 * Operation TIMEOUT_D defined by TCG spec for
> +			 * FIFO availability
> +			 */
> +		} while (delay < TCG_PTP_TIMEOUT_D);
> +
> +	}
> +
> +	if (delay >= TCG_PTP_TIMEOUT_D) {
> +		/**
> +		 * @fwts-label TPMWriteBurstcountBitTimeout
> +		 * @fwts-advice The burstcount bit of the tpm status register is
> +		 * taking longer to be settled. The wait time need to be increased or
> +		 * the TPM device is not functional.
> +		 */
> +		prlog(PR_ERR, "TPM: write FIFO, burstcount polling timeout."
> +		      " delay=%d/%d\n", delay, TCG_PTP_TIMEOUT_D);
> +		return STB_TPM_TIMEOUT;
> +	}
> +
> +	if (rc == 0)
> +	{
> +		if (tpm_is_expecting(&rc)) {
> +			 /**
> +			 * @fwts-label TPMWriteFifoOverflow2
> +			 * @fwts-advice We overflow to write to the TPM FIFO.
> +			 * It is expecting more data even though we think we
> +			 * are done. This indicates a bug in the TPM device
> +			 * driver.
> +			 */
> +			prlog(PR_ERR, "TPM: write FIFO overflow2\n");
> +			return STB_TPM_OVERFLOW;
> +		}
> +	}
> +
> +	return rc;
> +}

All of the delays add up to a *lot* here.

> +
> +static int tpm_read_fifo(uint8_t* buf, size_t* buflen)
> +{
> +	int rc;
> +	uint8_t burst_count;
> +
> +	int delay = 0;
> +	size_t curByte = 0;
> +	uint8_t* bytePtr = (uint8_t*)buf;
> +	uint8_t* curBytePtr = NULL;
> +
> +	/* Verify whether the TPM has data waiting for us */
> +	rc = tpm_poll_for_data_avail();
> +
> +	if (rc == 0)
> +	{
> +		do
> +		{
> +			rc = tpm_read_burst_count(&burst_count);
> +
> +			if (rc < 0)
> +				break;
> +			else if (burst_count == 0) {
> +				/* Need to delay to allow the TPM time */
> +				time_wait_ms(TPM_TIMEOUT_INTERVAL);
> +				delay += TPM_TIMEOUT_INTERVAL;
> +				continue;
> +			}
> +
> +			/* Check for a buffer overflow */
> +			if (curByte + burst_count > *buflen)
> +			{
> +				 /**
> +				 * @fwts-label TPMReadFifoOverflow1
> +				 * @fwts-advice We overflow to read the TPM FIFO. It is
> +				 * expecting more data even though we think we are done.
> +				 * This indicates a bug in the TPM device driver.
> +				 */
> +				prlog(PR_ERR, "TPM: read FIFO overflow1. delay %d/%d\n",
> +					  delay, TCG_PTP_TIMEOUT_D);
> +				rc = STB_TPM_OVERFLOW;
> +			}
> +
> +			/* Read some data */
> +			curBytePtr = &(bytePtr[curByte]);
> +			rc = tpm_i2c_request_send(SMBUS_READ,
> +					      NUVOTON_TPM_DATA_FIFO_R, 1,
> +					      curBytePtr, burst_count);
> +
> +			curByte += burst_count;
> +			DBG("%s read FIFO. received %zd bytes. burstcount"
> +			    " polling delay=%d/%d, rc=%d\n",
> +			    (rc) ? "!!!!" : "----", curByte, delay,
> +			    TCG_PTP_TIMEOUT_D, rc);
> +			delay = 0;
> +
> +			if (rc < 0)
> +				break;
> +
> +			if (!tpm_is_data_avail(&rc))
> +				break;
> +
> +		/* Operation TIMEOUT_D defined by TCG spec for
> +		 * FIFO availability */
> +		} while (delay < TCG_PTP_TIMEOUT_D);
> +	}
> +
> +	if (rc == 0 && delay >= TCG_PTP_TIMEOUT_D) {
> +		/**
> +		 * @fwts-label TPMReadBurstcountBitTimeout
> +		 * @fwts-advice The burstcount bit of the tpm status register is
> +		 * taking longer to be settled. The wait time need to be increased or
> +		 * the TPM device is not functional.
> +		 */
> +		prlog(PR_ERR, "TPM: read FIFO, burstcount polling timeout."
> +			  " delay=%d/%d\n",
> +			  delay, TCG_PTP_TIMEOUT_D);
> +		return STB_TPM_TIMEOUT;
> +	}
> +
> +	if (rc == 0) {
> +		/* We read it properly tell the caller the result length */
> +		*buflen = curByte;
> +	}
> +	else {
> +		*buflen = 0;
> +	}
> +
> +	return rc;
> +}
> +
> +static int tpm_transmit(struct tpm_dev *dev, uint8_t* buf, size_t cmdlen,
> +			size_t* buflen)
> +{
> +	int rc = STB_SUCCESS;
> +
> +	if (!dev) {
> +		/**
> +		 * @fwts-label TPMDeviceNotInitialized
> +		 * @fwts-advice TPM device is not initialized. This indicates a
> +		 * bug in the tpm_transmit() caller
> +		 */
> +		prlog(PR_ERR, "TPM: tpm device not initialized\n");
> +		return STB_ARG_ERROR;
> +	}
> +
> +	tpm_device = dev;
> +
> +	DBG("**** %s: dev %#x/%#x buf %016llx cmdlen %zu"
> +	    " buflen %zu ****\n",
> +	    __func__, dev->bus_id, dev->xscom_base, *(uint64_t*) buf,
> +	    cmdlen, *buflen);
> +
> +	DBG("step 1/5: check command ready\n");
> +	if (!tpm_is_command_ready(&rc)) {
> +		if (rc < 0)
> +			goto out;
> +		rc = tpm_poll_for_command_ready();
> +		if (rc < 0)
> +			goto out;
> +	}
> +
> +	DBG("step 2/5: write FIFO\n");
> +	rc = tpm_write_fifo(buf, cmdlen);
> +	if (rc < 0)
> +		goto out;
> +
> +	DBG("step 3/5: write tpmgo\n");
> +	rc = tpm_status_write_byte(TCG_PTP_STS_GO);
> +	if (rc < 0)
> +		goto out;
> +
> +	DBG("step 4/5: read FIFO\n");
> +	rc = tpm_read_fifo(buf, buflen);
> +	if (rc < 0)
> +		goto out;
> +
> +	DBG("step 5/5: write command ready\n");
> +	rc = tpm_status_write_byte(TCG_PTP_STS_COMMAND_READY);
> +
> +out:
> +	DBG("**** tpm_transmit %s, rc=%d ****\n",
> +	    (rc) ? "ERROR" : "SUCCESS", rc);
> +	return rc;
> +}

This could take a very, very, very long time to complete if we hit a lot
of the timeouts (minutes), and that's concerning considering all the
places we hit this code during boot (for each resource loaded, right?
which means there's a few of them).

So.. what can we do to avoid making boot take longer?

For a start, have you measured any impact in boot time with this code
running?

It would be better if everything doing i2c was event based rather than
polling in a loop all over the place, but perhaps a better solution is
to offload this work to another CPU? At least then the sleeps wouldn't
impact the main thread in skiboot in the normal case of having vastly
more CPU threads than work to do.


-- 
Stewart Smith
OPAL Architect, IBM.



More information about the Skiboot mailing list