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.
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.
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.
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.
Change #1 – Host section changes
The below changes need to be done to the host section code.
host/Makefile
– Modify the value of theBINARY
variable. Let us call itoptee_example_ke_hello_world
host/main.c
– Change the name of the include file namedhello_world_ta.h
toke_hello_world_ta.h
– we will see this header file soon.host/main.c
– Change the value of theUUID
variable toTA_KE_HELLO_WORLD_UUID
– we will define this macro soon.host/main.c
– In theTEE_InvokeCommand(...)
command, change the command value toTA_KE_HELLO_WORLD_INC_VALUE
.
The below images summarize the changes to be done.
Change #2 – Trusted section changes
The below changes need to be done for the trusted section code.
ta/Android.mk
– Replace the value of thelocal_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 toke_hello_world_ta.h
ta/user_ta_header_defines.h
– Change the value of the macro namedTA_UUID
toTA_KE_HELLO_WORLD_UUID
ta/user_ta_header_defines.h
– Change the occurences of hello_world toke_hello_world
in theTA_CURRENT_TA_EXT_PROPERTIES
macrota/sub.mk
– Replace the file in thesrcs-y
variable toke_hello_world_ta.c
ta/Makefile
– Paste the version 4 UUID that we pasted in theta/Android.mk
into the value of theBINARY
variableta/ke_hello_world_ta.c
– Rename thehello_world_ta.c
toke_hello_world_ta.c
and inside it, change the name of the include file toke_hello_world_ta.h
ta/ke_hello_world_ta.c
– In the function namedTA_InvokeCommandEntryPoint(...)
, change the switch cases to reflect macros withKE_HELLO_WORLD_*
instead ofHELLO_WORLD_*
ta/include/ke_hello_world_ta.h
– Rename thehello_world_ta.h
toke_hello_world_ta.h
and inside it, change the defined macro toTA_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 useKE_HELLO_WORLD_*
instead ofHELLO_WORLD_*
.
The below images summarize the changes to be done.
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 tooptee_example_ke_hello_world
Android.mk
– Change the value of theLOCAL_MODULE
variable tooptee_example_ke_hello_world
The below images summarize the changes to be done.
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.
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!
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
Hi Ori – you don’t need to rebuild the OS every time. It is possible to build and add user TAs to the filesystem without necessarily building the entire OS.
Let me see if I can cover that in my next post.
Check out the OPTEE examples here: https://github.com/linaro-swg/optee_examples – you don’t need to rebuild the OS in order to build these.