Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                

Oxford Dic

Download as pdf or txt
Download as pdf or txt
You are on page 1of 95

OP-TEE Documentation

Linaro

Nov 13, 2018


Contents

1 Getting started 3
1.1 About OP-TEE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.2 Coding standards . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.3 Contribute . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.4 Contact . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
1.5 Disclosure policy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
1.6 License headers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
1.7 Platforms supported . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
1.8 Presentations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17

2 Architecture documentation 19
2.1 Core . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
2.2 Cryptographic implementation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
2.3 File structure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
2.4 GlobalPlatform API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
2.5 Libraries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
2.6 Porting guidelines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
2.7 Secure boot . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
2.8 Secure storage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
2.9 Trusted Applications . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64

3 Build and run 67


3.1 General build information . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
3.2 Prerequisites . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
3.3 OP-TEE gits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
3.4 Device specific build information . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
3.5 Linux kernel TEE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
3.6 AOSP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81

4 Debugging techniques 83
4.1 Abort dumps / call stack . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
4.2 Benchmark framework . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
4.3 Gprof . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
4.4 GDB . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
4.5 JTAG . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90

5 FAQ - Frequently Asked Questions 91

i
5.1 Frequently Asked Questions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91

ii
OP-TEE Documentation

OP-TEE is yada yada

Contents 1
OP-TEE Documentation

2 Contents
CHAPTER 1

Getting started

This is general information about OP-TEE.

1.1 About OP-TEE

OP-TEE is a Trusted Execution Environment (TEE) designed as companion to a non-secure Linux kernel running on
Arm; Cortex-A cores using the TrustZone technology. OP-TEE meets the TEE System Architecture and provides the
TEE Internal Core API v1.1.x to Trusted Applications and the TEE Client API v1.0, all as defined by the GlobalPlat-
form 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 mech-
anism. 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.

1.1.1 OP-TEE components

The OP-TEE solution is divided in various components:


• A secure privileged layer, executing at Arm secure PL-1 (v7-A) or EL-1 (v8-A) level.
• A set of secure user space libraries designed for Trusted Applications needs.

3
OP-TEE Documentation

• A Linux kernel TEE framework and driver (merged since v4.12).


• A Linux user space library designed upon the GlobalPlatform TEE Client API specifications.
• A Linux user space supplicant daemon responsible for remote services expected by the TEE OS.
• A test suite (xtest), for doing regression testing and testing the consistency of the API implementations.
• An example git containing a couple of simple host- and TA-examples.
• And some build scripts, debugging tools to ease its integration and the development of Trusted Applications and
secure services.
These components are available from several git repositories. The main ones are build, optee_os, optee_client,
optee_test, optee_examples and the Linux kernel TEE.

1.1.2 History

OP-TEE was initially developed by ST-Ericsson (and later on by STMicroelectronics), but this was before OP-TEE got
the name “OP-TEE” and was turned into an open source project. Back then it was a closed source and a proprietary
TEE solution. In 2013, ST-Ericsson obtained GlobalPlatform’s compliance qualification with this implementation,
proving that the APIs were behaving as expected in the GlobalPlatform specifications.
Later on the same year (2013) Linaro was about to form Security Working Group (SWG) and one of the initial key
tasks for SWG was to work on an open source TEE solution. After talking to various TEE vendors Linaro ended up
working with STMicroelectronics TEE solution. But before being able to open source it there was a need to replace
some proprietary components with open source components. For a couple of months Linaro/SWG together with
engineers from STMicroelectronics re-wrote major parts (crypto library, secure monitor, build system etc), cleaned up
the solution by enforcing Coding standards, running checkpatch etc.
June 12 2014 was the day when OP-TEE was “born” as an open source project. At that day the OP-TEE team pushed
the first commit to GitHub. A bit after this Linaro also made a press release about this. That press release contains a
bit more information. At the first year as an open source project it was owned by STMicroelectronics but maintained
by Linaro and STMicroelectronics. In 2015 there was an ownership transfer of OP-TEE from STMicroelectronics
to Linaro and since then it has been Linaro who is the primary owner and maintainer of the project. But for the
maintenance part, it has become a shared responsibility between Linaro, Linaro members and other companies who
are using OP-TEE.

1.2 Coding standards

In this project we are trying to adhere to the same coding convention as used in the Linux kernel (see CodingStyle).
We achieve this by running checkpatch from Linux kernel. However there are a few exceptions that we had to make
since the code also follows GlobalPlatform standards. The exceptions are as follows:
• CamelCase for GlobalPlatform types are allowed.
• And we also exclude checking third party code that we might use in this project, such as LibTomCrypt, MPA,
newlib (not in this particular git, but those are also part of the complete TEE solution, see repository-structure).
The reason for excluding and not fixing third party code is because we would probably deviate too much from
upstream and therefore it would be hard to rebase against those projects later on and we don’t expect that
it is easy to convince other software projects to change coding style. Automatic variables should always be
initialized. Mixed declarations and statements are allowed, and may be used to avoid assigning useless values.
Please leave one blank line before and after such declarations.
Regarding the checkpatch tool, it is not included directly into this project. Please use checkpatch.pl from the Linux
kernel git in combination with the local checkpatch script.

4 Chapter 1. Getting started


OP-TEE Documentation

1.3 Contribute

Contributions to OP-TEE are managed by the OP-TEE core team and anyone can contribute to OP-TEE as long as it is
understood that it will require a sign-off. The sign-off is a simple line at the end of the explanation for the patch, which
certifies that you wrote it or otherwise have the right to pass it on as an open-source patch (see below). You thereby
assure that you have read and are following the rules stated in the Developer Certificate of Origin as
stated below.

1.3.1 Developer Certificate of Origin

Developer Certificate of Origin


Version 1.1

Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
660 York Street, Suite 102,
San Francisco, CA 94110 USA

Everyone is permitted to copy and distribute verbatim copies of this


license document, but changing it is not allowed.

Developer's Certificate of Origin 1.1

By making a contribution to this project, I certify that:

(a) The contribution was created in whole or in part by me and I


have the right to submit it under the open source license
indicated in the file; or

(b) The contribution is based upon previous work that, to the best
of my knowledge, is covered under an appropriate open source
license and I have the right under that license to submit that
work with modifications, whether created in whole or in part
by me, under the same open source license (unless I am
permitted to submit under a different license), as indicated
in the file; or

(c) The contribution was provided directly to me by some other


person who certified (a), (b) or (c) and I have not modified
it.

(d) I understand and agree that this project and the contribution
are public and that a record of the contribution (including all
personal information I submit with it, including my sign-off) is
maintained indefinitely and may be redistributed consistent with
this project or the open source license(s) involved.

We have borrowed this procedure from the Linux kernel project to improve tracking of who did what, and for legal
reasons.
To sign-off a patch, just add a line in the commit message saying:

Signed-off-by: Random J Developer <random@developer.example.org>

using your real name (sorry, no pseudonyms or anonymous contributions.)

1.3. Contribute 5
OP-TEE Documentation

1.3.2 GitHub

This section describes how to use GitHub for OP-TEE development and contributions.

Setting up an account

You do not need to own a GitHub account in order to clone a repository. But if you want to contribute, you need to
create an account at GitHub first. Note that a free plan is sufficient to collaborate.
SSH is recommended to access your GitHub repositories securely and without supplying your username and password
each time you pull or push something. To configure SSH for GitHub, please refer to Connecting to GitHub with SSH.

Forking

Only owners of the OP-TEE projects have write permission to the Git repositories of those projects. Contributors
should fork OP-TEE/*.git and/or linaro-swg/*.git into their own account, then work on this forked repos-
itory. The complete documentation about forking can be found at fork a repo.
Note that the fork only has to be performed once.

Creating pull requests

When you want to submit a patch to the OP-TEE project, you are supposed to create a pull request to the git where
you forked your git from. How that is done using GitHub is explained at the GitHub pull request page.

Commit messages

• The subject line should explain what the patch does as precisely as possible. It is usually prefixed with key-
words indicating which part of the code is affected, but not always. Avoid lines longer than 80 characters.
• The commit description should give more details on what is changed, and explain why it is done. Indication
on how to enable and use some particular feature can be useful, too. Try to limit line length to 72 characters,
except when pasting some error message (compiler diagnostic etc.). Long lines are allowed to accommodate
URLs, too (preferably use URLs in a Fixes: or Link: tag).
• The commit message must end with a blank line followed by some tags, including your Signed-off-by:
tag. By applying such a tag to your commit, you are effectively declaring that your contribution follows the
terms stated by Developer Certificate of Origin (in the DCO section there is also a complete example).
• Other tags may be used, such as:
– Tested-by: Test R <test@r.com>
– Acked-by: Acke R <acke@r.com>
– Suggested-by: Suggeste R <suggeste@r.com>
– Reported-by: Reporte R <reporte@r.com>
• When citing a previous commit, whether it is in the text body or in a Fixes: tag, always use the format shown
above (12 hexadecimal digits prefix of the commit SHA1, followed by the commit subject in double quotes and
parentheses).

6 Chapter 1. Getting started


OP-TEE Documentation

Review feedback

It’s very likely that you will get review comments from other OP-TEE users asking you to fix certain things etc. When
fixing review comments, do:
• Add patches on top of your existing branch. Do not squash and force push when fixing review comments.
• When all comments have been addressed, just write a simple messages in the comments field saying something
like “All comments have been addressed”. By doing so you will notify the maintainers that the fix might be
ready for review again.

Finalizing your contribution

Once you and reviewers have agreed on the patch set, which is when all the people who have commented on the pull
request have given their Acked-by: or Reviewed-by:, you need to consolidate your commits:
Use git rebase -i to squash the fixup commits (if any) into the initial ones. For instance, suppose the git log
--oneline for you contribution looks as follows when the review process ends:

<commit1> Do something
<commit2> Do something else
<commit3> [Review] Do something
<commit4> [Review] Do something

Then you would do:

$ git rebase -i <commit1>^

Edit the commit script so it looks like so:

pick <commit1> Do something


squash <commit3> [Review] Do something
squash <commit4> [Review] Do something
pick <commit2> Do something else

Add the proper tags (Acked-by: ..., Reviewed-by: ..., Tested-by: ...) to the commit mes-
sage(s), as provided by the people who reviewed and/or tested the patches.
Once rebase -i is done, you need to force-push (-f) to your GitHub branch in order to update the pull request
page.

$ git push -f

At this point, it is the project maintainer’s job to apply your patches to the master branch.

1.4 Contact

1.4.1 GitHub

Our preference is to use GitHub for communication. The reason for that is that it is an open source project, so there
should be no real reason to hide discussions from other people. GitHub also makes it possible for anyone to chime
in into discussion etc. So besides sending patches as pull requests on GitHub we also encourage people to use the
“issues” to report bugs, give suggestions, ask questions etc.

1.4. Contact 7
OP-TEE Documentation

Please try to use the “issues” in the relevant git. I.e., if you want to discuss something related to optee_client, then use
“issues” in optee_client and so on. If you have a general question etc about OP-TEE that doesn’t really belong to a
specific git, then please use issues in optee_os in that case.

1.4.2 Email

You can reach the core team core team by sending an email to <op-tee[at]linaro[dot]org>. However note that the team
consist of engineers from different companies, i.e, it not just Linaro engineers on that email address.
From time to time we are also using the “TEE-dev” mailinglist <tee-dev[at]lists[dot]linaro[dot]org>. It has mostly
been used when we have discussed and sent patches related to the TEE framework in Linux kernel.
For pure Linux kernel patches, please use the appropriate Linux kernel mailinglist, basically run the
get_maintainer.pl script to know where to send your patches.

$ cd <linux-kernel>
$ ./scripts/get_maintainer.pl drivers/tee/

1.4.3 IRC

Some of the OP-TEE developers can be reached at Freenode (chat.freenode.net) at channel


#linaro-security. Having that said, the activity there is a bit limited, so it is probably not the best place to
discuss OP-TEE.

1.4.4 Vulnerability reporting

Please send an email to the address mentioned above (not to TEE-dev). Don’t include any details at this point, just
mention that you’d like to report a security issue. An engineer from the core OP-TEE team will get back to you for
further communication and discussions about your findings. Please also read the Disclosure policy page and especially
the Reporting issues section, so you are aware of the rules we are following.

1.5 Disclosure policy

When a vulnerability has been reported (see Vulnerability reporting) to the core team, it is up to them to implement
mitigations and fixes as well as report back to stakeholders in a responsible way. This page describes the responsible
disclosure policy that applies to the OP-TEE project.

Note: The “core team” in Linaro (who owns the OP-TEE project) consists of engineers directly employed by Linaro
as well as engineers employed by companies who are members of Linaro.

1.5.1 Rules

To have some kind of ground to stand on we have defined a set of rules and conditions that applies both when it comes
to being a taker of information as well as being reporter of security issues. It should be noted that it is hard to write
rules that you can follow to 100%, since depending on the type of security issues being dealt with it might or might
not be possible for the core team and Linaro to re-distribute the information right away.

8 Chapter 1. Getting started


OP-TEE Documentation

An example of when we couldn’t follow our rules and disclosure policy was when we got informed (under NDA)
about the Spectre and Meltdown issues (this was before it was public knowledge). That was considered so sensitive
that we weren’t even allowed to share or discuss this outside Linaro (employees).
But in general, we strive and try to do our best to follow the rules etc that have been defined on this particular page.

Receiving information

The one receiving information about and fixes related to OP-TEE security vulnerabilities must follow these rules:
1. The receiver of vulnerability information and/or security fixes shared by the core team and Linaro are not
allowed to share, re-distribute or otherwise spread knowledge about the issues and security fixes outside their
own company until the disclosure deadline has passed and the information is publicly available.
1.1. If the receiver still wants to share it with other people/companies he must first get approval from
the core team and Linaro to do so.

Reporting issues

The one reporting security vulnerabilities to the core team and Linaro are asked to do it under the conditions mentioned
below. It might seem like a long list, but we hope that it won’t scare people away from reporting issues. It’s mostly
common sense and also aims to rule out questions that otherwise might come to mind. In short it by default gives the
core team and Linaro the power to decide what to do with the reported issue if nothing else has been agreed between
them and the reporter.
1. If nothing else has been agreed between the reporter and the core team and Linaro, then the rules and information
as stated on this page applies.
1.1. This means that the core team and Linaro will re-distribute the information to the stakeholders
according to the plan described further down here.
1.2. This also means that patches etc will be submitted to the upstream project based on the proposed
disclosure day that will be given to the reporter after initial investigation.
2. By default, the information about the reported issue(s) will be shared within the core team (see the note about
the core team at the beginning of the page). If you as a reporter aren’t OK with that, then you must inform us
about that when reporting the issue.
3. By default, the core team and Linaro decides whether there should be a CVE created or not. If the reporter insist
on having a CVE created, then this should be expressed when doing the reporting.
4. The core team and Linaro have the rights to involve other experts to help us with mitigations and patches. If you
as a reporter aren’t OK with that, then you must inform us about that when reporting the issue.
5. Reporting security issues under NDA should be seen as a last resort thing. If/when that happens, then we will
come up with a mutual agreement on a disclosure plan.
6. It is appreciated if the reporter have estimated some initial severity scoring as described further down on this
page. This is mainly to get an indication whether we share the same view about the severity or not.

1.5.2 Trusted Stakeholders

The core team keeps track of companies and maintainers who are considered as trustworthy OP-TEE users. This is a
vetted list and people from companies can only be added to that list after first talking to the core team. In short what
is required to be added to that list is:
• A justification of why you need to know about security issues and should have security fixes before they are
going public.

1.5. Disclosure policy 9


OP-TEE Documentation

• A company email address (we do not accept gmail, yahoo and similar addresses).
• You accept our disclosure policy rules (as described at here).

Note: The core team and Linaro have the rights to deny anyone to be on this list. We also have the rights to remove
people on the list if there should be a reason to do so.

1.5.3 Disclosure deadline

By default we are following the industry standard with 90-days disclosure deadline. This applies both when we find
security issues that needs to be fixed in the upstream project, as well as when we are the ones reporting issues found in
vendor trees (forks of OP-TEE). The reason for 90-days is to give companies enough time to patch and deploy updated
software to their devices.
Likewise we are going to propose a 90-days disclosure deadline for issues that are being reported to us, that we are
supposed to fix.
However, for issues that falls in the severity category ‘low’ and in some cases ‘medium’ (see Severity table below),
we have the rights to decide whether to upstream patches as soon as they are ready. If the reporter or the some of the
trustworthy stakeholders knowing about the security issue disagrees, then they must inform the core team and Linaro
about it as soon as possible and then we will come up with an alternate plan.

0day exploits

This is a previously unknown and unpatched vulnerability which is been used actively in the wild. As a consequence
of that we believe that 0day exploits require a much more urgent action. I.e., a fix or some kind of mitigation that
limits the damage needs to be created as soon as possible. Our target for such fixes and mitigations are within 14 days
from the day when we learned about the 0day exploit (full weeks, including weekends).

1.5.4 Issue process

For regular security issues (non 0day) we follow the flow chart below. Note that the orange path is when it is a low
(and maybe medium) severity issue we are dealing with, so that is a special case with an alternate path.

10 Chapter 1. Getting started


OP-TEE Documentation

Issue reported
Day 1
90 day counter starts

Create mitigations

Patch ready

Check if patch should go upstream directly

Severity >= Low/Medium? Day 90 Share fixes

Yes No

Update CVE
Create CVE Upstream Fixes Update security advisories Inform stakeholders
(if created)

For 0day exploits we follow this flow chart:

0day issue reported


Day 1
14 day counter starts

Create mitigations

Patch ready Severity >= Medium?

Yes

Day 14 Share fixes Create CVE

Update security advisories Upstream Fixes Update CVE Inform stakeholders

1.5.5 Recognition

Once the disclosure deadline has passed and information and mitigations will go public we want to give credits to the
ones finding, reporting and fixing the issues. Typically that is given in two ways. One is in textual form at our security
advisories page and the other way is directly in patches applied on the upstream project in questions.
For patches we prefer having a real physical person being mentioned (see Reported-by and Suggested-by in the example
below), but also a company name or group could be used if it was a joint effort finding the security issue or if the person
finding the issue prefer not being mentioned directly for some reason. A patch would typically look like this:

1.5. Disclosure policy 11


OP-TEE Documentation

core: fixes privilege escalation

By doing X, one was able to exploit a privilege escalation


vulnerability. By changing Y this is no longer a security
issue.

Fixes CVE-20xx-YYYY

Signed-off-by: John Doe <john.doe@foobar.org>


Reviewed-by: Richard Roe <richard.roe@foobar.org>
Reported-by: Jane Doe <jane.doe@notable-hackers.com>
Suggested-by: Jane Doe <jane.doe@notable-hackers.com>

1.5.6 CVE

If there is a need to request a CVE identifier, then the Distributed Weakness Filing Project should be used. At that
page you will find the current link to the DWF project.

1.5.7 Severity scoring

When deciding the severity for a vulnerability we start out by doing a scoring similar to the DREAD scoring system,
but tweaked for OP-TEE purposes. This mainly serves as a guide to get some kind of indication of the severity. The
final severity is decided on case by case basis.

Note: A DREAD score can change over time. The initial analysis could give a certain score, but later on when a
vulnerability is well known and exploits are readily available the score will be different (ususally more severe).

Damage Potential
This should give an answer to much damage is caused if the vulnerability is exploited.

ScoreDamange potential
0 No damage.
1 Normal World User space is compromised and could leak sensitive data.
1 Denial of service from Normal World.
2 Normal World Linux kernel space is compromised and could leak sensitive data.
5 TEE Trusted Application compromised and could leak data only accessible by the Trusted Application.
7 TEE core (kernel space) compromised and leaking trivial information.
9 TEE core (kernel space) compromised and leaking sensitive information.
10 TEE fully compromised and the attacker in full control.

Reproducibility
This describes how easy (or hard) it is to reproduce the attack.

12 Chapter 1. Getting started


OP-TEE Documentation

ScoreReproducibility
0 Not reproducible.
1 No proven attack exists.
1 The attack is very difficult to reproduce, even with knowledge of the security hole (requires special lab
equipment for example)
2 Proof of concept attack exists, but only works in a specially crafted, non-standard configuration.
4 The attack can be reproduced, but only with tooling / software / knowledge that has not been made public
(typically the one finding the security issue have created a tool, which hasn’t been released yet).
9 The attack can be reproduced, but only with tooling (JTAG, ChipWhisperer etc) / software / knowledge that
is readily available to anyone.
10 The attack can be reproduced every time by a novice user without any need for extra tools.

Exploitability
This should answer how easy it is to launch an attack.

ScoreExploitability
0 Not exploitable.
1 Theoretically exploitable (even with knowledge, there seems to be no viable path for a real exploit).
7 Only authenticated user(s) can make the attack.
8 A skilled programmer with in-depth knowledge could make the attack.
9 A novice programmer could make the attack in a short time.
10 A novice user could make the attack in a short time (exploits readily available on internet and/or integrated in
known hacker/pen-testing tools).

Affected Users
This should give a rough answer to how many people are affected by a successful attack.

ScoreAffected Users
0 No users affected.
1 All users, running a debug/developer configuration.
1 A single user.
10 All users, running a release configuration (key customers).

Discoverability
This should answer how easy it is to discover the threat.

ScoreDiscoverability
0 Not discoverable.
1 The vulnerability would require other successful exploits in order to be able to discover this bug.
2 The bug is obscure, and it is unlikely that users will work out damage potential.
5 Information explaining the attack exists, but is only shared with a small group of people (and it is not intended
to be shared publicly in a foreseeable time or until mitigations has been merged).
10 Published information explains the attack.

Severity table

Based on the DREAD score, we get some kind of indication of the severity. In the table below you can see how we
are mapping things between a DREAD score and severity.

1.5. Disclosure policy 13


OP-TEE Documentation

Severity
Score CVE? Comment
No [0, 1) No This is not considered as a security issue, it’s a regular bug.
risk CVE
cre-
ated.
Low[1, 4) No This could be seen as a security issue, but could probably be treated as general bug.
CVE
cre-
ated.
Medium
[4, 7) Depends.
This is a security issue, but on the lower side of the score it might be treated as a bug.
For the higher end it is likely that a CVE will be created.
High[7, 9) CVEIt is definitely a security issue.
cre-
ated.
Critical
[9, 10] CVEIt is definitely a security issue, very urgent to start working with mitigations etc.
cre-
ated.

Example

To have a better understanding how this would look like in practice, let’s show a couple of examples.
Example 1 - Spectre v2 - Branch Target Injection (CVE-2017-5715)
Note that this example should be seed from a TrustZone / TEE point of view.
• D: What damage could it cause?
– TEE leaking sensitive data, i.e., 9.
• R: Easy to reproduce?
– No proven attack exists on TrustZone/TEE software, i.e, 1.
• E: Easy to launch the attack?
– Theoretically exploitable, i.e., 1
• A: How many users would be affected by a successful attack?
– All users, i.e., 10.
• D: How easy is it to discover this issue?
– It’s public information, i.e., 10.
This gives the score: (9 + 1 + 1 + 10 + 10) / 5 = 6.2 which indicates that this would a bit on the higher end of medium
severity.
Example 2 - Bellcore attack on OP-TEE (CVE-2017-1000412)
• D: What damage could it cause?
– TEE leaking sensitive data (private key used to sign and verify Trusted Applications), i.e., 9.
• R: Easy to reproduce?
– With a ChipWhisperer (readily available) it would be possible for a somewhat skilled engineer to do
this on their own on a device running OP-TEE, i.e., 9.
• E: Easy to launch the attack?

14 Chapter 1. Getting started


OP-TEE Documentation

– A skilled engineer with in-depth knowledge could make the attack, i.e., 8.
• A: How many users would be affected by a successful attack?
– All users, i.e., 10.
• D: How easy is it to discover this issue?
– It’s public information, i.e., 10.
This gives the score: (9 + 9 + 8 + 10 + 10) / 5 = 9.2 which indicates that this would be a critical issue.

1.6 License headers

This document defines the format of the copyright and license headers in OP-TEE source files. Such headers shall
comply with the rules described here, which are compatible with the rules adopted by the Linux kernel community.

1.6.1 New source files

• (Rule 1.1) Shall contain exactly one SPDX license identifier, which can express a single or multiple licenses
(refer to SPDX for syntax details)
• (Rule 1.2) The SPDX license identifier shall be added as a comment line. It shall be the first possible line in the
file which can contain a comment. The comment style shall depend on the file type:
• (Rule 1.2.1) C source: // SPDX-License-Identifier: <expression>
• (Rule 1.2.2) C header: /* SPDX-License-Identifier: <expression> */
• (Rule 1.2.3) Assembly: /* SPDX-License-Identifier: <expression> */
• (Rule 1.2.4) Python, shell: # SPDX-License-Identifier: <expression>
• (Rule 1.3) Shall contain at least one copyright line
• (Rule 1.4) Shall not contain the mention ‘All rights reserved’
• (Rule 1.5) Shall not contain any license notice other than the SPDX license identifier
Note that files imported from external projects are not new files. The rules for pre-existing files (below) apply.

