Version 5 (modified by Tim Harvey, 13 months ago) (diff)

fixed tee build instructions for latest tee branch

Trusted Execution Environment (TEE)

The Trusted Execution Environment (TEE) is a set of specifications published by the GlobalPlatform association. The purpose of the TEE is to provide a safe environment within the application processor for developing and executing secure applications. We call an application processor a system running a Rich OS like Android or Linux. A Rich environment represents a huge amount of code. It is open to third-party applications and it is an open ecosystem: it makes a Rich OS hard to audit. It is prone to bugs/vulnerability, which may compromise the security and integrity of the entire system. The TEE offers another level of protection against attacks from the rich OS. The TEE is only open to trusted partners, which makes it easier to audit. It executes only trusted and authorized software. All sensitive data are protected from the rest of the application processor and from the outside world.

Many modern devices make use of a Trusted Execution Environment, including smartphones, set-top-boxes, game consoles and Smart TVs. Some example use cases of a TEE:

  • biometric authentication (ie facial, fingerprint, voide recognition) code and sensitive data
  • e-commerce digital wallet code and sensitive data
  • DRM credentials

Open Portable Trusted Execution Environment (OP-TEE)

OP-TEE (Open Portable Trusted Execution Environment) is an open-source TEE designed as a companion to a non-secure Linux kernel running on ARM Cortex-A cores using ARM's TrustZone technology.

The TEE relies on the Arm TrustZone technology. The TrustZone is a system-on-chip security feature available on most Arm Cortex A/M processors. It provides a strict hardware isolation between the secure world (TEE) and the normal world (REE). This technology allows each physical processor core to provide two virtual cores: one for the normal world and one for the secure world.

OP-TEE is an open source stack of the Trusted Execution Environment which includes:

  • OP-TEE OS: Trusted side of the TEE
  • OP-TEE Client: Normal world client side of the TEE
  • OP-TEE Test (or xtest): OP-TEE Test Suite

The OP-TEE project is developed and maintained by Linaro under BSD 2-Clause. The source code is available at https:// This stack supports Arm-v7 and Arm-v8 architectures.

The TEE exposes its features through a tandem operation between a Client Application and a Trusted Application. The client application runs in the Rich OS and always initiates the communication with the Trusted Application that runs in the Trusted OS. The Client application interacts with the TEE through the TEE client API interface. The Secure Application interacts with the TEE Core through the TEE Internal API.

OP-TEE is a Trusted Execution Environment (TEE) designed as a companion to a non-secure Linux kernel running on Arm cores using the TrustZone? technology. OP-TEE implements TEE Internal Core API v1.1.x which is the API exposed to Trusted Applications and the TEE Client API v1.0, which is the API describing how to communicate with a TEE. Those APIs are defined in the GlobalPlatform API specifications.

The non-secure OS is referred to as the Rich Execution Environment (REE) in TEE specifications. It is typically a Linux OS flavor as a GNU/Linux distribution or the AOSP.

OP-TEE is designed primarily to rely on the Arm TrustZone? technology as the underlying hardware isolation mechanism. However, it has been structured to be compatible with any isolation technology suitable for the TEE concept and goals, such as running as a virtual machine or on a dedicated CPU.

The main design goals for OP-TEE are:

  • Isolation - the 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 HW and has to support various setups such as multiple client OSes or multiple TEEs.

For more info:

OP-TEE on venice

In general the following things need to be done to use OP-TEE in the 'SPL -> ATF -> OP-TEE -> U-Boot -> Linux' boot flow:

  • build ATF (bl31.bin) with OP-TEE as a Secure Payload Dispatcher (SPD) (add 'SPD=opteed' to env). Take care to define BL32_BASE according to your board's DRAM size and SoC type. For IMX8MM TEE is loaded to the last 32MiB of DRAM and for IMX8MM TEE is loaded to a fixed location of 0x56000000
  • build OP-TEE (tee.bin) and copy it to your U-Boot build dir. Take care to define CFG_DDR_SIZE which is used to determine TEE's load address
  • add tee.bin to U-Boot FIT image and take care to specify the load address of the ATF (BL31) (0x920000 for MX8MM and 0x960000 for MX8MN) and the load address of TEE (BL32) as specified above
    • for U-Boot v2021.07 which uses arch/arm/mach-imx/ to create the FIT image the tee.bin will be added as long as it is in your U-Boot's build director but you also need to set TEE_LOAD_ADDR env to specify the load addr of the TEE as well as ATF_LOAD_ADDRESS per your SoC
    • for U-Boot v2022.01 the generation of the FIT image has moved to using the 'binman' tool thus adding tee.bin requires you to modify your board's u-boot.dtsi itb node to add tee.bin to itb/fit/images/ and add a reference to it in the loadables property of your boot configuration following atf
  • To enable OP-TEE support in U-Boot and Linux a /firmware/optee node needs to be added to your board u-boot.dtsi with 'compatible = "linaro,optee-tz"' and 'method = "smc"' nodes. This node is used by the U-Boot dek_blob command and if present and OP-TEE is loaded will be added to your Linux dtb via ft_system_setup in arch/arm/mach-imx/imx8m/fdt.c
  • To enable the 'dek_blob' command in U-Boot which uses OP-TEE enable CMD_DEKBLOB=y
  • To enable optee driver in Linux enable "CONFIG_TEE" and "CONFIG_OPTEE" and optionally CONFIG_HW_RANDOM_OPTEE as well as the /firmware/optee node in your U-Boot board u-boot.dtsi as that automates adding it to the Linux dtb during boot
  • Add a memory carve-out for the memory reserved for OP-TEE core and shared-memory block via /reserved-memory/optee_core and /reserved_memory/optee_shm nodes in u-boot.dtsi (they will be copied to the Linux dtb when booting of OP-TEE is enabled)

