Add optional TPM nRST reset HAL and document ST33-on-Pi wiring

pull/546/head
David Garske 2026-07-06 11:49:22 -07:00
parent 30562ce46a
commit a2345b471a
8 changed files with 189 additions and 4 deletions

View File

@ -99,6 +99,10 @@ jobs:
# STMicro ST33KTPM2
- name: st33ktpm2 firmware
wolftpm_config: --enable-st33 --enable-firmware --disable-fwtpm
# HAL nRST reset control (compile-only, no GPIO hardware in CI)
- name: hal-reset
wolftpm_config: --enable-st33 --enable-hal-reset --disable-fwtpm
test_command: "true"
# SPDM + Nuvoton (compile-only, no hardware in CI)
- name: spdm-nuvoton
wolfssl_config: --enable-wolftpm --enable-ecc --enable-sha384 --enable-aesgcm --enable-hkdf --enable-sp

View File

@ -405,6 +405,8 @@ make
Note: The `--enable-firmware` option enables firmware upgrade support for ST33 TPMs. This adds the `st33_fw_update` example tool for performing firmware updates.
Raspberry Pi wiring: ST33KTPM2X SPI is on `/dev/spidev0.0` with `nRST` (active low) on GPIO24 (pin 18); Nuvoton uses GPIO4. Optionally drive nRST from code with `--enable-hal-reset` and `TPM2_IoCb_Reset()` (see `hal/README.md`).
### Building Microchip ATTPM20
Build wolfTPM:

View File

