Skip to content

Commit 0aefc3b

Browse files
committed
Add auto detection on linux
1 parent a014432 commit 0aefc3b

10 files changed

Lines changed: 208 additions & 19 deletions

File tree

.github/workflows/make-test-swtpm.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,16 @@ jobs:
102102
wolftpm_config: --enable-advio
103103
needs_swtpm: false
104104

105+
# Autodetect (default configure, /dev/tpm0 + SPI dual support)
106+
- name: autodetect
107+
wolftpm_config: ""
108+
needs_swtpm: false
109+
110+
# Autodetect with debug
111+
- name: autodetect-debug
112+
wolftpm_config: --enable-debug
113+
needs_swtpm: false
114+
105115
# Clang ASAN
106116
- name: clang-asan
107117
wolftpm_cflags: "-fsanitize=address -fno-omit-frame-pointer -g"

README.md

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ Portable TPM 2.0 project designed for embedded use.
99
* Wrappers provided to simplify Key Generation/Loading, RSA encrypt/decrypt, ECC sign/verify, ECDH, NV, Hashing/HACM, AES, Sealing/Unsealing, Attestation, PCR Extend/Quote and Secure Root of Trust.
1010
* Testing done using TPM 2.0 modules from STMicro ST33 (SPI/I2C), Infineon OPTIGA SLB9670/SLB9672/SLB9673, Microchip ATTPM20, Nations Tech Z32H330TC/NS350 and Nuvoton NPCT650/NPCT750.
1111
* wolfTPM uses the TPM Interface Specification (TIS) to communicate either over SPI, or using a memory mapped I/O range.
12+
* 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.
1213
* wolfTPM can also use the Linux TPM kernel interface (`/dev/tpmX`) to talk with any physical TPM on SPI, I2C and even LPC bus.
1314
* Platform support for Raspberry Pi (Linux), MMIO, STM32 with CubeMX, Atmel ASF, Xilinx, QNX Infineon TriCore and Barebox.
1415
* The design allows for easy portability to different platforms:
@@ -192,12 +193,16 @@ make install
192193
--enable-firmware Enable firmware upgrade support for Infineon SLB9672/SLB9673 and ST ST33 (default: disabled) - WOLFTPM_FIRMWARE_UPGRADE
193194
194195
--enable-autodetect Enable Runtime Module Detection (default: enable - when no module specified) - WOLFTPM_AUTODETECT
196+
On Linux this also auto-detects /dev/tpmrm0 or /dev/tpm0 at runtime,
197+
falling back to SPI if the kernel driver is not available.
195198
--enable-infineon Enable Infineon SLB9670/SLB9672/SLB9673 TPM Support (default: disabled) - WOLFTPM_SLB9670 / WOLFTPM_SLB9672
196199
--enable-st Enable ST ST33 Support (default: disabled) - WOLFTPM_ST33
197200
--enable-microchip Enable Microchip ATTPM20 Support (default: disabled) - WOLFTPM_MICROCHIP
198201
--enable-nuvoton Enable Nuvoton NPCT65x/NPCT75x Support (default: disabled) - WOLFTPM_NUVOTON
199202
200203
--enable-devtpm Enable using Linux kernel driver for /dev/tpmX (default: disabled) - WOLFTPM_LINUX_DEV
204+
Note: With autodetect (default) this is no longer required on Linux;
205+
the kernel driver is tried automatically before SPI.
201206
--enable-swtpm Enable using SWTPM TCP protocol. For use with simulator. (default: disabled) - WOLFTPM_SWTPM
202207
--enable-winapi Use Windows TBS API. (default: disabled) - WOLFTPM_WINAPI
203208
@@ -283,7 +288,15 @@ idf.py build
283288

284289
### Building for "/dev/tpmX"
285290

286-
The `--enable-devtpm` or `WOLFTPM_LINUX_DEV` build option allows you to use the Linux supplied TPM (TIS) driver.
291+
**Auto-detection (recommended):** On Linux, a default `./configure && make` will automatically try `/dev/tpmrm0` then `/dev/tpm0` at runtime. If the kernel driver is available it will be used; otherwise wolfTPM falls back to direct SPI access. No special configure options are needed.
292+
293+
```bash
294+
./autogen.sh
295+
./configure
296+
make
297+
```
298+
299+
Previously, using the kernel TPM driver required the `--enable-devtpm` flag. This is no longer necessary with autodetect (enabled by default). You can still use `--enable-devtpm` to force kernel-driver-only mode, which disables SPI fallback.
287300

