Implementation of a Trusted Application using OP-TEE and JetPack5.1
|
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 .
This implementation uses the 35.2.1. |
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.