1.6.2 Pre-existing or imported files

• (Rule 2.1) SPDX license identifiers shall be added according to the license notice(s) in the file and the rules
above (1.1 and 1.2*)
• (Rule 2.2) It is recommended that license notices be removed once the corresponding identifier has been added.
Note however that this may only be done by the copyright holder(s) of the file.
• (Rule 2.3) Similar to 2.2, and subject to the same conditions, the text: “All rights reserved” shall be removed
also.

1.7 Platforms supported

Several platforms are supported. In order to manage slight differences between platforms, a PLATFORM_FLAVOR
flag has been introduced. The PLATFORM and PLATFORM_FLAVOR flags define the whole configuration for a
chip the where the Trusted OS runs. Note that there is also a composite form which makes it possible to append

1.6. License headers 15


OP-TEE Documentation

PLATFORM_FLAVOR directly, by adding a dash in-between the names. The composite form is shown below for the
different boards. For more specific details about build flags etc, please read Configuration and flags. Some platforms
have different sub-maintainers, please refer to the file MAINTAINERS for contact details for various platforms.

Table 1: Platforms officially supported in OP-TEE


Platform Composite PLATFORM flag Publicly available
ARM Juno Board PLATFORM=vexpress-juno Yes
Atmel ATSAMA5D2-XULT Board PLATFORM=sam Yes
DeveloperBox (Socionext Synquacer SC2A11) PLATFORM=synquacer Yes
FSL ls1021a PLATFORM=ls-ls1021atwr Yes
NXP ls1043ardb PLATFORM=ls-ls1043ardb Yes
NXP ls1046ardb PLATFORM=ls-ls1046ardb Yes
NXP ls1012ardb PLATFORM=ls-ls1012ardb Yes
NXP ls1088ardb PLATFORM=ls-ls1088ardb Yes
NXP ls2088ardb PLATFORM=ls-ls2088ardb Yes
NXP ls1012afrwy PLATFORM=ls-ls1012afrwy Yes
FSL i.MX6 Quad SABRE Lite Board PLATFORM=imx-mx6qsabrelite Yes
FSL i.MX6 Quad SABRE SD Board PLATFORM=imx-mx6qsabresd Yes
SolidRun i.MX6 Quad Hummingboard Edge PLATFORM=imx-mx6qhmbedge Yes
SolidRun i.MX6 Dual Hummingboard Edge PLATFORM=imx-mx6dhmbedge Yes
SolidRun i.MX6 Dual Lite Hummingboard Edge PLATFORM=imx-mx6dlhmbedge Yes
SolidRun i.MX6 Solo Hummingboard Edge PLATFORM=imx-mx6shmbedge Yes
FSL i.MX6 UltraLite EVK Board PLATFORM=imx-mx6ulevk Yes
NXP i.MX7Dual SabreSD Board PLATFORM=imx-mx7dsabresd Yes
NXP i.MX7Solo WaRP7 Board PLATFORM=imx-mx7swarp7 Yes
ARM Foundation FVP PLATFORM=vexpress-fvp Yes
HiSilicon D02 PLATFORM=d02 No
HiKey Board (HiSilicon Kirin 620) PLATFORM=hikey` or `PLATFORM=hikey-hikey Yes
HiKey960 Board (HiSilicon Kirin 960) PLATFORM=hikey-hikey960 Yes
Marvell ARMADA 7K Family PLATFORM=marvell-armada7k8k Yes
Marvell ARMADA 8K Family PLATFORM=marvell-armada7k8k Yes
Marvell ARMADA 3700 Family PLATFORM=marvell-armada3700 Yes
MediaTek MT8173 EVB Board PLATFORM=mediatek-mt8173 No
Poplar Board (HiSilicon Hi3798C V200) PLATFORM=poplar Yes
QEMU PLATFORM=vexpress-qemu_virt Yes
QEMUv8 PLATFORM=vexpress-qemu_armv8a Yes
Raspberry Pi 3 PLATFORM=rpi3 Yes
Renesas RCAR PLATFORM=rcar No
Rockchip RK322X PLATFORM=rockchip-rk322x No
STMicroelectronics b2260 - h410 (96boards fmt) PLATFORM=stm-b2260 No
STMicroelectronics b2120 - h310 / h410 PLATFORM=stm-cannes No
STMicroelectronics stm32mp1 PLATFORM=stm32mp1 No
Allwinner A64 Pine64 Board PLATFORM=sunxi-sun50i_a64 Yes
Texas Instruments AM65x PLATFORM=k3-am65x Yes
Texas Instruments DRA7xx PLATFORM=ti-dra7xx Yes
Texas Instruments AM57xx PLATFORM=ti-am57xx Yes
Texas Instruments AM43xx PLATFORM=ti-am43xx Yes
Xilinx Zynq 7000 ZC702 PLATFORM=zynq7k-zc702 Yes
Xilinx Zynq UltraScale+ MPSOC PLATFORM=zynqmp-zcu102 Yes
Spreadtrum SC9860 PLATFORM=sprd-sc9860 No

16 Chapter 1. Getting started


OP-TEE Documentation

1.8 Presentations

Below are presentations coming from engineers working with OP-TEE in one or another way. Note that the older they
are, the less relevant is the information in them. So do not trust blindly what was said back in the days, cross check
with latest version to understand whether things have changed or not.
The links are sorted in chronological order, newest first and oldest at the end.
• YVR18
– YVR18-414 - Keymaster and Gatekeeper (slides, video)
– YVR18-117 - SWG updates since HKG18 (slides, video)
• HKG18
– HKG18-402 - Build secure key management services in OP-TEE (slides, video)
• SFO17
– SFO17-309 - Secure storage updates (slides, video)
• Webinar
– TEE Linux kernel support and open source security (slides, video)
• BUD17
– BUD17-416 - Benchmark and profiling in OP TEE (slides, video)
– BUD17-400 - Secure Data Path with OPTEE (slides, video)
• LAS16
– LAS16-504 - Secure Storage updates in OP-TEE (slides, video)
– LAS16-406 - Android Widevine on OP-TEE (slides, video)
– LAS16-111 - Easing Access to ARM TrustZone OP TEE and Raspberry Pi 3 (slides, video)
• BKK16
– BKK16-201 - PlayReady OP-TEE Integration with Secure Video Path (slides, video)
– BKK16-110 - A Gentle Introduction to Trusted Execution and OP-TEE (slides)
• SFO15
– SFO15-503 - Secure storage in OP-TEE (slides, video)
– SFO15-205 - OP-TEE Content Decryption with Microsoft PlayReady on ARM TrustZone (slides,
video)
– SFO15-200 - TEE kernel driver (slides, video)
• HKG15
– HKG15-311 - OP-TEE for Beginners and Porting Review (slides, video)
– HKG15-307 - OP-TEE pager (slides, video)
• LCU14
– LCU14-306 - OP-TEE Future Enhancements (slides)
– LCU14-302 - How to port OP-TEE to another platform (slides, video)
– LCU14-107 - OP-TEE on ARMv8-A (slides, video)

1.8. Presentations 17
OP-TEE Documentation

– LCU14-103 - How to create and run Trusted Applications on OP-TEE (slides, video)
• LCA14
– LCA14-502 - The way to a generic TrustZone solution (slides)
– LCA14-418 - Testing a secure framework (slides)

18 Chapter 1. Getting started


CHAPTER 2

Architecture documentation

2.1 Core

2.1.1 Interrupt handling

This document describes how optee_os handles switches of world execution context based on SMC exceptions and in-
terrupt notifications Interrupt notifications are IRQ/FIQ exceptions which may also imply switching of world execution
context: normal world to secure world, or secure world to normal world.

Use cases of world context switch

This section lists all the cases where optee_os is involved in world context switches. Optee_os executes in the secure
world. World switch is done by the cores secure monitor level/mode, referred below as the Monitor.
When the normal world invokes the secure world, the normal world executes a SMC instruction. The SMC exception
is always trapped by the Monitor. If the related service targets the trusted OS, the Monitor will switch to optee_os
world execution. When the secure world returns to the normal world, optee_os executes a SMC that is caught by the
Monitor which switches back to the normal world.
When a secure interrupt is signaled by the Arm GIC, it shall reach the optee_os interrupt exception vector. If the
secure world is executing, optee_os will handle straight the interrupt from its exception vector. If the normal world is
executing when the secure interrupt raises, the Monitor vector must handle the exception and invoke the optee_os to
serve the interrupt.
When a non-secure interrupt is signaled by the Arm GIC, it shall reach the normal world interrupt exception vector.
If the normal world is executing, it will handle straight the exception from its exception vector. If the secure world is
executing when the non-secure interrupt raises, optee_os will temporarily return back to normal world via the Monitor
to let normal world serve the interrupt.

19
OP-TEE Documentation

Core exception vectors

Monitor vector is VBAR_EL3 in AArch64 and MVBAR in Armv7-A/AArch32. Monitor can be reached while normal
world or secure world is executing. The executing secure state is known to the Monitor through the SCR_NS.
Monitor can be reached from a SMC exception, an IRQ or FIQ exception (so-called interrupts) and from asynchronous
aborts. Obviously monitor aborts (data, prefetch, undef) are local to the Monitor execution.
The Monitor can be external to optee_os (case CFG_WITH_ARM_TRUSTED_FW=y). If not, provides a local secure
monitor core/arch/arm/sm. Armv7-A platforms should use the optee_os secure monitor. Armv8-A platforms
are likely to rely on an Trusted Firmware A.
When executing outside the Monitor, the system is executing either in the normal world (SCR_NS=1) or in the secure
world (SCR_NS=0). Each world owns its own exception vector table (state vector):
• VBAR_EL2 or VBAR_EL1 non-secure or VBAR_EL1 secure for AArch64.
• HVBAR or VBAR non-secure or VBAR secure for Armv7-A and AArch32.
All SMC exceptions are trapped in the Monitor vector. IRQ/FIQ exceptions can be trapped either in the Monitor vector
or in the state vector of the executing world.
When the normal world is executing, the system is configured to route:
• secure interrupts to the Monitor that will forward to optee_os
• non-secure interrupts to the executing world exception vector.
When the secure world is executing, the system is configured to route:
• secure and non-secure interrupts to the executing optee_os exception vector. optee_os shall forward the non-
secure interrupts to the normal world.
Optee_os non-secure interrupts are always trapped in the state vector of the executing world. This is reflected by a
static value of SCR_(IRQ|FIQ).

Native and foreign interrupts

Two types of interrupt are defined in optee_os:


• Native interrupt - The interrupt handled by optee_os (for example: secure interrupt)
• Foreign interrupt - The interrupt not handled by optee_os (for example: non-secure interrupt which is handled
by normal world)
For Arm GICv2 mode, native interrupt is sent as FIQ and foreign interrupt is sent as IRQ. For Arm GICv3 mode,
foreign interrupt is sent as FIQ which could be handled by either secure world (aarch32 Monitor mode or aarch64
EL3) or normal world. Arm GICv3 mode can be enabled by setting CFG_ARM_GICV3=y. For clarity, this document
mainly chooses the GICv2 convention and refers the IRQ as optee_os foreign interrupts, and FIQ as optee_os native
interrupts. Native interrupts must be securely routed to optee_os. Foreign interrupts, when trapped during secure
world execution might need to be efficiently routed to the normal world.

Normal World invokes optee_os using SMC

Entering the Secure Monitor


The monitor manages all entries and exits of secure world. To enter secure world from normal world the monitor
saves the state of normal world (general purpose registers and system registers which are not banked) and restores the
previous state of secure world. Then a return from exception is performed and the restored secure state is resumed.
Exit from secure world to normal world is the reverse.

20 Chapter 2. Architecture documentation


OP-TEE Documentation

Some general purpose registers are not saved and restored on entry and exit, those are used to pass parameters between
secure and normal world (see ARM_DEN0028A_SMC_Calling_Convention for details).
Entry and exit of Trusted OS
On entry and exit of Trusted OS each CPU is uses a separate entry stack and runs with IRQ and FIQ blocked. SMCs
are categorised in two flavor: fast and standard.
• For fast SMCs, optee_os will execute on the entry stack with IRQ/FIQ blocked until the execution returns to
normal world.
• For standard SMCs, optee_os will at some point execute the requested service with interrupts unblocked.
In order to handle interrupts, mainly forwarding of foreign interrupts, optee_os assigns a trusted thread
(core/arch/arm/kernel/thread.c) to the SMC request. The trusted thread stores the execution context of the re-
quested service. This context can be suspended and resumed as the requested service executes and is interrupted.
The trusted thread is released only once the service execution returns with a completion status.
For standard SMCs, optee_os allocates or resumes a trusted thread then unblock the IRQ/FIQ lines. When the
optee_os needs to invoke the normal world from a foreign interrupt or a remote service call, optee_os blocks
IRQ/FIQ and suspends the trusted thread. When suspending, optee_os gets back to the entry stack.
• Both fast and standard SMC end on the entry stack with IRQ/FIQ blocked and optee_os invokes the Monitor
through a SMC to return to the normal world.

Fig. 1: SMC entry to secure world

Deliver non-secure interrupts to Normal World

This section uses the Arm GICv1/v2 conventions: IRQ signals non-secure interrupts while FIQ signals secure inter-
rupts. On a GICv3 configuration, one should exchange IRQ and FIQ in this section.
Forward a Foreign Interrupt from Secure World to Normal World
When an IRQ is received in secure world as an IRQ exception then secure world:

2.1. Core 21
OP-TEE Documentation

