← Back to list

Porting Guide for OP-TEE with Android on iMX6 custom board

Key words: TEE, OP-TEE, Android, Porting guide, iMX6 DualLite

Anass Elmoadine · 2024-07-10 09:51 · 2 claps · 14.4 min read
#tees #android #imx6 #iot-security #security
Open on Medium ↗
Wiki topics: 📟 · Gadgets & IoT

Porting Guide for OP-TEE with Android on iMX6 custom board

Key words: TEE, OP-TEE, Android, Porting guide, iMX6 DualLite

Written by Thomas Corbin and Anass Elmoadine

Introduction

The Trusted Execution Environment (TEE) is a secure area within modern CPUs designed to handle cryptographic operations such as encryption and signing. Our company, *Horanet, provides physical access control solutions using devices powered by the NXP iMX6 CPU and Android. Since these devices are often deployed in public spaces and are vulnerable to theft or physical attacks, the TEE *is essential for protecting cryptographic secrets like private keys.

A TEE operates on a specialized, compact OS known as a TEE-OS or secure OS. Examples include Trusty (by Google), OP-TEE (by Linaro), QTEE (by Qualcomm), Teegris (by Samsung), iTrustee (by Huawei), and Secure Enclave (by Apple). Among these, only Trusty and OP-TEE provide an interface with the Android OS. Trusty is specifically designed for Android but has not been adapted for the iMX6. On the other hand, OP-TEE is an open-source, well-documented OS that has already been ported to the SabreSD development board, which is NXP’s official development platform for the i.MX6.

As our custom board (PM098) has similarities with the Sabre SD board, notably the i.MX6 DualLite ARM Cortex A9 CPU (in spite of a difference on RAM size : 2 GB DDR3 RAM), we have based our solution on OP-TEE, which contains an implementation for a complete use of the Trusted Execution Environment (Fig.1) using ARM® TrustZone® technology. But we had to modify and adapt the code already implemented for the Sabre SD to make it compatible with our board.

Fig. 1: Booting Flow on i.MX6 and i.MX7 [1]

Fig. 1: Booting Flow on i.MX6 and i.MX7 [1]

This article provides a guide for porting OP-TEE to the PM098 board and can serve as support for other porting efforts on boards with similar CPUs.

We will start by preparing the work environment. Then, we will explain how to adapt the OP-TEE source codes, compile the project, deploy it on the board, and finally test its functionality.

Preparation of the Environment

Prerequisites:

  • An AOSP9 adapted to the PM098.

Clone the following three GitHub repositories:

Porting OP-TEE to the PM098 Board

1. Adaptation to the PM098 Board:

Specific modifications to be made:

  • In the imx-optee-os project

First, add the target for the PM098 in the core/arch/arm/plat-imx/conf.mk file. We will base this on the configurations of the Sabre SD target by copying its configurations and adapting them to our board. On the PM098, the kernel is loaded at 0x14008000 (CFG_NS_ENTRY_ADDR) and the device tree at 0x14f00000 (CFG_DT_ADDR). The RAM is 2 GB, so CFG_DDR_SIZE needs to be modified accordingly. Next, define where to place the beginning of the Trusted Zone in the RAM. We have chosen to place it at the end of the RAM at 0x8e000000 (CFG_TZDRAM_START). The configurations related to log level and debug are up to the developer's preference.

- - a/core/arch/arm/plat-imx/conf.mk
+++ b/core/arch/arm/plat-imx/conf.mk
@@ -2,7 +2,7 @@ PLATFORM_FLAVOR ?= mx6ulevk
# Get SoC associated with the PLATFORM_FLAVOR
mx6d-flavorlist =
-mx6dl-flavorlist = mx6dlsabresd mx6dlsabreauto
+mx6dl-flavorlist = mx6dlsabresd mx6dlsabreauto mx6dlpm098
mx6q-flavorlist = mx6qsabrelite mx6qsabresd mx6qsabreauto
mx6qp-flavorlist = mx6qpsabresd mx6qpsabreauto
mx6s-flavorlist = mx6solosabresd mx6solosabreauto
@@ -297,6 +297,29 @@ CFG_BOOT_SECONDARY_REQUEST = y
CFG_UART_BASE ?= UART1_BASE
endif

+ifneq (,$(filter $(PLATFORM_FLAVOR),mx6dlpm098))
+CFG_DT = y
+# For kernel entry addr.
+CFG_NS_ENTRY_ADDR = 0x14008000
+CFG_DT_ADDR = 0x14f00000
+CFG_DDR_SIZE = 0x80000000
+CFG_TZDRAM_START = 0x8e000000
+CFG_PSCI_ARM32 ?= y
+CFG_BOOT_SYNC_CPU = n
+CFG_BOOT_SECONDARY_REQUEST = y
+CFG_UART_BASE = UART1_BASE
+# For debugging, set log level to max
+# Override /mk/config.mk
+CFG_TEE_CORE_LOG_LEVEL = 1
+CFG_TEE_TA_LOG_LEVEL = 1
+CFG_TEE_CORE_MALLOC_DEBUG = n
+CFG_TEE_TA_MALLOC_DEBUG = n
+endif
ifneq (,$(filter $(PLATFORM_FLAVOR),mx6dlsabreauto))
CFG_DT ?= y
CFG_NS_ENTRY_ADDR ?= 0x12000000

Additionally, modify the core/arch/arm/plat-imx/tzasc.c file, since unlike the Sabre SD, our board has 2 GB of RAM.