@ -474,6 +474,34 @@ then
AM_CFLAGS="$AM_CFLAGS -DWOLFTPM_ST33"
fi
# Optional TPM hardware reset (nRST) HAL control via Linux GPIO char device.
# Accepts an optional GPIO line number (default ST33 GPIO24 / Pi pin 18, Nuvoton GPIO4).
AC_ARG_ENABLE([hal-reset],
[AS_HELP_STRING([--enable-hal-reset@<:@=LINE@:>@],[Enable TPM nRST reset HAL (optional GPIO line; default ST33 GPIO24, Nuvoton GPIO4) (default: disabled)])],
[ ENABLED_HAL_RESET=$enableval ],
[ ENABLED_HAL_RESET=no ]
)
if test "x$ENABLED_HAL_RESET" != "xno"
then
# The reset HAL implementation (hal/tpm_io.c) only builds for the SPI/I2C
# example HAL, not the native kernel (/dev/tpm0) or swtpm transports, so
# reject those combinations rather than report "yes" with no callable API.
# Note swtpm is the default on Linux x86_64/aarch64, so the hardware reset
# HAL requires explicitly selecting a hardware interface (--enable-spi/-i2c).
if test "x$ENABLED_DEVTPM" = "xyes" || test "x$ENABLED_SWTPM" = "xyes" || test "x$ENABLED_SWTPM" = "xuart"
then
AC_MSG_ERROR([--enable-hal-reset needs the SPI/I2C hardware HAL; it is not available with the swtpm simulator (default on Linux x86_64/aarch64) or --enable-devtpm. Select a hardware interface, e.g. --enable-spi or --enable-i2c.])
fi
AM_CFLAGS="$AM_CFLAGS -DWOLFTPM_HAL_RESET"
case "x$ENABLED_HAL_RESET" in
xyes) ;;
x|x*[[!0-9]]*)
AC_MSG_ERROR([--enable-hal-reset=LINE requires a numeric GPIO line number (e.g. --enable-hal-reset=24).]) ;;
*) AM_CFLAGS="$AM_CFLAGS -DWOLFTPM_RESET_LINE=$ENABLED_HAL_RESET" ;;
esac
ENABLED_HAL_RESET=yes
fi
# Microchip ATTPM20
AC_ARG_ENABLE([mchp],,
[ ENABLED_MCHP=$enableval ],
@ -1132,6 +1160,7 @@ if test "x$ENABLED_SWTPM" = "xyes"; then
fi
echo " * WINAPI: $ENABLED_WINAPI"
echo " * TIS/SPI Check Wait State: $ENABLED_CHECKWAITSTATE"
echo " * HAL Reset (nRST GPIO): $ENABLED_HAL_RESET"
echo " * Infineon SLB967X $ENABLED_INFINEON"
echo " * STM ST33: $ENABLED_ST"

View File

@ -98,15 +98,17 @@ effect. The reset pin must be connected and controllable by the host.
to a host-controllable GPIO. Without reset pin control, SPDM mode changes
cannot be applied and recovery from SPDM-only mode is not possible.
### Raspberry Pi Example (GPIO 4)
The reset line is board specific. On a Raspberry Pi, Nuvoton uses GPIO4 and the
ST33KTPM uses GPIO24 (pin 18); confirm your wiring before toggling.
```bash
# Assert reset low, wait, release high, wait for TPM startup
# Assert reset low, release high, wait for TPM startup (Nuvoton GPIO4 shown)
gpioset gpiochip0 4=0 && sleep 0.1 && gpioset gpiochip0 4=1 && sleep 2
# ST33: use line 24 instead of 4
```
Other platforms will use their own GPIO control mechanism. The key requirement
is toggling the TPM reset line (active low) with sufficient hold time.
wolfTPM can also drive this from code: build with `--enable-hal-reset` and call
`TPM2_IoCb_Reset()` (default line: ST33 GPIO24, Nuvoton GPIO4). See `hal/README.md`.
## Automated Test Suite

View File

@ -59,6 +59,13 @@ int TPM2_IoCb(TPM2_CTX* ctx, const byte* txBuf, byte* rxBuf,
* `WOLFTPM_CHECK_WAIT_STATE`: Enables check of the wait state during a SPI transaction. Most TPM 2.0 chips require this and typically only require 0-2 wait cycles depending on the command. Only the Infineon TPM's guarantee no wait states.
* `WOLFTPM_ADV_IO`: Enables advanced IO callback mode that includes TIS register and read/write flag. This is requires for I2C, but can be used with SPI also.
* `WOLFTPM_DEBUG_IO`: Enable logging of the IO (if using the example HAL).
* `WOLFTPM_HAL_RESET`: Optional TPM hardware reset (nRST) control in the example HAL (`--enable-hal-reset`). On Linux, `TPM2_IoCb_Reset(&dev->ctx, userCtx)` pulses nRST (active low) via the GPIO char device (raw GPIO v2 uAPI, no libgpiod).
## TPM reset (nRST) HAL macros (when `WOLFTPM_HAL_RESET` is set)
* `WOLFTPM_RESET_GPIOCHIP`: GPIO char device. Default: `/dev/gpiochip0`
* `WOLFTPM_RESET_LINE`: GPIO line wired to nRST. Default: ST33 = `24` (GPIO24, Pi pin 18), Nuvoton = `4` (GPIO4). Also settable via `--enable-hal-reset=<line>`.
* `WOLFTPM_RESET_HOLD_US` / `WOLFTPM_RESET_SETTLE_US`: reset hold / post-reset settle time (us). Defaults: `300000` / `1000000`.
## Additional Compiler macros

View File

@ -239,6 +239,29 @@ int TPM2_IoCb(TPM2_CTX* ctx, const BYTE* txBuf, BYTE* rxBuf,
}
#endif /* WOLFTPM_ADV_IO */
#ifdef WOLFTPM_HAL_RESET
/* Pulse the TPM hardware reset (nRST) line to reset the TPM. Dispatches to the
* platform implementation. Returns TPM_RC_SUCCESS on success. */
int TPM2_IoCb_Reset(TPM2_CTX* ctx, void* userCtx)
{
int ret;
#if defined(__linux__)
ret = TPM2_IoCb_Linux_Reset(ctx, userCtx);
#else
/* No reset HAL for this OS - return NOT_COMPILED_IN so callers can tell it
* apart from a genuine reset failure (TPM_RC_FAILURE) on a supported OS. */
(void)ctx;
(void)userCtx;
ret = NOT_COMPILED_IN;
#ifdef DEBUG_WOLFTPM
printf("TPM reset HAL not implemented for this platform\n");
#endif
#endif
return ret;
}
#endif /* WOLFTPM_HAL_RESET */
#endif /* !(WOLFTPM_LINUX_DEV || WOLFTPM_SWTPM || WOLFTPM_WINAPI) */
/******************************************************************************/

View File

@ -145,6 +145,15 @@ WOLFTPM_LOCAL int TPM2_IoCb_FwTPM(TPM2_CTX* ctx, int isRead, word32 addr,
byte* buf, word16 size, void* userCtx);
#endif
#ifdef WOLFTPM_HAL_RESET
/* Optional TPM hardware reset (nRST) control. Pulses the reset line to reset
* the TPM. Enable with --enable-hal-reset (-DWOLFTPM_HAL_RESET). */
WOLFTPM_API int TPM2_IoCb_Reset(TPM2_CTX* ctx, void* userCtx);
#if defined(__linux__)
WOLFTPM_LOCAL int TPM2_IoCb_Linux_Reset(TPM2_CTX* ctx, void* userCtx);
#endif
#endif
#endif /* WOLFTPM_EXAMPLE_HAL */
#endif /* !(WOLFTPM_LINUX_DEV || WOLFTPM_SWTPM || WOLFTPM_WINAPI) */

View File

@ -64,6 +64,11 @@
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#ifdef WOLFTPM_HAL_RESET
/* GPIO character-device uAPI for optional nRST control */
#include <linux/gpio.h>
#include <time.h> /* nanosleep (usleep is undefined for >= 1s) */
#endif
#ifdef WOLFTPM_I2C
/* I2C - (Only tested with SLB9673 and ST33 I2C) */
@ -415,6 +420,110 @@
return ret;
}
#endif /* WOLFTPM_I2C */
#ifdef WOLFTPM_HAL_RESET
/* Pulse the TPM nRST (active low) via the Linux GPIO char device (raw GPIO
* v2 uAPI, no libgpiod). Default line: Raspberry Pi ST33 = GPIO24 (pin 18),
* Nuvoton = GPIO4; override with WOLFTPM_RESET_GPIOCHIP / WOLFTPM_RESET_LINE. */
#ifndef WOLFTPM_RESET_GPIOCHIP
#define WOLFTPM_RESET_GPIOCHIP "/dev/gpiochip0"
#endif
#ifndef WOLFTPM_RESET_LINE
#if defined(WOLFTPM_NUVOTON)
#define WOLFTPM_RESET_LINE 4
#else
#define WOLFTPM_RESET_LINE 24
#endif
#endif
#ifndef WOLFTPM_RESET_HOLD_US
#define WOLFTPM_RESET_HOLD_US 300000 /* reset asserted 300ms */
#endif
#ifndef WOLFTPM_RESET_SETTLE_US
#define WOLFTPM_RESET_SETTLE_US 1000000 /* TPM boot settle 1s */
#endif
/* usleep() is undefined for values >= 1000000 (POSIX); nanosleep has no
* such limit and handles the 1s settle and any larger override. */
static void TPM2_Reset_DelayUs(unsigned long us)
{
struct timespec ts;
ts.tv_sec = (time_t)(us / 1000000UL);
ts.tv_nsec = (long)((us % 1000000UL) * 1000UL);
(void)nanosleep(&ts, NULL);
}
/* Note: this reset HAL is only compile-checked in CI (no GPIO hardware or
* gpio-sim there); the open/GET_LINE/SET_VALUES flow, the hold/settle
* timing, and the fd lifecycle are functionally regression-verified on real
* hardware - the ST33 on a Raspberry Pi 5, nRST wired to GPIO24 (pin 18). */
int TPM2_IoCb_Linux_Reset(TPM2_CTX* ctx, void* userCtx)
{
int ret = TPM_RC_FAILURE;
int chipFd, reqFd;
struct gpio_v2_line_request req;
struct gpio_v2_line_values vals;
(void)ctx;
(void)userCtx;
chipFd = open(WOLFTPM_RESET_GPIOCHIP, O_RDONLY);
if (chipFd < 0) {
#ifdef DEBUG_WOLFTPM
printf("TPM Reset: open %s failed (errno %d)\n",
WOLFTPM_RESET_GPIOCHIP, errno);
#endif
return TPM_RC_FAILURE;
}
/* Acquire the line as an output driven low (assert reset) */
XMEMSET(&req, 0, sizeof(req));
req.offsets[0] = (unsigned int)WOLFTPM_RESET_LINE;
req.num_lines = 1;
req.config.flags = GPIO_V2_LINE_FLAG_OUTPUT;
req.config.num_attrs = 1;
req.config.attrs[0].attr.id = GPIO_V2_LINE_ATTR_ID_OUTPUT_VALUES;
req.config.attrs[0].attr.values = 0; /* drive low (assert reset) */
req.config.attrs[0].mask = 1; /* applies to line index 0 */
XMEMCPY(req.consumer, "wolfTPM-reset", sizeof("wolfTPM-reset"));
if (ioctl(chipFd, GPIO_V2_GET_LINE_IOCTL, &req) < 0 || req.fd < 0) {
#ifdef DEBUG_WOLFTPM
printf("TPM Reset: GET_LINE ioctl failed (errno %d)\n", errno);
#endif
close(chipFd);
return TPM_RC_FAILURE;
}
close(chipFd);
reqFd = req.fd;
/* Hold reset asserted, then release (drive high) and let the TPM boot */
TPM2_Reset_DelayUs(WOLFTPM_RESET_HOLD_US);
XMEMSET(&vals, 0, sizeof(vals));
vals.mask = 1;
vals.bits = 1; /* drive high = release reset */
if (ioctl(reqFd, GPIO_V2_LINE_SET_VALUES_IOCTL, &vals) < 0) {
#ifdef DEBUG_WOLFTPM
printf("TPM Reset: SET_VALUES ioctl failed (errno %d)\n", errno);
#endif
}
else {
ret = TPM_RC_SUCCESS;
#ifdef DEBUG_WOLFTPM
printf("TPM Reset: pulsed nRST on %s line %d\n",
WOLFTPM_RESET_GPIOCHIP, (int)WOLFTPM_RESET_LINE);
#endif
/* Only wait for the TPM to settle after a successful release; on a
* failed release the delay would just stall the error path. */
TPM2_Reset_DelayUs(WOLFTPM_RESET_SETTLE_US);
}
close(reqFd);
return ret;
}
#endif /* WOLFTPM_HAL_RESET */
#endif /* __linux__ */
#endif /* !(WOLFTPM_LINUX_DEV || WOLFTPM_SWTPM || WOLFTPM_WINAPI) */
#endif /* WOLFTPM_INCLUDE_IO_FILE */