Implementation of a Trusted Application using OP-TEE and JetPack5.1

From RidgeRun Developer Wiki



About OP-TEE

Open Portable Trusted Execution Environment (OP-TEE) is an Open-Source Trusted Execution Environment (TEE) based on Arm TrustZone technology. This framework combines two major components: optee_os, which is the trusted side of the TEE (the secure world), and the optee_client, that represents the untrusted, or normal, side of the TEE (the normal world).

optee_os is a TEE operating system that runs at the ARMv8 secure EL-1 level. It provides generic OS-level functions such as: interrupt handling, thread handling, crypto services, and shared memory. It also implements the GlobalPlatform TEE Internal Core API which can used to build Trusted Applications (TAs). This Core API runs in the secure world (at ARMv8 secure EL-0 level).

optee_client , on the other side has two components: a normal world user space library and a normal world user space daemon. The first one implements the GlobalPlatform TEE Client API, which defines the interface that normal world Client Applications (CAs) use to communicate with the TAs in the secure world. The daemon tee-supplicant implements some miscellaneous features for TrustedOS, for instance, file system access to load the TAs from the normal world file system into the secure world.

The main design goals for OP-TEE are:

  • Isolation: TEE provides isolation from the non-secure OS, and protects the loaded Trusted Applications (TAs) from each other using underlying hardware support.
  • Small footprint: the TEE should remain small enough to reside in a reasonable amount of on-chip memory as found on Arm based systems.
  • Portability: the TEE aims at being easily "pluggable" to different architectures and available hardware, and has to support various setups.

Implementation

This section aims to guide the user through the building process of the OP-TEE sources for JetPack 5.1 and add a "hello_world" Trusted Application example to them. Consider that the target platform that was used to implement the following steps is a Jetson Xavier AGX. Hence, commands may change if a different target platform is used.

Prerequisites

  • This wiki assumes that the JetPack 5.1 sources were downloaded, built, and installed using the NVIDIA SDKManager.
  • Install the prerequisites from here: Prerequisites

Platforms

The placeholder "<platform>" indicates Jetson platforms. Its possible values are:

  • 194 = Jetson Xavier series
  • 234 = Jetson Orin series

Toolchain

Download the toolchain from Jetson release page according to your L4T version: Toolchains list .

Set environment variables

export CROSS_COMPILE_AARCH64_PATH=/$HOME/jp5.1-l4t-gcc

export CROSS_COMPILE_AARCH64=$CROSS_COMPILE_AARCH64_PATH/bin/aarch64-buildroot-linux-gnu-

export NV_TARGET_BOARD=194

Add the UEFI StMM image. The Jetson AGX Xavier series and the Jetson Xavier NX, it is located at:

/$HOME/<$SDKManager_JetPack_sources>/<Linux_for_Tegra>/bootloader/standalonemm_optee_t194.bin

For the Jetson AGX Orin series, it can be found here:

/$HOME/<$SDKManager_JetPack_sources>/<Linux_for_Tegra>/bootloader/standalonemm_optee_t234.bin

Set the environment variable "UEFI_STMM_PATH" depending on the platform selected:

export UEFI_STMM_PATH=<StMM image path>

Building the ATF source code

Extract the JetPack 5.1 sources:

source_sync.sh -t jetson_35.2.1

Along with them, there should be a Tegra directory where you can find the OP-TEE sources:

/$HOME/<$SDKManager_JetPack_sources>/Linux_for_Tegra/sources/tegra/optee-src

Inside of it you should see the ATF source code:

cd atf

And then, build the source code

cd arm-trusted-firmware
   make BUILD_BASE=./build \
       CROSS_COMPILE="${CROSS_COMPILE_AARCH64}" \
       DEBUG=0 LOG_LEVEL=20 PLAT=tegra SPD=opteed TARGET_SOC=t<platform> V=0
   cd ../..

After that, built files should be stored here:

/$HOME/$OPTEE_SOURCES/arm-trusted-firmware/build/tegra/t194/release

Building the OP-TEE source code

Go to the OP-TEE sources:

cd Linux_for_Tegra/sources/tegra/optee-src/nv-optee/optee

Run the script to build the OP-TEE sources:

./optee_src_build.sh -p t194

Building the OP-TEE dtb

Within the OP-TEE sources, run:

