[U-Boot] [PATCH v2 1/2] Introduce generic TPM support in u-boot

Marek Vasut marek.vasut at gmail.com
Sat Oct 15 20:08:37 CEST 2011


On Saturday, October 15, 2011 05:38:50 AM Vadim Bendebury wrote:
> TPM (Trusted Platform Module) is an integrated circuit and
> software platform that provides computer manufacturers with the
> core components of a subsystem used to assure authenticity,
> integrity and confidentiality.

[...]

Quick points:
* The license
* Use debug() when fitting

> diff --git a/drivers/tpm/generic_lpc_tpm.c b/drivers/tpm/generic_lpc_tpm.c
> new file mode 100644
> index 0000000..8319286
> --- /dev/null
> +++ b/drivers/tpm/generic_lpc_tpm.c

[...]

> +
> +struct lpc_tpm {
> +	struct tpm_locality locality[TPM_TOTAL_LOCALITIES];
> +};

Do you need such envelope ?

> +
> +static struct lpc_tpm *lpc_tpm_dev =
> +	(struct lpc_tpm *)CONFIG_TPM_TIS_BASE_ADDRESS;
> +
> +/* Some registers' bit field definitions */
> +#define TIS_STS_VALID                  (1 << 7) /* 0x80 */
> +#define TIS_STS_COMMAND_READY          (1 << 6) /* 0x40 */
> +#define TIS_STS_TPM_GO                 (1 << 5) /* 0x20 */
> +#define TIS_STS_DATA_AVAILABLE         (1 << 4) /* 0x10 */
> +#define TIS_STS_EXPECT                 (1 << 3) /* 0x08 */
> +#define TIS_STS_RESPONSE_RETRY         (1 << 1) /* 0x02 */
> +
> +#define TIS_ACCESS_TPM_REG_VALID_STS   (1 << 7) /* 0x80 */
> +#define TIS_ACCESS_ACTIVE_LOCALITY     (1 << 5) /* 0x20 */
> +#define TIS_ACCESS_BEEN_SEIZED         (1 << 4) /* 0x10 */
> +#define TIS_ACCESS_SEIZE               (1 << 3) /* 0x08 */
> +#define TIS_ACCESS_PENDING_REQUEST     (1 << 2) /* 0x04 */
> +#define TIS_ACCESS_REQUEST_USE         (1 << 1) /* 0x02 */
> +#define TIS_ACCESS_TPM_ESTABLISHMENT   (1 << 0) /* 0x01 */
> +
> +#define TIS_STS_BURST_COUNT_MASK       (0xffff)
> +#define TIS_STS_BURST_COUNT_SHIFT      (8)
> +
> +/*
> + * Error value returned if a tpm register does not enter the expected
> state + * after continuous polling. No actual TPM register reading ever
> returns ~0, + * so this value is a safe error indication to be mixed with
> possible status + * register values.
> + */
> +#define TPM_TIMEOUT_ERR			(~0)
> +
> +/* Error value returned on various TPM driver errors */

Dot missing at the end.

> +#define TPM_DRIVER_ERR		(-1)
> +
> + /* 1 second is plenty for anything TPM does.*/
> +#define MAX_DELAY_US	(1000 * 1000)
> +
> +/* Retrieve burst count value out of the status register contents. */
> +#define BURST_COUNT(status) ((u16)(((status) >> TIS_STS_BURST_COUNT_SHIFT)
> & \ +				   TIS_STS_BURST_COUNT_MASK))

Do you need the cast ?

> +
> +/*
> + * Structures defined below allow creating descriptions of TPM
> vendor/device + * ID information for run time discovery. The only device
> the system knows + * about at this time is Infineon slb9635
> + */
> +struct device_name {
> +	u16 dev_id;
> +	const char * const dev_name;
> +};
> +
> +struct vendor_name {
> +	u16 vendor_id;
> +	const char *vendor_name;
> +	const struct device_name *dev_names;
> +};
> +
> +static const struct device_name infineon_devices[] = {
> +	{0xb, "SLB9635 TT 1.2"},
> +	{0}
> +};
> +
> +static const struct vendor_name vendor_names[] = {
> +	{0x15d1, "Infineon", infineon_devices},
> +};
> +
> +/*
> + * Cached vendor/device ID pair to indicate that the device has been
> already + * discovered
> + */
> +static u32 vendor_dev_id;
> +
> +/* TPM access going through macros to make tracing easier. */
> +#define tpm_read(ptr) ({ \
> +	u32  __ret; \
> +	__ret = (sizeof(*ptr) == 1) ? readb(ptr) : readl(ptr); \
> +	 debug(PREFIX "Read reg 0x%x returns 0x%x\n", \
> +	       (u32)ptr - (u32)lpc_tpm_dev, __ret); \
> +					 __ret; })
> +