288301
To specify a different `/dev/tpmX` device use `CFLAGS="-DTPM2_LINUX_DEV=/dev/tpm1"`
289302

hal/tpm_io_linux.c

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -99,9 +99,7 @@
9999
static char TPM2_SPI_DEV[] = TPM2_SPI_DEV_PATH "0";
100100
#define MAX_SPI_DEV_CS '4'
101101
static int foundSpiDev = 0;
102-
#ifdef DEBUG_WOLFTPM
103102
static int spiDevNotFound = 0;
104-
#endif
105103
#else
106104
#define TPM2_SPI_DEV TPM2_SPI_DEV_PATH TPM2_SPI_DEV_CS
107105
static int spiOpenFailed = 0;
@@ -218,6 +216,39 @@
218216
}
219217

220218
#else
219+
/* Called when SPI device cannot be opened or no TPM found on SPI bus.
220+
* Checks if the Linux kernel TPM driver is available and suggests
221+
* alternatives. */
222+
static void spiOpenFailedMessage(void)
223+
{
224+
#ifdef WOLFTPM_LINUX_DEV_AUTODETECT
225+
/* Autodetect already tried /dev/tpm0; SPI also failed */
226+
#ifdef DEBUG_WOLFTPM
227+
printf("Neither /dev/tpm0 nor SPI bus produced a TPM response.\n"
228+
"Ensure a TPM is connected and the kernel driver or spidev "
229+
"is enabled.\n");
230+
#endif
231+
#else
232+
if (access("/dev/tpm0", F_OK) == 0 ||
233+
access("/dev/tpmrm0", F_OK) == 0) {
234+
printf("TPM kernel driver detected (/dev/tpm0).\n"
235+
"Either build wolfTPM with ./configure --enable-devtpm\n"
236+
"or disable the kernel driver by commenting out the TPM\n"
237+
"overlay in /boot/config.txt or /boot/firmware/config.txt\n"
238+
"and enable spidev to use direct SPI access.\n");
239+
}
240+
#ifdef DEBUG_WOLFTPM
241+
else {
242+
printf("If using Linux kernel TPM driver (/dev/tpm0), "
243+
"build with --enable-devtpm.\n"
244+
"To use SPI directly, make sure /dev/spidev is available "
245+
"and the TPM\nkernel overlay is disabled in /boot/config.txt "
246+
"or /boot/firmware/config.txt.\n");
247+
}
248+
#endif
249+
#endif /* WOLFTPM_LINUX_DEV_AUTODETECT */
250+
}
251+
221252
/* Use Linux SPI synchronous access */
222253
int TPM2_IoCb_Linux_SPI(TPM2_CTX* ctx, const byte* txBuf, byte* rxBuf,
223254
word16 xferSz, void* userCtx)
@@ -336,12 +367,13 @@
336367
"Use sudo or check device permissions.\n",
337368
TPM2_SPI_DEV);
338369
}
339-
#ifdef DEBUG_WOLFTPM
340370
else {
371+
#ifdef DEBUG_WOLFTPM
341372
printf("Failed to open SPI device %s (errno %d)\n",
342373
TPM2_SPI_DEV, errno);
374+
#endif
375+
spiOpenFailedMessage();
343376
}
344-
#endif
345377
}
346378
#endif
347379
}
@@ -362,13 +394,14 @@
362394
TPM2_SPI_DEV[devLen-1]++;
363395
goto tryagain;
364396
}
365-
#ifdef DEBUG_WOLFTPM
366397
if (!spiDevNotFound) {
367398
spiDevNotFound = 1;
399+
#ifdef DEBUG_WOLFTPM
368400
printf("TPM not found on SPI bus %s[0-%c]\n",
369401
TPM2_SPI_DEV_PATH, MAX_SPI_DEV_CS);
402+
#endif
403+
spiOpenFailedMessage();
370404
}
371-
#endif
372405
}
373406
}
374407
#endif

src/include.am

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,8 @@ src_libwolftpm_la_SOURCES = \
1111
src/tpm2_wrap.c \
1212
src/tpm2_asn.c \
1313
src/tpm2_param_enc.c \
14-
src/tpm2_cryptocb.c
15-
16-
if BUILD_DEVTPM
17-
src_libwolftpm_la_SOURCES += src/tpm2_linux.c
18-
endif
14+
src/tpm2_cryptocb.c \
15+
src/tpm2_linux.c
1916
if BUILD_SWTPM
2017
src_libwolftpm_la_SOURCES += src/tpm2_swtpm.c
2118
endif