cd /$HOME/$OPTEE_SOURCES/nv-optee
dtc -I dts -O dtb -o ./optee/tegra<platform>-optee.dtb ./optee/tegra<platform>-optee.dts

This will output something like this, which is a Warning from Linaro.

./optee/tegra194-optee.dts:33.17-38.4: Warning (unit_address_format): /efuse@03820000: unit name should not have leading 0s
./optee/tegra194-optee.dts:40.15-45.4: Warning (unit_address_format): /se0@03ac0000: unit name should not have leading 0s
./optee/tegra194-optee.dts:47.20-52.4: Warning (unit_address_format): /se0-rng1@03ae0000: unit name should not have leading 0s

The new dtb file will be located at:

/$HOME/$OPTEE_SOURCES/optee

Generating the tos.img using ATF and OP-TEE sources

Go to the OP-TEE source code:

cd /$HOME/<SDKManager_JetPack_sources>/Linux_for_Tegra/sources/tegra/optee-src/nv-optee

Move the gen_tos_part_img.py script to the recent location. You can find the python script within $HOME/<SDKManager_JetPack_sources>/Linux_For_Tegra/nv_tegra/tos-scripts path.

cp $HOME/<SDKManager_JetPack_sources>/Linux_for_Tegra/nv_tegra/tos-scripts/gen_tos_part_img.py .

Run:

./gen_tos_part_img.py --monitor ./atf/arm-trusted-firmware/build/tegra/t194/release/bl31.bin --os ./nv-optee/optee/build/t194/core/tee-raw.bin --dtb ./nv-optee/optee/tegra194-optee.dtb --tostype optee ./tos.img

At the end, the output should look like this:

Generating Trusted OS Partition Image File
Generate TOS Image File for boot-wrapper.

Two files should be created after this:

  • img.bin
  • tos.img

Flash the target platform

The recently generated tos.img should be renamed and relocated in order to start the flashing process. Go to the directory where tos.img is stored, and rename it as follows:

cd /$HOME/<SDKManager_JetPack_sources>/Linux_for_Tegra/sources/tegra/optee-src/nv-optee/optee
mv tos.img tos_t<platform>.img

Where <platform> should be rename for one of the following options: 194 or 234.

Move the new img file to the bootloader directory. This will replaced the old tos_t<platform>.img:

mv tos_t<platform>.img /$HOME/<SDKManager_JetPack_sources>/Linux_For_Tegra/bootloader

Finally, proceed to flash the target board.

Add a new Trusted Application to the OP-TEE sources

Clone the OP-TEE examples repository:

git clone https://github.com/linaro-swg/optee_examples.git

Then copy the hello_world example to the OP-TEE sources:

cd $HOME/optee_examples 
cp -r hello_world /$HOME/<SDKManager_JetPack_sources>/Linux_for_Tegra/sources/tegra/optee-src/nv-optee/optee/samples

Modify the TA root Makefile. Go to the hello_world directory within the sources:

cd /$HOME/<SDKManager_JetPack_sources>/Linux_for_Tegra/sources/tegra/optee-src/nv-optee/optee/samples/hello_world
sudo vim Makefile

Add the following changes:

-# If _HOST or _TA specific compilers are not specified, then use CROSS_COMPILE
-HOST_CROSS_COMPILE ?= $(CROSS_COMPILE)
-TA_CROSS_COMPILE ?= $(CROSS_COMPILE)
-
-.PHONY: all
-all:
-	$(MAKE) -C host CROSS_COMPILE="$(HOST_CROSS_COMPILE)" --no-builtin-variables
-	$(MAKE) -C ta CROSS_COMPILE="$(TA_CROSS_COMPILE)" LDFLAGS=""

-.PHONY: clean
-clean:
-	$(MAKE) -C host clean
-	$(MAKE) -C ta clean

+TARGET_DIR := $(notdir $(shell pwd))
+
+.PHONY: all
+all:
+	$(MAKE) -C ta \
+		CROSS_COMPILE=$(CROSS_COMPILE) \
+		TA_DEV_KIT_DIR=$(TA_DEV_KIT_DIR) \
+		O=$(O)/ta/$(TARGET_DIR)
+	$(MAKE) -C host \
+		CROSS_COMPILE=$(CROSS_COMPILE) \
+		OPTEE_CLIENT_EXPORT=$(OPTEE_CLIENT_EXPORT) \
+		O=$(O)/ca/$(TARGET_DIR) \
+		--no-builtin-variables