Make this inline function

> +#define tpm_write(value, ptr) ({		   \
> +	u32 __v = value;	\
> +	debug(PREFIX "Write reg 0x%x with 0x%x\n", \
> +	       (u32)ptr - (u32)lpc_tpm_dev, __v); \
> +	if (sizeof(*ptr) == 1) \
> +		writeb(__v, ptr); \
> +	else \
> +		writel(__v, ptr); })
> +

DTTO

[...]

> +static u32 tis_senddata(const u8 * const data, u32 len)
> +{
> +	u32 offset = 0;
> +	u16 burst = 0;
> +	u32 max_cycles = 0;
> +	u8 locality = 0;
> +	u32 value;
> +
> +	value = tis_wait_reg(&lpc_tpm_dev->locality[locality].tpm_status,
> +			     TIS_STS_COMMAND_READY, TIS_STS_COMMAND_READY);
> +	if (value == TPM_TIMEOUT_ERR) {
> +		printf("%s:%d - failed to get 'command_ready' status\n",
> +		       __FILE__, __LINE__);
> +		return TPM_DRIVER_ERR;
> +	}
> +	burst = BURST_COUNT(value);
> +
> +	while (1) {

No endless loops please.

> +		unsigned count;
> +
> +		/* Wait till the device is ready to accept more data. */
> +		while (!burst) {
> +			if (max_cycles++ == MAX_DELAY_US) {
> +				printf("%s:%d failed to feed %d bytes of %d\n",
> +				       __FILE__, __LINE__, len - offset, len);
> +				return TPM_DRIVER_ERR;
> +			}
> +			udelay(1);
> +			burst = BURST_COUNT(tpm_read(&lpc_tpm_dev->locality
> +						     [locality].tpm_status));
> +		}
> +
> +		max_cycles = 0;
> +
> +		/*
> +		 * Calculate number of bytes the TPM is ready to accept in one
> +		 * shot.
> +		 *
> +		 * We want to send the last byte outside of the loop (hence
> +		 * the -1 below) to make sure that the 'expected' status bit
> +		 * changes to zero exactly after the last byte is fed into the
> +		 * FIFO.
> +		 */
> +		count = min(burst, len - offset - 1);
> +		while (count--)
> +			tpm_write(data[offset++],
> +				  &lpc_tpm_dev->locality[locality].data);
> +
> +		value = tis_wait_reg(&lpc_tpm_dev->locality
> +				     [locality].tpm_status,
> +				     TIS_STS_VALID, TIS_STS_VALID);
> +
> +		if ((value == TPM_TIMEOUT_ERR) || !(value & TIS_STS_EXPECT)) {
> +			printf("%s:%d TPM command feed overflow\n",
> +			       __FILE__, __LINE__);
> +			return TPM_DRIVER_ERR;
> +		}
> +
> +		burst = BURST_COUNT(value);
> +		if ((offset == (len - 1)) && burst)
> +			/*
> +			 * We need to be able to send the last byte to the
> +			 * device, so burst size must be nonzero before we
> +			 * break out.
> +			 */
> +			break;
> +	}
> +
> +	/* Send the last byte. */
> +	tpm_write(data[offset++], &lpc_tpm_dev->locality[locality].data);
> +	/*
> +	 * Verify that TPM does not expect any more data as part of this
> +	 * command.
> +	 */
> +	value = tis_wait_reg(&lpc_tpm_dev->locality[locality].tpm_status,
> +			     TIS_STS_VALID, TIS_STS_VALID);
> +	if ((value == TPM_TIMEOUT_ERR) || (value & TIS_STS_EXPECT)) {
> +		printf("%s:%d unexpected TPM status 0x%x\n",
> +		       __FILE__, __LINE__, value);
> +		return TPM_DRIVER_ERR;
> +	}
> +
> +	/* OK, sitting pretty, let's start the command execution. */
> +	tpm_write(TIS_STS_TPM_GO, &lpc_tpm_dev->locality[locality].tpm_status);
> +	return 0;
> +}
> +
> +/*
> + * tis_readresponse()
> + *
> + * read the TPM device response after a command was issued.
> + *
> + * @buffer - address where to read the response, byte by byte.
> + * @len - pointer to the size of buffer
> + *
> + * On success stores the number of received bytes to len and returns 0. On
> + * errors (misformatted TPM data or synchronization problems) returns
> + * TPM_DRIVER_ERR.
> + */
> +static u32 tis_readresponse(u8 *buffer, u32 *len)
> +{
> +	u16 burst_count;
> +	u32 status;
> +	u32 offset = 0;
> +	u8 locality = 0;
> +	const u32 has_data = TIS_STS_DATA_AVAILABLE | TIS_STS_VALID;
> +	u32 expected_count = *len;
> +	int max_cycles = 0;
> +
> +	/* Wait for the TPM to process the command */
> +	status = tis_wait_reg(&lpc_tpm_dev->locality[locality].tpm_status,
> +			      has_data, has_data);
> +	if (status == TPM_TIMEOUT_ERR) {

Just make it return non-zero for timeout and check for non-zero. And unify the 
variable names please.

> +		printf("%s:%d failed processing command\n",
> +		       __FILE__, __LINE__);
> +		return TPM_DRIVER_ERR;
> +	}
> +
> +	do {
> +		while ((burst_count = BURST_COUNT(status)) == 0) {
> +			if (max_cycles++ == MAX_DELAY_US) {
> +				printf("%s:%d TPM stuck on read\n",
> +				       __FILE__, __LINE__);
> +				return TPM_DRIVER_ERR;
> +			}
> +			udelay(1);
> +			status = tpm_read(&lpc_tpm_dev->locality
> +					  [locality].tpm_status);
> +		}
> +
> +		max_cycles = 0;
> +
> +		while (burst_count-- && (offset < expected_count)) {
> +			buffer[offset++] = (u8) tpm_read(&lpc_tpm_dev->locality
> +							 [locality].data);
> +
> +			if (offset == 6) {
> +				/*
> +				 * We got the first six bytes of the reply,
> +				 * let's figure out how many bytes to expect
> +				 * total - it is stored as a 4 byte number in
> +				 * network order, starting with offset 2 into
> +				 * the body of the reply.
> +				 */
> +				u32 real_length;
> +				memcpy(&real_length,
> +				       buffer + 2,
> +				       sizeof(real_length));
> +				expected_count = be32_to_cpu(real_length);
> +
> +				if ((expected_count < offset) ||
> +				    (expected_count > *len)) {
> +					printf("%s:%d bad response size %d\n",
> +					       __FILE__, __LINE__,
> +					       expected_count);
> +					return TPM_DRIVER_ERR;
> +				}
> +			}
> +		}
> +
> +		/* Wait for the next portion */
> +		status = tis_wait_reg(&lpc_tpm_dev->locality
> +				      [locality].tpm_status,
> +				      TIS_STS_VALID, TIS_STS_VALID);
> +		if (status == TPM_TIMEOUT_ERR) {
> +			printf("%s:%d failed to read response\n",
> +			       __FILE__, __LINE__);
> +			return TPM_DRIVER_ERR;
> +		}
> +
> +		if (offset == expected_count)
> +			break;	/* We got all we need */
> +
> +	} while ((status & has_data) == has_data);

No endless loop please.

> +
> +	/*
> +	 * Make sure we indeed read all there was. The TIS_STS_VALID bit is
> +	 * known to be set.
> +	 */
> +	if (status & TIS_STS_DATA_AVAILABLE) {
> +		printf("%s:%d wrong receive status %x\n",
> +		       __FILE__, __LINE__, status);
> +		return TPM_DRIVER_ERR;
> +	}
> +
> +	/* Tell the TPM that we are done. */
> +	tpm_write(TIS_STS_COMMAND_READY, &lpc_tpm_dev->locality
> +		  [locality].tpm_status);
> +	*len = offset;
> +	return 0;
> +}
> +
> +int tis_init(void)
> +{
> +	return tis_probe();
> +}

Make tis_probe into tis_init and be done with it ?
> +
> +int tis_open(void)
> +{
> +	u8 locality = 0; /* we use locality zero for everything */
> +
> +	if (tis_close())
> +		return TPM_DRIVER_ERR;
> +
> +	/* now request access to locality */
> +	tpm_write(TIS_ACCESS_REQUEST_USE,
> +		  &lpc_tpm_dev->locality[locality].access);
> +
> +
> +	/* did we get a lock? */
> +	if (tis_wait_reg(&lpc_tpm_dev->locality[locality].access,
> +			 TIS_ACCESS_ACTIVE_LOCALITY,
> +			 TIS_ACCESS_ACTIVE_LOCALITY) == TPM_TIMEOUT_ERR) {
> +		printf("%s:%d - failed to lock locality %d\n",
> +		       __FILE__, __LINE__, locality);
> +		return TPM_DRIVER_ERR;
> +	}
> +
> +	tpm_write(TIS_STS_COMMAND_READY,
> +		  &lpc_tpm_dev->locality[locality].tpm_status);
> +	return 0;
> +}

[...]

Cheers


More information about the U-Boot mailing list