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 | ================= |
| 58 | SP images are stored in the OP-TEE image as early TAs are: the binary images are |
| 59 | embedded in OP-TEE Core in a read-only data section. |
| 60 | This makes it possible to load SPs during boot and no rich-os is needed in the |
| 61 | normal world. ``ldelf`` is used to load and initialise the SPs. |
| 62 | |
| 63 | Starting SPs |
| 64 | ------------ |
| 65 | SPs are loaded and started as the last step in OP-TEE's initialisation process. |
| 66 | This is done by adding ``sp_init_all()`` to the ``boot_final`` initcall level. |
| 67 | |
| 68 | .. uml:: |
| 69 | :width: 800 |
| 70 | |
| 71 | autoactivate on |
| 72 | thread_optee_smc_a64.s -> boot_final |
| 73 | boot_final -> secure_partition.c: sp_init_all() |
| 74 | loop for each SP |
| 75 | secure_partition.c -> secure_partition.c: sp_init_uuid() |
| 76 | return |
| 77 | end |
| 78 | return |
| 79 | return |
| 80 | autoactivate off |
| 81 | thread_optee_smc_a64.s -> thread_optee_smc_a64.s: thread_ffa_msg_wait() |
| 82 | thread_optee_smc_a64.s -> thread_optee_smc_a64.s: ffa_msg_loop() |
| 83 | autoactivate off |
| 84 | thread_optee_smc_a64.s -> SPMD: SMC |
| 85 | |
| 86 | :``sp_init_all()``: Initialise all SPs which have been added by the |
| 87 | ``SP_PATHS`` compiler option and run them |
| 88 | :``thread_ffa_msg_wait()``: All SPs are loaded and started. A |
| 89 | ``FFA_MSG_WAIT`` message is sent to the Normal |
| 90 | World. |
| 91 | |
| 92 | |
| 93 | Each SP is loaded into the system using ``ldelf`` and started. This is based |
| 94 | around the same process as loading the early TAs. |
| 95 | All SPs are run after they are loaded and run until a ``FFA_MSG_WAIT`` is sent |
| 96 | by the SP. |
| 97 | |
| 98 | |
| 99 | .. uml:: |
| 100 | |
| 101 | autoactivate on |
| 102 | secure_partition.c -> secure_partition.c: sp_init_uuid() |
| 103 | secure_partition.c -> secure_partition.c: sp_open_session() |
| 104 | secure_partition.c -> secure_partition.c: find_sp() |
| 105 | return |
| 106 | secure_partition.c -> secure_partition.c: sp_create_session() |
| 107 | return |
| 108 | secure_partition.c -> secure_partition.c: ldelf_load_ldelf() |
| 109 | return |
| 110 | secure_partition.c -> secure_partition.c: ldlelf_init_with_ldelf() |
| 111 | return |
| 112 | secure_partition.c -> secure_partition.c: sp_init_set_registers() |
| 113 | return |
| 114 | return |
| 115 | secure_partition.c -> secure_partition.c: enter_sp() |
| 116 | return |
| 117 | secure_partition.c -> secure_partition.c: sp_msg_handler() |
| 118 | return |
| 119 | return |
| 120 | |
| 121 | :``init_with_ldelf()``: Load the SP |
| 122 | :``sp_init_info()``: Initialise the ``struct ffa_init_info``. The |
| 123 | ``struct ffa_init_info`` is passed to the SP |
| 124 | during it first run. |
| 125 | :``sp_init_set_registers()``: Initialise the registers of the SP |
| 126 | :``sp_msg_handler()``: Handle the SPs FF-A message |
| 127 | |
| 128 | Once all SPs are loaded and started we return to the SPMD and the Normal World |
| 129 | is booted. |
| 130 | |
| 131 | |
| 132 | SP message handling |
| 133 | ------------------- |
| 134 | |
| 135 | The SPMC is split into 2 main message handlers: |
| 136 | |
| 137 | :``thread_spmc_msg_recv()`` thread_spmc.c: Used to handle message coming |
| 138 | from the Normal World. |
| 139 | :``sp_msg_handler()`` spmc_sp_handler.c: Used to handle message where |
| 140 | the source or the destination |
| 141 | is a SP. |
| 142 | |
| 143 | When a ``FFA_MSG_SEND_DIRECT_REQ`` message is received by the SPMC from the |
| 144 | Normal World, a new thread is started. |
| 145 | The FF-A message is passed to the thread and it will call the |
| 146 | ``sp_msg_handler()`` function. |
| 147 | |
| 148 | Whenever the SPMC (``sp_msg_handler()``) receives a message not intended for |
| 149 | one of the SPs, it will exit the thread and return to the Normal World |
| 150 | passing the FF-A message. |
| 151 | |
| 152 | Currently only a ``FFA_MSG_SEND_DIRECT_REQ`` can be passed from the Normal |
| 153 | World to a SP. |
| 154 | |
| 155 | .. uml:: |
| 156 | :width: 800 |
| 157 | |
| 158 | skinparam backgroundcolor transparent |
| 159 | participant "None-secure world" as None_secure_world |
| 160 | |
| 161 | box "S-EL3" |
| 162 | participant SPMD |
| 163 | end box |
| 164 | |
| 165 | box "S-EL1" |
| 166 | participant thread_spmc |
| 167 | participant spmc_sp_handler |
| 168 | end box |
| 169 | |
| 170 | |
| 171 | box "S-EL0" |
| 172 | participant SP |
| 173 | end box |
| 174 | |
| 175 | autoactivate on |
| 176 | None_secure_world -> SPMD: FFA_MSG_SEND_DIRECT_REQ \n <font color=red>SMC |
| 177 | SPMD -> thread_spmc: thread_spmc_msg_recv() \n <font color=red>ERET |
| 178 | |
| 179 | thread_spmc -> spmc_sp_handler : spmc_sp_start_thread() |
| 180 | == thread == |
| 181 | spmc_sp_handler -> spmc_sp_handler : sp_msg_handler() |
| 182 | |
| 183 | loop FF-A dst != NSW |
| 184 | spmc_sp_handler -> spmc_sp_handler: ffa_handle_sp_direct_req() |
| 185 | spmc_sp_handler -> spmc_sp_handler: enter_sp() |
| 186 | spmc_sp_handler -> spmc_sp_handler: sp_enter_invoke_cmd() |
| 187 | autoactivate off |
| 188 | spmc_sp_handler -> SP: __thread_enter_user_mode() \n <font color=red>ERET |
| 189 | activate SP |
| 190 | |
| 191 | SP -> spmc_sp_handler : (FF-A message) sp_handle_svc() \n <font color=red>SVC |
| 192 | deactivate SP |
| 193 | activate spmc_sp_handler |
| 194 | spmc_sp_handler -> spmc_sp_handler: Store SP context |
| 195 | spmc_sp_handler -> spmc_sp_handler: return to sp_enter_invoke_cmd() |
| 196 | deactivate spmc_sp_handler |
| 197 | spmc_sp_handler -> spmc_sp_handler: Retrieve FF-A message from SP context |
| 198 | return |
| 199 | return |
| 200 | return |
| 201 | end |
| 202 | return |
| 203 | == End of thread == |
| 204 | return |
| 205 | return \n <font color=red>SMC |
| 206 | return FFA_MSG_SEND_DIRECT_RESP \n <font color=red>ERET |
| 207 | |
| 208 | |
| 209 | Every message received by the SPMC from the Normal World is handled in the |
| 210 | ``thread_spmc_msg_recv()`` function. |
| 211 | |
| 212 | When entering a SP we need to be running in a OP-TEE thread. This is needed to |
| 213 | be able to push the TS session (We push the TS session to get access to the SP |
| 214 | memory). |
| 215 | Currently the only possibility to enter a SP from the Normal world is via a |
| 216 | ``FFA_MSG_SEND_DIRECT_REQ``. Whenever we receive a ``FFA_MSG_SEND_DIRECT_REQ`` |
| 217 | message which doesn't have OP-TEE as the endpoint-id, we start a thread and |
| 218 | forward the FF-A message to the ``sp_msg_handler()``. |
| 219 | |
| 220 | The ``sp_msg_handler()`` is responsible for all messages coming or going |
| 221 | to/from a SP. It runs in a while loop and will handle every message until it |
| 222 | comes across a messages which is not intended for the secure world. |
| 223 | After a message is handled by the SPMC or when it needs to be forwarded to a SP, |
| 224 | ``sp_enter()`` is called. |
| 225 | ``sp_enter()`` will copy the FF-A arguments and resume the SP. |
| 226 | |
| 227 | When the SPMC needs to have access to the SPs memory, it will call |
| 228 | ``ts_push_current_session()`` to gain access and ``ts_pop_current_session()`` |
| 229 | to release the access. |
| 230 | |
| 231 | Running and exiting SPs |
| 232 | ----------------------- |
| 233 | |
| 234 | The SPMC resumes/starts the SP by calling the ``sp_enter()``. This will set up |
| 235 | the SP context and jump into S-EL0. |
| 236 | Whenever the SP performs a system call it will end up in ``sp_handle_svc()``. |
| 237 | ``sp_handle_svc()`` stores the current context of the SP and makes sure that we |
| 238 | don't return to S-EL0 but instead returns to S-EL1 back to ``sp_enter()``. |
| 239 | ``sp_enter()`` will pass the FF-A registers (x0-x7) to |
| 240 | ``spmc_sp_msg_handler()``. This will process the FF-A message. |
| 241 | |
| 242 | |
| 243 | RxTx buffer managment |
| 244 | --------------------- |
| 245 | RxTx buffers are used by the SPMC to exchange information between an endpoint |
| 246 | and the SPMC. The rxtx_buf struct is used by the SPMC for abstracting buffer |
| 247 | management. |
| 248 | Every SP has a ``struct rxtx_buf`` wich will be passed to every function that |
| 249 | needs access to the rxtx buffer. |
| 250 | A separate ``struct rxtx_buf`` is defined for the Normal World, which gives |
| 251 | access to the Normal World buffers. |
| 252 | |
| 253 | |
| 254 | Configuration |
| 255 | ============= |
| 256 | |
| 257 | Adding SPs to the Image |
| 258 | ----------------------- |
| 259 | |
| 260 | The following flags have to be enabled to enable the SPMC. The SP images |
| 261 | themself are loaded by using the ``SP_PATHS`` flag. |
| 262 | These should be added to the OP-TEE configuration inside the OP-TEE/build.git |
| 263 | directory. |
| 264 | |
| 265 | .. code-block:: Make |
| 266 | |
| 267 | OPTEE_OS_COMMON_FLAGS += CFG_CORE_FFA=y # Enable the FF-A transport layer |
| 268 | OPTEE_OS_COMMON_FLAGS += SP_PATHS="path/to/sp-xxx.elf path/to/sp-yyy.elf" # Add the SPs to the OP-TEE image |
| 269 | TF_A_FLAGS += SPD=spmd SPMD_SPM_AT_SEL2=0 # Build TF-A with the SPMD enabled and without S-EL2 |
| 270 | |