+.PHONY: clean
+clean:
+	$(MAKE) -C ta \
+		TA_DEV_KIT_DIR=$(TA_DEV_KIT_DIR) \
+		O=$(O)/ta/$(TARGET_DIR) \
+		clean
+	$(MAKE) -C host \
+		OPTEE_CLIENT_EXPORT=$(OPTEE_CLIENT_EXPORT) \
+		O=$(O)/ca/$(TARGET_DIR) \
+		clean
+	rm -rf $(O)/ca/$(TARGET_DIR)

Modify the Makefile within the host directory.

cd /host
sudo vim Makefile

The final file should look like this:


-CC      ?= $(CROSS_COMPILE)gcc
-LD      ?= $(CROSS_COMPILE)ld
-AR      ?= $(CROSS_COMPILE)ar
-NM      ?= $(CROSS_COMPILE)nm
-OBJCOPY ?= $(CROSS_COMPILE)objcopy
-OBJDUMP ?= $(CROSS_COMPILE)objdump
-READELF ?= $(CROSS_COMPILE)readelf
-
-OBJS = main.o
-
-CFLAGS += -Wall -I../ta/include -I$(TEEC_EXPORT)/include -I./include
-Add/link other required libraries here
-LDADD += -lteec -L$(TEEC_EXPORT)/lib
-
-BINARY = optee_example_hello_world
-
-.PHONY: all
-all: $(BINARY)
-
-$(BINARY): $(OBJS)
-	$(CC) $(LDFLAGS) -o $@ $< $(LDADD)
-
-.PHONY: clean
-clean:
-	rm -f $(OBJS) $(BINARY)

-%.o: %.c
-	$(CC) $(CFLAGS) -c $< -o $@
+
+CC ?= $(CROSS_COMPILE)gcc
+
+CFLAGS += -Wall -I../ta/include -I./include
+CFLAGS += -I$(OPTEE_CLIENT_EXPORT)/include
+CFLAGS += -fstack-protector-strong
+LDADD += -lteec -L$(OPTEE_CLIENT_EXPORT)/lib
+
+SRCS = main.c
+OBJS = $(patsubst %.c,$(O)/%.o,$(SRCS))
+BINARY = optee_example_hello_world
+
+.PHONY: all install
+all: $(BINARY) install
+
+$(BINARY): $(OBJS)
+	$(CC) -o $(O)/$@ $< $(LDADD)
+
+$(O)/%.o: %.c
+	mkdir -p $(O)
+	$(CC) $(CFLAGS) -c $< -o $@
+
+install: $(BINARY)
+	mkdir -p $(OPTEE_CLIENT_EXPORT)/sbin
+	cp $(O)/$(BINARY) $(OPTEE_CLIENT_EXPORT)/sbin
+
+.PHONY: clean
+clean:
+	rm -f $(OBJS) $(O)/$(BINARY) $(OPTEE_CLIENT_EXPORT)/sbin/$(BINARY)

Go to the /ta directory and make sure that the sub.mk includes the source and header file for the TA.

cd ta/
vim sub.mk

It should look like this:

global-incdirs-y += include
srcs-y += hello_world_ta.c

hello_world_ta.c is the source code and hello_world_ta.h lives within the include directory. Modify the Makefile inside the /ta directory.The final file should look like this:

-CFG_TEE_TA_LOG_LEVEL ?= 4
-CFG_TA_OPTEE_CORE_API_COMPAT_1_1=y

- The UUID for the Trusted Application
-BINARY=8aaaf200-2450-11e4-abe2-0002a5d5c51b

-include $(TA_DEV_KIT_DIR)/mk/ta_dev_kit.mk

-ifeq ($(wildcard $(TA_DEV_KIT_DIR)/mk/ta_dev_kit.mk), )
-clean:
-	@echo 'Note: $$(TA_DEV_KIT_DIR)/mk/ta_dev_kit.mk not found, cannot clean TA'
-	@echo 'Note: TA_DEV_KIT_DIR=$(TA_DEV_KIT_DIR)'
-endif

- Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
- SPDX-License-Identifier: BSD-2-Clause
+
+# The UUID for the hello world TA
+BINARY=8aaaf200-2450-11e4-abe2-0002a5d5c51b
+
+include $(TA_DEV_KIT_DIR)/mk/ta_dev_kit.mk

