You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
* NVIDIA Jetson Orin (Tegra234) firmware TPM - a TPM 2.0 running as an OP-TEE trusted application, reached through the Linux kernel driver rather than a bus. See [docs/DEVTPM.md](docs/DEVTPM.md#nvidia-jetson-orin-tegra234-firmware-tpm).
246
247
247
248
#### Device Identification
248
249
@@ -292,6 +293,11 @@ SealSQ QVault TPM 2.0
292
293
TPM2: Caps 0x30000797, Did 0x0083, Vid 0x2406, Rid 0x 3
293
294
Mfg SEAL (6), Vendor QVault TPM, Fw 2.1 (0x3010303), FIPS 140-3, CC-EAL4 0
294
295
296
+
NVIDIA Jetson Orin (Tegra234) OP-TEE firmware TPM, via /dev/tpmrm0
There is no `Caps/Did/Vid/Rid` line above because those values come from TIS bus registers, which a firmware TPM does not have. The entry was captured with `--enable-autodetect`, where `wolfTPM2_Init_ex` returns as soon as the kernel device opens, so the debug line is never reached; an `--enable-devtpm` build still prints it, reading all zeros. `Fw 8216.1808` is `TPM_PT_FIRMWARE_VERSION_1` = `0x20180710`, which this implementation uses to carry a build date (2018-07-10) rather than a version number. Spec revision is 1.62, and all four PCR banks (SHA-1, SHA-256, SHA-384, SHA-512) are allocated with PCRs 0-23.
300
+
295
301
## Building
296
302
297
303
### Building wolfSSL
@@ -463,24 +469,26 @@ idf.py build
463
469
464
470
### Building for "/dev/tpmX"
465
471
466
-
**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.
472
+
**Auto-detection (recommended):**`--enable-autodetect` tries `/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.
467
473
468
474
```bash
469
475
./autogen.sh
470
-
./configure
476
+
./configure --enable-autodetect
471
477
make
472
478
```
473
479
474
-
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.
480
+
**Important:** on Linux `x86_64` and `aarch64`, a bare `./configure` does *not* reach `/dev/tpmX`. On those hosts the software TPMs (swTPM and fwTPM) are auto-enabled so that `make check` works without hardware, and defining `WOLFTPM_SWTPM` suppresses the kernel-device autodetect path. The resulting build talks to a simulator on TCP port 2321, not to your TPM. Selecting any hardware path explicitly - `--enable-autodetect`, `--enable-devtpm`, or any `--enable-<vendor>` - turns the software defaults back off. This matters on single-board machines with a firmware TPM, such as the NVIDIA Jetson Orin, where the kernel device is the only transport.
475
481
476
-
To specify a different `/dev/tpmX` device use `CFLAGS="-DTPM2_LINUX_DEV=/dev/tpm1"`
482
+
Use `--enable-devtpm` to force kernel-driver-only mode, which disables the SPI fallback:
477
483
478
484
```bash
479
485
./autogen.sh
480
486
./configure --enable-devtpm
481
487
make
482
488
```
483
489
490
+
To specify a different `/dev/tpmX` device use `CFLAGS='-DTPM2_LINUX_DEV="/dev/tpm1"'` - the inner quotes are required, since the macro is used directly as a C string literal. To pin the resource manager and never fall back to the raw device, build with `-DWOLFTPM_USE_TPMRM`.
491
+
484
492
The `TPM2_Init` or `wolfTPM2_Init` calls should use NULL for the HAL IO callback argument. The default HAL IO `TPM2_IoCb` maps to a macro specifying NULL (`#define TPM2_IoCb NULL`) in tpm_io.h for the devtpm option.
485
493
486
494
By default the `/dev/tpmX` requires sudo permissions to use it. If using the tpm2-tss it will install a "tss" group that you can add permissions to `sudo adduser [username] tss`.
4) Reboot or reload rules: `sudo udevadm control -R`
507
515
516
+
For the resource manager (`/dev/tpmrm0`) versus the raw device, which operations the kernel refuses, and firmware-TPM platforms such as the NVIDIA Jetson Orin, see [docs/DEVTPM.md](docs/DEVTPM.md).
# wolfTPM with the Linux Kernel TPM Device (/dev/tpmX)
2
+
3
+
On Linux the kernel's TPM driver stack exposes a TPM through a character device, and wolfTPM can use it directly instead of driving SPI or I2C itself. This is the right transport whenever the kernel already owns the TPM: a discrete chip bound to a kernel driver, a Windows-style firmware TPM, or a TEE-resident firmware TPM such as the one on NVIDIA Jetson platforms.
4
+
5
+
With `--enable-devtpm` there is **no TIS layer and no HAL IO callback**: `hal/tpm_io.c` is compiled out entirely and `TPM2_IoCb` is `NULL` (see `hal/tpm_io.h`), so pass `NULL` for the callback argument of `TPM2_Init` / `wolfTPM2_Init`.
6
+
7
+
With `--enable-autodetect` this is **not** the case. The TIS/SPI HAL stays compiled in on purpose - it is the fallback - and `TPM2_IoCb` is a real function. Keep passing it, or the SPI fallback that build exists to provide is unreachable.
8
+
9
+
## Two device nodes
10
+
11
+
The kernel presents up to two nodes per TPM:
12
+
13
+
*`/dev/tpm0` - the raw device. One user at a time, no resource management. Whatever you send reaches the TPM.
14
+
*`/dev/tpmrm0` - the in-kernel resource manager (kernel 4.12+, practical from 5.12+). It virtualizes handles, swaps transient objects and sessions in and out as needed, and flushes everything belonging to a connection when that connection closes.
15
+
16
+
wolfTPM prefers `/dev/tpmrm0` and falls back to `/dev/tpm0`. The resource manager is the better default: a TPM has very few transient object slots, and without it a program that leaks a handle wedges the TPM for everything else on the system.
17
+
18
+
Build-time overrides, honored by both `--enable-devtpm` and `--enable-autodetect`:
19
+
20
+
*`-DWOLFTPM_USE_TPMRM` - use `/dev/tpmrm0` only, with no fallback to the raw device.
21
+
*`CFLAGS='-DTPM2_LINUX_DEV="/dev/tpm1"'` - use a specific node. The inner quotes are required: the macro is used directly as a C string literal, so an unquoted value does not compile.
22
+
23
+
## Startup, shutdown, and shared state
24
+
25
+
The TPM is started by firmware long before Linux runs, and on the resource manager it is shared with every other process on the system. Restarting or shutting it down is therefore not an individual caller's decision, so wolfTPM stays out of the way on this transport:
26
+
27
+
*`wolfTPM2_Init` skips the startup and self-test sequence.
28
+
*`wolfTPM2_Reset` and `wolfTPM2_Shutdown` are no-ops that return success.
29
+
*`wolfTPM2_SetLocality` returns `NOT_COMPILED_IN` - the kernel owns the locality.
30
+
31
+
Be aware that the kernel does **not** reliably stop you here. Command filtering on `/dev/tpmrm0` is primarily about handle isolation, not about blocking global state changes, and behavior varies by kernel version and TPM implementation. On Linux 5.15 with the Jetson OP-TEE fTPM, a `TPM2_Shutdown(TPM_SU_CLEAR)` sent through the resource manager is passed straight through and returns success - both from wolfTPM and from `tpm2_shutdown`. So this is a case where the library declining to send the command is what protects other users of the TPM, rather than the kernel doing it for you.
32
+
33
+
If you genuinely need to control TPM startup state, you need `/dev/tpm0` and exclusive use of the TPM, or direct SPI access with wolfTPM's own TIS driver.
34
+
35
+
## Transient handles do not outlive a process
36
+
37
+
This is the difference most likely to break an existing application.
38
+
39
+
On `/dev/tpmrm0` the kernel gives each open file description its own handle space. Transient object handles are **virtualized** - the value the TPM assigned is not the value you get back - and everything in that space is **flushed when the file descriptor closes**. So a transient key created by one process is gone by the time a second process runs, and the handle number it printed is meaningless to anyone else.
40
+
41
+
Creating a primary key on the Jetson fTPM through the resource manager returns:
42
+
43
+
```
44
+
Create Primary Handle: 0x80ffffff
45
+
```
46
+
47
+
not the `0x80000000` a raw device would report. Query the transient handles from a separate process afterwards and the list is empty:
48
+
49
+
```bash
50
+
tpm2_getcap handles-transient # no output - the space was torn down
51
+
```
52
+
53
+
Two practical consequences:
54
+
55
+
* A "create a key, keep it, use it from the next command" workflow does not work across processes. Do the whole sequence in one process, or make the object persistent with `TPM2_EvictControl` so it gets a stable `0x81xxxxxx` handle that does survive.
56
+
* Passing a hard-coded transient handle such as `0x80000000` on a command line will fail. The kernel rejects the reference before it reaches the TPM, and because that happens at the file-descriptor layer the error surfaces as an `errno 22 = Invalid argument` on `read()`, which wolfTPM reports as `TPM_RC_FAILURE` rather than as a handle error. If you see `TPM_RC_FAILURE` alongside `Failed to read from /dev/tpmrm0 ... errno 22`, suspect a stale or cross-process transient handle before suspecting the TPM.
57
+
58
+
wolfTPM's own `examples/run_examples.sh` hits exactly this: its provisioning section creates IAK and IDevID primaries with `-keep` in one process and then references `0x80000000` / `0x80000001` from another. That block cannot pass on the resource manager by construction. Everything either side of it is unaffected. Use `/dev/tpm0` with exclusive access if you need to run it as written.
59
+
60
+
## Building
61
+
62
+
```bash
63
+
./autogen.sh
64
+
./configure --enable-devtpm
65
+
make
66
+
```
67
+
68
+
`--enable-devtpm` uses the kernel node only. Use `--enable-autodetect` instead if you want wolfTPM to try `/dev/tpmrm0`, then `/dev/tpm0`, and finally fall back to probing SPI - useful for one binary that has to run on several boards.
69
+
70
+
Only one transport can be enabled at a time. `--enable-devtpm` conflicts with `--enable-swtpm` and `--enable-winapi`, and configure will stop if you ask for more than one.
71
+
72
+
### The x86_64 / aarch64 default
73
+
74
+
A bare `./configure` on Linux `x86_64` or `aarch64` does **not** produce a build that talks to `/dev/tpmX`. On those hosts wolfTPM auto-enables the software TPMs (swTPM and fwTPM) so that `make check` passes with no hardware attached, and defining `WOLFTPM_SWTPM` suppresses the kernel-device autodetect path. The result talks to a simulator on TCP port 2321.
75
+
76
+
Selecting any hardware path explicitly turns that default back off - `--enable-autodetect`, `--enable-devtpm`, or any `--enable-<vendor>`. Configure prints a notice when the software default is taken, so check the tail of its output if a build unexpectedly fails to find your TPM.
77
+
78
+
This bites hardest on single-board `aarch64` machines with a firmware TPM, where the kernel device is the only transport there is.
79
+
80
+
## Permissions
81
+
82
+
The TPM character devices are not world-accessible. On a typical system they are mode `0660` owned by group `tss`:
83
+
84
+
```
85
+
crw-rw---- 1 tss root 10, 224 /dev/tpm0
86
+
crw-rw---- 1 tss tss 252, 65536 /dev/tpmrm0
87
+
```
88
+
89
+
wolfTPM detects `EACCES` and reports it plainly:
90
+
91
+
```
92
+
Permission denied on /dev/tpm0
93
+
Use sudo or add tss group to user.
94
+
```
95
+
96
+
The fix is to put your user in the owning group and start a new login session:
97
+
98
+
```bash
99
+
sudo usermod -aG tss $USER
100
+
```
101
+
102
+
Note that the `tss` group is created by tpm2-tss, and on distributions that ship it the group frequently exists with no members - so this step is required even though the group looks correctly set up.
103
+
104
+
To use a group of your own instead, add a udev rule:
3) Reload the rules: `sudo udevadm control -R`, then re-plug or reboot.
120
+
121
+
## NVIDIA Jetson Orin (Tegra234) firmware TPM
122
+
123
+
Jetson Orin platforms carry a TPM 2.0 implemented in firmware, running as a trusted application inside OP-TEE rather than as a discrete package on a bus. Linux reaches it through the `tpm_ftpm_tee` driver, which speaks to the TA over the TEE interface and registers an ordinary TPM chip - so from wolfTPM's point of view it is just another `/dev/tpmrm0`.
If the module is missing, try `sudo modprobe tpm_ftpm_tee` and check that the kernel was configured with `CONFIG_TCG_FTPM_TEE`. On NVIDIA's Jetson Linux (L4T) images the driver is present and an `fTPM Device Provisioning Service` systemd unit runs at boot; you can see it complete in the boot log.
134
+
135
+
Note that an OP-TEE boot message about silicon-identity fTPM provisioning not being enabled refers to a separate NVIDIA feature and does **not** mean the TPM 2.0 device is unavailable.
136
+
137
+
Build as above with `--enable-devtpm` or `--enable-autodetect`, then confirm with:
138
+
139
+
```bash
140
+
./examples/wrap/caps
141
+
```
142
+
143
+
Because this is a firmware TPM, expect two differences from a discrete part. There is no TIS bus, so the `TPM2: Caps/Did/Vid/Rid` values do not exist and the device is identified purely from `TPM2_GetCapability` properties. Under `--enable-devtpm` the `DEBUG_WOLFTPM` line is still printed but reads all zeros; under `--enable-autodetect``wolfTPM2_Init_ex` returns as soon as the kernel device opens, before that printf, so the line is absent entirely. And its algorithm coverage is set by the firmware build rather than by a datasheet, so some operations the benchmark exercises may report as unsupported; that is expected, not a fault.
144
+
145
+
See the main [README.md](/README.md#device-identification) for this platform's identification values and benchmark results.
146
+
147
+
## Testing
148
+
149
+
The examples run unchanged on this transport:
150
+
151
+
```bash
152
+
./examples/wrap/caps
153
+
./examples/native/native_test
154
+
./examples/wrap/wrap_test
155
+
./examples/bench/bench
156
+
./examples/run_examples.sh
157
+
```
158
+
159
+
`run_examples.sh` already skips the locality test on backends that do not support it.
160
+
161
+
## CI coverage
162
+
163
+
Both `--enable-devtpm` and `--enable-autodetect` are build-tested in CI, but not run - GitHub-hosted runners have no `/dev/tpm*` node. Runtime coverage of this transport requires a self-hosted runner with a real TPM bound to the kernel driver.
0 commit comments