src/tpm2.c

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@ static THREAD_LS_T TPM2_CTX* gActiveTPM;
6060
#ifdef WOLFTPM_LINUX_DEV
6161
#define INTERNAL_SEND_COMMAND TPM2_LINUX_SendCommand
6262
#define TPM2_INTERNAL_CLEANUP(ctx)
63+
#elif defined(WOLFTPM_LINUX_DEV_AUTODETECT)
64+
#define INTERNAL_SEND_COMMAND TPM2_LINUX_AUTODETECT_SendCommand
65+
#define TPM2_INTERNAL_CLEANUP(ctx)
6366
#elif defined(WOLFTPM_SWTPM)
6467
#define INTERNAL_SEND_COMMAND TPM2_SWTPM_SendCommand
6568
#define TPM2_INTERNAL_CLEANUP(ctx)
@@ -647,6 +650,13 @@ TPM_RC TPM2_Init_ex(TPM2_CTX* ctx, TPM2HalIoCb ioCb, void* userCtx,
647650
if (ioCb != NULL || userCtx != NULL) {
648651
return BAD_FUNC_ARG;
649652
}
653+
#elif defined(WOLFTPM_LINUX_DEV_AUTODETECT)
654+
/* Accept IO callback for SPI fallback path */
655+
if (ioCb != NULL) {
656+
rc = TPM2_SetHalIoCb(ctx, ioCb, userCtx);
657+
if (rc != TPM_RC_SUCCESS)
658+
return rc;
659+
}
650660
#else
651661
#ifdef WOLFTPM_MMIO
652662
if (ioCb == NULL)
@@ -658,14 +668,18 @@ TPM_RC TPM2_Init_ex(TPM2_CTX* ctx, TPM2HalIoCb ioCb, void* userCtx,
658668
return rc;
659669
#endif
660670

661-
#ifdef WOLFTPM_LINUX_DEV
671+
#if defined(WOLFTPM_LINUX_DEV) || defined(WOLFTPM_LINUX_DEV_AUTODETECT)
662672
ctx->fd = -1;
663673
#endif
664674

665675
/* Set the active TPM global */
666676
TPM2_SetActiveCtx(ctx);
667677

668-
if (timeoutTries > 0) {
678+
if (timeoutTries > 0
679+
#ifdef WOLFTPM_LINUX_DEV_AUTODETECT
680+
&& ctx->ioCb != NULL /* autodetect: skip if no IO callback */
681+
#endif
682+
) {
669683
/* Perform chip startup and assign locality */
670684
rc = TPM2_ChipStartup(ctx, timeoutTries);
671685
}
@@ -729,7 +743,8 @@ TPM_RC TPM2_Cleanup(TPM2_CTX* ctx)
729743
}
730744
#endif /* !WOLFTPM2_NO_WOLFCRYPT */
731745

732-
#if defined(WOLFTPM_LINUX_DEV) && !defined(__UBOOT__)
746+
#if (defined(WOLFTPM_LINUX_DEV) || defined(WOLFTPM_LINUX_DEV_AUTODETECT)) \
747+
&& !defined(__UBOOT__)
733748
if (ctx->fd >= 0)
734749
close(ctx->fd);
735750
#endif

src/tpm2_linux.c

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525

2626
#include <wolftpm/tpm2_types.h>
2727

28-
#ifdef WOLFTPM_LINUX_DEV
28+
#if defined(WOLFTPM_LINUX_DEV) || defined(WOLFTPM_LINUX_DEV_AUTODETECT)
2929
#include <wolftpm/tpm2_linux.h>
3030
#include <wolftpm/tpm2_packet.h>
3131

@@ -194,4 +194,46 @@ int TPM2_LINUX_SendCommand(TPM2_CTX* ctx, TPM2_Packet* packet)
194194
return rc;
195195
}
196196
#endif /* __UBOOT__ __linux__ */
197-
#endif /* WOLFTPM_LINUX_DEV */
197+
198+
#ifdef WOLFTPM_LINUX_DEV_AUTODETECT
199+
#include <wolftpm/tpm2_tis.h>
200+
201+
int TPM2_LINUX_TryOpen(TPM2_CTX* ctx)
202+
{
203+
/* Try resource manager first (kernel 4.12+), then raw device */
204+
ctx->fd = open("/dev/tpmrm0", O_RDWR | O_NONBLOCK);
205+
if (ctx->fd >= 0) {
206+
#ifdef DEBUG_WOLFTPM
207+
printf("Opened /dev/tpmrm0\n");
208+
#endif
209+
return TPM_RC_SUCCESS;
210+
}
211+
212+
ctx->fd = open("/dev/tpm0", O_RDWR | O_NONBLOCK);
213+
if (ctx->fd >= 0) {
214+
#ifdef DEBUG_WOLFTPM
215+
printf("Opened /dev/tpm0\n");
216+
#endif
217+
return TPM_RC_SUCCESS;
218+
}
219+
220+
/* Distinguish "not available" from "permission denied" */
221+
if (errno == EACCES) {
222+
printf("Permission denied on /dev/tpm0\n"
223+
"Use sudo or add tss group to user.\n");
224+
return TPM_RC_FAILURE;
225+
}
226+
227+
/* ENOENT or other: device not present, caller should try SPI */
228+
return TPM_RC_INITIALIZE; /* sentinel: "not found, try next" */
229+
}
230+
231+
int TPM2_LINUX_AUTODETECT_SendCommand(TPM2_CTX* ctx, TPM2_Packet* packet)
232+
{
233+
if (ctx->fd >= 0)
234+
return TPM2_LINUX_SendCommand(ctx, packet);
235+
return TPM2_TIS_SendCommand(ctx, packet);
236+
}
237+
#endif /* WOLFTPM_LINUX_DEV_AUTODETECT */
238+
239+
#endif /* WOLFTPM_LINUX_DEV || WOLFTPM_LINUX_DEV_AUTODETECT */

src/tpm2_wrap.c

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@
3131
/* For some struct to buffer conversions */
3232
#include <wolftpm/tpm2_packet.h>
3333
#include <hal/tpm_io.h> /* for default IO callback */
34+
#ifdef WOLFTPM_LINUX_DEV_AUTODETECT
35+
#include <wolftpm/tpm2_linux.h>
36+
#endif
3437

3538
/* Local Functions */
3639
static int wolfTPM2_GetCapabilities_NoDev(WOLFTPM2_CAPS* cap);
@@ -63,7 +66,53 @@ static int wolfTPM2_Init_ex(TPM2_CTX* ctx, TPM2HalIoCb ioCb, void* userCtx,
6366
if (ctx == NULL)
6467
return BAD_FUNC_ARG;
6568

66-
#if defined(WOLFTPM_LINUX_DEV) || defined(WOLFTPM_SWTPM) || \
69+
#if defined(WOLFTPM_LINUX_DEV_AUTODETECT)
70+
/* Phase 1: Minimal init (sets up IO callback, no TIS chip startup) */
71+
rc = TPM2_Init_ex(ctx, ioCb, userCtx, 0);
72+
if (rc != TPM_RC_SUCCESS) {
73+
printf("TPM2_Init failed 0x%x: %s\n", rc, wolfTPM2_GetRCString(rc));
74+
return rc;
75+
}
76+
77+
/* Phase 2: Try /dev/tpmrm0 then /dev/tpm0 */
78+
rc = TPM2_LINUX_TryOpen(ctx);
79+
if (rc == TPM_RC_SUCCESS) {
80+
/* Using kernel driver - startup/locality handled by kernel */
81+
#ifdef DEBUG_WOLFTPM
82+
printf("TPM2: Using Linux kernel driver\n");
83+
#endif
84+
return TPM_RC_SUCCESS;
85+
}
86+
else if (rc == TPM_RC_FAILURE) {
87+
/* Permission denied or hard error - don't try SPI */
88+
return rc;
89+
}
90+
/* rc == TPM_RC_INITIALIZE means "not found", fall through to SPI */
91+
92+
/* Phase 3: SPI fallback - requires IO callback */
93+
if (ioCb == NULL) {
94+
#ifdef DEBUG_WOLFTPM
95+
printf("TPM2: Kernel driver not available and no IO callback for SPI\n");
96+
#endif
97+
return TPM_RC_FAILURE;
98+
}
99+
#ifdef DEBUG_WOLFTPM
100+
printf("TPM2: Kernel driver not available, trying SPI\n");
101+
#endif
102+
if (timeoutTries > 0) {
103+
rc = TPM2_ChipStartup(ctx, timeoutTries);
104+
if (rc != TPM_RC_SUCCESS) {
105+
printf("TPM2_Init failed 0x%x: %s\n", rc,
106+
wolfTPM2_GetRCString(rc));
107+
return rc;
108+
}
109+
}
110+
else {
111+
rc = TPM_RC_SUCCESS; /* clear TryOpen sentinel */
112+
}
113+
/* Fall through to Startup/SelfTest below */
114+
115+
#elif defined(WOLFTPM_LINUX_DEV) || defined(WOLFTPM_SWTPM) || \
67116
defined(WOLFTPM_WINAPI)
68117
rc = TPM2_Init_minimal(ctx);
69118
/* Using standard file I/O for the Linux TPM device */
@@ -8254,6 +8303,9 @@ static int tpm2_ifx_firmware_start(WOLFTPM2_DEV* dev, TPM_ALG_ID hashAlg,
82548303
#if !defined(WOLFTPM_LINUX_DEV) && !defined(WOLFTPM_SWTPM) && \
82558304
!defined(WOLFTPM_WINAPI)
82568305
/* Do chip startup and request locality again */
8306+
#ifdef WOLFTPM_LINUX_DEV_AUTODETECT
8307+
if (dev->ctx.fd < 0) /* Only needed for SPI path */
8308+
#endif
82578309
rc = TPM2_ChipStartup(&dev->ctx, 10);
82588310
#endif
82598311
}
@@ -8367,6 +8419,9 @@ static int tpm2_ifx_firmware_data(WOLFTPM2_DEV* dev,
83678419
#if !defined(WOLFTPM_LINUX_DEV) && !defined(WOLFTPM_SWTPM) && \
83688420
!defined(WOLFTPM_WINAPI)
83698421
/* Do chip startup and request locality again */
8422+
#ifdef WOLFTPM_LINUX_DEV_AUTODETECT
8423+
if (dev->ctx.fd < 0) /* Only needed for SPI path */
8424+
#endif
83708425
rc = TPM2_ChipStartup(&dev->ctx, 10);
83718426
#endif
83728427
}
@@ -8610,6 +8665,9 @@ static int tpm2_st33_firmware_start_common(WOLFTPM2_DEV* dev,
86108665
#if !defined(WOLFTPM_LINUX_DEV) && !defined(WOLFTPM_SWTPM) && \
86118666
!defined(WOLFTPM_WINAPI)
86128667
/* Do chip startup and request locality again */
8668+
#ifdef WOLFTPM_LINUX_DEV_AUTODETECT
8669+
if (dev->ctx.fd < 0) /* Only needed for SPI path */
8670+
#endif
86138671
rc = TPM2_ChipStartup(&dev->ctx, 10);
86148672
#endif
86158673
}
@@ -8732,6 +8790,9 @@ static int tpm2_st33_firmware_data(WOLFTPM2_DEV* dev,
87328790
#if !defined(WOLFTPM_LINUX_DEV) && !defined(WOLFTPM_SWTPM) && \
87338791
!defined(WOLFTPM_WINAPI)
87348792
/* Do chip startup and request locality again */
8793+
#ifdef WOLFTPM_LINUX_DEV_AUTODETECT
8794+
if (dev->ctx.fd < 0) /* Only needed for SPI path */
8795+
#endif
87358796
rc = TPM2_ChipStartup(&dev->ctx, 10);
87368797
#endif
87378798
}

wolftpm/tpm2.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1899,7 +1899,7 @@ typedef struct TPM2_CTX {
18991899
unsigned int rngInit:1;
19001900
#endif
19011901
#endif
1902-
#ifdef WOLFTPM_LINUX_DEV
1902+
#if defined(WOLFTPM_LINUX_DEV) || defined(WOLFTPM_LINUX_DEV_AUTODETECT)
19031903
int fd;
19041904
#endif
19051905
} TPM2_CTX;

wolftpm/tpm2_linux.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,17 @@
3232
/* TPM2 IO for using TPM through the Linux kernel driver */
3333
WOLFTPM_LOCAL int TPM2_LINUX_SendCommand(TPM2_CTX* ctx, TPM2_Packet* packet);
3434

35+
#ifdef WOLFTPM_LINUX_DEV_AUTODETECT
36+
/* Try opening /dev/tpmrm0 then /dev/tpm0. Returns TPM_RC_SUCCESS if opened,
37+
* sets ctx->fd. On EACCES prints permission message and returns FAILURE.
38+
* Returns TPM_RC_INITIALIZE if device not found (caller should try SPI). */
39+
WOLFTPM_LOCAL int TPM2_LINUX_TryOpen(TPM2_CTX* ctx);
40+
41+
/* Runtime dispatch: uses /dev/tpm0 if ctx->fd >= 0, otherwise TIS/SPI */
42+
WOLFTPM_LOCAL int TPM2_LINUX_AUTODETECT_SendCommand(TPM2_CTX* ctx,
43+
TPM2_Packet* packet);
44+
#endif
45+
3546
#ifdef __cplusplus
3647
} /* extern "C" */
3748
#endif

0 commit comments

Comments
 (0)