[Skiboot] [PATCH v2 14/31] libstb/drivers/tpm_i2c_nuvoton.c: write FIFO

Claudio Carvalho cclaudio at linux.vnet.ibm.com
Wed Sep 28 18:01:13 AEST 2016


This adds the 2/5 step performed by the TPM I2C Nuvoton driver to
transmit a command to the TPM device. In this step the driver
writes a given command to master I2C FIFO.

Signed-off-by: Claudio Carvalho <cclaudio at linux.vnet.ibm.com>
---
 libstb/drivers/tpm_i2c_nuvoton.c | 178 +++++++++++++++++++++++++++++++++++++++
 libstb/status_codes.h            |   1 +
 2 files changed, 179 insertions(+)

diff --git a/libstb/drivers/tpm_i2c_nuvoton.c b/libstb/drivers/tpm_i2c_nuvoton.c
index ead0f15..e418d7b 100644
--- a/libstb/drivers/tpm_i2c_nuvoton.c
+++ b/libstb/drivers/tpm_i2c_nuvoton.c
@@ -35,14 +35,20 @@
  * 00.43.
  */
 #define TCG_PTP_TIMEOUT_B	2000
+#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
 
 /* Bit masks for the TPM STATUS register */
+#define TCG_PTP_STS_VALID		0x80
 #define TCG_PTP_STS_COMMAND_READY	0x40
+#define TCG_PTP_STS_EXPECT		0x08
 
 /* TPM Driver values */
+#define MAX_STSVALID_POLLS 	5   /* Max poll of 50ms (5*10ms) */
 #define TPM_TIMEOUT_INTERVAL	10
 
 static struct tpm_dev *tpm_device = NULL;
@@ -55,6 +61,33 @@ static int tpm_status_write_byte(uint8_t byte)
 				    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(tpm_device->bus_id,
+					  tpm_device->xscom_base, SMBUS_READ,
+					  NUVOTON_TPM_STS, 1, value, sizeof(uint8_t));
+		if (rc < 0)
+			return rc;
+		if (rc == 0  &&
+		    ((*value & TCG_PTP_STS_VALID) == TCG_PTP_STS_VALID))
+			return 0;
+		/* Wait TPM STS register 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. Either the wait time needs 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;
@@ -100,6 +133,145 @@ static int tpm_poll_for_command_ready(void)
 	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 int tpm_read_burst_count(uint8_t* burst_count)
+{
+	int rc = 0;
+	/* In i2C, burstCount is 1 byte */
+	rc = tpm_i2c_request_send(tpm_device->bus_id, tpm_device->xscom_base,
+				  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(tpm_device->bus_id,
+					  tpm_device->xscom_base,
+					  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 The write to the TPM FIFO overflowed,
+			 * 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;
+	} while (delay < TCG_PTP_TIMEOUT_D);
+
+	if (delay < TCG_PTP_TIMEOUT_D) {
+		/*
+		 *  Send the final byte
+		 */
+		delay = 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;
+			}
+			curBytePtr = &(bytePtr[curByte]);
+			rc = tpm_i2c_request_send(tpm_device->bus_id,
+						  tpm_device->xscom_base,
+						  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);
+			break;
+		} 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. Either 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 The write to the TPM FIFO overflowed.
+			 * 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;
+}
+
 static int tpm_transmit(struct tpm_dev *dev, uint8_t* buf, size_t cmdlen,
 			size_t* buflen)
 {
@@ -127,6 +299,12 @@ static int tpm_transmit(struct tpm_dev *dev, uint8_t* buf, size_t cmdlen,
 		if (rc < 0)
 			goto out;
 	}
+
+	DBG("step 2/5: write FIFO\n");
+	rc = tpm_write_fifo(buf, cmdlen);
+	if (rc < 0)
+		goto out;
+
 out:
 	DBG("**** tpm_transmit %s, rc=%d ****\n",
 	    (rc) ? "ERROR" : "SUCCESS", rc);
diff --git a/libstb/status_codes.h b/libstb/status_codes.h
index 88443b9..1637e9f 100644
--- a/libstb/status_codes.h
+++ b/libstb/status_codes.h
@@ -26,6 +26,7 @@
 #define STB_VERIFY_FAILED  		-100
 
 /* TPM */
+#define STB_TPM_OVERFLOW	-300
 #define STB_TPM_TIMEOUT	-301
 
 #endif /* __STB_STATUS_CODES_H */
-- 
1.9.1



More information about the Skiboot mailing list