Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
164 changes: 145 additions & 19 deletions hal/tpm_io_uboot.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/

/* This example shows IO interfaces for U-boot */
/* This example shows IO interfaces for U-boot using raw SPI */

#include <wolftpm/tpm2.h>
#include <wolftpm/tpm2_tis.h>
Expand All @@ -43,36 +43,162 @@

#if defined(__UBOOT__)
#include <config.h>
#include <spi.h>
#include <dm/device.h>
#include <dm/uclass.h>

/* SPI bus and chip select configuration for TPM
* These can be overridden in user_settings.h or board config */
#ifndef TPM_SPI_BUS
#define TPM_SPI_BUS 0
#endif
#ifndef TPM_SPI_CS
#define TPM_SPI_CS 0
#endif
#ifndef TPM_SPI_MAX_HZ
#define TPM_SPI_MAX_HZ 1000000 /* 1 MHz - safe default */
#endif
#define TPM_SPI_MODE SPI_MODE_0 /* Mode 0 (CPOL=0, CPHA=0) */

/* Maximum SPI frame size */
#define MAX_SPI_FRAMESIZE 64

/* Static SPI device handles */
static struct udevice *g_spi_bus = NULL;
static struct spi_slave *g_spi_slave = NULL;
static int g_spi_initialized = 0;

/* Initialize SPI for TPM communication */
static int uboot_spi_init(void)
{
int ret;

if (g_spi_initialized) {
return 0; /* Already initialized */
}

/* Get SPI bus and slave device */
ret = spi_get_bus_and_cs(TPM_SPI_BUS, TPM_SPI_CS,
&g_spi_bus, &g_spi_slave);
if (ret != 0) {
#ifdef DEBUG_WOLFTPM
printf("Failed to get SPI bus %d cs %d: %d\n",
TPM_SPI_BUS, TPM_SPI_CS, ret);
#endif
return ret;
}

g_spi_initialized = 1;

#ifdef DEBUG_WOLFTPM
printf("TPM SPI initialized: bus %d, cs %d\n", TPM_SPI_BUS, TPM_SPI_CS);
#endif

return 0;
}

/* Raw SPI transfer for wolfTPM TIS layer
* This is called by wolfTPM's TIS implementation for register read/write.
* The txBuf/rxBuf contain TIS-formatted SPI data including the 4-byte header.
*/
int TPM2_IoCb_Uboot_SPI(TPM2_CTX* ctx, const byte* txBuf,
byte* rxBuf, word16 xferSz, void* userCtx)
{
int ret = 0;
struct udevice *dev;
int ret;
#ifdef WOLFTPM_CHECK_WAIT_STATE
int timeout = TPM_SPI_WAIT_RETRY;
byte tmp_rx;
#endif

/* Get the TPM device */
if (ret == 0) {
ret = tcg2_platform_get_tpm2(&dev);
if ( ret != 0 || dev == NULL) {
#ifdef DEBUG_WOLFTPM
printf("Failed to get TPM device with error: %d\n", ret);
#endif
return TPM_RC_FAILURE;
}
(void)ctx;
(void)userCtx;

/* Initialize SPI if needed */
ret = uboot_spi_init();
if (ret != 0) {
return TPM_RC_FAILURE;
}

/* Claim the SPI bus */
ret = spi_claim_bus(g_spi_slave);
if (ret != 0) {
#ifdef DEBUG_WOLFTPM
printf("Failed to claim SPI bus: %d\n", ret);
#endif
return TPM_RC_FAILURE;
}

#ifdef WOLFTPM_CHECK_WAIT_STATE
/* Send TIS header first (4 bytes) with CS held */
ret = spi_xfer(g_spi_slave, TPM_TIS_HEADER_SZ * 8,
txBuf, rxBuf, SPI_XFER_BEGIN);
if (ret != 0) {
#ifdef DEBUG_WOLFTPM
printf("SPI header xfer failed: %d\n", ret);
#endif
goto cleanup;
}

/* Transfer the device data using tpm_xfer */
if (ret == 0) {
ret = tpm_xfer(dev, txBuf, xferSz, rxBuf, &xferSz);
if (ret != 0) {
/* Check for wait state - TPM holds ready bit low if busy */
if ((rxBuf[TPM_TIS_HEADER_SZ - 1] & TPM_TIS_READY_MASK) == 0) {
/* Poll for ready */
do {
ret = spi_xfer(g_spi_slave, 8, NULL, &tmp_rx, 0);
if (ret != 0) {
break;
}
if (tmp_rx & TPM_TIS_READY_MASK) {
break;
}
} while (--timeout > 0);

if (timeout <= 0 || ret != 0) {
#ifdef DEBUG_WOLFTPM
printf("tpm_xfer failed with error: %d\n", ret);
printf("SPI wait state timeout\n");
#endif
return TPM_RC_FAILURE;
/* Deassert CS */
spi_xfer(g_spi_slave, 0, NULL, NULL, SPI_XFER_END);
ret = TPM_RC_FAILURE;
goto cleanup;
}
}

return TPM_RC_SUCCESS;
/* Transfer remainder of data with CS deasserted at end */
if (xferSz > TPM_TIS_HEADER_SZ) {
ret = spi_xfer(g_spi_slave, (xferSz - TPM_TIS_HEADER_SZ) * 8,
&txBuf[TPM_TIS_HEADER_SZ],
&rxBuf[TPM_TIS_HEADER_SZ],
SPI_XFER_END);
} else {
/* Just deassert CS if no more data */
ret = spi_xfer(g_spi_slave, 0, NULL, NULL, SPI_XFER_END);
}

#else
/* No wait state handling - send entire message at once
* This works for Infineon TPMs (SLB9670/SLB9672) which guarantee
* no wait states */
ret = spi_xfer(g_spi_slave, xferSz * 8, txBuf, rxBuf,
SPI_XFER_BEGIN | SPI_XFER_END);
#endif /* WOLFTPM_CHECK_WAIT_STATE */

if (ret != 0) {
#ifdef DEBUG_WOLFTPM
printf("SPI xfer failed: %d\n", ret);
#endif
ret = TPM_RC_FAILURE;
} else {
ret = TPM_RC_SUCCESS;
}

#ifdef WOLFTPM_CHECK_WAIT_STATE
cleanup:
#endif
spi_release_bus(g_spi_slave);

return ret;
}

#endif /* __UBOOT__ */
#endif /* WOLFTPM_LINUX_DEV || WOLFTPM_SWTPM || WOLFTPM_WINAPI */
#endif /* WOLFTPM_INCLUDE_IO_FILE */
Expand Down
2 changes: 1 addition & 1 deletion src/tpm2.c
Original file line number Diff line number Diff line change
Expand Up @@ -729,7 +729,7 @@ TPM_RC TPM2_Cleanup(TPM2_CTX* ctx)
}
#endif /* !WOLFTPM2_NO_WOLFCRYPT */

#ifdef WOLFTPM_LINUX_DEV
#if defined(WOLFTPM_LINUX_DEV) && !defined(__UBOOT__)
if (ctx->fd >= 0)
close(ctx->fd);
#endif
Expand Down