After that, build the sources again. Follow the steps of the Building the OP-TEE source code.

The output of the building process should look like this:

make[1]: Entering directory '/home/jdelgado/JetPack5.1/Linux_for_Tegra/source/public/tos_image/nvidia-jetson-optee-source/optee/samples/hello_world'
make -C ta \
	CROSS_COMPILE=/home/jdelgado/jp5.1-l4t-gcc/bin/aarch64-buildroot-linux-gnu- \
	TA_DEV_KIT_DIR=/home/jdelgado/JetPack5.1/Linux_for_Tegra/source/public/tos_image/nvidia-jetson-optee-source/optee/build/t194/export-ta_arm64/ \
	O=/home/jdelgado/JetPack5.1/Linux_for_Tegra/source/public/tos_image/nvidia-jetson-optee-source/optee/build/t194//ta/hello_world
make[2]: Entering directory '/home/jdelgado/JetPack5.1/Linux_for_Tegra/source/public/tos_image/nvidia-jetson-optee-source/optee/samples/hello_world/ta'
  CPP     /home/jdelgado/JetPack5.1/Linux_for_Tegra/source/public/tos_image/nvidia-jetson-optee-source/optee/build/t194//ta/hello_world/ta.lds
  CC      /home/jdelgado/JetPack5.1/Linux_for_Tegra/source/public/tos_image/nvidia-jetson-optee-source/optee/build/t194//ta/hello_world/hello_world_ta.o
  GEN     /home/jdelgado/JetPack5.1/Linux_for_Tegra/source/public/tos_image/nvidia-jetson-optee-source/optee/build/t194//ta/hello_world/dyn_list
  CC      /home/jdelgado/JetPack5.1/Linux_for_Tegra/source/public/tos_image/nvidia-jetson-optee-source/optee/build/t194//ta/hello_world/user_ta_header.o
  LD      /home/jdelgado/JetPack5.1/Linux_for_Tegra/source/public/tos_image/nvidia-jetson-optee-source/optee/build/t194//ta/hello_world/8aaaf200-2450-11e4-abe2-0002a5d5c51b.elf
  OBJDUMP /home/jdelgado/JetPack5.1/Linux_for_Tegra/source/public/tos_image/nvidia-jetson-optee-source/optee/build/t194//ta/hello_world/8aaaf200-2450-11e4-abe2-0002a5d5c51b.dmp
  OBJCOPY /home/jdelgado/JetPack5.1/Linux_for_Tegra/source/public/tos_image/nvidia-jetson-optee-source/optee/build/t194//ta/hello_world/8aaaf200-2450-11e4-abe2-0002a5d5c51b.stripped.elf
  SIGN    /home/jdelgado/JetPack5.1/Linux_for_Tegra/source/public/tos_image/nvidia-jetson-optee-source/optee/build/t194//ta/hello_world/8aaaf200-2450-11e4-abe2-0002a5d5c51b.ta
make[2]: Leaving directory '/home/jdelgado/JetPack5.1/Linux_for_Tegra/source/public/tos_image/nvidia-jetson-optee-source/optee/samples/hello_world/ta'
make -C host \
	CROSS_COMPILE=/home/jdelgado/jp5.1-l4t-gcc/bin/aarch64-buildroot-linux-gnu- \
	OPTEE_CLIENT_EXPORT=/home/jdelgado/JetPack5.1/Linux_for_Tegra/source/public/tos_image/nvidia-jetson-optee-source/optee/install/t194/usr \
	O=/home/jdelgado/JetPack5.1/Linux_for_Tegra/source/public/tos_image/nvidia-jetson-optee-source/optee/build/t194//ca/hello_world \
	--no-builtin-variables