--- a/core/arch/arm/plat-imx/tzasc.c
+++ b/core/arch/arm/plat-imx/tzasc.c
@@ -110,6 +111,35 @@ static int board_imx_tzasc_configure(vaddr_t addr)

        return 0;
}
+#elif (defined(PLATFORM_FLAVOR_mx6dlpm098))
+static int board_imx_tzasc_configure(vaddr_t addr)
+{
+    tzc_init(addr);
+
+    tzc_configure_region(0, 0x00000000, TZC_ATTR_SP_S_RW);
+
+       /* We map 2Go and 256Mo to effectively map 2Go starting at 0x1000 0000
+          Disabling 1 subsection of 256Mo
+        */
+       tzc_configure_region(1, 0x00000000,
+               TZC_ATTR_REGION_SIZE(TZC_REGION_SIZE_2G) |
+               TZC_ATTR_REGION_EN_MASK | TZC_ATTR_SP_NS_RW
+               | TZC_ATTR_SUBREGION_DIS(0));
+       tzc_configure_region(2, 0x80000000,
+               TZC_ATTR_REGION_SIZE(TZC_REGION_SIZE_256M) |
+               TZC_ATTR_REGION_EN_MASK | TZC_ATTR_SP_NS_RW);
+       tzc_configure_region(3, CFG_TZDRAM_START,
+               TZC_ATTR_REGION_SIZE(TZC_REGION_SIZE_32M) |
+               TZC_ATTR_REGION_EN_MASK | TZC_ATTR_SP_S_RW);
+       tzc_configure_region(4, CFG_SHMEM_START,
+               TZC_ATTR_REGION_SIZE(TZC_REGION_SIZE_4M) |
+               TZC_ATTR_REGION_EN_MASK | TZC_ATTR_SP_ALL);
+
+    tzc_dump_state();
+
+    return 0;
+}
#elif (defined(PLATFORM_FLAVOR_mx6qpsabreauto) \
        || defined(PLATFORM_FLAVOR_mx6qsabreauto) \
        || defined(PLATFORM_FLAVOR_mx6dlsabreauto) \
  • Update the lib/libmbedtls library, as it will be useful later for compiling Keymaster and Gatekeeper.
  • Add toolchains from the upstream OP-TEE OS GitHub project by running:
` mkdir optee; cd optee; repo init -u https://github.com/OP-TEE/manifest.git -m imx.xml -b 3.18.0; repo sync -j4 --no-clone-bundle; cd build; make -j2 toolchains; cp -r ./toolchains/ PATH/TO/imx-optee-os `
  • In the kmgk project :

We will start by modifying the rc files. Initially, we have removed the class from our keymaster and gatekeeper services and added the disabled keyword to prevent them from starting automatically. For the wait_for_keymaster_optee service, we added the oneshot keyword to prevent it from restarting repeatedly once it has been killed.

--- a/gatekeeper/android.hardware.gatekeeper@1.0-service.optee.rc
+++ b/gatekeeper/android.hardware.gatekeeper@1.0-service.optee.rc
@@ -1,6 +1,6 @@
service vendor.gatekeeper-1-0-optee /vendor/bin/hw/android.hardware.gatekeeper@1.0-service.optee
     interface android.hardware.gatekeeper@1.0::IGatekeeper optee
-    interface android.hardware.gatekeeper@1.0::IGatekeeper default
+#    interface android.hardware.gatekeeper@1.0::IGatekeeper default
-    class hal
     user system
     group system shell
+    disabled
--- a/keymaster/3.0/android.hardware.keymaster@3.0-service.optee.rc
+++ b/keymaster/3.0/android.hardware.keymaster@3.0-service.optee.rc
@@ -1,6 +1,6 @@
service vendor.keymaster-3-0-optee /vendor/bin/hw/android.hardware.keymaster@3.0-service.optee
     interface android.hardware.keymaster@3.0::IKeymasterDevice optee
     interface android.hardware.keymaster@3.0::IKeymasterDevice default
-    class early_hal
     user system
     group system drmrpc shell
+    disabled
--- a/keymaster/wait/wait_for_keymaster_optee.rc
+++ b/keymaster/wait/wait_for_keymaster_optee.rc
@@ -3,3 +3,4 @@ service wait_for_keymaster_optee /vendor/bin/hw/wait_for_keymaster_optee
     group root system
     priority -20
     ioprio rt 0
+    oneshot

To build the trusted apps for Gatekeeper and Keymaster, you need to add some information regarding our target and specify the path to the root of the imx-optee-os project as well as the path to the aosp_optee.mk file located in the imx-optee-os project.

--- a/gatekeeper/ta/Android.mk
+++ b/gatekeeper/ta/Android.mk
@@ -1,4 +1,10 @@
LOCAL_PATH := $(call my-dir)

+OPTEE_PLATFORM = imx
+OPTEE_PLATFORM_FLAVOR = mx6dlpm098
+BUILD_OPTEE_MK=./vendor/nxp-opensource/imx-optee-os/mk/aosp_optee.mk
+OPTEE_OS_DIR=./vendor/nxp-opensource/imx-optee-os
+OPTEE_TA_TARGETS=ta_arm32
local_module := 4d573443-6a56-4272-ac6f-2425af9ef9bb.ta
include $(BUILD_OPTEE_MK)
--- a/keymaster/ta/Android.mk
+++ b/keymaster/ta/Android.mk
@@ -1,4 +1,11 @@
LOCAL_PATH := $(call my-dir)

+OPTEE_PLATFORM = imx
+OPTEE_PLATFORM_FLAVOR = mx6dlpm098
+BUILD_OPTEE_MK=./vendor/nxp-opensource/imx-optee-os/mk/aosp_optee.mk
+OPTEE_OS_DIR=./vendor/nxp-opensource/imx-optee-os
+OPTEE_TA_TARGETS=ta_arm32
local_module := dba51a17-0563-11e7-93b1-6fa7b0071a51.ta
include $(BUILD_OPTEE_MK)

Next, you need to modify the Android.mk file of Keymaster so that the object can be built during an AOSP build.

--- a/keymaster/Android.mk
+++ b/keymaster/Android.mk
@@ -19,6 +19,34 @@
#ifneq (,$(filter $(TARGET_PRODUCT), hikey))
LOCAL_PATH:= $(call my-dir)

+include $(CLEAR_VARS)
+LOCAL_MODULE := keystore.optee
+LOCAL_PROPRIETARY_MODULE := true
+LOCAL_MODULE_RELATIVE_PATH := hw
+LOCAL_VENDOR_MODULE := true
+LOCAL_SRC_FILES := \
+       3.0/optee_keymaster3_device.cpp \
+       ipc/optee_keymaster_ipc.cpp \
+       optee_keymaster.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+       libteec \
+       liblog \
+       libhidlbase \
+       libhidltransport \
+       libhardware \
+       libutils \
+       libcutils \
+       libkeymaster_messages \
+       libkeymaster3device \
+       android.hardware.keymaster@3.0
+
+LOCAL_C_INCLUDES := \
+       $(LOCAL_PATH)/include
+
+
+include $(BUILD_SHARED_LIBRARY)
################################################################################
# Build keymaster HAL                                                          #
########################################################################################################
  • In the optee-client project:

In the optee-client project, there’s only a slight modification to the Android.mk, as it uses the 64bit library by default, whereas we’re using 32bit.

--- a/Android.mk
+++ b/Android.mk
@@ -13,9 +13,9 @@ TEEC_TEST_LOAD_PATH ?= /data/vendor/tee
CFG_TEE_FS_PARENT_PATH ?= /data/vendor/tee
CFG_TEE_SUPP_PLUGINS ?= y
ifneq ($(strip $($(combo_2nd_arch_prefix)TARGET_TOOLS_PREFIX)),)
-CFG_TEE_PLUGIN_LOAD_PATH ?= /vendor/lib64/tee-supplicant/plugins/
-else
CFG_TEE_PLUGIN_LOAD_PATH ?= /vendor/lib/tee-supplicant/plugins/
+else
+CFG_TEE_PLUGIN_LOAD_PATH ?= /vendor/lib64/tee-supplicant/plugins/
endif

$(info CFG_TEE_PLUGIN_LOAD_PATH = ${CFG_TEE_PLUGIN_LOAD_PATH})
  • On AOSP9 :

The modifications made to AOSP9 present a novel approach for loading OPTEE-OS at startup. While there may be better methods, we maintained and satisfied with the actual solution.

  1. Modification of boot.img compilation

To add the OPTEE-OS firmware to the boot.img, we need to modify the Makefile in the build/core project of AOSP9. Additionally, to include OPTEE-OS in the boot.img, we had to create the mkhbootimg tool because the mkbootimg tool did not support it.

diff --git a/core/Makefile b/core/Makefile
index ee3eb1c5d0..f51e93600f 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -676,6 +676,9 @@ else
   INSTALLED_KERNEL_TARGET :=
endif

