OP-TEE: Part 4 – Writing Your First Trusted Application

In the previous three posts, we talked about the basics of Trusted Execution Environment (TEE), various terms that are used in this context and what they mean. Also, we talked more about the OP-TEE software offering, its various components and finally talked a little about GlobalPlatform and why it is relevant in the context of TEEs. Finally, we learnt how to build our first images for two popular platforms – QEMU and Raspberry Pi 3.

In case you missed these posts, you can find them here.

In this post, we will talk about what a typical trusted application looks like and how we can build one ourselves. During the course of this post, we will also take a look at some examples that Linaro (the maintainers of OP-TEE) provide us with.

You can click on any of the below items to navigate this post.

Example applications by Linaro

In order to help beginners kickstart their trusted application development, the good folks at Linaro provide a few examples that one can refer to. These examples are not a part of the official OP-TEE repositories that we saw in part 2 – hence we did not discuss them there.

These examples are located here. What’s more? These examples are already a part of the image that you built in part 3.

These are located inside the /usr/bin folder in both the QEMU as well as RPi3 images

Once the image has booted up, one can simply execute these applications just like we executed the OP-TEE test suite named xtest in part 3.

Let us now understand the anatomy of a typical trusted application using the hello_world example as a reference.

Anatomy of a basic trusted application

This is how the hello_world sample application looks like.

A tree of the contents inside the hello_world trusted application

This example can be broken down into three main entities to understand the structure more clearly – host section (host/ folder), trusted section (ta/ folder) and the high-level build helpers.

Host section

The host section is the piece of code that runs in the rich world. In the case of OP-TEE, this is the linux world. One of the outputs of the build process is the libteec.so which has to be linked to the host section. The overall flow of the host section looks something like this.

Trusted section

The trusted section is the piece of code that runs in the secure world. The secure world OS is OP-TEE itself. The overall structure of the trusted section looks something like this. Notice how the various functions are actually callbacks for events that the host section started like the context initialization and finalization, session opening and closure, etc.

Trusted section functions mapped to what they do

Notice that the functions in the host section start with TEEC_ and those in the trusted section start with TA_.

High-level build helpers

The build helpers are the Makefiles and the CMake-related files that will help us to build the trusted application correctly. One of the best features of the examples that Linaro provides us is that one can simply add their own sample applications to this repo and if the folder structure is maintained correctly, your application will be built as it was a default example!

Do explore the build helper files to see the implementation for yourself!

Your first trusted application – based on hello_world

We will write a modified version of the hello_world application provided by Linaro. More importantly, we will make it completely separate from the provided examples rather than modifying the source in-tree. This will also help us understand better what components are important for us.

Now would be a good time to check out part 3 if you already haven’t! Some of the work in the below sections borrows from the knowledge gained and builds done there.

According to the build/common.mk which talks about how to build the target named buildroot, the path for finding the source code of the optee_examples repo is $(ROOT)/optee_examples. We will make our new application inside that location. Any new changes will be picked up by the build system when we do the next build of the target or the entire image for that matter.

Now, we will simply make a duplicate of the hello_world example and call it ke_hello_world. And one-by-one, we will make the changes as shown below. These changes are categorised in the same way we analysed the application – host section, trusted section and high-level build helpers. The file paths are relative to the ke_hello_world/ folder.

The folder structure inside optee_examples repo

Change #1 – Host section changes

The below changes need to be done to the host section code.

  • host/Makefile – Modify the value of the BINARY variable. Let us call it optee_example_ke_hello_world
  • host/main.c – Change the name of the include file named hello_world_ta.h to ke_hello_world_ta.h – we will see this header file soon.
  • host/main.c – Change the value of the UUID variable to TA_KE_HELLO_WORLD_UUID – we will define this macro soon.
  • host/main.c – In the TEE_InvokeCommand(...) command, change the command value to TA_KE_HELLO_WORLD_INC_VALUE.

The below images summarize the changes to be done.

host/Makefile
host/main.c
host/main.c
host/main.c

Change #2 – Trusted section changes

