How to enable TPM2 module in Trusted Zone (OP-TEE) on Jetson Orin Nano?

Hi everyone,

I’m currently trying to get a TPM2 chip working via SPI1 (40-pin header) on a Jetson Orin Nano board, with the specific goal of using the TPM from within the Trusted Zone with OP-TEE and the ARM Trusted Firmware. For reference, when booting an image without the dTPM component, it works fine outside of the Trusted Zone and I am able to use tpm2 commands like pcrread or pcrextend. The ultimate goal is to enable measured boot and have the ability to read PCR registers from within OP-TEE.

To enable TPM support, I’m following the guidance from the Measured Boot with dTPM PoC and used the official Driver Package (BSP) Sources as a base. I made the necessary changes to integrate the TPM module with the arm-trusted-firmware sources, including adding the TPM folder from the upstream repo, updating the YAML, Makefile, and event_log.c, and was able to successfully build the BL31 binary using the following make command:

make PLAT=tegra TARGET_SOC=t234 DEBUG=1 SPD=opteed MEASURED_BOOT=1 TRUSTED_BOARD_BOOT=1 DISCRETE_TPM=1 MBOOT_TPM_HASH_ALG=sha256 TPM_INTERFACE=FIFO_SPI EVENT_LOG_LEVEL=40

With the custom BL31, I generated a new tos-optee image using gen_tos_part_img.py and flashed the Jetson with:

./gen_tos_part_img.py \
    --monitor /home/nik71841/jetpack/jp62/target/Linux_for_Tegra/source/atf_build/arm-trusted-firmware/generic-t234/tegra/t234/release/bl31.bin \
    --os /home/nik71841/jetpack/jp62/target/Linux_for_Tegra/source/jetson-optee-srcs/nvidia-jetson-optee-source/optee/build/t234/core/tee-raw.bin \
    --dtb /home/nik71841/jetpack/jp62/target/Linux_for_Tegra/source/jetson-optee-srcs/nvidia-jetson-optee-source/optee/tegra234-optee.dtb \
    --tostype optee \
    ./tos.img

After flashing, during boot, I only see this message:

tpm tpm0: A TPM error (256) occurred attempting the self test

Additionally, when running tpm2_pcrread, all PCR registers are empty.

Given this behavior, I am wondering if I might be using a tegra234-optee.dtb that does not actually support the dTPM node, or if there are required changes to the device tree (or elsewhere) to properly enable or discover the dTPM for use in OP-TEE/secure world. Do I need to manually patch the DTB for dTPM support? Is there anything else I might be missing in the integration or initialization steps? I would really appreciate any advice, hints, or pointers for debugging this issue, especially since the chip itself works fine in the normal world but fails when trying to use it from the secure world.

My current tegra234-optee.dts looks like this:

/*
 * SPDX-License-Identifier: BSD-2-Clause
 *
 * SPDX-FileCopyrightText: Copyright (c) 2021-2024, NVIDIA CORPORATION. All rights reserved.
 */

/dts-v1/;

 / {
	#address-cells = <2>;
	#size-cells = <2>;

	reserved-memory {
		#address-cells = <2>;
		#size-cells = <2>;

		tpm-event-log@0 {
			compatible = "arm,tpm_event_log";
			tpm_event_log_addr = <0x0 0x0>;
			tpm_event_log_size = <0x0>;
		};
	};

	efuse@3810000 {
		compatible = "nvidia,tegra234-efuse";
		reg = <0x0 0x3810000 0x0 0x600>;
		status = "disabled";
		secure-status = "okay";
	};

	se0@3b50000 {
		compatible = "nvidia,tegra234-se0";
		reg = <0x0 0x3b50000 0x0 0x30000>;
		status = "disabled";
		secure-status = "okay";
	};

	rng1@3b70000 {
		compatible = "nvidia,tegra234-rng1";
		reg = <0x0 0x3b70000 0x0 0x10000>;
		status = "disabled";
		secure-status = "okay";
	};

	stmm-device-mappings {
		uuid = <0xed32d533 0x99e64209 0x9cc02d72 0xcdd998a7>;
		description = "UEFI-mm";

		device-regions {
			combuart-t234 {
				base-address = <0x00000000 0x0c198000>;
				pages-count = <0x1>;
				attributes = <0x3>;
			};

			qspi0-t234 {
				base-address = <0x00000000 0x03270000>;
				pages-count = <0x10>;
				attributes = <0x3>;
			};

			scratch-t234 {
				base-address = <0x00000000 0x0c390000>;
				pages-count = <0x2>;
				attributes = <0x3>;
			};
		};
	};

	spi@3210000 {
		slb9670@1 {
			compatible = "infineon,slb9670", "infineon,slb9672", "tis,tpm2-spi", "tcg,tpm_tis-spi";
			reg = <1>;
			spi-max-frequency = <32000000>;
			status = "okay";
		};
	};
};

Thanks in advance for any help!

Hi nik01flink,

Are you using the devkit or custom board for Orin Nano?

It seems you want to integrate a TPM module on Orin Nano to enable dTPM feature in secure world.
We don’t verified this use case before.
Have you considered using fTPM for Secureboot instead?

Do you mean that you’ve verified it working after booting up and you can interact with TPM module through SPI?

Could you add more debug logs in tpm-interface.c for further check?

Hi, thanks for your feedback!