make[2]: Entering directory '/home/jdelgado/JetPack5.1/Linux_for_Tegra/source/public/tos_image/nvidia-jetson-optee-source/optee/samples/hello_world/host'
mkdir -p /home/jdelgado/JetPack5.1/Linux_for_Tegra/source/public/tos_image/nvidia-jetson-optee-source/optee/build/t194//ca/hello_world
/home/jdelgado/jp5.1-l4t-gcc/bin/aarch64-buildroot-linux-gnu-gcc -Wall -I../ta/include -I./include -I/home/jdelgado/JetPack5.1/Linux_for_Tegra/source/public/tos_image/nvidia-jetson-optee-source/optee/install/t194/usr/include -fstack-protector-strong -c main.c -o /home/jdelgado/JetPack5.1/Linux_for_Tegra/source/public/tos_image/nvidia-jetson-optee-source/optee/build/t194//ca/hello_world/main.o
/home/jdelgado/jp5.1-l4t-gcc/bin/aarch64-buildroot-linux-gnu-gcc -o /home/jdelgado/JetPack5.1/Linux_for_Tegra/source/public/tos_image/nvidia-jetson-optee-source/optee/build/t194//ca/hello_world/optee_example_hello_world /home/jdelgado/JetPack5.1/Linux_for_Tegra/source/public/tos_image/nvidia-jetson-optee-source/optee/build/t194//ca/hello_world/main.o -lteec -L/home/jdelgado/JetPack5.1/Linux_for_Tegra/source/public/tos_image/nvidia-jetson-optee-source/optee/install/t194/usr/lib
mkdir -p /home/jdelgado/JetPack5.1/Linux_for_Tegra/source/public/tos_image/nvidia-jetson-optee-source/optee/install/t194/usr/sbin
cp /home/jdelgado/JetPack5.1/Linux_for_Tegra/source/public/tos_image/nvidia-jetson-optee-source/optee/build/t194//ca/hello_world/optee_example_hello_world /home/jdelgado/JetPack5.1/Linux_for_Tegra/source/public/tos_image/nvidia-jetson-optee-source/optee/install/t194/usr/sbin
make[2]: Leaving directory '/home/jdelgado/JetPack5.1/Linux_for_Tegra/source/public/tos_image/nvidia-jetson-optee-source/optee/samples/hello_world/host'
make[1]: Leaving directory '/home/jdelgado/JetPack5.1/Linux_for_Tegra/source/public/tos_image/nvidia-jetson-optee-source/optee/samples/hello_world'
.
.
.
.
optee sources compiled successfully.

The build process will output an optee_example_hello_world binary (from the host Makefile), and a uuid.{ta,elf,dmp,map} (from the ta Makefile). They can be found in this location:

/$HOME/<SDKManager_JetPack_sources>/Linux_for_Tegra/sources/tegra/optee-src/nv-optee/optee/build/t194/ta/hello_world

Build and flash the OP-TEE image and dtb again, as it was shown before.

Run the new Trusted Application

After building the application, copy the generated files to the Jetson platform. Go to the install directory and look for a t<platform> directory. Compress it and send it to the Jetson platform.

cd /$HOME/JetPack_5.1_Linux_JETSON_AGX_XAVIER_TARGETS/Linux_for_Tegra/sources/tegra/optee-src/nv-optee/optee
/install
tar -cvf t194.tar t194/
scp t194.tar nvidia@192.168.100.233:~/

On the Jetson platofm. Decompress the t<platform> file and copy the files and directories inside of it to its respective location on the Jetson.

Then, you are going to be able to run the trusted application.

$sudo optee_example_hello_world
Invoking TA to increment 42
TA incremented value to 43

Security Considerations

Universal User Identification (UUID)

For every TA created, a UUID must be generated and added. This UUID must be added to its corresponding header file and Makefile. To generate a UUID, there are two approaches: Using Python:

python -c 'import uuid; print(uuid.uuid4())'

Using random (Linux only):

cat /proc/sys/kernel/random/uuid # Linux only uuidgen

uuidgen is available from the util-linux package in most distributions.

Potential applications

OP-TEE OS may offer the following features in which developers can build potential applications:

  • Memory Segregation: establish memory boundaries between the rich world and the trusted world.
  • Inter-world Communication: a way to receive data, interrupts, and events from the rich world.
  • Crypto and Storage: to facilitate cryptography and secure storage.
  • Standardised API: inter-operable implementation of the software protocol between the rich and trusted world. Also, there needs to be a way to specify how a TA should respond to a requested functionality so that the rich world can parse it.
  • Authenticated TA Execution: Considering we will be executing an application in the trusted world, one would want that the TA itself should be trusted. Authenticating a TA before passing control to it would be a fantastic feature.

See also

Developer Guide OPTEE OP-TEE Documentation



For direct inquiries, please refer to the contact information available on our Contact page. Alternatively, you may complete and submit the form provided at the same link. We will respond to your request at our earliest opportunity.


Links to RidgeRun Resources and RidgeRun Artificial Intelligence Solutions can be found in the footer below.