Joakim Bech | 8e5c5b3 | 2018-10-25 08:18:32 +0200 | [diff] [blame] | 1 | .. _porting_guidelines: |
| 2 | |
| 3 | ################## |
| 4 | Porting guidelines |
| 5 | ################## |
| 6 | This document serves a dual purpose: |
| 7 | |
| 8 | * Serve as a base for getting OP-TEE up and running on a new device with initial |
| 9 | xtest validation passing. This is the first part of this document (section 2). |
| 10 | |
| 11 | * Highlight the missing pieces if you intend to make a real secure product, that |
| 12 | is what the second part of this document is about. |
| 13 | |
| 14 | We are trying our best to implement full end to end security in OP-TEE in a |
| 15 | generic way, but due to the nature of devices being different, NDA etc, it is |
| 16 | not always possible for us to do so and in those cases, we most often try to |
| 17 | write a generic API, but we will just stub the code. This porting guideline |
| 18 | highlights the missing pieces that must be addressed in a real secure consumer |
| 19 | device. Hopefully we will sooner or later get access to devices where we at |
| 20 | least can make reference implementations publicly available to everyone for the |
| 21 | missing pieces we are talking about here. |
| 22 | |
| 23 | .. _add_a_new_platform: |
| 24 | |
| 25 | Add a new platform |
| 26 | ****************** |
| 27 | The first thing you need to do after you have decided to port OP-TEE to another |
| 28 | device is to add a new platform device. That can either be adding a new platform |
| 29 | variant (``PLATFORM_FLAVOR``) if it is a device from a family already supported, |
| 30 | or it can be a brand new platform family (``PLATFORM``). Typically this initial |
| 31 | setup involve configuring UART, memory addresses etc. For simplicity let us call |
| 32 | our fictive platform for "gendev" just so we have something to refer to when |
| 33 | writing examples further down. |
| 34 | |
| 35 | core/arch/arm |
| 36 | ================= |
| 37 | In ``core/arch/arm`` you will find all the currently supported devices. That is |
| 38 | where you are supposed to add a new platform or modify an existing one. |
| 39 | Typically you will find this set of files in a specific platform folder: |
| 40 | |
| 41 | .. code-block:: bash |
| 42 | |
| 43 | $ ls |
| 44 | conf.mk main.c platform_config.h sub.mk |
| 45 | |
| 46 | So for the gendev platform it means that the files should be placed in this |
| 47 | folder: |
| 48 | |
| 49 | .. code-block:: bash |
| 50 | |
| 51 | core/arch/arm/plat-gendev |
| 52 | |
| 53 | **conf.mk** |
| 54 | |
| 55 | This is the device specific makefile where you define configurations unique to |
| 56 | your platform. This mainly comprises two things: - OP-TEE configuration |
| 57 | variables (``CFG_``), which may be assigned values in two ways. ``CFG_FOO ?= |
| 58 | bar`` should be used to provide a default value that may be modified at compile |
| 59 | time. On the other hand, variables that must be set to some value and cannot be |
| 60 | modified should be set by: ``$(call force,CFG_FOO,bar)``. - Compiler flags for |
| 61 | the TEE core, the user mode libraries and the Trusted Applications, which may be |
| 62 | added to macros used by the build system. Please see `Platform-specific |
| 63 | configuration and flags`_ in the build system documentation. |
| 64 | |
| 65 | It is recommended to use a existing platform configuration file as a starting |
| 66 | point. For instance, `core/arch/arm/plat-hikey/conf.mk`_. |
| 67 | |
| 68 | The platform ``conf.mk`` file should at least define the default platform flavor |
| 69 | for the platform, the core configurations (architecture and number of cores), |
| 70 | the main configuration directives (generic boot, arm trusted firmware support, |
| 71 | generic time source, console driver, etc...) and some platform default |
| 72 | configuration settings. |
| 73 | |
| 74 | .. code-block:: make |
| 75 | |
| 76 | PLATFORM_FLAVOR ?= hikey |
| 77 | |
| 78 | include core/arch/arm/cpu/cortex-armv8-0.mk |
| 79 | |
| 80 | $(call force,CFG_TEE_CORE_NB_CORE,8) |
| 81 | $(call force,CFG_GENERIC_BOOT,y) |
| 82 | $(call force,CFG_PL011,y) |
| 83 | $(call force,CFG_PM_STUBS,y) |
| 84 | $(call force,CFG_SECURE_TIME_SOURCE_CNTPCT,y) |
| 85 | $(call force,CFG_WITH_ARM_TRUSTED_FW,y) |
| 86 | $(call force,CFG_WITH_LPAE,y) |
| 87 | |
| 88 | ta-targets = ta_arm32 |
| 89 | ta-targets += ta_arm64 |
| 90 | |
| 91 | CFG_NUM_THREADS ?= 8 |
| 92 | CFG_CRYPTO_WITH_CE ?= y |
| 93 | CFG_WITH_STACK_CANARIES ?= y |
| 94 | CFG_CONSOLE_UART ?= 3 |
| 95 | CFG_DRAM_SIZE_GB ?= 2 |
| 96 | |
| 97 | **main.c** |
| 98 | |
| 99 | This platform specific file will contain power management handlers and code |
| 100 | related to the UART. We will talk more about the information related to the |
| 101 | handlers further down in this document. For our gendev device it could look like |
| 102 | this (here we are excluding the necessary license header to save some space): |
| 103 | |
| 104 | .. code-block:: c |
| 105 | |
| 106 | #include <console.h> |
| 107 | #include <drivers/serial8250_uart.h> |
| 108 | #include <kernel/generic_boot.h> |
| 109 | #include <kernel/panic.h> |
| 110 | #include <kernel/pm_stubs.h> |
| 111 | #include <mm/core_mmu.h> |
| 112 | #include <platform_config.h> |
| 113 | #include <stdint.h> |
| 114 | #include <tee/entry_fast.h> |
| 115 | #include <tee/entry_std.h> |
| 116 | |
| 117 | static void main_fiq(void) |
| 118 | { |
| 119 | panic(); |
| 120 | } |
| 121 | |
| 122 | static const struct thread_handlers handlers = { |
| 123 | .std_smc = tee_entry_std, |
| 124 | .fast_smc = tee_entry_fast, |
| 125 | .nintr = main_fiq, |
| 126 | .cpu_on = cpu_on_handler, |
| 127 | .cpu_off = pm_do_nothing, |
| 128 | .cpu_suspend = pm_do_nothing, |
| 129 | .cpu_resume = pm_do_nothing, |
| 130 | .system_off = pm_do_nothing, |
| 131 | .system_reset = pm_do_nothing, |
| 132 | }; |
| 133 | |
| 134 | const struct thread_handlers *generic_boot_get_handlers(void) |
| 135 | { |
| 136 | return &handlers; |
| 137 | } |
| 138 | |
| 139 | /* |
| 140 | * Register the physical memory area for peripherals etc. Here we are |
| 141 | * registering the UART console. |
| 142 | */ |
| 143 | register_phys_mem(MEM_AREA_IO_NSEC, CONSOLE_UART_BASE, SERIAL8250_UART_REG_SIZE); |
| 144 | |
| 145 | static struct serial8250_uart_data console_data; |
| 146 | |
| 147 | void console_init(void) |
| 148 | { |
| 149 | serial8250_uart_init(&console_data, CONSOLE_UART_BASE, |
| 150 | CONSOLE_UART_CLK_IN_HZ, CONSOLE_BAUDRATE); |
| 151 | register_serial_console(&console_data.chip); |
| 152 | } |
| 153 | |
| 154 | **platform_config.h** |
| 155 | |
| 156 | This is a mandatory header file for every platform, since there are several |
| 157 | files relaying upon the existence of this particular file. This file is where |
| 158 | you will find the major differences between different platforms, since this is |
| 159 | where you do the memory configuration, define base addresses etc. we are going |
| 160 | to list a few here, but it probably makes more sense to have a look at the |
| 161 | already existing ``platform_config.h`` files for the other platforms. Our |
| 162 | fictive gendev could look like this: |
| 163 | |
| 164 | .. code-block:: c |
| 165 | |
| 166 | #ifndef PLATFORM_CONFIG_H |
| 167 | #define PLATFORM_CONFIG_H |
| 168 | |
| 169 | /* Make stacks aligned to data cache line length */ |
| 170 | #define STACK_ALIGNMENT 64 |
| 171 | |
| 172 | /* 8250 UART */ |
| 173 | #define CONSOLE_UART_BASE 0xcafebabe /* UART0 */ |
| 174 | #define CONSOLE_BAUDRATE 115200 |
| 175 | #define CONSOLE_UART_CLK_IN_HZ 19200000 |
| 176 | |
| 177 | /* Optional: when used with CFG_WITH_PAGER, defines the device SRAM */ |
| 178 | #define TZSRAM_BASE 0x3F000000 |
| 179 | #define TZSRAM_SIZE (200 * 1024) |
| 180 | |
| 181 | /* Mandatory main secure RAM usually DDR */ |
| 182 | #define TZDRAM_BASE 0x60000000 |
| 183 | #define TZDRAM_SIZE (32 * 1024 * 1024) |
| 184 | |
| 185 | /* Mandatory TEE RAM location and core load address */ |
| 186 | #define TEE_RAM_START TZDRAM_BASE |
| 187 | #define TEE_RAM_PH_SIZE TEE_RAM_VA_SIZE |
| 188 | #define TEE_RAM_VA_SIZE (4 * 1024 * 1024) |
| 189 | #define TEE_LOAD_ADDR (TZDRAM_BASE + 0x20000) |
| 190 | |
| 191 | /* Mandatory TA RAM (external less secure RAM) */ |
| 192 | #define TA_RAM_START (TZDRAM_BASE + TEE_RAM_VA_SIZE) |
| 193 | #define TA_RAM_SIZE (TZDRAM_SIZE - TEE_RAM_VA_SIZE) |
| 194 | |
| 195 | /* Mandatory: for static SHM, need a hardcoded physical address */ |
| 196 | #define TEE_SHMEM_START 0x08000000 |
| 197 | #define TEE_SHMEM_SIZE (4 * 1024 * 1024) |
| 198 | |
| 199 | #endif /* PLATFORM_CONFIG_H */ |
| 200 | |
| 201 | This is minimal amount of information in the ``platform_config.h`` file. I.e, |
| 202 | the memory layout for on-chip and external RAM. Note that parts of the DDR |
| 203 | typically will need to be shared with normal world, so there is need for some |
| 204 | kind of memory firewall for this (more about that further down). As you can see |
| 205 | we have also added the UART configuration here, i.e., the ``DEVICE0_xyz`` part. |
| 206 | |
| 207 | Official board support in OP-TEE? |
| 208 | ================================= |
| 209 | We do encourage everyone to submit their board support to the OP-TEE project |
| 210 | itself, so it becomes part of the official releases and will be maintained by |
| 211 | the OP-TEE community itself. If you intend to do so, then there are a few more |
| 212 | things that you are supposed to do. |
| 213 | |
| 214 | **Update platforms supported** |
| 215 | |
| 216 | There is a section at the :ref:`platforms_supported` page that lists all devices |
| 217 | officially supported in OP-TEE, that is where you also shall list your device. |
| 218 | It should contain the name of the platform, then composite ``PLATFORM`` flag and |
| 219 | whether the device is publicly available or not. If there is a product page on |
| 220 | the internet for the device, please also create a link when writing the device |
| 221 | name. |
| 222 | |
| 223 | **Update .shippable.yml** |
| 224 | |
| 225 | Since we are using Shippable to test pull requests etc, we would like that you |
| 226 | also add your device to the `.shippable.yml |
| 227 | <https://github.com/OP-TEE/optee_os/blob/master/.shippable.yml>`_ file, so that |
| 228 | it will at least be built when someone is doing a pull request. Add a line at |
| 229 | the end of file: |
| 230 | |
| 231 | .. code-block:: xml |
| 232 | |
| 233 | - _make PLATFORM=<platform-name>_ |
| 234 | |
| 235 | **Maintainer** |
| 236 | |
Joakim Bech | e58b15c | 2020-04-15 10:48:41 +0200 | [diff] [blame] | 237 | If you are submitting the board support upstream we are going to ask you to |
| 238 | become the maintainer for the device you have added. This means that you should |
| 239 | also update the MAINTAINERS.md_ file accordingly. By being a maintainer for a |
| 240 | device you are responsible to keep it up to date and you will be asked every |
| 241 | quarter as part of the OP-TEE release schedule to test your device running the |
| 242 | latest OP-TEE software. |
Joakim Bech | 8e5c5b3 | 2018-10-25 08:18:32 +0200 | [diff] [blame] | 243 | |
| 244 | **Update build.git and manifest.git** |
| 245 | |
| 246 | This isn't strictly necessary, but we are trying to create and maintain OP-TEE |
| 247 | developer builds that should make it easy to setup, build and deploy OP-TEE on |
| 248 | various devices. We encourage all maintainers to do the same for the boards they |
| 249 | are in charge of. Therefore please consider creating a new :ref:`manifest` (and |
| 250 | a new ``*.mk`` in :ref:`build`) for the device you have added to OP-TEE. |
| 251 | |
| 252 | .. _hardware_unique_key: |
| 253 | |
| 254 | Hardware Unique Key |
| 255 | ******************* |
| 256 | Most devices have some kind of Hardware Unique Key (HUK) that is mainly used to |
| 257 | derive other keys. The HUK could for example be used when deriving keys used in |
| 258 | secure storage etc. The important thing with the HUK is that it needs to be well |
| 259 | protected and in the best case the HUK should never ever be readable directly |
| 260 | from software, not even from the secure side. There are different solutions to |
| 261 | this, crypto accelerator might have support for it or, it could involve another |
| 262 | secure co-processor. |
| 263 | |
| 264 | In OP-TEE the HUK **is** just **stubbed** and you will see that in the function |
| 265 | called ``tee_otp_get_hw_unique_key(...)`` in |
| 266 | `core/include/kernel/tee_common_otp.h`_. In a real secure product you **must** |
| 267 | replace this with something else. If your device lacks the hardware support for |
| 268 | a HUK, then you must at least change this to something else than just zeroes. |
| 269 | But, remember it is not good secure practice to store a key in software, |
| 270 | especially not the key that is the root for everything else, so this is not |
| 271 | something we recommend that you should do. |
| 272 | |
| 273 | Secure Clock |
| 274 | ************ |
| 275 | The Time API in GlobalPlatform Internal Core API specification defines three |
| 276 | sources of time; system time, TA persistent time and REE time. The REE time is |
| 277 | by nature considered as an unsecure source of time, but the other two should in |
| 278 | a fully trustable hardware make use of trustable source of time, i.e., a secure |
| 279 | clock. Note that from GlobalPlatform point of view it is not required to make |
| 280 | use of a secure clock, i.e., it is OK to use time from REE, but the level of |
| 281 | trust should be reflected by the ``gpd.tee.systemTime.protectionLevel`` property |
| 282 | and the ``gpd.tee.TAPersistentTime.protectionLevel`` property (100=REE |
| 283 | controlled clock, 1000=TEE controlled clock). So the functions that one needs to |
| 284 | pay attention to are ``tee_time_get_sys_time(...)`` and |
| 285 | ``tee_time_get_ta_time(...)``. If your hardware has a secure clock, then you |
| 286 | probably want to change the implementation there to instead use the secure clock |
| 287 | (and then you would also need to update the property accordingly, i.e., |
| 288 | ``tee_time_get_sys_time_protection_level()`` and the variable |
| 289 | ``ta_time_prot_lvl`` in ``tee_svc.c``). |
| 290 | |
| 291 | Root and Chain of Trust |
| 292 | *********************** |
| 293 | To be able to assure that your devices are running the (untampered) binaries you |
| 294 | intended to run you will need to establish some kind of trust anchor on the |
| 295 | devices. |
| 296 | |
| 297 | The most common way of doing that is to put the root public key in some read |
| 298 | only memory on the device. Quite often SoC's/OEM's stores public key(s) directly |
| 299 | or the hash(es) of the public key(s) in OTP_. When the boot ROM (which indeed |
| 300 | needs to be ROM) is about to load the first stage bootloader it typically reads |
| 301 | the public key from the software binary itself, hash the key and compare it to |
| 302 | the key in OTP_. If they are matching, then the boot ROM can be sure that the |
| 303 | first stage bootloader was indeed signed with the corresponding private key. |
| 304 | |
| 305 | In OP-TEE you will not find any code at all related to this and this is a good |
| 306 | example when it is hard for us to do this in a generic way since device |
| 307 | manufacturers all tend to do this in their own unique way and they are not very |
| 308 | keen on sharing their low level boot details and security implementation with |
| 309 | the rest of the world. This is especially true on ARMv7-A. For ARMv8-A it looks |
| 310 | bit better, since Arm in Trusted Firmware A have implemented and defined how a |
| 311 | abstract the chain of trust (see auth-framework.rst_). |
| 312 | We have successfully verified OP-TEE by using the authentication framework from |
| 313 | Trusted Firmware A (see :ref:`secure_boot` for the details). |
| 314 | |
| 315 | Hardware Crypto IP |
| 316 | ****************** |
| 317 | By default OP-TEE uses a software crypto library (currently mbed TLS and |
| 318 | LibTomCrypt) and you have the ability to enable Crypto Extensions that were |
| 319 | introduced with ARMv8-A (if the device is capable of that). Some of the devices |
| 320 | we have in our hands do have hardware crypto IP's, but due to NDA's etc it has |
| 321 | not been possible to enable it. If you have a device capable of doing crypto |
| 322 | operations on a dedicated crypto block and you prefer to use that in favor for |
| 323 | the software implementation, then you will need to implement relevant functions |
| 324 | defined in `core/include/crypto/crypto.h`_, the Crypto API, and write the low |
| 325 | level driver that communicates with the device. Our |
| 326 | :ref:`cryptographic_implementation` page describes in detail how the Crypto API |
| 327 | is integrated. Since the communication with crypto blocks tends to be quite |
| 328 | different depending on what kind of crypto IP you have, we have not written |
| 329 | how that should be done. It might be that we do that in the future when get hold |
| 330 | of a device where we can use the crypto block. |
| 331 | |
| 332 | By default OP-TEE is configured with a software PRNG. The entropy is added to |
| 333 | software PRNG at various places, but unfortunately it is still quite easy to |
| 334 | predict the data added as entropy. As a consequence, unless the RNG is based on |
| 335 | hardware the generated random will be quite weak. |
| 336 | |
| 337 | Power Management / PSCI |
| 338 | *********************** |
| 339 | In the :ref:`add_a_new_platform` section where we talked about the file |
| 340 | ``main.c``, we added a couple of handlers related to power management, we are |
| 341 | talking about the following lines: |
| 342 | |
| 343 | .. code-block:: c |
| 344 | |
| 345 | .cpu_on = cpu_on_handler, |
| 346 | .cpu_off = pm_do_nothing, |
| 347 | .cpu_suspend = pm_do_nothing, |
| 348 | .cpu_resume = pm_do_nothing, |
| 349 | .system_off = pm_do_nothing, |
| 350 | .system_reset = pm_do_nothing, |
| 351 | |
| 352 | The only function that actually does something there is the ``cpu_on`` function, |
| 353 | the rest of them are stubbed. The main reason for that is because we think that |
| 354 | how to suspend and resume is a device dependent thing. The code in OP-TEE is |
| 355 | prepared so that callbacks etc from Trusted Firmware A will be routed to OP-TEE, |
| 356 | but since the function(s) are just stubbed we will not do anything and just |
| 357 | return. In a real production device, you would probably want to save and restore |
| 358 | CPU states, secure hardware IPs' registers and TZASC and other memory firewall |
| 359 | related setting when these callbacks are being called. |
| 360 | |
| 361 | Memory firewalls / TZASC |
| 362 | ************************ |
| 363 | Arm have defined a system IP / SoC peripheral called TrustZone Address Space |
| 364 | Controller (TZASC, see TZC-380_ and TZC-400_). TZASC can be used to configure |
| 365 | DDR memory into separate regions in the physcial address space, where each |
| 366 | region can have an individual security level setting. After enabling TZASC, it |
| 367 | will perform security checks on transactions to memory or peripherals. It is not |
| 368 | always the case that TZASC is on a device, in some cases the SoC has developed |
| 369 | something equivalent. In OP-TEE this is very well reflected, i.e., different |
| 370 | platforms have different ways of protecting their memory. On ARMv8-A platforms |
| 371 | we are in most of the cases using Trusted Firmware A as the boot firmware and |
| 372 | there the secure bootloader is the one that configures secure vs non-secure |
| 373 | memory using TZASC (see plat_arm_security_setup_ in TF-A). The takeaway here is |
| 374 | that you must make sure that you have configured whatever memory firewall your |
| 375 | device has such that it has a secure and a non-secure memory area. |
| 376 | |
| 377 | .. _core_pub_priv_keypair: |
| 378 | |
| 379 | Trusted Application private/public keypair |
| 380 | ****************************************** |
| 381 | By default all Trusted Applications (TA's) are signed with the pre-generated |
| 382 | 2048-bit RSA development key (private key). This key is located in the ``keys`` |
| 383 | folder (in the root of optee_os.git) and is named ``default_ta.pem``. This key |
| 384 | **must** be replaced with your own key and you should **never ever** check-in |
| 385 | this private key in the source code tree when in use in a real product. The |
| 386 | recommended way to store private keys is to use some kind of HSM_ (Hardware |
| 387 | Security Module), but an alternative would be temporary put the private key on a |
| 388 | computer considered as secure when you are about to sign TA's intended to be |
| 389 | used in real products. Typically it is only a few number of people having access |
| 390 | to this type of key in company. The key handling in OP-TEE is currently a bit |
| 391 | limited since we only support a single key which is used for all TA's. We have |
| 392 | plans on extending this to make it a bit more flexible. Exactly when that will |
| 393 | happen has not been decided yet. |
| 394 | |
| 395 | .. _core/arch/arm/plat-hikey/conf.mk: https://github.com/OP-TEE/optee_os/blob/master/core/arch/arm/plat-hikey/conf.mk |
| 396 | .. _core/include/crypto/crypto.h: https://github.com/OP-TEE/optee_os/blob/master/core/include/crypto/crypto.h |
| 397 | .. _core/include/kernel/tee_common_otp.h: https://github.com/OP-TEE/optee_os/blob/master/core/include/kernel/tee_common_otp.h |
| 398 | |
| 399 | |
| 400 | .. _auth-framework.rst: https://github.com/ARM-software/arm-trusted-firmware/blob/master/docs/auth-framework.rst |
| 401 | .. _HSM: https://en.wikipedia.org/wiki/Hardware_security_module |
| 402 | .. _MAINTAINERS.md: https://github.com/OP-TEE/optee_os/blob/master/MAINTAINERS |
| 403 | .. _OTP: https://en.wikipedia.org/wiki/Programmable_read-only_memory |
| 404 | .. _plat_arm_security_setup: https://github.com/ARM-software/arm-trusted-firmware/search?utf8=%E2%9C%93&q=plat_arm_security_setup&type= |
| 405 | .. _Platform-specific configuration and flags: build_system.md#platform-specific-configuration-and-flags |
| 406 | .. _TZC-380: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0431c/index.html |
| 407 | .. _TZC-400: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.100325_0001_02_en/index.html |