+INSTALLED_OPTEE_TARGET := $(PRODUCT_OUT)/tee.orig.bin
+BOARD_OPTEE_BASE := 0x8e000000
+INSTALLED_INTERMEDIATE_BOOTIMAGE_TARGET = $@_intermediate
# -----------------------------------------------------------------
# the ramdisk
INTERNAL_RAMDISK_FILES := $(filter $(TARGET_ROOT_OUT)/%, \
@@ -701,10 +704,14 @@ ifneq ($(strip $(TARGET_NO_KERNEL)),true)

# -----------------------------------------------------------------
# the boot image, which is a collection of other images.
-INTERNAL_BOOTIMAGE_ARGS := \
+#INTERNAL_BOOTIMAGE_ARGS := \
        $(addprefix --second ,$(INSTALLED_2NDBOOTLOADER_TARGET)) \
        --kernel $(INSTALLED_KERNEL_TARGET)

+INTERNAL_BOOTIMAGE_ARGS := \
+        $(addprefix --second ,$(INSTALLED_2NDBOOTLOADER_TARGET)) \
+        --kernel $(INSTALLED_OPTEE_TARGET)
ifneq ($(BOARD_BUILD_SYSTEM_ROOT_IMAGE),true)
INTERNAL_BOOTIMAGE_ARGS += --ramdisk $(INSTALLED_RAMDISK_TARGET)
endif
@@ -719,6 +726,8 @@ ifdef BOARD_KERNEL_PAGESIZE
   INTERNAL_BOOTIMAGE_ARGS += --pagesize $(BOARD_KERNEL_PAGESIZE)
endif

+INTERNAL_HBOOTIMAGE_ARGS := $(INSTALLED_KERNEL_TARGET) $(BOARD_OPTEE_BASE)
ifeq ($(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SUPPORTS_VERITY),true)
ifeq ($(BOARD_BUILD_SYSTEM_ROOT_IMAGE),true)
VERITY_KEYID := veritykeyid=id:`openssl x509 -in $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_VERITY_SIGNING_KEY).x509.pem -text \
@@ -749,9 +758,10 @@ $(error TARGET_BOOTIMAGE_USE_EXT2 is not supported anymore)

else ifeq (true,$(BOARD_AVB_ENABLE)) # TARGET_BOOTIMAGE_USE_EXT2 != true

-$(INSTALLED_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(AVBTOOL) $(INTERNAL_BOOTIMAGE_FILES) $(BOARD_AVB_BOOT_KEY_PATH)
+$(INSTALLED_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(MKHBOOTIMG) $(AVBTOOL) $(INTERNAL_BOOTIMAGE_FILES) $(BOARD_AVB_BOOT_KEY_PATH)
        $(call pretty,"Target boot image: $@")
-       $(hide) $(MKBOOTIMG) $(INTERNAL_BOOTIMAGE_ARGS) $(INTERNAL_MKBOOTIMG_VERSION_ARGS) $(BOARD_MKBOOTIMG_ARGS) --output $@
+       $(hide) $(MKBOOTIMG) $(INTERNAL_BOOTIMAGE_ARGS) $(INTERNAL_MKBOOTIMG_VERSION_ARGS) $(BOARD_MKBOOTIMG_ARGS) --output $(INSTALLED_INTERMEDIATE_BOOTIMAGE_TARGET)
+       $(hide) $(MKHBOOTIMG) $(INTERNAL_HBOOTIMAGE_ARGS) $(INSTALLED_INTERMEDIATE_BOOTIMAGE_TARGET) $@
        $(hide) $(call assert-max-image-size,$@,$(call get-hash-image-max-size,$(BOARD_BOOTIMAGE_PARTITION_SIZE)))
        $(hide) $(AVBTOOL) add_hash_footer \
          --image $@ \
@@ -760,9 +770,10 @@ $(INSTALLED_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(AVBTOOL) $(INTERNAL_BOOTIMAGE_FILE
          $(BOARD_AVB_BOOT_ADD_HASH_FOOTER_ARGS)

.PHONY: bootimage-nodeps
-bootimage-nodeps: $(MKBOOTIMG) $(AVBTOOL) $(BOARD_AVB_BOOT_KEY_PATH)
+bootimage-nodeps: $(MKBOOTIMG) $(MKHBOOTIMG) $(AVBTOOL) $(BOARD_AVB_BOOT_KEY_PATH)
        @echo "make $@: ignoring dependencies"
-       $(hide) $(MKBOOTIMG) $(INTERNAL_BOOTIMAGE_ARGS) $(INTERNAL_MKBOOTIMG_VERSION_ARGS) $(BOARD_MKBOOTIMG_ARGS) --output $(INSTALLED_BOOTIMAGE_TARGET)
+       $(hide) $(MKBOOTIMG) $(INTERNAL_BOOTIMAGE_ARGS) $(INTERNAL_MKBOOTIMG_VERSION_ARGS) $(BOARD_MKBOOTIMG_ARGS) --output $(INSTALLED_INTERMEDIATE_BOOTIMAGE_TARGET)
+       $(hide) $(MKHBOOTIMG) $(INTERNAL_HBOOTIMAGE_ARGS) $(INSTALLED_INTERMEDIATE_BOOTIMAGE_TARGET) $(INSTALLED_BOOTIMAGE_TARGET)
        $(hide) $(call assert-max-image-size,$(INSTALLED_BOOTIMAGE_TARGET),$(call get-hash-image-max-size,$(BOARD_BOOTIMAGE_PARTITION_SIZE)))
        $(hide) $(AVBTOOL) add_hash_footer \
          --image $(INSTALLED_BOOTIMAGE_TARGET) \
@@ -772,45 +783,51 @@ bootimage-nodeps: $(MKBOOTIMG) $(AVBTOOL) $(BOARD_AVB_BOOT_KEY_PATH)

else ifeq (true,$(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SUPPORTS_BOOT_SIGNER)) # BOARD_AVB_ENABLE != true

-$(INSTALLED_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(INTERNAL_BOOTIMAGE_FILES) $(BOOT_SIGNER)
+$(INSTALLED_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(MKHBOOTIMG) $(INTERNAL_BOOTIMAGE_FILES) $(BOOT_SIGNER)
        $(call pretty,"Target boot image: $@")
-       $(hide) $(MKBOOTIMG) $(INTERNAL_BOOTIMAGE_ARGS) $(INTERNAL_MKBOOTIMG_VERSION_ARGS) $(BOARD_MKBOOTIMG_ARGS) --output $@
+       $(hide) $(MKBOOTIMG) $(INTERNAL_BOOTIMAGE_ARGS) $(INTERNAL_MKBOOTIMG_VERSION_ARGS) $(BOARD_MKBOOTIMG_ARGS) --output $(INSTALLED_INTERMEDIATE_BOOTIMAGE_TARGET)
+       $(hide) $(MKHBOOTIMG) $(INTERNAL_HBOOTIMAGE_ARGS) $(INSTALLED_INTERMEDIATE_BOOTIMAGE_TARGET) $@
        $(BOOT_SIGNER) /boot $@ $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_VERITY_SIGNING_KEY).pk8 $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_VERITY_SIGNING_KEY).x509.pem $@
        $(hide) $(call assert-max-image-size,$@,$(BOARD_BOOTIMAGE_PARTITION_SIZE))

.PHONY: bootimage-nodeps
bootimage-nodeps: $(MKBOOTIMG) $(BOOT_SIGNER)
        @echo "make $@: ignoring dependencies"
-       $(hide) $(MKBOOTIMG) $(INTERNAL_BOOTIMAGE_ARGS) $(INTERNAL_MKBOOTIMG_VERSION_ARGS) $(BOARD_MKBOOTIMG_ARGS) --output $(INSTALLED_BOOTIMAGE_TARGET)
+       $(hide) $(MKBOOTIMG) $(INTERNAL_BOOTIMAGE_ARGS) $(INTERNAL_MKBOOTIMG_VERSION_ARGS) $(BOARD_MKBOOTIMG_ARGS) --output $(INSTALLED_INTERMEDIATE_BOOTIMAGE_TARGET)
+       $(hide) $(MKHBOOTIMG) $(INTERNAL_HBOOTIMAGE_ARGS) $(INSTALLED_INTERMEDIATE_BOOTIMAGE_TARGET) $(INSTALLED_BOOTIMAGE_TARGET)
        $(BOOT_SIGNER) /boot $(INSTALLED_BOOTIMAGE_TARGET) $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_VERITY_SIGNING_KEY).pk8 $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_VERITY_SIGNING_KEY).x509.pem $(INSTALLED_BOOTIMAGE_TARGET)
        $(hide) $(call assert-max-image-size,$(INSTALLED_BOOTIMAGE_TARGET),$(BOARD_BOOTIMAGE_PARTITION_SIZE))

else ifeq (true,$(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SUPPORTS_VBOOT)) # PRODUCT_SUPPORTS_BOOT_SIGNER != true

-$(INSTALLED_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(INTERNAL_BOOTIMAGE_FILES) $(VBOOT_SIGNER) $(FUTILITY)
+$(INSTALLED_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(MKHBOOTIMG) $(INTERNAL_BOOTIMAGE_FILES) $(VBOOT_SIGNER) $(FUTILITY)
        $(call pretty,"Target boot image: $@")
-       $(hide) $(MKBOOTIMG) $(INTERNAL_BOOTIMAGE_ARGS) $(INTERNAL_MKBOOTIMG_VERSION_ARGS) $(BOARD_MKBOOTIMG_ARGS) --output $@.unsigned
+       $(hide) $(MKBOOTIMG) $(INTERNAL_BOOTIMAGE_ARGS) $(INTERNAL_MKBOOTIMG_VERSION_ARGS) $(BOARD_MKBOOTIMG_ARGS) --output $(INSTALLED_INTERMEDIATE_BOOTIMAGE_TARGET)
+       $(hide) $(MKHBOOTIMG) $(INTERNAL_HBOOTIMAGE_ARGS) $(INSTALLED_INTERMEDIATE_BOOTIMAGE_TARGET) $@.unsigned
        $(VBOOT_SIGNER) $(FUTILITY) $@.unsigned $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_VBOOT_SIGNING_KEY).vbpubk $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_VBOOT_SIGNING_KEY).vbprivk $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_VBOOT_SIGNING_SUBKEY).vbprivk $@.keyblock $@
        $(hide) $(call assert-max-image-size,$@,$(BOARD_BOOTIMAGE_PARTITION_SIZE))

.PHONY: bootimage-nodeps
-bootimage-nodeps: $(MKBOOTIMG) $(VBOOT_SIGNER) $(FUTILITY)
+bootimage-nodeps: $(MKBOOTIMG) $(MKHBOOTIMG) $(VBOOT_SIGNER) $(FUTILITY)
        @echo "make $@: ignoring dependencies"
-       $(hide) $(MKBOOTIMG) $(INTERNAL_BOOTIMAGE_ARGS) $(INTERNAL_MKBOOTIMG_VERSION_ARGS) $(BOARD_MKBOOTIMG_ARGS) --output $(INSTALLED_BOOTIMAGE_TARGET).unsigned
+       $(hide) $(MKBOOTIMG) $(INTERNAL_BOOTIMAGE_ARGS) $(INTERNAL_MKBOOTIMG_VERSION_ARGS) $(BOARD_MKBOOTIMG_ARGS) --output $(INSTALLED_INTERMEDIATE_BOOTIMAGE_TARGET)
+       $(hide) $(MKHBOOTIMG) $(INTERNAL_HBOOTIMAGE_ARGS) $(INSTALLED_INTERMEDIATE_BOOTIMAGE_TARGET) $(INSTALLED_BOOTIMAGE_TARGET).unsigned
        $(VBOOT_SIGNER) $(FUTILITY) $(INSTALLED_BOOTIMAGE_TARGET).unsigned $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_VBOOT_SIGNING_KEY).vbpubk $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_VBOOT_SIGNING_KEY).vbprivk $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_VBOOT_SIGNING_SUBKEY).vbprivk $(INSTALLED_BOOTIMAGE_TARGET).keyblock $(INSTALLED_BOOTIMAGE_TARGET)
        $(hide) $(call assert-max-image-size,$(INSTALLED_BOOTIMAGE_TARGET),$(BOARD_BOOTIMAGE_PARTITION_SIZE))

else # PRODUCT_SUPPORTS_VBOOT != true

-$(INSTALLED_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(INTERNAL_BOOTIMAGE_FILES)
+$(INSTALLED_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(MKHBOOTIMG) $(INTERNAL_BOOTIMAGE_FILES)
        $(call pretty,"Target boot image: $@")
-       $(hide) $(MKBOOTIMG) $(INTERNAL_BOOTIMAGE_ARGS) $(INTERNAL_MKBOOTIMG_VERSION_ARGS) $(BOARD_MKBOOTIMG_ARGS) --output $@
+       $(hide) $(MKBOOTIMG) $(INTERNAL_BOOTIMAGE_ARGS) $(INTERNAL_MKBOOTIMG_VERSION_ARGS) $(BOARD_MKBOOTIMG_ARGS) --output $(INSTALLED_INTERMEDIATE_BOOTIMAGE_TARGET)
+       $(hide) $(MKHBOOTIMG) $(INTERNAL_HBOOTIMAGE_ARGS) $(INSTALLED_INTERMEDIATE_BOOTIMAGE_TARGET) $@
        $(hide) $(call assert-max-image-size,$@,$(BOARD_BOOTIMAGE_PARTITION_SIZE))

.PHONY: bootimage-nodeps
-bootimage-nodeps: $(MKBOOTIMG)
+bootimage-nodeps: $(MKBOOTIMG) $(MKHBOOTIMG)
        @echo "make $@: ignoring dependencies"
-       $(hide) $(MKBOOTIMG) $(INTERNAL_BOOTIMAGE_ARGS) $(INTERNAL_MKBOOTIMG_VERSION_ARGS) $(BOARD_MKBOOTIMG_ARGS) --output $(INSTALLED_BOOTIMAGE_TARGET)
+       $(hide) $(MKBOOTIMG) $(INTERNAL_BOOTIMAGE_ARGS) $(INTERNAL_MKBOOTIMG_VERSION_ARGS) $(BOARD_MKBOOTIMG_ARGS) --output $(INSTALLED_INTERMEDIATE_BOOTIMAGE_TARGET)
+       $(hide) $(MKHBOOTIMG) $(INTERNAL_HBOOTIMAGE_ARGS) $(INSTALLED_INTERMEDIATE_BOOTIMAGE_TARGET) $(INSTALLED_BOOTIMAGE_TARGET)
        $(hide) $(call assert-max-image-size,$(INSTALLED_BOOTIMAGE_TARGET),$(BOARD_BOOTIMAGE_PARTITION_SIZE))

endif # TARGET_BOOTIMAGE_USE_EXT2
@@ -2337,6 +2354,7 @@ OTATOOLS :=  $(HOST_OUT_EXECUTABLES)/minigzip \
   $(HOST_OUT_EXECUTABLES)/checkvintf \
   $(HOST_OUT_EXECUTABLES)/mkbootfs \
   $(HOST_OUT_EXECUTABLES)/mkbootimg \
+  $(HOST_OUT_EXECUTABLES)/mkhbootimg \
   $(HOST_OUT_EXECUTABLES)/fs_config \
   $(HOST_OUT_EXECUTABLES)/zipalign \
   $(HOST_OUT_EXECUTABLES)/bsdiff \
--- a/core/config.mk
+++ b/core/config.mk
@@ -652,6 +652,7 @@ MKBOOTIMG := $(HOST_OUT_EXECUTABLES)/mkbootimg$(HOST_EXECUTABLE_SUFFIX)
else
MKBOOTIMG := $(BOARD_CUSTOM_MKBOOTIMG)
endif
+MKHBOOTIMG := $(HOST_OUT_EXECUTABLES)/mkhbootimg$(HOST_EXECUTABLE_SUFFIX)
ifeq (,$(strip $(BOARD_CUSTOM_BPTTOOL)))
BPTTOOL := $(HOST_OUT_EXECUTABLES)/bpttool$(HOST_EXECUTABLE_SUFFIX)
else

The modifications in common.py pertain to the construction of the OTA image, which must also include the OPTEE-OS firmware.

--- a/tools/releasetools/common.py
+++ b/tools/releasetools/common.py
@@ -425,7 +425,7 @@ def _BuildBootableImage(sourcedir, fs_config_file, info_dict=None,
   # use MKBOOTIMG from environ, or "mkbootimg" if empty or not set
   mkbootimg = os.getenv('MKBOOTIMG') or "mkbootimg"

-  cmd = [mkbootimg, "--kernel", os.path.join(sourcedir, "kernel")]
+  cmd = [mkbootimg, "--kernel", os.path.join(sourcedir, '../../../../..', "tee.orig.bin")]

   fn = os.path.join(sourcedir, "second")
   if os.access(fn, os.F_OK):
@@ -461,9 +461,10 @@ def _BuildBootableImage(sourcedir, fs_config_file, info_dict=None,
   img_unsigned = None
   if info_dict.get("vboot", None):
     img_unsigned = tempfile.NamedTemporaryFile()
-    cmd.extend(["--output", img_unsigned.name])
+    img_name = img_unsigned.name
   else:
-    cmd.extend(["--output", img.name])
+    img_name = img.name
+  cmd.extend(["--output", img_name + "_intermediate"])

   # "boot" or "recovery", without extension.
   if (partition_name == "recovery" and
@@ -475,6 +476,10 @@ def _BuildBootableImage(sourcedir, fs_config_file, info_dict=None,
   p.communicate()
   assert p.returncode == 0, "mkbootimg of %s image failed" % (partition_name,)

+  p = Run(["mkhbootimg", os.path.join(sourcedir, "kernel"), '0x8e000000', img_name + "_intermediate", img_name], stdout=subprocess.PIPE)
+  p.communicate()
+  assert p.returncode == 0, "mkhbootimg of %s image failed" % (partition_name,)
+
   if (info_dict.get("boot_signer", None) == "true" and
       info_dict.get("verity_key", None)):
  1. Changes in device/fsl/imx6dq/pm098

We modify the init.rc of our target to include the launch of services related to OPTEE-OS, namely the keymaster, gatekeeper, and wait_for_keymaster service.

--- a/imx6dq/pm098/init.rc
+++ b/imx6dq/pm098/init.rc
@@ -36,13 +35,12 @@ on init
     mkdir /mnt/shm  0775 media drmrpc
     mount tmpfs tmpfs /mnt/shm mode=0775,uid=1013,gid=1031,size=1m

-on late-fs
-    # Start keymaster service
-    start vendor.keymaster-3-0-${ro.boot.keystore}
-
@@ -246,10 +237,16 @@ on boot
     start drm

     # Set rotation to 270 to cofigure as portrait mode
     setprop ro.sf.hwrotation 90

     # Set the density to 160dpi, default 128dpi is not good
     setprop ro.sf.lcd_density 120
+on boot && property:ro.boot.tee=yes
+   start vendor.gatekeeper-1-0-optee
+on boot && property:ro.boot.tee=no
+   start vendor.gatekeeper-1-0
@@ -364,15 +390,47 @@ on post-fs
     # Swap in only 1 page at a time
     write /proc/sys/vm/page-cluster 0
+on late-fs
+    # Start keymaster service
+    start vendor.keymaster-3-0-${ro.boot.keystore}
+on late-fs && property:ro.boot.tee=yes
+   start vendor.keymaster-3-0-optee
+on post-fs-data && property:ro.boot.tee=yes
+   # for OPTEE
+    start tee_supplicant
+    exec_start wait_for_keymaster_optee

We create the .rc file for the tee-supplicant service.

--- /dev/null
+++ b/imx6dq/pm098/tee-supplicant.rc
@@ -0,0 +1,4 @@
+service tee_supplicant /vendor/bin/tee-supplicant
+    user root
+    group root system

In the Makefile of our target, we add the libraries and objects required to run the client part of OPTEE-OS.

--- a/imx6dq/pm098/pm098.mk
+++ b/imx6dq/pm098/pm098.mk
@@ -163,6 +189,15 @@ PRODUCT_PACKAGES += \
     libgpuhelper \
     gatekeeper.imx6

+# OP-TEE client, tee supplicant
+PRODUCT_PACKAGES += \
+       libteec \
+       tee-supplicant
@@ -174,3 +209,20 @@ PRODUCT_PROPERTY_OVERRIDES += \
     ro.crypto.fde_sector_size=4096 \
     ro.crypto.volume.contents_mode=adiantum \
     ro.crypto.volume.filenames_mode=adiantum
+#PRODUCT_PROPERTY_OVERRIDES += \
+       ro.hardware.keystore=optee \
+       ro.hardware.gatekeeper=optee
+# For OP-TEE
+PRODUCT_COPY_FILES += \
+       $(IMX_DEVICE_PATH)/tee-supplicant.rc:$(TARGET_COPY_OUT_VENDOR)/etc/init/tee-supplicant.rc

Finally, the SELinux rules need to be written for the OPTEE-OS client by creating the files hal_gatekeeper_default.te, hal_keymaster_default.te, and tee.te.

--- a/imx6dq/pm098/sepolicy/file_contexts
+++ b/imx6dq/pm098/sepolicy/file_contexts
@@ -22,10 +22,25 @@
/dev/block/platform/soc0/soc/2100000\.aips-bus/219c000\.usdhc/by-name/userdata    u:object_r:userdata_block_device:s0
/dev/block/platform/soc0/soc/2100000\.aips-bus/219c000\.usdhc/by-name/datafooter  u:object_r:metadata_block_device:s0

+# For OPTEE, Default HALs
+/(vendor|system/vendor)/bin/hw/android\.hardware\.gatekeeper@1\.0-service\.optee     u:object_r:hal_gatekeeper_default_exec:s0
+/(vendor|system/vendor)/bin/hw/android\.hardware\.keymaster@3\.0-service\.optee      u:object_r:hal_keymaster_default_exec:s0
+/vendor/bin/tee-supplicant                  u:object_r:tee_exec:s0
+/vendor/bin/hw/wait_for_keymaster_optee     u:object_r:tee_exec:s0
+/dev/teepriv[0-9]*                          u:object_r:tee_device:s0
+/dev/tee[0-9]*                              u:object_r:tee_device:s0
+/data/vendor/tee                            u:object_r:tee_data_file:s0
--- /dev/null
+++ b/imx6dq/pm098/sepolicy/hal_gatekeeper_default.te
@@ -0,0 +1,4 @@
+allow hal_gatekeeper_default vendor_data_file:dir rw_dir_perms;
+allow hal_gatekeeper_default vendor_data_file:file create_file_perms;
+allow hal_gatekeeper_default vendor_file:file { map };
--- /dev/null
+++ b/imx6dq/pm098/sepolicy/hal_keymaster_default.te
@@ -0,0 +1,4 @@
+allow hal_keymaster_default vendor_data_file:dir rw_dir_perms;
+allow hal_keymaster_default vendor_data_file:file create_file_perms;
+allow hal_keymaster_default vendor_file:file { map };
--- /dev/null
+++ b/imx6dq/pm098/sepolicy/tee.te
@@ -0,0 +1,7 @@
+allow tee vendor_data_file:dir rw_dir_perms;
+allow tee vendor_data_file:dir create_dir_perms;
+allow tee vendor_data_file:file create_file_perms;
+allow tee system_data_file:dir { getattr };
+allow tee kmsg_device:chr_file rw_file_perms;
+get_prop(tee, hwservicemanager_prop)

3.Modification of U-Boot

We copy the CONFIG_IMX_OPTEE section from the .cfg .cfg file of the Sabre SD.

--- a/board/freescale/pm098/800mhz_4x256mx16.cfg
+++ b/board/freescale/pm098/800mhz_4x256mx16.cfg
@@ -3,7 +3,22 @@
  *
  * SPDX-License-Identifier:    GPL-2.0+
  */

+#ifdef CONFIG_IMX_OPTEE
+SET_BIT 4 0x20c4070 0x3c00000
+DATA 4 0x20e0024 0x00000003
+CHECK_BITS_SET 4 0x20e0024 0x3
+#endif
DATA 4, MX6_MMDC_P0_MDPDC, 0x0002002D
DATA 4, MX6_MMDC_P0_MDCFG0, 0x696C5323
DATA 4, MX6_MMDC_P0_MDCFG1, 0xB66E8D63

You need to enable the CONFIG_IMX_OPTEE configuration in the defconfig of our target.

--- a/configs/pm098_defconfig
+++ b/configs/pm098_defconfig
@@ -5,7 +5,8 @@ CONFIG_VIDEO=y
#CONFIG_SYS_EXTRA_OPTIONS="IMX_CONFIG=board/freescale/pm098/pm098.cfg,ANDROID_SUPPORT"
CONFIG_SYS_EXTRA_OPTIONS="IMX_CONFIG=board/freescale/pm098/pm098mx6dl2g.cfg,ANDROID_SUPPORT"
CONFIG_EFI_PARTITION=y
CONFIG_BOOTDELAY=5
+CONFIG_IMX_OPTEE=y

Here, we introduce the U-boot environment variable called tee. If it is set to *yes*, the system will boot with OPTEE-OS; if it is set to *no*, the system will boot without it.

Note: this variable must be initialized at the first boot; if it is modified later, it may cause issues with the system.

The secondary_kernel is also introduced here; it refers to the kernel launched after the OPTEE-OS kernel, which is our Linux kernel.

--- a/common/image-android.c
+++ b/common/image-android.c
@@ -232,16 +232,23 @@ int android_image_get_kernel(const struct andr_img_hdr *hdr, int verify,
#endif

        char *keystore = env_get("keystore");
-       if ((keystore == NULL) || strncmp(keystore, "trusty", sizeof("trusty"))) {
-               char *bootargs_trusty = "androidboot.keystore=software";
-               strncat(commandline, " ", sizeof(commandline) - strlen(commandline));
-               strncat(commandline, bootargs_trusty, sizeof(commandline) - strlen(commandline));
-       } else {
-               char *bootargs_trusty = "androidboot.keystore=trusty";
-               strncat(commandline, " ", sizeof(commandline) - strlen(commandline));
-               strncat(commandline, bootargs_trusty, sizeof(commandline) - strlen(commandline));
-       }
-
+       char *bootargs_keystore = "androidboot.keystore=software";
+
+       // TODO: In android, we require the software keystore to be enabled no matter what.
+       //       Changing the var here will break that.
+       //       We need to first check why we need the software keystore to add this back.
+       strncat(commandline, " ", sizeof(commandline) - strlen(commandline));
+       strncat(commandline, bootargs_keystore, sizeof(commandline) - strlen(commandline));
+
+       // FIXME: Fix for optee. Read above.
+       strncat(commandline, " ", sizeof(commandline) - strlen(commandline));
+       strncat(commandline, "androidboot.tee=", sizeof(commandline) - strlen(commandline));
+       strncat(commandline, env_get("tee") ?: "no", sizeof(commandline) - strlen(commandline));
#ifdef CONFIG_APPEND_BOOTARGS
        /* Add 'append_bootargs' to hold some paramemters which need to be appended
         * to bootargs */
@@ -286,6 +293,7 @@ ulong android_image_get_end(const struct andr_img_hdr *hdr)
        end += ALIGN(hdr->kernel_size, hdr->page_size);
        end += ALIGN(hdr->ramdisk_size, hdr->page_size);
        end += ALIGN(hdr->second_size, hdr->page_size);
+       end += ALIGN(hdr->secondary_kernel_size, hdr->page_size);

        return end;
}

In the file f_fastboot.c, we will manage how the do_boota function loads the Linux kernel and OPTEE-OS images into RAM

--- a/drivers/usb/gadget/f_fastboot.c
+++ b/drivers/usb/gadget/f_fastboot.c
@@ -2383,6 +2383,9 @@ int do_boota(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) {
                /* copy kernel image and boot header to hdr->kernel_addr - hdr->page_size */
                memcpy((void *)(ulong)(hdr->kernel_addr - hdr->page_size), (void *)hdr,
                                hdr->page_size + ALIGN(hdr->kernel_size, hdr->page_size));
+               if (hdr->secondary_kernel_addr && hdr->secondary_kernel_size)
+                       memcpy((void *)(ulong)hdr->secondary_kernel_addr, (void *)(ulong)hdr + hdr->page_size
+                                       + ALIGN(hdr->kernel_size, hdr->page_size) + ALIGN(hdr->ramdisk_size, hdr->page_size), hdr->secondary_kernel_size);
#endif /* CONFIG_ARCH_IMX8 || CONFIG_ARCH_IMX8M */
        } else {
                /* Fall into fastboot mode if get unacceptable error from avb

@@ -2470,18 +2473,30 @@ int do_boota(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) {
        if (check_image_arm64) {
                android_image_get_kernel(hdr, 0, NULL, NULL);
                addr = hdr->kernel_addr;
-       } else {
+       } else if (!hdr->secondary_kernel_addr || !hdr->secondary_kernel_size || !env_get("tee") || strcmp(env_get("tee"), "yes") == 0) {
                addr = (ulong)(hdr->kernel_addr - hdr->page_size);
+       } else {
+               addr = (ulong)(hdr->secondary_kernel_addr - hdr->page_size);
+               memcpy((void *)addr, (void *)(hdr->kernel_addr - hdr->page_size), hdr->page_size);
+
+               struct andr_img_hdr *hdr2 = (struct andr_img_hdr *)addr;
+
+               hdr2->kernel_addr = hdr2->secondary_kernel_addr;
+               hdr2->kernel_size = hdr2->secondary_kernel_size;
+               hdr2->secondary_kernel_addr = 0;
+               hdr2->secondary_kernel_size = 0;
        }
-       printf("kernel   @ %08x (%d)\n", hdr->kernel_addr, hdr->kernel_size);
-       printf("ramdisk  @ %08x (%d)\n", hdr->ramdisk_addr, hdr->ramdisk_size);
+       printf("kernel     @ %08x (%d)\n", hdr->kernel_addr, hdr->kernel_size);
+       if (hdr->secondary_kernel_size)
+               printf("2nd_kernel @ %08x (%d)\n", hdr->secondary_kernel_addr, hdr->secondary_kernel_size);
+       printf("ramdisk    @ %08x (%d)\n", hdr->ramdisk_addr, hdr->ramdisk_size);

Here, we are modifying the Android image header to include the size and address of the secondary_kernel, so that they can be correctly loaded into RAM in the do_boota function of the f_fastboot.cfile.

--- a/include/android_image.h
+++ b/include/android_image.h
@@ -68,6 +68,8 @@ struct andr_img_hdr {
        uint32_t recovery_dtbo_size;   /* size of recovery dtbo image */
        uint64_t recovery_dtbo_offset; /* offset in boot image */
        uint32_t header_size;   /* size of boot image header in bytes */
+       u32 secondary_kernel_size;
+       u32 secondary_kernel_addr;
} __attribute__((packed));
  1. Modification of the Linux kernel

You need to add the CONFIG_OPTEE configuration in thedefconfig of our target.

--- a/arch/arm/configs/imx_v7_android_defconfig
+++ b/arch/arm/configs/imx_v7_android_defconfig
@@ -4789,7 +4789,7 @@ CONFIG_TEE=y
#
# TEE drivers
#
-# CONFIG_OPTEE is not set
+CONFIG_OPTEE=y

#
# Firmware Drivers

The reserved-memory node is dynamically created by OPTEE-OS based on its configuration if it is not already present in the device tree of our target. We have chosen to explicitly write it in our device tree to have control over the memory reserved by OPTEE-OS.

In the optee_ node, 0x8c000000 corresponds to the RAM address where OPTEE-OS reserves 0x04000000 of memory.

--- a/arch/arm/boot/dts/imx6qdl-horanet.dtsi
+++ b/arch/arm/boot/dts/imx6qdl-horanet.dtsi
@@ -52,6 +48,24 @@
     };
};+/  {
+   reserved-memory {
+          #address-cells = <1>;
+           #size-cells = <1>;
+          ranges;
+          optee_ {
+                  reg = <0x8c000000 0x04000000>;
+                  no-map;
+          };
+   };
+};

2. Project Compilation:

For compiling OP-TEE for the PM098, it is necessary to build OPTEE-OS. Note that the following operations are performed from the root of the imx-optee-os project.

# Define the destination directory for compiled objects
O="${O:-$ANDROID_PRODUCT_OUT/optee/arm-plat-imx}"
# Build OPTEE-OS
make -j4 PLATFORM=imx PLATFORM_FLAVOR=mx6dlpm098 O=${O}
# Copy the firmware to $ANDROID_PRODUCT_OUT
${CROSS_COMPILE}objcopy -O binary ${O}/core/tee.elf $ANDROID_PRODUCT_OUT/tee.bin
# Create an Android image with mkimage
imx_load_addr=$(cat ${O}/core/tee-init_load_addr.txt) && mkimage -A arm -O linux -C none -a $imx_load_addr -e $imx_load_addr -d $ANDROID_PRODUCT_OUT/tee.bin

Secondly, you just need to initiate an Android build from within the Android Docker.

source build/envsetup.sh
lunch pm098-user
m 2>&1

3. Deployment on the Board:

  • Instructions for flashing an SD card for PM098:

To flash an SD card, we use the script provided by NXP fsl-sdcard-partition.sh.

We also need the following images: u-boot-imx6dl.imx,boot.img, recovery-imx6dl.img, dtbo-imx6dl.img, vendor.img,vbmeta-imx6dl.img, system.img, and partition-table-14GB.img.

#We specify our target with -f imx6dl, we have a 16GB SD card so -c 14, and /dev/sdX is the location of the SD card.
sudo ./fsl-sdcard-partition.sh -f imx6dl -c 14 /dev/sdX && sync

4. Tests and Validation:

Execution of Basic Tests:

Functionality tests of OP-TEE on the PM098

  • Invoking Tee via an Android application: to verify if the keystore indeed uses OPTEE-OS, you can use a Java application and implement a test with the isHardwareBacked() function.

Troubleshooting

  • From U-Boot, launching Tee, if nothing happens:

  • OP-TEE might not be loaded into RAM.

  • OP-TEE might be loaded to the wrong address.

  • The OP-TEE image might be invalid.

  • If Tee starts and nothing happens afterwards:

  • The Linux kernel might not be loaded into RAM.

  • The kernel might be loaded to the wrong address.

  • OP-TEE might be trying to load the kernel to the wrong address.

Resources:

With special thanks to : François-Xavier Deltombe, Thai ha Nguyen And Andgel Halley.


메타데이터
post_id
dedffe504cb3
slug
porting-guide-for-op-tee-with-android-on-imx6-custom-board-dedffe504cb3
url
https://medium.com/@elmoadineanass.horanet/porting-guide-for-op-tee-with-android-on-imx6-custom-board-dedffe504cb3
canonical_url
https://medium.com/@elmoadineanass.horanet/porting-guide-for-op-tee-with-android-on-imx6-custom-board-dedffe504cb3
author_url
https://medium.com/@elmoadineanass.horanet
status
ok
fetched_at
2026-06-29 02:33:43