It is important to specify the 'load address' of the TEE appropriately for each component (ATF, OP-TEE, and U-Boot FIT image) and the value depends both on the IMX8M SoC flavor (ie IMX8MM vs IMX8MN) and size of DRAM.

Here is how you can integrate OP-TEE into your boot firmware for U-Boot v2021.07-venice (or any 'pre-binman' venice) using the venice/bsp:

with setup cross-toolchain already setup (ie . ./setup-environment in venice bsp dir)

  • setup environment
    . ./setup-environment
    export ATF_ARGS="PLAT=imx8mm SPD=opteed"
    # venice boards have either 1, 2, or 4GiB of DRAM
    export DDR_GB=1 # 1, 2, or 4
    export CFG_DDR_SIZE=$(printf "0x%x\n" $(($DDR_GB*1024*1024*1024)))
    # load TEE to the last 32MiB of DRAM (IMX8MM) or to fixed location of 0x56000000 (for IMX8MN)
    export BL32_BASE=$(printf "0x%x\n" $((0x40000000 + $CFG_DDR_SIZE - $((32*1024*1024)) )))
    # ATF (BL31) load address is in on-chip ram (OCRAM) and the address depends on the SoC (IMX8MM vs IMX8MN)
    export ATF_LOAD_ADDR=0x920000 # MX8MM=0x920000 MX8MN=0x960000
    export TEE_LOAD_ADDR=$BL32_BASE
    • ATF_ARGS is used by the venice BSP Makefile to pass args tot he ATF build system; PLAT needs to be imx8mm or imx8mn and SPD=opteed to tell the ATF to jump to OP-TEE
    • CFG_DDR_SIZE is used by imx-optee-os to calculate the load address used when linking tee.bin
    • BL32_BASE is used to tell the ATF the jump address of BL32
    • ATF_LOAD_ADDR and TEE_LOAD_ADDR are used by U-Boot arch/arm/mach-imx/ for the load address of the ATF and TEE when creating the U-Boot FIT image which contains the ATF, TEE, DTB's and U-Boot proper
  • build OP-TEE
    # build imx-optee-os and copy tee.bin to the U-Boot build dir
    git clone -b lf-5.10.52_2.1.0
    cd imx-optee-os
    make -j8 \
      ARCH=arm \
      PLATFORM=imx \
      PLATFORM_FLAVOR=mx8mmevk \
      O=out && ${CROSS_COMPILE}objcopy -O binary out/core/tee.elf ../u-boot/tee.bin
    # confirm TEE_LOAD_ADDR
    ${CROSS_COMPILE}readelf -h out/core/tee.elf | grep "Entry point address" | awk '{print $4}'
    echo $TEE_LOAD_ADDR # should agree with above result
    • note that out/core/tee.bin is not the correct file that needs to be copied to your u-boot directory; you must use objcopy to produce it
  • rebuild ATF and copy bl31.bin to your U-Boot build dir
    make -C atf $ATF_ARGS clean
    make -C atf $ATF_ARGS bl31
  • add /firmware/optee and /reserved-memory nodes to U-Boot dtsi. The reserved memory ranges depend on the TEE_LOAD_ADDR: the optee_core starts at TEE_LOAD_ADDR and is 32MiB in size and the optee_shm starts at TEE_LOAD_ADDR + 0x1c000000 and is 4KiB in size. You will need to edit files in u-boot/arch/arm/dts and the diff will look like this for example for a 1GiB board:
    $ git -C u-boot diff
    diff --git a/arch/arm/dts/imx8mm-u-boot.dtsi b/arch/arm/dts/imx8mm-u-boot.dtsi
    index f833d9df59bd..c443d45515c5 100644
    --- a/arch/arm/dts/imx8mm-u-boot.dtsi
    +++ b/arch/arm/dts/imx8mm-u-boot.dtsi
    @@ -3,6 +3,31 @@
      * Copyright (C) 2020 Jagan Teki <>
    +/ {
    +       firmware {
    +               optee {
    +                       compatible = "linaro,optee-tz";
    +                       method = "smc";
    +               };
    +       };
    +       reserved-memory {
    +               #address-cells = <2>;
    +               #size-cells = <2>;
    +               ranges;
    +               /* 32MiB */
    +               optee_core@7e000000 {
    +                       reg = <0x00 0x7e000000 0x00 0x1c00000>;
    +               };
    +               /* 4K */
    +               optee_shm@7fc00000 {
    +                       reg = <0x00 0x7fc00000 0x00 0x400000>;
    +               };
    +       };
     &{/soc@0} {
    diff --git a/configs/imx8mm_venice_defconfig b/configs/imx8mm_venice_defconfig
    index e24b0ed317f6..262b4e399d7e 100644
    --- a/configs/imx8mm_venice_defconfig
    +++ b/configs/imx8mm_venice_defconfig
    @@ -21,6 +21,7 @@ CONFIG_SPL_SERIAL_SUPPORT=y
  • rebuild U-Boot:
    make -C u-boot imx8mm_venice_defconfig
    make -C u-boot flash.bin
    ./mkimage_jtag --emmc -s u-boot/flash.bin@user:erase_none:66-32640 > u-boot_spl.bin

U-Boot Support

OP-TEE is contained in the boot firmware. Specifically the Arm Trusted Firmware (ATF) needs to be compiled with OP-TEE support. U-Boot loads OP-TEE OS, LInux OS, and DTB into memory then jumps to OP-TEE OS. The OP-TEE OS initializes the secure world and modifies the DTB on the fly to add a specific node to load Linux TEE drivers then it jumps to normal world to boot the LInux OS.

See also:

OP-TEE LInux Support

The Linux TEE driver defines the generic interface to a TEE.

For more info see: