diff --git a/.github/workflows/wolfhal-build.yml b/.github/workflows/wolfhal-build.yml new file mode 100644 index 00000000..eabecf33 --- /dev/null +++ b/.github/workflows/wolfhal-build.yml @@ -0,0 +1,147 @@ +name: wolfHAL Build Tests + +on: + push: + branches: [ 'master', 'main', 'release/**' ] + pull_request: + branches: [ '*' ] + types: [opened, synchronize, reopened, ready_for_review] + repository_dispatch: + types: [nightly-trigger] + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + # Cross compile hal/tpm_io_wolfhal.c for a bare metal target against the real + # wolfHAL headers. A host build cannot cover this: __linux__ wins the platform + # selection chain in hal/tpm_io.c, so the wolfHAL branch is never reached. + cross-compile: + name: Cross compile (arm-none-eabi) + if: github.event_name != 'pull_request' || github.event.pull_request.draft == false + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - name: Checkout wolfTPM + uses: actions/checkout@v4 + + - name: Install ARM toolchain + uses: ./.github/actions/apt-retry + with: + packages: gcc-arm-none-eabi + + - name: Checkout wolfHAL + uses: actions/checkout@v4 + with: + repository: wolfSSL/wolfHAL + ref: main + path: wolfHAL + + # wolfTPM does not ship board definitions, so stand in for the board.h an + # application would provide. Doubles as a check that the documented + # contract in hal/README.md is complete and sufficient. + # + # user_settings.h is needed because this is a bare metal build that never + # runs ./configure, so the generated wolftpm/options.h does not exist. + # WOLFTPM_USER_SETTINGS selects this header instead, which is the same + # path a real wolfHAL application takes. + - name: Generate test board.h and user_settings.h + run: | + mkdir -p test-board + cat > test-board/user_settings.h <<'EOF' + #ifndef TEST_USER_SETTINGS_H + #define TEST_USER_SETTINGS_H + /* Build options come from the compiler command line below. */ + #endif + EOF + cat > test-board/board.h <<'EOF' + #ifndef TEST_BOARD_H + #define TEST_BOARD_H + #include + extern whal_Spi g_spi; + extern whal_Gpio g_gpio; + extern whal_I2c g_i2c; + extern whal_Spi_ComCfg g_spiComCfg; + extern whal_I2c_ComCfg g_i2cComCfg; + #define BOARD_SPI_DEV (&g_spi) + #define BOARD_SPI_COM_CFG (&g_spiComCfg) + #define BOARD_GPIO_DEV (&g_gpio) + #define BOARD_CS_PIN 15 + #define BOARD_I2C_DEV (&g_i2c) + #define BOARD_I2C_COM_CFG (&g_i2cComCfg) + #endif + EOF + + - name: Build + run: | + set -e + INC="-Itest-board -IwolfHAL -I. -Ihal" + BASE="-mcpu=cortex-m4 -mthumb -Wall -Wextra -Werror + -Wmissing-prototypes -Wconversion -Wno-unused-parameter + -DWOLFTPM_WOLFHAL -DWOLFTPM_EXAMPLE_HAL -DWOLFTPM_USER_SETTINGS + -DWOLFTPM2_NO_WOLFCRYPT -DNO_FILESYSTEM" + build() { + name="$1"; shift + echo "::group::$name" + arm-none-eabi-gcc -c -o /dev/null $BASE $INC "$@" hal/tpm_io.c + echo "::endgroup::" + } + build "SPI, wait state" -DWOLFTPM_CHECK_WAIT_STATE + build "SPI, no wait state" + build "SPI, verbose" -DWOLFTPM_CHECK_WAIT_STATE -DWOLFTPM_DEBUG_VERBOSE -DWOLFTPM_DEBUG_TIMEOUT + build "I2C" -DWOLFTPM_I2C -DWOLFTPM_ADV_IO + build "I2C, verbose" -DWOLFTPM_I2C -DWOLFTPM_ADV_IO -DWOLFTPM_DEBUG_VERBOSE + + # The board.h contract is enforced by #error guards. Verify a missing + # entry is reported by name rather than as a confusing undeclared symbol. + - name: Check board.h guards report missing macros + run: | + set -e + mkdir -p bad-board + sed '/BOARD_CS_PIN/d' test-board/board.h > bad-board/board.h + cp test-board/user_settings.h bad-board/ + if arm-none-eabi-gcc -fsyntax-only -mcpu=cortex-m4 -mthumb \ + -Ibad-board -IwolfHAL -I. -Ihal \ + -DWOLFTPM_WOLFHAL -DWOLFTPM_EXAMPLE_HAL -DWOLFTPM_USER_SETTINGS \ + -DWOLFTPM2_NO_WOLFCRYPT -DNO_FILESYSTEM -DWOLFTPM_INCLUDE_IO_FILE \ + hal/tpm_io_wolfhal.c 2> guard.log; then + echo "FAIL: expected a compile error for the missing macro" + exit 1 + fi + grep -q "board.h must define BOARD_CS_PIN" guard.log || { + echo "FAIL: guard did not name BOARD_CS_PIN"; cat guard.log; exit 1; } + echo "PASS: guard named the missing macro" + + # Verify --enable-wolfhal wires up, and that enabling it does not disturb an + # ordinary host build. On Linux the wolfHAL branch is not selected, so + # tpm_io_wolfhal.c must compile to an empty translation unit with no wolfHAL + # headers present. This covers the hal/include.am and configure.ac wiring. + autotools: + name: Autotools (--enable-wolfhal) + if: github.event_name != 'pull_request' || github.event.pull_request.draft == false + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - name: Checkout wolfTPM + uses: actions/checkout@v4 + + - name: Setup wolfSSL + uses: ./.github/actions/setup-wolfssl + + - name: Build with --enable-wolfhal (SPI) + run: | + set -e + ./autogen.sh + ./configure --enable-wolfhal | tee conf.log + grep -q "wolfHAL IO: *yes" conf.log || { + echo "FAIL: config summary did not report wolfHAL as enabled"; exit 1; } + make -j$(nproc) + + - name: Build with --enable-wolfhal --enable-i2c + run: | + set -e + make distclean || true + ./autogen.sh + ./configure --enable-wolfhal --enable-i2c + make -j$(nproc) diff --git a/README.md b/README.md index 5f8470b7..5ec2dca9 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Portable TPM 2.0 project designed for embedded use. * wolfTPM uses the TPM Interface Specification (TIS) to communicate either over SPI, or using a memory mapped I/O range. * On Linux, wolfTPM auto-detects between the kernel TPM driver (`/dev/tpmX`) and direct SPI access at runtime - a simple `./configure && make` works with either interface. * wolfTPM can also use the Linux TPM kernel interface (`/dev/tpmX`) to talk with any physical TPM on SPI, I2C and even LPC bus. -* Platform support for Raspberry Pi (Linux), MMIO, STM32 with CubeMX, Atmel ASF, Xilinx, QNX Infineon TriCore and Barebox. +* Platform support for Raspberry Pi (Linux), MMIO, STM32 with CubeMX, Atmel ASF, Xilinx, QNX, Infineon TriCore, wolfHAL and Barebox. * The design allows for easy portability to different platforms: * Native C code designed for embedded use. * Single IO callback for hardware SPI interface. @@ -223,6 +223,7 @@ There are HAL examples in `hal` directory for: * Infineon TriCore * Linux * STM32 CubeMX +* wolfHAL * Xilinx We also support an advanced IO option (`--enable-advio`/`WOLFTPM_ADV_IO`), which adds the register and read/write flag as parameter to the IO callback. This is required for I2C support. @@ -329,6 +330,9 @@ make install this flag adds no compile-time macro but disables the auto-enabled swTPM/fwTPM defaults. (default: not set) --enable-i2c Enable I2C TPM Support (default: disabled, requires advio) - WOLFTPM_I2C --enable-mmio Enable built-in MMIO callbacks (default: disabled) - WOLFTPM_MMIO +--enable-wolfhal Enable wolfHAL IO callbacks (default: disabled) - WOLFTPM_WOLFHAL + Requires the wolfHAL headers and an application provided board.h. + See hal/README.md for the required BOARD_* definitions. --enable-checkwaitstate Enable TIS / SPI Check Wait State support (default: depends on chip) - WOLFTPM_CHECK_WAIT_STATE --enable-smallstack Enable options to reduce stack usage --enable-tislock Enable Linux Named Semaphore for locking access to SPI device for concurrent access between processes - WOLFTPM_TIS_LOCK diff --git a/configure.ac b/configure.ac index e76e1406..642e2e04 100644 --- a/configure.ac +++ b/configure.ac @@ -645,6 +645,20 @@ then fi +# wolfHAL hardware abstraction layer +# Selects hal/tpm_io_wolfhal.c for SPI/I2C. Requires wolfHAL headers and a +# board.h supplying the BOARD_* peripheral definitions. +AC_ARG_ENABLE([wolfhal], + [AS_HELP_STRING([--enable-wolfhal],[Enable wolfHAL IO callbacks (default: disabled)])], + [ ENABLED_WOLFHAL=$enableval ], + [ ENABLED_WOLFHAL=no ] + ) +if test "x$ENABLED_WOLFHAL" = "xyes" +then + AM_CFLAGS="$AM_CFLAGS -DWOLFTPM_WOLFHAL" +fi + + # TIS / SPI Check Wait State support # Required for all but Infineon only if test "x$ENABLED_CHECKWAITSTATE" = "xyes" || test "x$ENABLED_AUTODETECT" = "xyes" || test "x$ENABLED_INFINEON" = "xno" @@ -1171,6 +1185,7 @@ fi echo " * WINAPI: $ENABLED_WINAPI" echo " * TIS/SPI Check Wait State: $ENABLED_CHECKWAITSTATE" echo " * HAL Reset (nRST GPIO): $ENABLED_HAL_RESET" +echo " * wolfHAL IO: $ENABLED_WOLFHAL" echo " * Infineon SLB967X $ENABLED_INFINEON" echo " * STM ST33: $ENABLED_ST" diff --git a/hal/README.md b/hal/README.md index ef790822..e59a582f 100644 --- a/hal/README.md +++ b/hal/README.md @@ -26,8 +26,75 @@ If using a HAL IO callback it is registered on library initialization using: | Microchip | `tpm_io_microchip.c` | `WOLFTPM_MICROCHIP_HARMONY` | | QNX | `tpm_io_qnx.c` | `__QNX__` | | ST Cube HAL | `tpm_io_st.c` | `WOLFSSL_STM32_CUBEMX` | +| wolfHAL | `tpm_io_wolfhal.c` | `WOLFTPM_WOLFHAL` | | Xilinx | `tpm_io_xilinx.c` | `__XILINX__` | +## wolfHAL + +Enabled with `WOLFTPM_WOLFHAL` or `--enable-wolfhal`. Requires the wolfHAL +headers on the include path. + +This HAL is placed last in the platform selection chain, so it is only used +when no other platform macro is defined. Building for an STM32 target with the +CubeMX headers present, for example, selects `tpm_io_st.c` instead. + +### Board definitions + +wolfTPM does not ship board definitions. `tpm_io_wolfhal.c` includes +`"board.h"`, which the application provides on its include path. A wolfHAL +project already has one, so in most cases only the TPM specific entries below +need adding to it. + +For SPI: + +| Macro | Type | Description | +| ----- | ---- | ----------- | +| `BOARD_SPI_DEV` | `whal_Spi*` | SPI instance the TPM is connected to | +| `BOARD_SPI_COM_CFG` | `whal_Spi_ComCfg*` | SPI session parameters | +| `BOARD_GPIO_DEV` | `whal_Gpio*` | GPIO instance driving chip select | +| `BOARD_CS_PIN` | pin number | Chip select pin, driven active low | + +For I2C (also requires `WOLFTPM_ADV_IO`, which `--enable-i2c` sets): + +| Macro | Type | Description | +| ----- | ---- | ----------- | +| `BOARD_I2C_DEV` | `whal_I2c*` | I2C instance the TPM is connected to | +| `BOARD_I2C_COM_CFG` | `whal_I2c_ComCfg*` | I2C session parameters, including the TPM target address | + +The TPM target address goes in the `addr` field of `BOARD_I2C_COM_CFG`, not in +a wolfTPM macro. Most TPM 2.0 I2C parts use `0x2e`. + +A TPM 2.0 I2C part takes roughly 80us to wake and NAKs until it is ready, so +each transfer is retried up to `TPM_I2C_TRIES` times (default 10). Define +`TPM_I2C_TRIES` to override. + +A missing entry is reported at compile time, naming the macro required. Only +the macros needed by the selected bus are checked. + +Example additions to an existing wolfHAL `board.h`: + +```c +/* TPM on SPI1, chip select on PA15 */ +extern whal_Spi_ComCfg g_tpmSpiComCfg; +#define BOARD_SPI_COM_CFG (&g_tpmSpiComCfg) +#define BOARD_CS_PIN 15 +``` + +For I2C, where the session config carries the TPM address: + +```c +/* board.c */ +whal_I2c_ComCfg g_tpmI2cComCfg = { + .freq = 400000, /* Hz */ + .addr = 0x2e, /* TPM target address */ + .addrSz = 7, /* bits */ +}; + +/* board.h */ +extern whal_I2c_ComCfg g_tpmI2cComCfg; +#define BOARD_I2C_COM_CFG (&g_tpmI2cComCfg) +``` + ## HAL IO Callback Function Here are the prototypes for the HAL callback function: diff --git a/hal/include.am b/hal/include.am index 29d31983..97322b06 100644 --- a/hal/include.am +++ b/hal/include.am @@ -15,6 +15,7 @@ src_libwolftpm_la_SOURCES += \ hal/tpm_io_st.c \ hal/tpm_io_qnx.c \ hal/tpm_io_uboot.c \ + hal/tpm_io_wolfhal.c \ hal/tpm_io_xilinx.c endif endif diff --git a/hal/tpm_io.c b/hal/tpm_io.c index 4590395e..9ed1cfe2 100644 --- a/hal/tpm_io.c +++ b/hal/tpm_io.c @@ -77,6 +77,8 @@ #include "hal/tpm_io_espressif.c" #elif defined(WOLFSSL_ZEPHYR) #include "hal/tpm_io_zephyr.c" +#elif defined(WOLFTPM_WOLFHAL) +#include "hal/tpm_io_wolfhal.c" #endif #if !defined(WOLFTPM_I2C) && !defined(WOLFTPM_MMIO) && !defined(WOLFTPM_FWTPM_HAL) @@ -107,6 +109,8 @@ static int TPM2_IoCb_SPI(TPM2_CTX* ctx, const byte* txBuf, byte* rxBuf, ret = TPM2_IoCb_Microchip_SPI(ctx, txBuf, rxBuf, xferSz, userCtx); #elif defined(WOLFSSL_ESPIDF) ret = TPM2_IoCb_Espressif_SPI(ctx, txBuf, rxBuf, xferSz, userCtx); +#elif defined(WOLFTPM_WOLFHAL) + ret = TPM2_IoCb_Wolfhal_SPI(ctx, txBuf, rxBuf, xferSz, userCtx); #else /* TODO: Add your platform here for HW SPI interface */ @@ -167,6 +171,8 @@ int TPM2_IoCb(TPM2_CTX* ctx, INT32 isRead, UINT32 addr, ret = TPM2_IoCb_MicrochipHarmony_I2C(ctx, isRead, addr, buf, size, userCtx); #elif defined(WOLFSSL_ZEPHYR) ret = TPM2_IoCb_Zephyr_I2C(ctx, isRead, addr, buf, size, userCtx); + #elif defined(WOLFTPM_WOLFHAL) + ret = TPM2_IoCb_Wolfhal_I2C(ctx, isRead, addr, buf, size, userCtx); #else /* TODO: Add your platform here for HW I2C interface */ printf("Add your platform here for HW I2C interface\n"); diff --git a/hal/tpm_io.h b/hal/tpm_io.h index 83783819..256a7355 100644 --- a/hal/tpm_io.h +++ b/hal/tpm_io.h @@ -95,6 +95,9 @@ WOLFTPM_LOCAL int TPM2_IoCb_MicrochipHarmony_I2C(TPM2_CTX* ctx, int isRead, word #elif defined(WOLFSSL_ZEPHYR) WOLFTPM_LOCAL int TPM2_IoCb_Zephyr_I2C(TPM2_CTX* ctx, int isRead, word32 addr, byte* buf, word16 size, void* userCtx); +#elif defined(WOLFTPM_WOLFHAL) +WOLFTPM_LOCAL int TPM2_IoCb_Wolfhal_I2C(TPM2_CTX* ctx, int isRead, word32 addr, + byte* buf, word16 size, void* userCtx); #endif /* __linux__ */ #else /* SPI */ @@ -129,6 +132,9 @@ WOLFTPM_LOCAL int TPM2_IoCb_Infineon_TriCore_SPI(TPM2_CTX* ctx, const byte* txBu #elif defined(WOLFTPM_MICROCHIP_HARMONY) WOLFTPM_LOCAL int TPM2_IoCb_Microchip_SPI(TPM2_CTX* ctx, const byte* txBuf, byte* rxBuf, word16 xferSz, void* userCtx); +#elif defined(WOLFTPM_WOLFHAL) +WOLFTPM_LOCAL int TPM2_IoCb_Wolfhal_SPI(TPM2_CTX* ctx, const byte* txBuf, + byte* rxBuf, word16 xferSz, void* userCtx); #endif #endif /* WOLFTPM_I2C */ diff --git a/hal/tpm_io_wolfhal.c b/hal/tpm_io_wolfhal.c new file mode 100644 index 00000000..4e47b556 --- /dev/null +++ b/hal/tpm_io_wolfhal.c @@ -0,0 +1,251 @@ +/* tpm_io_wolfhal.c + * + * Copyright (C) 2006-2026 wolfSSL Inc. + * + * This file is part of wolfTPM. + * + * wolfTPM is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfTPM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +/* This example shows IO interfaces for wolfHAL */ + +#ifdef HAVE_CONFIG_H + #include +#endif + +#include +#include +#include "tpm_io.h" + +/******************************************************************************/ +/* --- BEGIN IO Callback Logic -- */ +/******************************************************************************/ + +/* Included via tpm_io.c if WOLFTPM_INCLUDE_IO_FILE is defined */ +#ifdef WOLFTPM_INCLUDE_IO_FILE + +#if ! (defined(WOLFTPM_LINUX_DEV) || \ + defined(WOLFTPM_SWTPM) || \ + defined(WOLFTPM_WINAPI) ) + +#if defined(WOLFTPM_WOLFHAL) + +/* Only pull in wolfHAL and the board definitions when this backend is + * selected, so the file compiles to nothing when built directly on a + * platform that does not have wolfHAL available. */ +#include +#include "board.h" + +#ifdef WOLFTPM_I2C + /* board.h is supplied by the application, not by wolfTPM. It must + * describe how this board reaches the TPM. */ + #ifndef BOARD_I2C_DEV + #error "wolfHAL: board.h must define BOARD_I2C_DEV (whal_I2c*)" + #endif + /* Carries the TPM target address (typically 0x2e) and bus speed. */ + #ifndef BOARD_I2C_COM_CFG + #error "wolfHAL: board.h must define BOARD_I2C_COM_CFG (whal_I2c_ComCfg*)" + #endif + + #ifndef TPM_I2C_TRIES + #define TPM_I2C_TRIES 10 + #endif + + /* wolfHAL I2C */ + int TPM2_IoCb_Wolfhal_I2C(TPM2_CTX* ctx, int isRead, word32 addr, + byte* buf, word16 size, void* userCtx) + { + int ret = TPM_RC_FAILURE; + whal_Error status; + int tries = TPM_I2C_TRIES; + /* wolfHAL takes an 8-bit register, the TIS layer hands us the full + * address. Narrow it explicitly rather than relying on conversion. */ + uint8_t reg = (uint8_t)(addr & 0xFF); + + /* Open the session. This applies the TPM target address and bus + * speed, and must precede any transfer. */ + status = whal_I2c_StartCom(BOARD_I2C_DEV, BOARD_I2C_COM_CFG); + if (status != WHAL_SUCCESS) { + #ifdef WOLFTPM_DEBUG_VERBOSE + printf("I2C StartCom Failed: Status=%d\n", status); + #endif + return TPM_RC_FAILURE; + } + + /* The TPM takes about 80us to wake up and NAKs until it is ready, so + * retry the transfer. StartCom only configures the peripheral, the + * NAK surfaces here where the bus is actually driven. */ + do { + if (isRead) + status = whal_I2c_ReadReg(BOARD_I2C_DEV, reg, buf, size); + else + status = whal_I2c_WriteReg(BOARD_I2C_DEV, reg, buf, size); + } while (status != WHAL_SUCCESS && --tries > 0); + + if (status == WHAL_SUCCESS) { + ret = TPM_RC_SUCCESS; + } + #ifdef WOLFTPM_DEBUG_VERBOSE + else { + printf("I2C %s Failed: Reg 0x%x, Sz %d, Status=%d (tries %d)\n", + isRead ? "Read" : "Write", reg, size, status, + TPM_I2C_TRIES - tries); + } + #endif + + /* Always end the session, including on the error path above. A + * teardown failure leaves the bus in an unknown state, so it + * downgrades an otherwise successful transfer. */ + status = whal_I2c_EndCom(BOARD_I2C_DEV); + if (status != WHAL_SUCCESS) { + #ifdef WOLFTPM_DEBUG_VERBOSE + printf("I2C EndCom Failed: Status=%d\n", status); + #endif + ret = TPM_RC_FAILURE; + } + + (void)ctx; + (void)userCtx; + + return ret; + } + +#else /* wolfHAL SPI */ + /* board.h is supplied by the application, not by wolfTPM. It must + * describe how this board reaches the TPM. */ + #ifndef BOARD_SPI_DEV + #error "wolfHAL: board.h must define BOARD_SPI_DEV (whal_Spi*)" + #endif + #ifndef BOARD_SPI_COM_CFG + #error "wolfHAL: board.h must define BOARD_SPI_COM_CFG (whal_Spi_ComCfg*)" + #endif + #ifndef BOARD_GPIO_DEV + #error "wolfHAL: board.h must define BOARD_GPIO_DEV (whal_Gpio*) for chip select" + #endif + #ifndef BOARD_CS_PIN + #error "wolfHAL: board.h must define BOARD_CS_PIN (chip select pin, active low)" + #endif + + int TPM2_IoCb_Wolfhal_SPI(TPM2_CTX* ctx, const byte* txBuf, byte* rxBuf, + word16 xferSz, void* userCtx) + { + int ret = TPM_RC_FAILURE; + whal_Error status; + #ifdef WOLFTPM_CHECK_WAIT_STATE + int timeout = TPM_SPI_WAIT_RETRY; + #endif + + /* Assert chip select (active low) */ + status = whal_Gpio_Set(BOARD_GPIO_DEV, BOARD_CS_PIN, 0); + if (status != WHAL_SUCCESS) { + #ifdef WOLFTPM_DEBUG_VERBOSE + printf("SPI CS assert Failed: Status=%d\n", status); + #endif + return TPM_RC_FAILURE; + } + + /* Open the transaction. If this fails there is no communication to + * end, so only the chip select needs releasing. */ + status = whal_Spi_StartCom(BOARD_SPI_DEV, BOARD_SPI_COM_CFG); + if (status != WHAL_SUCCESS) { + #ifdef WOLFTPM_DEBUG_VERBOSE + printf("SPI StartCom Failed: Status=%d\n", status); + #endif + (void)whal_Gpio_Set(BOARD_GPIO_DEV, BOARD_CS_PIN, 1); + return TPM_RC_FAILURE; + } + + #ifdef WOLFTPM_CHECK_WAIT_STATE + /* Send Header */ + status = whal_Spi_SendRecv(BOARD_SPI_DEV, txBuf, TPM_TIS_HEADER_SZ, + rxBuf, TPM_TIS_HEADER_SZ); + if (status != WHAL_SUCCESS) + goto exit; + + /* Check for wait states */ + if ((rxBuf[TPM_TIS_HEADER_SZ-1] & TPM_TIS_READY_MASK) == 0) { + do { + /* Check for SPI ready */ + status = whal_Spi_SendRecv(BOARD_SPI_DEV, txBuf, 1, + rxBuf, 1); + if (status != WHAL_SUCCESS) + goto exit; + if (rxBuf[0] & TPM_TIS_READY_MASK) + break; + } while (--timeout > 0); + #ifdef WOLFTPM_DEBUG_TIMEOUT + printf("SPI Ready Wait %d\n", TPM_SPI_WAIT_RETRY - timeout); + #endif + if (timeout <= 0) { + #ifdef WOLFTPM_DEBUG_VERBOSE + printf("SPI Ready timeout after %d tries\n", + TPM_SPI_WAIT_RETRY); + #endif + goto exit; /* ret stays TPM_RC_FAILURE */ + } + } + + /* Send remainder of payload */ + status = whal_Spi_SendRecv(BOARD_SPI_DEV, + &txBuf[TPM_TIS_HEADER_SZ], xferSz - TPM_TIS_HEADER_SZ, + &rxBuf[TPM_TIS_HEADER_SZ], xferSz - TPM_TIS_HEADER_SZ); + #else + /* Send Entire Message - no wait states */ + status = whal_Spi_SendRecv(BOARD_SPI_DEV, txBuf, xferSz, rxBuf, xferSz); + #endif /* WOLFTPM_CHECK_WAIT_STATE */ + + if (status == WHAL_SUCCESS) { + ret = TPM_RC_SUCCESS; + } + #ifdef WOLFTPM_DEBUG_VERBOSE + else { + printf("SPI Failed: Xfer %d, Status=%d\n", xferSz, status); + } + #endif + + #ifdef WOLFTPM_CHECK_WAIT_STATE + exit: /* only the wait state path bails out early */ + #endif + /* Always tear down, including on the error paths above. A teardown + * failure leaves the bus in an unknown state, so it downgrades an + * otherwise successful transfer. */ + status = whal_Spi_EndCom(BOARD_SPI_DEV); + if (status != WHAL_SUCCESS) { + #ifdef WOLFTPM_DEBUG_VERBOSE + printf("SPI EndCom Failed: Status=%d\n", status); + #endif + ret = TPM_RC_FAILURE; + } + status = whal_Gpio_Set(BOARD_GPIO_DEV, BOARD_CS_PIN, 1); + if (status != WHAL_SUCCESS) { + #ifdef WOLFTPM_DEBUG_VERBOSE + printf("SPI CS release Failed: Status=%d\n", status); + #endif + ret = TPM_RC_FAILURE; + } + + (void)ctx; + (void)userCtx; + + return ret; + } +#endif /* WOLFTPM_I2C */ +#endif +#endif /* !(WOLFTPM_LINUX_DEV || WOLFTPM_SWTPM || WOLFTPM_WINAPI) */ +#endif /* WOLFTPM_INCLUDE_IO_FILE */ + +/******************************************************************************/ +/* --- END IO Callback Logic -- */ +/******************************************************************************/