fTPM still failing with 0xffff0008 after EKS/EKB regeneration

Hi,

this is a follow-up to my previous thread:
https://forums.developer.nvidia.com/t/accessing-ftpm-helper-from-a-trusted-application-on-jetson/344657/27

In that thread, my fuse XML was confirmed as correct, and I was told to re-generate the EKS image (eks_t234.img) with my own keys using the EKB tools before flashing. I’ve done that, but fTPM still doesn’t come up.


Platform

  • Board: Jetson Orin Nano Dev Kit

  • JetPack / L4T: 6.2 (36.x)

  • Secure Boot: enabled, board fused (SBK + PKC, plus Kdk0/OemK1 etc. for fTPM)

  • Root/UDA: LUKS-encrypted, system boots fine

  • TEE: OP-TEE from nvidia-jetson-optee-source

  • fTPM: Microsoft fTPM in OP-TEE, Linux driver tpm_ftpm_tee

On the device:

tr -d '\0' < /proc/device-tree/firmware/ftpm/status
# -> "okay"

ls -l /lib/optee_armtz | egrep 'bc50d971|ftpm'
# bc50d971-d4c9-42c4-82cb-343fb7f37896.ta

So the DT node is okay and the TA with the expected UUID is present.


What I did for EKS/EKB

On the host I followed NVIDIA’s “Tool for EKB Generation” flow (offline provisioning):

  1. KDK generation
./kdk_gen.py --oem_id 0x00000102 --sn 0x0000000100000001 --num_devices 1

ftpm_kdk/kdk_db_0102-0000000100000001-1.csv

  1. ODM EKB (offline)
python3 odm_ekb_gen.py \
  --kdk_db ./ftpm_kdk/kdk_db_0102-0000000100000001-1.csv \
  --prov_mode offline

odm_out/ftpm_eks_0102-0000000100000001.img + EK CSRs / certificates

  1. OEM EKB
python3 oem_ekb_gen.py \
  -oem_k1_key oem_keys/oem_k1.key \
  -in_sym_key  oem_keys/sym_t234.key \
  -in_sym_key2 oem_keys/sym2_t234.key \
  -in_auth_key oem_keys/auth_t234.key \
  -in_ftpm_odm_ekb odm_out

oem_out/eks_0102-0000000100000001.img
oem_out/signed/eks_0102-0000000100000001_sigheader_encrypt.img.signed

  1. Integration into L4T
cd Linux_for_Tegra
sudo mkdir -p tools/kernel_flash/images/internal/ekb_db

sudo cp \
  /path/to/optee/samples/ftpm-helper/host/tool/oem_out/signed/eks_0102-0000000100000001_sigheader_encrypt.img.signed \
  tools/kernel_flash/images/internal/ekb_db/

Then I re-generated QSPI/initrd images with l4t_initrd_flash.sh and reflashed. Secure Boot and disk encryption still work as expected.


Current problem on the board

When I try to load the fTPM driver:

sudo modprobe tpm_ftpm_tee
dmesg | grep -i ftpm | tail -n 10

I get:

I/TC: WARNING (insecure configuration): Failed to commit dirh counter 2
E/LD:   init_elf:493 sys_open_ta_bin(bc50d971-d4c9-42c4-82cb-343fb7f37896)
E/TC:?? 00 ldelf_init_with_ldelf:152 ldelf failed with res: 0xffff0008
[   41.827604] ftpm-tee firmware:ftpm: ftpm_tee_probe: tee_client_open_session failed, err=ffff0008
[   41.827657] ftpm-tee: probe of firmware:ftpm failed with error -22

And:

ls -l /dev/tpm*
# -> No such file or directory

So:

  • DT node firmware/ftpm is "okay",

  • fTPM TA with UUID bc50d971-d4c9-42c4-82cb-343fb7f37896 is present in /lib/optee_armtz,

  • but OP-TEE (ldelf) fails to load it with TEE_ERROR_ITEM_NOT_FOUND (0xffff0008) during tpm_ftpm_tee probe.


