GK SDK 源码库: XMIPCLinuxV100R005C00SPC030 (kernel/tools/open_source excluded)

This commit is contained in:
lai
2026-09-06 03:52:57 +08:00
commit b1928b41c0
21813 changed files with 4413081 additions and 0 deletions
@@ -0,0 +1,150 @@
#
# TPM subsystem configuration
#
menu "TPM support"
config TPM_V1
bool "TPMv1.x support"
depends on TPM
default y
help
Major TPM versions are not compatible at all, choose either
one or the other. This option enables TPMv1.x drivers/commands.
if TPM_V1
config TPM_TIS_SANDBOX
bool "Enable sandbox TPM driver"
depends on TPM_V1 && SANDBOX
default y
help
This driver emulates a TPMv1.x, providing access to base functions
such as reading and writing TPM private data. This is enough to
support Chrome OS verified boot. Extend functionality is not
implemented.
config TPM_ATMEL_TWI
bool "Enable Atmel TWI TPM device driver"
depends on TPM_V1
help
This driver supports an Atmel TPM device connected on the I2C bus.
The usual tpm operations and the 'tpm' command can be used to talk
to the device using the standard TPM Interface Specification (TIS)
protocol
config TPM_TIS_INFINEON
bool "Enable support for Infineon SLB9635/45 TPMs on I2C"
depends on TPM_V1 && DM_I2C
help
This driver supports Infineon TPM devices connected on the I2C bus.
The usual tpm operations and the 'tpm' command can be used to talk
to the device using the standard TPM Interface Specification (TIS)
protocol
config TPM_TIS_I2C_BURST_LIMITATION
bool "Enable I2C burst length limitation"
depends on TPM_TIS_INFINEON
help
Some broken TPMs have a limitation on the number of bytes they can
receive in one message. Enable this option to allow you to set this
option. The can allow a broken TPM to be used by splitting messages
into separate pieces.
config TPM_TIS_I2C_BURST_LIMITATION_LEN
int "Length"
depends on TPM_TIS_I2C_BURST_LIMITATION
help
Use this to set the burst limitation length
config TPM_TIS_LPC
bool "Enable support for Infineon SLB9635/45 TPMs on LPC"
depends on TPM_V1 && X86
help
This driver supports Infineon TPM devices connected on the LPC bus.
The usual tpm operations and the 'tpm' command can be used to talk
to the device using the standard TPM Interface Specification (TIS)
protocol
config TPM_AUTH_SESSIONS
bool "Enable TPM authentication session support"
depends on TPM_V1
help
Enable support for authorised (AUTH1) commands as specified in the
TCG Main Specification 1.2. OIAP-authorised versions of the commands
TPM_LoadKey2 and TPM_GetPubKey are provided. Both features are
available using the 'tpm' command, too.
config TPM_ST33ZP24_I2C
bool "STMicroelectronics ST33ZP24 I2C TPM"
depends on TPM_V1 && DM_I2C
---help---
This driver supports STMicroelectronics TPM devices connected on the I2C bus.
The usual tpm operations and the 'tpm' command can be used to talk
to the device using the standard TPM Interface Specification (TIS)
protocol
config TPM_ST33ZP24_SPI
bool "STMicroelectronics ST33ZP24 SPI TPM"
depends on TPM_V1 && DM_SPI
---help---
This driver supports STMicroelectronics TPM devices connected on the SPI bus.
The usual tpm operations and the 'tpm' command can be used to talk
to the device using the standard TPM Interface Specification (TIS)
protocol
config TPM_FLUSH_RESOURCES
bool "Enable TPM resource flushing support"
depends on TPM_V1
help
Enable support to flush specific resources (e.g. keys) from the TPM.
The functionality is available via the 'tpm' command as well.
config TPM_LOAD_KEY_BY_SHA1
bool "Enable TPM key loading by SHA1 support"
depends on TPM_V1
help
Enable support to load keys into the TPM by identifying
their parent via the public key's SHA1 hash.
The functionality is available via the 'tpm' command as well.
config TPM_LIST_RESOURCES
bool "Enable TPM resource listing support"
depends on TPM_V1
help
Enable support to list specific resources (e.g. keys) within the TPM.
The functionality is available via the 'tpm' command as well.
endif # TPM_V1
config TPM_V2
bool "TPMv2.x support"
depends on TPM
default y
help
Major TPM versions are not compatible at all, choose either
one or the other. This option enables TPMv2.x drivers/commands.
if TPM_V2
config TPM2_TIS_SANDBOX
bool "Enable sandbox TPMv2.x driver"
depends on TPM_V2 && SANDBOX
default y
help
This driver emulates a TPMv2.x, providing access to base functions
such as basic configuration, PCR extension and PCR read. Extended
functionalities are not implemented.
config TPM2_TIS_SPI
bool "Enable support for TPMv2.x SPI chips"
depends on TPM_V2 && DM_SPI
help
This driver supports TPMv2.x devices connected on the SPI bus.
The usual TPM operations and the 'tpm' command can be used to talk
to the device using the standard TPM Interface Specification (TIS)
protocol.
endif # TPM_V2
endmenu
@@ -0,0 +1,14 @@
# SPDX-License-Identifier: GPL-2.0+
# Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
obj-$(CONFIG_$(SPL_TPL_)TPM) += tpm-uclass.o
obj-$(CONFIG_TPM_ATMEL_TWI) += tpm_atmel_twi.o
obj-$(CONFIG_TPM_TIS_INFINEON) += tpm_tis_infineon.o
obj-$(CONFIG_TPM_TIS_LPC) += tpm_tis_lpc.o
obj-$(CONFIG_TPM_TIS_SANDBOX) += tpm_tis_sandbox.o
obj-$(CONFIG_TPM_ST33ZP24_I2C) += tpm_tis_st33zp24_i2c.o
obj-$(CONFIG_TPM_ST33ZP24_SPI) += tpm_tis_st33zp24_spi.o
obj-$(CONFIG_TPM2_TIS_SANDBOX) += tpm2_tis_sandbox.o
obj-$(CONFIG_TPM2_TIS_SPI) += tpm2_tis_spi.o
@@ -0,0 +1,136 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2015 Google, Inc
* Written by Simon Glass <sjg@chromium.org>
*/
#include <common.h>
#include <dm.h>
#include <linux/unaligned/be_byteshift.h>
#include <tpm-v1.h>
#include <tpm-v2.h>
#include "tpm_internal.h"
int tpm_open(struct udevice *dev)
{
struct tpm_ops *ops = tpm_get_ops(dev);
if (!ops->open)
return -ENOSYS;
return ops->open(dev);
}
int tpm_close(struct udevice *dev)
{
struct tpm_ops *ops = tpm_get_ops(dev);
if (!ops->close)
return -ENOSYS;
return ops->close(dev);
}
int tpm_get_desc(struct udevice *dev, char *buf, int size)
{
struct tpm_ops *ops = tpm_get_ops(dev);
if (!ops->get_desc)
return -ENOSYS;
return ops->get_desc(dev, buf, size);
}
/* Returns max number of milliseconds to wait */
static ulong tpm_tis_i2c_calc_ordinal_duration(struct tpm_chip_priv *priv,
u32 ordinal)
{
int duration_idx = TPM_UNDEFINED;
int duration = 0;
if (ordinal < TPM_MAX_ORDINAL) {
duration_idx = tpm_ordinal_duration[ordinal];
} else if ((ordinal & TPM_PROTECTED_ORDINAL_MASK) <
TPM_MAX_PROTECTED_ORDINAL) {
duration_idx = tpm_protected_ordinal_duration[
ordinal & TPM_PROTECTED_ORDINAL_MASK];
}
if (duration_idx != TPM_UNDEFINED)
duration = priv->duration_ms[duration_idx];
if (duration <= 0)
return 2 * 60 * 1000; /* Two minutes timeout */
else
return duration;
}
int tpm_xfer(struct udevice *dev, const uint8_t *sendbuf, size_t send_size,
uint8_t *recvbuf, size_t *recv_size)
{
struct tpm_chip_priv *priv = dev_get_uclass_priv(dev);
struct tpm_ops *ops = tpm_get_ops(dev);
ulong start, stop;
uint count, ordinal;
int ret, ret2;
if (ops->xfer)
return ops->xfer(dev, sendbuf, send_size, recvbuf, recv_size);
if (!ops->send || !ops->recv)
return -ENOSYS;
/* switch endianess: big->little */
count = get_unaligned_be32(sendbuf + TPM_CMD_COUNT_BYTE);
ordinal = get_unaligned_be32(sendbuf + TPM_CMD_ORDINAL_BYTE);
if (count == 0) {
debug("no data\n");
return -ENODATA;
}
if (count > send_size) {
debug("invalid count value %x %zx\n", count, send_size);
return -E2BIG;
}
debug("%s: Calling send\n", __func__);
ret = ops->send(dev, sendbuf, send_size);
if (ret < 0)
return ret;
start = get_timer(0);
stop = tpm_tis_i2c_calc_ordinal_duration(priv, ordinal);
do {
ret = ops->recv(dev, priv->buf, sizeof(priv->buf));
if (ret >= 0) {
if (ret > *recv_size)
return -ENOSPC;
memcpy(recvbuf, priv->buf, ret);
*recv_size = ret;
ret = 0;
break;
} else if (ret != -EAGAIN) {
return ret;
}
mdelay(priv->retry_time_ms);
if (get_timer(start) > stop) {
ret = -ETIMEDOUT;
break;
}
} while (ret);
ret2 = ops->cleanup ? ops->cleanup(dev) : 0;
return ret2 ? ret2 : ret;
}
UCLASS_DRIVER(tpm) = {
.id = UCLASS_TPM,
.name = "tpm",
.flags = DM_UC_FLAG_SEQ_ALIAS,
#if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA)
.post_bind = dm_scan_fdt_dev,
#endif
.per_device_auto_alloc_size = sizeof(struct tpm_chip_priv),
};
@@ -0,0 +1,628 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2018, Bootlin
* Author: Miquel Raynal <miquel.raynal@bootlin.com>
*/
#include <common.h>
#include <dm.h>
#include <tpm-v2.h>
#include <asm/state.h>
#include <asm/unaligned.h>
#include <u-boot/crc.h>
/* Hierarchies */
enum tpm2_hierarchy {
TPM2_HIERARCHY_LOCKOUT = 0,
TPM2_HIERARCHY_ENDORSEMENT,
TPM2_HIERARCHY_PLATFORM,
TPM2_HIERARCHY_NB,
};
/* Subset of supported capabilities */
enum tpm2_capability {
TPM_CAP_TPM_PROPERTIES = 0x6,
};
/* Subset of supported properties */
#define TPM2_PROPERTIES_OFFSET 0x0000020E
enum tpm2_cap_tpm_property {
TPM2_FAIL_COUNTER = 0,
TPM2_PROP_MAX_TRIES,
TPM2_RECOVERY_TIME,
TPM2_LOCKOUT_RECOVERY,
TPM2_PROPERTY_NB,
};
#define SANDBOX_TPM_PCR_NB 1
static const u8 sandbox_extended_once_pcr[] = {
0xf5, 0xa5, 0xfd, 0x42, 0xd1, 0x6a, 0x20, 0x30,
0x27, 0x98, 0xef, 0x6e, 0xd3, 0x09, 0x97, 0x9b,
0x43, 0x00, 0x3d, 0x23, 0x20, 0xd9, 0xf0, 0xe8,
0xea, 0x98, 0x31, 0xa9, 0x27, 0x59, 0xfb, 0x4b,
};
struct sandbox_tpm2 {
/* TPM internal states */
bool init_done;
bool startup_done;
bool tests_done;
/* TPM password per hierarchy */
char pw[TPM2_HIERARCHY_NB][TPM2_DIGEST_LEN + 1];
int pw_sz[TPM2_HIERARCHY_NB];
/* TPM properties */
u32 properties[TPM2_PROPERTY_NB];
/* TPM PCRs */
u8 pcr[SANDBOX_TPM_PCR_NB][TPM2_DIGEST_LEN];
/* TPM PCR extensions */
u32 pcr_extensions[SANDBOX_TPM_PCR_NB];
};
/*
* Check the tag validity depending on the command (authentication required or
* not). If authentication is required, check it is valid. Update the auth
* pointer to point to the next chunk of data to process if needed.
*/
static int sandbox_tpm2_check_session(struct udevice *dev, u32 command, u16 tag,
const u8 **auth,
enum tpm2_hierarchy *hierarchy)
{
struct sandbox_tpm2 *tpm = dev_get_priv(dev);
u32 handle, auth_sz, session_handle;
u16 nonce_sz, pw_sz;
const char *pw;
switch (command) {
case TPM2_CC_STARTUP:
case TPM2_CC_SELF_TEST:
case TPM2_CC_GET_CAPABILITY:
case TPM2_CC_PCR_READ:
if (tag != TPM2_ST_NO_SESSIONS) {
printf("No session required for command 0x%x\n",
command);
return TPM2_RC_BAD_TAG;
}
return 0;
case TPM2_CC_CLEAR:
case TPM2_CC_HIERCHANGEAUTH:
case TPM2_CC_DAM_RESET:
case TPM2_CC_DAM_PARAMETERS:
case TPM2_CC_PCR_EXTEND:
if (tag != TPM2_ST_SESSIONS) {
printf("Session required for command 0x%x\n", command);
return TPM2_RC_AUTH_CONTEXT;
}
handle = get_unaligned_be32(*auth);
*auth += sizeof(handle);
/*
* PCR_Extend had a different protection mechanism and does not
* use the same standards as other commands.
*/
if (command == TPM2_CC_PCR_EXTEND)
break;
switch (handle) {
case TPM2_RH_LOCKOUT:
*hierarchy = TPM2_HIERARCHY_LOCKOUT;
break;
case TPM2_RH_ENDORSEMENT:
if (command == TPM2_CC_CLEAR) {
printf("Endorsement hierarchy unsupported\n");
return TPM2_RC_AUTH_MISSING;
}
*hierarchy = TPM2_HIERARCHY_ENDORSEMENT;
break;
case TPM2_RH_PLATFORM:
*hierarchy = TPM2_HIERARCHY_PLATFORM;
break;
default:
printf("Wrong handle 0x%x\n", handle);
return TPM2_RC_VALUE;
}
break;
default:
printf("Command code not recognized: 0x%x\n", command);
return TPM2_RC_COMMAND_CODE;
}
auth_sz = get_unaligned_be32(*auth);
*auth += sizeof(auth_sz);
session_handle = get_unaligned_be32(*auth);
*auth += sizeof(session_handle);
if (session_handle != TPM2_RS_PW) {
printf("Wrong session handle 0x%x\n", session_handle);
return TPM2_RC_VALUE;
}
nonce_sz = get_unaligned_be16(*auth);
*auth += sizeof(nonce_sz);
if (nonce_sz) {
printf("Nonces not supported in Sandbox, aborting\n");
return TPM2_RC_HANDLE;
}
/* Ignore attributes */
*auth += sizeof(u8);
pw_sz = get_unaligned_be16(*auth);
*auth += sizeof(pw_sz);
if (auth_sz != (9 + nonce_sz + pw_sz)) {
printf("Authentication size (%d) do not match %d\n",
auth_sz, 9 + nonce_sz + pw_sz);
return TPM2_RC_SIZE;
}
/* No passwork is acceptable */
if (!pw_sz && !tpm->pw_sz[*hierarchy])
return TPM2_RC_SUCCESS;
/* Password is too long */
if (pw_sz > TPM2_DIGEST_LEN) {
printf("Password should not be more than %dB\n",
TPM2_DIGEST_LEN);
return TPM2_RC_AUTHSIZE;
}
pw = (const char *)*auth;
*auth += pw_sz;
/* Password is wrong */
if (pw_sz != tpm->pw_sz[*hierarchy] ||
strncmp(pw, tpm->pw[*hierarchy], tpm->pw_sz[*hierarchy])) {
printf("Authentication failed: wrong password.\n");
return TPM2_RC_BAD_AUTH;
}
return TPM2_RC_SUCCESS;
}
static int sandbox_tpm2_check_readyness(struct udevice *dev, int command)
{
struct sandbox_tpm2 *tpm = dev_get_priv(dev);
switch (command) {
case TPM2_CC_STARTUP:
if (!tpm->init_done || tpm->startup_done)
return TPM2_RC_INITIALIZE;
break;
case TPM2_CC_GET_CAPABILITY:
if (!tpm->init_done || !tpm->startup_done)
return TPM2_RC_INITIALIZE;
break;
case TPM2_CC_SELF_TEST:
if (!tpm->startup_done)
return TPM2_RC_INITIALIZE;
break;
default:
if (!tpm->tests_done)
return TPM2_RC_NEEDS_TEST;
break;
}
return 0;
}
static int sandbox_tpm2_fill_buf(u8 *recv, size_t *recv_len, u16 tag, u32 rc)
{
*recv_len = sizeof(tag) + sizeof(u32) + sizeof(rc);
/* Write tag */
put_unaligned_be16(tag, recv);
recv += sizeof(tag);
/* Write length */
put_unaligned_be32(*recv_len, recv);
recv += sizeof(u32);
/* Write return code */
put_unaligned_be32(rc, recv);
recv += sizeof(rc);
/* Add trailing \0 */
*recv = '\0';
return 0;
}
static int sandbox_tpm2_extend(struct udevice *dev, int pcr_index,
const u8 *extension)
{
struct sandbox_tpm2 *tpm = dev_get_priv(dev);
int i;
/* Only simulate the first extensions from all '0' with only '0' */
for (i = 0; i < TPM2_DIGEST_LEN; i++)
if (tpm->pcr[pcr_index][i] || extension[i])
return TPM2_RC_FAILURE;
memcpy(tpm->pcr[pcr_index], sandbox_extended_once_pcr,
TPM2_DIGEST_LEN);
tpm->pcr_extensions[pcr_index]++;
return 0;
};
static int sandbox_tpm2_xfer(struct udevice *dev, const u8 *sendbuf,
size_t send_size, u8 *recvbuf,
size_t *recv_len)
{
struct sandbox_tpm2 *tpm = dev_get_priv(dev);
enum tpm2_hierarchy hierarchy = 0;
const u8 *sent = sendbuf;
u8 *recv = recvbuf;
u32 length, command, rc = 0;
u16 tag, mode, new_pw_sz;
u8 yes_no;
int i, j;
/* TPM2_GetProperty */
u32 capability, property, property_count;
/* TPM2_PCR_Read/Extend variables */
int pcr_index = 0;
u64 pcr_map = 0;
u32 selections, pcr_nb;
u16 alg;
u8 pcr_array_sz;
tag = get_unaligned_be16(sent);
sent += sizeof(tag);
length = get_unaligned_be32(sent);
sent += sizeof(length);
if (length != send_size) {
printf("TPM2: Unmatching length, received: %ld, expected: %d\n",
send_size, length);
rc = TPM2_RC_SIZE;
sandbox_tpm2_fill_buf(recv, recv_len, tag, rc);
return 0;
}
command = get_unaligned_be32(sent);
sent += sizeof(command);
rc = sandbox_tpm2_check_readyness(dev, command);
if (rc) {
sandbox_tpm2_fill_buf(recv, recv_len, tag, rc);
return 0;
}
rc = sandbox_tpm2_check_session(dev, command, tag, &sent, &hierarchy);
if (rc) {
sandbox_tpm2_fill_buf(recv, recv_len, tag, rc);
return 0;
}
switch (command) {
case TPM2_CC_STARTUP:
mode = get_unaligned_be16(sent);
sent += sizeof(mode);
switch (mode) {
case TPM2_SU_CLEAR:
case TPM2_SU_STATE:
break;
default:
rc = TPM2_RC_VALUE;
}
tpm->startup_done = true;
sandbox_tpm2_fill_buf(recv, recv_len, tag, rc);
break;
case TPM2_CC_SELF_TEST:
yes_no = *sent;
sent += sizeof(yes_no);
switch (yes_no) {
case TPMI_YES:
case TPMI_NO:
break;
default:
rc = TPM2_RC_VALUE;
}
tpm->tests_done = true;
sandbox_tpm2_fill_buf(recv, recv_len, tag, rc);
break;
case TPM2_CC_CLEAR:
/* Reset this hierarchy password */
tpm->pw_sz[hierarchy] = 0;
/* Reset all password if thisis the PLATFORM hierarchy */
if (hierarchy == TPM2_HIERARCHY_PLATFORM)
for (i = 0; i < TPM2_HIERARCHY_NB; i++)
tpm->pw_sz[i] = 0;
/* Reset the properties */
for (i = 0; i < TPM2_PROPERTY_NB; i++)
tpm->properties[i] = 0;
/* Reset the PCRs and their number of extensions */
for (i = 0; i < SANDBOX_TPM_PCR_NB; i++) {
tpm->pcr_extensions[i] = 0;
for (j = 0; j < TPM2_DIGEST_LEN; j++)
tpm->pcr[i][j] = 0;
}
sandbox_tpm2_fill_buf(recv, recv_len, tag, rc);
break;
case TPM2_CC_HIERCHANGEAUTH:
new_pw_sz = get_unaligned_be16(sent);
sent += sizeof(new_pw_sz);
if (new_pw_sz > TPM2_DIGEST_LEN) {
rc = TPM2_RC_SIZE;
} else if (new_pw_sz) {
tpm->pw_sz[hierarchy] = new_pw_sz;
memcpy(tpm->pw[hierarchy], sent, new_pw_sz);
sent += new_pw_sz;
}
sandbox_tpm2_fill_buf(recv, recv_len, tag, rc);
break;
case TPM2_CC_GET_CAPABILITY:
capability = get_unaligned_be32(sent);
sent += sizeof(capability);
if (capability != TPM_CAP_TPM_PROPERTIES) {
printf("Sandbox TPM only support TPM_CAPABILITIES\n");
return TPM2_RC_HANDLE;
}
property = get_unaligned_be32(sent);
sent += sizeof(property);
property -= TPM2_PROPERTIES_OFFSET;
property_count = get_unaligned_be32(sent);
sent += sizeof(property_count);
if (!property_count ||
property + property_count > TPM2_PROPERTY_NB) {
rc = TPM2_RC_HANDLE;
return sandbox_tpm2_fill_buf(recv, recv_len, tag, rc);
}
/* Write tag */
put_unaligned_be16(tag, recv);
recv += sizeof(tag);
/* Ignore length for now */
recv += sizeof(u32);
/* Write return code */
put_unaligned_be32(rc, recv);
recv += sizeof(rc);
/* Tell there is more data to read */
*recv = TPMI_YES;
recv += sizeof(yes_no);
/* Repeat the capability */
put_unaligned_be32(capability, recv);
recv += sizeof(capability);
/* Give the number of properties that follow */
put_unaligned_be32(property_count, recv);
recv += sizeof(property_count);
/* Fill with the properties */
for (i = 0; i < property_count; i++) {
put_unaligned_be32(TPM2_PROPERTIES_OFFSET + property +
i, recv);
recv += sizeof(property);
put_unaligned_be32(tpm->properties[property + i],
recv);
recv += sizeof(property);
}
/* Add trailing \0 */
*recv = '\0';
/* Write response length */
*recv_len = recv - recvbuf;
put_unaligned_be32(*recv_len, recvbuf + sizeof(tag));
break;
case TPM2_CC_DAM_PARAMETERS:
tpm->properties[TPM2_PROP_MAX_TRIES] = get_unaligned_be32(sent);
sent += sizeof(*tpm->properties);
tpm->properties[TPM2_RECOVERY_TIME] = get_unaligned_be32(sent);
sent += sizeof(*tpm->properties);
tpm->properties[TPM2_LOCKOUT_RECOVERY] = get_unaligned_be32(sent);
sent += sizeof(*tpm->properties);
sandbox_tpm2_fill_buf(recv, recv_len, tag, rc);
break;
case TPM2_CC_PCR_READ:
selections = get_unaligned_be32(sent);
sent += sizeof(selections);
if (selections != 1) {
printf("Sandbox cannot handle more than one PCR\n");
rc = TPM2_RC_VALUE;
return sandbox_tpm2_fill_buf(recv, recv_len, tag, rc);
}
alg = get_unaligned_be16(sent);
sent += sizeof(alg);
if (alg != TPM2_ALG_SHA256) {
printf("Sandbox TPM only handle SHA256 algorithm\n");
rc = TPM2_RC_VALUE;
return sandbox_tpm2_fill_buf(recv, recv_len, tag, rc);
}
pcr_array_sz = *sent;
sent += sizeof(pcr_array_sz);
if (!pcr_array_sz || pcr_array_sz > 8) {
printf("Sandbox TPM cannot handle so much PCRs\n");
rc = TPM2_RC_VALUE;
return sandbox_tpm2_fill_buf(recv, recv_len, tag, rc);
}
for (i = 0; i < pcr_array_sz; i++)
pcr_map += (u64)sent[i] << (i * 8);
if (pcr_map >> SANDBOX_TPM_PCR_NB) {
printf("Sandbox TPM handles up to %d PCR(s)\n",
SANDBOX_TPM_PCR_NB);
rc = TPM2_RC_VALUE;
return sandbox_tpm2_fill_buf(recv, recv_len, tag, rc);
}
if (!pcr_map) {
printf("Empty PCR map.\n");
rc = TPM2_RC_VALUE;
return sandbox_tpm2_fill_buf(recv, recv_len, tag, rc);
}
for (i = 0; i < SANDBOX_TPM_PCR_NB; i++)
if (pcr_map & BIT(i))
pcr_index = i;
/* Write tag */
put_unaligned_be16(tag, recv);
recv += sizeof(tag);
/* Ignore length for now */
recv += sizeof(u32);
/* Write return code */
put_unaligned_be32(rc, recv);
recv += sizeof(rc);
/* Number of extensions */
put_unaligned_be32(tpm->pcr_extensions[pcr_index], recv);
recv += sizeof(u32);
/* Copy the PCR */
memcpy(recv, tpm->pcr[pcr_index], TPM2_DIGEST_LEN);
recv += TPM2_DIGEST_LEN;
/* Add trailing \0 */
*recv = '\0';
/* Write response length */
*recv_len = recv - recvbuf;
put_unaligned_be32(*recv_len, recvbuf + sizeof(tag));
break;
case TPM2_CC_PCR_EXTEND:
/* Get the PCR index */
pcr_index = get_unaligned_be32(sendbuf + sizeof(tag) +
sizeof(length) +
sizeof(command));
if (pcr_index > SANDBOX_TPM_PCR_NB) {
printf("Sandbox TPM handles up to %d PCR(s)\n",
SANDBOX_TPM_PCR_NB);
rc = TPM2_RC_VALUE;
}
/* Check the number of hashes */
pcr_nb = get_unaligned_be32(sent);
sent += sizeof(pcr_nb);
if (pcr_nb != 1) {
printf("Sandbox cannot handle more than one PCR\n");
rc = TPM2_RC_VALUE;
return sandbox_tpm2_fill_buf(recv, recv_len, tag, rc);
}
/* Check the hash algorithm */
alg = get_unaligned_be16(sent);
sent += sizeof(alg);
if (alg != TPM2_ALG_SHA256) {
printf("Sandbox TPM only handle SHA256 algorithm\n");
rc = TPM2_RC_VALUE;
return sandbox_tpm2_fill_buf(recv, recv_len, tag, rc);
}
/* Extend the PCR */
rc = sandbox_tpm2_extend(dev, pcr_index, sent);
sandbox_tpm2_fill_buf(recv, recv_len, tag, rc);
break;
default:
printf("TPM2 command %02x unknown in Sandbox\n", command);
rc = TPM2_RC_COMMAND_CODE;
sandbox_tpm2_fill_buf(recv, recv_len, tag, rc);
}
return 0;
}
static int sandbox_tpm2_get_desc(struct udevice *dev, char *buf, int size)
{
if (size < 15)
return -ENOSPC;
return snprintf(buf, size, "Sandbox TPM2.x");
}
static int sandbox_tpm2_open(struct udevice *dev)
{
struct sandbox_tpm2 *tpm = dev_get_priv(dev);
if (tpm->init_done)
return -EIO;
tpm->init_done = true;
return 0;
}
static int sandbox_tpm2_probe(struct udevice *dev)
{
struct sandbox_tpm2 *tpm = dev_get_priv(dev);
struct tpm_chip_priv *priv = dev_get_uclass_priv(dev);
/* Use the TPM v2 stack */
priv->version = TPM_V2;
memset(tpm, 0, sizeof(*tpm));
priv->pcr_count = 32;
priv->pcr_select_min = 2;
return 0;
}
static int sandbox_tpm2_close(struct udevice *dev)
{
return 0;
}
static const struct tpm_ops sandbox_tpm2_ops = {
.open = sandbox_tpm2_open,
.close = sandbox_tpm2_close,
.get_desc = sandbox_tpm2_get_desc,
.xfer = sandbox_tpm2_xfer,
};
static const struct udevice_id sandbox_tpm2_ids[] = {
{ .compatible = "sandbox,tpm2" },
{ }
};
U_BOOT_DRIVER(sandbox_tpm2) = {
.name = "sandbox_tpm2",
.id = UCLASS_TPM,
.of_match = sandbox_tpm2_ids,
.ops = &sandbox_tpm2_ops,
.probe = sandbox_tpm2_probe,
.priv_auto_alloc_size = sizeof(struct sandbox_tpm2),
};
@@ -0,0 +1,691 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Author:
* Miquel Raynal <miquel.raynal@bootlin.com>
*
* Description:
* SPI-level driver for TCG/TIS TPM (trusted platform module).
* Specifications at www.trustedcomputinggroup.org
*
* This device driver implements the TPM interface as defined in
* the TCG SPI protocol stack version 2.0.
*
* It is based on the U-Boot driver tpm_tis_infineon_i2c.c.
*/
#include <common.h>
#include <dm.h>
#include <fdtdec.h>
#include <log.h>
#include <spi.h>
#include <tpm-v2.h>
#include <linux/errno.h>
#include <linux/compiler.h>
#include <linux/types.h>
#include <linux/unaligned/be_byteshift.h>
#include <asm-generic/gpio.h>
#include "tpm_tis.h"
#include "tpm_internal.h"
DECLARE_GLOBAL_DATA_PTR;
#define TPM_ACCESS(l) (0x0000 | ((l) << 12))
#define TPM_INT_ENABLE(l) (0x0008 | ((l) << 12))
#define TPM_STS(l) (0x0018 | ((l) << 12))
#define TPM_DATA_FIFO(l) (0x0024 | ((l) << 12))
#define TPM_DID_VID(l) (0x0F00 | ((l) << 12))
#define TPM_RID(l) (0x0F04 | ((l) << 12))
#define MAX_SPI_FRAMESIZE 64
/* Number of wait states to wait for */
#define TPM_WAIT_STATES 100
/**
* struct tpm_tis_chip_data - Non-discoverable TPM information
*
* @pcr_count: Number of PCR per bank
* @pcr_select_min: Size in octets of the pcrSelect array
*/
struct tpm_tis_chip_data {
unsigned int pcr_count;
unsigned int pcr_select_min;
unsigned int time_before_first_cmd_ms;
};
/**
* tpm_tis_spi_read() - Read from TPM register
*
* @addr: register address to read from
* @buffer: provided by caller
* @len: number of bytes to read
*
* Read len bytes from TPM register and put them into
* buffer (little-endian format, i.e. first byte is put into buffer[0]).
*
* NOTE: TPM is big-endian for multi-byte values. Multi-byte
* values have to be swapped.
*
* @return -EIO on error, 0 on success.
*/
static int tpm_tis_spi_xfer(struct udevice *dev, u32 addr, const u8 *out,
u8 *in, u16 len)
{
struct spi_slave *slave = dev_get_parent_priv(dev);
int transfer_len, ret;
u8 tx_buf[MAX_SPI_FRAMESIZE];
u8 rx_buf[MAX_SPI_FRAMESIZE];
if (in && out) {
log(LOGC_NONE, LOGL_ERR, "%s: can't do full duplex\n",
__func__);
return -EINVAL;
}
ret = spi_claim_bus(slave);
if (ret < 0) {
log(LOGC_NONE, LOGL_ERR, "%s: could not claim bus\n", __func__);
return ret;
}
while (len) {
/* Request */
transfer_len = min_t(u16, len, MAX_SPI_FRAMESIZE);
tx_buf[0] = (in ? BIT(7) : 0) | (transfer_len - 1);
tx_buf[1] = 0xD4;
tx_buf[2] = addr >> 8;
tx_buf[3] = addr;
ret = spi_xfer(slave, 4 * 8, tx_buf, rx_buf, SPI_XFER_BEGIN);
if (ret < 0) {
log(LOGC_NONE, LOGL_ERR,
"%s: spi request transfer failed (err: %d)\n",
__func__, ret);
goto release_bus;
}
/* Wait state */
if (!(rx_buf[3] & 0x1)) {
int i;
for (i = 0; i < TPM_WAIT_STATES; i++) {
ret = spi_xfer(slave, 1 * 8, NULL, rx_buf, 0);
if (ret) {
log(LOGC_NONE, LOGL_ERR,
"%s: wait state failed: %d\n",
__func__, ret);
goto release_bus;
}
if (rx_buf[0] & 0x1)
break;
}
if (i == TPM_WAIT_STATES) {
log(LOGC_NONE, LOGL_ERR,
"%s: timeout on wait state\n", __func__);
ret = -ETIMEDOUT;
goto release_bus;
}
}
/* Read/Write */
if (out) {
memcpy(tx_buf, out, transfer_len);
out += transfer_len;
}
ret = spi_xfer(slave, transfer_len * 8,
out ? tx_buf : NULL,
in ? rx_buf : NULL,
SPI_XFER_END);
if (ret) {
log(LOGC_NONE, LOGL_ERR,
"%s: spi read transfer failed (err: %d)\n",
__func__, ret);
goto release_bus;
}
if (in) {
memcpy(in, rx_buf, transfer_len);
in += transfer_len;
}
len -= transfer_len;
}
release_bus:
/* If an error occurred, release the chip by deasserting the CS */
if (ret < 0)
spi_xfer(slave, 0, NULL, NULL, SPI_XFER_END);
spi_release_bus(slave);
return ret;
}
static int tpm_tis_spi_read(struct udevice *dev, u16 addr, u8 *in, u16 len)
{
return tpm_tis_spi_xfer(dev, addr, NULL, in, len);
}
static int tpm_tis_spi_read32(struct udevice *dev, u32 addr, u32 *result)
{
__le32 result_le;
int ret;
ret = tpm_tis_spi_read(dev, addr, (u8 *)&result_le, sizeof(u32));
if (!ret)
*result = le32_to_cpu(result_le);
return ret;
}
static int tpm_tis_spi_write(struct udevice *dev, u16 addr, const u8 *out,
u16 len)
{
return tpm_tis_spi_xfer(dev, addr, out, NULL, len);
}
static int tpm_tis_spi_check_locality(struct udevice *dev, int loc)
{
const u8 mask = TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID;
struct tpm_chip *chip = dev_get_priv(dev);
u8 buf;
int ret;
ret = tpm_tis_spi_read(dev, TPM_ACCESS(loc), &buf, 1);
if (ret)
return ret;
if ((buf & mask) == mask) {
chip->locality = loc;
return 0;
}
return -ENOENT;
}
static void tpm_tis_spi_release_locality(struct udevice *dev, int loc,
bool force)
{
const u8 mask = TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID;
u8 buf;
if (tpm_tis_spi_read(dev, TPM_ACCESS(loc), &buf, 1) < 0)
return;
if (force || (buf & mask) == mask) {
buf = TPM_ACCESS_ACTIVE_LOCALITY;
tpm_tis_spi_write(dev, TPM_ACCESS(loc), &buf, 1);
}
}
static int tpm_tis_spi_request_locality(struct udevice *dev, int loc)
{
struct tpm_chip *chip = dev_get_priv(dev);
unsigned long start, stop;
u8 buf = TPM_ACCESS_REQUEST_USE;
int ret;
ret = tpm_tis_spi_check_locality(dev, loc);
if (!ret)
return 0;
if (ret != -ENOENT) {
log(LOGC_NONE, LOGL_ERR, "%s: Failed to get locality: %d\n",
__func__, ret);
return ret;
}
ret = tpm_tis_spi_write(dev, TPM_ACCESS(loc), &buf, 1);
if (ret) {
log(LOGC_NONE, LOGL_ERR, "%s: Failed to write to TPM: %d\n",
__func__, ret);
return ret;
}
start = get_timer(0);
stop = chip->timeout_a;
do {
ret = tpm_tis_spi_check_locality(dev, loc);
if (!ret)
return 0;
if (ret != -ENOENT) {
log(LOGC_NONE, LOGL_ERR,
"%s: Failed to get locality: %d\n", __func__, ret);
return ret;
}
mdelay(TPM_TIMEOUT_MS);
} while (get_timer(start) < stop);
log(LOGC_NONE, LOGL_ERR, "%s: Timeout getting locality: %d\n", __func__,
ret);
return ret;
}
static u8 tpm_tis_spi_status(struct udevice *dev, u8 *status)
{
struct tpm_chip *chip = dev_get_priv(dev);
return tpm_tis_spi_read(dev, TPM_STS(chip->locality), status, 1);
}
static int tpm_tis_spi_wait_for_stat(struct udevice *dev, u8 mask,
unsigned long timeout, u8 *status)
{
unsigned long start = get_timer(0);
unsigned long stop = timeout;
int ret;
do {
mdelay(TPM_TIMEOUT_MS);
ret = tpm_tis_spi_status(dev, status);
if (ret)
return ret;
if ((*status & mask) == mask)
return 0;
} while (get_timer(start) < stop);
return -ETIMEDOUT;
}
static u8 tpm_tis_spi_valid_status(struct udevice *dev, u8 *status)
{
struct tpm_chip *chip = dev_get_priv(dev);
return tpm_tis_spi_wait_for_stat(dev, TPM_STS_VALID,
chip->timeout_c, status);
}
static int tpm_tis_spi_get_burstcount(struct udevice *dev)
{
struct tpm_chip *chip = dev_get_priv(dev);
unsigned long start, stop;
u32 burstcount, ret;
/* wait for burstcount */
start = get_timer(0);
stop = chip->timeout_d;
do {
ret = tpm_tis_spi_read32(dev, TPM_STS(chip->locality),
&burstcount);
if (ret)
return -EBUSY;
burstcount = (burstcount >> 8) & 0xFFFF;
if (burstcount)
return burstcount;
mdelay(TPM_TIMEOUT_MS);
} while (get_timer(start) < stop);
return -EBUSY;
}
static int tpm_tis_spi_cancel(struct udevice *dev)
{
struct tpm_chip *chip = dev_get_priv(dev);
u8 data = TPM_STS_COMMAND_READY;
return tpm_tis_spi_write(dev, TPM_STS(chip->locality), &data, 1);
}
static int tpm_tis_spi_recv_data(struct udevice *dev, u8 *buf, size_t count)
{
struct tpm_chip *chip = dev_get_priv(dev);
int size = 0, burstcnt, len, ret;
u8 status;
while (size < count &&
tpm_tis_spi_wait_for_stat(dev,
TPM_STS_DATA_AVAIL | TPM_STS_VALID,
chip->timeout_c, &status) == 0) {
burstcnt = tpm_tis_spi_get_burstcount(dev);
if (burstcnt < 0)
return burstcnt;
len = min_t(int, burstcnt, count - size);
ret = tpm_tis_spi_read(dev, TPM_DATA_FIFO(chip->locality),
buf + size, len);
if (ret < 0)
return ret;
size += len;
}
return size;
}
static int tpm_tis_spi_recv(struct udevice *dev, u8 *buf, size_t count)
{
struct tpm_chip *chip = dev_get_priv(dev);
int size, expected;
if (!chip)
return -ENODEV;
if (count < TPM_HEADER_SIZE) {
size = -EIO;
goto out;
}
size = tpm_tis_spi_recv_data(dev, buf, TPM_HEADER_SIZE);
if (size < TPM_HEADER_SIZE) {
log(LOGC_NONE, LOGL_ERR, "TPM error, unable to read header\n");
goto out;
}
expected = get_unaligned_be32(buf + 2);
if (expected > count) {
size = -EIO;
goto out;
}
size += tpm_tis_spi_recv_data(dev, &buf[TPM_HEADER_SIZE],
expected - TPM_HEADER_SIZE);
if (size < expected) {
log(LOGC_NONE, LOGL_ERR,
"TPM error, unable to read remaining bytes of result\n");
size = -EIO;
goto out;
}
out:
tpm_tis_spi_cancel(dev);
tpm_tis_spi_release_locality(dev, chip->locality, false);
return size;
}
static int tpm_tis_spi_send(struct udevice *dev, const u8 *buf, size_t len)
{
struct tpm_chip *chip = dev_get_priv(dev);
u32 i, size;
u8 status;
int burstcnt, ret;
u8 data;
if (!chip)
return -ENODEV;
if (len > TPM_DEV_BUFSIZE)
return -E2BIG; /* Command is too long for our tpm, sorry */
ret = tpm_tis_spi_request_locality(dev, 0);
if (ret < 0)
return -EBUSY;
/*
* Check if the TPM is ready. If not, if not, cancel the pending command
* and poll on the status to be finally ready.
*/
ret = tpm_tis_spi_status(dev, &status);
if (ret)
return ret;
if (!(status & TPM_STS_COMMAND_READY)) {
/* Force the transition, usually this will be done at startup */
ret = tpm_tis_spi_cancel(dev);
if (ret) {
log(LOGC_NONE, LOGL_ERR,
"%s: Could not cancel previous operation\n",
__func__);
goto out_err;
}
ret = tpm_tis_spi_wait_for_stat(dev, TPM_STS_COMMAND_READY,
chip->timeout_b, &status);
if (ret < 0 || !(status & TPM_STS_COMMAND_READY)) {
log(LOGC_NONE, LOGL_ERR,
"status %d after wait for stat returned %d\n",
status, ret);
goto out_err;
}
}
for (i = 0; i < len - 1;) {
burstcnt = tpm_tis_spi_get_burstcount(dev);
if (burstcnt < 0)
return burstcnt;
size = min_t(int, len - i - 1, burstcnt);
ret = tpm_tis_spi_write(dev, TPM_DATA_FIFO(chip->locality),
buf + i, size);
if (ret < 0)
goto out_err;
i += size;
}
ret = tpm_tis_spi_valid_status(dev, &status);
if (ret)
goto out_err;
if ((status & TPM_STS_DATA_EXPECT) == 0) {
ret = -EIO;
goto out_err;
}
ret = tpm_tis_spi_write(dev, TPM_DATA_FIFO(chip->locality),
buf + len - 1, 1);
if (ret)
goto out_err;
ret = tpm_tis_spi_valid_status(dev, &status);
if (ret)
goto out_err;
if ((status & TPM_STS_DATA_EXPECT) != 0) {
ret = -EIO;
goto out_err;
}
data = TPM_STS_GO;
ret = tpm_tis_spi_write(dev, TPM_STS(chip->locality), &data, 1);
if (ret)
goto out_err;
return len;
out_err:
tpm_tis_spi_cancel(dev);
tpm_tis_spi_release_locality(dev, chip->locality, false);
return ret;
}
static int tpm_tis_spi_cleanup(struct udevice *dev)
{
struct tpm_chip *chip = dev_get_priv(dev);
tpm_tis_spi_cancel(dev);
/*
* The TPM needs some time to clean up here,
* so we sleep rather than keeping the bus busy
*/
mdelay(2);
tpm_tis_spi_release_locality(dev, chip->locality, false);
return 0;
}
static int tpm_tis_spi_open(struct udevice *dev)
{
struct tpm_chip *chip = dev_get_priv(dev);
struct tpm_chip_priv *priv = dev_get_uclass_priv(dev);
if (chip->is_open)
return -EBUSY;
chip->is_open = 1;
return 0;
}
static int tpm_tis_spi_close(struct udevice *dev)
{
struct tpm_chip *chip = dev_get_priv(dev);
if (chip->is_open) {
tpm_tis_spi_release_locality(dev, chip->locality, true);
chip->is_open = 0;
}
return 0;
}
static int tpm_tis_get_desc(struct udevice *dev, char *buf, int size)
{
struct tpm_chip *chip = dev_get_priv(dev);
if (size < 80)
return -ENOSPC;
return snprintf(buf, size,
"%s v2.0: VendorID 0x%04x, DeviceID 0x%04x, RevisionID 0x%02x [%s]",
dev->name, chip->vend_dev & 0xFFFF,
chip->vend_dev >> 16, chip->rid,
(chip->is_open ? "open" : "closed"));
}
static int tpm_tis_wait_init(struct udevice *dev, int loc)
{
struct tpm_chip *chip = dev_get_priv(dev);
unsigned long start, stop;
u8 status;
int ret;
start = get_timer(0);
stop = chip->timeout_b;
do {
mdelay(TPM_TIMEOUT_MS);
ret = tpm_tis_spi_read(dev, TPM_ACCESS(loc), &status, 1);
if (ret)
break;
if (status & TPM_ACCESS_VALID)
return 0;
} while (get_timer(start) < stop);
return -EIO;
}
static int tpm_tis_spi_probe(struct udevice *dev)
{
struct tpm_tis_chip_data *drv_data = (void *)dev_get_driver_data(dev);
struct tpm_chip_priv *priv = dev_get_uclass_priv(dev);
struct tpm_chip *chip = dev_get_priv(dev);
int ret;
/* Use the TPM v2 stack */
priv->version = TPM_V2;
if (IS_ENABLED(CONFIG_DM_GPIO)) {
struct gpio_desc reset_gpio;
ret = gpio_request_by_name(dev, "gpio-reset", 0,
&reset_gpio, GPIOD_IS_OUT);
if (ret) {
log(LOGC_NONE, LOGL_NOTICE, "%s: missing reset GPIO\n",
__func__);
} else {
dm_gpio_set_value(&reset_gpio, 1);
mdelay(1);
dm_gpio_set_value(&reset_gpio, 0);
}
}
/* Ensure a minimum amount of time elapsed since reset of the TPM */
mdelay(drv_data->time_before_first_cmd_ms);
chip->locality = 0;
chip->timeout_a = TIS_SHORT_TIMEOUT_MS;
chip->timeout_b = TIS_LONG_TIMEOUT_MS;
chip->timeout_c = TIS_SHORT_TIMEOUT_MS;
chip->timeout_d = TIS_SHORT_TIMEOUT_MS;
priv->pcr_count = drv_data->pcr_count;
priv->pcr_select_min = drv_data->pcr_select_min;
ret = tpm_tis_wait_init(dev, chip->locality);
if (ret) {
log(LOGC_DM, LOGL_ERR, "%s: no device found\n", __func__);
return ret;
}
ret = tpm_tis_spi_request_locality(dev, chip->locality);
if (ret) {
log(LOGC_NONE, LOGL_ERR, "%s: could not request locality %d\n",
__func__, chip->locality);
return ret;
}
ret = tpm_tis_spi_read32(dev, TPM_DID_VID(chip->locality),
&chip->vend_dev);
if (ret) {
log(LOGC_NONE, LOGL_ERR,
"%s: could not retrieve VendorID/DeviceID\n", __func__);
return ret;
}
ret = tpm_tis_spi_read(dev, TPM_RID(chip->locality), &chip->rid, 1);
if (ret) {
log(LOGC_NONE, LOGL_ERR, "%s: could not retrieve RevisionID\n",
__func__);
return ret;
}
log(LOGC_NONE, LOGL_ERR,
"SPI TPMv2.0 found (vid:%04x, did:%04x, rid:%02x)\n",
chip->vend_dev & 0xFFFF, chip->vend_dev >> 16, chip->rid);
return 0;
}
static int tpm_tis_spi_remove(struct udevice *dev)
{
struct tpm_chip *chip = dev_get_priv(dev);
tpm_tis_spi_release_locality(dev, chip->locality, true);
return 0;
}
static const struct tpm_ops tpm_tis_spi_ops = {
.open = tpm_tis_spi_open,
.close = tpm_tis_spi_close,
.get_desc = tpm_tis_get_desc,
.send = tpm_tis_spi_send,
.recv = tpm_tis_spi_recv,
.cleanup = tpm_tis_spi_cleanup,
};
static const struct tpm_tis_chip_data tpm_tis_std_chip_data = {
.pcr_count = 24,
.pcr_select_min = 3,
.time_before_first_cmd_ms = 30,
};
static const struct udevice_id tpm_tis_spi_ids[] = {
{
.compatible = "tis,tpm2-spi",
.data = (ulong)&tpm_tis_std_chip_data,
},
{ }
};
U_BOOT_DRIVER(tpm_tis_spi) = {
.name = "tpm_tis_spi",
.id = UCLASS_TPM,
.of_match = tpm_tis_spi_ids,
.ops = &tpm_tis_spi_ops,
.probe = tpm_tis_spi_probe,
.remove = tpm_tis_spi_remove,
.priv_auto_alloc_size = sizeof(struct tpm_chip),
};
@@ -0,0 +1,166 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2013 Guntermann & Drunck, GmbH
*
* Written by Dirk Eibach <dirk.eibach@gdsys.cc>
*/
#include <common.h>
#include <dm.h>
#include <tpm-v1.h>
#include <i2c.h>
#include <asm/unaligned.h>
#include "tpm_internal.h"
#define ATMEL_TPM_TIMEOUT_MS 5000 /* sufficient for anything but
generating/exporting keys */
/*
* tpm_atmel_twi_open()
*
* Requests access to locality 0 for the caller. After all commands have been
* completed the caller is supposed to call tis_close().
*
* Returns 0 on success, -1 on failure.
*/
static int tpm_atmel_twi_open(struct udevice *dev)
{
return 0;
}
/*
* tpm_atmel_twi_close()
*
* terminate the currect session with the TPM by releasing the locked
* locality. Returns 0 on success of -1 on failure (in case lock
* removal did not succeed).
*/
static int tpm_atmel_twi_close(struct udevice *dev)
{
return 0;
}
/*
* tpm_atmel_twi_get_desc()
*
* @dev: Device to check
* @buf: Buffer to put the string
* @size: Maximum size of buffer
* @return length of string, or -ENOSPC it no space
*/
static int tpm_atmel_twi_get_desc(struct udevice *dev, char *buf, int size)
{
return 0;
}
/*
* tpm_atmel_twi_xfer()
*
* Send the requested data to the TPM and then try to get its response
*
* @sendbuf - buffer of the data to send
* @send_size size of the data to send
* @recvbuf - memory to save the response to
* @recv_len - pointer to the size of the response buffer
*
* Returns 0 on success (and places the number of response bytes at recv_len)
* or -1 on failure.
*/
static int tpm_atmel_twi_xfer(struct udevice *dev,
const uint8_t *sendbuf, size_t send_size,
uint8_t *recvbuf, size_t *recv_len)
{
int res;
unsigned long start;
#ifdef DEBUG
memset(recvbuf, 0xcc, *recv_len);
printf("send to TPM (%d bytes, recv_len=%d):\n", send_size, *recv_len);
print_buffer(0, (void *)sendbuf, 1, send_size, 0);
#endif
#ifndef CONFIG_DM_I2C
res = i2c_write(0x29, 0, 0, (uchar *)sendbuf, send_size);
#else
res = dm_i2c_write(dev, 0, sendbuf, send_size);
#endif
if (res) {
printf("i2c_write returned %d\n", res);
return -1;
}
start = get_timer(0);
#ifndef CONFIG_DM_I2C
while ((res = i2c_read(0x29, 0, 0, recvbuf, 10)))
#else
while ((res = dm_i2c_read(dev, 0, recvbuf, 10)))
#endif
{
/* TODO Use TIS_TIMEOUT from tpm_tis_infineon.h */
if (get_timer(start) > ATMEL_TPM_TIMEOUT_MS) {
puts("tpm timed out\n");
return -1;
}
udelay(100);
}
if (!res) {
unsigned int hdr_recv_len;
hdr_recv_len = get_unaligned_be32(recvbuf + 2);
if (hdr_recv_len < 10) {
puts("tpm response header too small\n");
return -1;
} else if (hdr_recv_len > *recv_len) {
puts("tpm response length is bigger than receive buffer\n");
return -1;
} else {
*recv_len = hdr_recv_len;
#ifndef CONFIG_DM_I2C
res = i2c_read(0x29, 0, 0, recvbuf, *recv_len);
#else
res = dm_i2c_read(dev, 0, recvbuf, *recv_len);
#endif
}
}
if (res) {
printf("i2c_read returned %d (rlen=%d)\n", res, *recv_len);
#ifdef DEBUG
print_buffer(0, recvbuf, 1, *recv_len, 0);
#endif
}
#ifdef DEBUG
if (!res) {
printf("read from TPM (%d bytes):\n", *recv_len);
print_buffer(0, recvbuf, 1, *recv_len, 0);
}
#endif
return res;
}
static int tpm_atmel_twi_probe(struct udevice *dev)
{
return 0;
}
static const struct udevice_id tpm_atmel_twi_ids[] = {
{ .compatible = "atmel,at97sc3204t"},
{ }
};
static const struct tpm_ops tpm_atmel_twi_ops = {
.open = tpm_atmel_twi_open,
.close = tpm_atmel_twi_close,
.xfer = tpm_atmel_twi_xfer,
.get_desc = tpm_atmel_twi_get_desc,
};
U_BOOT_DRIVER(tpm_atmel_twi) = {
.name = "tpm_atmel_twi",
.id = UCLASS_TPM,
.of_match = tpm_atmel_twi_ids,
.ops = &tpm_atmel_twi_ops,
.probe = tpm_atmel_twi_probe,
};
@@ -0,0 +1,286 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright (c) 2015 Google, Inc
*/
#ifndef __tpm_internal_h
#define __tpm_internal_h
enum {
TPM_MAX_ORDINAL = 243,
TPM_MAX_PROTECTED_ORDINAL = 12,
TPM_PROTECTED_ORDINAL_MASK = 0xff,
TPM_CMD_COUNT_BYTE = 2,
TPM_CMD_ORDINAL_BYTE = 6,
};
/*
* Array with one entry per ordinal defining the maximum amount
* of time the chip could take to return the result. The ordinal
* designation of short, medium or long is defined in a table in
* TCG Specification TPM Main Part 2 TPM Structures Section 17. The
* values of the SHORT, MEDIUM, and LONG durations are retrieved
* from the chip during initialization with a call to tpm_get_timeouts.
*/
static const u8 tpm_protected_ordinal_duration[TPM_MAX_PROTECTED_ORDINAL] = {
TPM_UNDEFINED, /* 0 */
TPM_UNDEFINED,
TPM_UNDEFINED,
TPM_UNDEFINED,
TPM_UNDEFINED,
TPM_UNDEFINED, /* 5 */
TPM_UNDEFINED,
TPM_UNDEFINED,
TPM_UNDEFINED,
TPM_UNDEFINED,
TPM_SHORT, /* 10 */
TPM_SHORT,
};
static const u8 tpm_ordinal_duration[TPM_MAX_ORDINAL] = {
TPM_UNDEFINED, /* 0 */
TPM_UNDEFINED,
TPM_UNDEFINED,
TPM_UNDEFINED,
TPM_UNDEFINED,
TPM_UNDEFINED, /* 5 */
TPM_UNDEFINED,
TPM_UNDEFINED,
TPM_UNDEFINED,
TPM_UNDEFINED,
TPM_SHORT, /* 10 */
TPM_SHORT,
TPM_MEDIUM,
TPM_LONG,
TPM_LONG,
TPM_MEDIUM, /* 15 */
TPM_SHORT,
TPM_SHORT,
TPM_MEDIUM,
TPM_LONG,
TPM_SHORT, /* 20 */
TPM_SHORT,
TPM_MEDIUM,
TPM_MEDIUM,
TPM_MEDIUM,
TPM_SHORT, /* 25 */
TPM_SHORT,
TPM_MEDIUM,
TPM_SHORT,
TPM_SHORT,
TPM_MEDIUM, /* 30 */
TPM_LONG,
TPM_MEDIUM,
TPM_SHORT,
TPM_SHORT,
TPM_SHORT, /* 35 */
TPM_MEDIUM,
TPM_MEDIUM,
TPM_UNDEFINED,
TPM_UNDEFINED,
TPM_MEDIUM, /* 40 */
TPM_LONG,
TPM_MEDIUM,
TPM_SHORT,
TPM_SHORT,
TPM_SHORT, /* 45 */
TPM_SHORT,
TPM_SHORT,
TPM_SHORT,
TPM_LONG,
TPM_MEDIUM, /* 50 */
TPM_MEDIUM,
TPM_UNDEFINED,
TPM_UNDEFINED,
TPM_UNDEFINED,
TPM_UNDEFINED, /* 55 */
TPM_UNDEFINED,
TPM_UNDEFINED,
TPM_UNDEFINED,
TPM_UNDEFINED,
TPM_MEDIUM, /* 60 */
TPM_MEDIUM,
TPM_MEDIUM,
TPM_SHORT,
TPM_SHORT,
TPM_MEDIUM, /* 65 */
TPM_UNDEFINED,
TPM_UNDEFINED,
TPM_UNDEFINED,
TPM_UNDEFINED,
TPM_SHORT, /* 70 */
TPM_SHORT,
TPM_UNDEFINED,
TPM_UNDEFINED,
TPM_UNDEFINED,
TPM_UNDEFINED, /* 75 */
TPM_UNDEFINED,
TPM_UNDEFINED,
TPM_UNDEFINED,
TPM_UNDEFINED,
TPM_LONG, /* 80 */
TPM_UNDEFINED,
TPM_MEDIUM,
TPM_LONG,
TPM_SHORT,
TPM_UNDEFINED, /* 85 */
TPM_UNDEFINED,
TPM_UNDEFINED,
TPM_UNDEFINED,
TPM_UNDEFINED,
TPM_SHORT, /* 90 */
TPM_SHORT,
TPM_SHORT,
TPM_SHORT,
TPM_SHORT,
TPM_UNDEFINED, /* 95 */
TPM_UNDEFINED,
TPM_UNDEFINED,
TPM_UNDEFINED,
TPM_UNDEFINED,
TPM_MEDIUM, /* 100 */
TPM_SHORT,
TPM_SHORT,
TPM_UNDEFINED,
TPM_UNDEFINED,
TPM_UNDEFINED, /* 105 */
TPM_UNDEFINED,
TPM_UNDEFINED,
TPM_UNDEFINED,
TPM_UNDEFINED,
TPM_SHORT, /* 110 */
TPM_SHORT,
TPM_SHORT,
TPM_SHORT,
TPM_SHORT,
TPM_SHORT, /* 115 */
TPM_SHORT,
TPM_SHORT,
TPM_UNDEFINED,
TPM_UNDEFINED,
TPM_LONG, /* 120 */
TPM_LONG,
TPM_MEDIUM,
TPM_UNDEFINED,
TPM_SHORT,
TPM_SHORT, /* 125 */
TPM_SHORT,
TPM_LONG,
TPM_SHORT,
TPM_SHORT,
TPM_SHORT, /* 130 */
TPM_MEDIUM,
TPM_UNDEFINED,
TPM_SHORT,
TPM_MEDIUM,
TPM_UNDEFINED, /* 135 */
TPM_UNDEFINED,
TPM_UNDEFINED,
TPM_UNDEFINED,
TPM_UNDEFINED,
TPM_SHORT, /* 140 */
TPM_SHORT,
TPM_UNDEFINED,
TPM_UNDEFINED,
TPM_UNDEFINED,
TPM_UNDEFINED, /* 145 */
TPM_UNDEFINED,
TPM_UNDEFINED,
TPM_UNDEFINED,
TPM_UNDEFINED,
TPM_SHORT, /* 150 */
TPM_MEDIUM,
TPM_MEDIUM,
TPM_SHORT,
TPM_SHORT,
TPM_UNDEFINED, /* 155 */
TPM_UNDEFINED,
TPM_UNDEFINED,
TPM_UNDEFINED,
TPM_UNDEFINED,
TPM_SHORT, /* 160 */
TPM_SHORT,
TPM_SHORT,
TPM_SHORT,
TPM_UNDEFINED,
TPM_UNDEFINED, /* 165 */
TPM_UNDEFINED,
TPM_UNDEFINED,
TPM_UNDEFINED,
TPM_UNDEFINED,
TPM_LONG, /* 170 */
TPM_UNDEFINED,
TPM_UNDEFINED,
TPM_UNDEFINED,
TPM_UNDEFINED,
TPM_UNDEFINED, /* 175 */
TPM_UNDEFINED,
TPM_UNDEFINED,
TPM_UNDEFINED,
TPM_UNDEFINED,
TPM_MEDIUM, /* 180 */
TPM_SHORT,
TPM_MEDIUM,
TPM_MEDIUM,
TPM_MEDIUM,
TPM_MEDIUM, /* 185 */
TPM_SHORT,
TPM_UNDEFINED,
TPM_UNDEFINED,
TPM_UNDEFINED,
TPM_UNDEFINED, /* 190 */
TPM_UNDEFINED,
TPM_UNDEFINED,
TPM_UNDEFINED,
TPM_UNDEFINED,
TPM_UNDEFINED, /* 195 */
TPM_UNDEFINED,
TPM_UNDEFINED,
TPM_UNDEFINED,
TPM_UNDEFINED,
TPM_SHORT, /* 200 */
TPM_UNDEFINED,
TPM_UNDEFINED,
TPM_UNDEFINED,
TPM_SHORT,
TPM_SHORT, /* 205 */
TPM_SHORT,
TPM_SHORT,
TPM_SHORT,
TPM_SHORT,
TPM_MEDIUM, /* 210 */
TPM_UNDEFINED,
TPM_MEDIUM,
TPM_MEDIUM,
TPM_MEDIUM,
TPM_UNDEFINED, /* 215 */
TPM_MEDIUM,
TPM_UNDEFINED,
TPM_UNDEFINED,
TPM_SHORT,
TPM_SHORT, /* 220 */
TPM_SHORT,
TPM_SHORT,
TPM_SHORT,
TPM_SHORT,
TPM_UNDEFINED, /* 225 */
TPM_UNDEFINED,
TPM_UNDEFINED,
TPM_UNDEFINED,
TPM_UNDEFINED,
TPM_SHORT, /* 230 */
TPM_LONG,
TPM_MEDIUM,
TPM_UNDEFINED,
TPM_UNDEFINED,
TPM_UNDEFINED, /* 235 */
TPM_UNDEFINED,
TPM_UNDEFINED,
TPM_UNDEFINED,
TPM_UNDEFINED,
TPM_SHORT, /* 240 */
TPM_UNDEFINED,
TPM_MEDIUM,
};
#endif
@@ -0,0 +1,131 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2011 Infineon Technologies
*
* Authors:
* Peter Huewe <huewe.external@infineon.com>
*
* Version: 2.1.1
*
* Description:
* Device driver for TCG/TCPA TPM (trusted platform module).
* Specifications at www.trustedcomputinggroup.org
*
* It is based on the Linux kernel driver tpm.c from Leendert van
* Dorn, Dave Safford, Reiner Sailer, and Kyleen Hall.
*/
#ifndef _TPM_TIS_I2C_H
#define _TPM_TIS_I2C_H
#include <linux/compiler.h>
#include <linux/types.h>
enum tpm_timeout {
TPM_TIMEOUT_MS = 5,
TIS_SHORT_TIMEOUT_MS = 750,
TIS_LONG_TIMEOUT_MS = 2000,
SLEEP_DURATION_US = 60,
SLEEP_DURATION_LONG_US = 210,
};
/* Size of external transmit buffer (used in tpm_transmit)*/
#define TPM_BUFSIZE 4096
/* Index of Count field in TPM response buffer */
#define TPM_RSP_SIZE_BYTE 2
#define TPM_RSP_RC_BYTE 6
struct tpm_chip {
int is_open;
int locality;
u32 vend_dev;
u8 rid;
unsigned long timeout_a, timeout_b, timeout_c, timeout_d; /* msec */
ulong chip_type;
};
struct tpm_input_header {
__be16 tag;
__be32 length;
__be32 ordinal;
} __packed;
struct tpm_output_header {
__be16 tag;
__be32 length;
__be32 return_code;
} __packed;
struct timeout_t {
__be32 a;
__be32 b;
__be32 c;
__be32 d;
} __packed;
struct duration_t {
__be32 tpm_short;
__be32 tpm_medium;
__be32 tpm_long;
} __packed;
union cap_t {
struct timeout_t timeout;
struct duration_t duration;
};
struct tpm_getcap_params_in {
__be32 cap;
__be32 subcap_size;
__be32 subcap;
} __packed;
struct tpm_getcap_params_out {
__be32 cap_size;
union cap_t cap;
} __packed;
union tpm_cmd_header {
struct tpm_input_header in;
struct tpm_output_header out;
};
union tpm_cmd_params {
struct tpm_getcap_params_out getcap_out;
struct tpm_getcap_params_in getcap_in;
};
struct tpm_cmd_t {
union tpm_cmd_header header;
union tpm_cmd_params params;
} __packed;
/* Max number of iterations after i2c NAK */
#define MAX_COUNT 3
/*
* Max number of iterations after i2c NAK for 'long' commands
*
* We need this especially for sending TPM_READY, since the cleanup after the
* transtion to the ready state may take some time, but it is unpredictable
* how long it will take.
*/
#define MAX_COUNT_LONG 50
enum tis_access {
TPM_ACCESS_VALID = 0x80,
TPM_ACCESS_ACTIVE_LOCALITY = 0x20,
TPM_ACCESS_REQUEST_PENDING = 0x04,
TPM_ACCESS_REQUEST_USE = 0x02,
};
enum tis_status {
TPM_STS_VALID = 0x80,
TPM_STS_COMMAND_READY = 0x40,
TPM_STS_GO = 0x20,
TPM_STS_DATA_AVAIL = 0x10,
TPM_STS_DATA_EXPECT = 0x08,
};
#endif
@@ -0,0 +1,635 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2011 Infineon Technologies
*
* Authors:
* Peter Huewe <huewe.external@infineon.com>
*
* Description:
* Device driver for TCG/TCPA TPM (trusted platform module).
* Specifications at www.trustedcomputinggroup.org
*
* This device driver implements the TPM interface as defined in
* the TCG TPM Interface Spec version 1.2, revision 1.0 and the
* Infineon I2C Protocol Stack Specification v0.20.
*
* It is based on the Linux kernel driver tpm.c from Leendert van
* Dorn, Dave Safford, Reiner Sailer, and Kyleen Hall.
*
* Version: 2.1.1
*/
#include <common.h>
#include <dm.h>
#include <fdtdec.h>
#include <i2c.h>
#include <tpm-v1.h>
#include <linux/errno.h>
#include <linux/compiler.h>
#include <linux/types.h>
#include <linux/unaligned/be_byteshift.h>
#include "tpm_tis.h"
#include "tpm_internal.h"
enum i2c_chip_type {
SLB9635,
SLB9645,
UNKNOWN,
};
/* expected value for DIDVID register */
#define TPM_TIS_I2C_DID_VID_9635 0x000b15d1L
#define TPM_TIS_I2C_DID_VID_9645 0x001a15d1L
static const char * const chip_name[] = {
[SLB9635] = "slb9635tt",
[SLB9645] = "slb9645tt",
[UNKNOWN] = "unknown/fallback to slb9635",
};
#define TPM_ACCESS(l) (0x0000 | ((l) << 4))
#define TPM_STS(l) (0x0001 | ((l) << 4))
#define TPM_DATA_FIFO(l) (0x0005 | ((l) << 4))
#define TPM_DID_VID(l) (0x0006 | ((l) << 4))
/*
* tpm_tis_i2c_read() - read from TPM register
* @addr: register address to read from
* @buffer: provided by caller
* @len: number of bytes to read
*
* Read len bytes from TPM register and put them into
* buffer (little-endian format, i.e. first byte is put into buffer[0]).
*
* NOTE: TPM is big-endian for multi-byte values. Multi-byte
* values have to be swapped.
*
* Return -EIO on error, 0 on success.
*/
static int tpm_tis_i2c_read(struct udevice *dev, u8 addr, u8 *buffer,
size_t len)
{
struct tpm_chip *chip = dev_get_priv(dev);
int rc;
int count;
uint32_t addrbuf = addr;
if ((chip->chip_type == SLB9635) || (chip->chip_type == UNKNOWN)) {
/* slb9635 protocol should work in both cases */
for (count = 0; count < MAX_COUNT; count++) {
rc = dm_i2c_write(dev, 0, (uchar *)&addrbuf, 1);
if (rc == 0)
break; /* Success, break to skip sleep */
udelay(SLEEP_DURATION_US);
}
if (rc)
return rc;
/* After the TPM has successfully received the register address
* it needs some time, thus we're sleeping here again, before
* retrieving the data
*/
for (count = 0; count < MAX_COUNT; count++) {
udelay(SLEEP_DURATION_US);
rc = dm_i2c_read(dev, 0, buffer, len);
if (rc == 0)
break; /* success, break to skip sleep */
}
} else {
/*
* Use a combined read for newer chips.
* Unfortunately the smbus functions are not suitable due to
* the 32 byte limit of the smbus.
* Retries should usually not be needed, but are kept just to
* be safe on the safe side.
*/
for (count = 0; count < MAX_COUNT; count++) {
rc = dm_i2c_read(dev, addr, buffer, len);
if (rc == 0)
break; /* break here to skip sleep */
udelay(SLEEP_DURATION_US);
}
}
/* Take care of 'guard time' */
udelay(SLEEP_DURATION_US);
if (rc)
return rc;
return 0;
}
static int tpm_tis_i2c_write_generic(struct udevice *dev, u8 addr,
const u8 *buffer, size_t len,
unsigned int sleep_time_us, u8 max_count)
{
struct tpm_chip_priv *priv = dev_get_uclass_priv(dev);
struct tpm_chip *chip = dev_get_priv(dev);
int rc = 0;
int count;
if (chip->chip_type == SLB9635) {
/* Prepare send buffer to include the address */
priv->buf[0] = addr;
memcpy(&(priv->buf[1]), buffer, len);
buffer = priv->buf;
len++;
addr = 0;
}
for (count = 0; count < max_count; count++) {
rc = dm_i2c_write(dev, addr, buffer, len);
if (rc == 0)
break; /* Success, break to skip sleep */
udelay(sleep_time_us);
}
/* take care of 'guard time' */
udelay(sleep_time_us);
if (rc)
return rc;
return 0;
}
/*
* tpm_tis_i2c_write() - write to TPM register
* @addr: register address to write to
* @buffer: containing data to be written
* @len: number of bytes to write
*
* Write len bytes from provided buffer to TPM register (little
* endian format, i.e. buffer[0] is written as first byte).
*
* NOTE: TPM is big-endian for multi-byte values. Multi-byte
* values have to be swapped.
*
* NOTE: use this function instead of the tpm_tis_i2c_write_generic function.
*
* Return -EIO on error, 0 on success
*/
static int tpm_tis_i2c_write(struct udevice *dev, u8 addr, const u8 *buffer,
size_t len)
{
return tpm_tis_i2c_write_generic(dev, addr, buffer, len,
SLEEP_DURATION_US, MAX_COUNT);
}
/*
* This function is needed especially for the cleanup situation after
* sending TPM_READY
*/
static int tpm_tis_i2c_write_long(struct udevice *dev, u8 addr, u8 *buffer,
size_t len)
{
return tpm_tis_i2c_write_generic(dev, addr, buffer, len,
SLEEP_DURATION_LONG_US,
MAX_COUNT_LONG);
}
static int tpm_tis_i2c_check_locality(struct udevice *dev, int loc)
{
const u8 mask = TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID;
struct tpm_chip *chip = dev_get_priv(dev);
u8 buf;
int rc;
rc = tpm_tis_i2c_read(dev, TPM_ACCESS(loc), &buf, 1);
if (rc < 0)
return rc;
if ((buf & mask) == mask) {
chip->locality = loc;
return loc;
}
return -ENOENT;
}
static void tpm_tis_i2c_release_locality(struct udevice *dev, int loc,
int force)
{
const u8 mask = TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID;
u8 buf;
if (tpm_tis_i2c_read(dev, TPM_ACCESS(loc), &buf, 1) < 0)
return;
if (force || (buf & mask) == mask) {
buf = TPM_ACCESS_ACTIVE_LOCALITY;
tpm_tis_i2c_write(dev, TPM_ACCESS(loc), &buf, 1);
}
}
static int tpm_tis_i2c_request_locality(struct udevice *dev, int loc)
{
struct tpm_chip *chip = dev_get_priv(dev);
unsigned long start, stop;
u8 buf = TPM_ACCESS_REQUEST_USE;
int rc;
rc = tpm_tis_i2c_check_locality(dev, loc);
if (rc >= 0) {
debug("%s: Already have locality\n", __func__);
return loc; /* We already have the locality */
} else if (rc != -ENOENT) {
debug("%s: Failed to get locality: %d\n", __func__, rc);
return rc;
}
rc = tpm_tis_i2c_write(dev, TPM_ACCESS(loc), &buf, 1);
if (rc) {
debug("%s: Failed to write to TPM: %d\n", __func__, rc);
return rc;
}
/* Wait for burstcount */
start = get_timer(0);
stop = chip->timeout_a;
do {
rc = tpm_tis_i2c_check_locality(dev, loc);
if (rc >= 0) {
debug("%s: Have locality\n", __func__);
return loc;
} else if (rc != -ENOENT) {
debug("%s: Failed to get locality: %d\n", __func__, rc);
return rc;
}
mdelay(TPM_TIMEOUT_MS);
} while (get_timer(start) < stop);
debug("%s: Timeout getting locality: %d\n", __func__, rc);
return rc;
}
static u8 tpm_tis_i2c_status(struct udevice *dev)
{
struct tpm_chip *chip = dev_get_priv(dev);
/* NOTE: Since i2c read may fail, return 0 in this case --> time-out */
u8 buf;
if (tpm_tis_i2c_read(dev, TPM_STS(chip->locality), &buf, 1) < 0)
return 0;
else
return buf;
}
static int tpm_tis_i2c_ready(struct udevice *dev)
{
struct tpm_chip *chip = dev_get_priv(dev);
int rc;
/* This causes the current command to be aborted */
u8 buf = TPM_STS_COMMAND_READY;
debug("%s\n", __func__);
rc = tpm_tis_i2c_write_long(dev, TPM_STS(chip->locality), &buf, 1);
if (rc)
debug("%s: rc=%d\n", __func__, rc);
return rc;
}
static ssize_t tpm_tis_i2c_get_burstcount(struct udevice *dev)
{
struct tpm_chip *chip = dev_get_priv(dev);
unsigned long start, stop;
ssize_t burstcnt;
u8 addr, buf[3];
/* Wait for burstcount */
/* XXX: Which timeout value? Spec has 2 answers (c & d) */
start = get_timer(0);
stop = chip->timeout_d;
do {
/* Note: STS is little endian */
addr = TPM_STS(chip->locality) + 1;
if (tpm_tis_i2c_read(dev, addr, buf, 3) < 0)
burstcnt = 0;
else
burstcnt = (buf[2] << 16) + (buf[1] << 8) + buf[0];
if (burstcnt)
return burstcnt;
mdelay(TPM_TIMEOUT_MS);
} while (get_timer(start) < stop);
return -EBUSY;
}
static int tpm_tis_i2c_wait_for_stat(struct udevice *dev, u8 mask,
unsigned long timeout, int *status)
{
unsigned long start, stop;
/* Check current status */
*status = tpm_tis_i2c_status(dev);
if ((*status & mask) == mask)
return 0;
start = get_timer(0);
stop = timeout;
do {
mdelay(TPM_TIMEOUT_MS);
*status = tpm_tis_i2c_status(dev);
if ((*status & mask) == mask)
return 0;
} while (get_timer(start) < stop);
return -ETIMEDOUT;
}
static int tpm_tis_i2c_recv_data(struct udevice *dev, u8 *buf, size_t count)
{
struct tpm_chip *chip = dev_get_priv(dev);
size_t size = 0;
ssize_t burstcnt;
int rc;
while (size < count) {
burstcnt = tpm_tis_i2c_get_burstcount(dev);
/* burstcount < 0 -> tpm is busy */
if (burstcnt < 0)
return burstcnt;
/* Limit received data to max left */
if (burstcnt > (count - size))
burstcnt = count - size;
rc = tpm_tis_i2c_read(dev, TPM_DATA_FIFO(chip->locality),
&(buf[size]), burstcnt);
if (rc == 0)
size += burstcnt;
}
return size;
}
static int tpm_tis_i2c_recv(struct udevice *dev, u8 *buf, size_t count)
{
struct tpm_chip *chip = dev_get_priv(dev);
int size = 0;
int status;
unsigned int expected;
int rc;
status = tpm_tis_i2c_status(dev);
if (status == TPM_STS_COMMAND_READY)
return -EINTR;
if ((status & (TPM_STS_DATA_AVAIL | TPM_STS_VALID)) !=
(TPM_STS_DATA_AVAIL | TPM_STS_VALID))
return -EAGAIN;
debug("...got it;\n");
/* Read first 10 bytes, including tag, paramsize, and result */
size = tpm_tis_i2c_recv_data(dev, buf, TPM_HEADER_SIZE);
if (size < TPM_HEADER_SIZE) {
debug("Unable to read header\n");
return size < 0 ? size : -EIO;
}
expected = get_unaligned_be32(buf + TPM_RSP_SIZE_BYTE);
if ((size_t)expected > count || (size_t)expected < TPM_HEADER_SIZE) {
debug("Error size=%x, expected=%x, count=%x\n", size, expected,
count);
return -ENOSPC;
}
size += tpm_tis_i2c_recv_data(dev, &buf[TPM_HEADER_SIZE],
expected - TPM_HEADER_SIZE);
if (size < expected) {
debug("Unable to read remainder of result\n");
return -ETIMEDOUT;
}
rc = tpm_tis_i2c_wait_for_stat(dev, TPM_STS_VALID, chip->timeout_c,
&status);
if (rc)
return rc;
if (status & TPM_STS_DATA_AVAIL) { /* Retry? */
debug("Error left over data\n");
return -EIO;
}
return size;
}
static int tpm_tis_i2c_send(struct udevice *dev, const u8 *buf, size_t len)
{
struct tpm_chip *chip = dev_get_priv(dev);
int rc, status;
size_t burstcnt;
size_t count = 0;
int retry = 0;
u8 sts = TPM_STS_GO;
debug("%s: len=%d\n", __func__, len);
if (len > TPM_DEV_BUFSIZE)
return -E2BIG; /* Command is too long for our tpm, sorry */
if (tpm_tis_i2c_request_locality(dev, 0) < 0)
return -EBUSY;
status = tpm_tis_i2c_status(dev);
if ((status & TPM_STS_COMMAND_READY) == 0) {
rc = tpm_tis_i2c_ready(dev);
if (rc)
return rc;
rc = tpm_tis_i2c_wait_for_stat(dev, TPM_STS_COMMAND_READY,
chip->timeout_b, &status);
if (rc)
return rc;
}
burstcnt = tpm_tis_i2c_get_burstcount(dev);
/* burstcount < 0 -> tpm is busy */
if (burstcnt < 0)
return burstcnt;
while (count < len) {
udelay(300);
if (burstcnt > len - count)
burstcnt = len - count;
#ifdef CONFIG_TPM_TIS_I2C_BURST_LIMITATION
if (retry && burstcnt > CONFIG_TPM_TIS_I2C_BURST_LIMITATION_LEN)
burstcnt = CONFIG_TPM_TIS_I2C_BURST_LIMITATION_LEN;
#endif /* CONFIG_TPM_TIS_I2C_BURST_LIMITATION */
rc = tpm_tis_i2c_write(dev, TPM_DATA_FIFO(chip->locality),
&(buf[count]), burstcnt);
if (rc == 0)
count += burstcnt;
else {
debug("%s: error\n", __func__);
if (retry++ > 10)
return -EIO;
rc = tpm_tis_i2c_wait_for_stat(dev, TPM_STS_VALID,
chip->timeout_c,
&status);
if (rc)
return rc;
if ((status & TPM_STS_DATA_EXPECT) == 0)
return -EIO;
}
}
/* Go and do it */
rc = tpm_tis_i2c_write(dev, TPM_STS(chip->locality), &sts, 1);
if (rc < 0)
return rc;
debug("%s: done, rc=%d\n", __func__, rc);
return len;
}
static int tpm_tis_i2c_cleanup(struct udevice *dev)
{
struct tpm_chip *chip = dev_get_priv(dev);
tpm_tis_i2c_ready(dev);
/*
* The TPM needs some time to clean up here,
* so we sleep rather than keeping the bus busy
*/
mdelay(2);
tpm_tis_i2c_release_locality(dev, chip->locality, 0);
return 0;
}
static int tpm_tis_i2c_init(struct udevice *dev)
{
struct tpm_chip *chip = dev_get_priv(dev);
u32 vendor;
u32 expected_did_vid;
int rc;
chip->is_open = 1;
/* Default timeouts - these could move to the device tree */
chip->timeout_a = TIS_SHORT_TIMEOUT_MS;
chip->timeout_b = TIS_LONG_TIMEOUT_MS;
chip->timeout_c = TIS_SHORT_TIMEOUT_MS;
chip->timeout_d = TIS_SHORT_TIMEOUT_MS;
rc = tpm_tis_i2c_request_locality(dev, 0);
if (rc < 0)
return rc;
/* Read four bytes from DID_VID register */
if (tpm_tis_i2c_read(dev, TPM_DID_VID(0), (uchar *)&vendor, 4) < 0) {
tpm_tis_i2c_release_locality(dev, 0, 1);
return -EIO;
}
if (chip->chip_type == SLB9635) {
vendor = be32_to_cpu(vendor);
expected_did_vid = TPM_TIS_I2C_DID_VID_9635;
} else {
/* device id and byte order has changed for newer i2c tpms */
expected_did_vid = TPM_TIS_I2C_DID_VID_9645;
}
if (chip->chip_type != UNKNOWN && vendor != expected_did_vid) {
pr_err("Vendor id did not match! ID was %08x\n", vendor);
return -ENODEV;
}
chip->vend_dev = vendor;
debug("1.2 TPM (chip type %s device-id 0x%X)\n",
chip_name[chip->chip_type], vendor >> 16);
/*
* A timeout query to TPM can be placed here.
* Standard timeout values are used so far
*/
return 0;
}
static int tpm_tis_i2c_open(struct udevice *dev)
{
struct tpm_chip *chip = dev_get_priv(dev);
int rc;
debug("%s: start\n", __func__);
if (chip->is_open)
return -EBUSY;
rc = tpm_tis_i2c_init(dev);
if (rc < 0)
chip->is_open = 0;
return rc;
}
static int tpm_tis_i2c_close(struct udevice *dev)
{
struct tpm_chip *chip = dev_get_priv(dev);
if (chip->is_open) {
tpm_tis_i2c_release_locality(dev, chip->locality, 1);
chip->is_open = 0;
chip->vend_dev = 0;
}
return 0;
}
static int tpm_tis_get_desc(struct udevice *dev, char *buf, int size)
{
struct tpm_chip *chip = dev_get_priv(dev);
if (size < 50)
return -ENOSPC;
return snprintf(buf, size, "1.2 TPM (%s, chip type %s device-id 0x%x)",
chip->is_open ? "open" : "closed",
chip_name[chip->chip_type],
chip->vend_dev >> 16);
}
static int tpm_tis_i2c_probe(struct udevice *dev)
{
struct tpm_chip_priv *uc_priv = dev_get_uclass_priv(dev);
struct tpm_chip *chip = dev_get_priv(dev);
chip->chip_type = dev_get_driver_data(dev);
/* TODO: These need to be checked and tuned */
uc_priv->duration_ms[TPM_SHORT] = TIS_SHORT_TIMEOUT_MS;
uc_priv->duration_ms[TPM_MEDIUM] = TIS_LONG_TIMEOUT_MS;
uc_priv->duration_ms[TPM_LONG] = TIS_LONG_TIMEOUT_MS;
uc_priv->retry_time_ms = TPM_TIMEOUT_MS;
return 0;
}
static const struct tpm_ops tpm_tis_i2c_ops = {
.open = tpm_tis_i2c_open,
.close = tpm_tis_i2c_close,
.get_desc = tpm_tis_get_desc,
.send = tpm_tis_i2c_send,
.recv = tpm_tis_i2c_recv,
.cleanup = tpm_tis_i2c_cleanup,
};
static const struct udevice_id tpm_tis_i2c_ids[] = {
{ .compatible = "infineon,slb9635tt", .data = SLB9635 },
{ .compatible = "infineon,slb9645tt", .data = SLB9645 },
{ }
};
U_BOOT_DRIVER(tpm_tis_i2c) = {
.name = "tpm_tis_infineon",
.id = UCLASS_TPM,
.of_match = tpm_tis_i2c_ids,
.ops = &tpm_tis_i2c_ops,
.probe = tpm_tis_i2c_probe,
.priv_auto_alloc_size = sizeof(struct tpm_chip),
};
@@ -0,0 +1,477 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2011 The Chromium OS Authors.
*/
/*
* The code in this file is based on the article "Writing a TPM Device Driver"
* published on http://ptgmedia.pearsoncmg.com.
*
* One principal difference is that in the simplest config the other than 0
* TPM localities do not get mapped by some devices (for instance, by Infineon
* slb9635), so this driver provides access to locality 0 only.
*/
#include <common.h>
#include <dm.h>
#include <mapmem.h>
#include <tpm-v1.h>
#include <asm/io.h>
#define PREFIX "lpc_tpm: "
enum i2c_chip_type {
SLB9635,
AT97SC3204,
};
static const char * const chip_name[] = {
[SLB9635] = "Infineon SLB9635 TT 1.2",
[AT97SC3204] = "Atmel AT97SC3204",
};
static const u32 chip_didvid[] = {
[SLB9635] = 0xb15d1,
[AT97SC3204] = 0x32041114,
};
struct tpm_locality {
u32 access;
u8 padding0[4];
u32 int_enable;
u8 vector;
u8 padding1[3];
u32 int_status;
u32 int_capability;
u32 tpm_status;
u8 padding2[8];
u8 data;
u8 padding3[3803];
u32 did_vid;
u8 rid;
u8 padding4[251];
};
struct tpm_tis_lpc_priv {
struct tpm_locality *regs;
};
/*
* This pointer refers to the TPM chip, 5 of its localities are mapped as an
* array.
*/
#define TPM_TOTAL_LOCALITIES 5
/* 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)
/* 1 second is plenty for anything TPM does. */
#define MAX_DELAY_US (1000 * 1000)
/* Retrieve burst count value out of the status register contents. */
static u16 burst_count(u32 status)
{
return (status >> TIS_STS_BURST_COUNT_SHIFT) &
TIS_STS_BURST_COUNT_MASK;
}
/* TPM access wrappers to support tracing */
static u8 tpm_read_byte(struct tpm_tis_lpc_priv *priv, const u8 *ptr)
{
u8 ret = readb(ptr);
debug(PREFIX "Read reg 0x%4.4x returns 0x%2.2x\n",
(u32)(uintptr_t)ptr - (u32)(uintptr_t)priv->regs, ret);
return ret;
}
static u32 tpm_read_word(struct tpm_tis_lpc_priv *priv, const u32 *ptr)
{
u32 ret = readl(ptr);
debug(PREFIX "Read reg 0x%4.4x returns 0x%8.8x\n",
(u32)(uintptr_t)ptr - (u32)(uintptr_t)priv->regs, ret);
return ret;
}
static void tpm_write_byte(struct tpm_tis_lpc_priv *priv, u8 value, u8 *ptr)
{
debug(PREFIX "Write reg 0x%4.4x with 0x%2.2x\n",
(u32)(uintptr_t)ptr - (u32)(uintptr_t)priv->regs, value);
writeb(value, ptr);
}
static void tpm_write_word(struct tpm_tis_lpc_priv *priv, u32 value,
u32 *ptr)
{
debug(PREFIX "Write reg 0x%4.4x with 0x%8.8x\n",
(u32)(uintptr_t)ptr - (u32)(uintptr_t)priv->regs, value);
writel(value, ptr);
}
/*
* tis_wait_reg()
*
* Wait for at least a second for a register to change its state to match the
* expected state. Normally the transition happens within microseconds.
*
* @reg - pointer to the TPM register
* @mask - bitmask for the bitfield(s) to watch
* @expected - value the field(s) are supposed to be set to
*
* Returns the register contents in case the expected value was found in the
* appropriate register bits, or -ETIMEDOUT on timeout.
*/
static int tis_wait_reg(struct tpm_tis_lpc_priv *priv, u32 *reg, u8 mask,
u8 expected)
{
u32 time_us = MAX_DELAY_US;
while (time_us > 0) {
u32 value = tpm_read_word(priv, reg);
if ((value & mask) == expected)
return value;
udelay(1); /* 1 us */
time_us--;
}
return -ETIMEDOUT;
}
/*
* Probe the TPM device and try determining its manufacturer/device name.
*
* Returns 0 on success, -ve on error
*/
static int tpm_tis_lpc_probe(struct udevice *dev)
{
struct tpm_tis_lpc_priv *priv = dev_get_priv(dev);
fdt_addr_t addr;
u32 didvid;
ulong chip_type = dev_get_driver_data(dev);
addr = dev_read_addr(dev);
if (addr == FDT_ADDR_T_NONE)
return -EINVAL;
priv->regs = map_sysmem(addr, 0);
didvid = tpm_read_word(priv, &priv->regs[0].did_vid);
if (didvid != chip_didvid[chip_type]) {
u32 vid, did;
vid = didvid & 0xffff;
did = (didvid >> 16) & 0xffff;
debug("Invalid vendor/device ID %04x/%04x\n", vid, did);
return -ENODEV;
}
debug("Found TPM: %s\n", chip_name[chip_type]);
return 0;
}
/*
* tis_senddata()
*
* send the passed in data to the TPM device.
*
* @data - address of the data to send, byte by byte
* @len - length of the data to send
*
* Returns 0 on success, -ve on error (in case the device does not accept
* the entire command).
*/
static int tis_senddata(struct udevice *dev, const u8 *data, size_t len)
{
struct tpm_tis_lpc_priv *priv = dev_get_priv(dev);
struct tpm_locality *regs = priv->regs;
u32 offset = 0;
u16 burst = 0;
u32 max_cycles = 0;
u8 locality = 0;
u32 value;
value = tis_wait_reg(priv, &regs[locality].tpm_status,
TIS_STS_COMMAND_READY, TIS_STS_COMMAND_READY);
if (value == -ETIMEDOUT) {
printf("%s:%d - failed to get 'command_ready' status\n",
__FILE__, __LINE__);
return value;
}
burst = burst_count(value);
while (1) {
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 %zd bytes of %zd\n",
__FILE__, __LINE__, len - offset, len);
return -ETIMEDOUT;
}
udelay(1);
burst = burst_count(tpm_read_word(priv,
&regs[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((size_t)burst, len - offset - 1);
while (count--)
tpm_write_byte(priv, data[offset++],
&regs[locality].data);
value = tis_wait_reg(priv, &regs[locality].tpm_status,
TIS_STS_VALID, TIS_STS_VALID);
if ((value == -ETIMEDOUT) || !(value & TIS_STS_EXPECT)) {
printf("%s:%d TPM command feed overflow\n",
__FILE__, __LINE__);
return value == -ETIMEDOUT ? value : -EIO;
}
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_byte(priv, data[offset++], &regs[locality].data);
/*
* Verify that TPM does not expect any more data as part of this
* command.
*/
value = tis_wait_reg(priv, &regs[locality].tpm_status,
TIS_STS_VALID, TIS_STS_VALID);
if ((value == -ETIMEDOUT) || (value & TIS_STS_EXPECT)) {
printf("%s:%d unexpected TPM status 0x%x\n",
__FILE__, __LINE__, value);
return value == -ETIMEDOUT ? value : -EIO;
}
/* OK, sitting pretty, let's start the command execution. */
tpm_write_word(priv, TIS_STS_TPM_GO, &regs[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
* -ve value.
*/
static int tis_readresponse(struct udevice *dev, u8 *buffer, size_t len)
{
struct tpm_tis_lpc_priv *priv = dev_get_priv(dev);
struct tpm_locality *regs = priv->regs;
u16 burst;
u32 value;
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. */
value = tis_wait_reg(priv, &regs[locality].tpm_status,
has_data, has_data);
if (value == -ETIMEDOUT) {
printf("%s:%d failed processing command\n",
__FILE__, __LINE__);
return value;
}
do {
while ((burst = burst_count(value)) == 0) {
if (max_cycles++ == MAX_DELAY_US) {
printf("%s:%d TPM stuck on read\n",
__FILE__, __LINE__);
return -EIO;
}
udelay(1);
value = tpm_read_word(priv, &regs[locality].tpm_status);
}
max_cycles = 0;
while (burst-- && (offset < expected_count)) {
buffer[offset++] = tpm_read_byte(priv,
&regs[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 -ENOSPC;
}
}
}
/* Wait for the next portion. */
value = tis_wait_reg(priv, &regs[locality].tpm_status,
TIS_STS_VALID, TIS_STS_VALID);
if (value == -ETIMEDOUT) {
printf("%s:%d failed to read response\n",
__FILE__, __LINE__);
return value;
}
if (offset == expected_count)
break; /* We got all we needed. */
} while ((value & has_data) == has_data);
/*
* Make sure we indeed read all there was. The TIS_STS_VALID bit is
* known to be set.
*/
if (value & TIS_STS_DATA_AVAILABLE) {
printf("%s:%d wrong receive status %x\n",
__FILE__, __LINE__, value);
return -EBADMSG;
}
/* Tell the TPM that we are done. */
tpm_write_word(priv, TIS_STS_COMMAND_READY,
&regs[locality].tpm_status);
return offset;
}
static int tpm_tis_lpc_close(struct udevice *dev)
{
struct tpm_tis_lpc_priv *priv = dev_get_priv(dev);
struct tpm_locality *regs = priv->regs;
u8 locality = 0;
if (tpm_read_word(priv, &regs[locality].access) &
TIS_ACCESS_ACTIVE_LOCALITY) {
tpm_write_word(priv, TIS_ACCESS_ACTIVE_LOCALITY,
&regs[locality].access);
if (tis_wait_reg(priv, &regs[locality].access,
TIS_ACCESS_ACTIVE_LOCALITY, 0) == -ETIMEDOUT) {
printf("%s:%d - failed to release locality %d\n",
__FILE__, __LINE__, locality);
return -ETIMEDOUT;
}
}
return 0;
}
static int tpm_tis_lpc_open(struct udevice *dev)
{
struct tpm_tis_lpc_priv *priv = dev_get_priv(dev);
struct tpm_locality *regs = priv->regs;
u8 locality = 0; /* we use locality zero for everything. */
int ret;
ret = tpm_tis_lpc_close(dev);
if (ret) {
printf("%s: Failed to close TPM\n", __func__);
return ret;
}
/* now request access to locality. */
tpm_write_word(priv, TIS_ACCESS_REQUEST_USE, &regs[locality].access);
/* did we get a lock? */
ret = tis_wait_reg(priv, &regs[locality].access,
TIS_ACCESS_ACTIVE_LOCALITY,
TIS_ACCESS_ACTIVE_LOCALITY);
if (ret == -ETIMEDOUT) {
printf("%s:%d - failed to lock locality %d\n",
__FILE__, __LINE__, locality);
return ret;
}
tpm_write_word(priv, TIS_STS_COMMAND_READY,
&regs[locality].tpm_status);
return 0;
}
static int tpm_tis_get_desc(struct udevice *dev, char *buf, int size)
{
ulong chip_type = dev_get_driver_data(dev);
if (size < 50)
return -ENOSPC;
return snprintf(buf, size, "1.2 TPM (%s)",
chip_name[chip_type]);
}
static const struct tpm_ops tpm_tis_lpc_ops = {
.open = tpm_tis_lpc_open,
.close = tpm_tis_lpc_close,
.get_desc = tpm_tis_get_desc,
.send = tis_senddata,
.recv = tis_readresponse,
};
static const struct udevice_id tpm_tis_lpc_ids[] = {
{ .compatible = "infineon,slb9635lpc", .data = SLB9635 },
{ .compatible = "atmel,at97sc3204", .data = AT97SC3204 },
{ }
};
U_BOOT_DRIVER(tpm_tis_lpc) = {
.name = "tpm_tis_lpc",
.id = UCLASS_TPM,
.of_match = tpm_tis_lpc_ids,
.ops = &tpm_tis_lpc_ops,
.probe = tpm_tis_lpc_probe,
.priv_auto_alloc_size = sizeof(struct tpm_tis_lpc_priv),
};
@@ -0,0 +1,365 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2013 Google, Inc
*/
#include <common.h>
#include <dm.h>
#include <tpm-v1.h>
#include <asm/state.h>
#include <asm/unaligned.h>
#include <u-boot/crc.h>
/* TPM NVRAM location indices. */
#define FIRMWARE_NV_INDEX 0x1007
#define KERNEL_NV_INDEX 0x1008
#define BACKUP_NV_INDEX 0x1009
#define FWMP_NV_INDEX 0x100a
#define REC_HASH_NV_INDEX 0x100b
#define REC_HASH_NV_SIZE VB2_SHA256_DIGEST_SIZE
#define NV_DATA_PUBLIC_PERMISSIONS_OFFSET 60
/* Kernel TPM space - KERNEL_NV_INDEX, locked with physical presence */
#define ROLLBACK_SPACE_KERNEL_VERSION 2
#define ROLLBACK_SPACE_KERNEL_UID 0x4752574C /* 'GRWL' */
struct rollback_space_kernel {
/* Struct version, for backwards compatibility */
uint8_t struct_version;
/* Unique ID to detect space redefinition */
uint32_t uid;
/* Kernel versions */
uint32_t kernel_versions;
/* Reserved for future expansion */
uint8_t reserved[3];
/* Checksum (v2 and later only) */
uint8_t crc8;
} __packed rollback_space_kernel;
/*
* These numbers derive from adding the sizes of command fields as shown in
* the TPM commands manual.
*/
#define TPM_REQUEST_HEADER_LENGTH 10
#define TPM_RESPONSE_HEADER_LENGTH 10
/* These are the different non-volatile spaces that we emulate */
enum {
NV_GLOBAL_LOCK,
NV_SEQ_FIRMWARE,
NV_SEQ_KERNEL,
NV_SEQ_BACKUP,
NV_SEQ_FWMP,
NV_SEQ_REC_HASH,
NV_SEQ_COUNT,
};
/* Size of each non-volatile space */
#define NV_DATA_SIZE 0x20
struct nvdata_state {
bool present;
u8 data[NV_DATA_SIZE];
};
/*
* Information about our TPM emulation. This is preserved in the sandbox
* state file if enabled.
*/
static struct tpm_state {
bool valid;
struct nvdata_state nvdata[NV_SEQ_COUNT];
} g_state;
/**
* sandbox_tpm_read_state() - read the sandbox EC state from the state file
*
* If data is available, then blob and node will provide access to it. If
* not this function sets up an empty TPM.
*
* @blob: Pointer to device tree blob, or NULL if no data to read
* @node: Node offset to read from
*/
static int sandbox_tpm_read_state(const void *blob, int node)
{
const char *prop;
int len;
int i;
if (!blob)
return 0;
for (i = 0; i < NV_SEQ_COUNT; i++) {
char prop_name[20];
sprintf(prop_name, "nvdata%d", i);
prop = fdt_getprop(blob, node, prop_name, &len);
if (prop && len == NV_DATA_SIZE) {
memcpy(g_state.nvdata[i].data, prop, NV_DATA_SIZE);
g_state.nvdata[i].present = true;
}
}
g_state.valid = true;
return 0;
}
/**
* cros_ec_write_state() - Write out our state to the state file
*
* The caller will ensure that there is a node ready for the state. The node
* may already contain the old state, in which case it is overridden.
*
* @blob: Device tree blob holding state
* @node: Node to write our state into
*/
static int sandbox_tpm_write_state(void *blob, int node)
{
int i;
/*
* We are guaranteed enough space to write basic properties.
* We could use fdt_add_subnode() to put each set of data in its
* own node - perhaps useful if we add access informaiton to each.
*/
for (i = 0; i < NV_SEQ_COUNT; i++) {
char prop_name[20];
if (g_state.nvdata[i].present) {
sprintf(prop_name, "nvdata%d", i);
fdt_setprop(blob, node, prop_name,
g_state.nvdata[i].data, NV_DATA_SIZE);
}
}
return 0;
}
SANDBOX_STATE_IO(sandbox_tpm, "google,sandbox-tpm", sandbox_tpm_read_state,
sandbox_tpm_write_state);
static int index_to_seq(uint32_t index)
{
switch (index) {
case FIRMWARE_NV_INDEX:
return NV_SEQ_FIRMWARE;
case KERNEL_NV_INDEX:
return NV_SEQ_KERNEL;
case BACKUP_NV_INDEX:
return NV_SEQ_BACKUP;
case FWMP_NV_INDEX:
return NV_SEQ_FWMP;
case REC_HASH_NV_INDEX:
return NV_SEQ_REC_HASH;
case 0:
return NV_GLOBAL_LOCK;
}
printf("Invalid nv index %#x\n", index);
return -1;
}
static void handle_cap_flag_space(u8 **datap, uint index)
{
struct tpm_nv_data_public pub;
/* TPM_NV_PER_PPWRITE */
memset(&pub, '\0', sizeof(pub));
pub.nv_index = __cpu_to_be32(index);
pub.pcr_info_read.pcr_selection.size_of_select = __cpu_to_be16(
sizeof(pub.pcr_info_read.pcr_selection.pcr_select));
pub.permission.attributes = __cpu_to_be32(1);
pub.pcr_info_write = pub.pcr_info_read;
memcpy(*datap, &pub, sizeof(pub));
*datap += sizeof(pub);
}
static int sandbox_tpm_xfer(struct udevice *dev, const uint8_t *sendbuf,
size_t send_size, uint8_t *recvbuf,
size_t *recv_len)
{
struct tpm_state *tpm = dev_get_priv(dev);
uint32_t code, index, length, type;
uint8_t *data;
int seq;
code = get_unaligned_be32(sendbuf + sizeof(uint16_t) +
sizeof(uint32_t));
#ifdef DEBUG
printf("tpm: %zd bytes, recv_len %zd, cmd = %x\n", send_size,
*recv_len, code);
print_buffer(0, sendbuf, 1, send_size, 0);
#endif
switch (code) {
case TPM_CMD_GET_CAPABILITY:
type = get_unaligned_be32(sendbuf + 14);
switch (type) {
case TPM_CAP_FLAG:
index = get_unaligned_be32(sendbuf + 18);
printf("Get flags index %#02x\n", index);
*recv_len = 22;
memset(recvbuf, '\0', *recv_len);
data = recvbuf + TPM_RESPONSE_HEADER_LENGTH +
sizeof(uint32_t);
switch (index) {
case FIRMWARE_NV_INDEX:
break;
case KERNEL_NV_INDEX:
handle_cap_flag_space(&data, index);
*recv_len = data - recvbuf -
TPM_RESPONSE_HEADER_LENGTH -
sizeof(uint32_t);
break;
case TPM_CAP_FLAG_PERMANENT: {
struct tpm_permanent_flags *pflags;
pflags = (struct tpm_permanent_flags *)data;
memset(pflags, '\0', sizeof(*pflags));
put_unaligned_be32(TPM_TAG_PERMANENT_FLAGS,
&pflags->tag);
*recv_len = TPM_HEADER_SIZE + 4 +
sizeof(*pflags);
break;
}
default:
printf(" ** Unknown flags index %x\n", index);
return -ENOSYS;
}
put_unaligned_be32(*recv_len,
recvbuf +
TPM_RESPONSE_HEADER_LENGTH);
break;
case TPM_CAP_NV_INDEX:
index = get_unaligned_be32(sendbuf + 18);
printf("Get cap nv index %#02x\n", index);
put_unaligned_be32(22, recvbuf +
TPM_RESPONSE_HEADER_LENGTH);
break;
default:
printf(" ** Unknown 0x65 command type %#02x\n",
type);
return -ENOSYS;
}
break;
case TPM_CMD_NV_WRITE_VALUE:
index = get_unaligned_be32(sendbuf + 10);
length = get_unaligned_be32(sendbuf + 18);
seq = index_to_seq(index);
if (seq < 0)
return -EINVAL;
printf("tpm: nvwrite index=%#02x, len=%#02x\n", index, length);
memcpy(&tpm->nvdata[seq].data, sendbuf + 22, length);
tpm->nvdata[seq].present = true;
*recv_len = 12;
memset(recvbuf, '\0', *recv_len);
break;
case TPM_CMD_NV_READ_VALUE: /* nvread */
index = get_unaligned_be32(sendbuf + 10);
length = get_unaligned_be32(sendbuf + 18);
seq = index_to_seq(index);
if (seq < 0)
return -EINVAL;
printf("tpm: nvread index=%#02x, len=%#02x, seq=%#02x\n", index,
length, seq);
*recv_len = TPM_RESPONSE_HEADER_LENGTH + sizeof(uint32_t) +
length;
memset(recvbuf, '\0', *recv_len);
put_unaligned_be32(length, recvbuf +
TPM_RESPONSE_HEADER_LENGTH);
if (seq == NV_SEQ_KERNEL) {
struct rollback_space_kernel rsk;
data = recvbuf + TPM_RESPONSE_HEADER_LENGTH +
sizeof(uint32_t);
memset(&rsk, 0, sizeof(struct rollback_space_kernel));
rsk.struct_version = 2;
rsk.uid = ROLLBACK_SPACE_KERNEL_UID;
rsk.crc8 = crc8(0, (unsigned char *)&rsk,
offsetof(struct rollback_space_kernel,
crc8));
memcpy(data, &rsk, sizeof(rsk));
} else if (!tpm->nvdata[seq].present) {
put_unaligned_be32(TPM_BADINDEX, recvbuf +
sizeof(uint16_t) + sizeof(uint32_t));
} else {
memcpy(recvbuf + TPM_RESPONSE_HEADER_LENGTH +
sizeof(uint32_t), &tpm->nvdata[seq].data,
length);
}
break;
case TPM_CMD_EXTEND:
*recv_len = 30;
memset(recvbuf, '\0', *recv_len);
break;
case TPM_CMD_NV_DEFINE_SPACE:
case 0x15: /* pcr read */
case 0x5d: /* force clear */
case 0x6f: /* physical enable */
case 0x72: /* physical set deactivated */
case 0x99: /* startup */
case 0x50: /* self test full */
case 0x4000000a: /* assert physical presence */
*recv_len = 12;
memset(recvbuf, '\0', *recv_len);
break;
default:
printf("Unknown tpm command %02x\n", code);
return -ENOSYS;
}
#ifdef DEBUG
printf("tpm: rx recv_len %zd\n", *recv_len);
print_buffer(0, recvbuf, 1, *recv_len, 0);
#endif
return 0;
}
static int sandbox_tpm_get_desc(struct udevice *dev, char *buf, int size)
{
if (size < 15)
return -ENOSPC;
return snprintf(buf, size, "sandbox TPM");
}
static int sandbox_tpm_probe(struct udevice *dev)
{
struct tpm_state *tpm = dev_get_priv(dev);
memcpy(tpm, &g_state, sizeof(*tpm));
return 0;
}
static int sandbox_tpm_open(struct udevice *dev)
{
return 0;
}
static int sandbox_tpm_close(struct udevice *dev)
{
return 0;
}
static const struct tpm_ops sandbox_tpm_ops = {
.open = sandbox_tpm_open,
.close = sandbox_tpm_close,
.get_desc = sandbox_tpm_get_desc,
.xfer = sandbox_tpm_xfer,
};
static const struct udevice_id sandbox_tpm_ids[] = {
{ .compatible = "google,sandbox-tpm" },
{ }
};
U_BOOT_DRIVER(sandbox_tpm) = {
.name = "sandbox_tpm",
.id = UCLASS_TPM,
.of_match = sandbox_tpm_ids,
.ops = &sandbox_tpm_ops,
.probe = sandbox_tpm_probe,
.priv_auto_alloc_size = sizeof(struct tpm_state),
};
@@ -0,0 +1,544 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* STMicroelectronics TPM ST33ZP24 I2C UBOOT driver
*
* Copyright (C) 2016, STMicroelectronics - All Rights Reserved
* Author(s): Christophe Ricard <christophe-h.ricard@st.com> for STMicroelectronics.
*
* Description: Device driver for ST33ZP24 I2C TPM TCG.
*
* This device driver implements the TPM interface as defined in
* the TCG TPM Interface Spec version 1.21, revision 1.0 and the
* STMicroelectronics Protocol Stack Specification version 1.2.0.
*/
#include <common.h>
#include <dm.h>
#include <fdtdec.h>
#include <i2c.h>
#include <tpm-v1.h>
#include <errno.h>
#include <linux/types.h>
#include <asm/unaligned.h>
#include "tpm_tis.h"
#include "tpm_internal.h"
#define TPM_ACCESS 0x0
#define TPM_STS 0x18
#define TPM_DATA_FIFO 0x24
#define LOCALITY0 0
#define TPM_DUMMY_BYTE 0xAA
#define TPM_ST33ZP24_I2C_SLAVE_ADDR 0x13
#define TPM_WRITE_DIRECTION 0x80
/*
* st33zp24_i2c_write8_reg
* Send byte to the TIS register according to the ST33ZP24 I2C protocol.
* @param: tpm_register, the tpm tis register where the data should be written
* @param: tpm_data, the tpm_data to write inside the tpm_register
* @param: tpm_size, The length of the data
* @return: Number of byte written successfully else an error code.
*/
static int st33zp24_i2c_write8_reg(struct udevice *dev, u8 tpm_register,
const u8 *tpm_data, size_t tpm_size)
{
struct tpm_chip_priv *chip_priv = dev_get_uclass_priv(dev);
chip_priv->buf[0] = tpm_register;
memcpy(chip_priv->buf + 1, tpm_data, tpm_size);
return dm_i2c_write(dev, 0, chip_priv->buf, tpm_size + 1);
}
/*
* st33zp24_i2c_read8_reg
* Recv byte from the TIS register according to the ST33ZP24 I2C protocol.
* @param: tpm_register, the tpm tis register where the data should be read
* @param: tpm_data, the TPM response
* @param: tpm_size, tpm TPM response size to read.
* @return: Number of byte read successfully else an error code.
*/
static int st33zp24_i2c_read8_reg(struct udevice *dev, u8 tpm_register,
u8 *tpm_data, size_t tpm_size)
{
int status;
u8 data;
data = TPM_DUMMY_BYTE;
status = st33zp24_i2c_write8_reg(dev, tpm_register, &data, 1);
if (status < 0)
return status;
return dm_i2c_read(dev, 0, tpm_data, tpm_size);
}
/*
* st33zp24_i2c_write
* Send byte to the TIS register according to the ST33ZP24 I2C protocol.
* @param: phy_id, the phy description
* @param: tpm_register, the tpm tis register where the data should be written
* @param: tpm_data, the tpm_data to write inside the tpm_register
* @param: tpm_size, the length of the data
* @return: number of byte written successfully: should be one if success.
*/
static int st33zp24_i2c_write(struct udevice *dev, u8 tpm_register,
const u8 *tpm_data, size_t tpm_size)
{
return st33zp24_i2c_write8_reg(dev, tpm_register | TPM_WRITE_DIRECTION,
tpm_data, tpm_size);
}
/*
* st33zp24_i2c_read
* Recv byte from the TIS register according to the ST33ZP24 I2C protocol.
* @param: phy_id, the phy description
* @param: tpm_register, the tpm tis register where the data should be read
* @param: tpm_data, the TPM response
* @param: tpm_size, tpm TPM response size to read.
* @return: number of byte read successfully: should be one if success.
*/
static int st33zp24_i2c_read(struct udevice *dev, u8 tpm_register,
u8 *tpm_data, size_t tpm_size)
{
return st33zp24_i2c_read8_reg(dev, tpm_register, tpm_data, tpm_size);
}
/*
* st33zp24_i2c_release_locality release the active locality
* @param: chip, the tpm chip description.
*/
static void st33zp24_i2c_release_locality(struct udevice *dev)
{
u8 data = TPM_ACCESS_ACTIVE_LOCALITY;
st33zp24_i2c_write(dev, TPM_ACCESS, &data, 1);
}
/*
* st33zp24_i2c_check_locality if the locality is active
* @param: chip, the tpm chip description
* @return: the active locality or -EACCES.
*/
static int st33zp24_i2c_check_locality(struct udevice *dev)
{
struct tpm_chip *chip = dev_get_priv(dev);
u8 data;
u8 status;
status = st33zp24_i2c_read(dev, TPM_ACCESS, &data, 1);
if (!status && (data &
(TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) ==
(TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID))
return chip->locality;
return -EACCES;
}
/*
* st33zp24_i2c_request_locality request the TPM locality
* @param: chip, the chip description
* @return: the active locality or negative value.
*/
static int st33zp24_i2c_request_locality(struct udevice *dev)
{
struct tpm_chip *chip = dev_get_priv(dev);
unsigned long start, stop;
long ret;
u8 data;
if (st33zp24_i2c_check_locality(dev) == chip->locality)
return chip->locality;
data = TPM_ACCESS_REQUEST_USE;
ret = st33zp24_i2c_write(dev, TPM_ACCESS, &data, 1);
if (ret < 0)
return ret;
/* wait for locality activated */
start = get_timer(0);
stop = chip->timeout_a;
do {
if (st33zp24_i2c_check_locality(dev) >= 0)
return chip->locality;
udelay(TPM_TIMEOUT_MS * 1000);
} while (get_timer(start) < stop);
return -EACCES;
}
/*
* st33zp24_i2c_status return the TPM_STS register
* @param: chip, the tpm chip description
* @return: the TPM_STS register value.
*/
static u8 st33zp24_i2c_status(struct udevice *dev)
{
u8 data;
st33zp24_i2c_read(dev, TPM_STS, &data, 1);
return data;
}
/*
* st33zp24_i2c_get_burstcount return the burstcount address 0x19 0x1A
* @param: chip, the chip description
* return: the burstcount or -TPM_DRIVER_ERR in case of error.
*/
static int st33zp24_i2c_get_burstcount(struct udevice *dev)
{
struct tpm_chip *chip = dev_get_priv(dev);
unsigned long start, stop;
int burstcnt, status;
u8 tpm_reg, temp;
/* wait for burstcount */
start = get_timer(0);
stop = chip->timeout_d;
do {
tpm_reg = TPM_STS + 1;
status = st33zp24_i2c_read(dev, tpm_reg, &temp, 1);
if (status < 0)
return -EBUSY;
tpm_reg = TPM_STS + 2;
burstcnt = temp;
status = st33zp24_i2c_read(dev, tpm_reg, &temp, 1);
if (status < 0)
return -EBUSY;
burstcnt |= temp << 8;
if (burstcnt)
return burstcnt;
udelay(TIS_SHORT_TIMEOUT_MS * 1000);
} while (get_timer(start) < stop);
return -EBUSY;
}
/*
* st33zp24_i2c_cancel, cancel the current command execution or
* set STS to COMMAND READY.
* @param: chip, tpm_chip description.
*/
static void st33zp24_i2c_cancel(struct udevice *dev)
{
u8 data;
data = TPM_STS_COMMAND_READY;
st33zp24_i2c_write(dev, TPM_STS, &data, 1);
}
/*
* st33zp24_i2c_wait_for_stat wait for a TPM_STS value
* @param: chip, the tpm chip description
* @param: mask, the value mask to wait
* @param: timeout, the timeout
* @param: status,
* @return: the tpm status, 0 if success, -ETIME if timeout is reached.
*/
static int st33zp24_i2c_wait_for_stat(struct udevice *dev, u8 mask,
unsigned long timeout, int *status)
{
unsigned long start, stop;
/* Check current status */
*status = st33zp24_i2c_status(dev);
if ((*status & mask) == mask)
return 0;
start = get_timer(0);
stop = timeout;
do {
udelay(TPM_TIMEOUT_MS * 1000);
*status = st33zp24_i2c_status(dev);
if ((*status & mask) == mask)
return 0;
} while (get_timer(start) < stop);
return -ETIME;
}
/*
* st33zp24_i2c_recv_data receive data
* @param: chip, the tpm chip description
* @param: buf, the buffer where the data are received
* @param: count, the number of data to receive
* @return: the number of bytes read from TPM FIFO.
*/
static int st33zp24_i2c_recv_data(struct udevice *dev, u8 *buf, size_t count)
{
struct tpm_chip *chip = dev_get_priv(dev);
int size = 0, burstcnt, len, ret, status;
while (size < count &&
st33zp24_i2c_wait_for_stat(dev, TPM_STS_DATA_AVAIL | TPM_STS_VALID,
chip->timeout_c, &status) == 0) {
burstcnt = st33zp24_i2c_get_burstcount(dev);
if (burstcnt < 0)
return burstcnt;
len = min_t(int, burstcnt, count - size);
ret = st33zp24_i2c_read(dev, TPM_DATA_FIFO, buf + size, len);
if (ret < 0)
return ret;
size += len;
}
return size;
}
/*
* st33zp24_i2c_recv received TPM response through TPM phy.
* @param: chip, tpm_chip description.
* @param: buf, the buffer to store data.
* @param: count, the number of bytes that can received (sizeof buf).
* @return: Returns zero in case of success else -EIO.
*/
static int st33zp24_i2c_recv(struct udevice *dev, u8 *buf, size_t count)
{
struct tpm_chip *chip = dev_get_priv(dev);
int size;
unsigned int expected;
if (!chip)
return -ENODEV;
if (count < TPM_HEADER_SIZE) {
size = -EIO;
goto out;
}
size = st33zp24_i2c_recv_data(dev, buf, TPM_HEADER_SIZE);
if (size < TPM_HEADER_SIZE) {
debug("TPM error, unable to read header\n");
goto out;
}
expected = get_unaligned_be32(buf + 2);
if (expected > count || expected < TPM_HEADER_SIZE) {
size = -EIO;
goto out;
}
size += st33zp24_i2c_recv_data(dev, &buf[TPM_HEADER_SIZE],
expected - TPM_HEADER_SIZE);
if (size < expected) {
debug("TPM error, unable to read remaining bytes of result\n");
size = -EIO;
goto out;
}
out:
st33zp24_i2c_cancel(dev);
st33zp24_i2c_release_locality(dev);
return size;
}
/*
* st33zp24_i2c_send send TPM commands through TPM phy.
* @param: chip, tpm_chip description.
* @param: buf, the buffer to send.
* @param: len, the number of bytes to send.
* @return: Returns zero in case of success else the negative error code.
*/
static int st33zp24_i2c_send(struct udevice *dev, const u8 *buf, size_t len)
{
struct tpm_chip *chip = dev_get_priv(dev);
u32 i, size;
int burstcnt, ret, status;
u8 data, tpm_stat;
if (!chip)
return -ENODEV;
if (len < TPM_HEADER_SIZE)
return -EIO;
ret = st33zp24_i2c_request_locality(dev);
if (ret < 0)
return ret;
tpm_stat = st33zp24_i2c_status(dev);
if ((tpm_stat & TPM_STS_COMMAND_READY) == 0) {
st33zp24_i2c_cancel(dev);
if (st33zp24_i2c_wait_for_stat(dev, TPM_STS_COMMAND_READY,
chip->timeout_b, &status) < 0) {
ret = -ETIME;
goto out_err;
}
}
for (i = 0; i < len - 1;) {
burstcnt = st33zp24_i2c_get_burstcount(dev);
if (burstcnt < 0)
return burstcnt;
size = min_t(int, len - i - 1, burstcnt);
ret = st33zp24_i2c_write(dev, TPM_DATA_FIFO, buf + i, size);
if (ret < 0)
goto out_err;
i += size;
}
tpm_stat = st33zp24_i2c_status(dev);
if ((tpm_stat & TPM_STS_DATA_EXPECT) == 0) {
ret = -EIO;
goto out_err;
}
ret = st33zp24_i2c_write(dev, TPM_DATA_FIFO, buf + len - 1, 1);
if (ret < 0)
goto out_err;
tpm_stat = st33zp24_i2c_status(dev);
if ((tpm_stat & TPM_STS_DATA_EXPECT) != 0) {
ret = -EIO;
goto out_err;
}
data = TPM_STS_GO;
ret = st33zp24_i2c_write(dev, TPM_STS, &data, 1);
if (ret < 0)
goto out_err;
return len;
out_err:
st33zp24_i2c_cancel(dev);
st33zp24_i2c_release_locality(dev);
return ret;
}
static int st33zp24_i2c_cleanup(struct udevice *dev)
{
st33zp24_i2c_cancel(dev);
/*
* The TPM needs some time to clean up here,
* so we sleep rather than keeping the bus busy
*/
mdelay(2);
st33zp24_i2c_release_locality(dev);
return 0;
}
static int st33zp24_i2c_init(struct udevice *dev)
{
struct tpm_chip *chip = dev_get_priv(dev);
chip->is_open = 1;
/* Default timeouts - these could move to the device tree */
chip->timeout_a = TIS_SHORT_TIMEOUT_MS;
chip->timeout_b = TIS_LONG_TIMEOUT_MS;
chip->timeout_c = TIS_SHORT_TIMEOUT_MS;
chip->timeout_d = TIS_SHORT_TIMEOUT_MS;
chip->locality = LOCALITY0;
/*
* A timeout query to TPM can be placed here.
* Standard timeout values are used so far
*/
return 0;
}
static int st33zp24_i2c_open(struct udevice *dev)
{
struct tpm_chip *chip = dev_get_priv(dev);
int rc;
debug("%s: start\n", __func__);
if (chip->is_open)
return -EBUSY;
rc = st33zp24_i2c_init(dev);
if (rc < 0)
chip->is_open = 0;
return rc;
}
static int st33zp24_i2c_close(struct udevice *dev)
{
struct tpm_chip *chip = dev_get_priv(dev);
if (chip->is_open) {
st33zp24_i2c_release_locality(dev);
chip->is_open = 0;
chip->vend_dev = 0;
}
return 0;
}
static int st33zp24_i2c_get_desc(struct udevice *dev, char *buf, int size)
{
struct tpm_chip *chip = dev_get_priv(dev);
if (size < 50)
return -ENOSPC;
return snprintf(buf, size, "1.2 TPM (%s, chip type %s device-id 0x%x)",
chip->is_open ? "open" : "closed",
dev->name,
chip->vend_dev >> 16);
}
static const struct tpm_ops st33zp24_i2c_tpm_ops = {
.open = st33zp24_i2c_open,
.close = st33zp24_i2c_close,
.recv = st33zp24_i2c_recv,
.send = st33zp24_i2c_send,
.cleanup = st33zp24_i2c_cleanup,
.get_desc = st33zp24_i2c_get_desc,
};
static int st33zp24_i2c_probe(struct udevice *dev)
{
struct tpm_chip *chip = dev_get_priv(dev);
/* Default timeouts */
chip->timeout_a = TIS_SHORT_TIMEOUT_MS;
chip->timeout_b = TIS_LONG_TIMEOUT_MS;
chip->timeout_c = TIS_SHORT_TIMEOUT_MS;
chip->timeout_d = TIS_SHORT_TIMEOUT_MS;
chip->locality = LOCALITY0;
i2c_set_chip_offset_len(dev, 0);
debug("ST33ZP24 I2C TPM from STMicroelectronics found\n");
return 0;
}
static int st33zp24_i2c_remove(struct udevice *dev)
{
st33zp24_i2c_release_locality(dev);
return 0;
}
static const struct udevice_id st33zp24_i2c_ids[] = {
{ .compatible = "st,st33zp24-i2c" },
{ }
};
U_BOOT_DRIVER(st33zp24_i2c) = {
.name = "st33zp24-i2c",
.id = UCLASS_TPM,
.of_match = of_match_ptr(st33zp24_i2c_ids),
.probe = st33zp24_i2c_probe,
.remove = st33zp24_i2c_remove,
.ops = &st33zp24_i2c_tpm_ops,
.priv_auto_alloc_size = sizeof(struct tpm_chip),
};
@@ -0,0 +1,673 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* STMicroelectronics TPM ST33ZP24 SPI UBOOT driver
*
* Copyright (C) 2016, STMicroelectronics - All Rights Reserved
* Author(s): Christophe Ricard <christophe-h.ricard@st.com> for STMicroelectronics.
*
* Description: Device driver for ST33ZP24 SPI TPM TCG.
*
* This device driver implements the TPM interface as defined in
* the TCG TPM Interface Spec version 1.21, revision 1.0 and the
* STMicroelectronics Protocol Stack Specification version 1.2.0.
*/
#include <common.h>
#include <dm.h>
#include <fdtdec.h>
#include <spi.h>
#include <tpm-v1.h>
#include <errno.h>
#include <linux/types.h>
#include <asm/unaligned.h>
#include <linux/compat.h>
#include "tpm_tis.h"
#include "tpm_internal.h"
#define TPM_ACCESS 0x0
#define TPM_STS 0x18
#define TPM_DATA_FIFO 0x24
#define LOCALITY0 0
#define TPM_DATA_FIFO 0x24
#define TPM_INTF_CAPABILITY 0x14
#define TPM_DUMMY_BYTE 0x00
#define TPM_WRITE_DIRECTION 0x80
#define MAX_SPI_LATENCY 15
#define LOCALITY0 0
#define ST33ZP24_OK 0x5A
#define ST33ZP24_UNDEFINED_ERR 0x80
#define ST33ZP24_BADLOCALITY 0x81
#define ST33ZP24_TISREGISTER_UKNOWN 0x82
#define ST33ZP24_LOCALITY_NOT_ACTIVATED 0x83
#define ST33ZP24_HASH_END_BEFORE_HASH_START 0x84
#define ST33ZP24_BAD_COMMAND_ORDER 0x85
#define ST33ZP24_INCORECT_RECEIVED_LENGTH 0x86
#define ST33ZP24_TPM_FIFO_OVERFLOW 0x89
#define ST33ZP24_UNEXPECTED_READ_FIFO 0x8A
#define ST33ZP24_UNEXPECTED_WRITE_FIFO 0x8B
#define ST33ZP24_CMDRDY_SET_WHEN_PROCESSING_HASH_END 0x90
#define ST33ZP24_DUMMY_BYTES 0x00
/*
* TPM command can be up to 2048 byte, A TPM response can be up to
* 1024 byte.
* Between command and response, there are latency byte (up to 15
* usually on st33zp24 2 are enough).
*
* Overall when sending a command and expecting an answer we need if
* worst case:
* 2048 (for the TPM command) + 1024 (for the TPM answer). We need
* some latency byte before the answer is available (max 15).
* We have 2048 + 1024 + 15.
*/
#define ST33ZP24_SPI_BUFFER_SIZE (TPM_BUFSIZE + (TPM_BUFSIZE / 2) +\
MAX_SPI_LATENCY)
struct st33zp24_spi_phy {
int latency;
u8 tx_buf[ST33ZP24_SPI_BUFFER_SIZE];
u8 rx_buf[ST33ZP24_SPI_BUFFER_SIZE];
};
static int st33zp24_spi_status_to_errno(u8 code)
{
switch (code) {
case ST33ZP24_OK:
return 0;
case ST33ZP24_UNDEFINED_ERR:
case ST33ZP24_BADLOCALITY:
case ST33ZP24_TISREGISTER_UKNOWN:
case ST33ZP24_LOCALITY_NOT_ACTIVATED:
case ST33ZP24_HASH_END_BEFORE_HASH_START:
case ST33ZP24_BAD_COMMAND_ORDER:
case ST33ZP24_UNEXPECTED_READ_FIFO:
case ST33ZP24_UNEXPECTED_WRITE_FIFO:
case ST33ZP24_CMDRDY_SET_WHEN_PROCESSING_HASH_END:
return -EPROTO;
case ST33ZP24_INCORECT_RECEIVED_LENGTH:
case ST33ZP24_TPM_FIFO_OVERFLOW:
return -EMSGSIZE;
case ST33ZP24_DUMMY_BYTES:
return -ENOSYS;
}
return code;
}
/*
* st33zp24_spi_send
* Send byte to TPM register according to the ST33ZP24 SPI protocol.
* @param: tpm, the chip description
* @param: tpm_register, the tpm tis register where the data should be written
* @param: tpm_data, the tpm_data to write inside the tpm_register
* @param: tpm_size, The length of the data
* @return: should be zero if success else a negative error code.
*/
static int st33zp24_spi_write(struct udevice *dev, u8 tpm_register,
const u8 *tpm_data, size_t tpm_size)
{
int total_length = 0, ret;
struct spi_slave *slave = dev_get_parent_priv(dev);
struct st33zp24_spi_phy *phy = dev_get_platdata(dev);
u8 *tx_buf = (u8 *)phy->tx_buf;
u8 *rx_buf = phy->rx_buf;
tx_buf[total_length++] = TPM_WRITE_DIRECTION | LOCALITY0;
tx_buf[total_length++] = tpm_register;
if (tpm_size > 0 && tpm_register == TPM_DATA_FIFO) {
tx_buf[total_length++] = tpm_size >> 8;
tx_buf[total_length++] = tpm_size;
}
memcpy(tx_buf + total_length, tpm_data, tpm_size);
total_length += tpm_size;
memset(tx_buf + total_length, TPM_DUMMY_BYTE, phy->latency);
total_length += phy->latency;
ret = spi_claim_bus(slave);
if (ret < 0)
return ret;
ret = spi_xfer(slave, total_length * 8, tx_buf, rx_buf,
SPI_XFER_BEGIN | SPI_XFER_END);
if (ret < 0)
return ret;
spi_release_bus(slave);
if (ret == 0)
ret = rx_buf[total_length - 1];
return st33zp24_spi_status_to_errno(ret);
}
/*
* spi_st33zp24_spi_read8_reg
* Recv byte from the TIS register according to the ST33ZP24 SPI protocol.
* @param: tpm, the chip description
* @param: tpm_loc, the locality to read register from
* @param: tpm_register, the tpm tis register where the data should be read
* @param: tpm_data, the TPM response
* @param: tpm_size, tpm TPM response size to read.
* @return: should be zero if success else a negative error code.
*/
static u8 st33zp24_spi_read8_reg(struct udevice *dev, u8 tpm_register,
u8 *tpm_data, size_t tpm_size)
{
int total_length = 0, ret;
struct spi_slave *slave = dev_get_parent_priv(dev);
struct st33zp24_spi_phy *phy = dev_get_platdata(dev);
u8 *tx_buf = (u8 *)phy->tx_buf;
u8 *rx_buf = phy->rx_buf;
/* Pre-Header */
tx_buf[total_length++] = LOCALITY0;
tx_buf[total_length++] = tpm_register;
memset(&tx_buf[total_length], TPM_DUMMY_BYTE,
phy->latency + tpm_size);
total_length += phy->latency + tpm_size;
ret = spi_claim_bus(slave);
if (ret < 0)
return 0;
ret = spi_xfer(slave, total_length * 8, tx_buf, rx_buf,
SPI_XFER_BEGIN | SPI_XFER_END);
if (ret < 0)
return 0;
spi_release_bus(slave);
if (tpm_size > 0 && ret == 0) {
ret = rx_buf[total_length - tpm_size - 1];
memcpy(tpm_data, rx_buf + total_length - tpm_size, tpm_size);
}
return ret;
}
/*
* st33zp24_spi_recv
* Recv byte from the TIS register according to the ST33ZP24 SPI protocol.
* @param: phy_id, the phy description
* @param: tpm_register, the tpm tis register where the data should be read
* @param: tpm_data, the TPM response
* @param: tpm_size, tpm TPM response size to read.
* @return: number of byte read successfully: should be one if success.
*/
static int st33zp24_spi_read(struct udevice *dev, u8 tpm_register,
u8 *tpm_data, size_t tpm_size)
{
int ret;
ret = st33zp24_spi_read8_reg(dev, tpm_register, tpm_data, tpm_size);
if (!st33zp24_spi_status_to_errno(ret))
return tpm_size;
return ret;
}
static int st33zp24_spi_evaluate_latency(struct udevice *dev)
{
int latency = 1, status = 0;
u8 data = 0;
struct st33zp24_spi_phy *phy = dev_get_platdata(dev);
while (!status && latency < MAX_SPI_LATENCY) {
phy->latency = latency;
status = st33zp24_spi_read8_reg(dev, TPM_INTF_CAPABILITY,
&data, 1);
latency++;
}
if (status < 0)
return status;
if (latency == MAX_SPI_LATENCY)
return -ENODEV;
return latency - 1;
}
/*
* st33zp24_spi_release_locality release the active locality
* @param: chip, the tpm chip description.
*/
static void st33zp24_spi_release_locality(struct udevice *dev)
{
u8 data = TPM_ACCESS_ACTIVE_LOCALITY;
st33zp24_spi_write(dev, TPM_ACCESS, &data, 1);
}
/*
* st33zp24_spi_check_locality if the locality is active
* @param: chip, the tpm chip description
* @return: the active locality or -EACCES.
*/
static int st33zp24_spi_check_locality(struct udevice *dev)
{
u8 data;
u8 status;
struct tpm_chip *chip = dev_get_priv(dev);
status = st33zp24_spi_read(dev, TPM_ACCESS, &data, 1);
if (status && (data &
(TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) ==
(TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID))
return chip->locality;
return -EACCES;
}
/*
* st33zp24_spi_request_locality request the TPM locality
* @param: chip, the chip description
* @return: the active locality or negative value.
*/
static int st33zp24_spi_request_locality(struct udevice *dev)
{
unsigned long start, stop;
long ret;
u8 data;
struct tpm_chip *chip = dev_get_priv(dev);
if (st33zp24_spi_check_locality(dev) == chip->locality)
return chip->locality;
data = TPM_ACCESS_REQUEST_USE;
ret = st33zp24_spi_write(dev, TPM_ACCESS, &data, 1);
if (ret < 0)
return ret;
/* wait for locality activated */
start = get_timer(0);
stop = chip->timeout_a;
do {
if (st33zp24_spi_check_locality(dev) >= 0)
return chip->locality;
udelay(TPM_TIMEOUT_MS * 1000);
} while (get_timer(start) < stop);
return -EACCES;
}
/*
* st33zp24_spi_status return the TPM_STS register
* @param: chip, the tpm chip description
* @return: the TPM_STS register value.
*/
static u8 st33zp24_spi_status(struct udevice *dev)
{
u8 data;
st33zp24_spi_read(dev, TPM_STS, &data, 1);
return data;
}
/*
* st33zp24_spi_get_burstcount return the burstcount address 0x19 0x1A
* @param: chip, the chip description
* return: the burstcount or -TPM_DRIVER_ERR in case of error.
*/
static int st33zp24_spi_get_burstcount(struct udevice *dev)
{
struct tpm_chip *chip = dev_get_priv(dev);
unsigned long start, stop;
int burstcnt, status;
u8 tpm_reg, temp;
/* wait for burstcount */
start = get_timer(0);
stop = chip->timeout_d;
do {
tpm_reg = TPM_STS + 1;
status = st33zp24_spi_read(dev, tpm_reg, &temp, 1);
if (status < 0)
return -EBUSY;
tpm_reg = TPM_STS + 2;
burstcnt = temp;
status = st33zp24_spi_read(dev, tpm_reg, &temp, 1);
if (status < 0)
return -EBUSY;
burstcnt |= temp << 8;
if (burstcnt)
return burstcnt;
udelay(TIS_SHORT_TIMEOUT_MS * 1000);
} while (get_timer(start) < stop);
return -EBUSY;
}
/*
* st33zp24_spi_cancel, cancel the current command execution or
* set STS to COMMAND READY.
* @param: chip, tpm_chip description.
*/
static void st33zp24_spi_cancel(struct udevice *dev)
{
u8 data;
data = TPM_STS_COMMAND_READY;
st33zp24_spi_write(dev, TPM_STS, &data, 1);
}
/*
* st33zp24_spi_wait_for_stat wait for a TPM_STS value
* @param: chip, the tpm chip description
* @param: mask, the value mask to wait
* @param: timeout, the timeout
* @param: status,
* @return: the tpm status, 0 if success, -ETIME if timeout is reached.
*/
static int st33zp24_spi_wait_for_stat(struct udevice *dev, u8 mask,
unsigned long timeout, int *status)
{
unsigned long start, stop;
/* Check current status */
*status = st33zp24_spi_status(dev);
if ((*status & mask) == mask)
return 0;
start = get_timer(0);
stop = timeout;
do {
udelay(TPM_TIMEOUT_MS * 1000);
*status = st33zp24_spi_status(dev);
if ((*status & mask) == mask)
return 0;
} while (get_timer(start) < stop);
return -ETIME;
}
/*
* st33zp24_spi_recv_data receive data
* @param: chip, the tpm chip description
* @param: buf, the buffer where the data are received
* @param: count, the number of data to receive
* @return: the number of bytes read from TPM FIFO.
*/
static int st33zp24_spi_recv_data(struct udevice *dev, u8 *buf, size_t count)
{
struct tpm_chip *chip = dev_get_priv(dev);
int size = 0, burstcnt, len, ret, status;
while (size < count &&
st33zp24_spi_wait_for_stat(dev, TPM_STS_DATA_AVAIL | TPM_STS_VALID,
chip->timeout_c, &status) == 0) {
burstcnt = st33zp24_spi_get_burstcount(dev);
if (burstcnt < 0)
return burstcnt;
len = min_t(int, burstcnt, count - size);
ret = st33zp24_spi_read(dev, TPM_DATA_FIFO, buf + size, len);
if (ret < 0)
return ret;
size += len;
}
return size;
}
/*
* st33zp24_spi_recv received TPM response through TPM phy.
* @param: chip, tpm_chip description.
* @param: buf, the buffer to store data.
* @param: count, the number of bytes that can received (sizeof buf).
* @return: Returns zero in case of success else -EIO.
*/
static int st33zp24_spi_recv(struct udevice *dev, u8 *buf, size_t count)
{
struct tpm_chip *chip = dev_get_priv(dev);
int size;
unsigned int expected;
if (!chip)
return -ENODEV;
if (count < TPM_HEADER_SIZE) {
size = -EIO;
goto out;
}
size = st33zp24_spi_recv_data(dev, buf, TPM_HEADER_SIZE);
if (size < TPM_HEADER_SIZE) {
debug("TPM error, unable to read header\n");
goto out;
}
expected = get_unaligned_be32(buf + 2);
if (expected > count || expected < TPM_HEADER_SIZE) {
size = -EIO;
goto out;
}
size += st33zp24_spi_recv_data(dev, &buf[TPM_HEADER_SIZE],
expected - TPM_HEADER_SIZE);
if (size < expected) {
debug("TPM error, unable to read remaining bytes of result\n");
size = -EIO;
goto out;
}
out:
st33zp24_spi_cancel(dev);
st33zp24_spi_release_locality(dev);
return size;
}
/*
* st33zp24_spi_send send TPM commands through TPM phy.
* @param: chip, tpm_chip description.
* @param: buf, the buffer to send.
* @param: len, the number of bytes to send.
* @return: Returns zero in case of success else the negative error code.
*/
static int st33zp24_spi_send(struct udevice *dev, const u8 *buf, size_t len)
{
struct tpm_chip *chip = dev_get_priv(dev);
u32 i, size;
int burstcnt, ret, status;
u8 data, tpm_stat;
if (!chip)
return -ENODEV;
if (len < TPM_HEADER_SIZE)
return -EIO;
ret = st33zp24_spi_request_locality(dev);
if (ret < 0)
return ret;
tpm_stat = st33zp24_spi_status(dev);
if ((tpm_stat & TPM_STS_COMMAND_READY) == 0) {
st33zp24_spi_cancel(dev);
if (st33zp24_spi_wait_for_stat(dev, TPM_STS_COMMAND_READY,
chip->timeout_b, &status) < 0) {
ret = -ETIME;
goto out_err;
}
}
for (i = 0; i < len - 1;) {
burstcnt = st33zp24_spi_get_burstcount(dev);
if (burstcnt < 0)
return burstcnt;
size = min_t(int, len - i - 1, burstcnt);
ret = st33zp24_spi_write(dev, TPM_DATA_FIFO, buf + i, size);
if (ret < 0)
goto out_err;
i += size;
}
tpm_stat = st33zp24_spi_status(dev);
if ((tpm_stat & TPM_STS_DATA_EXPECT) == 0) {
ret = -EIO;
goto out_err;
}
ret = st33zp24_spi_write(dev, TPM_DATA_FIFO, buf + len - 1, 1);
if (ret < 0)
goto out_err;
tpm_stat = st33zp24_spi_status(dev);
if ((tpm_stat & TPM_STS_DATA_EXPECT) != 0) {
ret = -EIO;
goto out_err;
}
data = TPM_STS_GO;
ret = st33zp24_spi_write(dev, TPM_STS, &data, 1);
if (ret < 0)
goto out_err;
return len;
out_err:
st33zp24_spi_cancel(dev);
st33zp24_spi_release_locality(dev);
return ret;
}
static int st33zp24_spi_cleanup(struct udevice *dev)
{
st33zp24_spi_cancel(dev);
/*
* The TPM needs some time to clean up here,
* so we sleep rather than keeping the bus busy
*/
mdelay(2);
st33zp24_spi_release_locality(dev);
return 0;
}
static int st33zp24_spi_init(struct udevice *dev)
{
struct tpm_chip *chip = dev_get_priv(dev);
struct st33zp24_spi_phy *phy = dev_get_platdata(dev);
chip->is_open = 1;
/* Default timeouts - these could move to the device tree */
chip->timeout_a = TIS_SHORT_TIMEOUT_MS;
chip->timeout_b = TIS_LONG_TIMEOUT_MS;
chip->timeout_c = TIS_SHORT_TIMEOUT_MS;
chip->timeout_d = TIS_SHORT_TIMEOUT_MS;
chip->locality = LOCALITY0;
phy->latency = st33zp24_spi_evaluate_latency(dev);
if (phy->latency <= 0)
return -ENODEV;
/*
* A timeout query to TPM can be placed here.
* Standard timeout values are used so far
*/
return 0;
}
static int st33zp24_spi_open(struct udevice *dev)
{
struct tpm_chip *chip = dev_get_priv(dev);
int rc;
debug("%s: start\n", __func__);
if (chip->is_open)
return -EBUSY;
rc = st33zp24_spi_init(dev);
if (rc < 0)
chip->is_open = 0;
return rc;
}
static int st33zp24_spi_close(struct udevice *dev)
{
struct tpm_chip *chip = dev_get_priv(dev);
if (chip->is_open) {
st33zp24_spi_release_locality(dev);
chip->is_open = 0;
chip->vend_dev = 0;
}
return 0;
}
static int st33zp24_spi_get_desc(struct udevice *dev, char *buf, int size)
{
struct tpm_chip *chip = dev_get_priv(dev);
if (size < 50)
return -ENOSPC;
return snprintf(buf, size, "1.2 TPM (%s, chip type %s device-id 0x%x)",
chip->is_open ? "open" : "closed",
dev->name,
chip->vend_dev >> 16);
}
const struct tpm_ops st33zp24_spi_tpm_ops = {
.open = st33zp24_spi_open,
.close = st33zp24_spi_close,
.recv = st33zp24_spi_recv,
.send = st33zp24_spi_send,
.cleanup = st33zp24_spi_cleanup,
.get_desc = st33zp24_spi_get_desc,
};
static int st33zp24_spi_probe(struct udevice *dev)
{
struct tpm_chip_priv *uc_priv = dev_get_uclass_priv(dev);
uc_priv->duration_ms[TPM_SHORT] = TIS_SHORT_TIMEOUT_MS;
uc_priv->duration_ms[TPM_MEDIUM] = TIS_LONG_TIMEOUT_MS;
uc_priv->duration_ms[TPM_LONG] = TIS_LONG_TIMEOUT_MS;
uc_priv->retry_time_ms = TPM_TIMEOUT_MS;
debug("ST33ZP24 SPI TPM from STMicroelectronics found\n");
return 0;
}
static int st33zp24_spi_remove(struct udevice *dev)
{
st33zp24_spi_release_locality(dev);
return 0;
}
static const struct udevice_id st33zp24_spi_ids[] = {
{ .compatible = "st,st33zp24-spi" },
{ }
};
U_BOOT_DRIVER(st33zp24_spi_spi) = {
.name = "st33zp24-spi",
.id = UCLASS_TPM,
.of_match = of_match_ptr(st33zp24_spi_ids),
.probe = st33zp24_spi_probe,
.remove = st33zp24_spi_remove,
.ops = &st33zp24_spi_tpm_ops,
.priv_auto_alloc_size = sizeof(struct tpm_chip),
.platdata_auto_alloc_size = sizeof(struct st33zp24_spi_phy),
};