1. Saves trusted thread context (entire state of all processor modes for Armv7-A)
2. Blocks (masks) all interrupts (IRQ and FIQ)
3. Switches to entry stack
4. Issues an SMC with a value to indicates to normal world that an IRQ has been delivered and last SMC call
should be continued
The monitor restores normal world context with a return code indicating that an IRQ is about to be delivered. Normal
world issues a new SMC indicating that it should continue last SMC.
The monitor restores secure world context which locates the previously saved context and checks that it is a return
from IRQ that is requested before restoring the context and lets the secure world IRQ handler return from exception
where the execution would be resumed.
Note that the monitor itself does not know/care that it has just forwarded an IRQ to normal world. The bookkeeping is
done in the trusted thread handling in Trusted OS. Normal world is responsible to decide when the secure world thread
should resume execution. See some details in section [Trusted Thread Scheduling](#5-trusted-thread-scheduling).

Fig. 2: IRQ received in secure world and forwarded to normal world

Deliver a non-secure interrupt to normal world when ‘‘SCR_NS‘‘ is set


Since SCR_IRQ is cleared, an IRQ will be delivered using the state vector (VBAR) in the normal world. The IRQ is
received as any other exception by normal world, the monitor and the Trusted OS are not involved at all.

22 Chapter 2. Architecture documentation


OP-TEE Documentation

Deliver secure interrupts to Secure World

This section uses the Arm GICv1/v2 conventions: FIQ signals secure interrupts while IRQ signals non-secure inter-
rupts. On a GICv3 configuration, one should exchange IRQ and FIQ in this section. A FIQ can be received during
two different states, either in normal world (SCR_NS is set) or in secure world (SCR_NS is cleared). When the secure
monitor is active (Armv8-A EL3 or Armv7-A Monitor mode) FIQ is masked. FIQ reception in the two different states
is described below.
Deliver FIQ to secure world when SCR_NS is set
When the monitor gets an FIQ exception it:
1. Saves normal world context and restores secure world context from last secure world exit (which will have IRQ
and FIQ blocked)
2. Clears SCR_FIQ when clearing SCR_NS
3. Sets “FIQ” as parameter to secure world entry
4. Does a return from exception into secure context
5. Secure world unmasks FIQs because of the “FIQ” parameter
6. FIQ is received as in exception using the state vector
7. The state vector handle returns from exception in secure world
8. Secure world issues an SMC to return to normal world
9. Monitor saves secure world context and restores normal world context
10. Does a return from exception into restored context

Fig. 3: FIQ received when SCR_NS is set

Deliver FIQ to secure world when SCR_NS is cleared


Since SCR_FIQ is cleared when SCR_NS is cleared a FIQ will be delivered using the state vector (VBAR) in secure
world. The FIQ is received as any other exception by Trusted OS, the monitor is not involved at all.

2.1. Core 23
OP-TEE Documentation

Fig. 4: FIQ received while processing an IRQ forwarded from secure world

24 Chapter 2. Architecture documentation


OP-TEE Documentation

Trusted thread scheduling

Trusted thread for standard services


OP-TEE standard services are carried through standard SMC. Execution of these services can be interrupted by foreign
interrupts. To suspend and restore the service execution, optee_os assigns a trusted thread at standard SMCs entry.
The trusted thread terminates when optee_os returns to the normal world with a service completion status.
A trusted thread execution can be interrupted by a native interrupt. In this case the native interrupt is handled by the
interrupt exception handlers and once served, optee_os returns to the execution trusted thread.
A trusted thread execution can be interrupted by a foreign interrupt. In this case, optee_os suspends the trusted thread
and invokes the normal world through the Monitor (optee_os so-called RPC services). The trusted threads will resume
only once normal world invokes the optee_os with the RPC service status.
A trusted thread execution can lead optee_os to invoke a service in normal world: access a file, get the REE current
time, etc. The trusted thread is suspended/resumed during remote service execution.
Scheduling considerations
When a trusted thread is interrupted by a foreign interrupt and when optee_os invokes a normal world service, the
normal world gets the opportunity to reschedule the running applications. The trusted thread will resume only once
the client application is scheduled back. Thus, a trusted thread execution follows the scheduling of the normal world
caller context.
Optee_os does not implement any thread scheduling. Each trusted thread is expected to track a service that is invoked
from the normal world and should return to it with an execution status.
The OP-TEE Linux driver (as implemented in drivers/tee/optee since Linux kernel 4.12) is designed so that the Linux
thread invoking OP-TEE gets assigned a trusted thread on TEE side. The execution of the trusted thread is tied to the
execution of the caller Linux thread which is under the Linux kernel scheduling decision. This means trusted threads
are scheduled by the Linux kernel.
Trusted thread constraints
optee_os handles a static number of trusted threads, see CFG_NUM_THREADS.
Trusted threads are only expensive on memory constrained system, mainly regarding the execution stack size.
On SMP systems, optee_os can execute several trusted threads in parallel if the normal world supports scheduling of
processes. Even on UP systems, supporting several trusted threads in optee_os helps normal world scheduler to be
efficient.

2.1.2 Memory objects

A memory object, MOBJ, describes a piece of memory. The interface provided is mostly abstract when it comes to
using the MOBJ to populate translation tables etc. There are different kinds of MOBJs describing:
• Physically contiguous memory
– created with mobj_phys_alloc().
• Virtual memory
– one instance with the name mobj_virt available.
– spans the entire virtual address space.
• Physically contiguous memory allocated from a tee_mm_pool_t *
– created with mobj_mm_alloc().

2.1. Core 25
OP-TEE Documentation

• Paged memory
– created with mobj_paged_alloc().
– only contains the supplied size and makes mobj_is_paged() return true if supplied as argument.
• Secure copy paged shared memory
– created with mobj_seccpy_shm_alloc().
– makes mobj_is_paged() and mobj_is_secure() return true if supplied as argument.

2.1.3 MMU

Translation tables

OP-TEE uses several L1 translation tables, one large spanning 4 GiB and two or more small tables spanning 32 MiB.
The large translation table handles kernel mode mapping and matches all addresses not covered by the small translation
tables. The small translation tables are assigned per thread and covers the mapping of the virtual memory space for
one TA context.
Memory space between small and large translation table is configured by TTBRC. TTBR1 always points to the large
translation table. TTBR0 points to the a small translation table when user mapping is active and to the large translation
table when no user mapping is currently active. For details about registers etc, please refer to a Technical Reference
Manual for your architecture, for example Cortex-A53 TRM.
The translation tables has certain alignment constraints, the alignment (of the physical address) has to be the same as
the size of the translation table. The translation tables are statically allocated to avoid fragmentation of memory due
to the alignment constraints.
Each thread has one small L1 translation table of its own. Each TA context has a compact representation of its L1
translation table. The compact representation is used to initialize the thread specific L1 translation table when the TA
context is activated.

26 Chapter 2. Architecture documentation


OP-TEE Documentation

Small L1
Spans 32 MiB
per entry
Thread 0 ctx active
0

Thread 1 ctx active


1
...
TTBR0 Thread n ctx active n
TTBR1 No active ctx
Large L1
Spans 4 GiB

Switching to user mode

This section only applies with following configuration flags:


• CFG_WITH_LPAE=n
• CFG_CORE_UNMAP_CORE_AT_EL0=y
When switching to user mode only a minimal kernel mode mapping is kept. This is achieved by selecting a zeroed out
big L1 translation in TTBR1 when transitioning to user mode. When returning back to kernel mode the original L1
translation table is restored in TTBR1.

Switching to normal world

When switching to normal world either via a foreign interrupt or RPC there is a chance that secure world will resume
execution on a different CPU. This means that the new CPU need to be configured with the context of the currently
active TA. This is solved by always setting the TA context in the CPU when resuming execution. Here is room for
improvements since it is more likely than not that it is the same CPU that resumes execution in secure world.

2.1.4 Pager

OP-TEE currently requires ~256 KiB RAM for OP-TEE kernel memory. This is not a problem if OP-TEE uses
TrustZone protected DDR, but for security reasons OP-TEE may need to use TrustZone protected SRAM instead. The
amount of available SRAM varies between platforms, from just a few KiB up to over 512 KiB. Platforms with just a
few KiB of SRAM cannot be expected to be able to run a complete TEE solution in SRAM. But those with 128 to

2.1. Core 27
OP-TEE Documentation

256 KiB of SRAM can be expected to have a capable TEE solution in SRAM. The pager provides a solution to this by
demand paging parts of OP-TEE using virtual memory.

Secure memory

TrustZone protected SRAM is generally considered more secure than TrustZone protected DRAM as there is usually
more attack vectors on DRAM. The attack vectors are hardware dependent and can be different for different platforms.

Backing store

TrustZone protected DRAM or in some cases non-secure DRAM is used as backing store. The data in the backing
store is integrity protected with one hash (SHA-256) per page (4KiB). Readonly pages are not encrypted since the
OP-TEE binary itself is not encrypted.

Partitioning of memory

The code that handles demand paging must always be available as it would otherwise lead to deadlock. The virtual
memory is partitioned as:

Type Sections
+--------------+-----------------+
| | text |
| | rodata |
| | data |
| unpaged | bss |
| | heap1 |
| | nozi |
| | heap2 |
+--------------+-----------------+
| init / paged | text_init |
| | rodata_init |
+------------- +-----------------+
| paged | text_pageable |
| | rodata_pageable |
+--------------+-----------------+
| demand alloc | |
| | |
+--------------+-----------------+

Where nozi stands for “not zero initialized”, this section contains entry stacks (thread stack when TEE pager is not
enabled) and translation tables (TEE pager cached translation table when the pager is enabled and LPAE MMU is
used).
The init area is available when OP-TEE is initializing and contains everything that is needed to initialize the pager.
After the pager has been initialized this area will be used for demand paged instead.
The demand alloc area is a special area where the pages are allocated and removed from the pager on demand.
Those pages are returned when OP-TEE does not need them any longer. The thread stacks currently belongs this area.
This means that when a stack is not used the physical pages can be used by the pager for better performance.
The technique to gather code in the different area is based on compiling all functions and data into separate sections.
The unpaged text and rodata is then gathered by linking all object files with --gc-sections to eliminate sections
that are outside the dependency graph of the entry functions for unpaged functions. A script analyzes this ELF file and
generates the bits of the final link script. The process is repeated for init text and rodata. What is not “unpaged” or
“init” becomes “paged”.

28 Chapter 2. Architecture documentation


OP-TEE Documentation

Partitioning of the binary

The binary is partitioned into four parts as:

+----------+
| Header |
+----------+
| Init |
+----------+
| Hashes |
+----------+
| Pageable |
+----------+

The header is defined as:

#define OPTEE_MAGIC 0x4554504f


#define OPTEE_VERSION 1
#define OPTEE_ARCH_ARM32 0
#define OPTEE_ARCH_ARM64 1

struct optee_header {
uint32_t magic;
uint8_t version;
uint8_t arch;
uint16_t flags;
uint32_t init_size;
uint32_t init_load_addr_hi;
uint32_t init_load_addr_lo;
uint32_t init_mem_usage;
uint32_t paged_size;
};

The header is only used by the loader of OP-TEE, not OP-TEE itself. To initialize OP-TEE the loader loads
the complete binary into memory and copies what follows the header and the following init_size bytes to
(init_load_addr_hi << 32 | init_load_addr_lo). init_mem_usage is used by the loader to be
able to check that there is enough physical memory available for OP-TEE to be able to initialize at all. The loader
supplies in r0/x0 the address of the first byte following what was not copied and jumps to the load address to start
OP-TEE.
In addition to overall binary with partitions inside described as above, extra three binaries are generated simultaneously
during build process for loaders who support loading separate binaries:

+----------+
| Header |
+----------+

+----------+
| Init |
+----------+
| Hashes |
+----------+

+----------+
| Pageable |
+----------+

In this case, loaders load header binary first to get image list and information of each image; and then load each of

2.1. Core 29
OP-TEE Documentation

them into specific load address assigned in structure. These binaries are named with v2 suffix to distinguish from the
existing binaries. Header format is updated to help loaders loading binaries efficiently:

#define OPTEE_IMAGE_ID_PAGER 0
#define OPTEE_IMAGE_ID_PAGED 1

struct optee_image {
uint32_t load_addr_hi;
uint32_t load_addr_lo;
uint32_t image_id;
uint32_t size;
};

struct optee_header_v2 {
uint32_t magic;
uint8_t version;
uint8_t arch;
uint16_t flags;
uint32_t nb_images;
struct optee_image optee_image[];
};

Magic number and architecture are identical as original. Version is increased to two. load_addr_hi and
load_addr_lo may be 0xFFFFFFFF for pageable binary since pageable part may get loaded by loader into
dynamic available position. image_id indicates how loader handles current binary. Loaders who don’t support
separate loading just ignore all v2 binaries.

Initializing the pager

The pager is initialized as early as possible during boot in order to minimize the “init” area. The global variable
tee_mm_vcore describes the virtual memory range that is covered by the level 2 translation table supplied to
tee_pager_init().
Assign pageable areas
A virtual memory range to be handled by the pager is registered with a call to tee_pager_add_core_area().

bool tee_pager_add_area(tee_mm_entry_t *mm,


uint32_t flags,
const void *store,
const void *hashes);

which takes a pointer to tee_mm_entry_t to tell the range, flags to tell how memory should be mapped (readonly,
execute etc), and pointers to backing store and hashes of the pages.
Assign physical pages
Physical SRAM pages are supplied by calling tee_pager_add_pages()

void tee_pager_add_pages(tee_vaddr_t vaddr,


size_t npages,
bool unmap);

tee_pager_add_pages() takes the physical address stored in the entry mapping the virtual address “vaddr” and
“npages” entries after that and uses it to map new pages when needed. The unmap parameter tells whether the pages
should be unmapped immediately since they does not contain initialized data or be kept mapped until they need to be
recycled. The pages in the “init” area are supplied with unmap == false since those page have valid content and
are in use.

30 Chapter 2. Architecture documentation


OP-TEE Documentation

Invocation

The pager is invoked as part of the abort handler. A pool of physical pages are used to map different virtual addresses.
When a new virtual address needs to be mapped a free physical page is mapped at the new address, if a free physical
page cannot be found the oldest physical page is selected instead. When the page is mapped new data is copied from
backing store and the hash of the page is verified. If it is OK the pager returns from the exception to resume the
execution.

Paging of user TA

Paging of user TAs can optionally be enabled with CFG_PAGED_USER_TA=y. Paging of user TAs is analogous to
paging of OP-TEE kernel parts but with a few differences:
• Read/write pages are paged in addition to read-only pages
• Page tables are managed dynamically
tee_pager_add_uta_area() is used to setup initial read/write mapping needed when populating the TA. When
the TA is fully populated and relocated tee_pager_set_uta_area_attr() changes the mapping of the area
to strict permissions used when the TA is running.

2.1.5 Stacks

Different stacks are used during different stages. The stacks are:
• Secure monitor stack (128 bytes), bound to the CPU. Only available if OP-TEE is compiled with a secure
monitor always the case if the target is Armv7-A but never for Armv8-A.
• Temp stack (small ~1KB), bound to the CPU. Used when transitioning from one state to another. Interrupts are
always disabled when using this stack, aborts are fatal when using the temp stack.
• Abort stack (medium ~2KB), bound to the CPU. Used when trapping a data or pre-fetch abort. Aborts from
user space are never fatal the TA is only killed. Aborts from kernel mode are used by the pager to do the demand
paging, if pager is disabled all kernel mode aborts are fatal.
• Thread stack (large ~8KB), not bound to the CPU instead used by the current thread/task. Interrupts are usually
enabled when using this stack.
Notes for Armv7-A/AArch32:

Table 1: Armv7-A / AArch32


Stack Comment
Temp Assigned to SP_SVC during entry/exit, always assigned to SP_IRQ and SP_FIQ
Abort Always assigned to SP_ABT
Thread Assigned to SP_SVC while a thread is active

Notes for AArch64:


There are only two stack pointers, SP_EL1 and SP_EL0, available for OP-TEE in AArch64. When an exception
is received stack pointer is always SP_EL1 which is used temporarily while assigning an appropriate stack pointer
for SP_EL0. SP_EL1 is always assigned the value of thread_core_local[cpu_id]. This structure has some
spare space for temporary storage of registers and also keeps the relevant stack pointers. In general when we talk about
assigning a stack pointer to the CPU below we mean SP_EL0.

2.1. Core 31
OP-TEE Documentation

Boot

During early boot the CPU is configured with the temp stack which is used until OP-TEE exits to normal world the
first time.
Notes for AArch64:
SPSEL is always 0 on entry/exit to have SP_EL0 acting as stack pointer.

Normal entry

Each time OP-TEE is entered from normal world the temp stack is used as the initial stack. For fast calls this is the
only stack used. For normal calls an empty thread slot is selected and the CPU switches to that stack.

Normal exit

Normal exit occurs when a thread has finished its task and the thread is freed. When the main thread function,
tee_entry_std(), returns interrupts are disabled and the CPU switches to the temp stack instead. The thread is
freed and OP-TEE exits to normal world.

RPC exit

RPC exit occurs when OP-TEE need some service from normal world. RPC can currently only be performed with a
thread is in running state. RPC is initiated with a call to thread_rpc() which saves the state in a way that when
the thread is restored it will continue at the next instruction as if this function did a normal return. CPU switches to
use the temp stack before returning to normal world.

Foreign interrupt exit

Foreign interrupt exit occurs when OP-TEE receives a foreign interrupt. For Arm GICv2 mode, foreign interrupt
is sent as IRQ which is always handled in normal world. Foreign interrupt exit is similar to RPC exit but it is
thread_irq_handler() and elx_irq() (respectively for Armv7-A/Aarch32 and for Aarch64) that saves the
thread state instead. The thread is resumed in the same way though. For Arm GICv3 mode, foreign interrupt is sent as
FIQ which could be handled by either secure world (EL3 in AArch64) or normal world. This mode is not supported
yet.
Notes for Armv7-A/AArch32:
SP_IRQ is initialized to temp stack instead of a separate stack. Prior to exiting to normal world CPU state is changed
to SVC and temp stack is selected.
Notes for AArch64:
SP_EL0 is assigned temp stack and is selected during IRQ processing. The original SP_EL0 is saved in the thread
context to be restored when resuming.

Resume entry

OP-TEE is entered using the temp stack in the same way as for normal entry. The thread to resume is looked up and
the state is restored to resume execution. The procedure to resume from an RPC exit or an foreign interrupt exit is
exactly the same.

32 Chapter 2. Architecture documentation


OP-TEE Documentation

Syscall

Syscalls are executed using the thread stack.


Notes for Armv7-A/AArch32:
Nothing special SP_SVC is already set with thread stack.
Notes for syscall AArch64:
Early in the exception processing the original SP_EL0 is saved in struct thread_svc_regs in case the TA is
executed in AArch64. Current thread stack is assigned to SP_EL0 which is then selected. When returning SP_EL0
is assigned what is in struct thread_svc_regs. This allows tee_svc_sys_return_helper() having
the syscall exception handler return directly to thread_unwind_user_mode().

2.1.6 Shared Memory

Shared Memory is a block of memory that is shared between the non-secure and the secure world. It is used to transfer
data between both worlds.

Shared Memory Allocation

The shared memory is allocated by the Linux driver from a pool struct shm_pool, the pool contains:
• The physical address of the start of the pool
• The size of the pool
• Whether or not the memory is cached
• List of chunk of memory allocated.

Note:
• The shared memory pool is physically contiguous.
• The shared memory area is not secure as it is used by both non-secure and secure world.

Shared Memory Configuration

It is the Linux kernel driver for OP-TEE that is responsible for initializing the shared memory pool, given information
provided by the OP-TEE core. The Linux driver issues a SMC call OPTEE_SMC_GET_SHM_CONFIG to retrieve the
information
• Physical address of the start of the pool
• Size of the pool
• Whether or not the memory is cached
The shared memory pool configuration is platform specific. The memory mapping, including the area
MEM_AREA_NSEC_SHM (shared memory with non-secure world), is retrieved by calling the platform-specific func-
tion bootcfg_get_memory(). Please refer to this function and the area type MEM_AREA_NSEC_SHM to see the
configuration for the platform of interest. The Linux driver will then initialize the shared memory pool accordingly.

2.1. Core 33
OP-TEE Documentation

Shared Memory Chunk Allocation

It is the Linux kernel driver for OP-TEE that is responsible for allocating chunks of shared memory. OP-TEE
linux kernel driver relies on linux kernel generic allocation support (CONFIG_GENERIC_ALLOCATION) to allo-
cation/release of shared memory physical chunks. OP-TEE linux kernel driver relies on linux kernel dma-buf support
(CONFIG_DMA_SHARED_BUFFER) to track shared memory buffers references.

Using shared memory

From the Client Application


The client application can ask for shared memory allocation using the GlobalPlatform Client API function
TEEC_AllocateSharedMemory(). The client application can also provide shared memory through the Glob-
alPlatform Client API function TEEC_RegisterSharedMemory(). In such a case, the provided memory must be
physically contiguous so that the OP-TEE core, that does not handle scatter-gather memory, is able to use the provided
range of memory addresses. Note that the reference count of a shared memory chunk is incremented when shared
memory is registered, and initialized to 1 on allocation.
From the Linux Driver
Occasionally the Linux kernel driver needs to allocate shared memory for the communication with secure world, for
example when using buffers of type TEEC_TempMemoryReference.
From the OP-TEE core
In case the OP-TEE core needs information from the TEE supplicant (dynamic TA loading, REE time request,. . . ),
shared memory must be allocated. Allocation depends on the use case. The OP-TEE core asks for the following shared
memory allocation:
• optee_msg_arg structure, used to pass the arguments to the non-secure world, where the allocation will be
done by sending a OPTEE_SMC_RPC_FUNC_ALLOC message.
• In some cases, a payload might be needed for storing the result from TEE supplicant, for example
when loading a Trusted Application. This type of allocation will be done by sending the message
OPTEE_MSG_RPC_CMD_SHM_ALLOC(OPTEE_MSG_RPC_SHM_TYPE_APPL,...), which then will re-
turn:
– the physical address of the shared memory
– a handle to the memory, that later on will be used later on when freeing this memory.
From the TEE Supplicant
The TEE supplicant is also working with shared memory, used to exchange data between normal and secure worlds.
The TEE supplicant receives a memory address from the OP-TEE core, used to store the data. This is for example the
case when a Trusted Application is loaded. In this case, the TEE supplicant must register the provided shared memory
in the same way a client application would do, involving the Linux driver.

2.1.7 SMC

SMC Interface

OP-TEE’s SMC interface is defined in two levels using optee_smc.h and optee_msg.h. The former file defines SMC
identifiers and what is passed in the registers for each SMC. The latter file defines the OP-TEE Message protocol
which is not restricted to only SMC even if that currently is the only option available.

34 Chapter 2. Architecture documentation


OP-TEE Documentation

SMC communication

The main structure used for the SMC communication is defined in struct optee_msg_arg (in optee_msg.h). If
we are looking into the source code, we could see that communication mainly is achieved using optee_msg_arg
and thread_smc_args (in thread.h), where optee_msg_arg could be seen as the main structure. What will
happen is that the Linux kernel TEE driver will get the parameters either from optee_client or directly from an internal
service in Linux kernel. The TEE driver will populate the struct optee_msg_arg with the parameters plus some
additional bookkeeping information. Parameters for the SMC are passed in registers 1 to 7, register 0 holds the SMC
id which among other things tells whether it is a standard or a fast call.

2.1.8 Thread handling

OP-TEE core uses a couple of threads to be able to support running jobs in parallel (not fully enabled!). There are
handlers for different purposes. In thread.c you will find a function called thread_init_primary which assigns
init_handlers (functions) that should be called when OP-TEE core receives standard or fast calls, FIQ and PSCI
calls. There are default handlers for these services, but the platform can decide if they want to implement their own
platform specific handlers instead.

Synchronization primitives

OP-TEE has three primitives for synchronization of threads and CPUs: spin-lock, mutex, and condvar.
Spin-lock
A spin-lock is represented as an unsigned int. This is the most primitive lock. Interrupts should be disabled
before attempting to take a spin-lock and should remain disabled until the lock is released. A spin-lock is initialized
with SPINLOCK_UNLOCK.

Table 2: Spin lock functions


Function Purpose
cpu_spin_lock() Locks a spin-lock
cpu_spin_trylock()Locks a spin-lock if unlocked and returns 0 else the spin-lock is unchanged and the
function returns !0
cpu_spin_unlock() Unlocks a spin-lock

Mutex
A mutex is represented by struct mutex. A mutex can be locked and unlocked with interrupts enabled or disabled,
but only from a normal thread. A mutex cannot be used in an interrupt handler, abort handler or before a thread has
been selected for the CPU. A mutex is initialized with either MUTEX_INITIALIZER or mutex_init().

Table 3: Mutex functions


Function Purpose
mutex_lock() Locks a mutex. If the mutex is unlocked this is a fast operation, else the function issues an RPC
to wait in normal world.
Unlocks a mutex. If there is no waiters this is a fast operation, else the function issues an RPC
mutex_unlock()
to wake up a waiter in normal world.
Locks a mutex if unlocked and returns true else the mutex is unchanged and the function
mutex_trylock()
returns false.
Asserts that the mutex is unlocked and there is no waiters, after this the memory used by the
mutex_destroy()
mutex can be freed.

2.1. Core 35
OP-TEE Documentation

When a mutex is locked it is owned by the thread calling mutex_lock() or mutex_trylock(), the mutex may
only be unlocked by the thread owning the mutex. A thread should not exit to TA user space when holding a mutex.
Condvar A condvar is represented by struct condvar. A condvar is similar to a pthread_condvar_t in the
pthreads standard, only less advanced. Condition variables are used to wait for some condition to be fulfilled
and are always used together a mutex. Once a condition variable has been used together with a certain mutex, it
must only be used with that mutex until destroyed. A condvar is initialized with CONDVAR_INITIALIZER or
condvar_init().

Table 4: Condvar functions


Function Purpose
Atomically unlocks the supplied mutex and waits in normal world via an RPC for the condition
condvar_wait()
variable to be signaled, when the function returns the mutex is locked again.
Wakes up one waiter of the condition variable (waiting in condvar_wait()).
condvar_signal()
Wake up all waiters of the condition variable.
condvar_broadcast()

The caller of condvar_signal() or condvar_broadcast() should hold the mutex associated with the con-
dition variable to guarantee that a waiter does not miss the signal.

2.2 Cryptographic implementation

This document describes how the TEE Cryptographic Operations API is implemented, how the default crypto provider
may be configured at compile time, and how it may be replaced by another implementation.

2.2.1 Overview

There are several layers from the Trusted Application to the actual crypto algorithms. Most of the crypto code runs in
kernel mode inside the TEE core.
Here is a schematic view of a typical call to the crypto API. The numbers in square brackets ([1], [2]. . . ) refer to the
sections below.

- some_function() (Trusted App) -


[1] TEE_*() User space (libutee.a)
------- utee_*() ----------------------------------------------
[2] tee_svc_*() Kernel space
[3] crypto_*() (libtomcrypt.a and crypto.c)
[4] /* LibTomCrypt */ (libtomcrypt.a)

2.2.2 [1] The TEE Cryptographic Operations API

OP-TEE implements the Cryptographic Operations API defined by the GlobalPlatform association in the TEE In-
ternal Core API. This includes cryptographic functions that span various cryptographic needs: message digests,
symmetric ciphers, message authentication codes (MAC), authenticated encryption, asymmetric operations (encryp-
tion/decryption or signing/verifying), key derivation, and random data generation. These functions make up the TEE
Cryptographic Operations API.
The Internal API is implemented in tee_api_operations.c, which is compiled into a static library: ${O}/
ta_arm{32,64}-lib/libutee/libutee.a.
Most API functions perform some parameter checking and manipulations, then invoke some utee_* function to switch
to kernel mode and perform the low-level work.

36 Chapter 2. Architecture documentation


OP-TEE Documentation

The utee_* functions are declared in utee_syscalls.h and implemented in utee_syscalls_asm.S They are simple system
call wrappers which use the SVC instruction to switch to the appropriate system service in the OP-TEE kernel.

2.2.3 [2] The crypto services

All cryptography-related system calls are declared in tee_svc_cryp.h and implemented in tee_svc_cryp.c. In addition
to dealing with the usual work required at the user/kernel interface (checking parameters and copying memory buffers
between user and kernel space), the system calls invoke a private abstraction layer: the Crypto API, which is declared
in crypto.h. It serves two main purposes:
1. Allow for alternative implementations, such as hardware-accelerated versions.
2. Provide an easy way to disable some families of algorithms at compile-time to save space. See LibTomCrypt
below.

2.2.4 [3] crypto_*()

The crypto_*() functions implement the actual algorithms and helper functions. TEE Core has one global active
implementation of this interface. The default implementation, mostly based on LibTomCrypt, is as follows:

Listing 1: File: core/crypto/crypto.c


/*
* Default implementation for all functions in crypto.h
*/

#if !defined(_CFG_CRYPTO_WITH_HASH)
TEE_Result crypto_hash_get_ctx_size(uint32_t algo __unused,
size_t *size __unused)
{
return TEE_ERROR_NOT_IMPLEMENTED;
}
...
#endif /*_CFG_CRYPTO_WITH_HASH*/

Listing 2: File: core/lib/libtomcrypt/tee_ltc_provider.c


#if defined(_CFG_CRYPTO_WITH_HASH)
TEE_Result crypto_hash_get_ctx_size(uint32_t algo, size_t *size)
{
/* ... */
return TEE_SUCCESS;
}

#endif /*_CFG_CRYPTO_WITH_HASH*/

As shown above, families of algorithms can be disabled and crypto.c will provide default null implementations that
will return TEE_ERROR_NOT_IMPLEMENTED.

2.2.5 Public/private key format

crypto.h uses implementation-specific types to hold key data for asymmetric algorithms. For instance, here is how a
public RSA key is represented:

2.2. Cryptographic implementation 37


OP-TEE Documentation

Listing 3: File: core/include/crypto/crypto.h


struct rsa_public_key {
struct bignum *e; /* Public exponent */
struct bignum *n; /* Modulus */
};

This is also how such keys are stored inside the TEE object attributes (TEE_ATTR_RSA_PUBLIC_KEY in this
case). struct bignum is an opaque type, known to the underlying implementation only. struct bignum_ops
provides functions so that the system services can manipulate data of this type. This includes allocation/deallocation,
copy, and conversion to or from the big endian binary format.

Listing 4: File: core/include/crypto/crypto.h


struct bignum *crypto_bignum_allocate(size_t size_bits);

TEE_Result crypto_bignum_bin2bn(const uint8_t *from, size_t fromsize,


struct bignum *to);

void crypto_bignum_bn2bin(const struct bignum *from, uint8_t *to);


/*...*/

2.2.6 [4] LibTomCrypt

Some algorithms may be disabled at compile time if they are not needed, in order to reduce the size of the OP-TEE
image and reduces its memory usage. This is done by setting the appropriate configuration variable. For example:

$ make CFG_CRYPTO_AES=n # disable AES only


$ make CFG_CRYPTO_{AES,DES}=n # disable symmetric ciphers
$ make CFG_CRYPTO_{DSA,RSA,DH,ECC}=n # disable public key algorithms
$ make CFG_CRYPTO=n # disable all algorithms

Please refer to core/lib/libtomcrypt/sub.mk for the list of all supported variables.


Note that the application interface is not modified when algorithms are disabled. This means, for in-
stance, that the functions TEE_CipherInit(), TEE_CipherUpdate() and TEE_CipherFinal() would
remain present in libutee.a even if all symmetric ciphers are disabled (they would simply return
TEE_ERROR_NOT_IMPLEMENTED).

2.2.7 Add a new crypto implementation

To add a new implementation, the default one in core/lib/libtomcrypt in combination with what is in core/crypto should
be used as a reference. Here are the main things to consider when adding a new crypto provider:
• Put all the new code in its own directory under core/lib unless it is code that will be used regardless of which
crypto provider is in use. How we are dealing with AES-GCM in core/crypto could serve as an example.
• Avoid modifying tee_svc_cryp.c. It should not be needed.
• Although not all crypto families need to be defined, all are required for compliance to the GlobalPlatform
specification.
• If you intend to make some algorithms optional, please try to re-use the same names for configuration variables
as the default implementation.

38 Chapter 2. Architecture documentation


OP-TEE Documentation

2.3 File structure

This page describes what different folders in optee_os contains.

2.3.1 Top level directories

Table 5: Top level directories


Directory Description
/core Files that are only used building TEE Core
/lib Files that are used both when building TEE Core and TAs
/ta Files that are only used when building TAs
/mk Makefiles supporting the build system
/tmp-stuff Temporary stuff that will be removed before the final commit is made
/scripts Helper scripts for miscellaneous tasks
/out Created when building unless a different out directory is specified with O=... on the command line

2.3.2 /core

Table 6: Structure of /core


Directory Description
/arch Architecture and platform specific files
/lib Generic libraries that are likely to be replaced in a final product
/mm Generic memory management, currently empty
/tee Generic TEE files

2.3.3 /core/arch

Table 7: Structure of /core/arch


Directory Description
/arm ARMv7 and Aarch32 specific architecture and platform specific files
/user_mode Linux used space specific files when debugging TEE Core as a user space process, only used for some
development

2.3.4 /core/arch/arm

Table 8: Structure of /core/arch/arm


Directory Description
/include Include files used in rest of TEE core but not in any supporting libraries
/kern Low level and core parts of TEE Core
/mm Memory management
/tee TEE files
/sm Secure Monitor
/plat-foo Specific files for the foo platform

2.3. File structure 39


OP-TEE Documentation

2.3.5 /core/arch/arm/include

Table 9: Structure of /core/arch/arm/include


Directory Description
/kern Include files exposing API for /core/arch/arm/kern files
/kta Include files exposing the KTA API that is mainly used by kernel TAs
/mm Include files exposing API for /core/arch/arm/mm files
/rom Old ROM files that should be removed before going public
/sm Include files exposing API for Secure Monitor

2.3.6 /core/lib/lib{crypto,sla}

Table 10: Structure of /core/lib/lib{crypto,sla}


Directory Description
/ Source files for the library
/include Include files exposing the API of the library

2.3.7 /lib/libutils

Table 11: Structure of /lib/libutils


Directory Description
/ Source file for the library
/arch Architecture specific source files
/arch/arm ARMv7 and Aarch32 specific source files
/arch/arm/include ARMv7 and Aarch32 specific include files
/include Include files exposing the API of the library

2.4 GlobalPlatform API

2.4.1 Introduction

GlobalPlatform works across industries to identify, develop and publish specifications which facilitate the secure and
interoperable deployment and management of multiple embedded applications on secure chip technology. OP-TEE
has support for GlobalPlatform TEE Client API Specification v1.0 (GPD_SPE_007) and TEE Internal Core API
Specification v1.1.2 (GPD_SPE_010).

2.4.2 TEE Client API

The TEE Client API describes and defines how a client running in a rich operating environment (REE) should commu-
nicate with the TEE. To identify a Trusted Application (TA) to be used, the client provides an UUID. All TA’s exposes
one or several functions. Those functions corresponds to a so called commandID which also is sent by the client.

40 Chapter 2. Architecture documentation


OP-TEE Documentation

TEE Contexts

The TEE Context is used for creating a logical connection between the client and the TEE. The context must be
initialized before the TEE Session can be created. When the client has completed a jobs running in secure world, it
should finalize the context and thereby also releasing resources.

TEE Sessions

Sessions are used to create logical connections between a client and a specific Trusted Application. When the session
has been established the client have a opened up the communication channel towards the specified Trusted Application
identified by the UUID. At this stage the client and the Trusted Application can start to exchange data.

TEE Client API example / usage

Below you will find the main functions as defined by GlobalPlatform and which are used in the communication
between the client and the TEE.

TEEC_Result TEEC_InitializeContext(
const char* name,
TEEC_Context* context)

void TEEC_FinalizeContext(
TEEC_Context* context)

TEEC_Result TEEC_OpenSession (
TEEC_Context* context,
TEEC_Session* session,
const TEEC_UUID* destination,
uint32_t connectionMethod,
const void* connectionData,
TEEC_Operation* operation,
uint32_t* returnOrigin)

void TEEC_CloseSession (
TEEC_Session* session)

TEEC_Result TEEC_InvokeCommand(
TEEC_Session* session,
uint32_t commandID,
TEEC_Operation* operation,
uint32_t* returnOrigin)

In principle the commands are called in this order:

TEEC_InitializeContext(...)
TEEC_OpenSession(...)
TEEC_InvokeCommand(...)
TEEC_CloseSession(...)
TEEC_FinalizeContext(...)

It is not uncommon that TEEC_InvokeCommand is called several times in row when the session has been estab-
lished.
For a complete example, please see chapter 5.2 Example 1: Using the TEE Client API in the GlobalPlatform TEE
Client API Specification v1.0.

2.4. GlobalPlatform API 41


OP-TEE Documentation

2.4.3 TEE Internal Core API

The Internal Core API is the API that is exposed to the Trusted Applications running in the secure world. The TEE
Internal API consists of four major parts:
1. Trusted Storage API for Data and Keys
2. Cryptographic Operations API
3. Time API
4. Arithmetical API

Examples / usage

Calling the Internal Core API is done in the same way as described above using Client API. The best place to find
information how this should be done is in the TEE Internal Core API Specification v1.1.2 which contains many
examples of how to call the various APIs. One can also have a look at the examples in the optee_examples git.

2.4.4 Extensions

In addition to what is stated in TEE Internal Core API, there are some non-official extensions in OP-TEE.

Cache Maintenance Support

Following functions have been introduced in order to operate with cache:

TEE_Result TEE_CacheClean(char *buf, size_t len);


TEE_Result TEE_CacheFlush(char *buf, size_t len);
TEE_Result TEE_CacheInvalidate(char *buf, size_t len);

These functions are available to any Trusted Application defined with the flag TA_FLAG_CACHE_MAINTENANCE
sets on. When not set, each function returns the error code TEE_ERROR_NOT_SUPPORTED.
Within these extensions, a Trusted Application is able to operate on the data cache, with the following specification:

Function Description
Write back to memory any dirty data cache lines. The line is marked as not dirty. The valid bit is
TEE_CacheClean()
unchanged.
Purges any valid data cache lines. Any dirty cache lines are first written back to memory, then the
TEE_CacheFlush()
cache line is invalidated.
Invalidate any valid data cache lines. Any dirty line are not written back to memory.
TEE_CacheInvalidate()

In the following two cases, the error code TEE_ERROR_ACCESS_DENIED is returned:


• The memory range has not the write access, that is TEE_MEMORY_ACCESS_WRITE is not set.
• The memory is not user space memory.

Concat KDF

Support for the Concatenation Key Derivation Function (Concat KDF) according to SP 800-56A (Recommendation
for Pair-Wise Key Establishment Schemes Using Discrete Logarithm Cryptography) can be found in OP-TEE.
You may disable this extension by setting the following configuration variable in conf.mk:

42 Chapter 2. Architecture documentation


OP-TEE Documentation

CFG_CRYPTO_CONCAT_KDF := n

Implementation notes
All key and parameter sizes must be multiples of 8 bits. That is:
• Input parameters: the shared secret (Z) and OtherInfo.
• Output parameter: the derived key (DerivedKeyingMaterial).
In addition, the maximum size of the derived key is limited by the size of an object of type
TEE_TYPE_GENERIC_SECRET (512 bytes).
This implementation does not enforce any requirement on the content of the OtherInfo parameter. It is the appli-
cation’s responsibility to make sure this parameter is constructed as specified by the NIST specification if compliance
is desired.
API extension
To support Concat KDF, the TEE Internal Core API v1.1 was extended with new algorithm descriptors, new object
types, and new object attributes as described below.
p.95 Add new object type to TEE_PopulateTransientObject
The following entry shall be added to Table 5-8:

Object type Parts


TEE_TYPE_CONCAT_KDF_Z
The TEE_ATTR_CONCAT_KDF_Z part (input shared secret) must be provided.

p.121 Add new algorithms for TEE_AllocateOperation


The following entry shall be added to Table 6-3:

Algorithm Possible Modes


TEE_ALG_CONCAT_KDF_SHA1_DERIVE_KEY
TEE_MODE_DERIVE
TEE_ALG_CONCAT_KDF_SHA224_DERIVE_KEY
TEE_ALG_CONCAT_KDF_SHA256_DERIVE_KEY
TEE_ALG_CONCAT_KDF_SHA384_DERIVE_KEY
TEE_ALG_CONCAT_KDF_SHA512_DERIVE_KEY
TEE_ALG_CONCAT_KDF_SHA512_DERIVE_KEY

p.126 Explain usage of HKDF algorithms in TEE_SetOperationKey


In the bullet list about operation mode, the following shall be added:
• For the Concat KDF algorithms, the only supported mode is TEE_MODE_DERIVE.
p.150 Define TEE_DeriveKey input attributes for new algorithms
The following sentence shall be deleted:

The TEE_DeriveKey function can only be used with the algorithm


TEE_ALG_DH_DERIVE_SHARED_SECRET.

The following entry shall be added to Table 6-7:

2.4. GlobalPlatform API 43


OP-TEE Documentation

Algorithm Possible operation parame-


ters
TEE_ALG_CONCAT_KDF_SHA1_DERIVE_KEY TEE_ATTR_CONCAT_KDF_DKM_LENGTH:
TEE_ALG_CONCAT_KDF_SHA224_DERIVE_KEY up to 512 bytes. This
TEE_ALG_CONCAT_KDF_SHA256_DERIVE_KEY parameter is mandatory:
TEE_ALG_CONCAT_KDF_SHA384_DERIVE_KEY TEE_ATTR_CONCAT_KDF_OTHER_INFO
TEE_ALG_CONCAT_KDF_SHA512_DERIVE_KEY
TEE_ALG_CONCAT_KDF_SHA512_DERIVE_KEY

p.152 Add new algorithm identifiers


The following entries shall be added to Table 6-8:

Algorithm Identifier
TEE_ALG_CONCAT_KDF_SHA1_DERIVE_KEY 0x800020C1
TEE_ALG_CONCAT_KDF_SHA224_DERIVE_KEY 0x800030C1
TEE_ALG_CONCAT_KDF_SHA256_DERIVE_KEY 0x800040C1
TEE_ALG_CONCAT_KDF_SHA384_DERIVE_KEY 0x800050C1
TEE_ALG_CONCAT_KDF_SHA512_DERIVE_KEY 0x800060C1

p.154 Define new main algorithm


In Table 6-9 in section 6.10.1, a new value shall be added to the value column for row bits [7:0]:

Bits Function Value


Bits [7:0] Identifiy the main underlying algorithm itself ...
0xC1: Concat KDF

The function column for bits[15:12] shall also be modified to read:

Bits Function Value


Bits [15:12] Define the message digest for asymmetric signature algorithms or Concat KDF

p.155 Add new object type for Concat KDF input shared secret
The following entry shall be added to Table 6-10:

Name Identifier Possible sizes


TEE_TYPE_CONCAT_KDF_Z 0xA10000C1 8 to 4096 bits (multiple of 8)

p.156 Add new operation attributes for Concat KDF


The following entries shall be added to Table 6-11:

Name Value
Pro- Type Comment
tec-
tion
TEE_ATTR_CONCAT_KDF_Z
0xC00001C1
Pro- Ref The shared secret (Z)
tected
TEE_ATTR_CONCAT_KDF_OTHER_INFO
0xD00002C1
Pub- Ref OtherInfo
lic
TEE_ATTR_CONCAT_KDF_DKM_LENGTH
0xF00003C1
Pub- Value The length (in bytes) of the derived keying material to be
lic generated, maximum 512. This is KeyDataLen / 8.

44 Chapter 2. Architecture documentation


OP-TEE Documentation

HKDF

OP-TEE implements the HMAC-based Extract-and-Expand Key Derivation Function (HKDF) as specified in RFC
5869. This file documents the extensions to the TEE Internal Core API v1.1 that were implemented to support this
algorithm. Trusted Applications should include <tee_api_defines_extensions.h> to import the definitions.
Note that the implementation follows the recommendations of version 1.1 of the specification for adding new algo-
rithms. It should make it compatible with future changes to the official specification.
You can disable this extension by setting the following in conf.mk:

CFG_CRYPTO_HKDF := n

p.95 Add new object type to TEE_PopulateTransientObject


The following entry shall be added to Table 5-8:

Object type Parts


TEE_TYPE_HKDF_IKM The TEE_ATTR_HKDF_IKM (Input Keying Material) part must be provided.

p.121 Add new algorithms for TEE_AllocateOperation


The following entry shall be added to Table 6-3:

Algorithm Pos-
sible
Modes
TEE_ALG_HKDF_MD5_DERIVE_KEY TEE_ALG_HKDF_SHA1_DERIVE_KEY TEE_MODE_DERIVE
TEE_ALG_HKDF_SHA224_DERIVE_KEY TEE_ALG_HKDF_SHA256_DERIVE_KEY
TEE_ALG_HKDF_SHA384_DERIVE_KEY TEE_ALG_HKDF_SHA512_DERIVE_KEY
TEE_ALG_HKDF_SHA512_DERIVE_KEY

p.126 Explain usage of HKDF algorithms in TEE_SetOperationKey


In the bullet list about operation mode, the following shall be added:
• For the HKDF algorithms, the only supported mode is TEE_MODE_DERIVE.
p.150 Define TEE_DeriveKey input attributes for new algorithms
The following sentence shall be deleted:

The TEE_DeriveKey function can only be used with the algorithm


TEE_ALG_DH_DERIVE_SHARED_SECRET

The following entry shall be added to Table 6-7:

Algorithm Possible operation parameters


TEE_ALG_HKDF_MD5_DERIVE_KEY TEE_ATTR_HKDF_OKM_LENGTH: Number
TEE_ALG_HKDF_SHA1_DERIVE_KEY of bytes in the Output Keying Material
TEE_ALG_HKDF_SHA224_DERIVE_KEY TEE_ATTR_HKDF_SALT (optional) Salt to be
TEE_ALG_HKDF_SHA256_DERIVE_KEY used during the extract step
TEE_ALG_HKDF_SHA384_DERIVE_KEY TEE_ATTR_HKDF_INFO (optional) Info to be
TEE_ALG_HKDF_SHA512_DERIVE_KEY used during the expand step
TEE_ALG_HKDF_SHA512_DERIVE_KEY

p.152 Add new algorithm identifiers

2.4. GlobalPlatform API 45


OP-TEE Documentation

The following entries shall be added to Table 6-8:

Algorithm Identifier
TEE_ALG_HKDF_MD5_DERIVE_KEY 0x800010C0
TEE_ALG_HKDF_SHA1_DERIVE_KEY 0x800020C0
TEE_ALG_HKDF_SHA224_DERIVE_KEY 0x800030C0
TEE_ALG_HKDF_SHA256_DERIVE_KEY 0x800040C0
TEE_ALG_HKDF_SHA384_DERIVE_KEY 0x800050C0
TEE_ALG_HKDF_SHA512_DERIVE_KEY 0x800060C0

## p.154 Define new main algorithm


In Table 6-9 in section 6.10.1, a new value shall be added to the value column for row bits [7:0]:

Bits Function Value


Bits [7:0] Identifiy the main underlying algorithm itself ...
0xC0: HKDF

The function column for bits[15:12] shall also be modified to read:

Bits Function Value


Bits [15:12] Define the message digest for asymmetric signature algorithms or HKDF

p.155 Add new object type for HKDF input keying material
The following entry shall be added to Table 6-10:

Name Identifier Possible sizes


TEE_TYPE_HKDF_IKM 0xA10000C0 8 to 4096 bits (multiple of 8)

p.156 Add new operation attributes for HKDF salt and info
The following entries shall be added to Table 6-11:

Name Value ProtectionType Comment


TEE_ATTR_HKDF_IKM 0xC00001C0
Protected Ref
TEE_ATTR_HKDF_SALT 0xD00002C0
Public Ref
TEE_ATTR_HKDF_INFO 0xD00003C0
Public Ref
TEE_ATTR_HKDF_OKM_LENGTH 0xF00004C0
Public Value

PBKDF2

This document describes the OP-TEE implementation of the key derivation function, PBKDF2 as specified in RFC
2898 section 5.2. This RFC is a republication of PKCS #5 v2.0 from RSA Laboratories’ Public-Key Cryptography
Standards (PKCS) series.
You may disable this extension by setting the following configuration variable in conf.mk:

CFG_CRYPTO_PBKDF2 := n

API extension

46 Chapter 2. Architecture documentation


OP-TEE Documentation

To support PBKDF2, the TEE Internal Core API v1.1 was extended with a new algorithm descriptor, new object types,
and new object attributes as described below.
p.95 Add new object type to TEE_PopulateTransientObject
The following entry shall be added to Table 5-8:

Object type Parts


TEE_TYPE_PBKDF2_PASSWORD The TEE_ATTR_PBKDF2_PASSWORD part must be provided.

p.121 Add new algorithms for TEE_AllocateOperation


The following entry shall be added to Table 6-3:

Algorithm Possible Modes


TEE_ALG_PBKDF2_HMAC_SHA1_DERIVE_KEY TEE_MODE_DERIVE

p.126 Explain usage of PBKDF2 algorithm in TEE_SetOperationKey


In the bullet list about operation mode, the following shall be added:
• For the PBKDF2 algorithm, the only supported mode is TEE_MODE_DERIVE.
p.150 Define TEE_DeriveKey input attributes for new algorithms
The following sentence shall be deleted:

The TEE_DeriveKey function can only be used with the algorithm


TEE_ALG_DH_DERIVE_SHARED_SECRET

The following entry shall be added to Table 6-7:

Algorithm Possible operation parameters


TEE_ALG_PBKDF2_HMAC_SHA1_DERIVE_KEY
TEE_ATTR_PBKDF2_DKM_LENGTH: up to 512 bytes. This parameter is manda-
tory.
TEE_ATTR_PBKDF2_SALT
TEE_ATTR_PBKDF2_ITERATION_COUNT: This parameter is mandatory.

p.152 Add new algorithm identifiers


The following entries shall be added to Table 6-8:

Algorithm Identifier
TEE_ALG_PBKDF2_HMAC_SHA1_DERIVE_KEY 0x800020C2

p.154 Define new main algorithm


In Table 6-9 in section 6.10.1, a new value shall be added to the value column for row bits [7:0]:

Bits Function Value


Bits [7:0] Identifiy the main underlying algorithm itself ...
0xC2: PBKDF2

The function column for bits[15:12] shall also be modified to read:

2.4. GlobalPlatform API 47


OP-TEE Documentation

Bits Function Value


Bits [15:12] Define the message digest for asymmetric signature algorithms or PBKDF2

p.155 Add new object type for PBKDF2 password


The following entry shall be added to Table 6-10:

Name Identifier Possible sizes


TEE_TYPE_PBKDF2_PASSWORD 0xA10000C2 8 to 4096 bits (multiple of 8)

p.156 Add new operation attributes for Concat KDF


The following entries shall be added to Table 6-11:

Name Value ProtectionType Comment


TEE_ATTR_PBKDF2_PASSWORD 0xC00001C2
Protected Ref
TEE_ATTR_PBKDF2_SALT 0xD00002C2
Public Ref
TEE_ATTR_PBKDF2_ITERATION_COUNT
0xF00003C2
Public Value
TEE_ATTR_PBKDF2_DKM_LENGTH 0xF00004C2Public Value The length (in bytes) of the derived key-
ing material to be generated, maximum
512.

2.4.5 Secure Element API

Note: It’s been a long time since this feature was tested. Most likely things will not work. There are no plans on
updating this at the moment. But if there is anyone out there interested in this willing to spend time on this, it would
of course be appreciated.

A Secure Element (SE) is a tamper-resistant platform (typically a one chip secure microcontroller) capable of
securely hosting applications and their confidential and cryptographic data (e.g. key management) in accordance with
the rules and security requirements set forth by a set of well-identified trusted authorities. Simplified speaking, SE
is a secure platform that can run application (called Applet) on it. In order to communicate with Applet, we need a
transport interface.
SE can be implemented via one of the following technologies
• Embedded SE (accessed via platform dependent interface, unremovable)
• Universal Integrated Circuit Card (UICC, accessed via SIM interface)
• Advanced secure MicroSD (accessed via sdio/mmc interface)
Which means the physical interface between application processor (AP) and SE can be quite different. GlobalPlatform
tries to remove this gap and defined a standard transport API called Secure Element API to cover those different
physical transport layer protocols. SE can be accessed directly in TEE, or indirectly accessed via REE. In later case,
a secure channel is needed to ensure the data stream is not hijacked in REE. (For secure channel, we may leverage
TZC-400 to create a secure memory that is not accessible in REE). To understand SE API, you need to understand the
following terms:
• Trusted Application (TA): An application execute in Trust Execution Environment (TEE), which is the initiator
of SE API.
• Applet: Applications that run on smartcard OS. Secure Element API defines the method to communicate be-
tween host application (in our case, TA) and Applet.

48 Chapter 2. Architecture documentation


OP-TEE Documentation

• Service: A service can be used to retrieve all SE readers available in the system, it also provides a service to
create a session from TA to a specific Reader.
• Session: It maintains the connection between TA and a specific Reader. Different TAs can have a session opened
on the same reader. It is SE manager’s responsibility to demux the request from different TAs. Upon a session
is opened by a TA, the card is power-up and ready to accept commands.
• Reader: It is an abstraction to describe the transport interface between the system and SEs. You can imagine
that a SD card slot is a Reader connected with assd. A ril daemon can be another read to talk with UICC cards.
Even embedded SE should have a (virtual) Reader attached to it.
• Logical Channel: It is used by host application (in our case, a TA) to communicate with applets on the smart-
card. [GlobalPlatform Card Specification] defines maximum 20 logical channels, numbered from 0~19. Chan-
nel number 0 is so-called Basic logical channel, or in short, Basic channel. A channel can be
opened or closed by a host application. It is the smartcard OS’s responsibility to manage the state of each logi-
cal channel. Basic channel is always open and cannot be closed. A channel must select an applet, which means
the command passed through the channel will be processed by the selected applet. GlobalPlatform requires a
default applet must be selected on basic channel after system reset. Host application can select different applet
by issuing a SELECT command on basic channel. Other logical channels (numbered 1~19) can be opened
with or without a given Application Identifier (AID). If AID is not given, the applet selected on basic
channel will be selected on the just opened logical channel.
• MultiSelectable or Non-MultiSelectable: An applet can be MultiSelectable or Non-MultiSelectable. For a
Non-MultiSelectable applet, it can only be selected by one channel, further SELECT command on another
channel that is targeting to the applet will fail. MultiSelectable applet can be selected by multiple channels, the
applet can decide maximum number of channels it is willing to accept.

Design

• Manager core/include/tee/se/manager.h: This component manages all Readers on the system. It should provide
reader interface for the Reader developers to register their own Reader instance. (In the case of [JavaCard
Simulator], we should have [PC/SC Passthru Reader] to talk with simulator) It also provides an interface for
client to get reader handle on the system.
• Reader core/include/tee/se/reader.h: It provides the operations that can be applied on a reader handle.
Just like get reader properties and create session to a reader. It’s also responsible for routing an operation(open,
transmit. . . etc) to a specific Reader implementation.
• Protocol (core/include/tee/se/{protocol.h,aid.h,apdu.h}): This module implements the ISO7816 transport layer
protocol that is used to talk with smartcard. It relies on operations provided by Reader to transmit Application
Protocol Data Unit (APDU, refer to ISO7816-4) to a specific SE.
• Session core/include/tee/se/session.h: It provides the operations that can be applied on a session. Just like open
basic or logical channel, and transmit APDU on the session. It relies on protocol layer to create logical, basic
channel and transmit APDU.
• Channel core/include/tee/se/channel.h: It provides the operations that can be applied on a channel. Like trans-
mit an APDU on the channel, select next applet. It relies on protocol module to select AID, and session module
to transport APDU.
• Reader interface core/include/tee/se/reader/interface.h: The abstract layer used to implement a specific Reader
instance, a set of operations need to be implemented to support a new Reader.
– open(): Triggered when the first session is connected, the Reader should be powered on and reset. Doing
initialization. Detect SE is present or not.
– close(): Triggered when the last session to the Reader has been closed. The Reader can be powered
down in this method.

2.4. GlobalPlatform API 49


OP-TEE Documentation

– get_properties(): Get properties of the Reader. Something like the Reader is exclusive to TEE or
not. SE is present. . . etc.
– get_atr(): Get ATR message from the Reader. ATR is defined in ISO7816-3, and it is the message
report by SE to describe the ability of SE.
– transmit(): Transmit an APDU through the Reader which SE attached to.

How to try it out

To test SE API, you need modified QEMU and enhanced JavaCard simulator. Please use this setup script to setup test
environment.

2.5 Libraries

2.5.1 libutee

The TEE Internal Core API describes services that are provided to Trusted Applications. libutee is a library that
implements this API.
libutee is a static library the Trusted Applications shall statically link against. Trusted Applications do execute in
non-privileged secure userspace and libutee also aims at being executed in the non-privileged secure userspace.
Some services for this API are fully statically implemented inside the libutee library while some services for the API
are implemented inside the OP-TEE core (privileged level) and libutee calls such services through system calls.

2.5.2 libmpa

Now deprectated, used to the the BigNum library in OP-TEE.

2.6 Porting guidelines

2.6.1 1. Introduction

This document serves a dual purpose:


• Serve as a base for getting OP-TEE up and running on a new device with initial xtest validation passing. This is
the first part of this document (section 2).
• Highlight the missing pieces if you intend to make a real secure product, that is what the second part of this
document is about.
We are trying our best to implement full end to end security in OP-TEE in a generic way, but due to the nature of
devices being different, NDA etc, it is not always possible for us to do so and in those cases, we most often try to
write a generic API, but we will just stub the code. This porting guideline highlights the missing pieces that must be
addressed in a real secure consumer device. Hopefully we will sooner or later get access to devices where we at least
can make reference implementations publicly available to everyone for the missing pieces we are talking about here.

50 Chapter 2. Architecture documentation


OP-TEE Documentation

2.6.2 2. Add a new platform

The first thing you need to do after you have decided to port OP-TEE to another device is to add a new platform device.
That can either be adding a new platform variant (PLATFORM_FLAVOR) if it is a device from a family already
supported, or it can be a brand new platform family (PLATFORM). Typically this initial setup involve configuring
UART, memory addresses etc. For simplicity let us call our fictive platform for “gendev” just so we have something
to refer to when writing examples further down.

2.1 core/arch/arm

In core/arch/arm you will find all the currently supported devices. That is where you are supposed to add a new
platform or modify an existing one. Typically you will find this set of files in a specific platform folder:

$ ls
conf.mk main.c platform_config.h sub.mk

So for the gendev platform it means that the files should be placed in this folder: .. code-block:: bash
core/arch/arm/plat-gendev
conf.mk
This is the device specific makefile where you define configurations unique to your platform. This mainly comprises
two things: - OP-TEE configuration variables (CFG_), which may be assigned values in two ways. CFG_FOO ?=
bar should be used to provide a default value that may be modified at compile time. On the other hand, variables that
must be set to some value and cannot be modified should be set by: $(call force,CFG_FOO,bar). - Compiler
flags for the TEE core, the user mode libraries and the Trusted Applications, which may be added to macros used by
the build system. Please see Platform-specific configuration and flags in the build system documentation.
It is recommended to use a existing platform configuration file as a starting point. For instance, core/arch/arm/plat-
hikey/conf.mk.
The platform conf.mk file should at least define the default platform flavor for the platform, the core configurations
(architecture and number of cores), the main configuration directives (generic boot, arm trusted firmware support,
generic time source, console driver, etc. . . ) and some platform default configuration settings.

PLATFORM_FLAVOR ?= hikey

include core/arch/arm/cpu/cortex-armv8-0.mk

$(call force,CFG_TEE_CORE_NB_CORE,8)
$(call force,CFG_GENERIC_BOOT,y)
$(call force,CFG_PL011,y)
$(call force,CFG_PM_STUBS,y)
$(call force,CFG_SECURE_TIME_SOURCE_CNTPCT,y)
$(call force,CFG_WITH_ARM_TRUSTED_FW,y)
$(call force,CFG_WITH_LPAE,y)

ta-targets = ta_arm32
ta-targets += ta_arm64

CFG_NUM_THREADS ?= 8
CFG_CRYPTO_WITH_CE ?= y
CFG_WITH_STACK_CANARIES ?= y
CFG_CONSOLE_UART ?= 3
CFG_DRAM_SIZE_GB ?= 2

main.c

2.6. Porting guidelines 51


OP-TEE Documentation

This platform specific file will contain power management handlers and code related to the UART. We will talk more
about the information related to the handlers further down in this document. For our gendev device it could look like
this (here we are excluding the necessary license header to save some space):
#include <console.h>
#include <drivers/serial8250_uart.h>
#include <kernel/generic_boot.h>
#include <kernel/panic.h>
#include <kernel/pm_stubs.h>
#include <mm/core_mmu.h>
#include <platform_config.h>
#include <stdint.h>
#include <tee/entry_fast.h>
#include <tee/entry_std.h>

static void main_fiq(void)


{
panic();
}

static const struct thread_handlers handlers = {


.std_smc = tee_entry_std,
.fast_smc = tee_entry_fast,
.nintr = main_fiq,
.cpu_on = cpu_on_handler,
.cpu_off = pm_do_nothing,
.cpu_suspend = pm_do_nothing,
.cpu_resume = pm_do_nothing,
.system_off = pm_do_nothing,
.system_reset = pm_do_nothing,
};

const struct thread_handlers *generic_boot_get_handlers(void)


{
return &handlers;
}

/*
* Register the physical memory area for peripherals etc. Here we are
* registering the UART console.
*/
register_phys_mem(MEM_AREA_IO_NSEC, CONSOLE_UART_BASE, SERIAL8250_UART_REG_SIZE);

static struct serial8250_uart_data console_data;

void console_init(void)
{
serial8250_uart_init(&console_data, CONSOLE_UART_BASE,
CONSOLE_UART_CLK_IN_HZ, CONSOLE_BAUDRATE);
register_serial_console(&console_data.chip);
}

platform_config.h
This is a mandatory header file for every platform, since there are several files relaying upon the existence of this
particular file. This file is where you will find the major differences between different platforms, since this is where
you do the memory configuration, define base addresses etc. we are going to list a few here, but it probably makes
more sense to have a look at the already existing platform_config.h files for the other platforms. Our fictive
gendev could look like this:

52 Chapter 2. Architecture documentation


OP-TEE Documentation

#ifndef PLATFORM_CONFIG_H
#define PLATFORM_CONFIG_H

/* Make stacks aligned to data cache line length */


#define STACK_ALIGNMENT 64

/* 8250 UART */
#define CONSOLE_UART_BASE 0xcafebabe /* UART0 */
#define CONSOLE_BAUDRATE 115200
#define CONSOLE_UART_CLK_IN_HZ 19200000

/* Optional: when used with CFG_WITH_PAGER, defines the device SRAM */


#define TZSRAM_BASE 0x3F000000
#define TZSRAM_SIZE (200 * 1024)

/* Mandatory main secure RAM usually DDR */


#define TZDRAM_BASE 0x60000000
#define TZDRAM_SIZE (32 * 1024 * 1024)

/* Mandatory TEE RAM location and core load address */


#define TEE_RAM_START TZDRAM_BASE
#define TEE_RAM_PH_SIZE TEE_RAM_VA_SIZE
#define TEE_RAM_VA_SIZE (4 * 1024 * 1024)
#define TEE_LOAD_ADDR (TZDRAM_BASE + 0x20000)

/* Mandatory TA RAM (external less secure RAM) */


#define TA_RAM_START (TZDRAM_BASE + TEE_RAM_VA_SIZE)
#define TA_RAM_SIZE (TZDRAM_SIZE - TEE_RAM_VA_SIZE)

/* Mandatory: for static SHM, need a hardcoded physical address */


#define TEE_SHMEM_START 0x08000000
#define TEE_SHMEM_SIZE (4 * 1024 * 1024)

#endif /* PLATFORM_CONFIG_H */

This is minimal amount of information in the platform_config.h file. I.e, the memory layout for on-chip and
external RAM. Note that parts of the DDR typically will need to be shared with normal world, so there is need for
some kind of memory firewall for this (more about that further down). As you can see we have also added the UART
configuration here, i.e., the DEVICE0_xyz part.

2.2 Get the device officially in OP-TEE?

We do encourage everyone to submit their board support to the OP-TEE project itself, so it becomes part of the official
releases and will be maintained by the OP-TEE community itself. If you intend to do so, then there are a few more
things that you are supposed to do.
Update README.md
There is a section (FIXME: update to new Sphinx) that lists all devices officially supported in OP-TEE, that is where
you also shall list your device. It should contain the name of the platform, then composite PLATFORM flag and whether
the device is publicly available or not.
Update .shippable.yml
Since we are using Shippable to test pull requests etc, we would like that you also add your device to the .shippable.yml
file, so that it will at least be built when someone is doing a pull request. Add a line at the end of file:

2.6. Porting guidelines 53


OP-TEE Documentation

- _make PLATFORM=<platform-name>_

Maintainer
If you are submitting the board support upstream and cannot give Linaro maintainers a device, then we are going
to ask you to become the maintainer for the device you have added. This means that you should also update the
MAINTAINERS.md file accordingly. By being a maintainer for a device you are responsible to keep it up to date and
you will be asked every quarter as part of the OP-TEE release schedule to test your device running the latest OP-TEE
software.
Update build.git
This isn’t strictly necessary, but we are trying to create repo setup(s) for the device(s) that we are in charge of. That
makes it very easy for newcomers to get started with a certain platform. So please consider creating a new manifest:
for the device you have added to OP-TEE.

2.6.3 3. Hardware Unique Key

Most devices have some kind of Hardware Unique Key (HUK) that is mainly used to derive other keys. The HUK
could for example be used when deriving keys used in secure storage etc. The important thing with the HUK is that
it needs to be well protected and in the best case the HUK should never ever be readable directly from software, not
even from the secure side. There are different solutions to this, crypto accelerator might have support for it or, it could
involve another secure co-processor.
In OP-TEE the HUK is just stubbed and you will see that in the function called
tee_otp_get_hw_unique_key() in core/include/kernel/tee_common_otp.h. In a real se-
cure product you must replace this with something else. If your device lacks the hardware support for a HUK, then
you must at least change this to something else than just zeroes. But, remember it is not good secure practice to store
a key in software, especially not the key that is the root for everything else, so this is not something we recommend
that you should do.

2.6.4 4. Secure Clock

The Time API in GlobalPlatform Internal Core API specification defines three sources of time; system time, TA
persistent time and REE time. The REE time is by nature considered as an unsecure source of time, but the other
two should in a fully trustable hardware make use of trustable source of time, i.e., a secure clock. Note that from
GlobalPlatform point of view it is not required to make use of a secure clock, i.e., it is OK to use time from
REE, but the level of trust should be reflected by the gpd.tee.systemTime.protectionLevel property
and the gpd.tee.TAPersistentTime.protectionLevel property (100=REE controlled clock, 1000=TEE
controlled clock). So the functions that one needs to pay attention to are tee_time_get_sys_time() and
tee_time_get_ta_time(). If your hardware has a secure clock, then you probably want to change the imple-
mentation there to instead use the secure clock (and then you would also need to update the property accordingly, i.e.,
tee_time_get_sys_time_protection_level() and the variable ta_time_prot_lvl in tee_svc.
c).

2.6.5 5. Root and Chain of Trust

To be able to assure that your devices are running the (untampered) binaries you intended to run you will need to
establish some kind of trust anchor on the devices.
The most common way of doing that is to put the root public key in some read only memory on the device. Quite often
SoC’s/OEM’s stores public key(s) directly or the hash(es) of the public key(s) in OTP. When the boot ROM (which
indeed needs to be ROM) is about to load the first stage bootloader it typically reads the public key from the software

54 Chapter 2. Architecture documentation


OP-TEE Documentation

binary itself, hash the key and compare it to the key in OTP. If they are matching, then the boot ROM can be sure that
the first stage bootloader was indeed signed with the corresponding private key.
In OP-TEE you will not find any code at all related to this and this is a good example when it is hard for us to do this
in a generic way since device manufacturers all tend to do this in their own unique way and they are not very keen on
sharing their low level boot details and security implementation with the rest of the world. This is especially true on
ARMv7-A. For ARMv8-A it looks bit better, since Arm in Trusted Firmware A have implemented and defined how a
abstract the chain of trust (see auth-framework.rst). We have successfully verified OP-TEE by using the authentication
framework from Trusted Firmware A (see optee_with_auth_framework.md for the details).

2.6.6 6. Hardware Crypto IP

By default OP-TEE uses a software crypto library (currently LibTomCrypt) and you have the ability to enable Crypto
Extensions that were introduced with ARMv8-A (if the device is capable of that). Some of the devices we have in
our hands do have hardware crypto IP’s, but due to NDA’s etc it has not been possible to enable it. If you have a
device capable of doing crypto operations on a dedicated crypto block and you prefer to use that in favor for the
software implementation, then you will need to implement relevant functions defined in core/include/crypto/
crypto.h, the Crypto API, and write the low level driver that communicates with the device. Our FIXME: Sphinx
[crypto.md] file describes how the Crypto API is integrated. Since the communication with crypto blocks tends to be
quite different depending on what kind of crypto block you have, we have not written how that should be done. It
might be that we do that in the future when get hold of a device where we can use the crypto block.
By default OP-TEE is configured with a software PRNG. The entropy is added to software PRNG at various places,
but unfortunately it is still quite easy to predict the data added as entropy. As a consequence, unless the RNG is based
on hardware the generated random will be quite weak.

2.6.7 7. Power Management / PSCI

In section 2 when we talked about the file main.c, we added a couple of handlers related to power management, we
are talking about the following lines:

.cpu_on = cpu_on_handler,
.cpu_off = pm_do_nothing,
.cpu_suspend = pm_do_nothing,
.cpu_resume = pm_do_nothing,
.system_off = pm_do_nothing,
.system_reset = pm_do_nothing,

The only function that actually does something there is the cpu_on function, the rest of them are stubbed. The main
reason for that is because we think that how to suspend and resume is a device dependent thing. The code in OP-TEE
is prepared so that callbacks etc from Trusted Firmware A will be routed to OP-TEE, but since the function(s) are just
stubbed we will not do anything and just return. In a real production device, you would probably want to save and
restore CPU states, secure hardware IPs’ registers and TZASC and other memory firewall related setting when these
callbacks are being called.

2.6.8 8. Memory firewalls / TZASC

Arm have defined a system IP / SoC peripheral called TrustZone Address Space Controller (TZASC, see TZC-380 and
TZC-400). TZASC can be used to configure DDR memory into separate regions in the physcial address space, where
each region can have an individual security level setting. After enabling TZASC, it will perform security checks on
transactions to memory or peripherals. It is not always the case that TZASC is on a device, in some cases the SoC has
developed something equivalent. In OP-TEE this is very well reflected, i.e., different platforms have different ways of
protecting their memory. On ARMv8-A platforms we are in most of the cases using Trusted Firmware A as the boot

2.6. Porting guidelines 55


OP-TEE Documentation

firmware and there the secure bootloader is the one that configures secure vs non-secure memory using TZASC (see
plat_arm_security_setup in TF-A). The takeaway here is that you must make sure that you have configured whatever
memory firewall your device has such that it has a secure and a non-secure memory area.

2.6.9 9. Trusted Application private/public keypair

By default all Trusted Applications (TA’s) are signed with the pre-generated 2048-bit RSA development key (private
key). This key is located in the keys folder (in the root of optee_os.git) and is named default_ta.pem. This key
must be replaced with your own key and you should never ever check-in this private key in the source code tree when
in use in a real product. The recommended way to store private keys is to use some kind of HSM (Hardware Security
Module), but an alternative would be temporary put the private key on a computer considered as secure when you are
about to sign TA’s intended to be used in real products. Typically it is only a few number of people having access to
this type of key in company. The key handling in OP-TEE is currently a bit limited since we only support a single
key which is used for all TA’s. We have plans on extending this to make it a bit more flexible. Exactly when that will
happen has not been decided yet.

2.7 Secure boot

2.7.1 Armv8-A - Using the authentication framework in TF-A

This section gives a brief description on how to enable the verification of OP-TEE using the authentication framework
in Trusted Firmware A (TF-A), i.e., something that could be used in an Armv8-A environment.
According to user-guide.rst, there is no additional specific build options for the verification of OP-TEE. If we have
enabled the authentication framework and specified the BL32 build option when building TF-A, the BL32 related
certificates will be created automatically by the cert_create tool, and then these certificates will be verified during
booting up.
To enable the authentication framework, the following steps should be followed according to user-guide.rst. For more
details about the authentication framework, please see auth-framework.rst and trusted-board-boot.rst.
• Check out a recent version of the mbed TLS repository and then switch to tag mbedtls-2.2.0
• Besides the normal build options, add the following build options for TF-A

MBEDTLS_DIR=<path of the directory containing mbed TLS sources>


TRUSTED_BOARD_BOOT=1
GENERATE_COT=1
ARM_ROTPK_LOCATION=devel_rsa
ROT_KEY=<TF-A-PATH/plat/arm/board/common/rotpk/arm_rotprivk_rsa.pem>

Above steps have been tested on FVP platform, all verification steps are OK and xtest runs successfully without
regression.

2.7.2 Armv7-A systems

Unlike for Armv8-A systems where one can use a more standardized way of doing secure boot by leverage the authen-
tication framework as described above, most device manufacturers have their own way of doing secure boot. Please
reach out directly to the manufacturer for the device you are working with to be able to understand how to do secure
boot on their devices.

56 Chapter 2. Architecture documentation


OP-TEE Documentation

2.8 Secure storage

2.8.1 Background

Secure Storage in OP-TEE is implemented according to what has been defined in GloblaPlatform’s TEE Internal Core
API (here called Trusted Storage). This specification mandates that it should be possible to store general-purpose data
and key material that guarantees confidentiality and integrity of the data stored and the atomicity of the operations that
modifies the storage (atomicity here means that either the entire operation completes successfully or no write is done).
There are currently two secure storage implementations in OP-TEE:
• The first one relies on the normal world (REE) file system. It is described in this document and is the default
implementation. It is enabled at compile time by CFG_REE_FS=y.
• The second one makes use of the Replay Protected Memory Block (RPMB) partition of an eMMC device, and is
enabled by setting CFG_RPMB_FS=y. It is described in [secure_storage_rpmb.md](secure_storage_rpmb.md).
It is possible to use the normal world file systems and the RPMB implementations simultaneously. For
this, two OP-TEE specific storage identifiers have been defined: TEE_STORAGE_PRIVATE_REE and
TEE_STORAGE_PRIVATE_RPMB. Depending on the compile-time configuration, one or several values may be used.
The value TEE_STORAGE_PRIVATE selects the REE FS when available, otherwise the RPMB FS (in this order).

2.8.2 REE FS Secure Storage

Fig. 5: Secure Storage System Architecture

Source Files in OP-TEE OS

2.8. Secure storage 57


OP-TEE Documentation

Table 12: Secure storage files


Source file Purpose
core/tee/tee_svc_storage.c TEE trusted storage service calls
core/tee/tee_ree_fs.c TEE file system & REE file operation interface
core/tee/fs_htree.c Hash tree
core/tee/tee_fs_key_manager.c Key manager
lib/libutee/ GlobalPlatform Internal API library

Basic File Operation Flow

When a TA is calling the write function provided by GP Trusted Storage API to write data to a persistent object, a
corresponding syscall implemented in TEE Trusted Storage Service will be called, which in turn will invoke a series of
TEE file operations to store the data. TEE file system will then encrypt the data and send REE file operation commands
and the encrypted data to TEE supplicant by a series of RPC messages. TEE supplicant will receive the messages and
store the encrypted data accordingly to the Linux file system. Reading files are handled in a similar manner.

GlobalPlatform Trusted Storage Requirement

Below is an excerpt from the specification, listing the most vital requirements:

1. The Trusted Storage may be backed by non-secure resources as long as


suitable cryptographic protection is applied, which MUST be as
strong as the means used to protect the TEE code and data itself.

2. The Trusted Storage MUST be bound to a particular device, which means


that it MUST be accessible or modifiable only by authorized TAs
running in the same TEE and on the same device as when the data was
created.

3. Ability to hide sensitive key material from the TA itself.

4. Each TA has access to its own storage space that is shared among all the
instances of that TA but separated from the other TAs.

5. The Trusted Storage must provide a minimum level of protection against


rollback attacks. It is accepted that the actually physical storage
may be in an insecure area and so is vulnerable to actions from
outside of the TEE. Typically, an implementation may rely on the REE
for that purpose (protection level 100) or on hardware assets
controlled by the TEE (protection level 1000).

(see GP TEE Internal Core API section 2.5 and 5.2)

If configured with CFG_RPMB_FS=y the protection against rollback is controlled by the TEE and is set to 1000. If
CFG_RPMB_FS=n, there’s no protection against rollback, and the protection level is set to 0.

TEE File Structure in Linux File System

OP-TEE by default uses /data/tee/ as the secure storage space in the Linux file system. Each persistent object
is assigned an internal identifier. It is an integer which is visible in the Linux file system as /data/tee/<file
number>.

58 Chapter 2. Architecture documentation


OP-TEE Documentation

A directory file, /data/tee/dirf.db, lists all the objects that are in the secure storage. All normal world files are
integrity protected and encrypted, as described below.

2.8.3 Key Manager

Key manager is an component in TEE file system, and is responsible for handling data encryption and decryption and
also management of the sensitive key materials. There are three types of keys used by the key manager: the Secure
Storage Key (SSK), the TA Storage Key (TSK) and the File Encryption Key (FEK).

Secure Storage Key (SSK)

SSK is a per-device key and is generated and stored in secure memory when OP-TEE is booting. SSK is used to derive
the TA Storage Key (TSK).
SSK is derived by
SSK = HMACSHA256 (HUK, Chip ID || “static string”)
The functions to get Hardware Unique Key (HUK) and chip ID depend on platform implementation. Currently, in
OP-TEE OS we only have a per-device key, SSK, which is used for secure storage subsystem, but, for the future we
might need to create different per-device keys for different subsystems using the same algorithm as we generate the
SSK; An easy way to generate different per-device keys for different subsystems is using different static strings to
generate the keys.

Trusted Application Storage Key (TSK)

The TSK is a per-Trusted Application key, which is generated from the SSK and the TA’s identifier (UUID). It is used
to protect the FEK, in other words, to encrypt/decrypt the FEK.
TSK is derived by:
TSK = HMACSHA256 (SSK, TA_UUID)

File Encryption Key (FEK)

When a new TEE file is created, key manager will generate a new FEK by PRNG (pesudo random number genera-
tor) for the TEE file and store the encrypted FEK in meta file. FEK is used for encrypting/decrypting the TEE file
information stored in meta file or the data stored in block file.

2.8.4 Hash Tree

The hash tree is responsible for handling data encryption and decryption of a secure storage file. The hash tree
is implemented as a binary tree where each node (struct tee_fs_htree_node_image below) in the tree
protects its two child nodes and a data block. The meta data is stored in a header (struct tee_fs_htree_image
below) which also protects the top node.
All fields (header, nodes, and blocks) are duplicated with two versions, 0 and 1, to ensure atomic updates. See
core/tee/fs_htree.c for details.

2.8. Secure storage 59


OP-TEE Documentation

Fig. 6: Meta data encryption

Meta Data Encryption Flow

A new meta IV will be generated by PRNG when a meta data needs to be updated. The size of meta IV is defined
in core/include/tee/fs_htree.h, likewise are the data structures of meta data and node data are defined in fs_htree.h as
follows:

struct tee_fs_htree_node_image {
uint8_t hash[TEE_FS_HTREE_HASH_SIZE];
uint8_t iv[TEE_FS_HTREE_IV_SIZE];
uint8_t tag[TEE_FS_HTREE_TAG_SIZE];
uint16_t flags;
};

struct tee_fs_htree_meta {
uint64_t length;
};

struct tee_fs_htree_imeta {
struct tee_fs_htree_meta meta;
uint32_t max_node_id;
};

struct tee_fs_htree_image {
uint8_t iv[TEE_FS_HTREE_IV_SIZE];
uint8_t tag[TEE_FS_HTREE_TAG_SIZE];
uint8_t enc_fek[TEE_FS_HTREE_FEK_SIZE];
uint8_t imeta[sizeof(struct tee_fs_htree_imeta)];
uint32_t counter;
};

60 Chapter 2. Architecture documentation


OP-TEE Documentation

Block Data Encryption Flow

Fig. 7: Block data encryption

A new block IV will be generated by PRNG when a block data needs to be updated. The size of block IV is defined in
core/include/tee/fs_htree.h.

2.8.5 Atomic Operation

According to GlobalPlatform Trusted Storage requirement of the atomicity, the following operations should support
atomic update:

Write, Truncate, Rename, Create and Delete

The strategy used in OP-TEE secure storage to guarantee the atomicity is out-of-place update.

2.8.6 RPMB Secure Storage

This document describes the RPMB secure storage implementation in OP-TEE, which is enabled by setting
CFG_RPMB_FS=y. Trusted Applications may use this implementation by passing a storage ID equal to
TEE_STORAGE_PRIVATE_RPMB, or TEE_STORAGE_PRIVATE if CFG_REE_FS is disabled. For details about
RPMB, please refer to the JEDEC eMMC specification (JESD84-B51).
The architecture is depicted below.

2.8. Secure storage 61


OP-TEE Documentation

| NORMAL WORLD : SECURE WORLD |


:
U tee-supplicant : Trusted application
S (rpmb.c) : (secure storage API)
E ^ ^ : ^
R | | :~~~~~~~~~~~~~~~~~~|~~~~~~~~~~~~~~~~~~~~
~~~~~~~ ioctl ~~~~~~~|~~~~~~~~~~~~: v
K | | : OP-TEE
E v v : (tee_svc_storage.c)
R MMC/SD subsys. OP-TEE driver : (tee_rpmb_fs.c, tee_fs_key_manager.c)
N ^ ^ : ^
E | | : |
L v | : |
Controller driver | : |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~|~~~~~~~~~~~~~~~~~~~~~~~~|~~~~~~~~~~~~~~~~~~~~
v v
Secure monitor / EL3 firmware

For information about the ioctl() interface to the MMC/SD subsystem in the Linux kernel, see the Linux core
MMC header file linux/mmc/core.h and the mmc-utils repository.

The Secure Storage API

This part is common with the REE-based filesystem. The interface between the system calls in
core/tee/tee_svc_storage.c and the RPMB filesystem is the tee_file_operations, namely struct tee_file_ops.

The RPMB filesystem

The FS implementation is entirely in core/tee/tee_rpmb_fs.c and the RPMB partition is divided in three parts:
• The first 128 bytes are reserved for partition data (struct rpmb_fs_partition).
• At offset 512 is the File Allocation Table (FAT). It is an array of struct rpmb_fat_entry elements, one
per file. The FAT grows dynamically as files are added to the filesystem. Among other things, each entry has
the start address for the file data, its size, and the filename.
• Starting from the end of the RPMB partition and extending downwards is the file data area.
Space in the partition is allocated by the general-purpose allocator functions, tee_mm_alloc() and
tee_mm_alloc2().
All file operations are atomic. This is achieved thanks to the following properties:
• Writing one single block of data to the RPMB partition is guaranteed to be atomic by the eMMC specification.
• The FAT block for the modified file is always updated last, after data have been written successfully.
• Updates to file content is done in-place only if the data do not span more than the “reliable write block count”
blocks. Otherwise, or if the file needs to be extended, a new file is created.

Device access

There is no eMMC controller driver in OP-TEE. The device operations all have to go through the normal world. They
are handled by the tee-supplicant process which further relies on the kernel’s ioctl() interface to access the
device. tee-supplicant also has an emulation mode which implements a virtual RPMB device for test purposes.
RPMB operations are the following:

62 Chapter 2. Architecture documentation


OP-TEE Documentation

• Reading device information (partition size, reliable write block count).


• Programming the security key. This key is used for authentication purposes. Note that it is different
from the Secure Storage Key (SSK) defined below, which is used for encryption. Like the SSK how-
ever, the security key is also derived from a hardware unique key or identifier. Currently, the function
tee_otp_get_hw_unique_key() is used to generate the RPMB security key.
• Reading the write counter value. The write counter is used in the HMAC computation during read and
write requests. The value is read at initialization time, and stored in struct tee_rpmb_ctx, i.e.,
rpmb_ctx->wr_cnt.
• Reading or writing blocks of data.
RPMB operations are initiated on request from the FS layer. Memory buffers for requests and responses are allocated
in shared memory using thread_optee_rpc_alloc_payload(). Buffers are passed to the normal world in a
TEE_RPC_RPMB_CMD message, thanks to the thread_rpc_cmd() function. Most RPMB requests and responses
use the data frame format defined by the JEDEC eMMC specification. HMAC authentication is implemented here also.

Encryption

The FS encryption routines are in core/tee/tee_fs_key_manager.c. Block encryption protects file data. The algorithm
is 128-bit AES in Cipher Block Chaining (CBC) mode with Encrypted Salt-Sector Initialization Vector (ESSIV), see
CBC-ESSIV for details.
• During OP-TEE initialization, a 128-bit AES Secure Storage Key (SSK) is derived from a Hardware
Unique Key (HUK). It is kept in secure memory and never written to disk. A Trusted Application
Storage Key is derived from the SSK and the TA UUID.
• For each file, a 128-bit encrypted File Encryption Key (FEK) is randomly generated when the file is
created, encrypted with the TSK and stored in the FAT entry for the file.
• Each 256-byte block of data is then encrypted in CBC mode. The initialization vector is obtained by
the ESSIV algorithm, that is, by encrypting the block number with a hash of the FEK. This allows
direct access to any block in the file, as follows:

FEK = AES-Decrypt(TSK, encrypted FEK);


k = SHA256(FEK);
IV = AES-Encrypt(128 bits of k, block index padded to 16 bytes)
Encrypted block = AES-CBC-Encrypt(FEK, IV, block data);
Decrypted block = AES-CBC-Decrypt(FEK, IV, encrypted block data);

SSK, TSK and FEK handling is common with the REE-based secure storage, while the AES CBC block encryption is
used only for RPMB (the REE implementation uses GCM). The FAT is not encrypted.

REE FS hash state

If configured with both CFG_REE_FS=y and CFG_RPMB_FS=y the REE FS will create a special file, dirfile.
db.hash in RPMB which hold a hash representing the state of REE FS.

2.8.7 Important caveats

Warning: Currently no OP-TEE platform is able to support retrieval of the Hardware Unique Key or Chip
ID required for secure operation. For all platforms, a constant key is used, resulting in no protection against

2.8. Secure storage 63


OP-TEE Documentation

decryption, or Secure Storage duplication to other devices. This is because information about how to retrieve key
data from the SoC is considered sensitive by the vendors and it is not freely available.
In OP-TEE, there are APIs for reading keys generically from One-Time-Programmable (OTP) memory. But there
are no existing platform implementations.

To allow Secure Storage to operate securely on your platform, you must define implementations in your platform code
for:

void tee_otp_get_hw_unique_key(struct tee_hw_unique_key *hwkey);

int tee_otp_get_die_id(uint8_t *buffer, size_t len);

These implementations should fetch the key data from your SoC-specific e-fuses, or crypto unit according to the
method defined by your SoC vendor.

2.8.8 References

For more information about secure storage, please see SFO15-503, LAS16-504, SFO17-309 at Presentations and the
TEE Internal Core API specification.

2.9 Trusted Applications

There are two ways to implement Trusted Applications (TAs), Pseudo TAs and user mode TAs. User mode TAs are
full featured Trusted Applications as specified by the GlobalPlatform API TEE specifications, these are simply the
ones people are referring to when they are saying “Trusted Applications” and in most cases this is the preferred type
of TA to write and use.

2.9.1 Pseudo Trusted Applications

These are implemented directly to the OP-TEE core tree in, e.g., core/arch/arm/pta and are built along with
and statically built into the OP-TEE core blob.
The Pseudo Trusted Applications included in OP-TEE already are OP-TEE secure privileged level services hidden
behind a “GlobalPlatform TA Client” API. These Pseudo TAs are used for various purposes such as specific secure
services or embedded tests services.
Pseudo TAs do not benefit from the GlobalPlatform Core Internal API support specified by the GlobalPlatform TEE
specs. These APIs are provided to TAs as a static library each TA shall link against (the “libutee”) and that calls
OP-TEE core service through system calls. As OP-TEE core does not link with libutee, Pseudo TAs can only use the
OP-TEE core internal APIs and routines.
As Pseudo TAs runs at the same privileged execution level as the OP-TEE core code itself and that might or might not
be desirable depending on the use case.
In most cases an unprivileged (user mode) TA is the best choice instead of adding your code directly to the OP-TEE
core. However if you decide your application is best handled directly in OP-TEE core like this, you can look at core/
arch/arm/pta/stats.c as a template and just add your Pseudo TA based on that to the sub.mk in the same
directory.

64 Chapter 2. Architecture documentation


OP-TEE Documentation

2.9.2 User Mode Trusted Applications

User Mode Trusted Applications are loaded (mapped into memory) by OP-TEE core in the Secure World when some-
thing in Rich Execution Environment (REE) wants to talk to that particular application UUID. They run at a lower
CPU privilege level than OP-TEE core code. In that respect, they are quite similar to regular applications running in
the REE, except that they execute in Secure World.
Trusted Application benefit from the GlobalPlatform TEE Internal Core API as specified by the GlobalPlatform TEE
specifications. There are several types of user mode TAs, which differ by the way they are stored.

2.9.3 TA locations

Plain TAs (user mode) can reside and be loaded from various places. There are three ways currently supported in
OP-TEE.

Early TA

The so-called early TAs are virtually identical to the REE FS TAs, but instead of being loaded from the Normal World
file system, they are linked into a special data section in the TEE core blob. Therefore, they are available even before
tee-supplicant and the REE’s filesystems have come up. Please find more details in the early TA commit.

REE filesystem TA

They consist of a cleartext signed ELF file, named from the UUID of the TA and the suffix .ta. They are built
separately from the OP-TEE core boot-time blob, although when they are built they use the same build system, and
are signed with the key from the build of the original OP-TEE core blob.
Because the TAs are signed, they are able to be stored in the untrusted REE filesystem, and tee-supplicant will
take care of passing them to be checked and loaded by the Secure World OP-TEE core. Note that this type of TA isn’t
encrypted.

Secure Storage TA

These are stored in secure storage. The meta data is stored in a database of all installed TAs and the actual binary is
stored encrypted and integrity protected as a separate file in the untrusted REE filesystem (flash). Before these TAs
can be loaded they have to be installed first, this is something that can be done during initial deployment or at a later
stage.
For test purposes the test program xtest can install a TA into secure storage with the command:

$ xtest --install-ta

2.9. Trusted Applications 65


OP-TEE Documentation

66 Chapter 2. Architecture documentation


CHAPTER 3

Build and run

3.1 General build information

Yada yada

3.1.1 Build System

The OP-TEE build system is based on GNU make and CMake. It consists of a main Makefile in the root of the
project together with sub.mk files in all source directories. In addition, some supporting files are used to recursively
process all sub.mk files and generate the build rules.

Name Description
core/core.mk Included from Makefile to build the TEE Core
ta/ta.mk Included from Makefile to create the TA devkit
mk/compile.mk Create rules to make objects from source files
mk/lib.mk Create rules to make a libraries (.a)
mk/subdir.mk Process sub.mk files recursively
mk/config.mk Global configuration variable
core/arch/$(ARCH)/$(ARCH).mk Arch-specific compiler flags
core/arch/$(ARCH)/ Platform-specific compiler flags and configuration variables
plat-$(PLATFORM)/conf.mk
core/arch/$(ARCH)/ Make recipes to link the TEE Core
plat-$(PLATFORM)/link.mk
ta/arch/arm/link.mk Make recipes to link Trusted Applications
ta/mk/ta_dev_kit.mk Main Makefile to be included when building Trusted Applica-
tions
mk/checkconf.mk Utility functions to manipulate configuration variables and gen-
erate a C header file
sub.mk List source files and define compiler flags

make is always invoked from the top-level directory; there is no recursive invocation of make itself.

67
OP-TEE Documentation

Choosing the build target

The target architecture, platform and build directory may be selected by setting environment or make variables
(VAR=value make or make VAR=value).
ARCH - CPU architecture
$(ARCH) is the CPU architecture to be built. Currently, the only supported value is arm for 32-bit or 64-bit
Armv7-A or Armv8-A. Please note that contrary to the Linux kernel, $(ARCH) should not be set to arm64 for
64-bit builds. The ARCH variable does not need to be set explicitly before building either, because the proper in-
struction set is selected from the $(PLATFORM) value. For platforms that support both 32-bit and 64-bit builds,
CFG_ARM64_core=y should be set to select 64-bit and not set (or set to n) to select 32-bit.
Architecture-specific source code belongs to sub-directories that follow the arch/$(ARCH) pattern, such as: core/
arch/arm, lib/libmpa/arch/arm, lib/libutee/arch/arm and so on.
CROSS_COMPILE
$(CROSS_COMPILE) is the prefix used to invoke the (32-bit) cross-compiler toolchain. The default value is
arm-linux-gnueabihf-. This is the variable you want to change in case you want to use ccache to speed
you recompilations:

$ make CROSS_COMPILE="ccache arm-linux-gnueabihf-"

If the build includes a mix of 32-bit and 64-bit code, for instance if you set CFG_ARM64_core=y to build a
64-bit secure kernel, then two different toolchains are used, that are controlled by $(CROSS_COMPILE32) and
$(CROSS_COMPILE64). The default value of $(CROSS_COMPILE32) is the value of CROSS_COMPILE, which
defaults to arm-linux-gnueabihf- as mentioned above. The default value of $(CROSS_COMPILE64) is
aarch64-linux-gnu-. Examples:

# For this example, select HiKey which supports both 32- and 64-bit builds
$ export PLATFORM=hikey

# 1. Build everything 32-bit


$ make

# 2. Same as (1.) but override the toolchain


$ make CROSS_COMPILE="ccache arm-linux-gnueabihf-"

# 3. Same as (2.)
$ make CROSS_COMPILE32="ccache arm-linux-gnueabihf-"

# 4. Select 64-bit secure 'core' (and therefore both 32- and 64-bit
# Trusted Application libraries)
$ make CFG_ARM64_core=y

# 5. Same as (4.) but override the toolchains


$ make CFG_ARM64_core=y \
CROSS_COMPILE32="ccache arm-linux-gnueabihf-" \
CROSS_COMPILE64="ccache aarch64-linux-gnu-"

PLATFORM / PLATFORM_FLAVOR
A platform is a family of closely related hardware configurations. A platform flavor is a variant of such configurations.
When used together they define the target hardware on which OP-TEE will be run.
For instance PLATFORM=stm PLATFORM_FLAVOR=b2260 will build for the ST Microelectronics
96boards/cannes2 board, while PLATFORM=vexpress PLATFORM_FLAVOR=qemu_virt will generate
code for a para-virtualized Arm Versatile Express board running on QEMU.

68 Chapter 3. Build and run


OP-TEE Documentation

For convenience, the flavor may be appended to the platform name with a dash, so make PLATFORM=stm-b2260
is a shortcut for make PLATFORM=stm PLATFORM_FLAVOR=b2260. Note that in both cases the value of
$(PLATFORM) is stm in the makefiles.
Platform-specific source code belongs to core/arch/$(ARCH)/plat-$(PLATFORM), for instance: core/
arch/arm/plat-vexpress or core/arch/arm/plat-stm.
O - output directory
All output files go into a platform-specific build directory, which is by default out/
$(ARCH)-plat-$(PLATFORM).
The output directory has basically the same structure as the source tree. For instance, assuming
ARCH=arm PLATFORM=stm, core/kernel/panic.c will compile into out/arm-plat-stm/core/
kernel/panic.o.
However, some libraries are compiled several times: once or twice for user mode, and once for kernel mode. This
is because they may be used by the TEE Core as well as by the Trusted Applications. As a result, the lib source
directory gives two or three build directories: ta_arm{32,64}-lib and core-lib.
The output directory also has an export-ta_arm{32,64} directory, which contains:
• All the files needed to build Trusted Applications.
– In lib/: libutee.a (the GlobalPlatform Internal API), libutils.a (which implements a part of
the standard C library), and libmpa.a (which implements multiple precision arithmetic and is required
by libutee.a).
– In include/: header files for the above libraries
– In mk/: ta_dev_kit.mk, which is a Make include file with suitable rules to build a TA, and its depen-
dencies
– scripts/sign.py: a Python script used by ta_dev_kit.mk to sign TAs.
– In src: user_ta_header.c: source file to add a suitable header to the Trusted Application (as ex-
pected by the loader code in the TEE Core).
• Some files needed to build host applications (using the Client API), under export-ta_arm{32,64}/
host_include.
Finally, the build directory contains the auto-generated configuration file for the TEE Core: $(O)/include/
generated/conf.h (see below).

Configuration and flags

The following variables are defined in core/arch/$(ARCH)/$(ARCH).mk:


• $(core-platform-aflags), $(core-platform-cflags) and $(core-platform-cppflags)
are added to the assembler / C compiler / preprocessor flags for all source files compiled for TEE Core including
the kernel versions of libmpa.a and libutils.a.
• $(ta_arm{32,64}-platform-aflags), $(ta_arm{32,64}-platform-cflags) and
$(ta_arm{32,64}-platform-cppflags) are added to the assembler / C compiler / preproces-
sor flags when building the user-mode libraries (libutee.a, libutils.a, libmpa.a) or Trusted
Applications.
The following variables are defined in core/arch/$(ARCH)/plat-$(PLATFORM)/conf.mk:
• If $(arm{32,64}-platform-cflags), $(arm{32,64}-platform-aflags) and $(arm{32,
64}-platform-cppflags) are defined their content will be added to $(\*-platform-\*flags)
when they are are initialized in core/arch/$(ARCH)/$(ARCH).mk as described above.

3.1. General build information 69


OP-TEE Documentation

• $(core-platform-subdirs) is the list of the subdirectories that are added to the TEE Core.

Linker scripts

The file core/arch/$(ARCH)/plat-$(PLATFORM)/link.mk contains the rules to link the TEE Core and
perform any related tasks, such as running objdump to produce a dump file. link.mk adds files to the all: target.

Source files

Each directory that contains source files has a file called sub.mk. This makefile defines the source files that should
be included in the build, as well as any subdirectories that should be processed, too. For example:

# core/arch/arm/sm/sub.mk
srcs-y += sm_asm.S
srcs-y += sm.c

# core/sub.mk
subdirs-y += kernel
subdirs-y += mm
subdirs-y += tee
subdirs-y += drivers

The -y suffix is meant to facilitate conditional compilation. See Configuration below.


srcs-y and subdirs-y are often not used together in the same sub.mk, because source files are usually alone in
leaf directories. But this is not a hard rule.
In addition to source files, sub.mk may define compiler flags, include directories and/or configuration variables as
explained below.

Compiler flags

Default compiler flags are defined in mk/compile.mk. Note that platform-specific flags must not appear in this file
which is common to all platforms.
To add flags for a given source file, you may use the following variables in sub.mk:
• cflags-<filename>-y for C files (*.c)
• aflags-<filename>-y for assembler files (*.S)
• cppflags-<filename>-y for both C and assembler
For instance:

# core/lib/libtomcrypt/src/pk/dh/sub.mk
srcs-y += dh.c
cflags-dh.c-y := -Wno-unused-variable

Compiler flags may also be removed, as follows:

# lib/libutils/isoc/newlib/sub.mk
srcs-y += memmove.c
cflags-remove-memmove.c-y += -Wcast-align

Some variables apply to libraries only (that is, when using mk/lib.mk) and affect all the source files that belong to
the library: cppflags-lib-y and cflags-lib-y.

70 Chapter 3. Build and run


OP-TEE Documentation

Include directories

Include directories may be added to global-incdirs-y, in which case they will be accessible from all the
source files and will be copied to export-ta_arm{32,64}/include and export-ta_arm{32,64}/
host_include.
When sub.mk is used to build a library, incdirs-lib-y may receive additional directories that will be used for
that library only.

Configuration variables

Some features may be enabled, disabled or otherwise controlled at compile time through makefile variables. De-
fault values are normally provided in makefiles with the ?= operator so that their value may be easily overridden by
environment variables. For instance:
PLATFORM ?= stm
PLATFORM_FLAVOR ?= default

Some global configuration variables are defined in mk/config.mk, but others may be defined in sub.mk when
then pertain to a specific library for instance.
Variables with the CFG_ prefix are treated in a special way: their value is automatically reflected in the generated
header file $(out-dir)/include/generated/conf.h, after all the included makefiles have been processed.
conf.h is automatically included by the preprocessor when a source file is built.
Depending on their value, variables may be considered either boolean or non-boolean, which affects how they are
translated into conf.h.
Boolean configuration variables
When a configuration variable controls the presence or absence of a feature, y means enabled, while n, empty value or
an undefined variable means disabled. For instance, the following commands are equivalent and would disable feature
CFG_CRYPTO_GCM:
$ make CFG_CRYPTO_GCM=n

$ make CFG_CRYPTO_GCM=

$ CFG_CRYPTO_GCM=n make

$ export CFG_CRYPTO_GCM=n
$ make

Configuration variables may then be used directly in sub.mk to trigger conditional compilation:
# core/lib/libtomcrypt/src/encauth/sub.mk
subdirs-$(CFG_CRYPTO_CCM) += ccm
subdirs-$(CFG_CRYPTO_GCM) += gcm

When a configuration variable is enabled (y), <generated/conf.h> contains a macro with the same name as
the variable and the value 1. If it is disabled, however, no macro definition is output. This allows the C code to use
constructs like:
/* core/lib/libtomcrypt/src/tee_ltc_provider.c */

/* ... */
(continues on next page)

3.1. General build information 71


OP-TEE Documentation

(continued from previous page)

#if defined(CFG_CRYPTO_GCM)
struct tee_gcm_state {
gcm_state ctx; /* the gcm state as defined by LTC */
size_t tag_len; /* tag length */
};
#endif

Non-boolean configuration variables


Configuration variables that are not recognized as booleans are simply output unchanged into <generated/conf.h>.
For instance:

$ make CFG_TEE_CORE_LOG_LEVEL=4

/* out/arm-plat-vexpress/include/generated/conf.h */

#define CFG_TEE_CORE_LOG_LEVEL 4 /* '4' */

Configuration dependencies
Some combinations of configuration variables may not be valid. This should be dealt with by custom checks in
makefiles. mk/checkconf.h provides functions to help detect and deal with such situations.

3.1.2 Buildroot

3.1.3 CMake

3.1.4 GNU Make

3.1.5 OpenEmbedded

3.2 Prerequisites

We believe that you can use any Linux distribution to build OP-TEE, but as maintainers of OP-TEE we are mainly
using Ubuntu-based distributions and to be able to build and run OP-TEE there are a few packages that needs to be
installed to start with. Therefore install the following packages regardless of what target you will use in the end.

$ sudo apt-get install android-tools-adb android-tools-fastboot autoconf \


automake bc bison build-essential cscope curl device-tree-compiler \
expect flex ftp-upload gdisk iasl libattr1-dev libc6:i386 libcap-dev \
libfdt-dev libftdi-dev libglib2.0-dev libhidapi-dev libncurses5-dev \
libpixman-1-dev libssl-dev libstdc++6:i386 libtool libz1:i386 make \
mtools netcat python-crypto python-serial python-wand unzip uuid-dev \
xdg-utils xterm xz-utils zlib1g-dev

3.3 OP-TEE gits

These are the gits considered as the main OP-TEE gits which together makes up the entire TEE solution.

72 Chapter 3. Build and run


OP-TEE Documentation

3.3.1 build

Why this particular git? As it turns out it’s totally possible to put together everything on your own. You can build
all the individual components, os, client, xtest, Linux kernel, TF-A, TianoCore, QEMU, Buildroot etc and put all the
binaries at correct locations and write your own command lines, Makefiles, shell-scripts etc that will work nicely on
the devices you are interested in. If you know how to do that, fine, please go a head. But for newcomers it’s way to
much behind the scenes to be able to setup a working environment. Also, if you for some reason want to run something
in an automated way, then you need something else wrapping it up for you.
With this particular git built.git our goal is to simply to make it easy for newcomers to get started with OP-TEE using
the devices we’ve listed in this document.

git location

https://github.com/OP-TEE/build

Why repo?

We discussed alternatives, initially we started out with having a simple shell-script, that worked to start with, but after
getting more gits in use and support for more devices it started to be difficult to maintain. In the end we ended up
choosing between repo from the Google AOSP project and git submodules. No matter which you choose, there will
always be some person arguing that one is better than the other. For us we decided to use repo. Not directly for
the features itself from repo, but for the ability to simply work with different manifests containing both stable and
non-stable release. Using some tips and tricks you can also speed up setup time significantly. For day to day work
with commits, branches etc we tend to use git commands directly.

Root FS

The root fs in the builds that we cover here are as small as possible and is based on a stripped down Buildroot
configuration adding just enough in the root fs such that one can:
• Boot OP-TEE.
• Run xtest with no regressions.
• Easily add additional developer tools like, strace, valgrind etc.

Note: As a consequence of enabling “just enough”, it is likely that non-UART based enviroments won’t work out of
the box. I.e., if you try to boot up an enviroment using HDMI and connect keyboards and other devices it is likely that
things will not work. To make them work, you probably need to rebuild Linux kernel with correct drivers/frameworks
enabled and in addition to that enable binaries/daemons in Buildroot that might be necessary (user space tools and
drivers).

How do I build using AOSP / OpenEmbedded?

For guides how to build AOSP, please refer to our AOSP page. For OpenEmbedded we have no guide ready, however
there are teams in Linaro who are building OP-TEE using OpenEmbedded. If you want to get in contact with them,
please reach out to us (see Contact).

3.3. OP-TEE gits 73


OP-TEE Documentation

Platforms supported by build.git

Below is a table showing the platforms supported by build.git. OP-TEE as such supports many more platforms, but
since quite a few of the other platforms are maintained by people outside Linaro or are using a special setup, we
encourage you to talk to the maintainer of that platform directly if you have build related questions etc. Please see the
MAINTAINERS file for contact information.

Platform Composite flag Publicly available?


ARM Juno Board PLATFORM=vexpress-juno Yes
ARM Foundation FVP PLATFORM=vexpress-fvp Yes
HiKey Kirin 620 PLATFORM=hikey Yes
HiKey 960 PLATFORM=hikey-hikey960 Yes
MediaTek MT8173 EVB Board PLATFORM=mediatek-mt8173 No
Poplar PLATFORM=poplar Yes
QEMU PLATFORM=vexpress-qemu_virt Yes
QEMUv8 PLATFORM=vexpress-qemu_armv8a Yes
Raspberry Pi 3 PLATFORM=rpi3 Yes
Texas Instruments DRA7xx PLATFORM=ti-dra7xx Yes
Texas Instruments AM57xx PLATFORM=ti-am57xx Yes
Texas Instruments AM43xx PLATFORM=ti-am43xx Yes

Manifests

Current version

Here is a list of manifests for the devices currently supported in build.git. With these you will get a setup
containing the all necessary software components to run OP-TEE on the chosen device. Beware that this will run
latest available on OP-TEE gits meaning that if you re-sync then you will most likely get new commits. If you need a
stable/tagged version with non-moving gits, then please refer to the next section instead.

Target Manifest xml Device documentation


AM43xx am43xx.xml TI devices
AM57xx am57xx.xml TI devices
ARM Juno board juno.xml Juno
DRA7xx dra7xx.xml TI devices
FVP fvp.xml FVP
HiKey 960 hikey960.xml HiKey 960
HiKey hikey.xml HiKey 6620
Poplar Debian poplar.xml
QEMU default.xml QEMU
QEMUv8 qemu_v8.xml QEMUv8
Raspberry Pi 3 rpi3.xml Raspberry Pi 3

Stable releases

Starting from OP-TEE v3.1 you can check out stable releases by using the same manifests as for current version
above, but with the difference that you also need to specify a branch where the name corresponds to the release
version. I.e., when we are doing releases we are creating a branch with a name corresponding to the release version.
So, let’s for example say that you want to checkout a stable OP-TEE v3.2 for Raspberry Pi 3, then you do like this
instead of what is mentioned further down in section 7.3 (note the -b 3.2.0):

74 Chapter 3. Build and run


OP-TEE Documentation

TODO
...
$ repo init -u https://github.com/OP-TEE/manifest.git -m rpi3.xml -b 3.2.0
...

Stable releases prior to OP-TEE v3.1 (v1.0.0 to v3.0.0)

Before OP-TEE v3.1 we used to have separate xml-manifest files for the stable builds. If you for some reason need
an older stable release, then you can use the xyz_stable.xml file corresponding to your device. The way to
init repo is almost the same as described above, the major difference is the name of manifest being referenced (-m
xyz_stable.xml) and that we are referring to a tag instead of a branch (-b refs/tags/MAJOR.MINOR.
PATCH). So as an example, if you need to setup the 2.1.0 stable release for HiKey, then you would do like this
instead of what is mentioned further down in section 7.3
TODO
...
repo init -u https://github.com/OP-TEE/manifest.git -m hikey_stable.xml -b refs/tags/
˓→2.1.0

...

Here is a list of targets and the names of the stable manifests files which were supported by older releases:

Target Stable manifest xml


AM43xx am43xx_stable.xml
AM57xx am57xx_stable.xml
ARM Juno board juno_stable.xml
DRA7xx dra7xx_stable.xml
FVP fvp_stable.xml
HiKey 960 hikey960_stable.xml
HiKey Debian hikey_debian_stable.xml
HiKey hikey_stable.xml
MTK8173 mt8173-evb_stable.xml
QEMU default_stable.xml
QEMUv8 qemu_v8_stable.xml
Raspberry Pi 3 rpi3_stable.xml

Get and build the solution

Below we will describe the general way of how to get the source, build the solution and how to run xtest on the device.
For device specific instructions, please see the links in the table in the Current version section.

Step 1 - Prerequisites

Install prerequisites according to the Prerequisites page.

Step 2 - Install Android repo

Note that here you don’t install a huge SDK, it’s simply a Python script that you download and put in your $PATH,
that’s it. Exactly how to “install” repo, could be found in the Google repo pages, so follow those instructions before

3.3. OP-TEE gits 75


OP-TEE Documentation

continuing.

Step 3 - Get the source code

Choose the manifest corresponding to the platform you intend to use (see the table in Current version. For example, if
you intend to use Raspberry Pi3, then at line 3 below, ${TARGET}.xml shall be rpi3.xml.

1 $ mkdir -p $HOME/devel/optee
2 $ cd $HOME/devel/optee
3 $ repo init -u https://github.com/OP-TEE/manifest.git -m ${TARGET}.xml [-b ${BRANCH}]
4 $ repo sync

Note: The repo sync step will take some time if you aren’t referencing an existing tree (see the Tips and Tricks
section).

Step 4 - Get the toolchains

In OP-TEE we’re using different toolchains for different targets (depends on ARMv7-A ARMv8-A 64/32bit solutions).
In any case start by downloading the toolchains by:

$ cd build
$ make toolchains

Step 5 - Build the solution

We’ve configured our repo manifests, so that repo will always automatically symlink the Makefile to the correct
device specific makefile, that means that you simply start the build by running:

$ make

This step will also take some time, but you can speed up subsequent builds by enabling ccache (again see Tips and
Tricks).

Step 6 - Flash the device

On non-emulated solutions, you will need to flash the software in some way. We’ve tried to “hide” that under the
following make target:

$ make flash

But, since some devices are trickier to flash than others, please see the Device specific build information. See this just
as a general instruction.

Step 7 - Boot up the device

This is device specific (see Device specific build information).

76 Chapter 3. Build and run


OP-TEE Documentation

Step 8 - Load tee-supplicant

On some solutions tee-supplicant is already running (check by running $ ps aux | grep tee-supplicant)
on others not. If it’s not running, then start it by running:

$ tee-supplicant &

Note: If you’ve built using our manifest you should not need to modprobe any OP-TEE/TEE kernel driver since it’s
built into the kernel in all our setups.

Step 9 - Run xtest

The entire xtest test suite has been deployed when you we’re running $ make run in previous step, i.e, in general
there is no need to copy any binaries manually. Everything has been put into the Root FS automatically. So, to run
xtest, you simply type:

$ xtest

If there are no regressions / issues found, xtest should end with something like this:

...
+-----------------------------------------------------
23476 subtests of which 0 failed
67 test cases of which 0 failed
0 test case was skipped
TEE test application done!

Tips and Tricks

Reference existing project to speed up repo sync

Doing a repo init, repo sync from scratch can take a fair amount of time. The main reason for that is simply
because of the size of some of the gits we are using, like for the Linux kernel and EDK2. With repo you can reference
an existing forest and by doing so you can speed up repo sync to taking 20 seconds instead of an hour. The way to do
this are as follows.
1. Start by setup a clean forest that you will not touch, in this example, let us call that optee-ref and put that
under for $HOME/devel/optee-ref. This step will take somewhere between 15- to 45 minutes, depending
on your connection speed to internet.
2. Then setup a cronjob (crontab -e) that does a repo sync in this folder particular folder once a night (that
is more than enough).
3. Now you should setup your actual tree which you are going to use as your working tree. The way to do this
is almost the same as stated in the instructions above, the only difference is that you reference the other local
forest when running repo init, like this

$ repo init -u https://github.com/OP-TEE/manifest.git --reference /home/jbech/


˓→devel/optee-ref

4. The rest is the same above, but now it will only take a couple of seconds to clone a forest.

3.3. OP-TEE gits 77


OP-TEE Documentation

Normally ‘1’ and ‘2’ above is something you will only do once. Also if you ignore step ‘2’, then you will still get the
latest from official git trees, since repo will also check for updates that aren’t at the local reference.

Use ccache

ccache is a tool that caches build object-files etc locally on the disc and can speed up build time significantly in
subsequent builds. On Debian-based systems (Ubuntu, Mint etc) you simply install it by running:

$ sudo apt-get install ccache

The makefiles in build.git are configured to automatically find and use ccache if ccache is installed on your system, so
other than having it installed you don’t have to think about anything.

3.3.2 manifest

This page contains a couple of guidelines and rules that we want to try to follow when it comes to managing the
manifests.

git location

https://github.com/OP-TEE/manifest

Remotes

Since most of our projects can be found on GitHub, we are using that as the main remote. If you need to include other
remotes for some reason, then that is OK, but please double check of there is any maintained (and preferably official)
mirror for the project at GitHub before adding a new remote.

Sections

To have some kind of structure of the files, we have split them up in three sections, one for pure OP-TEE gits, one for
OP-TEE supporting gits found at linaro-swg and then a third, misc section where everything else can be found. I.e.,
a template looks like this (this also includes the default remote for clarity):

<?xml version="1.0" encoding="UTF-8"?>


<manifest>
<remote name="github" fetch="https://github.com" />

<default remote="github" revision="master" />

<!-- OP-TEE gits -->


<!-- linaro-swg gits -->
<!-- Misc gits -->
</manifest>

Project XML elements

All <projects ... > lines should be on the format as shown below with the attributes in this order. The reason
for this is to have it uniformly done across all manifests and that it will make it easier when comparing various versions

78 Chapter 3. Build and run


OP-TEE Documentation

of manifests with diff tools. All three attributes are mandatory. The only exception is revision which does not
have to be stated if it is master that we are tracking.

<project path="name_and_path_on_disk" name="upstream_name.git" revision="git_revsion"


˓→/>

Alphabetic order

Within each of the three sections, all <project ... > lines shall be sorted in alphabetic order (this is again for
making it easier to diff manifests). The only expection here is build.git which uses the linkfile element.
Having that at the end makes it look cleaner.

Additional XML attributes

If you are using another remote than the default, then that should come after the revision attribute (this is true for
all attributes other than the path, name and revision).

Alignment of XML attributes

The three mandatory XML attributes path, name and revision should be column aligned. Alignment of additional
XML attributes are optional.

When to use clone-depth=”1”?

With clone-depth="1" you are telling repo and git that you only want a certain commit and not the entire git
log history. You can only use this under two conditions and that is when revision is either a branch or a tag. Pure
SHA-1's does not work and will even raise repo and git sync errors in some cases. So, the rules are, if you use
either revision="refs/tags/my_tag" or revision="refs/heads/my_branch", then you shall add
clone-depth="1" right after the revision attribute.

Spaces or tabs?

Only use spaces!

Example

Here is an example showing the basis for an OP-TEE manifest. The names are fictive etc, but it describes everything
said above.

<?xml version="1.0" encoding="UTF-8"?>


<manifest>
<remote name="github" fetch="https://github.com" />
<remote name="other" fetch="https://someotherlocation.com" />

<default remote="github" revision="master" />

<!-- OP-TEE gits -->


<project path="optee_abc" name="OP-TEE/optee_abc.git" />
<project path="optee_def" name="OP-TEE/optee_def.git" />

(continues on next page)

3.3. OP-TEE gits 79


OP-TEE Documentation

(continued from previous page)


<!-- linaro-swg gits -->
<project path="lswg_abc" name="linaro-swg/lswg-abc.git" revision=
˓→"aaaabbbbcccc93e64c2fdd6ae8b0be14a8c45719" />

<project path="lswg_def" name="linaro-swg/lswg-def.git" revision=


˓→"ddddeeeeffff83e64c2fdd6ae8b0be14a8c45719" />

<!-- Misc gits -->


<project path="my_other" name="my_other.git" revision="refs/tags/
˓→2017.11" clone-depth="1" remote="other" />

</manifest>

3.3.3 optee_client

optee_client.git is used to Yada yada

3.3.4 optee_examples

optee_examples.git is used to Yada yada

3.3.5 optee_os

optee_os.git is used to Yada yada

3.3.6 optee_test

optee_test.git is used to Yada yada

3.4 Device specific build information

3.4.1 FVP

3.4.2 HiKey 6620

Foo bar

3.4.3 HiKey 960

Foo bar

80 Chapter 3. Build and run


OP-TEE Documentation

3.4.4 Juno

3.4.5 QEMU

3.4.6 QEMUv8

3.4.7 Raspberry Pi 3

Foo bar

3.4.8 TI devices

DRA7xx

AM57xx

AM43xx

3.5 Linux kernel TEE

TEE framework and driver.

3.6 AOSP

AOSP builds yada yada

3.6.1 General builds

3.6.2 Keymaster and GateKeeper

3.5. Linux kernel TEE 81


OP-TEE Documentation

82 Chapter 3. Build and run


CHAPTER 4

Debugging techniques

4.1 Abort dumps / call stack

When OP-TEE encounters a serious error condition, it prints diagnostic information to the secure console. The mes-
sage contains a call stack if CFG_UNWIND=y (enabled by default).
The following errors will trigger a dump:
• Data or prefetch abort exception in the TEE core (kernel mode) or in a TA (user mode),
• When a user-mode Trusted Application panics, either by calling TEE_Panic() directly or due to some error
detected by the TEE Core Internal API,
• When the TEE core detects a fatal error and decides to hang the system because there is no way to proceed
safely (core panic).
The messages look slightly different depending on:
• Whether the error is an exception or a panic,
• The exception/privilege level when the exception occurred (PL0/EL0 if a user mode Trusted Application was
running, PL1/EL1 if it was the TEE core),
• Whether the TEE and TA are 32 or 64 bits,
• The exact type of exception (data or prefetch abort, translation fault, read or write permission fault, alignment
errors etc).
Here is an example of a panic in a 32-bit Trusted Application, running on a 32-bit TEE core (QEMU):

E/TC:0 TA panicked with code 0x0


E/TC:0 Status of TA 484d4143-2d53-4841-3120-4a6f636b6542 (0xe07ba50) (active)
E/TC:0 arch: arm load address: 0x101000 ctx-idr: 1
E/TC:0 stack: 0x100000 4096
E/TC:0 region 0: va 0x100000 pa 0xe31d000 size 0x1000 flags rw-
E/TC:0 region 1: va 0x101000 pa 0xe300000 size 0xf000 flags r-x
E/TC:0 region 2: va 0x110000 pa 0xe30f000 size 0x3000 flags r--
(continues on next page)

83
OP-TEE Documentation

(continued from previous page)


E/TC:0 region 3: va 0x113000 pa 0xe312000 size 0xb000 flags rw-
E/TC:0 region 4: va 0 pa 0 size 0 flags ---
E/TC:0 region 5: va 0 pa 0 size 0 flags ---
E/TC:0 region 6: va 0 pa 0 size 0 flags ---
E/TC:0 region 7: va 0 pa 0 size 0 flags ---
E/TC:0 Call stack:
E/TC:0 0x001044a8
E/TC:0 0x0010ba59
E/TC:0 0x00101093
E/TC:0 0x001013ed
E/TC:0 0x00101545
E/TC:0 0x0010441b
E/TC:0 0x00104477
D/TC:0 user_ta_enter:452 tee_user_ta_enter: TA panicked with code 0x0
D/TC:0 tee_ta_invoke_command:649 Error: ffff3024 of 3
D/TC:0 tee_ta_close_session:402 tee_ta_close_session(0xe07be98)
D/TC:0 tee_ta_close_session:421 Destroy session
D/TC:0 tee_ta_close_session:447 Destroy TA ctx

The above dump was triggered by the TA when entering an irrecoverable error ending up in a TEE_Panic(0) call.
OP-TEE provides a helper script called symbolize.py to facilitate the analysis of such issues. It is located in the
OP-TEE OS source tree in scripts/symbolize.py and is also copied to the TA development kit. Whenever you
are confronted with an error message reporting a serious error and containing a "Call stack:" line, you may use
the symbolize script.
symbolize.py reads its input from stdin and writes extended debug information to stdout. The -d (directo-
ries) option tells the script where to look for TA ELF file(s) (<uuid>.stripped.elf) or for tee.elf (the TEE
core). Please refer to symbolize.py --help for details.
Typical output:

$ cat dump.txt | ./optee_os/scripts/symbolize.py -d ./optee_examples/*/ta


# (or run the script, copy and paste the dump, then press Ctrl+D)
E/TC:0 TA panicked with code 0x0
E/TC:0 Status of TA 484d4143-2d53-4841-3120-4a6f636b6542 (0xe07ba50) (active)
E/TC:0 arch: arm load address: 0x101000 ctx-idr: 1
E/TC:0 stack: 0x100000 4096
E/TC:0 region 0: va 0x100000 pa 0xe31d000 size 0x1000 flags rw-
E/TC:0 region 1: va 0x101000 pa 0xe300000 size 0xf000 flags r-x .ta_head .text .
˓→rodata

E/TC:0 region 2: va 0x110000 pa 0xe30f000 size 0x3000 flags r-- .rodata .ARM.extab .
˓→ARM.extab.text.utee_panic .ARM.extab.text.__aeabi_ldivmod .ARM.extab.text.__aeabi_

˓→uldivmod .ARM.exidx .got .dynsym .rel.got .dynamic .dynstr .hash .rel.dyn

E/TC:0 region 3: va 0x113000 pa 0xe312000 size 0xb000 flags rw- .data .bss
E/TC:0 region 4: va 0 pa 0 size 0 flags ---
E/TC:0 region 5: va 0 pa 0 size 0 flags ---
E/TC:0 region 6: va 0 pa 0 size 0 flags ---
E/TC:0 region 7: va 0 pa 0 size 0 flags ---
E/TC:0 Call stack:
E/TC:0 0x001044a8 utee_panic at optee_os/lib/libutee/arch/arm/utee_syscalls_a32.S:74
E/TC:0 0x0010ba59 TEE_Panic at optee_os/lib/libutee/tee_api_panic.c:35
E/TC:0 0x00101093 hmac_sha1 at optee_examples/hotp/ta/hotp_ta.c:63
E/TC:0 0x001013ed get_hotp at optee_examples/hotp/ta/hotp_ta.c:171
E/TC:0 0x00101545 TA_InvokeCommandEntryPoint at optee_examples/hotp/ta/hotp_ta.c:225
E/TC:0 0x0010441b entry_invoke_command at optee_os/lib/libutee/arch/arm/user_ta_
˓→entry.c:207

(continues on next page)

84 Chapter 4. Debugging techniques


OP-TEE Documentation

(continued from previous page)


E/TC:0 0x00104477 __utee_entry at optee_os/lib/libutee/arch/arm/user_ta_entry.c:235
D/TC:0 user_ta_enter:452 tee_user_ta_enter: TA panicked with code 0x0 ???
D/TC:0 tee_ta_invoke_command:649 Error: ffff3024 of 3
D/TC:0 tee_ta_close_session:402 tee_ta_close_session(0xe07be98)
D/TC:0 tee_ta_close_session:421 Destroy session
D/TC:0 tee_ta_close_session:447 Destroy TA ctx

The Python script uses several tools from the GNU Binutils package to perform the following tasks:
1. Translate the call stack addresses into function names, file names and line numbers.
2. Convert the abort address to a symbol plus some offset and/or an ELF section name plus some offset.
3. Print the names of the ELF sections contained in each memory region of a TA.
Note that to successfully run symbolize.py you must also make your toolchain visible on the PATH (i.e., export
PATH=<my-toolchain-path>/bin:$PATH).

4.2 Benchmark framework

Due to its nature, OP-TEE is being a solution spanning over several architectural layers, where each layer includes its
own complex parts. For further optimizations of performance, there is a need of tool which will provide detailed and
precise profiling information for each layer.
It is necessary to receive latency values for:
• The roundtrip time for going from a client application in normal world, down to a Trusted Application and back
again.
• Detailed information for amount of time taken to go through each layer:
– libTEEC -> Linux OP-TEE kernel driver
– Linux OP-TEE kernel driver -> OP-TEE OS Core
– OP-TEE OS Core -> TA entry point (not supported yet)
– The same way back

4.2.1 Implementation details

Design overview

Benchmark framework consists of such components:


1. Benchmark Client Application (CA): a dedicated client application, which is responsible for allocating times-
tamp circular buffers, registering these buffers in the Benchmark PTA and consuming all timestamp data gener-
ated by all OP-TEE layers. Finally, it puts timestamp data into appropriate file with .ts extension. Additional
details can be found here here optee_benchmark.
2. Benchmark Pseudo Trusted Application (PTA): which owns all per-cpu circular non-secure buffers from a
shared memory. Benchmark PTA must be invoked (by a CA) to register the timestamp circular buffers. In turn,
the Benchmark PTA invokes the OP-TEE Linux driver (through some RPC mean) to register this circular buffers
in the Linux kernel layer.
3. libTEEC and Linux kernel OP-TEE driver include functionality for handling timestamp buffer registration
requests from the Benchmark PTA.

4.2. Benchmark framework 85


OP-TEE Documentation

When the benchmark is enabled, all OP-TEE layers (libTEEC, Linux kernel OP-TEE driver, OP-TEE OS core) do fill
the registered timestamp circular buffer with timestamp data for all invocation requests on condition that the circular
buffer is allocated/registered.

Timestamp source

Arm Performance Monitor Units are used as the main source of timestamp values. The reason why this technology
was chosen is that it is supported on all Armv7-A/Armv8-A cores. Besides it can provide precise pre-cpu cycle counter
values, it is possible to enable EL0 access to all events, so usermode applications can directly read cpu counter values
from coprocessor registers, achieving minimal latency by avoiding additional syscalls to EL1 core.
Besides CPU cycle counter values, timestamp by itself contains also information about:
• Executing CPU core index
• OP-TEE layer id, where this timestamp was obtained from
• Program counter value when timestamp was logged, which can be used for getting a symbol name (a filename
and line number)

86 Chapter 4. Debugging techniques


OP-TEE Documentation

4.2. Benchmark framework 87


OP-TEE Documentation

Call sequence diagram

88 Chapter 4. Debugging techniques


OP-TEE Documentation

4.2.2 Running benchmarks

Benchmark application usage

Before using Benchmark framework, OP-TEE should be rebuild with CFG_TEE_BENCHMARK flag enabled.
$ make CFG_TEE_BENCHMARK=y

Then, regular CA and its params should be by-passed to optee_benchmark CA.


$ benchmark client_app [client_app params]

When client_app finishes its execution, optee_benchmark will generate <client_app>.ts timestamp data file in
the same directory, where CA is stored.

Adding custom timestamps

Currently, timestamping is done only for InvokeCommand calls, but it’s also possible to choose custom places in
the supported OP-TEE layers. To add timestamp storing command to custom c source file:
1. Include appropriate header:
• OP-TEE OS Core: bench.h
• Linux kernel OP-TEE module: optee_bench.h
• libTEEC: teec_benchmark.h
2. Invoke bm_timestamp() (for linux kmod use optee_bm_timestamp()) in the function, where you want
to put timestamp from.

Analyzing results

Will be added soon.

4.2.3 Limitations and further steps

• Implementation of application which will analyze timestamp data and provide statistics for different types of
calls providing avg/min/max values (both CPU cycles and time values).
• Add support for all platforms, where OP-TEE is supported.
• Adding support of S-EL0 timestamping.
• Attaching additional payload information to each timestamp, for example, session.
• Timestamping within interrupt context in the OP-TEE OS Core.

4.3 Gprof

This describes to do profiling of user Trusted Applications with gprof.


The configuration option CFG_TA_GPROF_SUPPORT=y enables OP-TEE to collect profiling information from
Trusted Applications running in user mode and compiled with -pg. Once collected, the profiling data are formatted
in the gmon.out format and sent to tee-supplicant via RPC, so they can be saved to disk and later processed
and displayed by the standard gprof tool.

4.3. Gprof 89
OP-TEE Documentation

4.3.1 Usage

• Build OP-TEE OS with CFG_TA_GPROF_SUPPORT=y. You may also set CFG_ULIBS_GPROF=y to instru-
ment the user TA libraries (libutee, libutils, libmpa).
• Build user TAs with -pg, for instance using: CFLAGS_ta_arm32=-pg or CFLAGS_ta_arm64=-pg. Note
that instrumented TAs have a larger .bss section. The memory overhead is 1.36 times the .text size for 32-
bit TAs, and 1.77 times for 64-bit ones (refer to the TA linker script for details: ta/arch/arm/ta.ld.S).
• Run the application normally. When the last session exits, tee-supplicant will write profiling
data to /tmp/gmon-<ta_uuid>.out. If the file already exists, a number is appended, such as:
gmon-<ta_uuid>.1.out.
• Run gprof on the TA ELF file and profiling output: gprof <ta_uuid>.elf gmon-<ta_uuid>.out

4.3.2 Implementation

Part of the profiling is implemented in libutee. Another part is done in the TEE core by a pseudo-TA (core/arch/
arm/sta/gprof.c). Two types of data are collected:
1. Call graph information
• When TA source files are compiled with the -pg switch, the compiler generates extra code into each
function prologue to call the instrumentation entry point (__gnu_mcount_nc or _mcount de-
pending on the architecture). Each time an instrumented function is called, libutee records a pair of
program counters (one is the caller and the other one is the callee) as well as the number of times this
specific arc of the call graph has been invoked.
2. PC distribution over time
• When an instrumented TA starts, libutee calls the pseudo-TA to start PC sampling for the current
session. Sampling data are written into the user-space buffer directly by the TEE core.
• Whenever the TA execution is interrupted, the TEE core records the current program counter value
and builds a histogram of program locations (i.e., relative amount of time spent for each value of the
PC). This is later used by the gprof tool to derive the time spent in each function. The sampling rate,
which is assumed to be roughly constant, is computed by keeping track of the time spent executing
user TA code and dividing the number of interrupts by the total time.
• The profiling buffer into which call graph and sampling data are recorded is allocated in the TA’s
.bss section. Some space is reserved by the linker script, only when the TA is instrumented.

4.4 GDB

This is how to use GDB

4.5 JTAG

This is how to use JTAG

4.5.1 OpenOCD

This is how to use OpenOCD

90 Chapter 4. Debugging techniques


CHAPTER 5

FAQ - Frequently Asked Questions

5.1 Frequently Asked Questions

5.1.1 Source code

My sub source section

5.1.2 Building

5.1.3 License

5.1.4 Contribution, Promotion and security flaws

5.1.5 Certification and security reviews

5.1.6 Interfaces

5.1.7 Architecture

5.1.8 Trusted Applications

5.1.9 Testing

91

You might also like