Questions

  1. EKS/EKB integration on Orin Nano
    Is it sufficient to copy
    eks_0102-0000000100000001_sigheader_encrypt.img.signed
    into tools/kernel_flash/images/internal/ekb_db/ and reflash, or does MB2/OP-TEE expect a specific filename (e.g. eks_t234.img) or configuration so that this EKS is actually used for fTPM?

  2. Root cause for 0xffff0008 when loading fTPM TA
    Given that the DT node is okay and the TA file exists: what are typical reasons on T234/Orin Nano why ldelf would still return TEE_ERROR_ITEM_NOT_FOUND for the fTPM TA?

  3. Tool / CSR compatibility
    I had to adjust the Python CSR helper (ftpm_ek_csr.py) slightly to satisfy current asn1crypto/oscrypto (mainly around subjectAltName and extension_request structure). Could a malformed EK CSR / EK cert / EKS structure cause this early TA-load error, or would that normally only fail later inside the TA logic?

Any hints on how to verify that my new EKS image is really being consumed by the boot chain and OP-TEE would be very helpful.

Thanks,
Niklas

hello nik01flink,

did you load the fTPM driver module after tee-supplicant startup?
you may refer to /etc/systemd/nv-tee-supplicant.sh, please confirm that both modules (tee and optee kernel modules) have been loaded before launching tee-supplicant.

it’s fTPM boot flow verifies and measures the integrity of firmware components during the boot process.
please also check PCR to confirm whether measured boot is active.
$ sudo modprobe tpm_ftpm_tee
$ sudo tpm2_pcrread

hello nik01flink,

BTW,
please also setup serial console for gathering complete bootloader logs.
if that’s the error of the EKB image extraction failed..

E/TC:0 0 ekb_extraction_process:319 Tried all EKB_RKs but still can't extract the EKB image.
E/TC:0 0 jetson_user_key_pta_init:898 jetson_user_key_pta_init: Failed (ffff000f).

that means OP-TEE can’t authenticate and decrypt the EKB image,
it might due to incorrect root EKB key for decryption and encryption.

Hi JerryChang,

first of all: it does work now in the sense that I can see /dev/tpm0 and /dev/tpmrm0, and tpm2_pcrread works. 😅 All I actually did on the running system was:

sudo systemctl restart nv-tee-supplicant.service
sudo modprobe tpm_ftpm_tee

After that I get:

$ lsmod | egrep 'tee|optee|tpm'
tpm_ftpm_tee  16384  0
tpm_tis_spi   16384  0
tpm_tis_core  28672  1 tpm_tis_spi

and

$ ls -l /dev/tpm*
crw-rw---- 1 tss root 10, 224    /dev/tpm0
crw-rw---- 1 tss tss  252, 65536 /dev/tpmrm0

So I assume the fTPM itself is basically functional.
Right now I have to modprobe tpm_ftpm_tee manually after every reboot. Is that expected on Orin Nano, or should the fTPM driver normally be loaded automatically during boot?


I actually built TF-A with measured boot enabled, but looking at the PCRs it seems like it’s not active yet. tpm2_pcrread shows (for all banks: sha1 / sha256 / sha384 / sha512):

  • PCR 0–16 and 23 = 0x00…00

  • PCR 17–22 = 0xFF…FF

So everything is either all zeros or all 0xFF; so no real measurements. From my understanding, with measured boot enabled I should see some PCRs change after a reboot (e.g. bootloader / firmware being extended), correct?

I also checked for EKB / OP-TEE errors you mentioned:

sudo dmesg | egrep -i 'E/TC|ekb|user_key|ftpm'
journalctl -k -b | egrep -i 'ekb|user_key|ftpm'

Both commands print nothing, so I don’t see ekb_extraction_process or jetson_user_key_pta_init errors. That makes me think the EKB is at least not obviously failing, but the PCR pattern looks like no measured boot path is wired up yet.

So ..

  1. Does this PCR pattern (0x00 for 0–16/23, 0xFF for 17–22 in all banks) match a “fTPM present but no measured boot” situation, or could it still point to a wrong EKS/EKB/fuse setup?

  2. On Orin Nano specifically, what should I double-check in TF-A / UEFI / OP-TEE (configs or logs) to make sure the fTPM is actually used for measured boot and PCR extension during boot, not just as a runtime TPM device in Linux?

Thanks again for your help!

hello nik01flink,

(1) there’s /etc/systemd/system/nv-tee-supplicant.service, which should loading tpm module automatically during boot.
please dig into the service of the for loop, you may try similar approach to avoid the loading failure.

(2) you may see-also Topic 347095, let me re-cap as below..

here’s known issue that only PCR0 ~ PCR7 were survived during the early boot flow. we’ve bug fixes to extend the number of PCR registers backup/restore (from 7 to 15) to support additional PCR extendings during early boot stage.

Hello! We can also consider using SecEdge SEC-TPM, which is an implementation of the NVIDIA fTPM . Nvidia also suggest SecEdge SEC-TPM development kits available online now

Thanks for the info in the previous thread but unfortunately I’m still stuck and not sure whether what I’m seeing is expected behavior or a real issue.

I rebuilt OP-TEE (with ftpm activated) and ATF (with Measured Boot = 1) from scratch, regenerated and flashed tos-optee_t234.imgand on the target, nv-tee-supplicant.service is enabled and running. But I have no clue where to start fixing my issue. Because (as written in my last thread) this service / the fTPM only works after I run

sudo systemctl restart nv-tee-supplicant.service
sudo modprobe tpm_ftpm_tee

This is critical for my goal since if fTPM is only brought up later after manual intervention, then PCRs are empty/incorrect at boot time, and Measured Boot can’t work.

So .. Is this even the correct way to achieve my goal? And do you have any idea what I am missing out?

Besides that: On the host I have ~25 .ta files under: optee/install/t234/lib/optee_armtz/
But after flashing, I only find ~20 in /lib/optee_armtz/ on the Jetson. Shouldn’t all .ta files from optee/install/... be copied into the target rootfs automatically? If not, where is the list/selection of TAs defined that get packaged into the image?

If needed, I can provide logs/outputs. Thanks in advance.

Thanks,
Niklas

hello nik01flink,

did you always got this failure after your self-compile TOS image has applied? is it related to Measured Boot configuration?

BTW,
please visit Jetson Linux Release 36.4.4 | NVIDIA Developer for the [Driver Package (BSP) Sources].
there’s readme file for more details..
$public_sources/r36.4.4/Linux_for_Tegra/source/atf_and_optee/optee/samples/ftpm-helper/host/tool/README_keylime_TPM_for_unfused_Jetson.md

please copy all the files under ./optee/install/t<platform> to the target.
the corresponding TA file for the fTPM helper is placed in the TA directory, early TAs were stored inside TOS image.

Hey,

thank you for your fast response.

I assume by “failure” you mean that fTPM only becomes usable after I manually restart the service / reload the driver. Yes — after every reboot I have to run it otherwise tpm2-tools cannot talk to the TPM.
Even after restarting the service and loading the driver, when I read PCRs they are empty, so with my understanding this is not compatible with “Measured Boot”, because PCRs should already contain measurements from MB2/OP-TEE/UEFI very early in boot.

Here is a bootlog after flashing and using the tpm2 tools with and without restarting the service. (Sorry for bad formatting in some lines - thats due to minicom)

BootLog.txt (176.4 KB)

Yes — for my last rebuild I used JetPack/L4T 36.4.4 sources. I unpacked atf_src, nvidia-jetson-optee-source, etc. and rebuilt OP-TEE (with fTPM enabled), rebuilt ATF (MEASURED_BOOT=1), regenerated tos-optee_t234.img, and flashed it.

I checked that README. I previously used it for unfused simulation mode, but now I’m working with a fused Jetson and my goal is to use fTPM / PCRs as early and reliably as possible (watchdog / attestation use-case). Most of the Keylime steps are user-space focused.

The last part “PCR0 Measurement Calculation” looks relevant — but I’m not sure how to interpret it given that my PCR0 stays empty after boot.

Thanks a lot,
Niklas

hello nik01flink,

per booting logs..
Jetson System firmware version 36.4.3-gcid-38968081 date 2025-01-08T01:18:20+00:

please use the same L4T version package since you’re based-on JP-6.2

Hi JerryChang,

.. yea, that makes sense 🤦‍♂️ I rebuilt and reflashed everything again using L4T 36.4.3 matching my firmware. After this, the previous issue improved: I no longer need to restart nv-tee-supplicant.service after every reboot.

However, right after boot I still can’t access PCRs until I manually load the fTPM driver. After boot sudo tpm2_pcrreadfails because there is no /dev/tpmrm0 or /dev/tpm0 yet. And after loading the driver with sudo modprobe tpm_ftpm_teeI get OPTEE errors:

E/LD: init_elf:493 sys_open_ta_bin(6c879517-2dfc-4663-863d-4896e8ccbe3a)
E/TC: ldelf_init_with_ldelf:152 ldelf failed with res: 0xffff0008

But even though I see these errors, I am able to use tpm2 tools after loading the driver. So ..

- is the 0xffff0008 during modprobe expected, or does it indicate missing something?

- is it possible to load tpm_ftpm_tee automatically too?

Thanks a lot,
Niklas

Flashlog:

flash_1-2_0_20260105-153604.log (64.7 KB)

Bootlog:

log (1).txt (86.1 KB)

hello nik01flink,

let me re-cap the logs when UEFI tried to access the fTPM TA in OP-TEE.

E/LD:   init_elf:ÿå[     5.682690] Camera-FW on tÿä493ÿå234-rce-safe ready SHA1=e2238c99 (crt 1.403 ms, total boot 95.292 ms)
ÿä sys_open_ta_bin(bc50d971-d4c9-42c4-82cb-343fb7f37896)
E/TC:?? 00 ldelf_init_with_ldelf:152 ldelf failed with res: 0xffff000c
ÿá

according to the logs, it’s TA is missing or not included in the build, it’s the UUID, bc50d971-d4c9-42c4-82cb-343fb7f37896 as identifier for the fTPM TA. it might due to fTPM TA is unavailable, please check fTPM TA is present, and correctly configured in your OP-TEE build.

