Mate Toth-Pal | f286177 | 2024-08-01 13:18:30 +0200 | [diff] [blame] | 1 | .. SPDX-License-Identifier: BSD-3-Clause |
| 2 | .. SPDX-FileCopyrightText: Copyright TF-RMM Contributors. |
| 3 | |
| 4 | ############### |
| 5 | EL0 apps in RMM |
| 6 | ############### |
| 7 | |
| 8 | **** |
| 9 | Goal |
| 10 | **** |
| 11 | |
| 12 | The goal of introducing EL0 app support in TF-RMM is to improve security by |
| 13 | minimizing the amount of code running in privileged mode. This is achieved by |
| 14 | deprivileging parts of RMM to run at EL0 using the Virtualization Host |
| 15 | Extensions (VHE). This approach is especially desirable for code pulled in from |
| 16 | external projects (like Mbed TLS), as such components are often complex and |
| 17 | sensitive. RMM apps now run at EL0 using the EL2&0 translation regime, with the |
| 18 | CPU configured such that an eret instruction in EL2 transitions directly to EL0, |
| 19 | and the target EL of an SVC instruction is EL2. Additionally, each EL0 app has |
| 20 | its own isolated address space, which not only improves fault isolation but also |
| 21 | helps mitigate speculation-based side channels, allowing secrets within apps to |
| 22 | be better protected from speculative execution. This isolation mechanism also |
| 23 | lays the groundwork for future side-channel mitigations and facilitates their |
| 24 | deployment. Overall, this scheme is especially useful for sand-boxing complex |
| 25 | and sensitive code at EL0 and increasing the robustness of RMM. |
| 26 | |
| 27 | |RMM app EL0| |
| 28 | |
| 29 | This design document covers the various aspects of the implementation of the EL0 |
| 30 | app support in RMM. |
| 31 | |
| 32 | |
| 33 | ********************* |
| 34 | Building app binaries |
| 35 | ********************* |
| 36 | |
| 37 | The RMM app's code is in the ``app`` directory, and they may link to |
| 38 | libraries from the ``lib`` directory. RMM apps use a common linker script that |
| 39 | contains a few predefined section. The RMM app cmake file first compiles and |
| 40 | links the RMM app elf binary. Then the python script ``app/gen_app_bin.py`` |
| 41 | is called that extracts the section contents along with address and size |
| 42 | information. Then this information is used to generate a bin file. The bin file |
| 43 | is a concatenation of the following: |
| 44 | |
| 45 | +----------------+-------------------------------------+ |
| 46 | | 1 page size of | 8 bytes padding (explained later) | |
| 47 | | header +-------------------------------------+ |
| 48 | | | magic | |
| 49 | | +-------------------------------------+ |
| 50 | | | header version | |
| 51 | | +-------------------------------------+ |
| 52 | | | app_name | |
| 53 | | +-------------------------------------+ |
| 54 | | | app id | |
| 55 | | +-------------------------------------+ |
| 56 | | | app_len | |
| 57 | | +-------------------------------------+ |
| 58 | | | section 1 offset | |
| 59 | | +-------------------------------------+ |
| 60 | | | section 1 size | |
| 61 | | +-------------------------------------+ |
| 62 | | | ... | |
| 63 | | +-------------------------------------+ |
| 64 | | | section n offset | |
| 65 | | +-------------------------------------+ |
| 66 | | | section n size | |
| 67 | | +-------------------------------------+ |
| 68 | | | magic | |
| 69 | | +-------------------------------------+ |
| 70 | | | Padding | |
| 71 | +----------------+-------------------------------------+ |
| 72 | | content of section 1 | |
| 73 | +------------------------------------------------------+ |
| 74 | | ... | |
| 75 | +------------------------------------------------------+ |
| 76 | | content of section n | |
| 77 | +------------------------------------------------------+ |
| 78 | |
| 79 | The number and order of sections in an RMM app elf/binary is hardcoded in the |
| 80 | linker script, the python script that does the extraction of section content, |
| 81 | and in the header definition. |
| 82 | |
| 83 | The RMM app bin size is padded to be a multiple of page size. |
| 84 | |
| 85 | ************************ |
| 86 | Building final RMM image |
| 87 | ************************ |
| 88 | |
| 89 | The RMM core code is built and linked using its own linker script. Then objcopy |
| 90 | is called on the resulting elf to extract the sections to an img file. Then the |
| 91 | bin files for the RMM apps are built as described above. The final RMM image |
| 92 | is created concatenating the following elements by the python script |
| 93 | ``app/bundle_app_rmm.py``: |
| 94 | |
| 95 | * The first RMM app bin. The first 8 byte padding of the header is updated to a |
| 96 | ``BL`` (branch with link) instruction, followed by 4 bytes of padding. This |
| 97 | allows the offset of the first meaningful field - the section offsets in the |
| 98 | app header - to remain consistent with its position in the original app |
| 99 | binary. The ``BL`` instruction branches to the start of the text section in |
| 100 | the RMM core img. The ``BL`` instruction uses ``PC`` relative offset to encode |
| 101 | the branch target, so the instruction can be calculated when the bundled image |
| 102 | is assembled. The return value that is saved in the Link Register by the |
| 103 | ``BL`` instruction is used by RMM core to determine the memory address of the |
| 104 | header of the first app in the bundle. |
| 105 | * The rest of the RMM app bins (if any) unmodified. |
| 106 | * The RMM core img file aligned to 64KB offset to meet ELF loader requirements. |
| 107 | |
| 108 | The packaged RMM binary must be loaded at a 64KB-aligned physical address to |
| 109 | ensure that the RMM core itself is also 64KB-aligned. This alignment requirement |
| 110 | stems from the use of a 1:1 virtual-to-physical address mapping for the RMM |
| 111 | core. In contrast, EL0 apps run at virtual addresses determined at compile time, |
| 112 | which are already 64KB-aligned. As a result, the physical address alignment of |
| 113 | app binaries is not significant. |
| 114 | |
| 115 | The number of RMM apps expected by RMM core is hardcoded in RMM core. |
| 116 | The RMM app headers are parsed by the RMM core during the coldboot phase. The |
| 117 | RMM app headers contain a magic value, so if less than the expected number of |
| 118 | RMM apps is added to the final bin that can be detected, then boot fails in that |
| 119 | case. If more than the hardcoded number of RMM apps is present, RMM boot fails |
| 120 | as well. |
| 121 | |
| 122 | |RMM bin generation| |
| 123 | |
| 124 | **************** |
| 125 | RMM App Instance |
| 126 | **************** |
| 127 | |
| 128 | In this document app instance refers to an execution context, along with the |
| 129 | necessary memory that is allocated for it. In operating system terms, a thread |
| 130 | is analogous to an EL0 App instance. |
| 131 | |
| 132 | To manage and run an RMM app instance, RMM core needs memory for the following |
| 133 | purposes: |
| 134 | |
| 135 | 1. RMM app context: A single page of memory for use by the context switch logic |
| 136 | to back up registers. This memory page acts as app's CPU context storage when |
| 137 | it is not scheduled. This page is mapped in the APP VA space as well, with |
| 138 | permissions that it is only accessible by privileged execution (i.e. EL2). |
| 139 | The mapping is necessary because, on exit from an RMM app to the RMM core, |
| 140 | the RMM app's virtual address (VA) space is still active and set up in the |
| 141 | High VA region, while the RMM core's VA space is not yet available. To switch |
| 142 | back to RMM Core High VA space, the general purpose registers need to be |
| 143 | backed up beforehand. To enable this, the app context page is mapped at the |
| 144 | beginning of the app’s VA space (without UNPRIV access). This fixed mapping |
| 145 | allows the context switch code to use a hardcoded address for the register |
| 146 | backup. Consequently, the EL0 app’s accessible address space begins at a 4KB |
| 147 | offset from the start of the High VA region |
| 148 | |
| 149 | 2. A memory page that is used to exchange RMM app specific data between the |
| 150 | RMM app and the RMM app caller mapped both in EL2 and in EL0. (Also |
| 151 | referenced as shared page by this document.) |
| 152 | |
| 153 | 3. Metadata for the RMM app (``struct app_data_cfg``). This includes |
| 154 | |
| 155 | * xlat library structures including the pointer to the pagetable for the RMM |
| 156 | app. |
| 157 | * entry point |
| 158 | * PA of the app_reg_ctx |
| 159 | * PA of the shared page |
| 160 | |
| 161 | 4. Page containing the pagetable for the EL0 memory mappings (currently the RMM |
| 162 | app has a limited 2MB VA space) |
| 163 | |
| 164 | 5. Stack for RMM app. |
| 165 | |
| 166 | 6. Heap for RMM app. |
| 167 | |
| 168 | In the current implementation there are 3 possible locations where the above |
| 169 | mentioned data is stored: |
| 170 | |
| 171 | a. In statically allocated per-cpu buffers. |
| 172 | b. In the rec structure for a REC and in the rec aux pages |
| 173 | c. In the pdev structure for a PDEV object in the pdev aux pages |
| 174 | |
| 175 | In case of *b.* and *c.* the memory pages are delegated by the host. |
| 176 | |
| 177 | Note that this introduces a limitation on the stack/heap of the RMM apps, as the |
| 178 | max count of aux pages is limited, and RMM core is already using some of them. |
| 179 | |
| 180 | **************** |
| 181 | RMM App VA space |
| 182 | **************** |
| 183 | |
| 184 | The RMM app va space (as it is configured in the xlat library) contains the |
| 185 | following regions: |
| 186 | |
| 187 | * Single page for the RMM app context (Transient) |
| 188 | * RMM app text |
| 189 | * RMM app RO data |
| 190 | * RMM app data |
| 191 | * RMM app BSS |
| 192 | * RMM app Shared (Transient) |
| 193 | * RMM app heap (Transient) |
| 194 | * RMM app stack (Transient) |
| 195 | |
| 196 | The pages that are mapped in the transient region are private to the RMM app |
| 197 | instance. As all the other regions are common, it is possible to initialise the |
| 198 | xlat_ctx_cfg used by apps during RMM boot. |
| 199 | |
| 200 | ************************** |
| 201 | Initialising RMM app pages |
| 202 | ************************** |
| 203 | |
| 204 | The pages that are instance specific (`RMM App Instance`_) needs to be initialised |
| 205 | on creating an RMM app instance. |
| 206 | |
| 207 | As it is possible that the pages are delegated by the host, and the pages need |
| 208 | to be mapped dynamically using slot-buffer framework, a new slot is added to the |
| 209 | slot buffer. When initialising such pages, the page under initialisation is |
| 210 | mapped into this new slot, and unmapped after initialising. |
| 211 | |
| 212 | The ``app_init_data`` function receives an array of physical addresses of |
| 213 | the pages to be used for the RMM app. In case of initialisation the code checks |
| 214 | whether the PA is in the range of the RMM core's rw PA range. If so, it can |
| 215 | write to it directly. If not, the slot buffer mechanism is used as described |
| 216 | above. |
| 217 | |
| 218 | Mappings when using AUX pages for RMM app RW memory: |
| 219 | |
| 220 | |RMM app memory aux| |
| 221 | |
| 222 | Mappings when using statically allocated pages for RMM app RW memory: |
| 223 | |
| 224 | |RMM app memory static| |
| 225 | |
| 226 | ********************* |
| 227 | RMM App Service Calls |
| 228 | ********************* |
| 229 | |
| 230 | Since the apps are running at EL0, it cannot perform some privileged operations |
| 231 | like communicate with EL3 or copy data from NS host buffer etc. RMM provides |
| 232 | some services for RMM apps (like printing). The services are required to have |
| 233 | the signature |
| 234 | |
| 235 | |
| 236 | .. code-block:: bash |
| 237 | |
| 238 | typedef uint64_t (*app_service_func)(struct app_data_cfg *app_data, |
| 239 | unsigned long arg0, |
| 240 | unsigned long arg1, |
| 241 | unsigned long arg2, |
| 242 | unsigned long arg3); |
| 243 | |
| 244 | Services are expected to be thread safe. |
| 245 | |
| 246 | Some services can call other apps to perform its functionality (like PRNG). But |
| 247 | this kind of nesting is limited to one other app to avoid interdependencies |
| 248 | between apps. |
| 249 | |
| 250 | Services are stored in a function pointer array. There is a single array in the |
| 251 | system. |
| 252 | |
| 253 | The RMM app that calls a service must do an SVC with a predefined immediate |
| 254 | value. The index of the service to be executed is selected by the value of the |
| 255 | X0 register. |
| 256 | |
| 257 | The el0_app framework calls app_enter in a loop. It exits the loop |
| 258 | if the SVC immediate value reports function return or yield (in future). |
| 259 | |
| 260 | ******************** |
| 261 | Debugging RMM on FVP |
| 262 | ******************** |
| 263 | |
| 264 | The RMM core text section offset is changed due to the app binaries being |
| 265 | prepended before the RMM core img. The ``bundle_app_rmm.py`` script prints the |
| 266 | text section offset in the binary which should be added to the RMM load address |
| 267 | when loading the elf in the debugger for symbols. The script output is in the |
| 268 | file ``<build_dir>/<build_type>/bundle_app_out.txt``. The offset is also printed |
| 269 | out during the build process. |
| 270 | |
| 271 | The RMM apps are linked to the VA address that they are going to be loaded by |
| 272 | RMM core, so the RMM app symbols needs to be loaded in the debugger with offset |
| 273 | ``0x0`` |
| 274 | |
| 275 | ******************* |
| 276 | RMM Fake Host Build |
| 277 | ******************* |
| 278 | |
| 279 | In case of the fake host build, the applications are compiled as a standalone |
| 280 | elf file. The RMM core is compiled to the elf file ``rmm_core.elf``. The path |
| 281 | to the RMM app elf files are passed to ``rmm_core.elf`` as a command line |
| 282 | parameter, along with the ID of the RMM apps. The first time the main RMM |
| 283 | process calls ``app_init_data`` the process is forked, the image of the |
| 284 | requested RMM app is loaded in the new process, and a named-pipe connection is |
| 285 | established between the two processes. When the RMM app process is created, for |
| 286 | each ``app_init_data`` (including the first call) a new thread is created. The |
| 287 | main thread in the RMM app process is responsible for dispatching the RMM app |
| 288 | calls and returns between the main RMM process and the RMM app thread. |
| 289 | |
| 290 | There is no shared memory between the main and the RMM app processes, memory |
| 291 | sharing is emulated by sending over the content of the main process's "shared |
| 292 | page" to the RMM app thread on an ``app_run`` call, and sending back the content |
| 293 | of the RMM app thread's "shared page" on RMM app function return. |
| 294 | |
| 295 | |RMM app host| |
| 296 | |
| 297 | The help of the main process: |
| 298 | |
| 299 | .. code-block:: |
| 300 | |
| 301 | $ rmm_core.elf --help |
| 302 | Run RMM on the host |
| 303 | |
| 304 | Usage: rmm_core.elf [-h|--help] [app_id app_elf [...]] |
| 305 | |
| 306 | Arguments: |
| 307 | -h, --help print this message and exit. |
| 308 | app_id Integer value of app id of the app. |
| 309 | app_elf path to the app's elf file. |
| 310 | |
| 311 | |
| 312 | An example call: |
| 313 | |
| 314 | .. code-block:: bash |
| 315 | |
| 316 | $ Debug/rmm_core.elf \ |
| 317 | 103 Debug/rmm_app_random.elf \ |
| 318 | 211 Debug/rmm_app_attestation.elf |
| 319 | |
| 320 | ************************* |
| 321 | RMM Apps currently in RMM |
| 322 | ************************* |
| 323 | |
| 324 | Currently there are 2 RMM apps in RMM: |
| 325 | |
| 326 | - attestation: Used for doing attestation related tasks, like attestation |
| 327 | token generation and hash calculation |
| 328 | - random: A small application with a pseudo random number generator, either |
| 329 | seeded with a true random sequence, or with input from an already initialised |
| 330 | random RMM app. |
| 331 | |
| 332 | Currently both apps have their own build of Mbed TLS linked to them. |
| 333 | |
| 334 | There is an instance of random number RMM app initialised for each of the CPUs. |
| 335 | There is also an attestation app initialised for each CPU for doing RIM |
| 336 | extension, and there is an RMM app instance initialised for each REC, to do |
| 337 | measurement extend and attestation token generation. |
| 338 | |
| 339 | The attestation RMM app uses the random RMM app via a service. The attestation |
| 340 | RMM app reaches the EL3 interface (for requesting realm attestation key |
| 341 | and platform token) via an RMM app service. |
| 342 | |
| 343 | The layout of the RMM apps can be seen on the image below: |
| 344 | |
| 345 | |RMM app layout| |
| 346 | |
| 347 | Note: To be continued with DA related RMM apps |
| 348 | |
| 349 | ************************ |
| 350 | Directory layout of apps |
| 351 | ************************ |
| 352 | |
| 353 | .. code-block:: bash |
| 354 | |
| 355 | . |
| 356 | └── app |
| 357 | ├── attestation |
| 358 | │ ├── el0_app |
| 359 | │ │ ├── mbedtls_attest # Library (EL0) |
| 360 | │ │ ├── qcbor # Library (EL0) |
| 361 | │ │ ├── src # Implementation of the attestation RMM app (EL0) |
| 362 | │ │ └── t_cose # Library (EL0) |
| 363 | │ └── rmm_stub # Helper function for using the Attestation RMM app (EL2) |
| 364 | ├── common |
| 365 | │ ├── el0_app # Common code that is used by multiple apps (EL0) |
| 366 | │ ├── framework # Framework for calling an app and provide app services (EL2) |
| 367 | │ ├── include |
| 368 | │ └── rmm_svc # Implementation of RMM services provided to apps (EL2) |
| 369 | └── random |
| 370 | ├── el0_app |
| 371 | │ ├── mbedtls_random # Library (EL0) |
| 372 | │ └── src # Implementation of the Random RMM app (EL0) |
| 373 | └── rmm_stub # Helper function for using the Random RMM app (EL2) |
| 374 | |
| 375 | ********************************************** |
| 376 | Proposed Enhancements to the RMM App Framework |
| 377 | ********************************************** |
| 378 | |
| 379 | #. Shared Memory for ``fake_host`` |
| 380 | |
| 381 | Implement support for shared memory to enable communication between processes |
| 382 | within the ``fake_host`` environment. |
| 383 | |
| 384 | #. Configurable App ID |
| 385 | |
| 386 | Currently, the App ID is hardcoded. There is a suggestion to derive it from a |
| 387 | header generated as part of the configuration. This approach can be explored |
| 388 | further. |
| 389 | |
| 390 | #. App BSS Allocation |
| 391 | |
| 392 | Consider whether the App BSS should be allocated dynamically. Alternatively, |
| 393 | RMM could reserve a dedicated section for app BSS and allocate memory from |
| 394 | there. |
| 395 | |
| 396 | #. Common Binary Generation Logic |
| 397 | |
| 398 | The logic for generating app bin files can be consolidated or reused. |
| 399 | |
| 400 | #. Avoid Unnecessary Remapping of Shared Buffers |
| 401 | |
| 402 | When shared buffers are located in auxiliary granules, remapping may not be |
| 403 | needed if the granules are already mapped. |
| 404 | |
| 405 | #. Unify VBAR Usage |
| 406 | |
| 407 | Currently, separate VBARs are set up for the app and RMM. Evaluate whether a |
| 408 | single VBAR could be used to simplify the design. |
| 409 | |
| 410 | #. Lightweight App Threading in ``fake_host`` |
| 411 | |
| 412 | Replace full PThreads with a lighter-weight threading model for running apps |
| 413 | in the ``fake_host`` test environment. |
| 414 | |
| 415 | #. Enable FEAT_PAN/PAN3 for EL0 |
| 416 | |
| 417 | Ensure that the appropriate Privileged Access Never (PAN) features (including |
| 418 | PAN3) are correctly set when executing code at EL0. |
| 419 | |
| 420 | #. Hardening ``app_services`` |
| 421 | |
| 422 | The ``app_services`` interface should be hardened. This includes: |
| 423 | |
| 424 | - Clear documentation of function arguments and return codes |
| 425 | - Proper validation of input parameters |
| 426 | |
| 427 | #. Harden ``rmm_stub`` and Define Conventions |
| 428 | |
| 429 | The ``rmm_stub`` must validate all return values from EL0 apps. Also, define |
| 430 | a consistent convention for parameter passing between the EL0 app and the |
| 431 | stub to support future extensibility. |
| 432 | |
| 433 | #. Optional TLBI on EL0 Entry |
| 434 | |
| 435 | In certain cases, Translation Lookaside Buffer Invalidate (TLBI) may not be |
| 436 | needed when entering an EL0 app. This optimization is already noted in the |
| 437 | code and should be validated and documented. |
| 438 | |
| 439 | #. Post-Build App Repackaging |
| 440 | |
| 441 | Explore the possibility of repackaging EL0 apps after the main RMM binary is |
| 442 | built. |
| 443 | |
| 444 | #. Unmap heap pages allocated to app from RMM high VA space. |
| 445 | |
| 446 | When heap is allocated from REC aux granules, the aux granules are always |
| 447 | mapped when the REC is in use and the app is not running. Even if app is |
| 448 | running stack and pages table page not needed to be mapped, and unless some |
| 449 | data is being shared between core and app via heap, heap pages are not needed |
| 450 | to be mapped |
| 451 | |
| 452 | .. |RMM app EL0| image:: ./diagrams/rmm_app_EL0.drawio.png |
| 453 | .. |RMM bin generation| image:: ./diagrams/rmm_bin_generation.drawio.png |
| 454 | .. |RMM app memory aux| image:: ./diagrams/rmm_app_memory_aux.drawio.png |
| 455 | .. |RMM app memory static| image:: ./diagrams/rmm_app_memory_static.drawio.png |
| 456 | .. |RMM app host| image:: ./diagrams/rmm_app_on_host.drawio.png |
| 457 | .. |RMM app layout| image:: ./diagrams/rmm_app_layout.drawio.png |