Jelle Sels | 3ef999a | 2021-03-29 09:35:37 +0200 | [diff] [blame] | 1 | ==== |
| 2 | SPMC |
| 3 | ==== |
| 4 | |
| 5 | This document describes the SPMC (S-EL1) implementation for OP-TEE. |
| 6 | More information on the SPMC can be found in the FF-A specification can be |
| 7 | found in the |
| 8 | `FF-A spec <https://developer.arm.com/documentation/den0077/latest>`_. |
| 9 | |
| 10 | .. toctree:: |
| 11 | :numbered: |
| 12 | |
| 13 | SPMC Responsibilities |
| 14 | ===================== |
| 15 | |
| 16 | The SPMC is a critical component in the FF-A flow. Some of its major |
| 17 | responsibilities are: |
| 18 | |
| 19 | - Initialisation and run-time management of the SPs: |
| 20 | The SPMC component is responsible for initialisation of the |
| 21 | Secure Partitions (loading the image, setting up the stack, heap, ...). |
| 22 | - Routing messages between endpoints: |
| 23 | The SPMC is responsible for passing FF-A messages from normal world |
| 24 | to SPs and back. It also responsible for passing FF-A messages between |
| 25 | SPs. |
| 26 | - Memory management: |
| 27 | The SPMC is responsible for the memory management of the SPs. Memory |
| 28 | can be shared between SPs and between a SP to the normal world. |
| 29 | |
| 30 | This document describes OP-TEE as a S-EL1 SPMC. |
| 31 | |
| 32 | Secure Partitions |
| 33 | ================= |
| 34 | Secure Partitions (SPs) are the endpoints used in the FF-A protocol. When |
| 35 | OP-TEE is used as a SPMC SPs run primarily inside S-EL0. |
| 36 | |
| 37 | OP-TEE will use FF-A for it transport layer when the OP-TEE ``CFG_CORE_FFA=y`` |
| 38 | configuration flag is enabled. |
| 39 | The SPMC will expose the OP-TEE core, privileged mode, as an secure endpoint |
| 40 | itself. This is used to handle all GlobalPlaform programming mode operations. |
| 41 | All GlobalPlatform messages are encapsulated inside FF-A messages. |
| 42 | The OP-TEE endpoint will unpack the messages and afterwards handle them as |
| 43 | standard OP-TEE calls. This is needed as TF-A (S-EL3) does only allow |
| 44 | FF-A messages to be passed to the secure world when the SPMD is enabled. |
| 45 | |
| 46 | SPs run from the initial boot of the system until power down and don't have any |
| 47 | built-in session management compared to GPD TEE TAs. The only means of |
| 48 | communicating with the outside world is through messages defined in the FF-A |
| 49 | specification. The context of a SP is saved between executions. |
| 50 | |
| 51 | The |
| 52 | `Trusted Service <https://www.trustedfirmware.org/projects/trusted-services/>`_ |
| 53 | repository includes the libsp libary which export all needed functions to build |
| 54 | a S-EL0 SP. It also includes many examples of how to create and implement a SP. |
| 55 | |
| 56 | SPMC Program Flow |
| 57 | ================= |
Balint Dobszay | fe29e2b | 2022-09-27 15:41:30 +0200 | [diff] [blame^] | 58 | SP images are either embedded into the OP-TEE image or loaded from the FIP by |
| 59 | BL2. This makes it possible to start SPs during boot, before the rich OS is |
| 60 | available in the normal world. ``ldelf`` is used to load and initialise the SPs. |
Jelle Sels | 3ef999a | 2021-03-29 09:35:37 +0200 | [diff] [blame] | 61 | |
| 62 | Starting SPs |
| 63 | ------------ |
| 64 | SPs are loaded and started as the last step in OP-TEE's initialisation process. |
| 65 | This is done by adding ``sp_init_all()`` to the ``boot_final`` initcall level. |
| 66 | |
| 67 | .. uml:: |
| 68 | :width: 800 |
| 69 | |
| 70 | autoactivate on |
| 71 | thread_optee_smc_a64.s -> boot_final |
| 72 | boot_final -> secure_partition.c: sp_init_all() |
| 73 | loop for each SP |
| 74 | secure_partition.c -> secure_partition.c: sp_init_uuid() |
| 75 | return |
| 76 | end |
| 77 | return |
| 78 | return |
| 79 | autoactivate off |
| 80 | thread_optee_smc_a64.s -> thread_optee_smc_a64.s: thread_ffa_msg_wait() |
| 81 | thread_optee_smc_a64.s -> thread_optee_smc_a64.s: ffa_msg_loop() |
| 82 | autoactivate off |
| 83 | thread_optee_smc_a64.s -> SPMD: SMC |
| 84 | |
| 85 | :``sp_init_all()``: Initialise all SPs which have been added by the |
| 86 | ``SP_PATHS`` compiler option and run them |
| 87 | :``thread_ffa_msg_wait()``: All SPs are loaded and started. A |
| 88 | ``FFA_MSG_WAIT`` message is sent to the Normal |
| 89 | World. |
| 90 | |
| 91 | |
| 92 | Each SP is loaded into the system using ``ldelf`` and started. This is based |
| 93 | around the same process as loading the early TAs. |
| 94 | All SPs are run after they are loaded and run until a ``FFA_MSG_WAIT`` is sent |
| 95 | by the SP. |
| 96 | |
| 97 | |
| 98 | .. uml:: |
| 99 | |
| 100 | autoactivate on |
| 101 | secure_partition.c -> secure_partition.c: sp_init_uuid() |
| 102 | secure_partition.c -> secure_partition.c: sp_open_session() |
| 103 | secure_partition.c -> secure_partition.c: find_sp() |
| 104 | return |
| 105 | secure_partition.c -> secure_partition.c: sp_create_session() |
| 106 | return |
| 107 | secure_partition.c -> secure_partition.c: ldelf_load_ldelf() |
| 108 | return |
| 109 | secure_partition.c -> secure_partition.c: ldlelf_init_with_ldelf() |
| 110 | return |
| 111 | secure_partition.c -> secure_partition.c: sp_init_set_registers() |
| 112 | return |
| 113 | return |
| 114 | secure_partition.c -> secure_partition.c: enter_sp() |
| 115 | return |
| 116 | secure_partition.c -> secure_partition.c: sp_msg_handler() |
| 117 | return |
| 118 | return |
| 119 | |
| 120 | :``init_with_ldelf()``: Load the SP |
| 121 | :``sp_init_info()``: Initialise the ``struct ffa_init_info``. The |
| 122 | ``struct ffa_init_info`` is passed to the SP |
| 123 | during it first run. |
| 124 | :``sp_init_set_registers()``: Initialise the registers of the SP |
| 125 | :``sp_msg_handler()``: Handle the SPs FF-A message |
| 126 | |
| 127 | Once all SPs are loaded and started we return to the SPMD and the Normal World |
| 128 | is booted. |
| 129 | |
| 130 | |
| 131 | SP message handling |
| 132 | ------------------- |
| 133 | |
| 134 | The SPMC is split into 2 main message handlers: |
| 135 | |
| 136 | :``thread_spmc_msg_recv()`` thread_spmc.c: Used to handle message coming |
| 137 | from the Normal World. |
| 138 | :``sp_msg_handler()`` spmc_sp_handler.c: Used to handle message where |
| 139 | the source or the destination |
| 140 | is a SP. |
| 141 | |
| 142 | When a ``FFA_MSG_SEND_DIRECT_REQ`` message is received by the SPMC from the |
| 143 | Normal World, a new thread is started. |
| 144 | The FF-A message is passed to the thread and it will call the |
| 145 | ``sp_msg_handler()`` function. |
| 146 | |
| 147 | Whenever the SPMC (``sp_msg_handler()``) receives a message not intended for |
| 148 | one of the SPs, it will exit the thread and return to the Normal World |
| 149 | passing the FF-A message. |
| 150 | |
| 151 | Currently only a ``FFA_MSG_SEND_DIRECT_REQ`` can be passed from the Normal |
| 152 | World to a SP. |
| 153 | |
| 154 | .. uml:: |
| 155 | :width: 800 |
| 156 | |
| 157 | skinparam backgroundcolor transparent |
| 158 | participant "None-secure world" as None_secure_world |
| 159 | |
| 160 | box "S-EL3" |
| 161 | participant SPMD |
| 162 | end box |
| 163 | |
| 164 | box "S-EL1" |
| 165 | participant thread_spmc |
| 166 | participant spmc_sp_handler |
| 167 | end box |
| 168 | |
| 169 | |
| 170 | box "S-EL0" |
| 171 | participant SP |
| 172 | end box |
| 173 | |
| 174 | autoactivate on |
| 175 | None_secure_world -> SPMD: FFA_MSG_SEND_DIRECT_REQ \n <font color=red>SMC |
| 176 | SPMD -> thread_spmc: thread_spmc_msg_recv() \n <font color=red>ERET |
| 177 | |
| 178 | thread_spmc -> spmc_sp_handler : spmc_sp_start_thread() |
| 179 | == thread == |
| 180 | spmc_sp_handler -> spmc_sp_handler : sp_msg_handler() |
| 181 | |
| 182 | loop FF-A dst != NSW |
| 183 | spmc_sp_handler -> spmc_sp_handler: ffa_handle_sp_direct_req() |
| 184 | spmc_sp_handler -> spmc_sp_handler: enter_sp() |
| 185 | spmc_sp_handler -> spmc_sp_handler: sp_enter_invoke_cmd() |
| 186 | autoactivate off |
| 187 | spmc_sp_handler -> SP: __thread_enter_user_mode() \n <font color=red>ERET |
| 188 | activate SP |
| 189 | |
| 190 | SP -> spmc_sp_handler : (FF-A message) sp_handle_svc() \n <font color=red>SVC |
| 191 | deactivate SP |
| 192 | activate spmc_sp_handler |
| 193 | spmc_sp_handler -> spmc_sp_handler: Store SP context |
| 194 | spmc_sp_handler -> spmc_sp_handler: return to sp_enter_invoke_cmd() |
| 195 | deactivate spmc_sp_handler |
| 196 | spmc_sp_handler -> spmc_sp_handler: Retrieve FF-A message from SP context |
| 197 | return |
| 198 | return |
| 199 | return |
| 200 | end |
| 201 | return |
| 202 | == End of thread == |
| 203 | return |
| 204 | return \n <font color=red>SMC |
| 205 | return FFA_MSG_SEND_DIRECT_RESP \n <font color=red>ERET |
| 206 | |
| 207 | |
| 208 | Every message received by the SPMC from the Normal World is handled in the |
| 209 | ``thread_spmc_msg_recv()`` function. |
| 210 | |
| 211 | When entering a SP we need to be running in a OP-TEE thread. This is needed to |
| 212 | be able to push the TS session (We push the TS session to get access to the SP |
| 213 | memory). |
| 214 | Currently the only possibility to enter a SP from the Normal world is via a |
| 215 | ``FFA_MSG_SEND_DIRECT_REQ``. Whenever we receive a ``FFA_MSG_SEND_DIRECT_REQ`` |
| 216 | message which doesn't have OP-TEE as the endpoint-id, we start a thread and |
| 217 | forward the FF-A message to the ``sp_msg_handler()``. |
| 218 | |
| 219 | The ``sp_msg_handler()`` is responsible for all messages coming or going |
| 220 | to/from a SP. It runs in a while loop and will handle every message until it |
| 221 | comes across a messages which is not intended for the secure world. |
| 222 | After a message is handled by the SPMC or when it needs to be forwarded to a SP, |
| 223 | ``sp_enter()`` is called. |
| 224 | ``sp_enter()`` will copy the FF-A arguments and resume the SP. |
| 225 | |
| 226 | When the SPMC needs to have access to the SPs memory, it will call |
| 227 | ``ts_push_current_session()`` to gain access and ``ts_pop_current_session()`` |
| 228 | to release the access. |
| 229 | |
| 230 | Running and exiting SPs |
| 231 | ----------------------- |
| 232 | |
| 233 | The SPMC resumes/starts the SP by calling the ``sp_enter()``. This will set up |
| 234 | the SP context and jump into S-EL0. |
| 235 | Whenever the SP performs a system call it will end up in ``sp_handle_svc()``. |
| 236 | ``sp_handle_svc()`` stores the current context of the SP and makes sure that we |
| 237 | don't return to S-EL0 but instead returns to S-EL1 back to ``sp_enter()``. |
| 238 | ``sp_enter()`` will pass the FF-A registers (x0-x7) to |
| 239 | ``spmc_sp_msg_handler()``. This will process the FF-A message. |
| 240 | |
| 241 | |
| 242 | RxTx buffer managment |
| 243 | --------------------- |
| 244 | RxTx buffers are used by the SPMC to exchange information between an endpoint |
| 245 | and the SPMC. The rxtx_buf struct is used by the SPMC for abstracting buffer |
| 246 | management. |
| 247 | Every SP has a ``struct rxtx_buf`` wich will be passed to every function that |
| 248 | needs access to the rxtx buffer. |
| 249 | A separate ``struct rxtx_buf`` is defined for the Normal World, which gives |
| 250 | access to the Normal World buffers. |
| 251 | |
| 252 | |
| 253 | Configuration |
| 254 | ============= |
| 255 | |
Balint Dobszay | fe29e2b | 2022-09-27 15:41:30 +0200 | [diff] [blame^] | 256 | SPMC config options |
| 257 | --------------------------- |
Jelle Sels | 3ef999a | 2021-03-29 09:35:37 +0200 | [diff] [blame] | 258 | |
Balint Dobszay | fe29e2b | 2022-09-27 15:41:30 +0200 | [diff] [blame^] | 259 | To configure OP-TEE as a S-EL1 SPMC with Secure Partition support, the following |
| 260 | flags should be set for optee_os: |
Jelle Sels | 3ef999a | 2021-03-29 09:35:37 +0200 | [diff] [blame] | 261 | |
Balint Dobszay | fe29e2b | 2022-09-27 15:41:30 +0200 | [diff] [blame^] | 262 | - ``CFG_CORE_SEL1_SPMC=y`` |
| 263 | - ``CFG_SECURE_PARTITION=y`` |
| 264 | - ``CFG_DT=y`` |
| 265 | - ``CFG_MAP_EXT_DT_SECURE=y`` |
Jelle Sels | 3ef999a | 2021-03-29 09:35:37 +0200 | [diff] [blame] | 266 | |
Balint Dobszay | fe29e2b | 2022-09-27 15:41:30 +0200 | [diff] [blame^] | 267 | Furthermore TF-A should be configured as the SPMD, expecting a S-EL1 SPMC: |
Jelle Sels | 3ef999a | 2021-03-29 09:35:37 +0200 | [diff] [blame] | 268 | |
Balint Dobszay | fe29e2b | 2022-09-27 15:41:30 +0200 | [diff] [blame^] | 269 | - ``SPD=spmd`` |
| 270 | - ``SPMD_SPM_AT_SEL2=0`` |
| 271 | - ``ARM_SPMC_MANIFEST_DTS=<path to SPMC manifest dts>`` |
| 272 | |
| 273 | SP loading mechanism |
| 274 | --------------------- |
| 275 | |
| 276 | OP-TEE SPMC supports two methods for finding and loading the SP executable |
| 277 | images. Currently only ELF executables are supported. In the build repo the |
| 278 | loading method can be selected with the SP_PACKAGING_METHOD option. |
| 279 | |
| 280 | Embedded SP |
| 281 | ^^^^^^^^^^^ |
| 282 | |
| 283 | In this case the early TA mechanism of optee_os is reused: the SP ELF files are |
| 284 | embedded into the main OP-TEE binary. Each ELF should start with a specific |
| 285 | section (.sp_head) containing a struct which describes the SP (UUID, stack size, |
| 286 | etc.). The images can be added to optee_os using the ``SP_PATHS`` config option, |
| 287 | the build repo will set this up automatically when |
| 288 | ``SP_PACKAGING_METHOD=embedded`` is selected. The images passed in ``SP_PATHS`` |
| 289 | are processed by ``ts_bin_to_c.py`` in optee_os and linked into the main binary. |
| 290 | At runtime the ``for_each_secure_partition()`` macro can iterate through these |
| 291 | images, so a particular SP can be found by UUID and then loaded by ldelf. |
| 292 | |
| 293 | The SP manifest file `[1]`_ used by the SPMC to setup SPs is also handled by |
| 294 | ``ts_bin_to_c.py``, it will be concatenated to the end of the SP ELF. |
| 295 | |
| 296 | FIP SP |
| 297 | ^^^^^^ |
| 298 | |
| 299 | In this case the SP ELF files and the corresponding SP manifest DTs are |
| 300 | encapsulated into SP packages and packed into the FIP. The goal of providing |
| 301 | this alternative flow is to make updating SPs easier (independent of the main |
| 302 | OP-TEE binary) and to get aligned with Hafnium (S-EL2 SPMC). For more |
| 303 | information about the FIP, please refer to the TF-A documentation `[2]`_. The SP |
| 304 | packaging process and the package format is provided by TF-A, detailed |
| 305 | description is available at `[3]`_. In the build repo this method can be |
| 306 | selected by ``SP_PACKAGING_METHOD=fip``, it covers all the necessary setup |
| 307 | automatically. In case of using another buildsystem, the following steps should |
| 308 | be implemented: |
| 309 | |
| 310 | - TF-A config ``SP_LAYOUT_FILE``: provide a JSON file which describes the SPs |
| 311 | (path to SP executable and corresponding DT, example `[4]`_). The TF-A |
| 312 | buildsystem will create the SP packages (using sptool) based on this and pack |
| 313 | them into the FIP. |
| 314 | |
| 315 | - TF-A config ``ARM_BL2_SP_LIST_DTS``: provide a DT snippet which describes the |
| 316 | SPs' UUIDs and load addresses (example: `[5]`_). This will be injected into |
| 317 | the SP list in ``TB_FW_CONFIG`` DT of TF-A, and BL2 will load the SP packages |
| 318 | based on this. Note that BL2 doesn't automatically load all images from the |
| 319 | FIP: it's necessary to explicitly define them in ``TB_FW_CONFIG`` (using this |
| 320 | injected snippet or manually editing the DT). |
| 321 | |
| 322 | - TF-A config ``ARM_SPMC_MANIFEST_DTS``: provide the SPMC manifest (example: |
| 323 | `[6]`_). This DT is passed to the SPMC as a boot argument (in the TF-A naming |
| 324 | convention this is the ``TOS_FW_CONFIG``). It should contain the list of SP |
| 325 | packages and their load addresses in the ``compatible = "arm,sp_pkg"`` node. |
| 326 | |
| 327 | At boot optee_os will parse the SP package load addresses from the SPMC manifest |
| 328 | and find the SP packages already loaded by BL2. Iterating through the SP |
| 329 | packages, based on the SP package header in each package it will map the SP |
| 330 | executable image and the corresponding manifest DT and collect these to the |
| 331 | ``fip_sp_list`` list. Later when initialising the SPs, the ``for_each_fip_sp`` |
| 332 | macro is used to iterate this list and load the executables using ldelf, just |
| 333 | like for the embedded SP case. |
| 334 | |
| 335 | .. _[1]: |
| 336 | |
| 337 | [1] https://trustedfirmware-a.readthedocs.io/en/v2.6/components/ffa-manifest-binding.html |
| 338 | |
| 339 | .. _[2]: |
| 340 | |
| 341 | [2] https://trustedfirmware-a.readthedocs.io/en/v2.6/design/firmware-design.html#firmware-image-package-fip |
| 342 | |
| 343 | .. _[3]: |
| 344 | |
| 345 | [3] https://trustedfirmware-a.readthedocs.io/en/v2.6/components/secure-partition-manager.html#secure-partition-packages |
| 346 | |
| 347 | .. _[4]: |
| 348 | |
| 349 | [4] https://trustedfirmware-a.readthedocs.io/en/v2.6/components/secure-partition-manager.html#describing-secure-partitions |
| 350 | |
| 351 | .. _[5]: |
| 352 | |
| 353 | [5] https://github.com/OP-TEE/build/blob/master/fvp/bl2_sp_list.dtsi |
| 354 | |
| 355 | .. _[6]: |
| 356 | |
| 357 | [6] https://github.com/OP-TEE/build/blob/master/fvp/spmc_manifest.dts |