let me share some general steps for building TOS image and OP-TEE files.
(1) Download the toolchain and public source tarball, and uncompress.
(2) Build the TOS image and all optee userspace programs and libraries, (following the atf_and_optee_README.txt)
(3) Replace the $OUT/Linux_for_Tegra/bootloader/tos-optee_t234.img
(4) Replace all files within $OUT/Linux_for_Tegra/rootfs with the files in optee/install/t234 directory, which includes bin/xtest, lib/optee_armtz/* and usr/*.
(5) Full-flash the target.

Hi JerryChang,

thanks for your reply. I did a lot of comparisons and checks, and I wanted to summarize what I verified. Sorry for the length, it is formatted by LLM.

1) fTPM TA file presence (/lib/optee_armtz)

You mentioned the issue could be that the fTPM TA (UUID bc50d971-d4c9-42c4-82cb-343fb7f37896) is missing / not found under /lib/optee_armtz. I can rule that out: on the Jetson, ls /lib/optee_armtz shows many .ta files, including:

  • bc50d971-d4c9-42c4-82cb-343fb7f37896.ta

So the TA file is present on the target rootfs.

2) Rebuilding with matching L4T (36.4.3) and rebuilding everything clean

I went through your 5 steps again, using L4T 36.4.3 (matching the firmware on the board).

  • Toolchain: I did not rebuild it, but when I started this project last year JetPack/L4T 36.4.4 was not released yet, so the toolchain is based on 36.4.3 and should match.

  • I re-downloaded and rebuilt OP-TEE + ATF exactly as documented in atf_and_optee_README.txt yesterday.

Concretely, I built OP-TEE with the -t flag (fTPM enabled), and built ATF using:

make -j"$(nproc)" \
  PLAT=tegra TARGET_SOC=t234 SPD=opteed \
  BUILD_BASE=./generic-t234 \
  DEBUG=0 LOG_LEVEL=20 V=0 \
  BRANCH_PROTECTION=3 ARM_ARCH_MINOR=3 \
  MEASURED_BOOT=1

I generated the OP-TEE DTB:

dtc -I dts -O dtb -o ./optee/tegra234-optee.dtb ./optee/tegra234-optee.dts

Then I generated tos.img using:

python3 ../nv_tegra/tos-scripts/gen_tos_part_img.py \
  --monitor ./atf_build/arm-trusted-firmware/generic-t234/tegra/t234/release/bl31.bin \
  --os ./jetson-optee-srcs/nvidia-jetson-optee-source/optee/build/t234/core/tee-raw.bin \
  --dtb ./jetson-optee-srcs/nvidia-jetson-optee-source/optee/tegra234-optee.dtb \
  --tostype optee \
  ./tos.img

I replaced Linux_for_Tegra/bootloader/tos-optee_t234.img with the newly generated image and reflashed.

3) Copying OP-TEE install files into rootfs (step 4)

Previously I did not copy all files from optee/install/t234/ into Linux_for_Tegra/rootfs/. This time I did:

  • compared and updated bin/, lib/, usr/ from optee/install/t234/ into the matching locations in Linux_for_Tegra/rootfs/.

  • xtest was definitely outdated before — I updated it as well.

  • besides that, the differences were mostly include files and a few user-space binaries/libs.

Then I reflashed again.

4) Current behavior / still stuck

After this, I still don’t see progress:

  • During boot, it still reports that it cannot open a TA with UUID bc50d971-d4c9-42c4-82cb-343fb7f37896 (even though the file exists under /lib/optee_armtz).

  • During modprobe tpm_ftpm_tee, it reports it cannot open the PTA UUID 6c879517-2dfc-4663-863d-4896e8ccbe3a.

The important difference here:
bc50... exists as a .ta in /lib/optee_armtz, but 6c87... does not exist as a .ta anywhere (neither on target nor on host). I can only find it as a header (pta_jetson_ftpm_helper.h). (See grep results below.)

5) Host-side search results (UUID content search)

I searched the whole Linux_for_Tegra/ directory (file contents, not filenames) for both UUIDs:

Search for bc50…

sudo grep -RIl --binary-files=without-match "bc50d971-d4c9-42c4-82cb-343fb7f37896" . 2>/dev/null
./source/kernel/kernel-jammy-src/drivers/char/tpm/tpm_ftpm_tee.mod.c
./source/jetson-optee-srcs/nvidia-jetson-optee-source/optee_src_build.sh
./source/jetson-optee-srcs/nvidia-jetson-optee-source/optee/samples/ms-tpm-20-ref/Samples/ARM32-FirmwareTPM/optee_ta/fTPM/Makefile
./source/jetson-optee-srcs/nvidia-jetson-optee-source/optee/build/t234/core/early_ta_bc50d971-d4c9-42c4-82cb-343fb7f37896.c
./source/jetson-optee-srcs/nvidia-jetson-optee-source/optee/build/t234/core/tee.dmp
./source/jetson-optee-srcs/nvidia-jetson-optee-source/optee/build/t234/core/.early_ta_bc50d971-d4c9-42c4-82cb-343fb7f37896.o.cmd
./source/jetson-optee-srcs/nvidia-jetson-optee-source/optee/build/t234/core/tee.map
./source/jetson-optee-srcs/nvidia-jetson-optee-source/optee/build/t234/core/.tee.elf.cmd
./source/jetson-optee-srcs/nvidia-jetson-optee-source/optee/build/t234/core/.early_ta_bc50d971-d4c9-42c4-82cb-343fb7f37896.o.d
./source/jetson-optee-srcs/nvidia-jetson-optee-source/optee/build/t234/early_ta/ms-tpm/bc50d971-d4c9-42c4-82cb-343fb7f37896.map
./source/jetson-optee-srcs/nvidia-jetson-optee-source/optee/build/t234/early_ta/ms-tpm/bc50d971-d4c9-42c4-82cb-343fb7f37896.dmp
./source/out/nvidia-linux-header/drivers/char/tpm/tpm_ftpm_tee.mod.c
./rootfs/lib/modules/5.15.148-tegra/modules.alias
./rootfs/lib/modules/5.15.148-tegra/build/drivers/char/tpm/tpm_ftpm_tee.mod.c
./rootfs/lib/modules/5.15.148-tegra/source/drivers/char/tpm/tpm_ftpm_tee.mod.c

Search for 6c87…

sudo grep -RIl --binary-files=without-match "6c879517-2dfc-4663-863d-4896e8ccbe3a" . 2>/dev/null
./source/jetson-optee-srcs/nvidia-jetson-optee-source/optee/optee_os/lib/libutee/include/pta_jetson_ftpm_helper.h
./source/jetson-optee-srcs/nvidia-jetson-optee-source/optee/build/t234/export-ta_arm64/host_include/pta_jetson_ftpm_helper.h
./source/jetson-optee-srcs/nvidia-jetson-optee-source/optee/build/t234/export-ta_arm64/include/pta_jetson_ftpm_helper.h

So 6c87... appears only in header files, not as a TA file.

6) xtest status

I also tried running the updated xtest, but it crashes on:

* regression_1024 Test PTA_SYSTEM_GET_TPM_EVENT_LOG Service
o regression_1024.1 TPM test service invocation
Segmentation fault

From what I found, this might be a known issue when measured boot is enabled.

Flashlog:

flash_1-2_0_20260106-153633.log (64.8 KB)

Bootlog (after new flash):

log (2).txt (172.1 KB)

Thanks a lot,
Niklas

I found that the system is still probing a discrete SPI TPM from the device tree (Infineon SLB9670 / tis,tpm2-spi). On every boot tpm_tis_spi auto-loads and tries to init spi0.1, which times out with probe … failed with error -110, and at the same time the fTPM driver (tpm_ftpm_tee) is not auto-loaded so /dev/tpm0 doesn’t exist until I manually run modprobe. Do you think the leftover SPI dTPM DT node could be the reason why fTPM isn’t coming up automatically / why measured boot doesn’t work as expected?

hello nik01flink,

fTPM should be auto start-up during boot time.

the fTPM is an early Trusted Application (TA) that is built into the OP-TEE image by default and does not exist in the file system. fTPM TA should be ready after booting into the console.
additionally, please use the tpm2 commands (instead of xtest) for testing, such as tpm2_pcrread.

Hey JerryChang,

thanks for clarification. I got the ftpm running so that it exists right after boot and I am able to use tpm2 tools. There was a tpm_ftpm_tee blacklist entry under /etc/modprobe.d/. Not sure if that was the problem.
But measured boot still seems inactive: PCRs stay all zeros after boot, and I can’t find any event log export (no binary_bios_measurements under /sys/kernel/security, so tpm2_eventlog has nothing to parse).

Is there a way to verify that the measured boot event log is produced (MB2/TF-A → OP-TEE → fTPM)? Do I need to enable any specific kernel config options to expose the event log? I also took another look at optee’s device tree. Should the event log be accessible in user space at all?

Thanks,
Niklas

hello nik01flink,

measured boot event log it should be configured as enabled.
for instance,

function build_optee_sources {
        echo "Building ${TARGET_PLATFORM} optee sources..."
...
                if [ "${ENABLE_FTPM_BUILD}" == "yes" ]; then
                        optee_config="CFG_CORE_TPM_EVENT_LOG=y \
                                CFG_REE_STATE=y \
                                CFG_JETSON_FTPM_HELPER_PTA=y"

you should update firmware components (such as mb1, mb2, TOS..etc),
the measurements of mb2, TOS, and CPUBL binaries are extended to PCR0 only.

Hi JerryChang,

I rebuilt OP-TEE with -t, and the build script passes the flags you mentioned.

Is there a recommended/official procedure (or specific commands) to update the relevant firmware components (MB1/MB2/TOS/CPUBL, etc.)?

Also, when looking at the boot flow diagram (I’ll attach it at the bottom), I see a pcr_extend step at the end (UEFI → pcr_extend → ftpm). My current understanding is that measurements still extend PCR0 even when Event Log isn’t enabled. So I am missing a step to activate the ftpm.

I noticed NVIDIA documentation splits “Secure Boot” into two stages:
“The root-of-trust that uses the NVIDIA SoCs fuses to authenticate boot codes ends at the Bootloader. After this, the current Bootloader (UEFI) will use UEFI’s Security Keys scheme to authenticate its payloads.”
I only fused the board (SoC secure boot), but UEFI Secure Boot is not enabled. Could UEFI Secure Boot being disabled be an indicator why Measured Boot / Event Log / PCR extensions are not happening?

they’re located in QSPI, you may execute the commands for Orin NX/Nano to flash QSPI only.
this may save time if you need to update/debug bootloader component only instead of doing full flash with initrd flash.
$ sudo ./flash.sh -c bootloader/generic/cfg/flash_t234_qspi.xml --no-systemimg jetson-orin-nano-devkit-nvme nvme0n1p1
or.. you may have -k option added to update specific partition.
$ sudo ./flash.sh -c bootloader/generic/cfg/flash_t234_qspi.xml -k <partition> --no-systemimg jetson-orin-nano-devkit-nvme nvme0n1p1