Just to clarify my setup:

  • I am using the original Jetson Orin Nano Devkit (not a custom board).
  • I know that using a discrete TPM (like the Infineon SLB9670 over SPI) with the secure world / OP-TEE has not really been demonstrated for Orin Nano/T234 yet, but I saw that ARM recently added support for dTPM for the t210, t194, and t186 platforms.
  • ARM also released a measured boot developer guide with an example for Raspberry Pi 3 using the same SLB9670 TPM. I am basically trying to adapt that approach for T234.

About your question:
Yes, after flashing the Jetson and enabling SPI1 via the 40-pin header, I can successfully access the TPM registers from Linux using commands like tpm2_pcrread and tpm2_pcrextend. So, the TPM is fully functional from the normal (non-secure) world – I can read and extend PCRs via terminal commands.
What I want is to access the TPM from within the Trusted Zone / secure world, i.e., from a Trusted Application via OP-TEE.

As additional info:
I also tried adjusting the gpio_spi_data structure (the pin definitions) in drivers/tpm/tpm2_slb9670/slb9670_gpio.c to match my board’s TPM wiring since the previous values were for the Raspberry Pi, which was for sure a mistake. However, updating those values did not help either.

For reference, using fTPM is really my last resort. I am trying to get measured boot with a discrete hardware TPM working.

Thanks for your explanation.

Okay, so your current issue is specific to using dTPM with OP-TEE.

Could you share more details about this?
Is that including T234?

Ah, sure, I can clarify this a bit more:

In NVIDIA’s official developer guide for setting up OP-TEE (as referenced in the atf_and_optee_README.txt), you are instructed to download the public sources in order to build the BL31 component for the TOS-OPTEE image. In the path
\public_sources\Linux_for_Tegra\source\atf_src\arm-trusted-firmware\plat\nvidia\tegra\soc
you can clearly see support for the platforms t186, t194, and t234. Using this “official” OP-TEE setup, I have successfully booted and run samples like hello_world and even hwkey-agent with disk encryption on my Jetson Orin Nano (t234).
So, for the standard trusted world and TEE operation, t234 is definitely supported.

However, the goal of my project is to implement Measured Boot using a GPIO (discrete) TPM with TF-A (Trusted Firmware-A). That makes it absolutely necessary to access the TPM from the trusted zone.

I’ve been working on this for several weeks now. I found that ARM has also worked on adding dTPM support, but unfortunately, not yet for t234. ARM recently published the section
“10.3. Measured Boot using a Discrete TPM (PoC)” in their developer guide, and in February 2025, they also published the relevant dTPM driver in their official TF-A repository.
But, as you can see here:

that ARM repo currently only supports the NVIDIA platforms t210, t186, and t194 – not t234.

My first approach was to try the “unofficial” ARM repo and implement t234 support myself, but that turned out to be very tricky. In the end, I couldn’t get it working because of assembler issues with the Cortex 78 AE code during BL31 build.

My second approach was to use NVIDIA’s official version for t234 and port the tpm driver files from the ARM repo into it, as I described in my first message (and as shown in the git commit).
This actually builds with the “Discrete TPM = 1” flag set, but it doesn’t work as intended, or at least not as I expected it to.

hello nik01flink,

please refer to Topic 327780, comment #22 for the case of dTPM to support measured boot.
please also note that, we do not support dTPM (i.e. discrete TPM) for Jetson.

1 Like

Hello JerryChang,

thanks for the answer.

Let’s say I want to use the firmware tpm instead of a discrete tpm. Would that be possible instead? And is there any support for setting up ftpm within a trusted application?

hello nik01flink,

here’re some info about fTPM TA.

  • The fTPM TA supports TPM2 functionalities defined by the Trusted Computing Group (TCG)
  • The TPM2 function in the fTPM TA should be a black box. It should NOT provide any interface other than TCG defined to access the TPM internal functions.
  • The fTPM TA gets the EPS (i.e. Endorsement Primary Seed) from the fTPM helper PTA during the first startup time and stores the EPS in the NV memory with the provisioning process.

please see-also.. GitHub - microsoft/ms-tpm-20-ref: Reference implementation of the TCG Trusted Platform Module 2.0 specification.

it’s $ sudo modprobe tpm_ftpm_tee to load the fTPM driver module.
you may running the script, ftpm_device_provision.sh script to have provision and activate the fTPM.
please see-also.. $public_sources/r36.4.4/Linux_for_Tegra/source/atf_and_optee/optee/samples/ftpm-helper/host/tool/ftpm_device_provision.sh

Hi,
thanks for your reply. In the meantime, I have been experimenting a bit. To run the fTPM or even just test it, do I need to burn the fuses?
If so, is there any other way to obtain the fuse-burning tools or the required keys without going through the NVIDIA sales channel?

hello nik01flink,

we’ve enabled fTPM simulation feature after r36.4 release version.
for testing, you may run simulated TPM to verify all TPM functions without actually burning the fuse.

we have a sample procedure to test with Keylime,
you can evaluate and try the flow with keylime on an unfused board (no fTPM provisioning or fusing is required).
here’s readme file, $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

let’s close this topic, please have another new discussion thread for issue follow-up.

Hello! Also consider SecEdge SEC-TPM, which is an implementation of the NVIDIA FTPM integrated with Jetpack with provisioning service. See documentation:

A test kit is available for Jetson here:

You can test without setting fuses.

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.