The below changes need to be done for the trusted section code.

  • ta/Android.mk – Replace the value of the local_module variable with a new version 4 UUID generated from a trusted website like this one.
  • ta/user_ta_header_defines.h – Change the include file to ke_hello_world_ta.h
  • ta/user_ta_header_defines.h – Change the value of the macro named TA_UUID to TA_KE_HELLO_WORLD_UUID
  • ta/user_ta_header_defines.h – Change the occurences of hello_world to ke_hello_world in the TA_CURRENT_TA_EXT_PROPERTIES macro
  • ta/sub.mk – Replace the file in the srcs-y variable to ke_hello_world_ta.c
  • ta/Makefile – Paste the version 4 UUID that we pasted in the ta/Android.mk into the value of the BINARY variable
  • ta/ke_hello_world_ta.c – Rename the hello_world_ta.c to ke_hello_world_ta.c and inside it, change the name of the include file to ke_hello_world_ta.h
  • ta/ke_hello_world_ta.c – In the function named TA_InvokeCommandEntryPoint(...), change the switch cases to reflect macros with KE_HELLO_WORLD_* instead of HELLO_WORLD_*
  • ta/include/ke_hello_world_ta.h – Rename the hello_world_ta.h to ke_hello_world_ta.h and inside it, change the defined macro to TA_KE_HELLO_WORLD_UUID. Also, change the value of this macro to the version 4 UUID that we used above.
  • ta/include/ke_hello_world_ta.h – Change the values of the function ID macros to use KE_HELLO_WORLD_* instead of HELLO_WORLD_*.

The below images summarize the changes to be done.

ta/Android.mk
ta/user_ta_header_defines.h
ta/user_ta_header_defines.h
ta/user_ta_header_defines.s
ta/sub.mk
ta/Makefile
ta/ke_hello_world_ta.c
ta/ke_hello_world_ta.c
ta/include/ke_hello_world_ta.h
ta/ke_hello_world_ta.h

Change #3 – High-level build helper changes

The below changes need to be done for the build helpers.

  • CMakeLists.txt – Change the name of the CMake project to optee_example_ke_hello_world
  • Android.mk – Change the value of the LOCAL_MODULE variable to optee_example_ke_hello_world

The below images summarize the changes to be done.

CMakeLists.txt
Android.mk

Building your first application

Like we saw before, we only needed to add code to the $(ROOT)/optee_examples in order for it to be picked up by our build system. What is our build system, you ask? Check out part 3 where we created OP-TEE images for QEMU and Raspberry Pi 3 platforms.

All that is left for us is to trigger the build. We simply go back to the build directory and run a make with the target set to buildroot. If your platform is QEMU, you could directly run the QEMU engine. The changed filesystem would be detected and the added example would be built and installed before QEMU is started.

$ cd build
// If doing this on QEMU and you wish to run directly
$ make run
// If doing this on any platform and you only wish to build
$ make buildroot

If you have booted up your platform with the modified image, you can now see that the list of example applications now contains the new application named optee_example_ke_hello_world in the /usr/bin directory.

Our custom application is now in our platform’s image!

Conclusion

In this post, we saw what a typical trusted application source tree contains using the hello_world example as our reference. We then built on this knowledge to create our own custom application that is then built and installed into our OP-TEE image made for a particular platform.

This work in this post relies heavily on the knowledge gained and methods used in part 3 – do check it out first.

See you in the next post!

4 thoughts on “OP-TEE: Part 4 – Writing Your First Trusted Application

  1. Hi! Excellent explanation about op-tee. Thank you.
    Is there an option to build my application without the need to wait a long time? It does not make sense to build optee all over again for each iteration. If so, how can I load the application to the secure space after it being built?
    Thanks,
    Ori

      1. I have the same issue. After using the ‘make buildroot’ command, I don’t know how to load the .ta file into the file system. I can only rebuild the OS each time and then burn it into the SD card. Looking forward to your subsequent blog.

        Thank you very much for this series of tutorials on OP-TEE. I have learned a lot from them.

      2. I have the same issue. After using the ‘make buildroot’ command, I don’t know how to load the UUID.ta file into the file system. I can only rebuild OS each time and then burn it into the SD card. Looking forward to your subsequent blog.

        Thank you very much for this series of tutorials on OP-TEE. I have learned a lot from them.

Leave a Reply